QSystemTrayIcon context menu doesn't show when subclassed - pyqt

I'm running python 3.9.7 on windows 10. Please consider the following code (note you will need icon.png to run)
from PyQt6.QtGui import QAction, QIcon
from PyQt6.QtWidgets import QSystemTrayIcon, QApplication, QMenu
app = QApplication([])
app.setQuitOnLastWindowClosed(False)
'''Create the system tray'''
tray = QSystemTrayIcon()
tray.setIcon(QIcon("icon.png"))
tray.show()
'''Add a menu with a quit action'''
menu = QMenu()
quit = QAction("Quit")
quit.triggered.connect(app.quit)
menu.addAction(quit)
'''Give the menu to the tray'''
tray.setContextMenu(menu)
'''Lets see where the menu is drawn'''
menu.aboutToShow.connect(lambda: print(menu.pos()))
app.exec()
The above works as expected on my system. However, when I subclass QSystemTrayIcon like so:
from PyQt6.QtGui import QAction, QIcon
from PyQt6.QtWidgets import QSystemTrayIcon, QApplication, QMenu
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon: QIcon, app: QApplication, *args, **kwargs):
super().__init__(*args, **kwargs)
'''setup'''
self.setIcon(icon)
self.show()
'''Add a menu with a quit action'''
menu = QMenu()
quit = QAction("Quit")
quit.triggered.connect(app.quit)
menu.addAction(quit)
'''Give the menu to the tray'''
self.setContextMenu(menu)
'''Lets see where the menu is drawn'''
menu.aboutToShow.connect(lambda: print(menu.pos()))
def main():
'''Define app'''
app = QApplication([])
app.setQuitOnLastWindowClosed(False)
'''System tray'''
tray_icon = SystemTrayIcon(QIcon('icon.png'), app)
app.exec()
if __name__ == '__main__':
main()
The menu does not show up. The lambda print positions are interesting, on my screen the first code returns something like: PyQt6.QtCore.QPoint(1686, 1036) whereas the second code returns PyQt6.QtCore.QPoint(1686, 1064), which is about 30 pixels lower. My screen resolution is 1080p so it's not off the screen as such but does indicate some different behaviour here. Any ideas why the menu doesn't display in the second example?

A reference to the QAction needs to be kept. This works:
from PyQt6.QtGui import QAction, QIcon
from PyQt6.QtWidgets import QSystemTrayIcon, QApplication, QMenu
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon: QIcon, app: QApplication, *args, **kwargs):
super().__init__(*args, **kwargs)
'''setup'''
self.setIcon(icon)
self.show()
'''Add a menu with a quit action'''
menu = QMenu()
self.quit = QAction("Quit")
self.quit.triggered.connect(app.quit)
menu.addAction(self.quit)
'''Give the menu to the tray'''
self.setContextMenu(menu)
'''Lets see where the menu is drawn'''
menu.aboutToShow.connect(lambda: print(menu.pos()))
def main():
'''Define app'''
app = QApplication([])
app.setQuitOnLastWindowClosed(False)
'''System tray'''
tray_icon = SystemTrayIcon(QIcon('icon.png'), app)
app.exec()
if __name__ == '__main__':
main()

Related

Issue in Pyqt with next and previous Pages

