ちろる

理系大学生が自由気ままに

PyQtにおけるクラス設計 -MVCパターン-

MVCパターン

MVCパターンとはUIをもつアプリケーションにおけるデザインパターンである。クラスをModel、View、Controllerの3つに分ける。'単一責任原則'を考えるとそれぞれのクラスは以下の責務をもつ。

Model: データの格納とそのデータを用いた処理。
View: Modelのデータを描画。
Controller: Model、Viewに司令を出す。

f:id:tsupiano:20171207001756p:plain

MVCパターンは上の図のようになる。記事によってはModelとViewを繋いでいないものがある。どちらも不正解ではなく、その状況によって繋ぐか繋がないかの選択をする。ここに関しては以下の記事の後半が参考になった。
qiita.com

まずはMVCパターンのベースを構築

ContollerとViewはそれぞれ他2つにアクセスできるが、Modelは他クラスの情報にアクセスできない。この状態をコードに落としてみたのが以下。

# -*- coding: utf-8 -*-

class View(object): # 何も継承しないときは'object'を継承することが推奨されている
  def __init__(self,model):
    self.model = model

  def register(self,controller):
    self.controller = controller


class Model(object):
  def __init__(self):
    pass


class Controller(object):
  def __init__(self,view,model):
    self.view = view
    self.model = model

    self.view.register(self)


if __name__ == '__main__':
  model = Model()
  view = View(model)
  controller = Controller(view,model)

ベースに簡単なGUIを載せてみる

PyQtについては以下のページなどを参照してほしい。

qiita.com
myenigma.hatenablog.com
d.hatena.ne.jp


実際に処理をそれぞれのクラスに乗せるときに、各クラスの役割の規範を超えたことをしないように注意して以下のプログラムを作成した。

# -*- coding: utf-8 -*-

from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLabel
import sys

class View(QWidget): 
  def __init__(self, model):
    super().__init__()

    self.model = model

  def register(self,controller):
    self.controller = controller
    # controller生成後にinitUIしないとcontrollerのメソッドにバインドできない
    # これであってるかは不明...
    self.initUI() 
  
  def initUI(self):
    self.setGeometry(200,200,150,100)
    button = QPushButton('sayHello!!',self)
    button.clicked.connect(self.controller.pushButton)
    self.label = QLabel("none",self)
    self.label.setGeometry(20,50,50,20)

  def updateUI(self):
    self.label.setText(model.word)


class Model(object): # 何も継承しないときは'object'を継承することが推奨されている
  def __init__(self):
    self.word = 'none'
  
  def sayHello(self):
    # Modelは自身のデータを処理するだけ
    self.word = 'Hello'


class Controller(object):
  def __init__(self, view, model):
    self.view = view
    self.model = model

    self.view.register(self)
  
  def pushButton(self):
    # 複数の処理を行いたい際などに直接Modelに渡すのでなくControllerを挟むということが活きてくる
    self.model.sayHello()
    self.view.updateUI()


if __name__ == '__main__':
  app = QApplication(sys.argv)

  model = Model()
  view = View(model)
  controller = Controller(view,model)

  view.show()
  sys.exit(app.exec_())