互联网上已经涌现出不少成熟、好用的第三方GUI界面库。
 https://github.com/avelino/awesome-go#gui
GTK2
GTK+ 是一种面向对象式的API(applicationprogramming interface)。Glib 是GTK+的基础,而这种“面向对象系统”正是由“Glib”来提供的。GTK+ 是一种函数库是用来帮助制作图形交互界面的,同时遵循 LGPL 许可证。
 GTK2:https://github.com/mattn/go-gtk
- 工具:Glade
- 布局

常用控件选择区:列举了常用的控件,常用的有三类:顶层(主窗口等),容器(各种布局容器等),控制和显示(按钮、便签、图片控件等)
界面编辑区:把控件拖放在这进行进行相应的布局
控件监视区:能够看到界面上所有的控件,同时,选中这个控件,可以看到这个控件的具体类型
属性编辑区:编辑选中控件的常用属性,如窗口设置标题、窗口类型、屏幕上显示位置等。
工具栏:常用的有以下几个按钮
新建:新建一个glade文件
打开:打开一个已经存在的glade文件
保存:保存一个glade文件
选择:按了这个按钮, 才能选择控件
拖拽调整大小:按了这个按钮,才能移动控件的位置,改变控件的大小
- Glade的操作
选择控件时,一定要先按工具栏的“选择”按钮
操作时,支持撤销(Ctrl+z)和恢复(Ctrl+y)等window的快捷键
操作的流程和布局的过程是一致的:
1.选择主窗口,根据需要设置窗口的相应属性
- 选择布局容器
- 根据需要选择相应的控件,根据需要设置控件的相应属性
- 环境搭建(windows)
- 下载安装msys2
官方网址:http://www.msys2.org/
- 安装所需软件
    安装gtk3:
    pacman -S mingw-w64-x86_64-gtk3
    安装gtk2:
    pacman -S mingw-w64-x86_64-gtk2
    安装glade
    pacman -S mingw-w64-x86_64-glade
    安装帮助文档
    pacman -S mingw-w64-x86_64-devhelp
    安装MinGW
    pacman -S mingw-w64-x86_64-toolchain base-devel
- 配置环境变量
    配置:
    PATH:
    C:\msys64\usr\bin
    C:\msys64\mingw64\bin
    测试:
    pkg-config --cflags gtk+-2.0
    make -v
- 下载依赖
    //官方
    # go get github.com/mattn/go-gtk/gtk
    //国内  放在GOPATH目录src下
    # git clone  https://github.com/mattn/go-gtk
    # cd go-gtk
    # make install
    # make example
    # ./example/demo/demo
- 运行官方demo
    # cd $GOPATH/github.com/mattn/go-gtk/example/demo
    # go build demo.go
    # demo.exe

- 导入依赖
    import (
        "github.com/mattn/go-gtk/gdkpixbuf"
        "github.com/mattn/go-gtk/glib"
        "github.com/mattn/go-gtk/gtk"
    )
- 控件
控件是对数据和方法的封装。控件有自己的属性和方法。属性是指控件的特征。方法是指控件的一些简单而可见的功能
GTK中控件主要分为两类:容器控件,非容器控件。
- 容器控件:它可以容纳别的控件,我们可以理解为盒子,盒子拿来装东西。容器控件又分为两类,一类只能容纳一个控件,如窗口,按钮;另一类能容纳多个控件,如布局控件。
- 非容器控件:它不可以容纳别的控件,如标签、行编辑。
Put:
func (v *Fixed) Put(w IWidget, x, y int)
功能:固定布局容器添加控件
参数:
    widget:要添加的控件
    x, y:控件摆放位置的起点坐标
