#pyqt

tord_dellsen@diasp.eu

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!

#python #decorators #programming #coding #pyqt #pyside

tord_dellsen@diasp.eu

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!

#python #python-decorators #programming #pyqt