¿Detener el script PyQGIS que tiene bucle infinito, usando el teclado?

12

Entonces escribí un script que hace lo que quiero una y otra vez usando un "while True:" en un cierto intervalo de tiempo (cada 5 segundos usando time.sleep (5)). Hasta ahora todo bien, PERO cuando quiero detenerlo simplemente no puedo.

He intentado Control + C, Control + Break, Escape y prácticamente ignora mi teclado. La única forma de detenerlo es cerrar QGIS. ¿Algunas ideas? Además, cuando el script llega a time.sleep (5), QGIS se retrasa y se congela durante 5 segundos y no puedo, por ejemplo, desplazar la capa, pero supongo que esto es normal.

Aquí está mi guión:

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
fuente
¿Cuáles son las condiciones que deberían desencadenar una 'salida'?
nickves
1
Hay muchas formas de implementar un proceso sin bloqueo en qgis. Obtiene el control sin dejarlo en el bucle de eventos Qt. Sugiero explorar: 1) crear una arquitectura basada en eventos o 2) administrar su proceso en un subproceso de Python o de la manera más simple) crear un script de Processing Toolbox y, si es necesario, integrarlo con la opción 2
Luigi Pirelli
3
Chicos, tal vez lo dije de manera incorrecta. Permítanme reformular la pregunta con un nuevo escenario: abre Python Console en QGIS, escribe: mientras que 1: imprime "a" y presiona enter. Luego imprime 'a' por siempre y para siempre. ¿CÓMO LO DETENGAS SIN SALIR DE QGIS? Esa es la pregunta y el verdadero problema
Stelios M
Esta podría ser más una pregunta general de Python, por lo que podría tener mejor suerte al obtener una respuesta en StackOverflow.
Martin
@Martin lo hará. Pero es una pregunta bastante directa y me sorprende que los desarrolladores principales de QGIS no hayan pensado en el escenario de bucle infinito en su consola Python. Si ejecuta mientras 1: imprime 'a' en su máquina, ¿puede detenerlo con el teclado o es culpa de mi sistema?
Stelios M

Respuestas:

2

QGIS le ofrece todo el poder de Python. Esto abre posibilidades increíbles, pero también conlleva posibles dificultades. Lo que puede hacer que QGIS no responda, se congele o incluso se bloquee. ¡Úsalo con sabiduría!

En su caso, en lugar de enviar el hilo principal a dormir durante 5 segundos, es mejor dejar que QGIS haga algo más (como escuchar las pulsaciones de teclas o presionar botones) y publicar un evento de temporizador en el bucle de eventos principal que devolverá el control a su guión 5 segundos después.

Puede usar el ejemplo de esta respuesta como un buen punto de partida. Para detenerlo, simplemente conecte algún evento a la stop()ranura del temporizador.

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

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

someButton.clicked.connect(timer.stop)

O simplemente llámalo manualmente desde la consola cuando creas que es hora de detenerlo

timer.stop()

También puede instalar un eventFilter () en la ventana principal para interceptar las pulsaciones de teclas si lo necesita.

Matthias Kuhn
fuente
0

Como solución alternativa, puede usar un widget QT con un botón cancelar.

Es un poco difícil, pero aquí está el script de widget que he usado:

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

Esto puede importarse a su script pyQgis (deberá agregar el directorio a sys.path) y luego puede usar la variable en ejecución para detener su ciclo while:

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

import cancel_widget

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

while btn.running:
    ...
usuario6072577
fuente