我在派生類接收PySide中的信號時遇到了問題。我在主(GUI或命令行應用程序)線程的兩個獨立線程上使用發送器和接收器。線程是QThread對象。發送器和接收器使用QObject.moveToThread()創建後立即移動到它們的線程。如果接收器是直接從QObject派生的,則所有工作正常,並且接收器在其線程中接收。但是,如果接收器是從派生自QObject的基類派生的,接收器仍然會收到該信號,但是在錯誤的線程(主線程)上會這樣做。在PySide(Qt/PyQt)中接收錯誤線程信號的派生類
例(有適於從PyQt & unittest - Testing signal and slots一些信號調試代碼):
#!/usr/bin/env python3
# weigh/bugtest_qt_signal_derived.py
import logging
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
import sys
import threading
import time
from PySide import QtCore
from PySide.QtCore import (
QCoreApplication,
QObject,
QThread,
Signal,
Slot,
)
_oldEmit = QtCore.QObject.emit # normal method
def debug_emit(self, *args):
logger.debug("EMIT: thread name={}, emit args={}".format(
threading.current_thread().name,
repr(args),
))
_oldEmit(self, *args)
QtCore.QObject.emit = debug_emit
def report(msg):
logger.info("{} [{}]".format(msg, threading.current_thread().name))
class Transmitter(QObject):
transmit = Signal()
finished = Signal()
def start(self):
count = 3
logger.info("Starting transmitter")
while count > 0:
time.sleep(1) # seconds
report("transmitting, count={}".format(count))
self.transmit.emit()
count -= 1
logger.info("Stopping transmitter")
self.finished.emit()
class Base(QObject):
def __init__(self, parent=None):
super().__init__(parent=parent)
@Slot()
def start(self):
report("Starting receiver")
@Slot()
def receive(self):
report("receive: BASE")
class Derived(Base):
def __init__(self, parent=None):
super().__init__(parent=parent)
@Slot()
def receive(self):
report("receive: DERIVED")
USE_DERIVED = True
if __name__ == '__main__':
logging.basicConfig()
logger.setLevel(logging.DEBUG)
# Objects
app = QCoreApplication(sys.argv)
tx_thread = QThread()
transmitter = Transmitter()
transmitter.moveToThread(tx_thread)
rx_thread = QThread()
if USE_DERIVED:
receiver = Derived()
else:
receiver = Base()
receiver.moveToThread(rx_thread)
# Signals: startup
tx_thread.started.connect(transmitter.start)
rx_thread.started.connect(receiver.start)
# ... shutdown
transmitter.finished.connect(tx_thread.quit)
tx_thread.finished.connect(rx_thread.quit)
rx_thread.finished.connect(app.quit)
# ... action
transmitter.transmit.connect(receiver.receive)
# Go
rx_thread.start()
tx_thread.start()
report("Starting app")
app.exec_()
輸出與USE_DERIVED = False
:
INFO:__main__:Starting app [MainThread]
INFO:__main__:Starting receiver [Dummy-1]
INFO:__main__:Starting transmitter
INFO:__main__:transmitting, count=3 [Dummy-2]
DEBUG:__main__:EMIT: thread name=Dummy-2, emit args=('2transmit()',)
INFO:__main__:receive: BASE [Dummy-1]
INFO:__main__:transmitting, count=2 [Dummy-2]
DEBUG:__main__:EMIT: thread name=Dummy-2, emit args=('2transmit()',)
INFO:__main__:receive: BASE [Dummy-1]
INFO:__main__:transmitting, count=1 [Dummy-2]
DEBUG:__main__:EMIT: thread name=Dummy-2, emit args=('2transmit()',)
INFO:__main__:Stopping transmitter
DEBUG:__main__:EMIT: thread name=Dummy-2, emit args=('2finished()',)
INFO:__main__:receive: BASE [Dummy-1]
輸出與USE_DERIVED = True
:
INFO:__main__:Starting app [MainThread]
INFO:__main__:Starting receiver [MainThread]
INFO:__main__:Starting transmitter
INFO:__main__:transmitting, count=3 [Dummy-1]
DEBUG:__main__:EMIT: thread name=Dummy-1, emit args=('2transmit()',)
INFO:__main__:receive: DERIVED [MainThread]
INFO:__main__:transmitting, count=2 [Dummy-1]
DEBUG:__main__:EMIT: thread name=Dummy-1, emit args=('2transmit()',)
INFO:__main__:receive: DERIVED [MainThread]
INFO:__main__:transmitting, count=1 [Dummy-1]
DEBUG:__main__:EMIT: thread name=Dummy-1, emit args=('2transmit()',)
INFO:__main__:Stopping transmitter
DEBUG:__main__:EMIT: thread name=Dummy-1, emit args=('2finished()',)
INFO:__main__:receive: DERIVED [MainThread]
......區別在於Base類在其自己的線程上接收,而Derived類在MainThread上接收。
有誰知道爲什麼?非常感謝!
軟件:PySide版本:1.2.4; QtCore版本:4.8.6; Ubuntu 14.04; Python 3.4.4。
繼@ 101的評論:
的信號覆蓋是沒有必要的失敗。這些派生類也不能(在被稱爲在錯誤的線程的意義上):
class DerivedTwo(Base):
def __init__(self, parent=None):
super().__init__(parent=parent)
class DerivedThree(Base):
def __init__(self, parent=None):
QObject.__init__(self, parent=parent)
由於輸出表明派生接收對象開始錯誤的線程上,我想知道,如果問題是QObject.moveToThread()
失敗了派生對象。然而,這似乎並沒有這樣的情況:
def debug_object(obj):
logger.debug("Object {} belongs to QThread {}".format(obj, obj.thread()))
def debug_thread(thread_name, thread):
logger.debug("{} is QThread {}".format(thread_name, thread))
# ...
tx_thread = QThread()
debug_thread("tx_thread", tx_thread)
transmitter = Transmitter()
debug_object(transmitter)
transmitter.moveToThread(tx_thread)
debug_object(transmitter)
rx_thread = QThread()
debug_thread("rx_thread", rx_thread)
receiver = DerivedTwo()
debug_object(receiver)
receiver.moveToThread(rx_thread)
debug_object(receiver)
給
DEBUG:__main__:tx_thread is QThread <PySide.QtCore.QThread object at 0x7fc4a3befd08>
DEBUG:__main__:Object <__main__.Transmitter object at 0x7fc4a3bf2648> belongs to QThread <PySide.QtCore.QThread object at 0x7fc4a3bf2688>
DEBUG:__main__:Object <__main__.Transmitter object at 0x7fc4a3bf2648> belongs to QThread <PySide.QtCore.QThread object at 0x7fc4a3befd08>
DEBUG:__main__:rx_thread is QThread <PySide.QtCore.QThread object at 0x7fc4a3bf2708>
DEBUG:__main__:Object <__main__.DerivedTwo object at 0x7fc4a3bf2788> belongs to QThread <PySide.QtCore.QThread object at 0x7fc4a3bf2688>
DEBUG:__main__:Object <__main__.DerivedTwo object at 0x7fc4a3bf2788> belongs to QThread <PySide.QtCore.QThread object at 0x7fc4a3bf2708>
INFO:__main__:Starting app [MainThread]
INFO:__main__:Starting receiver [MainThread]
INFO:__main__:Starting transmitter [Dummy-1]
INFO:__main__:transmitting, count=3 [Dummy-1]
DEBUG:__main__:EMIT: thread name=Dummy-1, emit args=('2transmit()',)
INFO:__main__:receive: BASE [MainThread]
...
這表明,我認爲派生的對象被正確地轉移到一個新的線程(理論上,Qt的事件處理)在moveToThread()
期間,但隨後以某種方式啓動(並接收)主線程。
附加:在C++ Qt的工作
部首:
// bugtest_qt_signal_derived.h
#include <QtCore/QCoreApplication>
#include <QtCore/QtDebug> // not QDebug
#include <QtCore/QObject>
#include <QtCore/QString> // works with qDebug where std::string doesn't
#include <QtCore/QThread>
void debug_object(const QString& obj_name, const QObject& obj);
void debug_thread(const QString& thread_name, const QThread& thread);
void report(const QString& msg);
class Transmitter : public QObject
{
Q_OBJECT // enables macros like "signals:", "slots:", "emit"
public:
Transmitter() {}
virtual ~Transmitter() {}
signals:
void transmit();
void finished();
public slots:
void start();
};
class Base : public QObject
{
Q_OBJECT
public:
Base() {}
public slots:
void start();
void receive();
};
class Derived : public Base
{
Q_OBJECT
public:
Derived() {}
public slots:
void receive();
};
來源:
// bugtest_qt_signal_derived.cpp
#include "bugtest_qt_signal_derived.h"
#include <unistd.h> // for sleep()
void debug_object(const QString& obj_name, const QObject& obj)
{
qDebug() << "Object" << obj_name << "belongs to QThread" << obj.thread();
}
void debug_thread(const QString& thread_name, const QThread& thread)
{
qDebug() << thread_name << "is QThread at" << &thread;
}
void report(const QString& msg)
{
qDebug().nospace() << msg << " [" << QThread::currentThreadId() << "]";
}
void Transmitter::start()
{
unsigned int count = 3;
report("Starting transmitter");
while (count > 0) {
sleep(1); // seconds
report(QString("transmitting, count=%1").arg(count));
emit transmit();
count -= 1;
}
report("Stopping transmitter");
emit finished();
}
void Base::start()
{
report("Starting receiver");
}
void Base::receive()
{
report("receive: BASE");
}
void Derived::receive()
{
report("receive: DERIVED");
}
#define USE_DERIVED
int main(int argc, char* argv[])
{
// Objects
QCoreApplication app(argc, argv);
QThread tx_thread;
debug_thread("tx_thread", tx_thread);
Transmitter transmitter;
debug_object("transmitter", transmitter);
transmitter.moveToThread(&tx_thread);
debug_object("transmitter", transmitter);
QThread rx_thread;
debug_thread("rx_thread", rx_thread);
#ifdef USE_DERIVED
Derived receiver;
#else
Base receiver;
#endif
debug_object("receiver", receiver);
receiver.moveToThread(&rx_thread);
debug_object("receiver", receiver);
// Signals: startup
QObject::connect(&tx_thread, SIGNAL(started()),
&transmitter, SLOT(start()));
QObject::connect(&rx_thread, SIGNAL(started()),
&receiver, SLOT(start()));
// ... shutdown
QObject::connect(&transmitter, SIGNAL(finished()),
&tx_thread, SLOT(quit()));
QObject::connect(&tx_thread, SIGNAL(finished()),
&rx_thread, SLOT(quit()));
QObject::connect(&rx_thread, SIGNAL(finished()),
&app, SLOT(quit()));
// ... action
QObject::connect(&transmitter, SIGNAL(transmit()),
&receiver, SLOT(receive()));
// Go
rx_thread.start();
tx_thread.start();
report("Starting app");
return app.exec();
}
輸出:
"tx_thread" is QThread at QThread(0x7ffc138c5330)
Object "transmitter" belongs to QThread QThread(0xdae1e0)
Object "transmitter" belongs to QThread QThread(0x7ffc138c5330)
"rx_thread" is QThread at QThread(0x7ffc138c5350)
Object "receiver" belongs to QThread QThread(0xdae1e0)
Object "receiver" belongs to QThread QThread(0x7ffc138c5350)
"Starting app" [0x7f032fb32780]
"Starting transmitter" [0x7f032ae77700]
"Starting receiver" [0x7f032b678700]
"transmitting, count=3" [0x7f032ae77700]
"receive: DERIVED" [0x7f032b678700]
"transmitting, count=2" [0x7f032ae77700]
"receive: DERIVED" [0x7f032b678700]
"transmitting, count=1" [0x7f032ae77700]
"Stopping transmitter" [0x7f032ae77700]
"receive: DERIVED" [0x7f032b678700]
附加:它也可以在PyQt的
代碼:
#!/usr/bin/env python2
import logging
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
import sys
import threading
import time
from PyQt4.QtCore import (
QCoreApplication,
QObject,
QThread,
pyqtSignal,
pyqtSlot,
)
def debug_object(obj):
logger.debug("Object {} belongs to QThread {}".format(obj, obj.thread()))
def debug_thread(thread_name, thread):
logger.debug("{} is QThread {}".format(thread_name, thread))
def report(msg):
logger.info("{} [{}]".format(msg, threading.current_thread().name))
class Transmitter(QObject):
transmit = pyqtSignal()
finished = pyqtSignal()
def start(self):
count = 3
report("Starting transmitter")
while count > 0:
time.sleep(1) # seconds
report("transmitting, count={}".format(count))
self.transmit.emit()
count -= 1
report("Stopping transmitter")
self.finished.emit()
class Base(QObject):
def __init__(self, parent=None):
super(Base, self).__init__(parent=parent)
@pyqtSlot()
def start(self):
report("Starting receiver")
@pyqtSlot()
def receive(self):
report("receive: BASE")
class Derived(Base):
def __init__(self, parent=None):
super(Derived, self).__init__(parent=parent)
@pyqtSlot()
def receive(self):
report("receive: DERIVED")
USE_DERIVED = True
if __name__ == '__main__':
logging.basicConfig()
logger.setLevel(logging.DEBUG)
# Objects
app = QCoreApplication(sys.argv)
tx_thread = QThread()
debug_thread("tx_thread", tx_thread)
transmitter = Transmitter()
debug_object(transmitter)
transmitter.moveToThread(tx_thread)
debug_object(transmitter)
rx_thread = QThread()
debug_thread("rx_thread", rx_thread)
if USE_DERIVED:
receiver = Derived()
else:
receiver = Base()
debug_object(receiver)
receiver.moveToThread(rx_thread)
debug_object(receiver)
# Signals: startup
tx_thread.started.connect(transmitter.start)
rx_thread.started.connect(receiver.start)
# ... shutdown
transmitter.finished.connect(tx_thread.quit)
tx_thread.finished.connect(rx_thread.quit)
rx_thread.finished.connect(app.quit)
# ... action
transmitter.transmit.connect(receiver.receive)
# Go
rx_thread.start()
tx_thread.start()
report("Starting app")
app.exec_()
輸出:
DEBUG:__main__:tx_thread is QThread <PyQt4.QtCore.QThread object at 0x7fd0b7ad0770>
DEBUG:__main__:Object <__main__.Transmitter object at 0x7fd0b7ad0808> belongs to QThread <PyQt4.QtCore.QThread object at 0x7fd0b7ad08a0>
DEBUG:__main__:Object <__main__.Transmitter object at 0x7fd0b7ad0808> belongs to QThread <PyQt4.QtCore.QThread object at 0x7fd0b7ad0770>
DEBUG:__main__:rx_thread is QThread <PyQt4.QtCore.QThread object at 0x7fd0b7ad08a0>
DEBUG:__main__:Object <__main__.Derived object at 0x7fd0b7ad0938> belongs to QThread <PyQt4.QtCore.QThread object at 0x7fd0b7ad09d0>
DEBUG:__main__:Object <__main__.Derived object at 0x7fd0b7ad0938> belongs to QThread <PyQt4.QtCore.QThread object at 0x7fd0b7ad08a0>
INFO:__main__:Starting app [MainThread]
INFO:__main__:Starting transmitter [Dummy-1]
INFO:__main__:Starting receiver [Dummy-2]
INFO:__main__:transmitting, count=3 [Dummy-1]
INFO:__main__:receive: DERIVED [Dummy-2]
INFO:__main__:transmitting, count=2 [Dummy-1]
INFO:__main__:receive: DERIVED [Dummy-2]
INFO:__main__:transmitting, count=1 [Dummy-1]
INFO:__main__:Stopping transmitter [Dummy-1]
INFO:__main__:receive: DERIVED [Dummy-2]
確認@ 101的在Python 3
結果正如下面描述。所有工作都很好,只需刪除所有@Slot()裝飾器。
所以它似乎是一個與插槽裝飾器相關的PySide錯誤。
非常感謝!
這會讓如果差'receive'在'Derived'沒有覆蓋在'Base'? – 101
謝謝 - 更多信息添加到上面的問題。 –
感謝@ 101的回答,我現在發現了一個關於這個問題的開放bug報告:https://bugreports.qt.io/browse/PYSIDE-249 –