サンプル1

PythonでMVVMパターンを使用したGUIアプリケーションのサンプルを以下に示します。この例では、Tkinterを使用して簡単なカウンターアプリケーションを作成します。

import tkinter as tk

# モデル
class CounterModel:
    def __init__(self):
        self._counter = 0

    @property
    def counter(self):
        return self._counter

    def increment(self):
        self._counter += 1

    def decrement(self):
        self._counter -= 1

# ビューモデル
class CounterViewModel:
    def __init__(self, model):
        self.model = model
        self._observers = []

    @property
    def counter_text(self):
        return str(self.model.counter)

    def increment(self):
        self.model.increment()
        self.notify_observers()

    def decrement(self):
        self.model.decrement()
        self.notify_observers()

    def add_observer(self, observer_func):
        self._observers.append(observer_func)

    def notify_observers(self):
        for observer in self._observers:
            observer()

# ビュー
class CounterView(tk.Frame):
    def __init__(self, root, viewmodel):
        super().__init__(root)
        self.viewmodel = viewmodel

        self.label = tk.Label(self, text=self.viewmodel.counter_text)
        self.label.pack()

        self.increment_button = tk.Button(self, text="増加", command=self.increment)
        self.increment_button.pack(side=tk.LEFT)

        self.decrement_button = tk.Button(self, text="減少", command=self.decrement)
        self.decrement_button.pack(side=tk.RIGHT)

        self.viewmodel.add_observer(self.update_view)

    def increment(self):
        self.viewmodel.increment()

    def decrement(self):
        self.viewmodel.decrement()

    def update_view(self):
        self.label.config(text=self.viewmodel.counter_text)

# メインアプリケーション
def main():
    root = tk.Tk()
    root.title("MVVM カウンターサンプル")

    model = CounterModel()
    viewmodel = CounterViewModel(model)
    view = CounterView(root, viewmodel)
    view.pack()

    root.mainloop()

if __name__ == '__main__':
    main()

説明:

この構造により、ビューとモデルの間の結合度が低くなり、コードの再利用性とテストの容易性が向上します。

実行方法:

  1. 上記のコードをPythonファイル(例:mvvm_example.py)にコピーします。
  2. ターミナルまたはコマンドプロンプトで、python mvvm_example.pyを実行します。
  3. 表示されるウィンドウで「増加」や「減少」ボタンをクリックしてカウンターを操作できます。

サンプル2

PythonでMVVMパターンを使用して、最大5つのカウントダウン処理を並行して実行するGUIアプリケーションのサンプルを作成しました。各カウントダウンは1から10秒の乱数で決定され、ボタンを押すたびに新しいカウントダウン処理が追加されます。

import tkinter as tk
import random

# モデル
class CountdownModel:
    def __init__(self, duration):
        self.duration = duration
        self.remaining = duration
        self.is_running = True

    def tick(self):
        if self.is_running and self.remaining > 0:
            self.remaining -= 1
            return True
        else:
            self.is_running = False
            return False

# ビューモデル
class CountdownViewModel:
    MAX_COUNTDOWNS = 5

    def __init__(self):
        self.countdowns = []
        self._observers = []

    def add_countdown(self):
        if len(self.countdowns) < self.MAX_COUNTDOWNS:
            duration = random.randint(1, 10)
            model = CountdownModel(duration)
            self.countdowns.append(model)
            self.notify_observers()
            return model
        else:
            return None

    def tick_all(self):
        for model in self.countdowns:
            model.tick()
        self.remove_finished()
        self.notify_observers()

    def remove_finished(self):
        self.countdowns = [c for c in self.countdowns if c.is_running]

    def add_observer(self, observer_func):
        self._observers.append(observer_func)

    def notify_observers(self):
        for observer in self._observers:
            observer()

# ビュー
class CountdownView(tk.Frame):
    def __init__(self, root, viewmodel):
        super().__init__(root)
        self.viewmodel = viewmodel

        self.add_button = tk.Button(self, text="カウントダウン追加", command=self.add_countdown)
        self.add_button.pack(pady=10)

        self.countdown_frames = []

        self.viewmodel.add_observer(self.update_view)
        self.update_view()
        self.schedule_tick()

    def add_countdown(self):
        self.viewmodel.add_countdown()

    def update_view(self):
        # 既存のフレームをクリア
        for frame in self.countdown_frames:
            frame.destroy()
        self.countdown_frames.clear()

        # 新しいカウントダウンフレームを作成
        for model in self.viewmodel.countdowns:
            frame = tk.Frame(self)
            label = tk.Label(frame, text=f"残り時間: {model.remaining} 秒")
            label.pack()
            frame.pack(pady=5)
            self.countdown_frames.append(frame)

    def schedule_tick(self):
        self.viewmodel.tick_all()
        self.after(1000, self.schedule_tick)

# メインアプリケーション
def main():
    root = tk.Tk()
    root.title("MVVM カウントダウンサンプル")

    viewmodel = CountdownViewModel()
    view = CountdownView(root, viewmodel)
    view.pack(padx=20, pady=20)

    root.mainloop()

if __name__ == '__main__':
    main()

説明: