GUI编程

互联网上已经涌现出不少成熟、好用的第三方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
  1. 布局

在这里插入图片描述

常用控件选择区:列举了常用的控件,常用的有三类:顶层(主窗口等),容器(各种布局容器等),控制和显示(按钮、便签、图片控件等)

界面编辑区:把控件拖放在这进行进行相应的布局

控件监视区:能够看到界面上所有的控件,同时,选中这个控件,可以看到这个控件的具体类型

属性编辑区:编辑选中控件的常用属性,如窗口设置标题、窗口类型、屏幕上显示位置等。

工具栏:常用的有以下几个按钮

新建:新建一个glade文件
打开:打开一个已经存在的glade文件
保存:保存一个glade文件
选择:按了这个按钮, 才能选择控件
拖拽调整大小:按了这个按钮,才能移动控件的位置,改变控件的大小

  1. Glade的操作

选择控件时,一定要先按工具栏的“选择”按钮
操作时,支持撤销(Ctrl+z)和恢复(Ctrl+y)等window的快捷键

操作的流程和布局的过程是一致的:

1.选择主窗口,根据需要设置窗口的相应属性

  1. 选择布局容器
  2. 根据需要选择相应的控件,根据需要设置控件的相应属性
  • 环境搭建(windows)
  1. 下载安装msys2

官方网址:http://www.msys2.org/

  1. 安装所需软件
    安装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
  1. 配置环境变量
    配置:
    PATH:
    C:\msys64\usr\bin
    C:\msys64\mingw64\bin

    测试:
    pkg-config --cflags gtk+-2.0
    make -v
  1. 下载依赖
    //官方
    # 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
  1. 运行官方demo
    # cd $GOPATH/github.com/mattn/go-gtk/example/demo
    # go build demo.go
    # demo.exe

在这里插入图片描述

  1. 导入依赖
    import (
        "github.com/mattn/go-gtk/gdkpixbuf"
        "github.com/mattn/go-gtk/glib"
        "github.com/mattn/go-gtk/gtk"
    )
  • 控件

控件是对数据和方法的封装。控件有自己的属性和方法。属性是指控件的特征。方法是指控件的一些简单而可见的功能

GTK中控件主要分为两类:容器控件,非容器控件。

  1. 容器控件:它可以容纳别的控件,我们可以理解为盒子,盒子拿来装东西。容器控件又分为两类,一类只能容纳一个控件,如窗口,按钮;另一类能容纳多个控件,如布局控件。
  2. 非容器控件:它不可以容纳别的控件,如标签、行编辑。
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

  • 安装
  1. 安装完整的Qt5在$HOME目录下
    https://download.qt.io/official_releases/online_installers/

  2. 配置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=".*"

  1. 补全依赖:

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