3 Likes
#pyqt
I just posted this question on codereview.stackexchange.com
Using a decorator for init in Python3+Pyside6, to avoid writing boilerplate code
I'm building a GUI application with Python3+Pyside6 and have many QDialog
classes that are similar, and i was looking
for a way to avoid having to rewrite the same code when initializing them. The code below is one solution to this
This code creates a python decorator modal_dialog_init
to avoid having to write boilerplate code when initializing
classes with similar __init__
functions
(An alternative solution is to use inheritance, but then i could not have code both before and after: Putting the call
to super()
at the beginning or end of the __init__
function)
import sys
from PySide6 import QtWidgets
from PySide6 import QtCore
from PySide6 import QtGui
def modal_dialog_init(i_init_function):
def init_wrapper(self, *args, **kwargs):
super(self.__class__, self).__init__()
self.setModal(True)
self.vbox = QtWidgets.QVBoxLayout(self)
label = QtWidgets.QLabel("init_wrapper, before __init__")
self.vbox.addWidget(label)
i_init_function(self) # <-------------------
label = QtWidgets.QLabel("init_wrapper, after __init__")
self.vbox.addWidget(label)
for arg in args:
print(f"{arg}")
for k, v in kwargs.items():
print(f"{k}: {v}")
self.button_box = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Close, QtCore.Qt.Horizontal,
self
)
self.vbox.addWidget(self.button_box)
self.button_box.rejected.connect(self.reject)
self.adjustSize()
return init_wrapper
def start(cls, *args, **kwargs):
dlg = cls(*args, **kwargs)
dlg.exec()
return None
class AboutDialog(QtWidgets.QDialog):
@modal_dialog_init
def __init__(self, *args, **kwargs):
label = QtWidgets.QLabel("during __init__ --- About this application")
self.vbox.addWidget(label)
class SystemInfoDialog(QtWidgets.QDialog):
@modal_dialog_init
def __init__(self, *args, **kwargs):
label = QtWidgets.QLabel("during __init__ --- System info")
self.vbox.addWidget(label)
if __name__ == "__main__":
qapplication = QtWidgets.QApplication(sys.argv)
start(AboutDialog, "test-arg", param1="test-kw-arg-1", param2="test-kw-arg-2")
start(SystemInfoDialog, "another-test")
sys.exit(qapplication.exec())
Running the code:
- Dependencies:
- Python 3.10
pip install Pyside6
- To illustrate i've added two example
QDialog
classes. When the application is run the first is shown, and only once that is closed is the next dialog shown
I'm mainly interested in how this code can be improved in terms of maintainability, readability, elegance. And as a side
issue if there are other (better) ways of achieving the same thing (for example other design patterns)
Grateful for your thoughts on this!
2 Likes
9 Comments
Python 3.9 has more flexible decorators which can be used with PyQt's signals+slots like this:
import sys
import logging
from PyQt5 import QtWidgets
from PyQt5 import QtCore
logging.basicConfig(level=logging.DEBUG)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.button_qpb = QtWidgets.QPushButton("Button")
self.setCentralWidget(self.button_qpb)
self.show()
@self.button_qpb.clicked.connect
def on_button_clicked():
print("clicked!")
print(self.button_qpb.text())
self.my_print()
def my_print(self):
print("my_print entered")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
Please note that the "slot function" on_button_clicked
is an inner function. (Using an inner function seems to be the best approach)
Thank you to Geir Arne Hjelle at realpython.com who helped me with this!
7 Likes
1 Comments