tkinterはPythonでデスクトップアプリを構築するライブラリです。
比較的簡単(GUIが簡単という意味です)なアプリを構築するのに使われています。
でも、いきなりtkinterで開発しろと言われても、システム全体の構造までtkinterは決めてくれません。
システム全体の構造は開発者の手に委ねられることになります。
そんなときに、アーキテクチャの知識があるとシステムが見通しのいいものになります。
この記事では、前編後編に分けて、tkinterで少し処理が込み入ったアプリを構築する際に必要なMVCアーキテクチャの解説と、MVCアーキテクチャをどうtkinterで実現するのかについて書いていきます。
はじめに:何も考えずtkinterで作ると
ネットの記事で多いのが、tkinter自体のコーディングの仕方を解説するだけの記事です。tkinterのウィジットの使い方について調べたいときはこれでいいのですが、何も考えずにtkinterだけを使うと、一つのソースコードにtkinterの処理とデータの処理が混在し、処理対象がソースコードの部分部分によってまちまちで、見通しの悪いコードになります。
開発するときはそれでも動くのでいいかもしれませんが、問題は保守のときです。
自分の書いたコードがどこで何をやっているのか分からなくなり、保守に難儀します。
そこで、tkinterを使いつつシステム全体はきちんとした構造をもったものにする必要があります。
そんなときに考え方の基本としたいのがMVCアーキテクチャです。
MVCアーキテクチャとは?
MVCアーキテクチャとは、
- Model:データとデータを処理するロジック
- View:画面と画面を処理するロジック
- Contoroller:全体の制御
に分けてシステムを構築するアーキテクチャです。
ContorollerがModelとViewを制御します。
通信はContorollerとModelの間、ContorollerとViewの間で行われ、ModelとViewは互いのことを知りません。
めんどくさそうだ…と思うかもしれませんが、実はこの構造でシステムを構築すると、大変見通しの良いシステムになります。
最近はCの代わりにVM(ViewModel)という呼称を導入しMVVMというものも出てきていますが、本質的にはMVCと変わりません。
サンプル1:アプリ制御と実際の処理の分離
それでは、さっそく例を見ていきます。
まずは、ViewとContorollerを分離した例です。
全体構造
このシステムは画面で指定したパラメータに従って処理を行わせるものです。
全体の一部を抜粋します。
View:desktop.py
import tkinter as tk import tkinter.ttk as ttk from tkinter import messagebox import extraction class Application(tk.Frame): def __init__(self,master): super().__init__(master) master.title("抽出ツール") …部品の描画… button_get = tk.Button(master, text='抽出', command=self.extract) button_get.grid(row=4, column=1) def extract(self): extraction.extract(self.taisho1.get(), self.taisho2.get(), self.pay_min.get(), self.pay_max.get()) messagebox.showinfo('終了', '抽出処理が終了しました') #メイン。クラスを作って呼んでいるだけ。 win = tk.Tk() app = Application(master=win) app.mainloop()
Contoroller:extraction.py
import openpyxl import glob import os # ex. taisho1:'中央' taisho2:'3連単' paymin:15000 pay_max:19999 def extract(taisho1, taisho2, pay_min, pay_max): …処理…
それぞれについて見ていきます。
アプリ制御
まず、desktop.pyです。
Viewというか、アプリ本体の制御ですね。
このファイルにはクラスApplicationが一つあります。
これがアプリのクラスです。
tkinterの作法に従って
win = tk.Tk() app = Application(master=win) app.mainloop()
でウィンドゥを生成してイベントループを回します。
Applicationには関数が2つあります。
def __init__(self,master): super().__init__(master) master.title("抽出ツール") …部品の描画… button_get = tk.Button(master, text='抽出', command=self.extract) button_get.grid(row=4, column=1)
これが初期化関数です。ApplicationはFrameの派生クラスなので、Frameを初期化するために、まずsuper()の__init__を呼びます。
その後、ウィンドゥタイトルや部品を描画します。
ボタンには押下時のイベントハンドラを紐づけます。
それがextract関数です。
extract関数は次のようになっています。
def extract(self): extraction.extract(self.taisho1.get(), self.taisho2.get(), self.pay_min.get(), self.pay_max.get()) messagebox.showinfo('終了', '抽出処理が終了しました')
処理関数であるextractionのextractを呼び出します。呼び出すとき、自身のメンバ(各入力部品に紐づけられたメンバです)の値を引数として渡します。処理が終わったらメッセージボックスを表示します。
実際の処理
contoroller、実際の処理を行う関数は、次のようになっています。
def extract(taisho1, taisho2, pay_min, pay_max): …処理…
ここにtkinterの影響は全くありません。tkinterの処理は、全てdesktop.py内で完結しています。別にこの関数はこのアプリでだけ使えるというものでもありません。import文を見てもらえば分かる通り、tkinterの影響は全くないのですから、処理だけを切り離すことができます。
これが、構造を分ける利点です。
各モジュールはモジュールの本来の目的に専念することができます。
見通しの良いシステムとは、このようなシステムのことを指すのです。
このシステムではModelは出てきませんが、extraction.pyのコードにあるように、Modelは現実のExcelファイルそのものになっています。
後編では、もう一つ例を見ます。