python – 從次要執行緒將stdout和stderr重定向到PyQt4 QTextEdit
堆疊溢位.再次,我在一個迫切需要的時候來找你,在瘋狂的邊緣岌岌可危.這個問題 – 從標題可以看出,這是我在這裡回答的另外幾個問題的合併.
我有一個PyQt應用程式,我想重新路由stdout和stderr流到我的GUI中的QTextEdit,沒有延遲.
最初,我發現以下堆疊溢位答案:ofollow,noindex" target="_blank">http://stackoverflow.com/a/17145093/629404
這是完美的,但有一個警告:如果stdout或stderr在CPU處理相對較長的方法時多次更新,則當主執行緒返回到應用程式迴圈時,所有更新都將同時顯示.不幸的是,我有幾種方法需要長達20秒才能完成(網路相關),因此應用程式將無響應,並且QTextEdit不會更新 – 直到它們完成.
為了解決這個問題,我把所有的GUI處理委託給主執行緒,我已經產生了第二個執行緒來處理更長的網路操作,使用pyqtSignals通知主執行緒什麼時候完成並通過回來的結果.當我開始測試這樣寫的程式碼時,python直譯器立即開始崩潰,沒有任何警告.
這是非常脆弱的地方:Python正在崩潰,因為 – 使用上面包含的連結中的類 – 我已將sys.stdout / err流分配給QTextEdit小部件; PyQt小部件不能從應用程式執行緒之外的任何執行緒修改,並且由於stdout和stderr的更新來自我建立的輔助工作執行緒,所以它們違反了此規則.我已經註釋掉了我重定向輸出流的程式碼段,當然這個程式執行沒有錯誤.
這讓我回到正方形,讓我感到困惑;假設我繼續處理主執行緒中的GUI相關操作,並處理次要執行緒中的計算和更長的操作(我明白是在使用者觸發事件時保持應用程式阻止的最佳方法),我怎麼能將Stdout和Stderr從兩個執行緒重定向到QTextEdit小部件?上述連結中的類對主執行緒工作正常,但是當更新來自第二個執行緒時,會殺死python– 因為上述原因.
首先,1為了實現執行緒不安全,許多堆疊溢位的例子是!
解決方案是使用執行緒安全物件(如Python Queue.Queue)來調解資訊的傳輸.我附下了一些示例程式碼,將stdout重定向到Python Queue.該佇列由QThread讀取,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_()
http://stackoverflow.com/questions/21071448/redirecting-stdout-and-stderr-to-a-pyqt4-qtextedit-from-a-secondary-thread