我使用以下行来通知用户有关状态:
iface.mainWindow().statusBar().showMessage("Status:" + str(i))
该插件大约需要2分钟才能在我的数据集上运行,但是Windows将其检测为“未响应”并停止显示状态更新。对于新用户来说,这不是很好,因为它看起来好像程序崩溃了。
有没有解决的办法,这样用户就不会对插件的状态感到困惑?
我使用以下行来通知用户有关状态:
iface.mainWindow().statusBar().showMessage("Status:" + str(i))
该插件大约需要2分钟才能在我的数据集上运行,但是Windows将其检测为“未响应”并停止显示状态更新。对于新用户来说,这不是很好,因为它看起来好像程序崩溃了。
有没有解决的办法,这样用户就不会对插件的状态感到困惑?
Answers:
正如Nathan W所指出的那样,实现此目标的方法是使用多线程,但是对QThread进行子类化不是最佳实践。参见此处:http : //mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
参见下面的示例,了解如何创建QObject
,然后将其移至的方式QThread
(即“正确”的方式)。本示例计算矢量层中所有要素的总面积(使用新的QGIS 2.0 API!)。
首先,我们创建“ worker”对象,它将为我们完成繁重的工作:
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)
要使用worker,我们需要在向量层上初始化它,将其移动到线程中,连接一些信号,然后启动它。最好查看上面链接的博客,以了解此处的情况。
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()
worker方法中的所有内容都在try-except语句中。当代码在线程内崩溃时,很难恢复。它通过错误信号发出回溯,我通常将其连接到QgsMessageLog
。kill()
方法,该方法允许函数正常终止。不要尝试使用该terminate()
方法QThread
-可能会发生不好的事情!确保在插件结构中的某个位置跟踪您thread
和worker
对象。如果不这样做,Qt会生气。最简单的方法是在创建它们时将它们存储在对话框中,例如:
thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)
或者,您可以让Qt拥有QThread的所有权:
thread = QtCore.QThread(self)
我花了很长时间来整理所有教程,以便将这个模板放在一起,但是从那时起,我就一直在各处重复使用它。
worker.progress.connect(self.ui.progressBar)
为其他内容,但是每次运行它时,qgis-bin都崩溃了。我没有调试python代码或qgis的经验。我得到的只是Access violation reading location 0x0000000000000008
看起来好像是空的。是否缺少一些设置代码才能在处理脚本中使用它?
唯一的真正方法是使用多线程。
class MyLongRunningStuff(QThread):
progressReport = pyqtSignal(str)
def __init__(self):
QThread.__init__(self)
def run(self):
# do your long runnning thing
self.progressReport.emit("I just did X")
thread = MyLongRunningStuff()
thread.progressReport.connect(self.updatetheuimethod)
thread.start()
一些额外的阅读http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/
注意有些人不喜欢从QThread继承,显然这不是“正确”的方法,但是它确实可以工作。
由于此问题相对较旧,因此值得更新。使用QGIS 3,可以使用QgsTask.fromFunction(),QgsProcessingAlgRunnerTask()和QgsApplication.taskManager()。addTask()方法。
有关更多信息,例如MARCO BERNASOCCHI的《在PyQGIS3中使用线程》