เมื่อNathan Wชี้ให้เห็นวิธีการทำเช่นนี้คือการมัลติเธรด แต่การทำคลาสย่อย QThread ไม่ใช่วิธีปฏิบัติที่ดีที่สุด ดูที่นี่: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
ดูตัวอย่างวิธีสร้าง a QObject
จากนั้นย้ายไปยัง a QThread
(เช่นวิธี "ถูกต้อง" เพื่อทำ) ตัวอย่างนี้คำนวณพื้นที่ทั้งหมดของฟีเจอร์ทั้งหมดในเวกเตอร์เลเยอร์ (ใช้ QGIS 2.0 API ใหม่!)
อันดับแรกเราสร้างวัตถุ "ผู้ปฏิบัติงาน" ที่จะยกของหนักสำหรับเรา:
class Worker(QtCore.QObject):
def __init__(self, layer, *args, **kwargs):
QtCore.QObject.__init__(self, *args, **kwargs)
self.layer = layer
self.total_area = 0.0
self.processed = 0
self.percentage = 0
self.abort = False
def run(self):
try:
self.status.emit('Task started!')
self.feature_count = self.layer.featureCount()
features = self.layer.getFeatures()
for feature in features:
if self.abort is True:
self.killed.emit()
break
geom = feature.geometry()
self.total_area += geom.area()
self.calculate_progress()
self.status.emit('Task finished!')
except:
import traceback
self.error.emit(traceback.format_exc())
self.finished.emit(False, self.total_area)
else:
self.finished.emit(True, self.total_area)
def calculate_progress(self):
self.processed = self.processed + 1
percentage_new = (self.processed * 100) / self.feature_count
if percentage_new > self.percentage:
self.percentage = percentage_new
self.progress.emit(self.percentage)
def kill(self):
self.abort = True
progress = QtCore.pyqtSignal(int)
status = QtCore.pyqtSignal(str)
error = QtCore.pyqtSignal(str)
killed = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal(bool, float)
ในการใช้ผู้ปฏิบัติงานเราต้อง initalise มันด้วยเวกเตอร์เลเยอร์, ย้ายไปที่เธรด, เชื่อมต่อสัญญาณบางอย่าง, จากนั้นเริ่มมัน. อาจเป็นการดีที่สุดที่จะดูบล็อกที่ลิงก์ด้านบนเพื่อทำความเข้าใจว่าเกิดอะไรขึ้นที่นี่
thread = QtCore.QThread()
worker = Worker(layer)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.progress.connect(self.ui.progressBar)
worker.status.connect(iface.mainWindow().statusBar().showMessage)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
worker.finished.connect(thread.quit)
thread.start()
ตัวอย่างนี้แสดงประเด็นสำคัญบางประการ:
- ทุกอย่างภายใน
run()
วิธีการของผู้ปฏิบัติงานอยู่ในคำสั่งลองยกเว้น เป็นการยากที่จะกู้คืนเมื่อรหัสของคุณขัดข้องภายในเธรด มันส่งเสียง traceback QgsMessageLog
ผ่านสัญญาณข้อผิดพลาดที่ฉันมักจะเชื่อมต่อกับที่
- สัญญาณเสร็จแล้วจะบอกวิธีการเชื่อมต่อหากกระบวนการเสร็จสมบูรณ์เป็นผลสำเร็จ
- สัญญาณความคืบหน้าจะถูกเรียกใช้เมื่อเปอร์เซ็นต์การเปลี่ยนแปลงเสร็จสมบูรณ์แทนที่จะเป็นหนึ่งครั้งสำหรับทุกคุณสมบัติ สิ่งนี้ป้องกันไม่ให้มีการเรียกจำนวนมากเกินไปในการอัปเดตแถบความคืบหน้าซึ่งทำให้กระบวนการของผู้ปฏิบัติงานช้าลงซึ่งจะทำลายจุดทั้งหมดของการเรียกใช้งานในเธรดอื่น: เพื่อแยกการคำนวณออกจากส่วนติดต่อผู้ใช้
- ผู้ปฏิบัติงานใช้
kill()
วิธีการที่อนุญาตให้ฟังก์ชั่นยุติอย่างสง่างาม อย่าพยายามใช้terminate()
วิธีการในQThread
- สิ่งเลวร้ายอาจเกิดขึ้นได้!
อย่าลืมติดตามthread
และworker
วัตถุในโครงสร้างปลั๊กอินของคุณ Qt โกรธถ้าคุณทำไม่ได้ วิธีที่ง่ายที่สุดในการทำเช่นนี้คือเก็บไว้ในกล่องโต้ตอบเมื่อคุณสร้างขึ้นเช่น:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
หรือคุณสามารถให้ Qt เป็นเจ้าของ QThread:
thread = QtCore.QThread(self)
ฉันใช้เวลานานในการขุดบทเรียนทั้งหมดเพื่อที่จะนำเทมเพลตนี้มารวมกัน แต่ตั้งแต่นั้นมาฉันก็นำมันกลับมาใช้ใหม่ทุกที่