发送自定义PyQt信号?
我正在练习使用PyQt和(Q)线程,做一个简单的Twitter客户端。我有两个线程。
主线程/图形界面线程。
Twitter获取线程 - 每隔X分钟从Twitter获取数据。
所以,每隔X分钟,我的Twitter线程就会下载一组新的状态更新(一个Python列表)。我想把这个列表交给主线程/图形界面线程,这样它就可以用这些状态更新来更新窗口。
我想我应该使用信号/槽机制,把“状态”这个Python列表从Twitter线程传递到主线程/图形界面线程。所以,我的问题有两个:
我该如何从Twitter线程发送状态?
我该如何在主线程/图形界面线程接收它们?
据我所知,PyQt默认只能通过信号/槽发送PyQt对象。我觉得我应该以某种方式注册一个自定义信号,然后发送它,但我找到的关于这方面的文档对像我这样的新手来说非常不清楚。我已经订了一本PyQt的书,但要等到下周才能到,我不想等那么久。:-)
我在Ubuntu上使用的是PyQt 4.6-1
更新:
这是一个不工作的代码片段。首先,我尝试把信号(“newStatuses”,这是我随便起的名字)连接到主线程/图形界面线程中的函数self.update_tweet_list:
QtCore.QObject.connect(self.twit_in,
QtCore.SIGNAL("newStatuses (statuses)"),
self.update_tweet_list)
然后,在Twitter线程中,我这样做:
self.emit(SIGNAL("newStatuses (statuses)"), statuses)
当这行代码被调用时,我收到以下消息:
QObject::connect: Cannot queue arguments of type 'statuses'
(Make sure 'statuses' is registered using qRegisterMetaType().)
我搜索了qRegisterMetaType(),但没有找到任何我能理解的与Python相关的内容。
5 个回答
你可以看看我之前问的这个问题。里面有个代码示例,可能对你理解需要做的事情有帮助。
你提到的关于注册信号的内容让我想到了这段代码(来自上面提到的问题):
class ProcessingThread(threading.Thread, QtCore.QObject):
__pyqtSignals__ = ( "progressUpdated(str)",
"resultsReady(str)")
我在示例中传递的是字符串,但你可以把str
替换成list
。
如果发现不能传递可变对象,你可以像我示例中那样处理结果(也就是说,在线程中设置一个results
变量,告诉主线程它们已经准备好了,然后让主线程“取走”它们)。
更新:
你会收到消息QObject::connect: Cannot queue arguments of type 'statuses'
,这是因为你需要定义在发出信号时将要传递的参数的类型。你想传递的类型是list
而不是statuses
。
当你连接信号时,应该像这样:
QtCore.QObject.connect(self.twit_in,
QtCore.SIGNAL("newStatuses(list)"),
self.update_tweet_list)
当你发出信号时,应该像这样:
self.emit(SIGNAL("newStatuses(list)"), statuses)
前提是statuses
是一个列表。注意,根据你的情况,你可能想要发出列表的深拷贝。
更新 2:
好的,使用list
作为类型是不正确的。根据PyQt4的帮助文档:
PyQt信号和Qt信号
Qt信号是静态定义的,作为C++类的一部分。它们通过
QtCore.SIGNAL()
函数来引用。这个方法接受一个字符串参数,这个字符串是信号的名称和它的C++签名。例如:QtCore.SIGNAL("finished(int)")
返回的值通常会传递给
QtCore.QObject.connect()
方法。PyQt允许动态定义新的信号。发出PyQt信号的行为隐式地定义了它。PyQt v4的信号也使用
QtCore.SIGNAL()
函数来引用。
PyQt_PyObject
信号参数类型通过在签名中指定
PyQt_PyObject
作为参数的类型,可以传递任何Python对象作为信号参数。例如:QtCore.SIGNAL("finished(PyQt_PyObject)")
虽然这通常用于传递像列表和字典这样的对象作为信号参数,但它可以用于任何Python类型。比如传递一个整数时,它的好处是通常不需要进行Python对象与C++整数之间的转换。
被传递对象的引用计数会自动维护。发出信号的对象在调用
QtCore.QObject.emit()
后,不需要再保持对该对象的引用,即使连接是排队的。
你也可以这样做,这样写更符合Python的风格(而且更容易读懂!)
# create a signal equivalent to "void someSignal(int, QWidget)"
someSignal = QtCore.pyqtSignal(int, QtGui.QWidget)
# define a slot with the same signature
@QtCore.pyqtSlot(int, QtGui.QWidget)
def someSlot(status, source):
pass
# connect the signal to the slot
self.someSignal.connect(self.someSlot)
从这个例子来看:
http://doc.qt.digia.com/4.5/qmetatype.html
int id = QMetaType.type("MyClass");
你可以在Python中写下:
from PyQt4 import QtCore
id = QtCore.QMetaType.type('MyClass')
编辑
这个答案是从评论中提取出来的:
self.emit(SIGNAL("newStatuses(PyQt_PyObject)"), statuses)