Parando o script PyQGIS que possui loop infinito, usando o teclado?

12

Então, escrevi um script que faz o que eu quero repetidamente, usando um "while True:" em um determinado intervalo de tempo (a cada 5 segundos usando time.sleep (5)). Até aí tudo bem, mas quando eu quero parar, eu simplesmente não posso.

Eu tentei Control + C, Control + Break, Escape e praticamente ignora meu teclado. A única maneira de impedir isso é fechar o QGIS. Alguma ideia? Além disso, quando o script atinge time.sleep (5), o tipo de QGIS fica lento e congela por 5 segundos e não posso, por exemplo, movimentar a camada, mas presumo que isso seja normal.

Aqui está o meu script:

from PyQt4.QtGui import *
from PyQt4.QtCore import *
from qgis.core import *
from qgis.utils import iface
import time


while True: 

    def change_color():
        active_layer = iface.activeLayer()
        pipeline=[]
        txt=open('C:/users/stelios/desktop/project/Sensor.txt','r')
        for line in txt.readlines():
            pipeline.append(line.split())
        print pipeline 
        pipeline2=[]
        for label,color in pipeline:
            if "0" in color:
                pipeline2.append([label,"green"])
            else:
                pipeline2.append([label,"red"])

        print pipeline2
        elatomatikoi=""
        categories=[]

        for label,color in pipeline2:
            if 'red' in color:
                elatomatikoi=elatomatikoi + label+","
            symbol = QgsSymbolV2.defaultSymbol(active_layer.geometryType())
            symbol.setColor(QColor(color))
            category = QgsRendererCategoryV2(int(label), symbol, label)
            categories.append(category)

        expression = 'id' 
        renderer = QgsCategorizedSymbolRendererV2(expression, categories)
        active_layer.setRendererV2(renderer)
        active_layer.setCacheImage(None)
        iface.mapCanvas().refresh()
        iface.legendInterface().refreshLayerSymbology(active_layer)
        elatomatikoi= elatomatikoi[:-1]

        for label,color in pipeline2:
            if 'red' in color:
                QMessageBox.critical(None,"Warning",("Leakage at pipe(s):%s\nCheck Pipeline status " %elatomatikoi))
                break
        txt.close()

    change_color()
    time.sleep(5)
Stelios M
fonte
Quais são as condições que devem desencadear uma 'saída'?
nickves 4/03/15
1
existem muitas maneiras de implementar um processo sem bloqueio no qgis. Você está obtendo o controle sem deixá-lo no loop de eventos Qt. Sugiro explorar: 1) criar uma arquitetura orientada a eventos ou 2) gerenciar seu processo em um subprocesso python ou da maneira simples) criar um script Processing Toolbox e, se necessário, integrar com a opção 2
Luigi Pirelli
3
Gente, talvez eu tenha dito errado. Deixe-me reformular a pergunta com um novo cenário: Você abre o Python Console no QGIS e digita: while 1: imprime "a" e pressiona enter. Em seguida, imprime 'a' para todo o sempre. Como você o interrompe sem sair do QGIS? Essa é a pergunta e a verdadeira questão
Stelios M
Essa pode ser uma pergunta mais geral sobre Python, portanto, você terá mais sorte em obter uma resposta no StackOverflow.
Martin
@ Martin fará. Mas é uma pergunta bastante direta e me surpreende que os desenvolvedores principais do QGIS não tenham pensado no cenário de loop infinito em seu console python. Se você executar enquanto 1: imprime 'a' em sua máquina, consegue parar com o teclado ou a culpa é do meu sistema?
Stelios H

Respostas:

2

O QGIS oferece todo o poder do python para você. Isso abre possibilidades surpreendentes, mas também vem com possíveis armadilhas. O que pode deixar o QGIS sem resposta, congelar ou até travá-lo. Use-o com sabedoria!

No seu caso, em vez de enviar o encadeamento principal para suspensão por 5 segundos, é melhor deixar o QGIS fazer outra coisa (como ouvir as teclas pressionadas ou pressionar os botões) e postar um evento de timer no loop do evento principal que retornará o controle para seu script 5 segundos depois.

Você pode usar o exemplo desta resposta como um bom ponto de partida. Para pará-lo, basta conectar algum evento ao stop()slot do timer.

def change_color():
    print('I am now red')

timer = QTimer()
timer.timeout.connect(change_color)
timer.start(5000)

someButton.clicked.connect(timer.stop)

Ou simplesmente chame-o manualmente no console quando achar que é hora de pará-lo

timer.stop()

Você também pode instalar um eventFilter () na janela principal para interceptar pressionamentos de tecla, se necessário.

Matthias Kuhn
fonte
0

Como alternativa, você pode usar um widget QT com um botão Cancelar.

É um pouco difícil, mas aqui está o script de widget que eu usei:

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_Form(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.setupUi(self)
        self.running = True
    def setupUi(self, Form):
        Form.setObjectName(_fromUtf8("Form"))
        Form.resize(100, 100)
        self.horizontalLayout_3 = QtGui.QHBoxLayout(Form)
        self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3"))
        self.horizontalLayout = QtGui.QHBoxLayout()
        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
        self.Cancel_btn = QtGui.QPushButton(Form)
        self.Cancel_btn.setMinimumSize(QtCore.QSize(0, 0))
        self.Cancel_btn.setMaximumSize(QtCore.QSize(425, 27))
        self.Cancel_btn.setObjectName(_fromUtf8("Cancel_btn"))
        self.horizontalLayout.addWidget(self.Cancel_btn)
        self.horizontalLayout_3.addLayout(self.horizontalLayout)
        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        Form.setWindowTitle(_translate("Form", "Cancel", None))
        self.Cancel_btn.setText(_translate("Form", "Cancel", None))
        self.Cancel_btn.clicked.connect(self.Cancel)


    def Cancel(self):
        self.running = False

Isso pode ser importado para o script pyQgis (você precisará anexar o diretório ao sys.path) e, em seguida, você pode usar a variável running para interromper o loop while:

import sys
sys.path.append("path/to/cancel_widget")

import cancel_widget

btn = cancel_widget.Ui_Form()
btn.show()

while btn.running:
    ...
user6072577
fonte