HaskellでTwitterクライアント開発blog(仮)

今すぐに挫折するかもしれない程度のモチベーションによるTwitterクライアント開発記

gtk2hs+GladeでGUI対応:タイムライン表示+ツイート

前回GLUTを使いましたが、やっぱりこのままこれでGUI開発を続けるのは不可能な気がしたので、
なんとかしてgtk2hsをインストールしました。

インストール時に出るエラーが"gtk2hsC2hsのバージョンが違います"だったので、cabalをアップデートしてからもう一度gtk2hs-buildtoolsをインストールしてみたのですが、うまく行かない。
なんでだ?と思ってよく読むと、gtk2hsC2hsのファイルがあるのは~/.cabal/binのはずなのに、"/usr/local/binにあるgtk2hsC2hsのバージョンが……"と怒られている。
どうやら、~/.cabal/bin以外にも/usr/local/binにgtk2hsC2hsがあったらしく、そのファイルはcabalでアップデートされていなかったと。
そこで、/usr/local/binにあるファイルを~/.cabal/binにあるものに代えたらインストールに成功しました。

で、まぁ苦労しましたがこれでめでたく使えるようになったんで、Gladeも入れてシンプルなGUIを作ってみました。
gtk2hsとGladeを対応させる方法はReal World Haskellに方法+コードが載っていたので、基本的な部分はそれの丸写しです。
Gladeで用意したhshstterGUI.gladeと、HshstterLocalMain.hs、HshstterMain.hsを用意して以下のようにGladeと結合。
GUIにはツイート書込欄・ツイートボタン・タイムライン表示領域の3つをセット。

HshstterLocalMain.hs

HshstterMain.hs

これでOK、コンパイル→実行……しようとしても読み込まれない。
調べてみると、同じようなエラーに悩まされている人のブログ記事を発見。

http://yasutech.blogspot.jp/2011/12/gtk2hs-glade.html

どうやら、Glade3.10系のXMLの形式にgtk2hsがまだ対応してない模様。
そこで、出力されたXMLファイルを3.8版っぽく書きなおした所、なんとか読み込んでくれました。
以下のようなところを書き換えました。

・<interface>タグを<glade-interface>タグに書き換え

・<requires ……>タグを消去

・<object>〜</object>タグを<widget>〜</widget>タグに置き換え

こんな機械的な書き換えで通ったので、スクリプト書いて置き換えたほうがよさそうですね……

ともあれこれでウインドウが表示されたので、あとはイベントを登録するだけなのですが、
ここはかなり直感的に書くことができて、使いやすかったです。

 ・タイムラインを表示

この部分の実装は、ひとまず書き込み不可なテキストエリアに表示する、という実装にしました。
(これだと画像の表示ができない気がするので、あくまで暫定的に。)

TextView Widgetにテキストを表示させるには、

TextBufferを取得→TextBufferにテキストをセット

という段階を踏めばとりあえず表示できます。

 TextBufferの取得は

Graphics.UI.Gtk.Multiline.TextView.textViewGetBuffer :: TextViewClass self => self -> IO TextBuffer

TextBufferへのテキストのセットは

Graphics.UI.Gtk.Multiline.TextBuffer.textBufferSetText :: TextBufferClass self => self -> String -> IO ()

 

で行えます。

・タイムラインの更新

上のshowTimeline関数はIO Bool型にしていますが、これは次の関数の引数にするためでした。

 

Graphics.UI.Gtk.General.General.timeoutAdd :: IO Bool -> Int -> IO HandlerId

 

第二引数に指定した秒数ごとに、第一引数に指定したアクションを実行します。
Bool型の引数がFalseになるとそこでタイマ繰り返しが終了し、Trueが返される限りインターバルごとに実行され続けます。

ここではshowTimelineはTrueをreturnし続けるので、更新され続けます。

・入力欄にツイート内容を書き込み、tweetボタンでツイートする

ツイート入力欄はEntry Widget、ボタンはButton Widgetを使用。

Entry Widgetの内容の取得は

Graphics.UI.Gtk.Entry.Entry.entryGetText :: EntryClass self => self -> IO String

 逆にEntry Widgetのセットは

Graphics.UI.Gtk.Entry.Entry.entrySetText :: EntryClass self => self -> String -> IO ()

で行えます。

ボタンがクリックされた時のアクションの設定は

Graphics.UI.Gtk.Buttons.Button.onClicked :: ButtonClass b => b -> IO () -> IO (ConnectId b)

を使い、第一引数にButton Widgetを、第一引数にクリック時のアクションを指定します。

・xボタンでプロセスを終了させる

実はこのままだと、デフォルトではxボタンを押してもプロセスは死なず、ウインドウだけ消えます。
そこで、xボタンをクリックした時のアクション

Graphics.UI.Gtk.Abstract.Widget.onDestroy :: WidgetClass w => w -> IO () -> IO (ConnectId w)

 

で指定して、プロセス終了関数

Graphics.UI.Gtk.General.General.mainQuit :: IO ()

 

を呼ぶ必要があります。

 

とりあえず、これでクライアントとして最低限の機能は実装できました。
エラー処理やらいろいろなところが雑ですが……
ツイートもByteStringで処理するべきでしょう。