QSystemTrayIcon Docs
Ive been looking through the documentation for this widget. I have a working icon and a context menu, however i would like to be able to also show the context menu when someone left clicks. Is this possible?
Edit:
This is for use in Windows 10.
You could connect the QSystemTrayIcon.activated signal to call QMenu.popup when the activation reason is QSystemTrayIcon.Trigger.
import sys
from PySide2.QtWidgets import *
from PySide2.QtGui import *
class TrayIcon(QSystemTrayIcon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.activated.connect(self.showMenuOnTrigger)
def showMenuOnTrigger(self, reason):
if reason == QSystemTrayIcon.Trigger:
self.contextMenu().popup(QCursor.pos())
if __name__ == '__main__':
app = QApplication(sys.argv)
tray = TrayIcon(QIcon('icon.png'))
menu = QMenu()
menu.addAction('Action 1')
menu.addAction('Action 2')
tray.setContextMenu(menu)
tray.show()
sys.exit(app.exec_())
Related
Normally in Qt applications you would use this at the start and end of your code:
app = QtWidgets.QApplication(sys.argv)
...
app.exec_()
But in Maya, you don't use this because Qt runs on the Maya application it self. I'm sure this works the same for many other applications as well if you don't know what Maya is. That said, my code looks like this:
import sys
from PySide2 import QtWidgets, QtGui, QtCore
class Test():
def __init__(self):
self.open_qt()
def open_qt(self):
# app = QtWidgets.QApplication(sys.argv) # Don't need this in Maya
self.window = QtWidgets.QWidget() # I tried QDialog also
btn = QtWidgets.QPushButton("press me")
btn.clicked.connect(self.login)
lay = QtWidgets.QVBoxLayout()
lay.addWidget(btn)
self.window.setLayout(lay)
self.window.show()
# app.exec_() # Don't need this in Maya
def login(self):
print("logged in!")
print("before")
temp = Test()
print("after")
But running this in Maya I get this result:
before
after
logged in!
But I need it to be:
before
logged in!
after
If you run this code outside of Maya (and you use those two commented out lines) then you get the correct result (block above here).
How can I get the Maya Qt to also wait correctly like it would if you used QtWidgets.QApplication(sys.argv)?
A QDialog might be more well suited for your needs, as it runs its own event loop that won't block the program, while waiting for the dialog to be completed.
The important thing to do is to call of its exec_() and call accept() when needed.
from PySide2 import QtWidgets, QtGui, QtCore
class Test(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
layout = QtWidgets.QVBoxLayout(self)
self.spinBox = QtWidgets.QSpinBox()
layout.addWidget(self.spinBox)
btn = QtWidgets.QPushButton("press me")
layout.addWidget(btn)
btn.clicked.connect(self.login)
def login(self):
print("logged in!")
self.accept()
dialog = Test()
if dialog.exec_():
print(dialog.spinBox.value())
I don't have Maya, but according to this answer you can get its main window using the maya.OpenMayaUI module and shiboken's wrapInstance().
I'm trying to get a QTreeView to allow the user to edit file names from a QFileSystemModel. However Qt just prints:
edit: editing failed
I get the same result with PySide and PyQt.
Opening the editor with openPersistenEditor() works, but I would prefer to use the build-in mechanism.
import sys
from PyQt4.QtGui import QTreeView, QFileSystemModel, QApplication
class TestView(QTreeView):
def __init__(self, directory, *args, **kwargs):
super(TestView, self).__init__(*args, **kwargs)
self.file_system_model = QFileSystemModel()
self.file_system_model.setRootPath(directory)
index = self.file_system_model.index(directory)
self.setModel(self.file_system_model)
self.setRootIndex(index)
self.activated.connect(self._on_edit)
def _on_edit(self, index):
# self.closePersistentEditor(index)
# app.processEvents()
self.edit(self.currentIndex())
# self.openPersistentEditor(index)
if __name__ == '__main__':
app = QApplication([])
directory = r'c:/'
dialog = TestView(directory)
dialog.show()
sys.exit(app.exec_())
The model is read-only by default, so you need to add:
self.file_system_model.setReadOnly(False)
In the below mentioned example when I click on 'Help' submenu under 'View' menu multiple times its creating multiple windows. Can anyone tell me how to resolve this issue?
import sys
from PySide import Qt Gui
from PySide.QtCore import Qt
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.menu_bar()
def menu_bar(self):
helpAction = QtGui.QAction('&Help', self)
helpAction.setShortcut('Ctrl+H')
helpAction.triggered.connect(self.add_helpWindow)
menu = self.menuBar().addMenu('View')
menu.addAction(helpAction)
def add_helpWindow(self):
window = QtGui.QMainWindow(self)
window.setWindowTitle('New Window')
window.show()
if __name__ == '__main__':
import sys
app=QtGui.QApplication.instance()
if not app:
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(300, 300)
window.show()
sys.exit(app.exec_())
You help window is just a QMainWindow, which is not modal and there are no restrictions on the number that can exist. Hence why if you select the help option multiple times, you get multiple windows.
You likely want to use a QMessageBox which has its modal property set. While there is nothing forcing only one dialog to exist at a time, being modal means that the use can only interact with that window so long as it is open. Example:
from Pyside.QtGui import QMessageBox
def add_helpWindow(self):
help_dialog = QMessageBox.information(self, 'Help', 'Some Help Text Here')
help_dialog.setModal(True)
return help_dialog.exec_()
You can also get a more generic dialog box using QDialog, which is the parent class of QMessageBox.
If that's not the behavior you want, you'll need to manually track whether the user has opened that window before, and then connect a signal that is emitted when the user closes the help window to a slot that reset the existence tracker. Here is an example using a non-modal QDialog:
from Pyside.QtGui import QDialog
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.menu_bar()
self.help_open = False # Tracks if the help dialog is already open
def help_closed(self):
self.help_open = False
...
def add_helpWindow(self):
if not self.help_open:
self.help_open = True
help_dialog = QDialog(self)
# Any other setup code here
help_dialog.setModal(False)
help_dialog.accepted.connect(self.help_closed)
help_dialog.rejected.connect(self.help_closed)
help_dialog.show()
I'm having trouble understanding the shortcut functionality of a QAction added to a QMenu. Lets start with an example:
from PyQt4 import QtCore, QtGui
import sys
class TestApp(QtGui.QMainWindow):
def __init__(self, *args):
super(TestApp, self).__init__(*args)
#create contex menu
self.menu = QtGui.QMenu(self)
self.menu.addAction("testEntry", self.action, "CTRL+T")
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.execContextMenu)
def execContextMenu(self, point):
self.menu.exec_(self.mapToGlobal(point))
def action(self):
print 'action called!'
app = QtGui.QApplication(sys.argv)
win = TestApp()
win.show()
app.exec_()
I would expect action() to be called either when the users presses "Ctrl+T" or when "Ctrl+T" is pressed after the context menu was invoked. But nothing happens when I press the shortcut. However when I add the QAction to the TestApp
act = self.menu.addAction("testEntry", self.action, "CTRL+T")
self.addAction(act)
it works (although it doesn't when the context menu is open). So what am I doing wrong?
I'm using PyQt4 on an OSX 10.6
Thanks for any help ;)
On Mac Qt maps the control key to Meta rather than Ctrl
I'm a beginner in PyQt. I was trying to create a simple app to try some of the toolkit's many features. My question is, how can I hide the app icon from the taskbar?
I don't want the user to be able to see the icon in taskbar and to minimize it using this icon. Is there any window flags that I can use to achieve this?
This should do the trick:
myApp.setWindowFlags(QtCore.Qt.Tool)
This drove me nuts for days. Complete app code to implement below.
Key bits:
override closeEvent(), enabling it to do either of just hiding window
or true exit
create some facility for user to choose either hide or
exit behavior
don't show() main window on instantiation, just exec_() the App
import sys
from PyQt4.QtGui import QAction, QApplication, QFrame, QIcon, \
QMainWindow, QMenu, QSystemTrayIcon
from PyQt4.QtCore import SIGNAL
class MyApp(QMainWindow):
def __init__(self, parent, title):
super(QMainWindow, self).__init__(parent)
self.exitOnClose = False
exit = QAction(QIcon(), "Exit", self)
self.connect(exit, SIGNAL("triggered()"), self.exitEvent)
self.trayIcon = QSystemTrayIcon(QIcon(), self)
menu = QMenu(self)
menu.addAction(exit)
self.trayIcon.setContextMenu(menu)
self.connect(self.trayIcon, \
SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), \
self.trayIconActivated)
self.trayIcon.show()
self.trayIcon.showMessage("MyApp is running!", "Click to open window\nRight click for menu" )
def trayIconActivated(self, reason):
if reason == QSystemTrayIcon.Context:
self.trayIcon.contextMenu().show()
elif reason == QSystemTrayIcon.Trigger:
self.show()
self.raise_()
def closeEvent(self, event):
if self.exitOnClose:
self.trayIcon.hide()
del self.trayIcon
event.accept()
else:
self.hide()
event.setAccepted(True)
event.ignore()
def exitEvent(self):
self.exitOnClose = True
self.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
myapp = MyApp(None, "My System Tray App")
app.exec_()
Adapted from this thread:
import sys
from PyQt4.QtGui import *
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QWidget()
mainWindow = QMainWindow(widget)
mainWindow.show()
sys.exit(app.exec_())
I wouldn't recommend trying to hide an application's taskbar presence, especially if the application is visible. If you are only trying to prevent minimizing from the taskbar then you can achieve this by creating your top level widget with the following window flags like this:
QWidget *mainWindow = new QWidget(0, Qt::CustomizeWindowHint
| Qt::WindowTitleHint | Qt::WindowSystemMenuHint
| Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint);
If you don't want a maximize flag, you can leave that one out of the list too.
The various window flags that Qt can use are documented here (Qt::WindowFlags).
If you are on Ubuntu with Unity and want to hide an application's icon from the launcher on the left-hand-side, you will probably need Qt.SplashScreen. This worked for me but I don't remember if I also needed Qt.Tool, which is enough on Windows. For the SplashScreen attempt you may have to reimplement the resize functionality as it disables this feature of a QStatusBar (that has a SizeGrip) for example.
Here is a little example to try out window flags.
Just initialise your main window like this self.setWindowFlags(Qt.ToolTip)