QSystemTrayIcon example

Heads up! You've already completed this tutorial.

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.

python
#!/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:

image|257x103

What happened to One and Two?

python
% 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

Create GUI Applications with Python & Qt6 by Martin Fitzpatrick — (PyQt6 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

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).

PyQt/PySide 1:1 Coaching with Martin Fitzpatrick — Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.

Book Now 60 mins ($195)

python
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.

The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]


Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak
Martin Fitzpatrick

QSystemTrayIcon example was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.