堆栈溢出。再一次,我在急需的时候来到你们身边,摇摇欲坠地走在精神错乱的边缘。这个问题--从标题中可以明显看出--是我在这里看到的其他几个问题的融合。
我有一个PyQt应用程序,我想将标准输出和标准错误流重新路由到没有delay.的中的QTextEdit
最初,我找到了以下堆栈溢出的答案: https://stackoverflow.com/a/17145093/629404
这种方法工作得很好,但有一点需要注意:如果在CPU处理一个相对较长的方法时多次更新stdout或stderr,那么当主线程返回到应用程序循环时,所有的更新都会同时出现。不幸的是,我有一些方法需要20秒才能完成(与网络相关),因此应用程序变得没有响应-并且QTextEdit不会更新-直到它们完成。
为了解决这个问题,python i将所有的图形用户界面处理委托给了主线程,并且我已经产生了第二个线程来处理更长的网络操作,使用pyqtSignals通知主线程工作何时完成并返回结果。当我开始测试以这种方式编写的代码时,python解释器立即开始崩溃,没有任何警告。
这就是它变得非常糟糕的地方: Python崩溃是因为-使用上面包含的链接中的类-我已经将sys.stdout/err流分配给了QTextEdit小部件;PyQt小部件不能从除应用程序线程以外的任何线程修改,而且由于对stdout和stderr的更新来自我创建的辅助工作线程,因此它们违反了这一规则。我注释掉了重定向输出流的代码部分,毫无疑问,程序运行没有错误。
这让我回到了起点,并使我陷入了一个令人困惑的境地;假设我继续在主线程中处理与图形用户界面相关的操作,并在辅助线程中处理计算和更长的操作(我逐渐理解这是防止应用程序在用户触发事件时阻塞的最佳方法),我如何将Stdout和Stderr从两个线程重定向到QTextEdit小部件?上面链接中的类在主线程上工作得很好,但当更新来自第二个线程时,就会杀死python -原因如上所述。
发布于 2014-01-12 14:11:27
首先,为了认识到线程溢出是多么的不安全,堆栈溢出上的许多例子是+1!
解决方案是使用线程安全的对象(如Python Queue.Queue
)来协调信息的传输。我在下面附加了一些示例代码,它们将stdout
重定向到Python Queue
。这个Queue
由一个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_()
https://stackoverflow.com/questions/21071448
复制相似问题