2013-04-14 24 views
5

Burada verilen yapıya dayanarak UI duyarlılığını korurken bazı çalışmalar yapmak için bir QThread oluşturan bir QDialog var: How To Really, Truly Use QThreads; The Full Explanation. Ben ne istiyorum neQDialog reddetme üzerinde QThread sonlandırılıyor()

QThread: Destroyed while thread is still running

döngüde içindir: (kullanıcıya iptal basarak veya iletişim kapatma) olarak adlandırılır() reddetmek eğer iplik hala çalışırken Ancak, bir hata alıyorum İşçiyi erken kırmak, sonra arka planda bazı temizlik yapmak (örneğin, bazı kuyrukları temizlemek, bir sinyal yayarlar). Bunu kendi "iptal etme" fonksiyonumla yapmayı başardım, ama nasıl reddedileceğini() (ve çağrılabilecek birçok yolu) güzelce nasıl oynayacağım? İletişim kutusunun temizlik için beklemeyi engellemesini istemiyorum - sadece arka planda çalışmaya devam etmeli, sonra zarafetle çıkmalıdır.

Sorunu gösteren örnek kod örneğine bakın. Herhangi bir yardım büyük takdir edilecektir.

#!/usr/bin/env python 

from PyQt4 import QtCore, QtGui 
import sys 
import time 

class Worker(QtCore.QObject): 
    def __init__(self): 
     QtCore.QObject.__init__(self) 

    def process(self): 
     # dummy worker process 
     for n in range(0, 10): 
      print 'process {}'.format(n) 
      time.sleep(0.5) 
     self.finished.emit() 

    finished = QtCore.pyqtSignal() 

class Dialog(QtGui.QDialog): 
    def __init__(self): 
     QtGui.QDialog.__init__(self) 
     self.init_ui() 

    def init_ui(self): 
     self.layout = QtGui.QVBoxLayout(self) 
     self.btn_run = QtGui.QPushButton('Run', self) 
     self.layout.addWidget(self.btn_run) 
     self.btn_cancel = QtGui.QPushButton('Cancel', self) 
     self.layout.addWidget(self.btn_cancel) 

     QtCore.QObject.connect(self.btn_run, QtCore.SIGNAL('clicked()'), self.run) 
     QtCore.QObject.connect(self.btn_cancel, QtCore.SIGNAL('clicked()'), self.reject) 

     self.show() 
     self.raise_() 

    def run(self): 
     # start the worker thread 
     self.thread = QtCore.QThread() 
     self.worker = Worker() 
     self.worker.moveToThread(self.thread) 
     QtCore.QObject.connect(self.thread, QtCore.SIGNAL('started()'), self.worker.process) 
     QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.thread.quit) 
     QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.worker.deleteLater) 
     QtCore.QObject.connect(self.thread, QtCore.SIGNAL('finished()'), self.thread.deleteLater) 
     self.thread.start() 

def main(): 
    app = QtGui.QApplication(sys.argv) 
    dlg = Dialog() 
    ret = dlg.exec_() 

if __name__ == '__main__': 
    main() 

cevap

8

Sizin sorundur: iletişim kapalı veya iptal düğmesine basıldıktan sonra Qt parçacığı hala çalışırken self.thread, Python tarafından arındırılır.

Böyle bir durumdan kaçınmak için, bu iş parçacığına bir üst öğe atayabilirsiniz. incelikle Qt sonlandırılır önce Örneğin,

Sonra Qt yerine PyQt'de ait olacak

    def run(self): 
     # start the worker thread 
     self.thread = QtCore.QThread(self) 
     self.worker = Worker() 
     self.worker.moveToThread(self.thread) 
     QtCore.QObject.connect(self.thread, QtCore.SIGNAL('started()'), self.worker.process) 
     QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.thread.quit) 
     QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.worker.deleteLater) 
     QtCore.QObject.connect(self.thread, QtCore.SIGNAL('finished()'), self.thread.deleteLater) 
     self.thread.start() 

ve dolayısıyla GC ile toplanmış edilmez. Aslında, bu yöntem sadece Qt'nin şikayet etmemesini ve sorunu tamamen çözmemesini sağlar. Bir iş parçacığını hassas bir şekilde sonlandırmak için ortak yaklaşım, çalışan işlevinin durmasını bildiren bir bayrak kullanmaktadır. Örneğin
:

class Worker(QtCore.QObject): 
    def __init__(self): 
     QtCore.QObject.__init__(self) 

    def process(self): 
     # dummy worker process 
     self.flag = False 
     for n in range(0, 10): 
      if self.flag: 
       print 'stop' 
       break 
      print 'process {}'.format(n) 
      time.sleep(0.5) 
     self.finished.emit() 

    finished = QtCore.pyqtSignal() 

class Dialog(QtGui.QDialog): 
    def __init__(self, parent=None): 
     QtGui.QDialog.__init__(self, parent) 
     self.init_ui() 

    def init_ui(self): 
     self.layout = QtGui.QVBoxLayout(self) 
     self.btn_run = QtGui.QPushButton('Run', self) 
     self.layout.addWidget(self.btn_run) 
     self.btn_cancel = QtGui.QPushButton('Cancel', self) 
     self.layout.addWidget(self.btn_cancel) 

     QtCore.QObject.connect(self.btn_run, QtCore.SIGNAL('clicked()'), self.run) 
     QtCore.QObject.connect(self.btn_cancel, QtCore.SIGNAL('clicked()'), self.reject) 

     QtCore.QObject.connect(self, QtCore.SIGNAL('rejected()'), self.stop_worker) 

     self.show() 
     self.raise_() 

    def stop_worker(self): 
     print 'stop' 
     self.worker.flag = True 

    def run(self): 
     # start the worker thread 
     self.thread = QtCore.QThread(self) 
     self.worker = Worker() 
     self.worker.moveToThread(self.thread) 
     QtCore.QObject.connect(self.thread, QtCore.SIGNAL('started()'), self.worker.process) 
     QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.thread.quit) 
     QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.worker.deleteLater) 
     QtCore.QObject.connect(self.thread, QtCore.SIGNAL('finished()'), self.thread.deleteLater) 
     self.thread.start() 
+0

Bu yöntemle Çıkmadan önce temizlemek için herhangi bir iş parçacığı için beklemek ana uygulamayı almak mümkündür? Kodu daha büyük bir uygulamada (QGIS) eklenti olarak çalıştırıyorum. – Snorfalorpagus

+0

@snorfalorpagus: Gönderiyi güncelledim. Bayrağı kullanmanın bir iş parçacığını durdurmanın en güvenli yolu olduğunu düşünüyorum. – nymk

İlgili konular