so I'm currently working on a little project but I have an issue and all what I've tried did not work. I have 2 files :
Page1test :
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel
import sys
from page2test import Page2
class Page1(QWidget):
def __init__(self):
super(Page1, self).__init__()
self.setWindowTitle("Page 1")
label1 = QLabel(self)
label1.setText("\n PAGE 1")
self.btn_inMyApp = QPushButton ('Next page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage1_OpenPage2)
self.show()
def closePage1_OpenPage2(self):
self.Open = Page2()
self.Open.showMaximized()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Page1()
window.showMaximized()
sys.exit(app.exec_())
And page2test :
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel
from Page1test import Page1
class Page2(QWidget):
def __init__(self):
super(Page2, self).__init__()
self.setWindowTitle("Test window principale")
label1 = QLabel(self)
label1.setText("\n Page2")
self.btn_inMyApp = QPushButton ('previous Page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage2_OpenPage1)
self.show()
def closePage2_OpenPage1(self):
self.Open = Page1()
self.Open.showMaximized()
self.close()
I run the code of Page1test : empty window with just a Qpushbutton "Next Page", goal : we click on it and it open Page 2 (and close Page 1). And, when we are in Page 2, we have A Qpushbutton with "Previous Page" and when we click on it, it opens page 1, and close Page 2. Like a loop.
But, when I run the code, it returns an error :
cannot import name 'Page2' from partially initialized module 'page2test' (most likely due to a circular import)
and I have no idea how to fix it...
If someone had an idea, it would be really helpful.
So, I've finally found the solution, that was not so difficult in the end. Here it is (if it can help someone) :
Instead of making 2 files, I've done just 1 file. Here is the code :
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel, QMainWindow
import sys
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setWindowTitle("Page 1")
label1 = QLabel(self)
label1.setText("\n PAGE 1")
self.btn_inMyApp = QPushButton ('Page suivante', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage1_OpenPage2)
self.show()
def btn1_onClicked(self):
pass
def closePage1_OpenPage2(self):
self.Open = NewApp()
self.Open.showMaximized()
self.close()
class NewApp(QMainWindow):
def __init__(self):
super(NewApp, self).__init__()
self.setWindowTitle("Test window principale")
label1 = QLabel(self)
label1.setText("\n Page2")
self.btn_inMyApp = QPushButton ('previous Page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage2_OpenPage1)
self.show()
def closePage2_OpenPage1(self):
self.Open = MyApp()
self.Open.showMaximized()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MyApp()
window.showMaximized()
sys.exit(app.exec_())
```

QMainWindow mouseDoubleClickEvent override not working

I'm using the below code to try and capture double clicks within the main window"
import sys
from PySide6 import QtCore
from PySide6.QtWidgets import (
QApplication,
QGridLayout,
QMainWindow,
QSplitter,
QTreeView,
QWidget,
)
from PySide6.QtWebEngineWidgets import QWebEngineView
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.windowLayout = QGridLayout()
self.splitter = QSplitter()
self.webView = QWebEngineView()
self.webView.setUrl(QtCore.QUrl("http://127.0.0.1:8080"))
self.tree = QTreeView()
self.splitter.addWidget(self.webView)
self.splitter.addWidget(self.tree)
self.windowLayout.addWidget(self.splitter)
self.mainWidget = QWidget()
self.mainWidget.setLayout(self.windowLayout)
self.setCentralWidget(self.mainWidget)
def mouseDoubleClickEvent(self, e):
print('test')
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.resize(1500, 1000)
window.show()
app.exec()
And I get the following console output:
qt.pointer.dispatch: delivering touch release to same window QWindow(0x0) not QWidgetWindow(0x600003bb8e40, name="MainWindowClassWindow")
qt.pointer.dispatch: skipping QEventPoint(id=1 ts=0 pos=0,0 scn=749.346,451.159 gbl=749.346,451.159 Released ellipse=(1x1 ∡ 0) vel=0,0 press=-749.346,-451.159 last=-749.346,-451.159 Δ 749.346,451.159) : no target window
however, when I comment out self.setCentralWidget(self.mainWidget) I obviously don't see anything, but the double click triggers. Any suggestions?
EDIT: update to min repro, also note that clicking anywhere not inside the QWebEngineView correctly results in mouseDoubleClickEvent triggering. However, the above error results when I click in the QWebEngineView, and nothing happens when I click inside the QTreeView
As per musicamante's very useful comments (see original post), the solution I ended up using was
...
self.tree.doubleClicked.connect(self.treeDoubleClicked)
...
def treeDoubleClicked(self, index):
self.selectedFilepath = index.model().filePath(index)

Entering fullscreen mode hides the icon from taskbar and the application runs from background

When I made a window go into fullscreen mode, the application icon was hidden from the taskbar (after doing alt-tab). It also didn't appear in the alt+tab windows. It only showed the other windows that I had open.
When I checked task manager, the python process was under the background processes category. I couldn't switch back to the window.
How can I stop the application from running in the background when I enter fullscreen mode?
My code:
import sys
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QShortcut
from PyQt5.QtCore import Qt
# Subclass QMainWindow to customise your application's main window
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("Hello fullscreen world")
label = QLabel("Hello fullscreen world")
# The `Qt` namespace has a lot of attributes to customise
# widgets. See: http://doc.qt.io/qt-5/qt.html
label.setAlignment(Qt.AlignCenter)
# Set the central widget of the Window. Widget will expand
# to take up all the space in the window by default.
self.setCentralWidget(label)
self.shortcut_close_window = QShortcut(QKeySequence('F11'), self)
self.shortcut_close_window.activated.connect(self.goFullscreen)
def goFullscreen(self):
if self.isFullScreen():
self.setWindowFlags(self._flags)
self.showNormal()
else:
self._flags = self.windowFlags()
self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowType_Mask)
self.showFullScreen()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Note: even terminating the process using ctrl+c or closing command prompt doesn't work, only task manager does.
I don't know why you are using self.setWindowFlags in goFullscreen() function. It is unnecessary.
def goFullscreen(self):
if self.isFullScreen():
#self.setWindowFlags(self._flags)
self.showNormal()
else:
#self._flags = self.windowFlags()
#self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowType_Mask)
self.showFullScreen()
Btw, the reason is that you use the Qt.WindowType_Mask flag.
WindowType_Mask: A mask for extracting the window type part of the
window flags.
~Window Flags~
I had no idea that there was such a strange bug in Ubuntu. So all you have to do is write your own FullScreen method:
(I don't use ubuntu. So I don't know if you will run into a bug again.)
import sys
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QShortcut
from PyQt5.QtCore import Qt
# Subclass QMainWindow to customise your application's main window
class MainWindow(QMainWindow):
isfullscreen = False
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("Hello fullscreen world")
self._flags = self.windowFlags()
self._geometry = ((self.screen().size().width() / 2) - (self.width() / 2),
(self.screen().size().height() / 2) - (self.height() / 2), 600, 400)
self.setGeometry(*self._geometry)
label = QLabel("Hello fullscreen world")
# The `Qt` namespace has a lot of attributes to customise
# widgets. See: http://doc.qt.io/qt-5/qt.html
label.setAlignment(Qt.AlignCenter)
# Set the central widget of the Window. Widget will expand
# to take up all the space in the window by default.
self.setCentralWidget(label)
self.shortcut_close_window = QShortcut(QKeySequence('F11'), self)
self.shortcut_close_window.activated.connect(self.goShowFullScreen)
self._geometry = self.geometry()
def goShowFullScreen(self):
if not self.isfullscreen:
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setGeometry(self.screen().geometry())
self.show()
self.isfullscreen = True
else:
self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
self.setWindowFlags(self.windowFlags() & ~Qt.FramelessWindowHint)
self.setGeometry(self._geometry)
self.show()
self.isfullscreen = False
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

Cannot exit fullscreen mode

I was just testing the fullscreen mode, but I can't exit fullscreen.
How do you exit fullscreen?
My code:
import sys
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QShortcut
from PyQt5.QtCore import Qt
# Subclass QMainWindow to customise your application's main window
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("Hello fullscreen world")
label = QLabel("Hello fullscreen world")
# The `Qt` namespace has a lot of attributes to customise
# widgets. See: http://doc.qt.io/qt-5/qt.html
label.setAlignment(Qt.AlignCenter)
# Set the central widget of the Window. Widget will expand
# to take up all the space in the window by default.
self.setCentralWidget(label)
self.shortcut_close_window = QShortcut(QKeySequence('F11'), self)
self.shortcut_close_window.activated.connect(self.goFullscreen)
def goFullscreen(self):
if self.isFullScreen():
self.exitFullScreen()
else:
self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowType_Mask)
self.showFullScreen()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
If I run that, it gives the error: AttributeError: 'MainWindow' object has no attribute 'exitFullScreen'
I also tried changing self.exitFullScreen() to
self.hide()
self.show()
but that just hides the window and shows it again in fullscreen mode.
I also tried changing it to self.showMaximized(), but that doesn't work either.
From the docs on showFullScreen: "To return from full-screen mode, call showNormal()."
E.g.:
def goFullscreen(self):
if self.isFullScreen():
self.showNormal()
else:
...
You should remember to restore the window flags when restoring from full screen mode, e.g.:
def goFullscreen(self):
if self.isFullScreen():
self.setWindowFlags(self._flags)
self.showNormal()
else:
self._flags = self.windowFlags()
self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowType_Mask)
self.showFullScreen()

PyQt5 shortcut in menubar is not working

I'm trying to create a simple UI that has the functionality to close when I use the shortcut Ctrl+Q or when I use the Exit into the File menu, to make it clearer I've created a fairly simple UI that goes with the code:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, qApp
class MGen(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
exit_action = QAction('Exit', self)
exit_action.setShortcut('Ctrl+Q')
exit_action.triggered.connect(qApp.quit)
menu_bar = self.menuBar()
file_menu = menu_bar.addMenu('&File')
file_menu.addAction(exit_action)
self.setGeometry(0, 0, 500, 500)
self.setWindowTitle('MGen')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
mgen = MGen()
sys.exit(app.exec_())
Could anyone point what I'm doing wrong?

Resources