2016-09-20 66 views
3

我的代碼有線程,但是當我關閉gui,它仍然在後臺工作。我怎樣才能停止線程?有什麼停止(),關閉()? 我不使用信號,插槽?我必須使用這個嗎?Qthread仍然工作時,我關閉蟒pyqt

from PyQt4 import QtGui, QtCore 
import sys 
import time 
import threading 

class Main(QtGui.QMainWindow): 
    def __init__(self, parent=None): 
     super(Main, self).__init__(parent) 
     self.kac_ders=QtGui.QComboBox() 
     self.bilgi_cek=QtGui.QPushButton("Save") 
     self.text=QtGui.QLineEdit() 
     self.widgetlayout=QtGui.QFormLayout() 
     self.widgetlar=QtGui.QWidget() 
     self.widgetlar.setLayout(self.widgetlayout) 
     self.bilgiler=QtGui.QTextBrowser() 
     self.bilgi_cek.clicked.connect(self.on_testLoop) 
     self.scrollArea = QtGui.QScrollArea() 
     self.scrollArea.setWidgetResizable(True) 
     self.scrollArea.setWidget(self.widgetlar) 
     self.analayout=QtGui.QVBoxLayout() 
     self.analayout.addWidget(self.text) 
     self.analayout.addWidget(self.bilgi_cek) 
     self.analayout.addWidget(self.bilgiler) 
     self.centralWidget=QtGui.QWidget() 
     self.centralWidget.setLayout(self.analayout) 
     self.setCentralWidget(self.centralWidget) 

    def on_testLoop(self): 
     self.c_thread=threading.Thread(target=self.kontenjan_ara) 
     self.c_thread.start() 

    def kontenjan_ara(self): 

     while(1): 
       self.bilgiler.append(self.text.text()) 
       time.sleep(10) 


app = QtGui.QApplication(sys.argv) 
myWidget = Main() 
myWidget.show() 
app.exec_() 
+1

此代碼不是線程安全的。您不允許從輔助線程訪問GUI對象(如'self.text')。你應該重構你的代碼以使用'QThread'和信號和插槽,或者簡單地使用一個線程和一個'QTimer'(因爲大部分時間你的線程正在睡覺) –

回答

2

我選擇重寫一下這個答案,因爲我沒有正確看待問題的上下文。正如其他答案和評論所說,你的代碼缺乏線程安全性。

解決此問題的最佳方法是嘗試真正地「在線程中」,將自己限制爲僅使用生活在同一線程中的對象或稱爲「線程安全」的函數。

投擲某些信號和插槽會有幫助,但也許你想回想一下原來的問題。在當前的代碼,每次按下按鈕,一個新的線程在推出,這將每10秒,做兩件事情: - 閱讀從self.text 一些文字 - 它附加到self.bilgiler

兩者的這些操作是非線程安全的,並且必須從擁有這些對象的線程(主線程)調用必須。您希望使工作線程「計劃&等待」讀取&附加oeprations,而不是簡單地「執行」它們。

我推薦使用其他答案(線程停止問題可以通過使用與Qt的事件循環很好地集成在一起的適當的QThreads自動修復),這將使您使用更清晰的方法,更加集成Qt。

您可能還需要重新考慮你的問題,因爲也許有您的問題,一個簡單的方法,例如:不產卵線程每次bilgi_cek被點擊,或者使用Queue的對象,這樣你的工人是完全無關的統一發票,並且只能使用線程安全對象與它進行交互。

祝你好運,對不起,如果我造成任何困惑。我的原始答案仍然可用here。我認爲將其他答案標記爲這個問題的有效答案是明智的。

+0

謝謝,它工作,但我有一些問題。當我關閉gui時,如何設置self.closed? 'self.closed = threading.Event()'當我關閉gui時,這一行就明白了。它必須是信號,不是嗎? –

+0

這是通過重寫'closeEvent'並調用'self.closed.set()'來處理的。請注意,其他答案可能是更好的解決方案,因爲它會直接使用Qt功能在循環停止時使線程死掉,並且因爲它還修復了代碼中缺少線程安全性的問題。 – pistache

+0

其實我真的沒有關於pyqt的經驗和能力,你的解決方案解決了我的問題,但是現在出現了一些新問題,我的代碼並不像這個例子那麼簡單,並且在我的代碼中有兩個線程。現在,我甚至不知道如何調用變量比其他類等。其他解決方案的第一部分工作,但我不明白第二部分。我如何與我的代碼同步,我只是想一想。我非常感謝你的關心,非常感謝你一次又一次。 –

3

有幾件事情:

  1. 你不應該在主線程調用外GUI代碼。 GUI元素不是線程安全的。 self.kontenjan_ara更新和從GUI元素讀取,它不應該是您的thread的目標。

  2. 在幾乎所有情況下,您應該使用QThreads而不是python線程。它們與Qt中的事件和信號系統很好地結合在一起。

如果你只是想運行的東西每隔幾秒鐘,你可以使用一個QTimer

def __init__(self, parent=None): 
    ... 
    self.timer = QTimer(self) 
    self.timer.timeout.connect(self.kontenjan_ara) 
    self.timer.start(10000) 

def kontenjan_ara(self): 
    self.bilgiler.append(self.text.text()) 

如果你的線程的計算操作更加複雜,你可以創建一個工作線程和工人之間傳遞數據線程和使用信號的主GUI線程。

class Worker(QObject): 

    work_finished = QtCore.pyqtSignal(object) 

    @QtCore.pyqtSlot() 
    def do_work(self): 
     data = 'Text' 
     while True: 
      # Do something with data and pass back to main thread 
      data = data + 'text' 
      self.work_finished.emit(data) 
      time.sleep(10) 


class MyWidget(QtGui.QWidget): 

    def __init__(self, ...) 
     ... 
     self.worker = Worker() 
     self.thread = QtCore.QThread(self) 
     self.worker.work_finished.connect(self.on_finished) 
     self.worker.moveToThread(self.thread) 
     self.thread.started.connect(self.worker.do_work) 
     self.thread.start() 

    @QtCore.pyqtSlot(object) 
    def on_finished(self, data): 
     self.bilgiler.append(data) 
     ... 

當主線程退出事件循環時,Qt會自動終止所有子線程。

+0

這比我的回答更好,更正確,但我認爲OP希望在每次點擊按鈕時啓動一名工作人員,並希望該工作人員從GUI讀取數據,而不僅僅是異步通知它。 – pistache

相關問題