hakeの日記

Windows環境でプログラミングの勉強をしています。

Go言語でGUIプログラム on Windows

VisualuRubyが使用できなくなってから、exeファイル単体で動作して、かつ、ファイルサイズが巨大にならないGUIアプリ制作環境を探していましたが、Go言語+WALKライブラリがなかなか良さげなのでメモ。
後述のhello.exeの実行ファイルのサイズは5MB弱で、Windows 8.1(32bit)でコンパイルした実行ファイルがWindows Vistaでも動作しました。

入手先

その他

  • play.golang.org : Go言語をインストールしていなくても簡単なプログラムを実行できるので便利。
  • 購入した本(GUI関連の記載はありません)

改訂2版 基礎からわかる Go言語

改訂2版 基礎からわかる Go言語

  • こんな本も出版されました(2016/04追記)

スターティングGo言語 (CodeZine BOOKS)

スターティングGo言語 (CodeZine BOOKS)

  • さらに追加(2016/06追記)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

インストール

Go言語本体はmsi(今回は go1.4.2.windows-386.msi)をダウンロードして、そのままインストールする。デフォルトインストール先はC:\GO です。
zipを使用した場合は、環境変数GOROOTを設定(C:\GO)する。


WALKライブラリは以下のコマンドを実行する。(要Git他)

go get github.com/lxn/walk

今回はPCに必要なアプリ(Git)をインストールしていなかったので、walkフォルダ、winフォルダのzipファイルをダウンロードして、C:\GO\src以下に展開しました。

C:\Go\src\github.com\lxn\walk\*
C:\Go\src\github.com\lxn\win\*

コンパイル

ソースを置いた場所で以下のコマンドを実行します。

go build src.go                               CUIアプリ
go build -ldflags="-H windowsgui" src.go      GUIアプリ(DOS窓を表示させない)
go build -ldflags="-s -H windowsgui" src.go   stripをかけてサイズ縮小

コマンドで、ソースファイル名を省略した場合(同package名の複数ソースファイルがある場合など?)は「フォルダ名」.exeが作成されます。またWALKライブラリを使用する場合は「実行ファイル名」.manifestという名前のマニフェストファイルを同じ場所に置いておかないと起動しないので注意が必要。マニフェストファイルはコピーしてファイル名だけを変更して(内容はいじらない)使いまわせます。

サンプル

当方Go言語関連の記事も参照ください。


以下は、ボタンをクリックすると"Hello 世界!"と表示するプログラムです。ソースはutf-8で保存します。
ライブラリの使用方法は参考サイトや、ライブラリのexampleを参照のこと、VisualuRubyを使用していたのでそんなに違和感なく使用できそうです。

hello.go

package main

import (
    "github.com/lxn/walk"
    . "github.com/lxn/walk/declarative"
)

import (
//    "fmt"
    "log"
)

type MyMainWindow struct {
    *walk.MainWindow
    textArea *walk.TextEdit
}

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

    // Windowのレイアウト
    //
    // MainWindow
    //     TextEdit
    //     PushButton
    //
    MW := MainWindow{
        AssignTo: &mw.MainWindow,     // Widgetを実体に割り当て
        Title: "Hello",
        MinSize: Size {300, 400},
        Layout: VBox {},              // ウィジェットを垂直方向に並べる
        Children: []Widget {          // ウィジェットを入れるスライス
            TextEdit {                // 最初のウィジェット(テキスト)
                AssignTo: &mw.textArea, 
            },
            PushButton {              // 次のウィジェット(ボタン)
                Text: "Hello!",       //     PushButtonは割り当てしない?
                                      //     内部にデータを持たない為?
                OnClicked: mw.pbClicked,   // ボタンクリックイベント
            },
        },
    }

    if _, err := MW.Run(); err != nil {
        log.Fatal(err)
    }
}


// ボタンクリック時の処理
func (mw *MyMainWindow) pbClicked() {

    const s string = "Hello 世界!\r\n" // TextEdit内の改行は\r\n

    text := mw.textArea
    text.AppendText(s)     // 文字列追加
}

hello.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>