Qt信号未按发出顺序处理

1 投票
1 回答
584 浏览
提问于 2025-04-18 07:51

简短版本:

突然向图形界面发送信号会导致它们携带的数据在文本框中乱七八糟地插入。

详细版本:

我有一个用PyQt做的图形界面,它连接着一个Python脚本。这个脚本在一个单独的线程中运行,并向stdoutstderr写入数据。脚本可以独立执行,但我创建了一个图形界面,以提供更友好的用户体验。

为了捕获stdout/stderr的输出并将其写入图形界面,我在这个线程中重写了sys.stdoutsys.stderr,用一个有写入方法的对象替代。

sys.stdout = StreamWrapper(sys.stdout, self, self.mutex)
sys.stderr = StreamWrapper(sys.stderr, self, self.mutex)

StreamWrapper:

class StreamWrapper:
    def __init__(self, stream, thread, mutex):
        self.stream = stream # reference to original stream (stdout or stderr)
        self.thread = thread # thread in which this object is initialised.
        self.mutex  = mutex  # mutex ensuring write is only called once at a time

    def write(self, msg):
        with QtCore.QMutexLocker(self.mutex):
            self.thread.emit(QtCore.SIGNAL('update_console(QString)'), msg)
            self.stream.write(msg)
            time.sleep(0.5)

这个写入方法会向图形界面发送一个信号和一条消息。图形界面(在主线程中)接收到这个信号后,会相应地更新一个普通文本框(叫做consoleOutput):

self.connect(self.thread, QtCore.SIGNAL('update_console(QString)'), self.ui.consoleOutput.insertPlainText)

我注意到,当stdout突然有大量输出时,图形界面中的输出会乱七八糟地写入(顺序完全不对,且会把之前的输出挤开,但不会覆盖)。而我在控制台中写入的输出则是完全有序的。

为了解决这个问题,我尝试引入一个互斥锁,但没有成功。然后我引入了一个延迟(通过调用time.sleep(0.5)),这虽然是个笨办法,但解决了问题。

经过观察信号的发送和处理顺序,我发现信号是按照发送的顺序被处理的(我没有重写任何标准行为),所以顺序可能不是问题所在。

有什么想法吗?

编辑:

我创建了一个可以编译的示例,包括三个需要放在同一目录下的文件:

这个可编译的示例会随机打印数据到屏幕上,并带有一个计数器来跟踪打印的顺序。这个截图显示了一些数据与其他数据不同步。

1 个回答

4

其实解决方案比我想的简单多了。

基本上,问题在于我需要在插入文本之前之后手动移动光标到最后。

我跟随的解决方案详细信息可以在这里找到:

在我的情况下,我首先更新了接收信号的槽函数,把插入文本的操作放在一个方法里(updateConsole):

self.connect(self.fpga_thread, QtCore.SIGNAL('update_console(QString)'), self.updateConsole)

看起来是这样的:

def updateConsole(self, msg):
    self.ui.consoleOutput.moveCursor(QtGui.QTextCursor.End)
    self.ui.consoleOutput.insertPlainText(msg)
    self.ui.consoleOutput.moveCursor(QtGui.QTextCursor.End)

现在它可以正常工作了,问题似乎和滚动有关。如果我在文本框更新时滚动(而不手动移动光标),它会把文本放在当前显示文本的底部(而不是文本框的底部)。

我最终不需要引入延迟,去掉了sleep,但为了保险起见,我保留了互斥锁。

撰写回答