QPixmap: Must construct a QGuiApplication before a QPixmap - Open an About dialog from tray icon

Hi,

in my TrayIcon App I’d like to have an about dialog which opens from the right click menu.

What I do is this:

class AboutWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("About")
        layout = QVBoxLayout()
        label = QLabel("IP Monitor 0.1")

        image = pixmap.toImage()
        font = label.font()
        font.setPointSize(16)
        label.setFont(font)
        label.setPicture(self.pixmap)
        layout.addWidget(label)
        okBtn = QPushButton(text="Ok", parent=self)
        okBtn.clicked.connect(self.ok_btn_clicked)
        layout.addWidget(okBtn)
        self.setLayout(layout)

    def ok_btn_clicked(self, is_checked):
        self.close()

class MyApp:
    def __init__(self):
        self.icon = None
        self.app = None

    def create_tray_icon(self):
    # Create the application instance
        self.app = QApplication(sys.argv)
        self.icon = QSystemTrayIcon()
        self.icon.setIcon(QIcon("MyIcon.ico"))
        self.icon.setToolTip(f"Row1\nRow2\n"
                             f"Row3")

    # Create a context menu for the tray icon
        menu = QMenu()

    # Add an "About" option to the menu
        self.about_action = QAction("About")
        self.about_action.triggered.connect(self.about_action_clicked)
        self.menu.addAction(self.about_action)

    # Add a "Quit" option to the menu
        quit_action = QAction("Quit")
        quit_action.triggered.connect(self.app.quit)
        menu.addAction(quit_action)

    # Set the context menu for the tray icon
        self.icon.setContextMenu(menu)

    # Show the tray icon
        self.icon.show()

    def about_action_clicked(self, is_checked):
        self.w = AboutWindow()
        self.w.show()

The problem is that I alsways get the error QPixmap: Must construct a QGuiApplication before a QPixmap.

What am I doing wrong?

Thanks

@sandman42
what is missing (but probably just another problem): self.app.exec() you never start event loop for your QApplication, and create_tray_icon() is never call as well

BTW, it always better post working code (or kind-of) or minimal code where you can show problem… from where you get pixmap in image = pixmap.toImage()

Hi,

thanks for the answer. I’ve just tried to be concise.
Anyway, here’s a running example:

import time
import os
from PyQt6.QtGui import QPixmap
from PyQt5.QtWidgets import (
    QApplication, 
    QLabel,
    QSystemTrayIcon, 
    QMenu, 
    QAction, 
    QPushButton,
    QWidget, 
    QVBoxLayout)

from PyQt5.QtGui import QIcon
import sys

basedir = os.path.dirname(__file__)

class AboutWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("About")
        layout = QVBoxLayout()
        label = QLabel("MyTest 0.1")

        font = label.font()
        font.setPointSize(16)
        label.setFont(font)
        label.setPicture(QPixmap(os.path.join(basedir,"otje.jpg")))
        layout.addWidget(label)
        okBtn = QPushButton(text="Ok", parent=self)
        okBtn.clicked.connect(self.ok_btn_clicked)
        layout.addWidget(okBtn)
        self.setLayout(layout)

    def ok_btn_clicked(self, is_checked):
        self.close()


class MyTest:
    def __init__(self):
        self.icon = None
        self.app = None

    def create_tray_icon(self):
        # Create the application instance
        self.app = QApplication(sys.argv)
        self.app.setQuitOnLastWindowClosed(False)
        self.icon = QSystemTrayIcon()
        self.icon.setIcon(QIcon("MyTest.ico"))
        # Create a context menu for the tray icon
        self.menu = QMenu()

        # Add an "About" option to the menu
        self.about_action = QAction("About")
        self.about_action.triggered.connect(self.about_action_clicked)
        self.menu.addAction(self.about_action)

        # Add a "Quit" option to the menu
        self.quit_action = QAction("Quit")
        self.quit_action.triggered.connect(self.app.quit)
        self.menu.addAction(self.quit_action)


        # Set the context menu for the tray icon
        self.icon.setContextMenu(self.menu)

        # Show the tray icon
        self.icon.show()

    def about_action_clicked(self, is_checked):
        self.w = AboutWindow()
        self.w.show()

    

    def myTest_ips(self):
        while True:
            print("Hello")

            time.sleep(60)  # Check every 60 seconds


if __name__ == "__main__":
    myTest = MyTest()
    myTest.create_tray_icon()

    # Start monitoring IPs in a separate thread
    from threading import Thread
    myTest_thread = Thread(target=myTest.myTest_ips)
    myTest_thread.daemon = True
    myTest_thread.start()

    sys.exit(myTest.app.exec_())

If you right-click on the tray icon, About, you’ll get a “QPixmap: Must construct a QGuiApplication before a QPixmap” error

Thanks

You meant PyQt5 or PyQt6 :wink: ?

PyQt6.

I’ve modified imports in this way:

from PyQt6.QtGui import QPixmap, QAction
from PyQt6.QtWidgets import (
    QApplication, 
    QLabel,
    QSystemTrayIcon, 
    QMenu, 
    QPushButton,
    QWidget, 
    QVBoxLayout)

Right now I get the error when I run the program

