আমি কীভাবে পাইকিজিআইএস-এর সাথে কিউথ্রেড ব্যবহার করে একটি রিসোসিভ জিইআইআই বজায় রাখি


11

আমি কিউজিআইএস 1.8 এর পাইথন প্লাগইন হিসাবে কিছু ব্যাচ প্রসেসিং সরঞ্জাম বিকাশ করছি।

আমি দেখতে পেয়েছি যে আমার সরঞ্জামগুলি চলাকালীন জিইউআই অ-প্রতিক্রিয়াশীল হয়ে ওঠে।

সাধারণ জ্ঞান হ'ল কর্মটি থ্রেডে কাজটি করা উচিত, স্থিতি / সমাপ্তির তথ্য জিইউআইতে সিগন্যাল হিসাবে ফিরিয়ে দেওয়া উচিত।

আমি নদীর তীরের ডক্সগুলি পড়েছি এবং doGeometry.py ( ftools থেকে কার্যকর বাস্তবায়ন ) এর উত্স অধ্যয়ন করেছি ।

এই উত্সগুলি ব্যবহার করে আমি একটি প্রতিষ্ঠিত কোড বেসে পরিবর্তন করার আগে এই কার্যকারিতাটি অন্বেষণ করতে একটি সাধারণ বাস্তবায়ন গড়ে তোলার চেষ্টা করেছি।

সামগ্রিক কাঠামোটি প্লাগইন মেনুতে একটি এন্ট্রি that বোতামগুলি এমন একটি থ্রেড নিয়ন্ত্রণ করে যা 100 হিসাবে গণ্য হয়, প্রতিটি সংখ্যার জন্য জিইউআইতে ফিরে একটি সংকেত প্রেরণ করে। জিইউআই প্রতিটি সিগন্যাল গ্রহণ করে এবং বার্তা লগ এবং উইন্ডো শিরোনাম উভয় সংখ্যক স্ট্রিং প্রেরণ করে।

এই প্রয়োগের কোডটি এখানে:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *

class ThreadTest:

    def __init__(self, iface):
        self.iface = iface

    def initGui(self):
        self.action = QAction( u"ThreadTest", self.iface.mainWindow())
        self.action.triggered.connect(self.run)
        self.iface.addPluginToMenu(u"&ThreadTest", self.action)

    def unload(self):
        self.iface.removePluginMenu(u"&ThreadTest",self.action)

    def run(self):
        BusyDialog(self.iface.mainWindow())

