PyQt: Mostra menu in un'applicazione vassoio di sistema
-
23-08-2019 - |
Domanda
Prima di tutto, io sono un programmatore C esperto ma nuovo a Python. Voglio creare una semplice applicazione in Python usando PyQt. Immaginiamo questa applicazione è semplice come quando viene eseguito deve mettere un'icona nella barra di sistema e ha offrire un'opzione nel suo menu per uscire dall'applicazione.
Questo codice funziona, mostra il menu (non collegare l'azione di uscita e così via per mantenere le cose semplici)
import sys
from PyQt4 import QtGui
def main():
app = QtGui.QApplication(sys.argv)
trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon("Bomb.xpm"), app)
menu = QtGui.QMenu()
exitAction = menu.addAction("Exit")
trayIcon.setContextMenu(menu)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Ma questo non lo fa:
import sys
from PyQt4 import QtGui
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu()
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
def main():
app = QtGui.QApplication(sys.argv)
trayIcon = SystemTrayIcon(QtGui.QIcon("Bomb.xpm"), app)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Probabilmente mi manca qualcosa. Non ci sono errori, ma nel secondo caso quando si fa clic con il tasto destro non mostra il menu.
Soluzione
Bene, dopo un po 'di debug ho trovato il problema. Il QMenu oggetto viene distrutto dopo la funzione di finitura __init__
perché non ha un genitore. Mentre il genitore di un QSystemTrayIcon può essere un oggetto per la QMenu deve essere un QWidget. Questo funziona codice (vedere come QMenu ottiene lo stesso genitore come il QSystemTrayIcon che è un QWidget):
import sys
from PyQt4 import QtGui
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon("Bomb.xpm"), w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Altri suggerimenti
Credo che io preferirei il seguente in quanto non sembra dipendere dalle decisioni di raccolta dei rifiuti interni del QT.
import sys
from PyQt4 import QtGui
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
self.menu = QtGui.QMenu(parent)
exitAction = self.menu.addAction("Exit")
self.setContextMenu(self.menu)
def main():
app = QtGui.QApplication(sys.argv)
style = app.style()
icon = QtGui.QIcon(style.standardPixmap(QtGui.QStyle.SP_FileIcon))
trayIcon = SystemTrayIcon(icon)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Ecco il codice con l'azione di uscita implementato
import sys
from PyQt4 import QtGui, QtCore
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
QtCore.QObject.connect(exitAction,QtCore.SIGNAL('triggered()'), self.exit)
def exit(self):
QtCore.QCoreApplication.exit()
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon("qtLogo.png"), w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Ecco la versione PyQt5 (era in grado di implementare l'azione di uscita di risposta di Demostene). Fonte href="https://stackoverflow.com/questions/20749819/pyqt5-failing-import-of-qtgui"> per il porting da PyQt4 a PyQt5
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# code source: https://stackoverflow.com/questions/893984/pyqt-show-menu-in-a-system-tray-application - add answer PyQt5
#PyQt4 to PyQt5 version: https://stackoverflow.com/questions/20749819/pyqt5-failing-import-of-qtgui
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
def main(image):
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon(image), w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
on=r''# ADD PATH OF YOUR ICON HERE .png works
main(on)
Con un evento pyqt5 collegato:
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
menu.triggered.connect(self.exit)
def exit(self):
QtCore.QCoreApplication.exit()
non ho potuto ottenere una qualsiasi delle risposte di cui sopra per lavorare in PyQt5 (l'uscita nel menu barra delle applicazioni, non sarebbe in realtà uscita), ma sono riuscito a combinarli per una soluzione che funziona. Sto ancora cercando di determinare se exitAction dovrebbe essere usato ulteriormente in qualche modo.
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
menu.triggered.connect(self.exit)
def exit(self):
QtCore.QCoreApplication.exit()
def main(image):
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon(image), w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
on='icon.ico'
main(on)