WindowsでGo言語でGUIするにはWALKがいいかもしれない

  • 154
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Go言語でGUIしたいかー?! おー!!

とまあ、そんなノリで始まります。

Go言語でGUIするには?

Go言語でGUIするライブラリは意外と色々あります。

  • go-ui
    「Go言語 GUI」でググった時に、おそらく一番情報の多いライブラリ。ただ情報はあるけど大して気合の入ったライブラリではない模様。
    go-uiなんて大層な名前をしてるけど、その実態はQtバインディングだったりする。
    開発はもう終わったみたいで、 GoQt というのが後継らしいけど、 GoQt の方はまだリポジトリを作っただけのような状態だった。
    Windowsで導入するのがほぼ無理ゲー。(最重要事案)

  • go-gtk
    mattn氏が開発の中心となっているGo言語のGtk+バインディング。多分Go言語のGUIライブラリで一番開発が活発。
    Windowsでも動くらしいけれど、Win64な環境ではしったこっちゃないし、そもそもGtkベースだからLGPLなわけで、嫌LGPLな現代っ子の僕には向いてない。

  • wxGo
    名前の通りwxWidgetsのGoバインディング。SWIGを使っている。
    既に二年近くメンテされてないし、どうしてもwxWidgets使いたい人向け。

他にもいくつかある。ここら辺を参照。

ここまで見た感じだと、まともに開発されているライブラリはgo-gtkしかない気がします。でもなぁ、Gtkなんだよなぁ…。

そんなあなたに、WALKはいかが?

WALKという、Windows向けのGUIライブラリがあります。これは、何かのGo言語バインディングというわけではなくて、Go言語から直接Win32APIを叩くことでGUIを作るライブラリです。
開発は盛んな方だと思います。600コミット以上されていて、最近もコミットされ続けていますし。 go-gtk ほどじゃないのが残念ですが。

どれだけの人に伝わるかわかりませんが、Rubyで言えばVisualRubyみたいなものです、多分。

ぶっちゃけたことを言ってしまえばただWin32APIのラッパなのですが、うまい具合に抽象化されていて、結構使いやすいです。

あと、完全にWindows向けかつGo言語単体で完結しているので、Windowsならインストールが楽ちんです。

なにはともあれインストール

僕の環境はWindows 8上のgo version 1.1.1 windows/amd64ですが、Windowsならおそらくはうまくいくと思います。

インストール方法は、コンソール上で、

go get github.com/lxn/walk

を叩くだけ。簡単ですよね?

何も出力されなければ成功です。Go言語は怖いぐらいに 「沈黙は成功なり」 の法則を順守している気がします。せめて依存関係ぐらい出力してくれてもいいのに…。

ちなみに、何か出力されていたら十中八九エラーだと思います。恐らく一番多いエラーは、バージョン1.1.x未満のGo言語を使っていることによるので。とりあえず最新版のGo言語をインストールしてきてください。

他にも、環境変数GOPATHが設定されていなかったり、Gitがインストールされていなかったりするとエラーが出ます。注意してください。

それでは使ってみよう

テキストエリアの文字列を検索する、というプログラムを作ることにします。名前はsearchboxです。

まずは、mkdir searchbox && cd searchboxで、新しいディレクトリを作って、移動してください。ここに必要なファイルを置いていきます。
(なぜわざわざディレクトリを掘る必要があるの? って感じですが、あとあとgo buildして実行可能ファイルを作ることになるので、ディレクトリを分けておいたほうが都合がいいのです)

