mikutter:おっさんだってサインコサインを学びたい!(学んだやん)
はじめに
なんか巷では三角関数が空前の大ブームと言うことで、デスマから解放されて無駄にテンションの高い僕もノッてみることにしました!!
冷静に考えると、もぐのはおっさんであり社会に出てから三角関数なんてとんと使ってない勢であるわけで、テメェが手を動かしたところで元ネタに擦りもしてませんね。アレ。
「くくく・・・計画ミス」
取り敢えず三角関数で作れるプログラムと言ったらアナログ時計ですよね!
(異論はrescueして揉み消します。)
アナログ時計の針の動きは円軌道なので、極座標(原点からの距離と偏角で平面上の点を特定するモデル。偏角を変化させていくと円が描けるよ。)との親和性が高いです。その極座標をcairoの座標系である直交座標に変換する際に、件の三角関数を用います。
距離r、偏角θの点を直行座標x, yに変換するには、
x = r * cos(θ)
y = r * sin(θ)
ですね。
てな訳でアナログ時計画像を作るプログラムが誕生したので、現在時刻を吐き出すmikutter-datasource-clockのアイコン画像にして終わりにしようとしたのですが、ここで問題が。
mikutter内での画像の指定方法はファイル名またはURLのみで、cairoで作成したPixbufを指定することはできません。どうしてもと言うならPixbufをテンポラリディレクトリにpngとかで保存すればいいですが、まぁ格好悪いなと。
ならばモンキーパッチとなるのですが、アイコン画像を差し替えるだけじゃつまんないなと思って、前からやってみたかった大技にチャレンジすることにしました。
ミクぺたをペタペタする
mikutterのTLの描画はGdk::MiraclePainter(以降「ミクぺた」)と言うクラスで行われており、Messageごとにそのインスタンスを保持しています。
と言うことは、オブジェクト志向的発想でミクぺたのサブクラスを作ってやれば、TLに独自レイアウトの描画が出来るはずです。
出来ました。割と簡単。
まずはGdk::MiraclePainterを継承してrender_to_context()をオーバーライドします。
引数のcairocontextにあなたのパトスをぶつけましょう。
次にMessageにミクぺたのインスタンスをアサインしているGtk::CellRendererMessageのcreate_miracle_painter()をモンキーパッチして、適宜さっきのサブクラスのインスタンスを返せばOKです。
んで、出来たのがこれ
でっかいみくったーちゃん時計がホームTLの上部に居座ります。割と邪魔です。
しかしながら、mikutterは時計すら無いヤバい環境で動かされることもしばしばなので、一周回って案外便利かもしれません。
mikutterパッケージマネージャ"Packaged"でもインストールが可能です。こちらもよろしくです。moguno.hatenablog.jp
マイクロソフト?ワイヤレス Bluetooth キーボード Wedge Mobile Keyboard U6R-00022
- 出版社/メーカー: マイクロソフト
- 発売日: 2012/09/07
- メディア: Personal Computers
- クリック: 6回
- この商品を含むブログ (8件) を見る
mikutterにおけるスレッドと並行処理機構のまとめ
mikutterの薄い本Vol.8に掲載したmikutter-datasource-aclogを入れてから、なんかたまにGUIが重たくなる気がして来ました。
原因を調べたところ、mikutterイベントon_period内のhttp通信処理がたまに重くなるのせいで、メインスレッドのGUIの動作を阻害していたためでした。
てっきり別スレッドで動いてると思ってたよ。あらあらうふふ。
いい機会なので、mikutterに於けるスレッドの取り扱いと、mikutterフレームワークの並列機構との関係を整理してみようと思いました。
mikutterに於けるスレッド
mikutterのスレッドは「メインスレッド」と「サブスレッド」に大別されます。
特徴はそれぞれこんな感じです。
mikutterフレームワークの並行処理機構
さて、各スレッドの得手不得手が整理できた所で、次はmikutterで使える並行処理について確認していきましょう。
Delayer
与えたブロック(処理)をメインスレッドで実行します。
DelayerはGTKのメインループと協調を取っており、GUI処理がひと段落するタイミングまで実行が保留される所がちょっとだけ注意です。
(/core/plugin/gtk/delayer.rbのGtk::idle_add_priority()で暇になった時にDelayerを実行する処理を設定しています。)
この性質を利用して、擬似的にサブスレッドからGUIの操作が行えるようになります。
Thread.new { # サブスレッドでなんか重い処理 Delayer.new { # メインスレッドでGUI操作 } }
Reserver
Reserverは引数に指定した時間待った後にブロックを実行する機構です。
Reserverは専用のスレッドで時間待ちをしているため、Reserver処理もサブスレッドでの実行されます。
つまり、GUIをいじる場合はDelayerを使用する必要が有ります。
# 30秒後に処理を実行する Reserver.new(30) { # ここはサブスレッドで実行される }
イベント
mikutterイベントは内部でDelayerが噛むので、必ずメインスレッドで実行されます。
データを定期的に取得するのに便利なon_period(1分周期で発生)も例外ではなく、そこに冒頭のhttp処理なんかを書くと「もっさり」の原因となります。
(冒頭のやらかしですね。)
サブスレッドを作ってさっさと明け渡しましょう。
# 1分周期イベント on_period { |service| # ここはメインスレッドなので、速やかに明け渡しましょう。 }
フィルタ
イベントと似た構文を持つフィルタは呼び出し元に値を返す必要があるため、処理が実行されるスレッドは呼び出し元と同じ(メイン、サブ特定できない)になります。
まぁ、フィルタ処理でスレッドやDelayerによる並行処理は原理上無意味なので、困ることはないと思います。
しかしながら、フィルタ内で重い処理を行うと後続の何らかの処理が遅れるはずなので、重い処理を伴うデータはReserverやon_periodなどで予め非同期に作っておくのが良いと思います。
on_period { |service| Thread { # 重い処理は予め実行しておく @data = heavy_proc() } } filter_hoge { |args| # フィルタでは@dataを使って答えを返すだけ(排他とかちゃんとしてね) [args] }