はじめに
この記事でのMVC(Model-View-Controller)は、以下の方針で機能を切り分けることを指します。
Model: ViewもControllerも知らない。ただ実直に計算を行う。
View: ModelもControllerも知らない。美を追求するのみ。
Controller: ModelとViewの両方を知っている神様的ポジション。ModelとViewの橋渡しをする。
何番煎じか分からないですが、
意外と上の方針に則るMVCの情報が少ないと思ったため記事にします。
いきなりコード
import tkinter as tk
class Model:
def __init__(self, parent):
self.val = tk.IntVar(parent, value=0)
def add_one(self):
self.val.set(self.val.get() + 1)
def reset(self):
self.val.set(0)
class View(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.value_label = tk.Label(self)
self.add_one_btn = tk.Button(self, text="ADD ONE")
self.reset_btn = tk.Button(self, text="RESET")
self.value_label.pack()
self.add_one_btn.pack()
self.reset_btn.pack()
self.pack()
class Controller:
def __init__(self, root):
self.model = Model(root)
self.view = View(root)
# Binding
self.view.value_label.config(textvariable=self.model.val)
# Callback
self.view.add_one_btn.config(command=self.model.add_one)
self.view.reset_btn.config(command=self.model.reset)
if __name__ == "__main__":
root = tk.Tk()
app = Controller(root)
root.mainloop()
完成品
ADD ONEボタンを押すと上の数字が1ずつ増えていきます。
RESETボタンを押すと上の数字が0に戻ります。
##解説
どうなってんの?
Model
tk.IntVar()
というのがすごい便利なやつです。これを通常のintの代わりに使っています。
ほかにもデータ型によってtk.DoubleVar()
, tk.BooleanVar()
, tk.StringVar()
があります。
値のやりとりにget()/set()を使う必要がある点にだけ注意して、淡々と計算をさせます。
View
Viewはただ見た目だけ気にしてコンポーネントを作っていきます。
変数になる部分(ラベルのテキスト)はここでは何も設定しません。
Controller
ControllerはModelとViewの両方を知っているのでやりたい放題です。
self.view.value_label.config(textvariable=self.model.val)
ここで、ModelのもつIntVar()を、Viewのテキストを空にしていたラベルコンポーネントに割り当てています。
こうすることで、ModelとViewの変数が同期するようになり、Modelで値を変更すると、自動的にViewに反映されます。
また、各ボタンを押した時の処理もControllerで割り当てます。
知らないってどういうこと?
「ModelがViewもControllerも知らない」というのは、簡単に言うとModelがViewやControllerのインスタンスを持たないことです。
例えば以下のようにModelがViewを持つような実装をしたとします。
class Model:
def __init__(self, view):
self.val = 0
self.view = view
こうすれば、Modelがもつ値が変わったとき、その変更をViewに教えることができます。
例えばこう
class Model:
#・・・
def add_one(self):
self.val += 1
self.view.label["text"] = str(self.val) # viewに値を反映
すごく簡単そうで何だか最初のコードよりも良さそうに思うかもしれません。
でも実はModelを実装する人の負担がめちゃ増えています><
この実装だとModel担当の人は、こんな足し算するだけの処理のために、次のことを知っている必要があります。
- Viewにはラベルコンポーネントがあること
- その名前はlabelということ
- ラベルコンポーネントのテキスト更新の方法
などなど
また、ModelがViewを持つことで、Viewを破壊する危険もあります。
ていうか一発で破壊できます。
class Model:
#・・・
def balse(self):
self.view = None
大いなる力には大いなる責任が伴います。
なるべく力は持たないようにしたいです。
ということで
MVCで役割分担を明確にして、負担も責任も最小にしましょう。
###余談
GUI作成の経験があんまりないので、いまいちどのライブラリが良いのか分からないです。
pythonだとtkinterしか使ったことがないのですが、
wxpythonとかkivyとかは、どこが優れているのか教えて頂けると助かります。