将stdout和stderr从辅助线程重定向到PyQt4 QTextEdit

 牛奶姆姆_592 发布于 2023-02-04 13:17

堆栈溢出.再一次,我在一个迫切需要的时候来找你,在疯狂的边缘岌岌可危地摇摇欲坠.这个问题 - 从标题中可以看出 - 是我在这里回答的其他几个问题的合并.

我有一个PyQt应用程序,我想将stdout和stderr流重新路由到我的GUI中的QTextEdit,没有延迟.

最初,我发现以下堆栈溢出答案: https ://stackoverflow.com/a/17145093/629404

这很有效,但有一点需要注意:如果在CPU处理相对较长的方法时多次更新stdout或stderr,则当主线程返回到应用程序循环时,所有更新都会同时显示.不幸的是,我有一些方法需要20秒才能完成(网络相关),因此应用程序变得无响应 - 并且QTextEdit不会更新 - 直到它们完成.

为了解决这个问题,我将所有GUI处理委托给主线程,并且我已经产生了第二个线程以处理更长的网络操作,使用pyqtSignals通知主线程工作何时完成并通过回来的结果.当我开始测试以这种方式编写的代码时,python解释器立刻开始崩溃而没有任何警告.

这是非常令人讨厌的地方:Python正在崩溃,因为 - 使用上面包含的链接中的类 - 我已经将sys.stdout/err流分配给QTextEdit小部件; PyQt小部件不能从除应用程序线程之外的任何线程修改,并且由于对stdout和stderr的更新来自我创建的辅助工作线程,因此它们违反了此规则.我已经注释掉了我重定向输出流的代码部分,果然,程序运行没有错误.

这让我回到原点,让我陷入困惑的境地; 假设我继续在主线程中处理GUI相关操作并处理辅助线程中的计算和更长时间的操作(我已经理解这是在用户触发事件时阻止应用程序阻塞的最佳方法),我该怎么办将Stdout和Stderr从两个线程重定向到QTextEdit小部件?上面链接中的类对于主线程来说效果很好,但是当更新来自第二个线程时,由于上述原因会杀死python.

1 个回答
  • 首先,+1用于实现堆栈溢出中的许多示例的线程不安全性!

    解决方案是使用线程安全对象(如Python Queue.Queue)来调解信息传输.我在下面添加了一些示例代码,重定向stdout到Python Queue.这Queue是由a读取的QThread,它通过Qt的信号/插槽机制将内容发送到主线程(发出信号是线程安全的).然后主线程将文本写入文本编辑.

    希望很清楚,如果不是,请随时提问!

    编辑:请注意,提供的代码示例不能很好地清理QThreads,因此您在退出时会收到警告.我会留给你扩展到你的用例并清理线程

    import sys
    from Queue import Queue
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    # The new Stream Object which replaces the default stream associated with sys.stdout
    # This object just puts data in a queue!
    class WriteStream(object):
        def __init__(self,queue):
            self.queue = queue
    
        def write(self, text):
            self.queue.put(text)
    
    # A QObject (to be run in a QThread) which sits waiting for data to come through a Queue.Queue().
    # It blocks until data is available, and one it has got something from the queue, it sends
    # it to the "MainThread" by emitting a Qt Signal 
    class MyReceiver(QObject):
        mysignal = pyqtSignal(str)
    
        def __init__(self,queue,*args,**kwargs):
            QObject.__init__(self,*args,**kwargs)
            self.queue = queue
    
        @pyqtSlot()
        def run(self):
            while True:
                text = self.queue.get()
                self.mysignal.emit(text)
    
    # An example QObject (to be run in a QThread) which outputs information with print
    class LongRunningThing(QObject):
        @pyqtSlot()
        def run(self):
            for i in range(1000):
                print i
    
    # An Example application QWidget containing the textedit to redirect stdout to
    class MyApp(QWidget):
        def __init__(self,*args,**kwargs):
            QWidget.__init__(self,*args,**kwargs)
    
            self.layout = QVBoxLayout(self)
            self.textedit = QTextEdit()
            self.button = QPushButton('start long running thread')
            self.button.clicked.connect(self.start_thread)
            self.layout.addWidget(self.textedit)
            self.layout.addWidget(self.button)
    
        @pyqtSlot(str)
        def append_text(self,text):
            self.textedit.moveCursor(QTextCursor.End)
            self.textedit.insertPlainText( text )
    
        @pyqtSlot()
        def start_thread(self):
            self.thread = QThread()
            self.long_running_thing = LongRunningThing()
            self.long_running_thing.moveToThread(self.thread)
            self.thread.started.connect(self.long_running_thing.run)
            self.thread.start()
    
    # Create Queue and redirect sys.stdout to this queue
    queue = Queue()
    sys.stdout = WriteStream(queue)
    
    # Create QApplication and QWidget
    qapp = QApplication(sys.argv)  
    app = MyApp()
    app.show()
    
    # Create thread that will listen on the other end of the queue, and send the text to the textedit in our application
    thread = QThread()
    my_receiver = MyReceiver(queue)
    my_receiver.mysignal.connect(app.append_text)
    my_receiver.moveToThread(thread)
    thread.started.connect(my_receiver.run)
    thread.start()
    
    qapp.exec_()
    

    2023-02-04 13:19 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有