プログラマブルなコマンドライン ランチャー CraftLaunch 3 が楽しい

自分はキーボード派なので、コマンドライン ランチャーを愛用している。歴史的には Moonlight → CraftLauch 2 という順に推移してきたのだけど、端末が変わったのをきっかけに CraftLaunch のサイトを見てみたら、いつのまにかメジャーバージョンアップして CraftLaunch 3 ができていた。

この CraftLaunch 3 がなかなかプログラマ魂を刺激するもので、「設定ファイルは config.py」というアグレッシブなものになっている。ソースも公開されていて、コードのほとんど全てが Python で実装されている。ソースを参照しつつ config.py に拡張機能を書くことで、いろいろとカスタマイズできる。

スクリプト言語でソフトウェアを作るということ

ちょっと前に、「Lightroom は 40% のコードが Lua で、プラグインも Lua で書く」という話を紹介したけど、CraftLaunch 3 はこれの Python 版。

どちらも、アプリケーションのコア部分をネイティブ言語で API として実装して、残りの本体をスクリプト言語で実装している。この方法のメリットは、コアとそれ以外が分離されるし、コアが提供する API を使って本体を開発するわけだから、自然と利用者に見せる「カスタマイズ可能な部分」の API も洗練されてくる。

カスタマイズ可能なソフトウェアを作るときには参考になるプログラミングモデルだと思った。

せっかくなのでカスタマイズ

前置きが長くなったけど、さっそく自作の拡張を書いてみた。

同じアプリケーションを何個も立ち上げていたとき、同じアプリケーションのウインドウをまとめて最小化したり閉じたりしたくなることがある。

そんな要望に対応するために、指定した exe のウインドウを「最前面・最大化・最小化・元のサイズに戻す・閉じる」コマンドを作ってみた。

ソースコードは https://github.com/nitoyon/clnch3_extensions/blob/master/window_command.py にある。

インストール方法

  1. CraftLaunch のインストールフォルダの extension フォルダに window_command.py を置く
  2. window_command.py 中の「定義例」のソースを config.py に書く。
  3. CraftLaunch を再起動するか、Reload を実行する。

ざっと使い方

  • foreground;notepad と入力すれば、メモ帳を最前面にできる。
  • minimize;notepad と入力すれば、メモ帳を全て最小化できる。
  • close;notepad の代わりに notepad と入力して Ctrl + [-] でもよい。

対象の実行ファイルは実行ファイル名、もしくは、登録しているコマンドで指定することができる。

内部的なこと

ウインドウ操作の部分は CraftLaunch 3 に同梱されている同じ作者が作った Python でウインドウを扱うライブラリ、pyauto を利用している。Win32 API を知っていれば素直に扱えた。

今回作ったコマンドでは、登録済みのコマンドを取得するために loadCommandListFromIniFile という関数を定義している。この実装は CraftLaunch 本体のclnch_mainwindow.MainWindow.loadCommandFromIniFile() 関数の真似をして実装している。そのまま流用できればよかったんだけれども、clnch_mainwindow.MainWindow.loadCommandFromIniFile()関数が破壊的だったので流用が難しかったのが残念だ。ぜひとも、設定ファイルから一覧を取得するメソッドとして実装しなおしてほしいと思った。こんな風に…。

@@ -850,7 +850,7 @@
             ( u"Quit",      self.command_Quit ),
         ]

-        self.loadCommandFromIniFile()
+        self.launcher.command_list += self.loadCommandFromIniFile()

         try:
             fd = file( self.config_filename, 'r' )
@@ -899,6 +899,7 @@
     def loadCommandFromIniFile(self):

         i=0
+        ret = []
         while True:
             try:
                 command_string = unicode( clnch_ini.get( "COMMANDLIST", "comman
d_%d"%(i,) ), "utf8" )
@@ -909,9 +910,10 @@
             command_name, command_args = command_tuple[0], command_tuple[1:]

             command = self.command_ShellExecute( None, *command_args )
-            self.launcher.command_list.append( ( command_name, command ) )
+            ret.append( ( command_name, command ) )

             i+=1
+        return ret

まとめ

普段つかうソフトウェアが拡張可能になってると楽しい。Windows 文化では結構珍しいことだからなおさら楽しい。