Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

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をやったばかりなんです。叫びたい年頃なんです。

alucky0707
ウィルキンソンジンジャエールを片手にコードを書く人。JavaScriptぐらいしかまともに書けない…。東京都内の工業高校に通うザコ学生。
http://alucky0707.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away