भारी प्लगइन चलाते समय मैं Qgis को "प्रतिसाद नहीं दे रहा" के रूप में कैसे पहचाना जा सकता है?


10

मैं स्थिति के बारे में उपयोगकर्ता को सूचित करने के लिए फ़ोलोइंग लाइन का उपयोग करता हूं:

iface.mainWindow().statusBar().showMessage("Status:" + str(i))

प्लगइन को मेरे डेटा सेट पर चलने में लगभग 2 मिनट लगते हैं, लेकिन विंडोज़ इसे "जवाब नहीं" के रूप में पहचानती है और स्टेटस अपडेट दिखाना बंद कर देती है। एक नए उपयोगकर्ता के लिए यह इतना अच्छा नहीं है क्योंकि यह दिखता है कि कार्यक्रम दुर्घटनाग्रस्त हो गया है।

क्या आसपास कोई काम है जिससे उपयोगकर्ता प्लगइन की स्थिति के बारे में अंधेरे में नहीं बचा है?

जवाबों:


13

जैसा कि नाथन डब्ल्यू बताते हैं, ऐसा करने का तरीका मल्टीथ्रेडिंग के साथ है, लेकिन क्यूथ्रेड को उप-वर्ग करना सबसे अच्छा अभ्यास नहीं है। यहाँ देखें: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

कैसे बनाएं इसका एक उदाहरण नीचे देखें QObject, फिर इसे QThread(यानी "सही" करने का तरीका) पर ले जाएँ। यह उदाहरण वेक्टर परत में सभी विशेषताओं के कुल क्षेत्र (नए QGIS 2.0 API का उपयोग करके!) की गणना करता है।

सबसे पहले, हम "कार्यकर्ता" ऑब्जेक्ट बनाते हैं जो हमारे लिए भारी उठाने का काम करेगा:

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)

कार्यकर्ता का उपयोग करने के लिए हमें इसे एक वेक्टर परत के साथ इनलेट करने की आवश्यकता है, इसे थ्रेड पर ले जाएं, कुछ सिग्नल कनेक्ट करें, फिर इसे शुरू करें। यहाँ क्या हो रहा है यह समझने के लिए ऊपर दिए गए ब्लॉग को देखना शायद सबसे अच्छा है।

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()कार्यकर्ता की पद्धति के अंदर सब कुछ एक कोशिश को छोड़कर बयान के अंदर है। जब आपका कोड किसी थ्रेड के अंदर क्रैश होता है, तो इसे पुनर्प्राप्त करना मुश्किल है। यह त्रुटि सिग्नल के माध्यम से ट्रेसबैक का उत्सर्जन करता है, जिसे मैं आमतौर पर कनेक्ट करता हूं QgsMessageLog
  • समाप्त संकेत जुड़ा हुआ विधि बताता है यदि प्रक्रिया सफलतापूर्वक पूरी हुई, साथ ही परिणाम भी।
  • प्रगति संकेत केवल तभी कहा जाता है जब प्रतिशत पूर्ण रूप से बदल जाता है, बजाय हर सुविधा के लिए एक बार। यह कार्यकर्ता प्रक्रिया को धीमा करने वाली प्रगति पट्टी को अद्यतन करने के लिए बहुत अधिक कॉलों को रोकता है, जो कि कार्यकर्ता को दूसरे धागे में चलाने के पूरे बिंदु को पराजित करेगा: उपयोगकर्ता इंटरफ़ेस से गणना को अलग करने के लिए।
  • कार्यकर्ता एक kill()विधि को लागू करता है, जो फ़ंक्शन को इनायत से समाप्त करने की अनुमति देता है। कोशिश न करें और terminate()विधि का उपयोग करें QThread- बुरी चीजें हो सकती हैं!

अपने प्लगइन संरचना में कहीं न कहीं अपने threadऔर workerवस्तुओं का ट्रैक रखना सुनिश्चित करें । यदि आप नहीं करते हैं तो क्यूटी को गुस्सा आता है। ऐसा करने का सबसे आसान तरीका है कि आप उन्हें बनाते समय अपने संवाद में संग्रहीत करें, जैसे:

thread = self.thread = QtCore.QThread()
worker = self.worker = Worker(layer)

या आप Qt को क्यूट्र्रेड के स्वामित्व को लेने दे सकते हैं:

thread = QtCore.QThread(self)

इस टेम्प्लेट को एक साथ रखने के लिए सभी ट्यूटोरियल्स को खोदने में मुझे एक लंबा समय लगा, लेकिन तब से मैं इसे सभी जगह पुनः उपयोग कर रहा हूं।


धन्यवाद, यह वही था जिसकी मुझे तलाश थी और यह बहुत मददगार था! मैं थ्रेड्स I C # का उपयोग कर रहा हूं, लेकिन अजगर में इसके बारे में नहीं सोचा।
जोहान होल्टबी

हाँ यह सही तरीका है।
नाथन डब्ल्यू

1
क्या "स्व" होना चाहिए। "फीचर्स = layer.getFeatures ()" में लेयर के सामने? -> "विशेषताएँ = self.layer.getFeatures ()"
Håvard Tveite

@ HåvardTveite आप सही हैं। मैंने उत्तर में कोड तय किया है।
स्नोरफालोर्पगस

मैं एक प्रसंस्करण स्क्रिप्ट के लिए इस पैटर्न का पालन करने की कोशिश कर रहा हूं जो मैं लिख रहा हूं, और मुझे इसे काम करने में परेशानी हो रही है। मैंने इस उदाहरण को एक स्क्रिप्ट फ़ाइल में कॉपी करने की कोशिश की, आवश्यक आयात विवरण जोड़े, और worker.progress.connect(self.ui.progressBar)कुछ और बदल दिया , लेकिन हर बार जब मैं इसे चलाता हूं तो क्यूगी-बिन दुर्घटनाग्रस्त हो जाता है। मुझे अजगर कोड या क्यूजीस को डिबग करने का कोई अनुभव नहीं है। सब मुझे मिल रहा है Access violation reading location 0x0000000000000008तो ऐसा लगता है जैसे कुछ अशक्त है। क्या कोई सेट अप कोड है जो प्रसंस्करण स्क्रिप्ट में इसका उपयोग करने में सक्षम होने के लिए गायब है?
टीजे रॉकफेलर

4

ऐसा करने का आपका एकमात्र सही तरीका मल्टीथ्रेडिंग है।

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/

नोट कुछ लोग क्यूथ्रेड से विरासत में लेना पसंद नहीं करते हैं, और जाहिर है यह ऐसा करने का "सही" तरीका नहीं है, लेकिन यह ऐसा काम करता है ...।


:) यह ऐसा करने के लिए एक अच्छा गंदा तरीका है। कुछ समय शैली आवश्यक नहीं है। इस समय के लिए (pyqt में पहला) मुझे लगता है कि मैं सही तरीके से जाऊंगा क्योंकि मैं C # में उपयोग कर रहा हूं।
जोहान होल्टबी

2
यह एक गंदा तरीका नहीं है, यह करने का पुराना तरीका था।
नाथन डब्ल्यू

2

जैसा कि यह प्रश्न अपेक्षाकृत पुराना है, यह एक अद्यतन का हकदार है। QGIS 3 के साथ QgsTask.fromFunction (), QgsProcessingAlgRunnerTask () और QgsApplication.taskManager ()। AddTask () के साथ दृष्टिकोण है।

उदाहरण के लिए इसके बारे में PyQGIS3 BY थ्रू मार्को बर्नोसची में थ्रेड्स का उपयोग करना

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.