class BusyDialog(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setLayout(QVBoxLayout())
        self.startButton = QPushButton("Start", self)
        self.startButton.clicked.connect(self.startButtonHandler)
        self.layout().addWidget(self.startButton)
        self.stopButton=QPushButton("Stop", self)
        self.stopButton.clicked.connect(self.stopButtonHandler)
        self.layout().addWidget(self.stopButton)
        self.show()

    def startButtonHandler(self, toggle):
        self.workerThread = WorkerThread(self.parent)
        QObject.connect( self.workerThread, SIGNAL( "killThread(PyQt_PyObject)" ), \
                                                self.killThread )
        QObject.connect( self.workerThread, SIGNAL( "echoText(PyQt_PyObject)" ), \
                                                self.setText)
        self.workerThread.start(QThread.LowestPriority)
        QgsMessageLog.logMessage("end: startButtonHandler")

    def stopButtonHandler(self, toggle):
        self.killThread()

    def setText(self, text):
        QgsMessageLog.logMessage(str(text))
        self.setWindowTitle(text)

    def killThread(self):
        if self.workerThread.isRunning():
            self.workerThread.exit(0)


class WorkerThread(QThread):
    def __init__(self, parent):
        QThread.__init__(self,parent)

    def run(self):
        self.emit( SIGNAL( "echoText(PyQt_PyObject)" ), "Emit: starting work" )
        self.doLotsOfWork()
        self.emit( SIGNAL( "echoText(PyQt_PyObject)" ), "Emit: finshed work" )
        self.emit( SIGNAL( "killThread(PyQt_PyObject)"), "OK")

    def doLotsOfWork(self):
        count=0
        while count < 100:
            self.emit( SIGNAL( "echoText(PyQt_PyObject)" ), "Emit: " + str(count) )
            count += 1
#           if self.msleep(10):
#               return
#          QThread.yieldCurrentThread()

দুর্ভাগ্যক্রমে এটি আশানুরূপে কাজ করে নি:

  • উইন্ডো শিরোনামটি কাউন্টারটির সাথে "লাইভ" আপডেট করছে তবে আমি যদি ডায়লগটিতে ক্লিক করি তবে এটি প্রতিক্রিয়াশীল নয়।
  • কাউন্টারটি শেষ না হওয়া পর্যন্ত বার্তা লগ নিষ্ক্রিয় থাকে, তারপরে সমস্ত বার্তাগুলি একবারে উপস্থাপন করে। এই বার্তাগুলি কিউএমসেসেজলগ দ্বারা টাইম স্ট্যাম্পের সাথে ট্যাগ করা হয়েছে এবং এই সময়ের স্ট্যাম্পগুলি নির্দেশ করে যে তারা কাউন্টারটির সাথে "লাইভ" পেয়েছিল অর্থাৎ তারা কর্মী থ্রেড, বা কথোপকথনের দ্বারা সারিযুক্ত নয়।
  • লগের বার্তাগুলির ক্রমানুসারে (এক্সারপ্ট অনুসারে) ইঙ্গিত দেয় যে স্টার্টবটনহ্যান্ডলার কর্মী থ্রেড আসার আগেই কার্য সম্পাদন সম্পন্ন করে অর্থাৎ থ্রেডটি থ্রেড হিসাবে আচরণ করে।

    end: startButtonHandler
    Emit: starting work
    Emit: 0
    ...
    Emit: 99
    Emit: finshed work
  • মনে হচ্ছে কর্মী থ্রেড কেবল জিইউআই থ্রেডের সাথে কোনও সংস্থান ভাগ করছে না। উপরের উত্সের শেষে বেশ কয়েকটি মন্তব্য রেখা রয়েছে যেখানে আমি এমএসপ্লিপ () এবং উত্পাদনকারেন্টথ্রেড () কল করার চেষ্টা করেছি, তবে উভয়ই সহায়তা করেছে বলে মনে হয় নি।

এই অভিজ্ঞতা আছে এমন কেউ কি আমার ত্রুটি চিহ্নিত করতে সক্ষম? আমি আশা করছি এটি একটি সাধারণ তবে মৌলিক ভুল যা এটি চিহ্নিত হয়ে গেলে এটি সংশোধন করা সহজ।


স্টপ বাটনটি ক্লিক করা যায় না এটাই কি স্বাভাবিক? প্রতিক্রিয়াশীল জিইউআইয়ের মূল লক্ষ্যটি যদি খুব দীর্ঘ হয় তবে প্রক্রিয়াটি বাতিল করা। আমি আপনার স্ক্রিপ্টটি পরিবর্তন করার চেষ্টা করি তবে আমি বোতামটি সঠিকভাবে কাজ করতে পারি না। কিভাবে আপনি আপনার থ্রেড বাতিল?
এট্রিমেইল

উত্তর:


6

সুতরাং আমি এই সমস্যাটি অন্য চেহারা ছিল। আমি স্ক্র্যাচ থেকে শুরু করেছি এবং সাফল্য পেয়েছি, তারপরে উপরের কোডটি দেখতে ফিরে গিয়েছি এবং এখনও এটি ঠিক করতে পারি না।

এই বিষয়ে যে কেউ গবেষণা করছে তার জন্য একটি কাজের উদাহরণ দেওয়ার স্বার্থে আমি এখানে কার্যকরী কোড সরবরাহ করব:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class ThreadManagerDialog(QDialog):
    def __init__( self, iface, title="Worker Thread"):
        QDialog.__init__( self, iface.mainWindow() )
        self.iface = iface
        self.setWindowTitle(title)
        self.setLayout(QVBoxLayout())
        self.primaryLabel = QLabel(self)
        self.layout().addWidget(self.primaryLabel)
        self.primaryBar = QProgressBar(self)
        self.layout().addWidget(self.primaryBar)
        self.secondaryLabel = QLabel(self)
        self.layout().addWidget(self.secondaryLabel)
        self.secondaryBar = QProgressBar(self)
        self.layout().addWidget(self.secondaryBar)
        self.closeButton = QPushButton("Close")
        self.closeButton.setEnabled(False)
        self.layout().addWidget(self.closeButton)
        self.closeButton.clicked.connect(self.reject)
    def run(self):
        self.runThread()
        self.exec_()
    def runThread( self):
        QObject.connect( self.workerThread, SIGNAL( "jobFinished( PyQt_PyObject )" ), self.jobFinishedFromThread )
        QObject.connect( self.workerThread, SIGNAL( "primaryValue( PyQt_PyObject )" ), self.primaryValueFromThread )
        QObject.connect( self.workerThread, SIGNAL( "primaryRange( PyQt_PyObject )" ), self.primaryRangeFromThread )
        QObject.connect( self.workerThread, SIGNAL( "primaryText( PyQt_PyObject )" ), self.primaryTextFromThread )
        QObject.connect( self.workerThread, SIGNAL( "secondaryValue( PyQt_PyObject )" ), self.secondaryValueFromThread )
        QObject.connect( self.workerThread, SIGNAL( "secondaryRange( PyQt_PyObject )" ), self.secondaryRangeFromThread )
        QObject.connect( self.workerThread, SIGNAL( "secondaryText( PyQt_PyObject )" ), self.secondaryTextFromThread )
        self.workerThread.start()
    def cancelThread( self ):
        self.workerThread.stop()
    def jobFinishedFromThread( self, success ):
        self.workerThread.stop()
        self.primaryBar.setValue(self.primaryBar.maximum())
        self.secondaryBar.setValue(self.secondaryBar.maximum())
        self.emit( SIGNAL( "jobFinished( PyQt_PyObject )" ), success )
        self.closeButton.setEnabled( True )
    def primaryValueFromThread( self, value ):
        self.primaryBar.setValue(value)
    def primaryRangeFromThread( self, range_vals ):
        self.primaryBar.setRange( range_vals[ 0 ], range_vals[ 1 ] )
    def primaryTextFromThread( self, value ):
        self.primaryLabel.setText(value)
    def secondaryValueFromThread( self, value ):
        self.secondaryBar.setValue(value)
    def secondaryRangeFromThread( self, range_vals ):
        self.secondaryBar.setRange( range_vals[ 0 ], range_vals[ 1 ] )
    def secondaryTextFromThread( self, value ):
        self.secondaryLabel.setText(value)

class WorkerThread( QThread ):
    def __init__( self, parentThread):
        QThread.__init__( self, parentThread )
    def run( self ):
        self.running = True
        success = self.doWork()
        self.emit( SIGNAL( "jobFinished( PyQt_PyObject )" ), success )
    def stop( self ):
        self.running = False
        pass
    def doWork( self ):
        return True
    def cleanUp( self):
        pass

class CounterThread(WorkerThread):
    def __init__(self, parentThread):
        WorkerThread.__init__(self, parentThread)
    def doWork(self):
        target = 100000000
        stepP= target/100
        stepS=target/10000
        self.emit( SIGNAL( "primaryText( PyQt_PyObject )" ), "Primary" )
        self.emit( SIGNAL( "secondaryText( PyQt_PyObject )" ), "Secondary" )
        self.emit( SIGNAL( "primaryRange( PyQt_PyObject )" ), ( 0, 100 ) )
        self.emit( SIGNAL( "secondaryRange( PyQt_PyObject )" ), ( 0, 100 ) )
        count = 0
        while count < target:
            if count % stepP == 0:
                self.emit( SIGNAL( "primaryValue( PyQt_PyObject )" ), int(count / stepP) )
            if count % stepS == 0:  
                self.emit( SIGNAL( "secondaryValue( PyQt_PyObject )" ), count % stepP / stepS )
            if not self.running:
                return False
            count += 1
        return True

d = ThreadManagerDialog(qgis.utils.iface, "CounterThread Demo")
d.workerThread = CounterThread(qgis.utils.iface.mainWindow())
d.run()

এই নমুনার কাঠামো একটি থ্রেডম্যানেজারডায়ালগ ক্লাস যা এর চেয়ে একটি ওয়ার্কারথ্রেড (বা সাবক্লাস) নির্ধারিত হতে পারে। যখন ডায়ালগটির রান পদ্ধতিটি বলা হয় তখন এটি কর্মীর উপর ডওর্ক পদ্ধতিতে কল করে। ফলাফলটি হ'ল ডুওয়ার্কের যে কোনও কোড পৃথক থ্রেডে চলবে, জিইউআই ব্যবহারকারীর ইনপুটটির প্রতিক্রিয়া মুক্ত রাখবে।

এই নমুনায় কাউন্টারথ্রেডের একটি উদাহরণ কর্মী হিসাবে অর্পণ করা হয়েছে এবং বেশ কয়েকটি অগ্রগতি বার এক মিনিট বা তার জন্য ব্যস্ত রাখা হবে।

দ্রষ্টব্য: এটি ফর্ম্যাট করা হয়েছে যাতে এটি পাইথন কনসোলে পেস্ট করতে প্রস্তুত। একটি .py ফাইলে সংরক্ষণ করার আগে শেষ তিনটি লাইন সরানো দরকার।


এটি একটি দুর্দান্ত প্লাগ এবং খেলার উদাহরণ! আমাদের নিজস্ব কার্যকরী অ্যালগরিদম্মান বাস্তবায়নের জন্য আমি এই কোডটির সেরা অবস্থানটি সম্পর্কে আগ্রহী। ওয়ার্কারথ্রেড ক্লাসে বা বরং কাউন্টারথ্রেড, ডিফ ডু ওয়ার্কে ক্লাসে এগুলি স্থাপন করা দরকার? [এই অগ্রগতি বারগুলি worker
োকানো

হ্যাঁ, CounterThreadএকটি খালি হাড়ের উদাহরণ শিশু শ্রেণির WorkerThread। যদি আপনি আরও একটি অর্থবহ বাস্তবায়ন দিয়ে আপনার নিজস্ব শিশু শ্রেণি তৈরি করেন doWorkতবে আপনার ভাল হওয়া উচিত।
কেলি টমাস

কাউন্টারথ্রেডের বৈশিষ্ট্যগুলি আমার লক্ষ্যটির জন্য প্রযোজ্য (অগ্রগতির ব্যবহারকারীদের জন্য বিশদ বিজ্ঞপ্তি) - তবে কীভাবে এগুলি একটি নতুন সি.ক্লাস 'ডো ওয়ার্ক' রুটিনের সাথে সংহত করা হবে? (এছাড়াও - কাউন্টারথ্রেডের ডান
ওয়ার্কে

উপরের কাউন্টারথ্রেড বাস্তবায়ন ক) কাজটি সূচনা করে, খ) ডায়লগটি আরম্ভ করে, গ) একটি মূল লুপ সম্পাদন করে, ঘ) সফল সমাপ্তির পরে সত্য উপস্থাপন করে। লুপ দিয়ে প্রয়োগ করা যেতে পারে এমন কোনও কাজ ঠিক জায়গায় ফেলে দেওয়া উচিত। একটি সতর্কতা আমি দিচ্ছি হ'ল ম্যানেজারের সাথে যোগাযোগের জন্য সিগন্যালগুলি নির্গত করা কিছু ওভারহেডের সাথে আসে - যদি দ্রুত লুপের প্রতিটি পুনরাবৃত্তির সাথে ডাকা হয় এটি প্রকৃত কাজের চেয়ে আরও বেশি বিলম্বিত হতে পারে।
কেলি টমাস

সব পরামর্শের জন্য ধন্যবাদ। আমার পরিস্থিতিতে এটি কাজ করতে সমস্যা হতে পারে। বর্তমানে, ডো ওয়ার্ক কিউজিসে মিনিডাম্প ক্র্যাশ ঘটায়। খুব ভারী বোঝা, বা আমার (নবজাতক) প্রোগ্রামিং দক্ষতার ফল?
কাতালপা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.