Zatrzymywanie skryptu PyQGIS z nieskończoną pętlą za pomocą klawiatury?

12

Napisałem więc skrypt, który robi to, co chcę w kółko, używając „while True:” w określonym przedziale czasu (co 5 sekund przy użyciu time.sleep (5)). Jak dotąd tak dobrze, ALE kiedy chcę to zatrzymać, po prostu nie mogę.

Próbowałem Control + C, Control + Break, Escape i praktycznie ignoruje moją klawiaturę. Jedynym sposobem, aby to zatrzymać, jest zamknięcie QGIS. Jakieś pomysły? Ponadto, gdy skrypt uderza w time.sleep (5), QGIS rodzi opóźnienia i zawiesza się na 5 sekund i nie mogę na przykład przesuwać warstwy, ale zakładam, że jest to normalne.

Oto mój skrypt:

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.
źródło
Jakie warunki powinny wywołać „wyjście”?
nickves
1
istnieje wiele sposobów na wdrożenie procesu bez blokowania w qgis. Otrzymujesz kontrolę bez pozostawiania jej w pętli zdarzeń Qt. Proponuję zbadać: 1) stworzyć architekturę sterowaną zdarzeniami lub 2) zarządzać procesem w podprocesie python lub w podobny sposób) stworzyć skrypt Processing Toolbox i w razie potrzeby zintegrować z wyborem 2
Luigi Pirelli
3
Chłopaki, może powiedziałem to w niewłaściwy sposób. Pozwól, że sformułuję to pytanie w nowym scenariuszu: otwierasz konsolę Python w QGIS, piszesz: podczas gdy 1: drukuj „a” i naciśnij Enter. Następnie drukuje „a” na wieki wieków. JAK ZATRZYMAĆ SIĘ BEZ WYJŚCIA Z QGIS? To jest pytanie i prawdziwy problem
Stelios M
To może być bardziej ogólne pytanie w języku Python, więc możesz mieć więcej szczęścia w uzyskaniu odpowiedzi na StackOverflow.
Martin
@Martin zrobi. Ale to dość proste pytanie i dziwi mnie, że główni programiści QGIS nie pomyśleli o scenariuszu nieskończonej pętli w swojej konsoli Pythona. Jeśli wykonasz polecenie 1: wydrukuj „a” na swoim komputerze, czy możesz go zatrzymać za pomocą klawiatury, czy to wina mojego systemu?
Stelios M

Odpowiedzi:

2

QGIS oferuje pełną moc Pythona. Otwiera to niesamowite możliwości, ale wiąże się także z potencjalnymi pułapkami. Co może spowodować, że QGIS przestanie odpowiadać, zawiesi się, a nawet zawiesi. Używać go mądrze!

W twoim przypadku zamiast wysyłać główny wątek do uśpienia na 5 sekund, lepiej pozwól QGIS zrobić coś innego (np. Słuchanie naciśnięć klawiszy lub naciśnięć przycisków) i opublikować zdarzenie timera w głównej pętli zdarzeń, które przekaże sterowanie z powrotem do twój skrypt 5 sekund później.

Możesz użyć przykładu z tej odpowiedzi jako dobrego punktu wyjścia. Aby go zatrzymać, po prostu podłącz jakieś zdarzenie do stop()gniazda timera.

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

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

someButton.clicked.connect(timer.stop)

Lub po prostu wywołaj go ręcznie z konsoli, jeśli uważasz, że nadszedł czas, aby go zatrzymać

timer.stop()

Możesz także zainstalować eventFilter () w głównym oknie, aby przechwytywać naciśnięcia klawiszy, jeśli potrzebujesz.

Matthias Kuhn
źródło
0

Aby obejść ten problem, możesz użyć widgetu QT z przyciskiem anulowania.

To trochę trudne, ale oto skrypt widgetu, którego użyłem:

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

Można to zaimportować do skryptu pyQgis (musisz dołączyć katalog do sys.path), a następnie możesz użyć działającej zmiennej, aby zatrzymać pętlę while:

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

import cancel_widget

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

while btn.running:
    ...
użytkownik6072577
źródło