I came across this issue when making 'QMenu' actions in a loop and assigning a connection with them. Here is an example:
import sys
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
mbar = self.menuBar()
file_menu = mbar.addMenu("File")
animals = ['Dog','Badger','Bear','Fox']
for animal in animals:
ac = file_menu.addAction(animal)
ac.triggered.connect(lambda: print(animal))
'''Even though each action is assigned a seperate connection with the current animal,
only the last animal is ever printed. Why?'''
def main():
app = QtWidgets.QApplication([sys.argv])
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
The last variable in the loop, 'Fox', is printed for all menu entries. The solution mentioned here is to use the QMenu triggered signal instead, allowing you to store the data in the action and access it that way. Like this:
import sys
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
mbar = self.menuBar()
file_menu = mbar.addMenu("File")
animals = ['Dog','Badger','Bear','Fox']
for animal in animals:
ac = file_menu.addAction(animal)
ac.setData(animal)
file_menu.triggered.connect(lambda a: print(a.data()))
def main():
app = QtWidgets.QApplication([sys.argv])
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
This works fine but is a little annoying because to work in the real world I would have to implement some sort of check to make sure I was dealing with the right sort of action, for example by making a dataclass to hold the animal and checking for that dataclass instance. But then I discovered that the following approach also works:
import sys
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
mbar = self.menuBar()
self.file_menu = mbar.addMenu("File")
animals = ['Dog','Badger','Bear','Fox']
for animal in animals:
self.make_menu_entry(animal)
def make_menu_entry(self, animal):
ac = self.file_menu.addAction(animal)
ac.triggered.connect(lambda: print(animal))
def main():
app = QtWidgets.QApplication([sys.argv])
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
So now I'm thinking that this a scope problem, but I don't understand it. Can anyone explain what's happening?