Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

pyqt signal doesnt seem to work correctly with decorators

When using the pyqt signals of the UI elements such as buttons with decorated methods, the signal doesn’t seem to work. Please find below the minimum reproducible code.

import sys
from PyQt5.QtWidgets import (QWidget, QToolTip, QPushButton, QApplication)
from PyQt5.QtGui import QFont

def ui_decorator(target_func):
    def call(self, *args, **kwargs):
        print("Init.")
        ret_code = target_func(self, *args, **kwargs)
        print("Deinit.")
        return ret_code
    return call

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        QToolTip.setFont(QFont('SansSerif', 10))
        self.setToolTip('This is a <b>QWidget</b> widget')
        btn = QPushButton('Button', self)
        btn.setToolTip('This is a <b>QPushButton</b> widget')
        btn.clicked.connect(self.button_action)
        btn.resize(btn.sizeHint())
        btn.move(50, 50)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Tooltips')
        self.show()

    @ui_decorator
    def button_action(self):
        print("Button Clicked")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

In the above code if the button is clicked the call to the button_action function fails with the following message: TypeError: button_action() takes 1 positional argument but 2 were given. But the code works alright when I don’t use the decorator (ui_decorator), even though it still takes only 1 positional argument.

Thanks

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

When PyQt calls a slot (which can be any callable) it does not just "call" it. In order to achieve the behaviour that additional slot parameters are ignored it analyzes the callable and only passes the appropriate number of parameters. You render this mechanism useless by having your decorator return a callable that accepts any number of parameters and simply passes all of them on to the actual function.

You have two options:

  • Adjust your decorator so that it returns a callable that accepts exactly the right amount of parameters (I suspect that to be rather difficult).
  • Use the PyQt pyqtSlot decorator on top of your own decorator. That decorator defines the signature of the slot and makes the above mentioned behaviour working again, because PyQt then knows that the slot has no parameters:
@pyqtSlot()
@ui_decorator
def button_action(self):
    print("Button Clicked")
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading