|
|
@@ -0,0 +1,134 @@
|
|
|
+import random
|
|
|
+import sys
|
|
|
+from collections import UserDict
|
|
|
+
|
|
|
+from PyQt5.QtCore import QObject, pyqtSignal, Qt
|
|
|
+from PyQt5.QtWidgets import QMainWindow, QApplication, QFormLayout, QLineEdit, QSpinBox, QComboBox, QPushButton, \
|
|
|
+ QCheckBox, QWidget
|
|
|
+
|
|
|
+
|
|
|
+class DataModelSignals(QObject):
|
|
|
+ # Emit an "updated" signal when a property changes.
|
|
|
+ updated = pyqtSignal()
|
|
|
+
|
|
|
+
|
|
|
+class DataModel(UserDict):
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ self.signals = DataModelSignals()
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+
|
|
|
+ def __setitem__(self, key, value):
|
|
|
+ previous = self.get(key) # Get the existing value.
|
|
|
+ super().__setitem__(key, value)
|
|
|
+ if value != previous: # There is a change
|
|
|
+ self.signals.updated.emit() # Emit the signal
|
|
|
+ print(self) # Show the current state.
|
|
|
+
|
|
|
+
|
|
|
+model = DataModel(
|
|
|
+ name="Johnina Smith",
|
|
|
+ age=10,
|
|
|
+ favorite_icecream="Vanilla",
|
|
|
+ disable_details=False,
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+class Controller:
|
|
|
+ """ Simple controller, which handles backups and other operations. """
|
|
|
+ backups = []
|
|
|
+
|
|
|
+ def capitalize(self):
|
|
|
+ model["name"] = model["name"].upper()
|
|
|
+
|
|
|
+ def store_backup(self):
|
|
|
+ self.backups.append(model.copy())
|
|
|
+
|
|
|
+ def restore_backup(self):
|
|
|
+ if not self.backups:
|
|
|
+ return
|
|
|
+ random.shuffle(self.backups)
|
|
|
+ backup = self.backups.pop()
|
|
|
+ model.update(backup) # Overwrite the data in the model
|
|
|
+ print("RESTORE:", model)
|
|
|
+ print("BACKUPS:", len(self.backups))
|
|
|
+
|
|
|
+ def apply_title_case(self):
|
|
|
+ model["name"] = model["name"].title()
|
|
|
+
|
|
|
+
|
|
|
+controller = Controller()
|
|
|
+
|
|
|
+
|
|
|
+class MainWindow(QMainWindow):
|
|
|
+ def __init__(self):
|
|
|
+ super().__init__()
|
|
|
+ self.setWindowTitle("MVC Example")
|
|
|
+ layout = QFormLayout()
|
|
|
+
|
|
|
+ # Dictionary to store the form data, with default data.
|
|
|
+ self.name = QLineEdit()
|
|
|
+ self.name.textChanged.connect(self.on_name_changed)
|
|
|
+ self.age = QSpinBox()
|
|
|
+ self.age.setRange(0, 200)
|
|
|
+ self.age.valueChanged.connect(self.on_age_changed)
|
|
|
+ self.icecream = QComboBox()
|
|
|
+ self.icecream.addItems(["Vanilla", "Strawberry", "Chocolate"])
|
|
|
+ self.icecream.currentTextChanged.connect(self.on_icecream_changed)
|
|
|
+
|
|
|
+ self.title_btn = QPushButton("Set Title Case")
|
|
|
+ self.title_btn.pressed.connect(controller.apply_title_case)
|
|
|
+
|
|
|
+ # tag::connect[]
|
|
|
+ self.save_btn = QPushButton("Save")
|
|
|
+ self.save_btn.pressed.connect(controller.store_backup)
|
|
|
+ self.restore_btn = QPushButton("Restore")
|
|
|
+ self.restore_btn.pressed.connect(controller.restore_backup)
|
|
|
+ # end::connect[]
|
|
|
+
|
|
|
+ self.disable_details = QCheckBox("Disable Details?")
|
|
|
+ self.disable_details.toggled.connect(self.on_disable_details_toggled)
|
|
|
+
|
|
|
+ layout.addRow("Name", self.name)
|
|
|
+ layout.addRow(self.title_btn)
|
|
|
+ layout.addRow("Age", self.age)
|
|
|
+ layout.addRow("Favorite Ice Cream", self.icecream)
|
|
|
+ layout.addWidget(self.disable_details) # QCheckBox has its own label.
|
|
|
+ layout.addRow(self.save_btn)
|
|
|
+ layout.addRow(self.restore_btn)
|
|
|
+ layout.setLabelAlignment(Qt.AlignLeft)
|
|
|
+
|
|
|
+ widget = QWidget()
|
|
|
+ widget.setLayout(layout)
|
|
|
+ self.setCentralWidget(widget)
|
|
|
+
|
|
|
+ self.update_ui()
|
|
|
+ # Hook our UI sync into the model updated signal
|
|
|
+ model.signals.updated.connect(self.update_ui)
|
|
|
+
|
|
|
+ def update_ui(self):
|
|
|
+ self.name.setText(model["name"])
|
|
|
+ self.age.setValue(model["age"])
|
|
|
+ self.icecream.setCurrentText(model["favorite_icecream"])
|
|
|
+ self.disable_details.setChecked(model["disable_details"])
|
|
|
+
|
|
|
+ self.age.setDisabled(model["disable_details"])
|
|
|
+ self.icecream.setDisabled(model["disable_details"])
|
|
|
+
|
|
|
+ def on_name_changed(self, name):
|
|
|
+ model["name"] = name
|
|
|
+
|
|
|
+ def on_age_changed(self, age):
|
|
|
+ model["age"] = age
|
|
|
+
|
|
|
+ def on_icecream_changed(self, icecream):
|
|
|
+ model["favorite_icecream"] = icecream
|
|
|
+
|
|
|
+ def on_disable_details_toggled(self, checked):
|
|
|
+ model["disable_details"] = checked
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ app = QApplication(sys.argv)
|
|
|
+ window = MainWindow()
|
|
|
+ window.show()
|
|
|
+ sys.exit(app.exec_())
|