python - 在 pyside 中使用 Qt 线程

标签 python pyside

我不明白下面示例代码的输出 found here .当前线程和工作线程具有相同的地址,这怎么可能?

from PySide import QtCore

class Master(QtCore.QObject):
    command = QtCore.Signal(str)
    def __init__(self):
        QtCore.QObject.__init__(self)

class Worker(QtCore.QObject):
    def __init__(self):
        QtCore.QObject.__init__(self)
    def do_something(self, text):
        print('in thread {} message {}'.format(QtCore.QThread.currentThread(), text))

if __name__ == '__main__':
    app = QtCore.QCoreApplication([])
    print(QtCore.QThread.currentThread())
    # give us a thread and start it
    thread = QtCore.QThread()
    thread.start()
    print(thread)

    # create a worker and move it to our extra thread
    worker = Worker()
    worker.moveToThread(thread)

    # create a master object and connect it to the worker
    master = Master()
    master.command.connect(worker.do_something)

    # call a method of the worker directly (will be executed in the actual thread)
    worker.do_something('in main thread')

    # communicate via signals, will execute the method now in the extra thread
    master.command.emit('in worker thread')

    # start the application and kill it after 1 second
    QtCore.QTimer.singleShot(1000, app.quit)
    app.exec_()

    # don't forget to terminate the extra thread
    thread.quit()

输出:

<PySide.QtCore.QThread object at 0x0000000002537688>
<PySide.QtCore.QThread object at 0x0000000002537688>
in thread <PySide.QtCore.QThread object at 0x00000000025377C8> message in main thread
in thread <PySide.QtCore.QThread object at 0x0000000002537688> message in worker thread

最佳答案

没有“包装器被重用”,只是旧包装器对象(已删除)恰好与新包装器对象位于同一内存地址。

PySide 使用shiboken 库将QObject 包装成Python 对象。 shiboken documentation说如下:

As any python binding, |project|-based bindings uses reference counting to handle the life of the wrapper object (the Python object that contains the C++ object, do not confuse with the wrapped C++ object). When a reference count reaches zero, the wrapper is deleted by Python garbage collector and tries to delete the wrapped instance, but sometimes the wrapped C++ object is already deleted, or maybe the C++ object should not be freed after the Python wrapper go out of scope and die, because C++ is already taking care of the wrapped instance.

除此之外,CPython malloc 实现(以及许多其他常见的 malloc)often reuse the memory address that belonged to just deleted object , 随后创建的对象,导致常见的陷阱:

>>> {} is {}
False
>>> id({}) == id({})
True

因此,事件对象的地址/ID 总是不同的;曾经存在然后死亡的对象的地址只是一种好奇。

在第一种情况下,两个词典的引用都被保留,因此从技术上讲,它们的 id 是不同的,而在第二种情况下,左侧词典很快被删除左边的 id 被调用,然后才创建右边的字典,并分配在相同的内存地址。


然而,它有点复杂。当您调用 currentThread() 时,如果旧包装器仍然存在(意味着它具有在 python 端引用它),在这种情况下返回旧包装器:因此,

QtCore.QThread.currentThread() is QtCore.QThread.currentThread() 

永远是真的!


现在,你得到2个地址的原因

in thread <PySide.QtCore.QThread object at 0x00000000028EB888> message in main thread
in thread <PySide.QtCore.QThread object at 0x00000000028EB8C8> message in worker thread

again 不一定与 wrapper “未被重复使用”或被持有无关;取而代之的是,在其间创建的一些其他对象可能会发生同样的效果,其引用被保留——也就是说,人们不应该认为包装对象一定会保持事件状态。

但是,如果您持有对 2 个不同的 QThread 对象的引用,则它们的 id() 在其生命周期内将是不同的。


总而言之,您不应该依赖 shiboken 包装器对象的地址来做任何有用的事情;相反,如果您自己持有 Python 代码中的引用,则 is 测试很有用


请注意,如果设置了 QObject__str__ 将打印它们的 objectName;因此,您可以通过以下操作轻松理解输出:

app = QtCore.QCoreApplication([])
QtCore.QThread.currentThread().setObjectName('main thread')

thread = QtCore.QThread()
thread.start()
thread.setObjectName('worker thread')

并且使用更清晰的打印:

print('in thread: "{}" - message: "{}"'
    .format(QtCore.QThread.currentThread().objectName(), text))

消息将是:

in thread: "main thread" - message: "in main thread"
in thread: "worker thread" - message: "in worker thread"

还有一种方法可以fetch the address of the underlying C++ object , 使用 shiboken模块;这对于调试其他东西也很方便;唉,Ubuntu 14.10 默认情况下根本没有安装 Python 库,或者任何与 shiboken 相关的包;经过无数次回答后,我终于设法手动安装了它。

结果比我想象的更可怕:

app = QtCore.QCoreApplication([])
print(QtCore.QCoreApplication.instance().thread())
obj = QtCore.QObject()
print(obj.thread())
del obj
print(QtCore.QThread.currentThread())

打印:

<PySide.QtCore.QThread object at 0x7fb4a1149cc8>
<PySide.QtCore.QThread object at 0x7fb4a1149cc8>
<PySide.QtCore.QThread object at 0x7fb4a1149d08>

是的,在我删除QObject之后线程包装器的地址改变了!因此,让我们从 Shiboken.shiboken 导入 dump:

from Shiboken.shiboken import dump

def info(obj):
    print(id(obj))
    print(dump(obj))

app = QtCore.QCoreApplication([])
info(QtCore.QCoreApplication.instance().thread())

obj = QtCore.QObject()
info(obj.thread())    
del obj

info(QtCore.QCoreApplication.instance().thread())

输出是

140323585370568
C++ address....... PySide.QtCore.QThread/0xe3d880 
hasOwnership...... 0
containsCppWrapper 0
validCppObject.... 1
wasCreatedByPython 0
parent............ <PySide.QtCore.QCoreApplication object at 0x7f9fa175a948>

140323585370568
C++ address....... PySide.QtCore.QThread/0xe3d880 
hasOwnership...... 0
containsCppWrapper 0
validCppObject.... 1
wasCreatedByPython 0
parent............ <PySide.QtCore.QObject object at 0x7f9fa175aa48>

140323585370696
C++ address....... PySide.QtCore.QThread/0xe3d880 
hasOwnership...... 0
containsCppWrapper 0
validCppObject.... 1
wasCreatedByPython 0

也就是说,shiboken 生成了 last 对象,我们将其 thread() 方法称为线程的 parent。每当 that 对象被删除时(也就是最后调用 .thread() 的对象),包装器也会被删除。确实是非常可疑的行为;我不确定这是否是一个错误,但这证明了信任包装对象的 id 是根本不值得信任的。

关于python - 在 pyside 中使用 Qt 线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29027813/

相关文章:

python - 集差与集减法

python - 如何在使用 QWaitCondition 的 QThread 中使用 QTimer? (皮塞德)

python - 防止在每个请求上创建昂贵的对象

python - SQLAlchemy 中的每列时间戳

qt - 如何通过拖动 Widget 的角来调整 Widget 上的 QTableWidget 大小

python - 带有圆形图像的 Qlabel

python - 组合框中的文本重叠图标

c++ - 我怎么知道鼠标是否在小部件上?

python - 漂白回车

python - 使用可变模式创建 Pyspark 数据框