自宅PCに wxHaskell をインストールする際に wxWidgets をコンパイルした wxPack というのがあることを知り、 wxPackを使って wxHaskell をインストールしました。
wxPackをインストールするとwxFormBuilder もインストールされました。
wxFormBuilder は wxWidgets のGUIを設計するツールで、C++、Pythonのコードを吐き出すことが出来ます。
また、C++、Pythonのコード以外にもGUIの情報をxmlで表現した xrc を吐き出すことが出来、wxHaskellは xrcを読み込むことが出来ます。
- XRC の作成
wxRubyでGUIプログラミング -- XRCを使うの手順で xrc ファイルを作ります。
Dialogウィンドウを使用していますがリソースの読み込みに失敗してしまうので、Form を使いました。(「Formsタブを選択してDialogをクリックします」の手順は「Formsタブを選択してFormをクリックします」)
Form が出来たらプロジェクトをセーブし、File -> generate Code を選択してクリックすると noname.xrc のファイルが出力されます。
- wxFormBuilder が吐き出した xrc ファイル
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1"> <object class="wxFrame" name="CalcFrame"> <style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style> <size>500,300</size> <title>Calculator</title> <centered>1</centered> <aui_managed>0</aui_managed> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <option>0</option> <flag>wxALL|wxEXPAND</flag> <border>5</border> <object class="wxTextCtrl" name="display"> <value></value> <maxlength>0</maxlength> </object> </object> <object class="sizeritem"> <option>1</option> <flag>wxEXPAND</flag> <border>5</border> <object class="wxGridSizer"> <rows>4</rows> <cols>4</cols> <vgap>5</vgap> <hgap>5</hgap> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button7"> <label>7</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button8"> <label>8</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button9"> <label>9</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button_ac"> <label>AC</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button4"> <label>4</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button5"> <label>5</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button6"> <label>6</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button_plus"> <label>+</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button1"> <label>1</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button2"> <label>2</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button3"> <label>3</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button_minus"> <label>-</label> <default>0</default> </object> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button0"> <label>0</label> <default>0</default> </object> </object> <object class="spacer"> <option>1</option> <flag>wxEXPAND</flag> <border>5</border> <size>0,0</size> </object> <object class="spacer"> <option>1</option> <flag>wxEXPAND</flag> <border>5</border> <size>0,0</size> </object> <object class="sizeritem"> <option>0</option> <flag>wxALL</flag> <border>5</border> <object class="wxButton" name="button_equal"> <label>=</label> <default>0</default> </object> </object> </object> </object> </object> </object> </resource>
import Graphics.UI.WX import Graphics.UI.WXCore import Data.IORef import Data.Char data State = EMPTY | REG1 | BRIGE | REG2 deriving (Eq,Show) data Calc = Calc{register1, register2 :: Integer, operator :: Maybe Char, state::State} deriving Show {- 状態 表示 入力 動作 次の状態 EMPTY REG1 数値 REG1に追加表示 REG1 演算子 何もしない EMPTY = 何もしない EMPTY REG1 REG1 数値 REG1に追加表示 REG1 演算子 演算子を登録 BRIGE = 何もしない REG1 BRIGE REG1 数値 REG2に登録表示 REG2 演算子 演算子を登録 BRIGE = 何もしない BRIGE REG2 REG2 数値 REG2に登録表示 REG2 演算子 結果をREG1へ、演算子を登録 BRIGE = 結果をREG1へ、BRIGE -} initCalc :: Calc initCalc = (Calc 0 0 Nothing EMPTY) chNumBase :: Int chNumBase = ord '0' charToInteger :: Char -> Integer charToInteger ch = fromIntegral $ (ord ch) - chNumBase isNumChar :: Char -> Bool isNumChar ch = ch >= '0' && ch <= '9' isOpChar :: Char -> Bool isOpChar ch = ch == '+' || ch == '-' isEqual :: Char -> Bool isEqual ch = ch == '=' main :: IO () main = start $ do calcState <- newIORef initCalc res <- xmlResourceCreateFromFile "wxcalc.xrc" wxXRC_USE_LOCALE form <- xmlResourceLoadFrame res objectNull "CalcFrame" display <- xmlResourceGetTextCtrl form "display" button0 <- xmlResourceGetButton form "button0" button1 <- xmlResourceGetButton form "button1" button2 <- xmlResourceGetButton form "button2" button3 <- xmlResourceGetButton form "button3" button4 <- xmlResourceGetButton form "button4" button5 <- xmlResourceGetButton form "button5" button6 <- xmlResourceGetButton form "button6" button7 <- xmlResourceGetButton form "button7" button8 <- xmlResourceGetButton form "button8" button9 <- xmlResourceGetButton form "button9" button_ac <- xmlResourceGetButton form "button_ac" button_plus <- xmlResourceGetButton form "button_plus" button_minus <- xmlResourceGetButton form "button_minus" button_equal <- xmlResourceGetButton form "button_equal" let numBtns = zip [button0,button1,button2, button3,button4, button5,button6,button7,button8,button9] ['0'..'9'] opBtns = [(button_plus,'+'),(button_minus,'-'),(button_equal,'=')] mapM_ (\(btn,ch) -> set btn [on command := pushButton display calcState ch]) (numBtns ++ opBtns) set button_ac [on command := pushACButton display calcState] windowShow form showText :: Textual w => w -> Calc -> IO () showText display (Calc reg1 reg2 op state) = case state of REG2 -> set display [text := show reg2] _ -> set display [text := show reg1] showAndWrite :: Textual w => w -> (IORef Calc) -> Calc -> IO () showAndWrite display ioState state = do showText display state writeIORef ioState state pushACButton :: Textual w => w -> (IORef Calc) -> IO () pushACButton display ioState = showAndWrite display ioState initCalc pushButton :: Textual w => w -> (IORef Calc) -> Char -> IO () pushButton display ioState ch = readIORef ioState >>= \state -> case state of (Calc r1 r2 op EMPTY) -> if isNumChar ch then showAndWrite display ioState (Calc (r1 * 10 + charToInteger ch) r2 op REG1) else showAndWrite display ioState (Calc r1 r2 op REG1) (Calc r1 r2 op REG1) -> let reg1Next | isNumChar ch = showAndWrite display ioState (Calc (r1 * 10 + charToInteger ch) r2 op REG1) | isOpChar ch = showAndWrite display ioState (Calc r1 r2 (Just ch) BRIGE) | otherwise = showAndWrite display ioState (Calc r1 r2 op REG1) in reg1Next (Calc r1 r2 op BRIGE) -> let brigeNext | isNumChar ch = showAndWrite display ioState (Calc r1 (r2 * 10 + charToInteger ch) op REG2) | isOpChar ch = showAndWrite display ioState (Calc r1 r2 (Just ch) BRIGE) | otherwise = showAndWrite display ioState (Calc r1 r2 op BRIGE) in brigeNext (Calc r1 r2 op REG2) -> let reg2Next | isNumChar ch = showAndWrite display ioState (Calc r1 (r2 * 10 + charToInteger ch) op REG2) | isOpChar ch = showAndWrite display ioState (Calc (doCalc op r1 r2) 0 (Just ch) BRIGE) | isEqual ch = showAndWrite display ioState (Calc (doCalc op r1 r2) 0 op BRIGE) in reg2Next doCalc :: Maybe Char -> Integer -> Integer -> Integer doCalc op reg1 reg2 | op == (Just '+') = reg1 + reg2 | op == (Just '-') = reg1 - reg2
- 【参考】
- wxRubyのこと wxRuby ですが、「Hello Worldをつくる」「Twitterクライアントをつくる」「双六ゲームをつくる」「時計をつくる」「3分でラーメンタイマーを作る」など wxFormBuilder を使ってGUI を設計しています。
- wxRubyでGUIプログラミング wxFormBuilder を使っての「電卓」、「落書きアプリ」。各種GUI部品一覧など。
- python入門から応用までの学習サイト Python-izm Pythonの学習サイトですがパーツの使い方が丁寧に説明されています。