I made some small changes:

  • AboutWindow is now QDialog
  • use .open() instead of .show() in about_action_clicked
  • add label.setScaledContents(True) to resize label
  • add showEvent() - you can do something when dialog will be paint on screen
import time
import os
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import (
    QApplication,
    QLabel,
    QSystemTrayIcon,
    QMenu,
    QAction,
    QPushButton,
    QDialog,
    QVBoxLayout)

import sys

basedir = os.path.dirname(__file__)


class AboutWindow(QDialog):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("About")
        layout = QVBoxLayout()
        label = QLabel("MyTest 0.1")

        font = label.font()
        font.setPointSize(16)
        label.setFont(font)
        label.setPicture(QPixmap(os.path.join(basedir,"otje.jpg")))
        label.setScaledContents(True)
        layout.addWidget(label)
        okBtn = QPushButton(text="Ok", parent=self)
        okBtn.clicked.connect(self.ok_btn_clicked)
        layout.addWidget(okBtn)
        self.setLayout(layout)

    def showEvent(self, event):
        super().showEvent(event)
        print("AboutWindow showEvent")

    def ok_btn_clicked(self, is_checked):
        print("Ok clicked")
        self.close()


class MyTest:
    def __init__(self):
        self.icon = None
        self.app = None

    def create_tray_icon(self):
        # Create the application instance
        self.app = QApplication(sys.argv)
        self.app.setQuitOnLastWindowClosed(False)
        self.icon = QSystemTrayIcon()
        self.icon.setIcon(QIcon("MyTest.ico"))
        # Create a context menu for the tray icon
        self.menu = QMenu()

        # Add an "About" option to the menu
        self.about_action = QAction("About")
        self.about_action.triggered.connect(self.about_action_clicked)
        self.menu.addAction(self.about_action)

        # Add a "Quit" option to the menu
        self.quit_action = QAction("Quit")
        self.quit_action.triggered.connect(self.app.quit)
        self.menu.addAction(self.quit_action)

        # Set the context menu for the tray icon
        self.icon.setContextMenu(self.menu)

        # Show the tray icon
        self.icon.show()

    def about_action_clicked(self, is_checked):
        self.w = AboutWindow()
        self.w.open()

    def myTest_ips(self):
        while True:
            print("Hello")

            time.sleep(60)  # Check every 60 seconds


if __name__ == "__main__":
    myTest = MyTest()
    myTest.create_tray_icon()

    # Start monitoring IPs in a separate thread
    from threading import Thread

    myTest_thread = Thread(target=myTest.myTest_ips)
    myTest_thread.daemon = True
    myTest_thread.start()

    sys.exit(myTest.app.exec_())

and PyQt6 with minor changes:

import time
import os
from PyQt6.QtGui import QPixmap, QIcon, QAction
from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QSystemTrayIcon,
    QMenu,
    QPushButton,
    QDialog,
    QVBoxLayout)

import sys

basedir = os.path.dirname(__file__)


class AboutWindow(QDialog):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("About")
        layout = QVBoxLayout()
        label = QLabel("MyTest 0.1")

        font = label.font()
        font.setPointSize(16)
        label.setFont(font)
        label.setPixmap(QPixmap(os.path.join(basedir,"otje.jpg")))
        label.setScaledContents(True)
        layout.addWidget(label)
        okBtn = QPushButton(text="Ok", parent=self)
        okBtn.clicked.connect(self.ok_btn_clicked)
        layout.addWidget(okBtn)
        self.setLayout(layout)

    def showEvent(self, event) -> None:
        super().showEvent(event)
        print("AboutWindow showEvent")

    def ok_btn_clicked(self, is_checked):
        print("Ok clicked")
        self.close()


class MyTest:
    def __init__(self):
        self.icon = None
        self.app = None

    def create_tray_icon(self):
        # Create the application instance
        self.app = QApplication(sys.argv)
        self.app.setQuitOnLastWindowClosed(False)
        self.icon = QSystemTrayIcon()
        self.icon.setIcon(QIcon("MyTest.ico"))
        # Create a context menu for the tray icon
        self.menu = QMenu()

        # Add an "About" option to the menu
        self.about_action = QAction("About")
        self.about_action.triggered.connect(self.about_action_clicked)
        self.menu.addAction(self.about_action)

        # Add a "Quit" option to the menu
        self.quit_action = QAction("Quit")
        self.quit_action.triggered.connect(self.app.quit)
        self.menu.addAction(self.quit_action)

        # Set the context menu for the tray icon
        self.icon.setContextMenu(self.menu)

        # Show the tray icon
        self.icon.show()

    def about_action_clicked(self, is_checked):
        self.w = AboutWindow()
        self.w.open()

    def myTest_ips(self):
        while True:
            print("Hello")

            time.sleep(60)  # Check every 60 seconds


if __name__ == "__main__":
    myTest = MyTest()
    myTest.create_tray_icon()

    # Start monitoring IPs in a separate thread
    from threading import Thread

    myTest_thread = Thread(target=myTest.myTest_ips)
    myTest_thread.daemon = True
    myTest_thread.start()

    sys.exit(myTest.app.exec())

Ok. Now works. Thanks a lot!

1 Like