【发布时间】:2022-07-07 06:36:08
【问题描述】:
我有一个事件生产者线程,在该Producer线程中创建事件实例并将它们作为信号参数传递给GUI线程,在将对象移动到GUI线程之后(在现实生活中的代码中,所以对象可以添加到模型中进行显示)。
在这里描述的第一次尝试中:
- PyQt5 成功让对象通过,但它在槽中被视为
QObject而不是事件 - PySide2 只是段错误
#! /usr/bin/env python3
import sys, threading
if True:
from PySide2 import QtCore
from PySide2.QtCore import QObject
QtSignal = QtCore.Signal
else:
from PyQt5 import QtCore
from PyQt5.QtCore import QObject
QtSignal = QtCore.pyqtSignal
def print_thread(ctx):
print(ctx, "in", threading.current_thread().name)
class Event(QObject):
def __init__(self, name: str):
super().__init__()
self.name = name
class Producer(QtCore.QThread):
woo = QtSignal(Event)
def run(self):
print_thread("Producer.run")
evt = Event("bar")
evt.moveToThread(QtCore.QCoreApplication.instance().thread())
print("emitting", evt)
self.woo.emit(evt)
class Listener(QObject):
def handle_event(self, event):
print_thread("Listener.handle_event")
print("got", event)
assert type(event) is Event
exit(0)
app = QtCore.QCoreApplication(sys.argv)
l = Listener()
p = Producer()
p.woo.connect(l.handle_event)
p.start()
sys.exit(app.exec_())
两者都在 PyQt5 下显示相同的打字问题,被我的 assert 捕获:
Producer.run in Dummy-1
emitting <__main__.Event object at 0x7af7ad95b790>
Listener.handle_event in MainThread
got <PyQt5.QtCore.QObject object at 0x7af7ad95b790>
Traceback (most recent call last):
File "/home/user/soft/bt2viz/testsig.py", line 35, in handle_event
assert type(event) is Event
AssertionError
Aborted
...在 PySide2 下:
$ gdb --args python3 testsig.py
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
...
(gdb) r
Starting program: /usr/bin/python3 testsig.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff3b4f700 (LWP 6190)]
Producer.run in Dummy-1
emitting <__main__.Event(0x7fffec005740) at 0x7ffff75577c0>
[Thread 0x7ffff3b4f700 (LWP 6190) exited]
Thread 1 "python3" received signal SIGSEGV, Segmentation fault.
0x00007fffec005760 in ?? ()
(gdb) bt
#0 0x00007fffec005760 in ?? ()
#1 0x00007ffff6add361 in QObject::property (this=this@entry=0x7fffec005740, name=name@entry=0x7ffff6d6e5f0 <PySide::invalidatePropertyName> "_PySideInvalidatePtr")
at kernel/qobject.cpp:4086
#2 0x00007ffff6d6ae17 in PySide::getWrapperForQObject (cppSelf=0x7fffec005740, sbk_type=0xbbb910) at ./sources/pyside2/libpyside/pyside.cpp:441
...
事实证明,正如https://stackoverflow.com/a/12363609/6285023 所暗示的那样,Event 对象在超出范围时被销毁——例如。只需在 Producer 对象中保留对它的引用即可避免此问题。
但这很浪费(在一个线程中保留大量事件的列表,当它们已经在另一个线程的数据模型中被引用时),并且感觉不是很pythonic:另一个线程得到了看起来像 ref 的东西到对象——至少它感觉像一个标准的 ref,除了它似乎没有包含在通常的 refcounting 中,而且被删除得太早了。
这看起来像是一个 C++ism 没有正确翻译成 Python,并且可以被视为 PyQt/PySide 错误,对吧?
是否有更合适的方法来实现相同的结果,还是我们必须接受上述解决方法?
【问题讨论】:
-
为什么
Event会继承QObject?如果它只是简单地继承object,那么一切都会按预期工作(包括保留类型)。 -
好点,确实效果很好,并且完全避免了这个问题(@eyllanesc 准确地描述了)。我使用 QObject 能够将对象传递给 QML,但我确实不需要它,并且仅使用
objects可以提供明显更好的性能。
标签: python pyqt5 pyside2 qt-signals