数年前、Javaが登場した際に大いに期待されたことの1つは、「ライト・ワンス・ラン・エニーウエア(一度作ればどこでも実行可能)」という概念でした。当初、Javaのユーザー・インターフェースは主に、Webブラウザーに組み込まれるアプレットと考えられていました。その後しばらくして、独立したAWTアプリケーションという考えが一般的になりました。その後、AWTがSwingに概ね取って代わられ、SwingはBeansへと進化しました(Swingをベースとして、要件が追加されました)。その過程で、Swingのクラスにはいろいろと手が加えられ、Javaのバージョンアップごとに追加と削除が繰り返されました。
Javaには、「ライト・ワンス・デバッグ・エブリウエア(一度作れば、至る所でデバッグが必要)というよく知られたジョークがあります。少なくとも、Javaアプリケーションを作成して、そのアプリケーションがすべてのユーザーのマシンで動作すると自信を持って言える人がいないことは確かです。もちろん、すべてのユーザーに対して、Javaのバージョンと構成をその特定のアプリケーションに一致させるという大変な作業をするのであれば話は別ですが…。アプリケーションがうまく動作するかどうかは、Javaのバージョンと、さらにはインストールされているJava仮想マシン(JVM)のベンダーやプラットフォームによっても結果が異なってくるのです。
移植性に関してはほとんどの点で、PythonやPerl、Tclといったスクリプト言語の方がJavaよりも優れています。例えば、Pythonスクリプトであれば、ほとんどの場合、スクリプトは、複数のユーザーのそれぞれのマシンで正しく同じように動作するでしょう(バージョン要件はありますが、Javaよりもはるかに簡単で信頼できます)。もちろん、このように移植性に問題のあるJavaであっても、静的な型付け(必要な場合)、巨大なクラス・ライブラリー、優れたドキュメンテーション、入念な設計の選択といった長所があります。しかし、ここではそのような特長は重要ではありません。
Pythonスクリプトの移植性がJavaアプリケーションの移植性にはるかに及ばない分野の1つがユーザー・インターフェースです。コマンド・ライン・ツールの場合は問題ありませんが、ユーザーとの複雑な対話が必要な場合、特にグラフィカル・インターフェースとなると、実際Pythonにはまったく利用できるものがありません。Javaには技術的な問題点が多少あるものの、JVMが実装されたすべてのプラットフォームは通常、SwingやAWTの基礎を備えています。それに対して、Pythonには「標準」のGUIライブラリーが装備されていないのです。
標準のPython GUIを望む声は、以前から多くありました。標準にやや近いものとしてはTkinterがあります。TkinterのWindowsおよびUNIX/X Window System版は安定しており、MacOS版もまずまずの出来です。しかし、TClとTKをシステムにインストールする必要がある上、BeOSやOS/2のような「マイナーな」プラットフォームはサポートされていません。様々な支持者の間では、何か他のライブラリー/バインディングを選んだ方が良いという意見も聞かれます(選択肢については参考文献を参照)。しかし、いずれの選択肢も期待されるプラットフォームの一部しかサポートしていません。そして最も重要なことは、どれも広く支持されるには至っていないということです(Pythonに標準で付属するものがないのはそのためです)。現状では、ユーザー・インターフェースを使ってアプリケーションを作成する手段も、また実際の「ユーザー」のための確実なインターフェースもありません。
Javaの哲学は、すべてのJVMに実装すべき一連の標準機能を開発することであり、必然的にJavaにはGUIが存在します。Pythonのアプローチは、これとは方向性が異なるものなのかもしれません。Pythonの場合、特定のAPIに従うことをすべてのマシンに対して求めるのではなく、マシンに「できる」ことをまず特定し、そこから取り組みを開始します。APIは、基本となるプラットフォームが実行できることに対するラッパーとして存在しているのです。
anyguiを使うことにより、Pythonの取り組み方を理解している人にとっては、まさに期待通りのことを行うことができます。anydbmモジュールは、実行時に最適な利用可能のバックエンドのデータベースを探し出しますが、その名前とやり方の両方を引き継いで、anyguiは最適の利用可能なGUIバックエンドをanyguiによるアプリケーションが実行されている環境から見つけ出します。anyguiは、すべてのバックエンドと連動する「有効な」インターフェース・エレメントの提供に重点が置かれています。バックエンドそのものの方がより高度なインターフェース機能を備えている場合も考えられますが、anyguiは、あらゆるものに対する共通性にその重点を置いています。
この記事の執筆時点では、anyguiはまだ α レベルのプロジェクトです。しかし、対象となるバックエンドの一部に関しては、すでにきわめて順調に機能しています。ただし、目標はあくまでも(ほぼ)汎用的なラッパーであり、一部に関する成功だけでは当然、十分とは言えません。最終的に、anyguiがこの目標をクリアすれば、anyguiが標準PythonパッケージとしてすべてのPythonディストリビューションに含まれるようになることも十分に考えられます(anydbmやxml.saxがシステム依存のバックエンドでもあるにかかわらず含まれているように)。最終的には、すべてのユーザーに対して提供し、利用できるようにするということが重要な点でしょう。ちなみに、anyguiは純粋なPythonであり、anygui自体はC/C++やその他の低水準言語の要素を一切必要としません(有用性を高めるには、当然、何らかのGUIライブラリーによるサポートが必要ですが)。
この記事のために、実用的なバックエンドのほとんどにざっと目を通してみました。まだ実装されていないものや、部分的にしか機能しないものもありますが、実用的なものとしてはTkinter、Java Swing、win32all、PyGTK、wxPythonが挙げられます。BeOSのネイティブ(Bethon)は、多少使えると言った程度に過ぎませんが、弛まぬ開発努力によって新しいビルドでは改善が見られるかもしれません。PyQTおよびMacOSの場合、ネイティブのものが計画されており、開発の有力候補が挙がっていますが、それらのラッパーの実装はまだ開発されていません。もちろん、このような状況は常に変化する可能性があります。また、ダイレクトxlibバックエンドも話題として挙げられるようになっていますが、現時点ではそれを率先して手掛けようとする者は特に見られません。
前述のグラフィカル・ツールキットはすべて、きわめて似通った方法で動作します(あるいはその予定)。正直なところ、私はバックエンド・ツールキットについてそれほど詳しくありませんが、私が見た限りではanygui APIは概ねTkinterに似ています。基本的には、コールバックを持つウィジェット群を開発し、それからメイン・イベント・ループに入ります。
その他に、将来「通常の」GUIツールキットの型を破る可能性のあるバックエンドもいくつかあります。ある意味では、これらは最も興味深い、あるいは少なくとも斬新なもののように思われます。その中のあるバックエンドに関しては、実は私が率先してその開発を進めなければならないのですが、まだ初期バージョンの開発に着手できない状態が続いています。この記事が掲載される頃までには、何とかしたいのですが…。私の担当の、このちょっとニッチなバックエンドはncursesです。これが完成すれば、SSH/telnetセッションのようなテキスト・モード端末(またはXWindow Systemがない簡単なUNIXボックス)でも、anyguiアプリケーションを実行することができるようになります。
anyguiのプロジェクト・リーダーのMagnus Lie Hetland氏は、cursesバックエンドに似た方法で、単純なライン指向のフォールバック・インターフェースを提案しています。これはおそらくreadlinesサポートを用いたものでしょう。このシナリオでは、メニューはプロンプトとして簡単化され、オプション選択、返答や結果がその後に続いて行われる、というようになるでしょう。anygui.backends.textgui(と仮定する)の動作に必要なのはSTDINとSTDOUTのみであるはずです。これらは、そのプログラムがそのままイベント・ドリブンの高度なWIMPグラフィカル・インターフェース(ウィンドウ、アイコン、マウス・ポインター)の環境で動作するときに興味対象となる最少のものです。もちろん、現段階では、これは1つのアイデアに過ぎません。
その他にも、同じように面白い型破りなアイデアがあります。(ほとんど)誰もがWebブラウザーを、それがたとえlynxであろうとlinksであろうと所有しています。Pythonの標準webbrowserモジュールを利用すれば、anyguiなどと同様の方法で「最高の」Webブラウザーを柔軟に立ち上げることが可能となります。何らかのLOCALHOSTサーバーとブラウザーを通信できるようにすれば、必要とされる基本インターフェース・デバイスをWebブラウザーですべて直接利用することができます(ボタン、入力フィールド、テキスト領域、グラフィックスなど)。このバックエンドも現在、計画段階にあります。
「百聞は一見に如かず」と言います(少なくとも時には)。この記事は、活字だらけの学術文献として掲載される予定にはなっていないので、活字の代わりにいくつかのスクリーンショットを見てみましょう。例として、ここでは、ボタンで非アクティブをアクティブにする簡単なアプリケーションを使用します(後にソースも示されています)。1組のテキスト・ラベルもあります。この他にも、anyguiディストリビューションのtestディレクトリーにウィジェットの例があります。
まず、「デフォルト中のデフォルト」バックエンドとされているTkinterについて見てみましょう。このバージョンは、形も動作も十分であるように見えます。しかし、win.destroy()呼び出しには若干問題があります。ウィンドウを直ちに消す(そして、アプリケーションを閉じる)のではなく、(ウィンドウを移動するなどして)よく見てみると、ウィンドウが一旦は消えてもゴーストとして残っているのが分かります。前述の通り、これはまだα レベルです。以下は、Win98で実行した場合の例です。
Tkinterで実行したButtonアプリケーション(Win98の場合)
Windowsで実行する場合、win32allモジュールによってWindowsのネイティブ・コールを使うこともできます。このモジュールは、ActiveStateからのActivePythonディストリビューションには標準で含まれていますが、それ以外のディストリビューションの場合は別途入手する必要があります(ActiveStateから)。全体として、このバインディングの振る舞いは私が見た中では最も優れています。ただし、私がテストしたバージョンに限っての話です。ラベルの配置はTkinterの場合とは少し異なっており、バックエンド間で全く同じ表示結果が得られるとは限らないことが分かります。
Win32で実行したButtonアプリケーション(Win98の場合)
次に、全く違うプラットフォームで試してみました。OS/2 Warp 4でJythonを実行したところ、以下に示すような結果が得られました。なぜか、テキスト・ラベルの先頭に必要のない<html>が付いています。しかし、若干の不具合を除けば、これほど異なったプラットフォームで同じコードを実行できるということは素晴らしいことであると言えるでしょう。
Java Swingで実行したButtonアプリケーション(OS/2 Warp 4の場合)
次にLinuxプラットフォームに移り、PyGTKがインストールされたシステムで同じアプリケーションを実行しました。面白半分に、数種類のウィンドウ・マネージャー環境で実行してみました。まず、Enlightenmentの結果です。
GTKで実行したButtonアプリケーション(Enlightenmentの場合)
次に、WindowMakerで試してみました。
GTKで実行したButtonアプリケーション(WindowMakerの場合)
ウィンドウ・マネージャーが異なっていてもウィンドウ・フレーム内は全く同じですが、ラベルの位置調整とサイジングはWindowsの場合とは若干異なります(単語が切れないようにするにはあと数ピクセル必要です)。wxPythonは、私のシステムには簡単にインストールできなかったため諦めましたが、結果は同様のはずです。
BeOSのバックエンドは、現時点では他のもののようには洗練されていません。この簡単なアプリケーションもうまく動作しません。しかし、基本的なWindowクラスの動作に関しては問題ありません。
BeOS r5でのウィンドウ・テスト・アプリケーション
検証したすべてのプラットフォームで実行したコードはきわめてシンプルなものです。この例では、プログラムの前半部分は、使用するバックエンドの選択をコマンド・ラインから手動で行えるようにする単なるスイッチです。本番用のコードには適していないと思いますが、初期テストには役立ちます。なお、スクリーンショットを示したテストはすべて、コマンド・ライン・オプションを付けずに単純に実行したもので、バックエンドの選択は自動で行われました。では、コードを見てみましょう。
[anygui] 用の「button.py」テキスト・アプリケーション
import sys
if len(sys.argv)==1
or sys.argv[1].upper()=='DEFAULT':
from anygui import Window, Button, Application, Label
elif sys.argv[1].upper()=='TK':
from anygui.backends.tkgui import Window, Button, Application, Label
elif sys.argv[1].upper()=='MSW':
from anygui.backends.tkgui import Window, Button, Application, Label
elif sys.argv[1].upper()=='BEOS':
from anygui.backends.beosgui import Window, Button, Application, Label
elif sys.argv[1].upper()=='GTK':
from anygui.backends.gtkgui import Window, Button, Application, Label
elif sys.argv[1].upper()=='JAVA':
from anygui.backends.javagui import Window, Button, Application, Label
elif sys.argv[1].upper()=='WX':
from anygui.backends.wxgui import Window, Button, Application, Label
def say_hello():
global bye
print
"Hello, world!"
bye._set_enabled(1)
app = Application()
win = Window(width=150, height=150, title="Beatles Lyric")
win.add(Label(x=10, y=10, width=140, text = "I don't know why you say..."))
bye = Button(x=30, y=40, width=70, height=30, text="Goodbye",
action=lambda: win.destroy(), enabled=0)
win.add(bye)
win.add(Label(x=10, y=70, width=120, height=32, text = "When I say..."))
hi = Button(x=30, y=100, width=70, height=30, text="Hello", action=say_hello)
win.add(hi)
win.show()
app.run() |
アプリケーションの構成は、(1) アプリケーションを作成する (2) 1つまたは複数のウィンドウを作成する (3)ウィンドウにウィジェットを追加する (4)app.run()イベント・ループを呼び出す という4つのステップのみです。ウィジェット・オプションはすべて、名前付きパラメーターとして渡されます。
anyguiは、まだ α レベルですが、すでに「データを取得し、処理し、結果を表示する」基本的なアプリケーションの作成に要するものはすべて備えています。ディスカッション・リストには、さらに細かいイベント処理や表示モデルなどに関する興味深い話題が数多く寄せられています。また、anygui APIはまだ正式には文書化されていませんが、anyguiはおそらく、最近私が見たどのPythonライブラリーよりも興味深いものです。プラットフォームの仕様に合わせたコード行の変更が全く必要なく、Pythonが動作するあらゆる環境で高度なユーザー・インターフェースを簡単に使うことができれば、信じられないほど便利になるはずです。
-
anyguiに関して調べるのであれば、まずSourceForgeページから始めることをお勧めします。このページには、開発者向けメーリング・リストやドキュメンテーションがあり、最新版のダウンロードも可能です。 - developerWorksに掲載されているDavid Mertz氏の関連記事「魅力的なPython: PythonでのTKプログラミング」と「魅力的なPython: cursesプログラミング」もご覧ください。
- developerWorksに掲載されているその他のLinux参考文献もご覧ください。
- developerWorksに掲載されているその他のOpen source参考文献もご覧ください。
David Mertz氏は今、シンクリティズム(混合主義)に没頭しています。優れたインタラクティブなマルチメディアライブラリ以上に彼に活力を与えるのは、パンチカードとバッチ処理システムの復活への想いだけです。彼のメール・アドレスはmertz@gnosis.cxです。彼の活動についてはhttp://gnosis.cx/publish/で詳しく見ることができます。これまでのコラム、今後のコラムに関するご意見、ご要望がありましたら、ぜひお知らせください。