Saya sudah membaca cukup banyak tentang subjek ini di masa lalu dan menonton beberapa pembicaraan menarik seperti ini dari Paman Bob . Namun, saya selalu menemukan kesulitan untuk merancang aplikasi desktop saya dengan benar dan membedakan mana yang harus menjadi tanggung jawab di sisi UI dan mana yang di sisi logika .
Ringkasan singkat dari praktik yang baik adalah sesuatu seperti ini. Anda harus mendesain logika Anda dipisahkan dari UI, sehingga Anda bisa menggunakan (secara teoritis) perpustakaan Anda apa pun jenis kerangka backend / UI. Apa artinya ini pada dasarnya UI harus sedapat mungkin dummy dan pemrosesan yang berat harus dilakukan pada sisi logika. Berkata sebaliknya, saya benar-benar dapat menggunakan perpustakaan saya yang bagus dengan aplikasi konsol, aplikasi web atau desktop.
Juga, paman Bob menyarankan diskusi berbeda tentang teknologi mana yang akan digunakan akan memberi Anda banyak manfaat (antarmuka yang baik), konsep penangguhan ini memungkinkan Anda untuk memiliki entitas yang teruji dengan sangat baik, kedengarannya hebat tetapi masih rumit.
Jadi, saya tahu pertanyaan ini adalah pertanyaan yang cukup luas yang telah dibahas berkali-kali di seluruh internet dan juga dalam banyak buku bagus. Jadi untuk mendapatkan sesuatu yang baik dari itu saya akan memposting contoh dummy yang sangat sedikit mencoba menggunakan MCV di pyqt:
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_())
Cuplikan di atas mengandung banyak kekurangan, yang lebih jelas adalah model yang digabungkan ke kerangka UI (QObject, sinyal pyqt). Saya tahu contohnya adalah benar-benar dummy dan Anda dapat mengkodekannya pada beberapa baris menggunakan QMainWindow tunggal tetapi tujuan saya adalah untuk memahami bagaimana merancang dengan benar aplikasi pyqt yang lebih besar.
PERTANYAAN
Bagaimana Anda merancang aplikasi PyQt besar menggunakan MVC dengan mengikuti praktik umum yang baik?
REFERENSI
Saya telah membuat pertanyaan serupa dengan ini di sini