ShowAll:
func (v *Widget) ShowAll()
功能:显示所有的控件,如果窗口放入一个容器,这时,容器上的所有控件也会跟着显示。
package main
import (
    "os"
    "github.com/mattn/go-gtk/gtk"
)
func main() {
    gtk.Init(&os.Args)
    /*
        gtk.Init(&os.Args):所有 GTK应用程序都要调用该函数,
        而且必须在控件定义之前使用,它为我们设置一些缺省值
        ( 例如视觉和颜色 )映射这个函数将函数库初始化,设置
        缺省的信号处理函数,并检查通过命令行传递给应用程序的
        参数,自动完成一些必要的初始化工作。
    */
    //--------------------------------------------------------
    // 主窗口
    //--------------------------------------------------------
    window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
    /*
        gtk.NewWindow(gtk.WINDOW_TOPLEVEL):创建一个窗口并
        返回这个窗口的控件指针。gtk.WINDOW_TOPLEVEL指明窗口
        的类型为最上层的主窗口(则带边框的窗口),它最常用。
    */
    window.SetPosition(gtk.WIN_POS_CENTER)       //设置窗口居中显示
    window.SetTitle("Minor Six Ren")                   //设置标题
    window.SetSizeRequest(300, 200)              //设置窗口的宽度和高度
    //--------------------------------------------------------
    // GtkFixed
    //--------------------------------------------------------
    layout := gtk.NewFixed() //创建固定布局
    //--------------------------------------------------------
    // GtkButton
    //--------------------------------------------------------
    b1 := gtk.NewButton() //新建按钮
    b1.SetLabel("^_@")    //设置内容
    //b1.SetSizeRequest(100, 50) //设置按钮大小
    b2 := gtk.NewButtonWithLabel("@_~") //新建按钮,同时设置内容
    b2.SetSizeRequest(100, 50)          //设置按钮大小
    //--------------------------------------------------------
    // 添加布局、添加容器
    //--------------------------------------------------------
    window.Add(layout) //把布局添加到主窗口中
    layout.Put(b1, 0, 0)    //设置按钮在容器的位置
    layout.Move(b1, 50, 50) //移动按钮的位置,必须先put,再用move
    layout.Put(b2, 50, 100)
    window.ShowAll() //显示所有的控件
    /*
        window.Show():显示上一步创建的窗口控件。
        window.ShowAll():显示所有的控件,如果窗口放入一个容器,这时,容器上的所有控件也会跟着显示
在这个简单例子里,所有事件都被忽略。用鼠标点击窗口右上角的“×”按钮也不能将窗口关闭。可通过任务管理器关闭。
    */
    gtk.Main() //主事件循环,等待用户操作
    /*
        gtk.Main():它是在每个Gtk应用程序都要调用的
        函数。程序运行停在这里等待事件(如键盘事件或
        鼠标事件)的发生,等待用户来操作窗口。
    */
}
- 信号处理
 “信号”在GTK中可以认为一种中断的标志
| 信号标识 | 触发条件 | 
|---|---|
| clicked | 按下按钮时触发 | 
| pressed | 按下按钮时触发 | 
| released | 释放按钮时触发 | 
| destroy | 按关闭窗口按钮时触发 | 
信号注册函数说明:
func (v *Widget) Connect(s string, f interface{}, datas ...interface{}) int
功能:信号注册
参数:
    v: 信号发出者,可以认为我们操作的控件,如按下按钮,这个就为按钮指针
    s:信号标志,如"pressed"
    f:回调函数的名称,
    datas:给回调函数传的参数,尽管是可变参数,但是只能传递一个参数,可变参数的目的为了让用户多个选择(可以传参,或者不传)
返回值:
    注册函数的标志
    package main
    import (
        "fmt"
        "os"
        "github.com/mattn/go-gtk/glib"
        "github.com/mattn/go-gtk/gtk"
    )
    //按钮b1信号处理的回调函数
    func HandleButton(ctx *glib.CallbackContext) {
        arg := ctx.Data()   //获取用户传递的参数,是空接口类型
        p, ok := arg.(*int) //类型断言
        if ok {             //如果ok为true,说明类型断言正确
            fmt.Println("*p = ", *p) //用户传递传递的参数为&tmp,是一个变量的地址
            *p = 250                 //操作指针所指向的内存
        }
        fmt.Println("按钮b1被按下")
        //gtk.MainQuit() //关闭gtk程序
    }
    func main() {
        gtk.Init(&os.Args)
        window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
        window.SetPosition(gtk.WIN_POS_CENTER)
        window.SetTitle("GTK Go!")
        window.SetSizeRequest(300, 200)
        layout := gtk.NewFixed()
        b1 := gtk.NewButton()
        b1.SetLabel("按钮1")
        b2 := gtk.NewButtonWithLabel("按钮2")
        b2.SetSizeRequest(100, 50)
        window.Add(layout)
        layout.Put(b1, 0, 0)
        layout.Move(b1, 50, 50) //移动按钮的位置,必须先put,再用move
        layout.Put(b2, 50, 100)
        //--------------------------------------------------------
        // 信号处理
        //--------------------------------------------------------
        //按钮按下自动触发"pressed",自动调用HandleButton, 同时将 &tmp 传递给HandleButton
        tmp := 10
        b1.Connect("pressed", HandleButton, &tmp)
        //回调函数为匿名函数,推荐写法
        //按钮按下自动触发"pressed",自动调用匿名函数,
        b2.Connect("pressed", func() {
            fmt.Println("b2被按下")
            fmt.Println("tmp = ", tmp)
        }) //注意:}和)在同一行
        window.ShowAll()
        gtk.Main()
    }
