我为 QGIS 3.36.2(使用 Python 3.12.3)编写了一个 Python 脚本,它执行以下操作:
步骤 1 仅发生一次。 2. + 3. 应无限期运行,但如果出现错误或用户停止脚本,则停止。为了测试我只想运行它,例如10次。
到目前为止我发现/尝试过的:
time.sleep()
(按照这里的建议)完全冻结了QGIS。sched
调度程序(参见下面的代码)也会阻塞主线程并冻结QGIS。threading.Timer
每次都会启动一个新线程(并且您将无法停止循环),因此 answer 建议不要使用它 - 因此未经测试。Tkinter
,因为QGIS的python不支持。asyncio
(如here建议)似乎在这个QGIS版本中也没有得到完全支持(尝试运行this示例时出现很多错误,但它在Python 3.9控制台中工作正常)而且它也很友善阻塞,因为它使用协程(参见this问题;你可以yield
)。如果没有错误,如何重复步骤 2 和 3 多次,例如最后一次迭代完成后 5 秒,不会使用某种类型的
sleep
阻塞 GUI(尤其是地图查看器),并且最好不使用任何额外的库?
我的代码:
#imports here
class ArrowDrawerClass:
layer = None
dataprovider = None
feature = None
repeat = True
url = "someURL"
repeatCounter = 0
myscheduler = sched.scheduler(time.time,time.sleep)
def __init__(self):
self.createNewLayer()
def createNewLayer(self):
layername = "ArrowLayer"
self.layer = QgsVectorLayer('Point', layername, "memory")
self.dataprovider = self.layer.dataProvider()
self.feature = QgsFeature()
#Set symbol, color,... of layer here
QgsProject.instance().addMapLayers([self.layer])
def doRequest(self):
request = QNetworkRequest(QUrl(self.url))
request.setTransferTimeout(10000) #10s
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.handleResponse)
self.manager.get(request)
def handleResponse(self, reply):
err = reply.error()
if err == QtNetwork.QNetworkReply.NetworkError.NoError:
bytes = reply.readAll()
replytext = str(bytes, 'utf-8').strip()
#extract coordinates here ...
self.drawArrow(x,y)
else:
self.displayError(str(err),reply.errorString())
def drawArrow(self,x,y):
self.layer.dataProvider().truncate() #removes old marker
point1 = QgsPointXY(x,y)
self.feature.setGeometry(QgsGeometry.fromPointXY(point1))
self.dataprovider.addFeatures([self.feature])
self.layer.updateExtents()
self.layer.triggerRepaint()
self.repeatCounter += 1
self.repeatEverything()
def displayError(self,code,msg):
self.repeat = False
#show error dialog here
def start(self):
self.myscheduler.enter(0,0,self.doRequest)
self.myscheduler.run()
def repeatEverything(self):
print("counter:",self.repeatCounter)
if self.repeat and self.repeatCounter<10:
print("repeat")
self.myscheduler.enter(5,0,self.test) #TODO: Call "self.doRequest()" instead
self.myscheduler.run()
else:
print("don't repeat!")
def test(self):
print("test!")
adc = ArrowDrawerClass()
adc.start()
我设法通过“单次射击”(仅触发一次)来完成此任务
QTimer
:
from PyQt5.QtCore import QTimer
#Other imports here
class ArrowDrawerClass:
#Declare variables here
def __init__(self):
self.timer = QTimer()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.doRequest)
self.createNewLayer()
#def createNewLayer(self): #No changes
#def doRequest(self): #No changes
#def handleResponse(self, reply): #No changes
def drawArrow():
#draw arrow here
self.repeatCounter += 1
self.repeatEverything()
def displayError(self,code,msg):
self.stopTimer()
self.repeat = False
#show error dialog here
def repeatEverything(self):
print("counter:",self.repeatCounter)
#print("Main Thread:",(isinstance(threading.current_thread(), threading._MainThread)))
if self.repeat and self.repeatCounter<10:
self.startTimer()
else:
self.stopTimer()
def startTimer(self):
if not self.timer.isActive():
self.timer.start(5000) #5s
def stopTimer(self):
if self.timer.isActive():
self.timer.stop()
adc = ArrowDrawerClass()
adc.doRequest() #Call the function directly, so there's no 5s delay at the beginning
这不会阻塞 UI 或冻结 QGIS(除了由
truncate()
引起的小冻结,但这是一个不同的问题)。
根据docs,
QTimer
使用事件循环,并且repeatEverything
中的第二次打印在我的测试中始终输出True
,因此无需担心更新UI。