質問: ### Python3でGUIをtkinterで後付したい。 python3で画像のヒストグラム調整をしたりするプログラムを書きました。 CUIでファイルの場所を入力すると自動で調整された画像が出力されるように作成しました。 このプログラムにGUIを足したいと思いTkinterを用いたのですが、うまくいきません。 TkinterでGUIを作成して、ファイルの場所を入力する場所を作り、ボタンを押すとファイルの場所を取得するところまではうまくいったのですが、ウィンドウを閉じないと得られたファイルの値を調整プログラムに渡せません。 このように、すでにCUIで処理を作成して、ここに必要な引数を入力するために後付で入力用のインターフェイスとしてGUIを作成する際に最も良い方法をご存知でしたら教えていただけると幸いです。
回答: ヒストグラム調整プログラムというのはこんな雰囲気に作成されたのだと思います。 ・(A)初期化など ・(B)filename = input('enter filename: ') <==入力 ・(C)画像読み込み/ヒストグラム調整等の計算 ・(D)画像を表示 ==> 出力 上記のようにCUIアプリは「メインプログラムが実行する順序を自ら制御して順に必要な処理を行う」ように作りますが、GUIプログラムの場合、プログラムの制御は「画面を操作する利用者が握る」構造になっています。例えばTkinterを使うと ``` ... root = tk.Tk() 画面の初期化 root.mainloop() # この関数がGUIを表示している間全ての制御を司る # (プログラマーが自由に処理順を決められない。全てmainloop()が支配する) <-- ユーザーがボタンをクリック --> ボタン操作に対するハンドラー関数が呼び出される <-- ユーザーがキー入力 --> キー操作に対するハンドラー関数が呼び出される ... <-- ウィンドウを閉じる(通常はGUIアプリの終了操作) このタイミングでようやくmainloopが終了 GUIプログラムではmainloop()が終わったらアプリケーションの終了になる ``` こんなふうに制御の流れがCUI/GUIでは大きく異なります。この「利用者がGUIを操作する度に必要な処理を行う」という制御方式をイベント駆動方式なんて言い方をします。 元々CUIで作成していたプログラムをCUIにする(多分)最も素朴な方法は * プログラムの各段階を「関数分割する」 例えば最初に述べたCUIの各段階((A)~(D))をそれぞれ関数に分割します。 * それぞれの関数をイベント駆動方式の中の各ハンドラーの中から呼び出すようにする もし最初に書いたような内容ですと(A)をmainloopの前に実行し、ファイル名をGUIで入力させて計算開始を指示するボタンなどを押したときのハンドラーから(B)~(D)を呼び出すようにします。 といった感じだと思います。要するに今まで主たる制御を行っていたCUIのコードでプログラムの制御を支配するのではなく、あくまでGUIを司るmainloop()関数に制御を支配させ、従来のCUIコードは「mainloop()の中から自動的に呼び出されるハンドラーからさらに呼び出されるサブルーチン」にするのです。 本来なら画像処理など時間がかかる処理をハンドラーの中から直接呼び出すのはGUIアプリケーションとしてあまりよくないのですが(なぜなら時間がかかる処理をやっている間、画面がフリーズしたような状態になるから)、とりあえず動かすためにはまずこのあたりを目指して作ってみるとよいのではないでしょうか? 長時間の計算中でも画面がフリーズするようなことなく「いつでもキャンセルボタンを押せる」ような作りにするには長時間かかる処理を別のスレッドにすればよいのですが、それはまた別の課題として考えるのがよいのではないかと思います。