HaskellでGUIプログラムを作成する方法を解説します。
具体的にはgi-gtk-declarativeというライブラリを使ってHello Worldするまでの流れを解説します。開発環境はWindows 10です。
ソースコードの詳しい解説は次の記事でする予定です。
Gtk+3開発環境の構築
https://www.msys2.org/ からMSYS2のインストーラーをダウンロードして実行します。バージョンが古いかもしれませんが、このリンクをクリックしてもインストールをダウンロードできます。
次にMSYS2を実行してMSYSの端末を開き、必要なパッケージをインストールします。
pacman -Syy
pacman -S -q --noconfirm mingw-w64-x86_64-glade mingw64/mingw-w64-x86_64-pkg-config mingw64/mingw-w64-x86_64-gobject-introspection mingw64/mingw-w64-x86_64-gtksourceview3
最後に環境変数の設定を行います。
PATH: <msys>, <msys>/mingw64/bin, <msys>/usr/bin
PKG_CONFIG_PATH: <msys>\mingw64\lib\pkgconfig
SET XDG_DATA_DIRS: <msys>\mingw64\share
環境変数が存在しない場合は新規作成、存在する場合は追記してください。また、<msys>
は先ほどMSYS2をインストールしたフォルダに置き換えてください。
stackのインストールと設定
https://docs.haskellstack.org/en/stable/README/ からインストーラーをダウンロードして実行します。このリンクをクリックしてもインストーラーをダウンロードすることができます。
インストールが終わったら、端末(Windows Terminalなど)を立ち上げ、
cd <フォルダ>
stack new <プロジェクト名>
を実行してstackプロジェクトを作ります。このコマンドを実行すると、<フォルダ>
下に<プロジェクト名>
という名前のフォルダが作られます。このフォルダがプロジェクトのルートフォルダとなります。
次にエディタを開き、ルートフォルダ下のstack.yaml
というファイルを編集します。
resolver: lts-16.18
skip-msys: true
allow-newer: true
extra-deps:
- haskell-gi-0.24.5
- haskell-gi-base-0.24.3
- gi-harfbuzz-0.0.3
- gi-pango-1.0.23
- gi-gtk-declarative-0.6.3
- gi-gtk-declarative-app-simple-0.6.3
extra-lib-dirs:
- <msys>\mingw64\bin
ghc-options:
"$locals": "-threaded"
<msys>
は先ほどMSYS2のインストールフォルダに置き換えてください。
さらに、package.yaml
というファイルも編集します。dependenciesという項目を以下に書き換えてください。
dependencies:
- base >= 4.7 && < 5
- haskell-gi-base
- gi-gtk
- gi-gtk-declarative
- gi-gtk-declarative-app-simple
- pipes
- pipes-extras
GHC(Haskellのコンパイラ)をインストールします。
stack setup
最後に、ライブラリのバグを回避するためにライブラリの差し替えを行います。
まずはstackがGHCをどこにインストールしたのかを調べます。
stack path --programs
そして、ライブラリの差し替えを行います。
cp <msys>/mingw64/bin/zlib1.dll <ghc>/ghc-<version>/mingw/bin/zlib1.dll
<msys>
はMSYS2のインストールフォルダに、<ghc>
は先ほど調べたGHCのインストール場所に、<version>
はインストールしたGHCのバージョンに置き換えてください。
ソースコードの記述
プロジェクトフォルダには既にMain.hsというファイルがあると思います。このファイルの中のmain関数がプロジェクトのエントリポイントとなります。
そのため、まずはMain.hsを書き換えます。
{-# LANGUAGE OverloadedStrings, OverloadedLists, OverloadedLabels #-}
module Main where
import Data.Function ( (&) )
import Data.Text ( Text )
import Pipes
import qualified Pipes.Extras as Pipes
import Control.Monad ( void )
import GI.Gtk ( Label(..), Window(..) )
import GI.Gtk.Declarative
import GI.Gtk.Declarative.App.Simple
data State = Initial | Showing Text
data Event = Show Text | Closed
view' :: State -> AppView Window Event
view' s =
bin
Window
[ #title := "First Example"
, on #deleteEvent (const (True, Closed))
, #widthRequest := 400
, #heightRequest := 300
]
$ case s of
Initial -> widget Label [ #label := "No message" ]
Showing msg -> widget Label [ #label := msg ]
update' :: State -> Event -> Transition State Event
update' _ (Show msg) = Transition (Showing msg) (return Nothing)
update' _ Closed = Exit
main :: IO ()
main = void $ run App { view = view'
, update = update'
, inputs = [ greetings ]
, initialState = Initial
}
where
greetings =
cycle ["Hello", "World"]
& map Show
& Pipes.each
& (>-> Pipes.delay 1.0)
元々のMain.hsでは、srcフォルダのLib.hsファイルをインポートして使っていました。Main.hs書き換えたことでこのファイルは要らなくなったので削除してください。
仕上げにプロジェクトをコンパイルして実行します。
stack build
stack exec <プロジェクト名>-exe
参考サイト
Using haskell-gi in Windows
https://github.com/owickstrom/gi-gtk-declarative/blob/master/examples/Hello.hs