PythonでTkinterを使ってGUIを開発する複数回連載の第1回目です。このシリーズの次のパートについては、以下のリンクをチェックしてください。
- TkinterによるPythonのGUI開発
- Tkinterを使ったPythonのGUI開発。パート2
- Tkinterを使ったPythonのGUI開発。パート3
この記事を読んでいるあなたは、シンプルなコマンドラインインターフェイスで操作するソフトウェアを高く評価している一人である可能性があります。素早く、システムのリソースに負担をかけず、そしておそらくあなたのようなキーボードの名人にとっては、より速く使用することができます。しかし、私たちのソフトウェアでより多くのユーザーにアプローチしたい場合、コマンドラインのソリューションだけを提供すると、潜在的なユーザーの多くが怖気づく可能性があることは周知の事実です。多くの人にとって、プログラムを操作する最も分かりやすい方法は、GUI(グラフィカル・ユーザー・インターフェース)を使用することです。
GUIを使用している間、ユーザーはウィジェットと呼ばれるインターフェイスの要素と対話し、操作することができます。ボタンやチェックボックスのようなウィジェットは、ユーザーとプログラムとの対話を可能にします。また、ウィンドウやフレームのように、他のウィジェットのコンテナとして機能するものもあります。
PythonでGUIを構築するためのパッケージはたくさんありますが、事実上の標準と考えられているパッケージは1つだけで、すべてのPythonのデフォルトインストールで配布されています。このパッケージはTkinterと呼ばれます。TkinterはオープンソースでクロスプラットフォームなGUIツールキットであるTkに対するPythonのバインディングです。
最初のウィンドウを作成する
先に述べたように、TkinterはPythonの標準的なインストールで利用できるので、オペレーティングシステムに関係なく、最初のウィンドウの作成はとても速くできるはずです。必要なのは3行のコードだけです。
import tkinter
root = tkinter.Tk()
root.mainloop()
出力
1行目で tkinter
パッケージをインポートした後、3行目でアプリケーションのメイン (ルート) ウィンドウウィジェットを作成しています。プログラムが正しく動作するためには、インターフェースの中にルートウィンドウウィジェットが1つだけ存在する必要があり、他のすべてのウィジェットはルートよりも低い階層にあるため、他のどのウィジェットよりも先に作成する必要があります。
5行目では、ルートの mainloop
を初期化しています。この行のおかげで、ウィンドウはイベント (ユーザーのインタラクションなど) を待ち、それに応じてインターフェイスを更新するループの中に残ります。ループは、ユーザーがウィンドウを閉じるか、quit()
メソッドが呼ばれたときに終了します。
ルートウィンドウにシンプルなウィジェットを追加する
次の例では、ウィンドウ以外のすべてのウィジェットに適用できる、ウィジェットを作成する一般的な2ステップの考え方を学びます。最初のステップは、特定のウィジェットのクラスのインスタンスを作成することです。2番目のステップでは、利用可能なメソッドの1つを使って、新しいウィジェットをすでに存在する別のウィジェット(親ウィジェット)の内部に配置する必要があります。Tkinterのインタフェースに配置できる最も単純なウィジェットはラベルで、これは単にテキストを表示するだけです。次の例では、単純なラベルウィジェットを作成しています。
import tkinter
root = tkinter.Tk()
simple_label = tkinter.Label(root, text="Easy, right?")
simple_label.pack()
root.mainloop()
出力
上のコードの5行目で Label
クラスのインスタンスを生成しています。最初の引数で、ラベルの親ウィジェットを指定します。この例では、ルートウィンドウを指定します。2番目の引数には、ラベルに表示させたいテキストを指定します。
そして、7行目では、ルート・ウィンドウの中でラベルの向きを変える方法を適用しています。Tkinterが提供するウィジェットの方向付けの最もシンプルな方法は pack()
です。ラベルはウィンドウの中にある唯一のウィジェットなので、単純にウィンドウの中央に表示されます。
この方法については、次の例で別のウィジェットをウィンドウに追加するときに、より詳しく説明します。ウィンドウのサイズは、中に置かれたウィジェットの大きさに合わせて自動的に調整されることに注意してください。
機能ボタンの追加
さて、ユーザーが操作できるものを追加してみましょう。一番わかりやすいのは、シンプルなボタンです。ウィンドウを閉じるためのボタンをウィンドウに追加してみましょう。
import tkinter
root = tkinter.Tk()
root.title("Hello!")
simple_label = tkinter.Label(root, text="Easy, right?")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)
simple_label.pack()
closing_button.pack()
root.mainloop()
Ouput
8行目では、ラベルを作成したのと同じように Button
クラスのインスタンスを作成しています。しかし、お分かりのように、ボタンがクリックされた後に何が起こるかをプログラムに伝えるために、コマンド引数を追加しています。この場合、root
のドラマチックな響きの destroy()
メソッドが呼び出され、実行されるとウィンドウが閉じられます。
10行目と11行目では、再び pack()
メソッドを使用しています。今回は、2つのウィジェットをウィンドウの中に配置するために使っているので、もう少し理解しやすいでしょう。ウィジェットを配置する順番によって、このメソッドはウィジェットを水平方向の中央に並べて配置します。ウィンドウの高さと幅は、ウィジェットのサイズに合わせます。
もうひとつ新しい行があることにお気づきでしょう。5行目では、ルートウィンドウのタイトルを指定しています。残念ながら、このインターフェイスで最も幅の広いウィジェットは、ウィンドウのタイトルが見えるようになるには十分な幅がありません。これをどうにかしましょう。
ウィンドウの大きさをコントロールする
ここでは、ウィンドウのサイズを簡単に変更するための3つの新しい行を見てみましょう。
import tkinter
root = tkinter.Tk()
root.title("Hello!")
root.resizable(width="false", height="false")
root.minsize(width=300, height=50)
root.maxsize(width=300, height=50)
simple_label = tkinter.Label(root, text="Easy, right?")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)
simple_label.pack()
closing_button.pack()
root.mainloop()
出力
7 行目では、プログラムのユーザーがウィンドウの幅と高さを変更できるようにするかどうかを定義しています。この場合、両方の引数は "false"
に設定されるので、ウィンドウのサイズは私たちのコードにのみ依存します。もし、9行目と10行目がなければ、ウィンドウの中に配置されたウィジェットのサイズに依存することになります。
しかし、この例では root の minsize
と maxsize
メソッドを使用して、ウィンドウの幅と高さの最大値と最小値を制御しています。ここでは、ウィンドウの幅と高さを正確に定義していますが、ウィジェットのサイズや、定義した最小値と最大値によってリサイズがどのように動作するか、この 3 行で遊んでみることをお勧めします。
Widget Orientationの詳細
既にお気づきかと思いますが、pack()
メソッドを使用しても、ウィジェットを親コンテナに格納した後の配置をあまりコントロールすることができません。pack()メソッドが予測可能でないというわけではありません。ただ、明らかに、ウィジェットを一列に並べてウィンドウに投入し、一つのウィジェットを前のウィジェットの上に配置することは、必ずしも洗練された美的感覚と一致しない場合があるということです。そのような場合には、いくつかの賢い引数を指定して
pack()を使用するか、コンテナ内のウィジェットを方向付ける別の方法である
grid()` を使用することができます。
まず、pack()
にもう一度チャンスを与えてみましょう。前の例の 15 行目と 16 行目を修正することで、インターフェイスを少し改善することができます。
simple_label.pack(fill="x")
closing_button.pack(fill="x")
出力
この簡単な方法で、pack()
メソッドにラベルとボタンを水平軸に沿ってずっと伸ばすように指示しています。また、pack()
がウィンドウ内に新しいウィジェットを投げる方法も変更できます。例えば、次のような引数を使用します。
simple_label.pack(side="left")
closing_button.pack(side="left")
出力します。
ウィンドウの左側から順に、同じ列のウィジェットをパックすることができます。しかし、ウィジェットを親ウィジェットの中に配置する方法は、pack()
だけではありません。最も美しい結果を得られるのは、おそらく grid()
メソッドでしょう。このメソッドでは、ウィジェットを行と列に並べることができます。次の例を見てください。
import tkinter
root = tkinter.Tk()
simple_label = tkinter.Label(root, text="Easy, right?")
another_label = tkinter.Label(root, text="More text")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)
another_button = tkinter.Button(root, text="Do nothing")
simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")
closing_button.grid(column=1, row=0, sticky="ew")
another_button.grid(column=1, row=1, sticky="ew")
root.mainloop()
出力
この例をもう少しわかりやすくするために、ルートウィンドウのタイトルとサイズを変更する行を削除しました。6 行目と 8 行目ではラベルとボタンを追加しています (ボタンをクリックしても何も起こらないことに注意してください)。
しかし、最も重要なことは、すべてのケースで pack()
が grid()
に置き換わっていることです。おそらく簡単に理解できると思いますが、引数 column
と row
によって、ウィジェットがグリッドのどのセルに配置されるかを定義することができます。2 つの異なるウィジェットに同じ座標を定義した場合、コード内で先にレンダリングされたものが、もう一方のウィジェットの上に表示されることを覚えておいてください。
sticky引数は、あまり知られていないかもしれません。このオプションを使用すると、ウィジェットの端をそれぞれのグリッドセルの端に貼り付けることができます。北(上)、南(下)、東(右)、西(左)の各セルの端です。そのためには、文字
n、
s、
e、
w` を含む単純な文字列を渡します。
この例では、4 つのウィジェットの縁をセルの東と西の縁に貼り付けるので、文字列は ew
となります。この結果、ウィジェットは水平方向に引き伸ばされます。これらの 4 文字の構成は、さまざまに変えて遊ぶことができます。文字列の中の順番は重要ではありません。
これで、ウィジェットの向きを変える 2 つの方法がわかったと思いますが、同じコンテナの中で grid()
と pack()
を混在させてはいけないということを覚えておいてください。
フレーム
他のウィジェットを含むことができるウィジェットは、ウィンドウだけではありません。複雑なインターフェイスを明確にするために、通常、ウィジェットをフレームに分離するのは良いアイデアです。
ここでは、4つのシンプルなウィジェットでそれを試してみましょう。
import tkinter
root = tkinter.Tk()
frame_labels = tkinter.Frame(root, borderwidth="2", relief="ridge")
frame_buttons = tkinter.Frame(root, borderwidth="2", relief="ridge")
simple_label = tkinter.Label(frame_labels, text="Easy, right?")
another_label = tkinter.Label(frame_labels, text="More text")
closing_button = tkinter.Button(frame_buttons, text="Close window", command=root.destroy)
another_button = tkinter.Button(frame_buttons, text="Do nothing")
frame_labels.grid(column=0, row=0, sticky="ns")
frame_buttons.grid(column=1, row=0)
simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")
closing_button.pack(fill="x")
another_button.pack(fill="x")
root.mainloop()
出力
上記の例を注意深く見てみましょう。5 行目と 6 行目では、2 つの新しい Frame
ウィジェットを定義しています。もちろん、最初の引数では、その親ウィジェットであるルートウィンドウを指定しています。
デフォルトでは、フレームの境界線は見えませんが、フレームがどこに配置されているかを確認したいとします。枠線を表示するには、一定の幅(この例では2ピクセル)と、枠線が描かれる relief
というスタイル(3D効果の一種)を指定する必要があります。レリーフのスタイルは5種類から選ぶことができます。この例では ridge
を使用しています。
ラベルとボタン
の定義も少し変更しました (8-12行目)。ラベルは frame_labels
フレーム内に、ボタンは frame_buttons
フレーム内に配置したいと思います。そのため、以前の親である root
をそれぞれの新しいフレームの親に置き換える必要がありました。
14行目と15行目では、grid()
メソッドを使用してルートウィンドウ内のフレームを配置しています。次に、grid()
メソッドを使ってラベルの向きを変え (17-18行目)、pack()
メソッドを使ってボタンの向きを変えています (20-21行目)。ラベルとボタンは別々のコンテナに入っているので、別のメソッドでウィジェットの向きを変えることはできません。
トップレベルウィンドウ
しかし、ルートウィンドウの子ウィンドウをたくさん作成することができます。そのための最良の方法は Toplevel
クラスを使用することです。
import tkinter
root = tkinter.Tk()
new_window = tkinter.Toplevel()
new_window.withdraw()
frame_labels = tkinter.Frame(root, borderwidth="2", relief="ridge")
frame_buttons = tkinter.Frame(root, borderwidth="2", relief="ridge")
simple_label = tkinter.Label(frame_labels, text="Easy, right?")
another_label = tkinter.Label(frame_labels, text="More text")
closing_button = tkinter.Button(frame_buttons, text="Close window", command=root.destroy)
window_button = tkinter.Button(frame_buttons, text="Show new window", command=new_window.deiconify)
frame_labels.grid(column=0, row=0, sticky="ns")
frame_buttons.grid(column=1, row=0)
simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")
closing_button.pack(fill="x")
window_button.pack(fill="x")
root.mainloop()
上の例では、5行目で新しいウィンドウを作成しています。ウィンドウは他のウィジェットの内部に固定されていないエンティティなので、その親を指す必要も、親ウィジェットの内部に配置する必要もありません。
ボタンが押された後に、新しいウィンドウを表示させたいと思います。5行目ですぐに表示されるので、6行目で withdraw()
メソッドを使用して非表示にしています。次に、15行目でボタンの定義を変更します。
新しい変数名とテキストに加えて、このボタンは new_window
オブジェクトのメソッドである deiconify
というコマンドを実行するようになり、ユーザーが window_button
ボタンをクリックした後にウィンドウを再表示させることができるようになりました。
Python GUI。AからZまで
PythonでGUIを作成する方法と、Tkinterのような利用可能なツールについてもっと学びたいですか?アプリのGUIを素早く構築する方法をステップバイステップで学べるトレーニングコースを受講してみてください!
結論
お分かりのように、Tkinterを使用すると、ソフトウェアの専門家ではないユーザ向けのGUIを簡単かつ迅速に作成することができます。このライブラリはすべてのPythonのインストールに含まれているので、最初の簡単なウィンドウを作るのはほんの数行のコードで済みます。上に示した例は、このパッケージの機能の表面をわずかに削ったものです。
このチュートリアルの第2部では、複雑で直感的で美しいグラフィカルユーザインターフェースを作成する方法について説明しますので、引き続きご覧ください。
$(document).ready(function() {)
$(‘pre code’).each(function(i, block) {)
hljs.lineNumbersBlock(ブロック);
});
});