PyQt4 最小化到系统托盘

23

有没有方法可以在PyQt4中将应用程序最小化到系统托盘?我已经使用了QSystemTrayIcon类,但现在我想将我的应用程序窗口最小化或“隐藏”,只显示托盘图标。

有人做过这个吗?任何指导都将不胜感激。

在Windows XP Pro上使用Python 2.5.4和PyQt4。

7个回答

36

一旦记住实际上没有办法最小化到系统托盘,这就很简单了。

相反,您可以通过以下方式来模拟:

  1. 捕获窗口的最小化事件
  2. 在最小化事件处理程序中,创建并显示QSystemTrayIcon
  3. 同样在最小化事件处理程序中,在窗口上调用hide()或setVisible(false)
  4. 捕获系统托盘图标的点击/双击/菜单项
  5. 在系统托盘图标事件处理程序中,在窗口上调用show()或setVisible(true),并可选择隐藏托盘图标。

12

代码有助于解决问题,这里是我为一个应用程序编写的一些内容,除了使用closeEvent而不是minimize事件。

注意事项:

"closeEvent(event)"是一个被覆盖的Qt事件,因此必须将其放置在实现您想要隐藏的窗口的类中。

"okayToClose()"是一个您可能考虑实现的函数(或者您可能想要存储的布尔标志),因为有时您实际上希望退出应用程序而不是最小化到系统托盘。

还有一个如何再次显示()您的窗口的示例。

def __init__(self):
  traySignal = "activated(QSystemTrayIcon::ActivationReason)"
  QtCore.QObject.connect(self.trayIcon, QtCore.SIGNAL(traySignal), self.__icon_activated)

def closeEvent(self, event):
  if self.okayToClose(): 
    #user asked for exit
    self.trayIcon.hide()
    event.accept()
  else:
    #"minimize"
    self.hide()
    self.trayIcon.show() #thanks @mojo
    event.ignore()

def __icon_activated(self, reason):
  if reason == QtGui.QSystemTrayIcon.DoubleClick:
    self.show()

#"最小化" self.hide() self.trayIcon.show() event.ignore() - Dmitry Mukhin
我加入了@mojo的建议,以增加系统托盘图标处理的灵活性。原始代码来自一个应用程序,其中托盘图标始终可见,self.trayIcon.hide()是为了在退出后不让图标停留在系统托盘中(遗憾的是,这是典型的Windows行为)。请注意,现在用户必须实现showEvent(event)方法并调用self.trayIcon.hide()来完成此示例。我本应该一开始就发布一个最小化/恢复示例 :) - user44484

7

仅仅是为了补充Chris的例子:

在声明信号时使用Qt符号非常重要,即:

正确的做法

self.connect(self.icon, SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.iconClicked)

并不是 PyQt 的内容

错误的,不能使用:

self.connect(self.icon, SIGNAL("activated(QSystemTrayIcon.ActivationReason)"), self.iconClicked)

请注意信号字符串中的::。这让我花了大约三个小时才理解清楚。

请在上面添加>>“非常重要的是使用Qt符号”<<。我已经在这里很久了...想了几个小时...做了其他事情,但直到我读到这篇文章为止... == QtCore.SISNAL("activated(QSystemTrayIcon::ActivationReason)").. 没有理由;-) - PedroMorgan
def create_sys_tray(self): self.sysTray = QtGui.QSystemTrayIcon(self) self.sysTray.setIcon( QtGui.QIcon('../images/corp/blip_32.png') ) self.sysTray.setVisible(True) self.connect(self.sysTray, QtCore.SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.on_sys_tray_activated) self.sysTrayMenu = QtGui.QMenu(self) act = self.sysTrayMenu.addAction("FOO") def on_sys_tray_activated(self, reason): print "reason-=" , reason - PedroMorgan

