30

我正在通过制作一个简单的 Twitter 客户端来练习 PyQt 和 (Q) 线程。我有两个 Qthread。

  1. 主/GUI 线程。

  2. Twitter 获取线程 - 每 X 分钟从 Twitter 获取数据。

因此,每隔 X 分钟,我的 Twitter 线程就会下载一组新的状态更新(Python 列表)。我想将此列表交给 Main/GUI 线程,以便它可以使用这些状态更新窗口。

我假设我应该使用信号/槽系统将“状态”Python 列表从 Twitter 线程传输到 Main/GUI 线程。所以,我的问题是双重的:

  1. 如何从 Twitter 线程发送状态?

  2. 如何在 Main/GUI 线程中接收它们?

据我所知,PyQt 默认只能通过信号/插槽发送 PyQt 对象。我想我应该以某种方式注册一个自定义信号,然后我可以发送它,但是我发现的关于这个的文档对于像我这样的新手来说非常不清楚。我订购了一本 PyQt 书,但它不会再过一周到货,我不想等到那时。:-)

我在 Ubuntu 上使用 PyQt 4.6-1

更新:

这是不起作用的代码的一个例子。首先,我尝试将信号(“newStatuses”,我刚刚编的一个名称)“连接”到 Main/GUI 线程中的函数 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 相关的内容。

4

5 回答 5

27

您也可以这样做,这更符合 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)
于 2010-04-07T20:15:58.367 回答
7

看看我不久前问的这个问题。有一个代码示例可以帮助您弄清楚您需要做什么。

你所说的关于注册你的信号让我想到了这段代码(来自上述问题):

class ProcessingThread(threading.Thread, QtCore.QObject):
    __pyqtSignals__ = ( "progressUpdated(str)",
                        "resultsReady(str)")

我在我的示例中传递字符串,但您应该能够替换strlist.

如果事实证明您不能传递可变对象,您可以按照我在示例中的方式处理您的结果(即results在线程中设置一个变量,告诉主线程它们已准备好,并让主线程“pick他们起来”)。

更新:

您收到消息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:

好的,使用listas 类型不正确。从 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(),即使连接已排队。

于 2010-04-06T16:27:20.853 回答
7

当您在 PyQt 中使用这些旧式信号/插槽时,实际上根本不需要声明类型。这些应该工作:

QtCore.QObject.connect(self.twit_in,
                   QtCore.SIGNAL("newStatuses"),
                   self.update_tweet_list)

...

self.emit(SIGNAL("newStatuses"), statuses)

在这种情况下,PyQt 只会在您发出信号时即时为您创建一个新的信号类型。如果您想使用新型信号,那么它看起来更像:

class TwitterThread(QThread):
    newStatuses = pyqtSignal(object)

....

self.newStatuses.emit(statuses)
于 2011-03-03T21:31:49.650 回答
7

从这个例子:

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)
于 2010-04-06T18:30:02.903 回答
3

(Py)Qt 信号和槽在跨线程中的工作就像在单线程中一样。所以没有什么特别的设置:

在主线程中定义一个槽(方法),并将线程的信号连接到该槽(连接也应在主线程中完成)。然后,在线程中,当您需要时,只需发出信号,它就可以工作。这是关于在 PyQt 中使用带有线程的信号和槽的教程

我建议您先在一个小玩具示例上尝试一下,然后再转到您的应用程序。

于 2010-04-06T14:28:06.817 回答