次に、プログラムの主役ソースコードを書きます。
とりあえず僕が書いたコードを載せておきます。(このコードのGist

searchbox.go
package main

//WALK関連のライブラリ
import (
    "github.com/lxn/walk"
    . "github.com/lxn/walk/declarative"
)

//その他標準ライブラリ
import (
    "fmt"
    "log"
    "strings"
)

func main() {
    mw := &MyMainWindow {}

    if _, err := (MainWindow {
        AssignTo: &mw.MainWindow,
        Title: "SearchBox",
        MinSize: Size {300, 400},
        Layout: VBox {},
        Children: []Widget {
            GroupBox {
                Layout: HBox {},
                Children: []Widget {
                    LineEdit {
                        AssignTo: &mw.searchBox,
                    },
                    PushButton {
                        Text: "検索",
                        OnClicked: mw.clicked,
                    },
                },
            },
            TextEdit {
                AssignTo: &mw.textArea,
            },
            ListBox {
                AssignTo: &mw.results,
                Row: 5,
            },
        },
    }.Run()); err != nil {
        log.Fatal(err)
    }

}

//structにまとめることで、グローバル変数を作らない
type MyMainWindow struct {
    *walk.MainWindow
    searchBox *walk.LineEdit
    textArea *walk.TextEdit
    results *walk.ListBox
}

func (mw *MyMainWindow) clicked() {
    word := mw.searchBox.Text()
    text := mw.textArea.Text()
    model := []string {}
    for _, i := range search(text, word) {
        model = append(model, fmt.Sprintf("%d文字目に発見", i))
    }
    log.Print(model)
    mw.results.SetModel(model)
}

//textからwordを検索して、位置をUnicode単位で返す
func search(text ,word string) (result []int) {
    result = []int {}
    i := 0
    for j, _ := range text {
        if strings.HasPrefix(text[j:], word) {
            log.Print(i)
            result = append(result, i)
        }
        i += 1
    }
    return
}

簡単にこのコードを説明すると、最初のimportでWALK関連のパッケージを読み込み、

//WALK関連のライブラリ
import (
    "github.com/lxn/walk"
    . "github.com/lxn/walk/declarative"
)

main関数の中でWindowの構造を作り(この部分がかなり宣言的でイケてると思います)、Runメソッドを呼びGUIを初めています。

    if _, err := (MainWindow {
        AssignTo: &mw.MainWindow,
        Title: "SearchBox",
        MinSize: Size {300, 400},
        Layout: VBox {},
        Children: []Widget {
            GroupBox {
                Layout: HBox {},
                Children: []Widget {
                    LineEdit {
                        AssignTo: &mw.searchBox,
                    },
                    PushButton {
                        Text: "検索",
                        OnClicked: mw.clicked,
                    },
                },
            },
            TextEdit {
                AssignTo: &mw.textArea,
            },
            ListBox {
                AssignTo: &mw.results,
                Row: 5,
            },
        },
    }.Run()); err != nil {

AssignToで実施のWidgetとの結びつけをするのですね。

で、その後のclickedメソッドが「検索」ボタンを押した時に呼ばれるものです。

func (mw *MyMainWindow) clicked() {
    word := mw.searchBox.Text()
    text := mw.textArea.Text()
    model := []string {}
    for _, i := range search(text, word) {
        model = append(model, fmt.Sprintf("%d文字目に発見", i))
    }
    log.Print(model)
    mw.results.SetModel(model)
}

search関数は、本当はindex/suffixarrayパッケージを使いたかったのですが、suffixarrayがbyteにしか対応していなかったため、byte位置を教えられてもなあ、という気がしたので定義したものです。

あとはまあ、雰囲気で察してください。

ソースコードができたら、マニフェストファイルを書きます。書かないと起動しないので注意してください。

マニフェストファイルは実行可能ファイル名.manifestという名前で保存します。
この場合はsearchbox.exe.manifestで問題ないでしょう。

searchbox.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
        <dependency>
            <dependentAssembly>
                <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
            </dependentAssembly>
        </dependency>
    </assembly>

ここまできたら、あとはbuildしてコンパイルするだけです。

go build

(起動時にコマンドプロンプトが表示されないようにするには、

go build -ldflags="-H windowsgui"

として下さい)

でオーケーです。

早速、起動してみましょう。

searchbox.exe

こんな画面が立ち上がれば、無事成功です。

searchbox1.png

青空文庫から適当に文章を引っ張ってきて「検索」をしてみると、こんな風になります。

searchbox2.png

良い感じじゃないでしょうか?

最後に

説明をかなり簡略化したため、わかりにくいところ(コードの意味とか)は多いと思います。そこはWALKのexamplesやgodocを見て補完してください。(他力本願ですみません)

Go言語でGUIをする、という記事はあまり無くて、しかも大抵は環境がOS XかLinuxという状況だったので、この記事がWindowsでGo言語やろうとしてる人の支えに、またGo言語を始める切っ掛けになったらいいな、なんて思ってみたりもしています。

それではっ。

イヤッッホォォォオオォオウ!Go言語最高ー!!

この前AIRをやったばかりなんです。叫びたい年頃なんです。