probono | 2021-01-15 20:18:06 UTC | #1
Trying to extend the QSystemTrayIcon
example by using a for loop to populate the menu only the last item in the list gets used in the menu. Why is this, and what would be the proper way to do this? In the real world, the contents of entries
would be determined programatically rather than hardcoded.
#!/usr/bin/env python3
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction
from PyQt5.QtGui import QIcon
app = QApplication([])
app.setQuitOnLastWindowClosed(False)
icon = QIcon.fromTheme("application")
tray = QSystemTrayIcon()
tray.setIcon(icon)
tray.setVisible(True)
menu = QMenu()
entries = ["One", "Two", "Three"]
for entry in entries:
action = QAction(entry)
menu.addAction(action)
action.triggered.connect(app.quit)
tray.setContextMenu(menu)
app.exec_()
Results in:
What happened to One and Two?
% python3 --version
Python 3.7.9
% pkg search Qt | grep py37-qt5-5
py37-qt5-5.15.2
martin | 2021-01-21 21:31:32 UTC | #2
Hi @probono
PyQt6 Crash Course — a new tutorial in your Inbox every day
Beginner-focused crash course explaining the basics with hands-on examples.
Normally these sorts of issues are the result of things going out of scope. In Python, unless you retain a reference to an object in the current scope it will be garbage collected. In your code, the action
object is being replaced on each iteration -- so on the second loop, the first action
object no longer has a reference anywhere, and is deleted. This in turn deletes the Qt object, and removes it from the menu.
All you need to do is keep a reference to each action
, for example in a list (here actions
).
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction
from PyQt5.QtGui import QIcon
app = QApplication([])
app.setQuitOnLastWindowClosed(False)
icon = QIcon.fromTheme("application")
tray = QSystemTrayIcon()
tray.setIcon(icon)
tray.setVisible(True)
menu = QMenu()
entries = ["One", "Two", "Three"]
actions = []
for entry in entries:
action = QAction(entry)
menu.addAction(action)
action.triggered.connect(app.quit)
actions.append(action)
tray.setContextMenu(menu)
app.exec_()
probono | 2021-01-21 21:32:24 UTC | #3
Sneaky! Thank you very much. With this trick it works.
Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!