LoginSignup
5
9

More than 3 years have passed since last update.

TkinterでMVC

Posted at

はじめに

この記事での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):
        self.val = tk.IntVar(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()
        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()

完成品

スクリーンショット 2020-08-29 16.25.14.png

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とかは、どこが優れているのか教えて頂けると助かります。

5
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
9