Gdaliy_Garmiza | 2020-11-16 12:18:04 UTC | #1
Hi, Martin!
I purchased your book a couple of months ago. It helps me a lot. I'm currently developing my own application, where I try to implement the 'Open recent files' functionality in the main menu. It's quite similar to your example here: https://www.pythonguis.com/tutorials/transmitting-extra-data-qt-signals/ with the difference that I connect the 'triggered' signals to the QActions (the list of files, generated in the loop). But neither my nor your code works for me.
Both
button.clicked.connect(lambda checked, a=a: self.button_clicked(a)) # [in your 'signals_extra_3.py' script]
and
new_act.triggered.connect(lambda checked, filename=file: self.OpenRecent(filename)) # [in my code]
Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PySide2 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!
give out the TypeError:
Thanks.
martin | 2020-11-16 14:51:04 UTC | #2
Hi @Gdaliy_Garmiza welcome to the forum! Glad you're finding the book useful.
Yep, this unfortunately due to differences in how the .triggered()
and .clicked()
signals work between version of PyQt5 (& PySide2). The "checked" parameter is (according to the documentation) supposed to be sent with the .triggered
signal, for example, but it isn't
Can you check if this also happens when connecting to a real method (vs. a lambda
)? I wonder if that has something to do with it.
If you don't need the checked
parameter (i.e. the action or button is not toggleable) you can just remove that parameter from your receiver method/function.
Gdaliy_Garmiza | 2020-11-17 00:53:23 UTC | #3
Hi, @martin!
Thanks for your quick response. I seem to have tried almost all the combinations with no success.
E.g.:
# self.OpenRecentList — list of the full filenames/paths
for file in self.OpenRecentList:
new_act = QAction(file, self)
self.menuOpen_Recent.addAction(new_act)
new_act.triggered.connect(lambda filename=file: self.OpenRecent(filename))
def OpenRecent(self, file):
# Code that opens a file
Result: When triggering the created actions(menu items) it tries to pass the boolean instead of the filename, because the error “File False not found” is raised within my app. It seems to be the boolean from the ‘checked’ parameter which is sent from the triggered signal, but omitted in my code.
—
for file in self.OpenRecentList:
def open_recent_connector(filename=file):
return self.OpenRecent(filename)
new_act = QAction(file, self)
self.menuOpen_Recent.addAction(new_act)
new_act.triggered.connect(open_recent_connector)
Result: the same as above.
for file in self.OpenRecentList:
def open_recent_connector(checked, filename=file):
return self.OpenRecent(filename)
new_act = QAction(file, self)
self.menuOpen_Recent.addAction(new_act)
new_act.triggered.connect(open_recent_connector)
Result (on triggering the action): TypeError: open_recent_connector() missing 1 required positional argument: 'checked'.
for file in self.OpenRecentList:
filename = file
def open_recent_connector():
return self.OpenRecent(filename)
new_act = QAction(file, self)
self.menuOpen_Recent.addAction(new_act)
new_act.triggered.connect(open_recent_connector)
Result (on triggering the action): the OpenRecent function is called (it opens the file), but the ‘loop problem’, described in your book, occurs. It passes the value of the last iteration when any QAction is triggered, and my app always opens the last file in the list.
Trying to fix it using your 'naming method' I went even as far as that:
new_act = []
for ind, file in enumerate(self.OpenRecentList):
def open_recent_connector():
filename = self.OpenRecentList[ind]
return self.OpenRecent(filename)
new_act.append(QAction(file, self))
self.menuOpen_Recent.addAction(new_act[ind])
new_act[ind].triggered.connect(open_recent_connector)
Result (on triggering the action): the same as above (the loop problem).
Gdaliy_Garmiza | 2020-11-17 17:41:40 UTC | #4
Hey, @martin
I have found the correct solution how to do this without lambda here: https://stackoverflow.com/questions/52283469/using-qsignalmapper
Here is the code which worked for me:
from functools import partial
...
def open_recent_menu_builder(self):
for file in self.OpenRecentList:
new_act = QAction(file, self)
self.menuOpen_Recent.addAction(new_act)
new_act.triggered.connect(partial(self.OpenRecent, file))
def OpenRecent(self, file):
# Code that opens a file
Jesus_Gangoso_Burgos | 2021-01-07 16:06:36 UTC | #5
Hi,
in order the code in the signals_extra_3.py script work, change "checked" by "*args". That is,
button.clicked.connect(lambda *args, val=a: self.button_clicked(val)) # <1>
in line 25.
Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.