PyQt:线程中有循环时setContextMenu无效
我正在我的托盘图标中创建一个 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_()
这个事件循环。如果没有这个事件循环在运行,你的图标就无法响应点击。
要想在新线程中真正运行代码,代码必须放在 QThreadObject
的 run
方法里。只有当你调用 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.