Issue
I have a syntax/understanding problem in an application using Python 3, PyQt5 and Qt5. I am unsure which of these is causing the problem.
I am charged with porting a GUI application which worked under Windows to Linux, with newer versions of libraries. I do not have access to the original running under Windows.
In several places I see:
menu = QMenu(self)
action = menu.addAction("Some string")
action.triggered[()].connect(self.handler)
I presume this used to work. I am porting to Python 3.5, PyQt 5.7 & Qt 5.7. I have reason to believe the code was written for earlier versions of each of these.
Executing it now generates a 'there is no matching overloaded signal'
error, on the action.triggered[()]
segment.
By guesswork and looking at a couple of examples I found somewhere, I have changed the last line to:
action.triggered.connect(self.handler)
and it seems to work now.
Could someone explain what the original triggered[()]
syntax meant/worked, and which "product" the evident change was in --- it would be nice to be able to read about where this got changed? Is my replacement by simply triggered
correct/same behaviour?
Solution
The relevant change happened in PyQt-5.3. It is not entirely safe to simply remove the old syntax, as this could easily create a subtle bug in both current and future code.
The issue is that certain signals like triggered
will always send a default boolean value unless you take steps to eliminate it. Qt defines these signals like this:
triggered(bool checked = false)
PyQt previously implemented this as two overloads:
triggered(bool checked)
triggered()
and if you wanted to explicitly select the latter overload, you had to use the following slightly awkward sytax:
triggered[()].connect(slot)
But this is now no longer an option, as only the first overload is implemented. So to get exactly the same behaviour, it is necessary to either wrap the handler like this:
triggered.connect(lambda checked: slot())
or decorate it like this:
@QtCore.pyqtSlot()
def handleTriggered(self):
pass
Otherwise, it is quite easy to fall into a little trap. Imagine you have a method defined like this:
def frobnicate(self, foobar=True):
if foobar:
# do something nice
else:
# empty my home directory without prompting
Later on, you decide to hook this up to a button, like this:
self.button.clicked.connect(self.frobnicate)
Which all seems perfectly fine, until you find out that the clicked
signal works just like triggered
, and always sends False
by default...
Of course, if you're absolutely certain that you will never connect slots which take arguments to signals like triggered
and clicked
, you could get away with connecting them all in the simplistic fashion shown above. But, really, that is just an accident waiting to happen...
Answered By - ekhumoro
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.