PyQt:线程中有循环时setContextMenu无效

0 投票
1 回答
503 浏览
提问于 2025-04-18 17:02

我正在我的托盘图标中创建一个 QtGui.QMenu(),并添加一个操作:.addAction("Close", lambda : exit(1)),这样当我点击托盘图标时,就可以快速退出应用程序:

在这里输入图片描述

问题出现在我在 QtCore.QThread 中创建一个循环时,出于某种原因,右键点击托盘图标时上下文菜单停止工作(我甚至看不到菜单,右键点击时什么也没有发生)。所以问题是 - 这是什么原因,为什么这个循环会让线程卡住呢?

代码(只需在 __init__ 函数中去掉循环的注释,就能重现这个问题):

# -*- coding: utf-8 -*-
import sys
import time
from PyQt4 import QtGui, QtCore

app = QtGui.QApplication(sys.argv)

class Main(QtCore.QThread):
    def __init__(self):
        QtCore.QThread.__init__(self)

        self.createTrayIcon()

        self.trayIcon.show()
        self.trayIcon.showMessage("Hello", u"This is a description.")

        # while True:
        #   self.doSomething()
        #   time.sleep(2)

    def createTrayIcon(self):
        self.trayIconMenu = QtGui.QMenu()
        self.trayIconMenu.addAction("Close", lambda : exit(1))

        self.trayIcon = QtGui.QSystemTrayIcon()
        self.trayIcon.setIcon(QtGui.QIcon(r"t_icon.png"))
        self.trayIcon.setToolTip("Tooltip")
        self.trayIcon.setContextMenu(self.trayIconMenu)

    def doSomething(self):
        print "Doing something"

mainThread = Main()
sys.exit(app.exec_())

1 个回答

1

你现在的设计其实并没有真正启动一个新线程。你只是创建了一个 QThread 对象,这个对象在程序的 主线程 中建立了一个托盘图标并显示出来。所以,当你显示消息 "Hello" 后,你就卡在了 while True 循环里,根本没有真正开始 app.exec_() 这个事件循环。如果没有这个事件循环在运行,你的图标就无法响应点击。

要想在新线程中真正运行代码,代码必须放在 QThreadObjectrun 方法里。只有当你调用 QThread.start() 时,run 方法才会被执行。下面是一个应该能工作的例子:

# -*- coding: utf-8 -*-
import sys 
import time
from PyQt4 import QtGui, QtCore

app = QtGui.QApplication(sys.argv)

class Main(QtCore.QThread):
    def __init__(self):
        QtCore.QThread.__init__(self)

    def run(self):
        while True:
            self.doSomething()
            time.sleep(2)

    def doSomething(self):
        print "Doing something"


class MyIcon(QtGui.QSystemTrayIcon):
    def __init__(self):
        QtGui.QSystemTrayIcon.__init__(self)
        self.trayIconMenu = QtGui.QMenu()
        self.trayIconMenu.addAction("Close", lambda : exit(1))

        self.setIcon(QtGui.QIcon(r"t_icon.png"))
        self.setToolTip("Tooltip")
        self.setContextMenu(self.trayIconMenu)
        self.show()
        self.showMessage("Hello", u"This is a description.")

icon = MyIcon() # create the icon in the main thread
mainThread = Main() # build the thread object (it won't be running yet)
mainThread.start()  # run will be executed in a separate thread
sys.exit(app.exec_()) # Start the main loop, so the icon will respond to clicks.

撰写回答