GTK+3
GTK+3:https://github.com/gotk3/gotk3
    import (
        "github.com/gotk3/gotk3/glib"
        "github.com/gotk3/gotk3/gtk"
    )
package main
import (
    "fmt"
    "github.com/gotk3/gotk3/glib"
    "github.com/gotk3/gotk3/gtk"
    "log"
    "os"
)
//这部分是相同的
func main() {
    const appId = "com.guidemo.example"
    //每个gtk3程序都需要一步
    app, err := gtk.ApplicationNew(appId, glib.APPLICATION_FLAGS_NONE)
    /*
        gtk.ApplicationNew()接受两个参数一个是 应用标识,其一般
        使用你域名的倒序形式。另一个是GApplicationFlags,其为了
        满足你对应用的特定需求。通常就像实例代码一样写
        glib.APPLICATION_FLAGS_NONE就可以了。
    */
    if err != nil {
        log.Fatal("Could not create application.", err)
    }
    //为activate事件绑定函数, activate会在程序启动时触发,也就是app.Run()时
    app.Connect("activate", func() {
        onActivate(app)
    } )
    //运行gtkApplication
    app.Run(os.Args)
}
func onActivate(application *gtk.Application) {
    appWindow, err := gtk.ApplicationWindowNew(application) //创建window控件
    if err != nil {
        log.Fatal("Could not create application window.", err)
    }
    //设置窗口属性
    appWindow.SetTitle("Basic Application.")
    appWindow.SetDefaultSize(400, 400)
    buttonBox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2)     //以水平布局创建一个容器, 第二个参数是其中控件的像素间隔
    if err != nil {
        log.Fatal(err)
    }
    appWindow.Add(buttonBox)    //将布局添加到window中
    button, err := gtk.ButtonNewWithLabel("Hello World")        //创建一个按钮
    if err != nil {
        log.Fatal(err)
    }
    buttonBox.Add(button)       //将按钮添加到box容器中
    button.Connect("clicked", func() {  //让我们为按钮点击添加一个函数,每次点击都会在命令行输出Hello World
        fmt.Println("Hello World")
        appWindow.Destroy()     //摧毁窗口
    })
    appWindow.ShowAll()     //与Show()不同在于,它会输出Window中的子控件。你可以修改,查看不同的效果
}
QT
QT: https://github.com/therecipe/qt
 参考:https://github.com/therecipe/qt/wiki/Installation
- 安装
-  安装完整的Qt5在$HOME目录下 
 https://download.qt.io/official_releases/online_installers/
-  配置Qt的环境 
# ~/.bash_profile
# therecipe/qt 需要的环境变量
export QT_DIR='/home/用户名/Qt5.11.1' # 安装Qt的目录
export QT_VERSION='5.11.1'           # Qt的版本号
export QT_DEBUG=false                # 是否启用debug
export QT_STUB=false                 # 内存低于2Gb或32位系统才需要设置true
# go1.10 cgo environments 使用go1.10时需要的设置
export CGO_CXXFLAGS_ALLOW=".*"
export CGO_LDFLAGS_ALLOW=".*"
export CGO_CFLAGS_ALLOW=".*"
- 补全依赖:
g++5.0+以及一些OpenGL的依赖
# Debian/Ubuntu的安装命令
sudo apt-get -y install build-essential libglu1-mesa-dev libpulse-dev libglib2.0-dev
# Fedora/RHEL/CentOS
sudo yum -y groupinstall "C Development Tools and Libraries"
sudo yum -y install mesa-libGLU-devel gstreamer-plugins-base pulseaudio-libs-devel glib2-devel
# openSUSE
sudo zypper -n install -t pattern devel_basis
# Arch Linux
sudo pacman -S base-devel
安装qt-tools:
go get -u -v github.com/therecipe/qt/cmd/...
安装bindings,记住远离sudo!!! (时间较久)
$GOPATH/bin/qtsetup
- 编译
 参考:https://github.com/therecipe/qt/wiki/Available-Tools
不能直接使用go build,因为qt使用了moc技术(元对象编译器),对于一些Qt的扩展语法需要进行额外的处理
qtdeploy build [target] [path/to/your/project]
说明:
target是指定的目标平台,编译完成后的程序将可以在target指定的平台上运行。
    如果也可以将target设为desktop,qtdeploy将会根据本地环境选择相对应的target。以下是部分可用的target选项:
                    desktop
                    windows
                    linux
                    android
                    android-emulator
                    ios
                    ios-simulator
                    sailfish
                    sailfish-emulator
                    rpi1
                    rpi2
                    rpi3
