Hiding main window on starting, showing later causes "QObject::setParent: Cannot set parent, new parent is in a different thread"
-
Hello. I am trying to start my app in the system tray only and then show the window later using global shortcuts.
Below is my sample code. I am using Python 3.10.0, PySide6 6.2.2.1, and a package for global shortcuts called "keyboard" which you can install using
pip install keyboard
.Qt Designer UI File (testui.py):
# -*- coding: utf-8 -*- ################################################################################ ## Form generated from reading UI file 'testui.ui' ## ## Created by: Qt User Interface Compiler version 6.2.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, QMetaObject, QObject, QPoint, QRect, QSize, QTime, QUrl, Qt) from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont, QFontDatabase, QGradient, QIcon, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QSizePolicy, QWidget) class Ui_MainWindow(object): def setupUi(self, MainWindow): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") MainWindow.resize(500, 300) self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) self.centralwidget.setSizePolicy(sizePolicy) self.gridLayout = QGridLayout(self.centralwidget) self.gridLayout.setObjectName(u"gridLayout") self.testLabel = QLabel(self.centralwidget) self.testLabel.setObjectName(u"testLabel") font = QFont() font.setFamilies([u"Segoe UI"]) font.setPointSize(22) self.testLabel.setFont(font) self.testLabel.setAlignment(Qt.AlignCenter) self.gridLayout.addWidget(self.testLabel, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) # setupUi def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None)) self.testLabel.setText(QCoreApplication.translate("MainWindow", u"TEST WINDOW", None)) # retranslateUi
Main.py File:
import sys import ctypes import PySide6 import keyboard from PySide6.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu from PySide6.QtGui import QIcon, QAction from testui import Ui_MainWindow class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) # Setup UI from Qt Designer generated file. self.ui = Ui_MainWindow() self.ui.setupUi(self) self.title = "Test Window" self.setWindowTitle(self.title) # Initial state of MainWindow. self.is_maximized = False # Show window here. Will be hidden later. self.show() def maximize_window(self): if self.is_maximized: return else: user32 = ctypes.WinDLL("user32") hwnd = user32.FindWindowW(None, self.title) user32.ShowWindow(hwnd, 9) self.showNormal() self.is_maximized = True if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() # Hide window after initializing. window.hide() # System tray. icon = QIcon("tray_icon.ico") system_tray = QSystemTrayIcon() system_tray.setIcon(icon) tray_menu = QMenu() tray_quit = QAction("Quit") tray_quit.triggered.connect(app.quit) tray_menu.addAction(tray_quit) system_tray.setContextMenu(tray_menu) system_tray.show() # Global Shortcuts keyboard.add_hotkey("ctrl+alt+t", lambda: window.maximize_window()) sys.exit(app.exec())
My assumption is there is a better way to start an application only in the system tray but I did not have any luck researching the topic or figuring out a different way to do so other than what I am currently doing. I first show the window as the class in created but then hide it right after. The global shortcut calls a method that uses ctypes to find the window, bring it to the front, and then call
show()
. This was the only way I could get it to work but it still gives the errorQObject::setParent: Cannot set parent, new parent is in a different thread
.When I researched into this error I mostly saw posts about calling GUI methods from outside the main GUI thread. I don't believe I am doing so unless my understanding of how the QMainWindow works with
show()
andhide()
. I was thinking maybe it was the global shortcut function that is calling the maximize_window() method outside the GUI thread but moving it inside the__init__()
of MainWindow did not resolve the issue. -
After beating my head against a wall for hours on end trying numerous things, I finally got it to work with no errors. Here is the updated code:
import keyboard import ctypes from PySide6.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu from PySide6.QtGui import QIcon, QAction from testui import Ui_MainWindow class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) # Setup UI from Qt Designer generated file. self.ui = Ui_MainWindow() self.ui.setupUi(self) self.title = "Test Window" self.setWindowTitle(self.title) # Initial state of MainWindow. self.is_maximized = False # Create QAction with a trigger for restoring window. restore_window = QAction("Restore Window") restore_window.triggered.connect(self.maximize_window) # Global shortcut triggers QAction. keyboard.add_hotkey("ctrl+alt+t", restore_window.trigger) def maximize_window(self): if self.is_maximized: print("Already maximized.") return else: self.show() self.is_maximized = True user32 = ctypes.WinDLL("user32") hwnd = user32.FindWindowW(None, self.title) user32.ShowWindow(hwnd, 9) def closeEvent(self, event): self.is_maximized = False return super().closeEvent(event) if __name__ == "__main__": app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) window = MainWindow() # Hide window after initializing. window.show() window.hide() # System tray. icon = QIcon("tray_icon.ico") system_tray = QSystemTrayIcon() system_tray.setIcon(icon) tray_menu = QMenu() tray_quit = QAction("Quit") tray_quit.triggered.connect(app.quit) tray_menu.addAction(tray_quit) system_tray.setContextMenu(tray_menu) system_tray.show() sys.exit(app.exec())
I created a QAction with a trigger and on press of the global shortcut, manually triggers the QAction. The trigger shows the window minimized (even those I'm not calling
showMinimized()
which is interesting). This is where ctypes comes in to bring it to the front.I've attempted to use Slots and Signals to get this to work and it did not so I'm not sure why a QAction trigger.
-
Hi,
On which OS are you running that code ?
On a side note, don't call show in the constructor of your widget. It's bad practice. You even call hide directly after the construction which you would not need to do in the first place. Widgets shall not be responsible for showing themselves upon construction, it's the responsibility of the code/object that will use them to do that at the right moment.
-
Hi,
On which OS are you running that code ?
On a side note, don't call show in the constructor of your widget. It's bad practice. You even call hide directly after the construction which you would not need to do in the first place. Widgets shall not be responsible for showing themselves upon construction, it's the responsibility of the code/object that will use them to do that at the right moment.
@SGaist OS is Windows 11. I'm not sure if you read my explanation of why I did that at the bottom of my code. It was the only way to get the window to pop back up. Otherwise, the window becomes unhidden (while remaining minimized) and the entire application freezes and crashes.
-
Did you already went through the system tray icon example ?
It does what you want to do or at least very closely.
What I would say is missing from your code is a call to QApplication::setQuitOnLastWindowClosed.
-
Did you already went through the system tray icon example ?
It does what you want to do or at least very closely.
What I would say is missing from your code is a call to QApplication::setQuitOnLastWindowClosed.
@SGaist I have not. I apologize but I'm not sure what that has to do with my problem. On run of my application, everything works fine. The window is hidden, the system tray is showing, and everything is running just fine. The problem comes once I try to show the window again and that error happens.
EDIT: I tried adding
app.setQuitOnLastWindowClosed(False)
anyways to see if it changed anything but it did not. Once I hit the Ctrl + Alt + T global shortcut the window becomes shown but I still get the errorQObject::setParent: Cannot set parent, new parent is in a different thread
.Here is my updated code:
import sys import ctypes import PySide6 import keyboard from PySide6.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu from PySide6.QtGui import QIcon, QAction from testui import Ui_MainWindow class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) # Setup UI from Qt Designer generated file. self.ui = Ui_MainWindow() self.ui.setupUi(self) self.title = "Test Window" self.setWindowTitle(self.title) # Initial state of MainWindow. self.is_maximized = False def maximize_window(self): if self.is_maximized: return else: self.show() self.is_maximized = True user32 = ctypes.WinDLL("user32") hwnd = user32.FindWindowW(None, self.title) user32.ShowWindow(hwnd, 9) if __name__ == "__main__": app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) window = MainWindow() # Hide window after initializing. window.showMinimized() window.hide() # System tray. icon = QIcon("tray_icon.ico") system_tray = QSystemTrayIcon() system_tray.setIcon(icon) tray_menu = QMenu() tray_quit = QAction("Quit") tray_quit.triggered.connect(app.quit) tray_menu.addAction(tray_quit) system_tray.setContextMenu(tray_menu) system_tray.show() # Global Shortcuts keyboard.add_hotkey("ctrl+alt+t", lambda: window.maximize_window()) sys.exit(app.exec())
show()
removed from constructor as specified.user32
section undermaximize_window()
method swapped to after showing the window (seems to work a little more fluid).window.showMinimized()
andwindow.hide()
added after window construction. Simply calling onlywindow.hide()
or nothing at all causes the application to freeze after using global shortcut to restore window. -
Did you already went through the system tray icon example ?
It does what you want to do or at least very closely.
What I would say is missing from your code is a call to QApplication::setQuitOnLastWindowClosed.
-
After beating my head against a wall for hours on end trying numerous things, I finally got it to work with no errors. Here is the updated code:
import keyboard import ctypes from PySide6.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu from PySide6.QtGui import QIcon, QAction from testui import Ui_MainWindow class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) # Setup UI from Qt Designer generated file. self.ui = Ui_MainWindow() self.ui.setupUi(self) self.title = "Test Window" self.setWindowTitle(self.title) # Initial state of MainWindow. self.is_maximized = False # Create QAction with a trigger for restoring window. restore_window = QAction("Restore Window") restore_window.triggered.connect(self.maximize_window) # Global shortcut triggers QAction. keyboard.add_hotkey("ctrl+alt+t", restore_window.trigger) def maximize_window(self): if self.is_maximized: print("Already maximized.") return else: self.show() self.is_maximized = True user32 = ctypes.WinDLL("user32") hwnd = user32.FindWindowW(None, self.title) user32.ShowWindow(hwnd, 9) def closeEvent(self, event): self.is_maximized = False return super().closeEvent(event) if __name__ == "__main__": app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) window = MainWindow() # Hide window after initializing. window.show() window.hide() # System tray. icon = QIcon("tray_icon.ico") system_tray = QSystemTrayIcon() system_tray.setIcon(icon) tray_menu = QMenu() tray_quit = QAction("Quit") tray_quit.triggered.connect(app.quit) tray_menu.addAction(tray_quit) system_tray.setContextMenu(tray_menu) system_tray.show() sys.exit(app.exec())
I created a QAction with a trigger and on press of the global shortcut, manually triggers the QAction. The trigger shows the window minimized (even those I'm not calling
showMinimized()
which is interesting). This is where ctypes comes in to bring it to the front.I've attempted to use Slots and Signals to get this to work and it did not so I'm not sure why a QAction trigger.
-
After beating my head against a wall for hours on end trying numerous things, I finally got it to work with no errors. Here is the updated code:
import keyboard import ctypes from PySide6.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu from PySide6.QtGui import QIcon, QAction from testui import Ui_MainWindow class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) # Setup UI from Qt Designer generated file. self.ui = Ui_MainWindow() self.ui.setupUi(self) self.title = "Test Window" self.setWindowTitle(self.title) # Initial state of MainWindow. self.is_maximized = False # Create QAction with a trigger for restoring window. restore_window = QAction("Restore Window") restore_window.triggered.connect(self.maximize_window) # Global shortcut triggers QAction. keyboard.add_hotkey("ctrl+alt+t", restore_window.trigger) def maximize_window(self): if self.is_maximized: print("Already maximized.") return else: self.show() self.is_maximized = True user32 = ctypes.WinDLL("user32") hwnd = user32.FindWindowW(None, self.title) user32.ShowWindow(hwnd, 9) def closeEvent(self, event): self.is_maximized = False return super().closeEvent(event) if __name__ == "__main__": app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) window = MainWindow() # Hide window after initializing. window.show() window.hide() # System tray. icon = QIcon("tray_icon.ico") system_tray = QSystemTrayIcon() system_tray.setIcon(icon) tray_menu = QMenu() tray_quit = QAction("Quit") tray_quit.triggered.connect(app.quit) tray_menu.addAction(tray_quit) system_tray.setContextMenu(tray_menu) system_tray.show() sys.exit(app.exec())
I created a QAction with a trigger and on press of the global shortcut, manually triggers the QAction. The trigger shows the window minimized (even those I'm not calling
showMinimized()
which is interesting). This is where ctypes comes in to bring it to the front.I've attempted to use Slots and Signals to get this to work and it did not so I'm not sure why a QAction trigger.
@donbfry said in Hiding main window on starting, showing later causes "QObject::setParent: Cannot set parent, new parent is in a different thread":
# Hide window after initializing. window.showMinimized() window.hide()
You do call showMinimized.
In any case, glad you found a solution and thanks for sharing it.
-
@donbfry said in Hiding main window on starting, showing later causes "QObject::setParent: Cannot set parent, new parent is in a different thread":
# Hide window after initializing. window.showMinimized() window.hide()
You do call showMinimized.
In any case, glad you found a solution and thanks for sharing it.