나는 과거 에이 주제에 대해 꽤 많이 읽었으며 밥 삼촌의 이와 같은 흥미로운 이야기를 보았습니다 . 그럼에도 불구하고 항상 데스크톱 응용 프로그램을 올바르게 설계하고 UI 쪽 의 책임 과 논리 쪽 의 책임을 구분하기가 매우 어렵습니다 .
모범 사례에 대한 매우 간단한 요약은 다음과 같습니다. 어떤 종류의 백엔드 / UI 프레임 워크에 상관없이 라이브러리를 (이론적으로) 사용할 수 있도록 UI와 분리 된 로직을 설계해야합니다. 이것이 의미하는 바는 기본적으로 UI는 가능한 한 더미가되어야하고 많은 처리는 로직 측면에서 수행되어야합니다. 그렇지 않으면 말 그대로 콘솔 응용 프로그램, 웹 응용 프로그램 또는 데스크톱 응용 프로그램과 함께 멋진 라이브러리를 사용할 수 있습니다.
또한 Bob 삼촌은 어떤 기술을 사용할 지에 대한 많은 토론 (좋은 인터페이스)에 대해 다른 토론을 제안합니다.이 지연 개념을 사용하면 잘 테스트 된 엔티티를 크게 분리 할 수 있지만 여전히 까다 롭습니다.
그래서 저는이 질문이 인터넷 전체와 많은 훌륭한 책들에서 여러 번 논의 된 광범위한 질문이라는 것을 알고 있습니다. 그래서 좋은 결과를 얻으려면 pyqt에서 MCV를 사용하는 더미 예제를 게시 할 것입니다.
import sys
import os
import random
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore
random.seed(1)
class Model(QtCore.QObject):
item_added = QtCore.pyqtSignal(int)
item_removed = QtCore.pyqtSignal(int)
def __init__(self):
super().__init__()
self.items = {}
def add_item(self):
guid = random.randint(0, 10000)
new_item = {
"pos": [random.randint(50, 100), random.randint(50, 100)]
}
self.items[guid] = new_item
self.item_added.emit(guid)
def remove_item(self):
list_keys = list(self.items.keys())
if len(list_keys) == 0:
self.item_removed.emit(-1)
return
guid = random.choice(list_keys)
self.item_removed.emit(guid)
del self.items[guid]
class View1():
def __init__(self, main_window):
self.main_window = main_window
view = QtWidgets.QGraphicsView()
self.scene = QtWidgets.QGraphicsScene(None)
self.scene.addText("Hello, world!")
view.setScene(self.scene)
view.setStyleSheet("background-color: red;")
main_window.setCentralWidget(view)
class View2():
add_item = QtCore.pyqtSignal(int)
remove_item = QtCore.pyqtSignal(int)
def __init__(self, main_window):
self.main_window = main_window
button_add = QtWidgets.QPushButton("Add")
button_remove = QtWidgets.QPushButton("Remove")
vbl = QtWidgets.QVBoxLayout()
vbl.addWidget(button_add)
vbl.addWidget(button_remove)
view = QtWidgets.QWidget()
view.setLayout(vbl)
view_dock = QtWidgets.QDockWidget('View2', main_window)
view_dock.setWidget(view)
main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, view_dock)
model = main_window.model
button_add.clicked.connect(model.add_item)
button_remove.clicked.connect(model.remove_item)
class Controller():
def __init__(self, main_window):
self.main_window = main_window
def on_item_added(self, guid):
view1 = self.main_window.view1
model = self.main_window.model
print("item guid={0} added".format(guid))
item = model.items[guid]
x, y = item["pos"]
graphics_item = QtWidgets.QGraphicsEllipseItem(x, y, 60, 40)
item["graphics_item"] = graphics_item
view1.scene.addItem(graphics_item)
def on_item_removed(self, guid):
if guid < 0:
print("global cache of items is empty")
else:
view1 = self.main_window.view1
model = self.main_window.model
item = model.items[guid]
x, y = item["pos"]
graphics_item = item["graphics_item"]
view1.scene.removeItem(graphics_item)
print("item guid={0} removed".format(guid))
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# (M)odel ===> Model/Library containing should be UI agnostic, right now it's not
self.model = Model()
# (V)iew ===> Coupled to UI
self.view1 = View1(self)
self.view2 = View2(self)
# (C)ontroller ==> Coupled to UI
self.controller = Controller(self)
self.attach_views_to_model()
def attach_views_to_model(self):
self.model.item_added.connect(self.controller.on_item_added)
self.model.item_removed.connect(self.controller.on_item_removed)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = MainWindow()
form.setMinimumSize(800, 600)
form.show()
sys.exit(app.exec_())
위의 스 니펫에는 많은 결함이 포함되어 있으며 모델이 UI 프레임 워크 (QObject, pyqt 신호)에 결합되어있는 것이 더 분명합니다. 예제가 실제로 더미라는 것을 알고 단일 QMainWindow를 사용하여 몇 줄로 코딩 할 수는 있지만 더 큰 pyqt 응용 프로그램을 올바르게 설계하는 방법을 이해하는 것이 목적입니다.
의문
좋은 일반 관행에 따라 MVC를 사용하여 큰 PyQt 응용 프로그램을 어떻게 올바르게 설계 하시겠습니까?
참조
나는 이것과 비슷한 질문을했습니다 .