4
这里是可运行的代码...感谢Matze和Crucial,SIGNAL让我花了更多时间去探究...但做其他事情时,这是一个#!时刻 :-)
def create_sys_tray(self):
    self.sysTray = QtGui.QSystemTrayIcon(self)
    self.sysTray.setIcon( QtGui.QIcon('../images/corp/blip_32.png') )
    self.sysTray.setVisible(True)
    self.connect(self.sysTray, QtCore.SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.on_sys_tray_activated)

    self.sysTrayMenu = QtGui.QMenu(self)
    act = self.sysTrayMenu.addAction("FOO")

def on_sys_tray_activated(self, reason):
    print "reason-=" , reason

4
这是vzades回复的修改版,但因多个原因被拒绝。它与他们的代码完全相同,但也会遵守最小化事件(并且不会出现语法错误/缺少图标)。
import sys
from PyQt4 import QtGui, QtCore


class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def initUI(self):
        style = self.style()

        # Set the window and tray icon to something
        icon = style.standardIcon(QtGui.QStyle.SP_MediaSeekForward)
        self.tray_icon = QtGui.QSystemTrayIcon()
        self.tray_icon.setIcon(QtGui.QIcon(icon))
        self.setWindowIcon(QtGui.QIcon(icon))

        # Restore the window when the tray icon is double clicked.
        self.tray_icon.activated.connect(self.restore_window)

    def event(self, event):
        if (event.type() == QtCore.QEvent.WindowStateChange and 
                self.isMinimized()):
            # The window is already minimized at this point.  AFAIK,
            # there is no hook stop a minimize event. Instead,
            # removing the Qt.Tool flag should remove the window
            # from the taskbar.
            self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.Tool)
            self.tray_icon.show()
            return True
        else:
            return super(Example, self).event(event)

    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(
            self,
            'Message',"Are you sure to quit?",
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
            QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            self.tray_icon.show()
            self.hide()
            event.ignore()

    def restore_window(self, reason):
        if reason == QtGui.QSystemTrayIcon.DoubleClick:
            self.tray_icon.hide()
            # self.showNormal will restore the window even if it was
            # minimized.
            self.showNormal()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

我该如何在启动后仅显示托盘图标?如果我将setWindowFlags行移动到initUI()中,它没有效果,正常窗口仍然会出现。 - Shuman

3

这是在PyQt5中处理托盘图标双击的正确方法。

def _create_tray(self):
    self.tray_icon = QSystemTrayIcon(self)
    self.tray_icon.activated.connect(self.__icon_activated)

def __icon_activated(self, reason):
    if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
        pass

1
我知道这并没有直接回答问题,但是这个问题有很多浏览量,而且来自谷歌的人正在寻找一个更加更新的答案。 - Jorjon

1

这是代码,我相信它能帮助我展示代码。

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QDialog, QApplication, QPushButton, QLineEdit, QFormLayout, QSystemTrayIcon


class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()
        self.initUI()

    def initUI(self):
        self.icon = QSystemTrayIcon()
        r = self.icon.isSystemTrayAvailable()
        print r
        self.icon.setIcon(QtGui.QIcon('/home/vzades/Desktop/web.png'))
        self.icon.show()
        # self.icon.setVisible(True)
        self.setGeometry(300, 300, 250, 150)
        self.setWindowIcon(QtGui.QIcon('/home/vzades/Desktop/web.png'))
        self.setWindowTitle('Message box')
        self.show()
        self.icon.activated.connect(self.activate)
        self.show()

    def closeEvent(self, event):

        reply = QtGui.QMessageBox.question(self, 'Message', "Are you sure to quit?", QtGui.QMessageBox.Yes |
                                           QtGui.QMessageBox.No, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            self.icon.show()

            self.hide()

            event.ignore()

    def activate(self, reason):
        print reason
        if reason == 2:
            self.show()

    def __icon_activated(self, reason):
        if reason == QtGui.QSystemTrayIcon.DoubleClick:
            self.show()


def main():

    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

2
这段代码看起来很糟糕。它是如何得到赞成票的,真是令人费解。 - qed
我不认为这很糟糕。也许比问题所要求的多一点,解释上有些简短,但看起来像正常的代码。 - Paul Rooney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接