2

I recently wrote a python script that works with raster data and makes some time-consuming operations. My script uses QThread to update a QList and a QProgressBar and everithing works fine if I run the script from PyCharm, the progressbar and the list are updated. Moving the code in a Qgis plugin the gui appears correctly and everything seems to work fine, but if I close and open the plugin again, when I click apply Qgis crashes... To better understand I wrote a code that reproduce the problem. The gui is a QDialog with a QListWidget, a QLineEdit and a QProgressBar. After writing a word in the QLineEdit and clicking on the apply buttom the result should be that the new thread makes a loop using the string and adds every single letter as item in the QListWidget. In the meantime the progressbar increases and I put a sleep time of 1 second to make it visible. I made use also of a QgsMessageBar to alert the user if no word are edited in the QLineEdit. I hope it is enough clear... Here the gui code:

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

from qgis.gui import QgsMessageBar

import sys, time

class my_worker(QObject):
    def __init__(self, word):
        QObject.__init__(self)
        self.word = word

    def run(self):
        bar_step = 100/len(self.word)
        for i in self.word:
            self.emit(SIGNAL("add_item(QString)"), i)
            self.emit(SIGNAL("increase_bar(int)"), bar_step)
            time.sleep(1)
        self.emit(SIGNAL("finished()"))

class my_ui(QDialog):
    def __init__(self, parent=None):
        """Constructor."""
        super(my_ui, self).__init__(parent)

        self.list = QListWidget()
        self.messagebar = QgsMessageBar()
        input_label = QLabel("Input a word")
        self.inputedit = QLineEdit()
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        self.button_box = QDialogButtonBox(QDialogButtonBox.Apply | QDialogButtonBox.Close)

        layout = QGridLayout()
        layout.addWidget(self.messagebar, 0, 0, 1, 4)
        layout.addWidget(self.list, 1, 0, 1, 4)
        layout.addWidget(input_label, 2, 0)
        layout.addWidget(self.inputedit, 2, 1, 1, 3)
        layout.addWidget(self.progress_bar, 3, 0, 1, 4)
        layout.addWidget(self.button_box, 4, 2, 1, 2)
        self.setLayout(layout)

        self.connect(self.button_box, SIGNAL("rejected()"), self, SLOT("reject()"))

        self.setWindowTitle("Example thread")


    def start_worker(self, word):
        self.worker = my_worker(word)
        self.thread = QThread()
        self.worker.moveToThread(self.thread)
        self.connect(self.worker, SIGNAL("finished()"), self.workerFinished)
        self.connect(self.worker, SIGNAL("add_item(QString)"), self.add_item)
        self.connect(self.worker, SIGNAL("increase_bar(int)"), self.increase_bar)
        self.thread.started.connect(self.worker.run)
        self.thread.start()

    def workerFinished(self):
        self.progress_bar.setValue(100)
        self.worker.deleteLater()
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()

    def add_item(self, item):
        self.list.addItem(item)
        return

    def increase_bar(self, bar_step):
        self.progress_bar.setValue(self.progress_bar.value() + bar_step)

And here the apply and run methods in the main file of the plugin:

    def apply(self):

    self.dlg.list.clear()
    self.dlg.progress_bar.setValue(0)
    if self.dlg.inputedit.text() == "":
        self.dlg.messagebar.pushMessage("Missing parameter", 'Please insert a word!',
                                        level=QgsMessageBar.WARNING, duration=2)
        return

    word = unicode(self.dlg.inputedit.text())
    self.dlg.start_worker(word)

def run(self):
    """Run method that performs all the real work"""        
    self.dlg.list.clear()
    self.dlg.inputedit.clear()
    self.dlg.progress_bar.setValue(0)
    self.dlg.connect(self.dlg.button_box.button(QDialogButtonBox.Apply), SIGNAL("clicked()"), self.apply)

    # show the dialog
    self.dlg.show()
    # Run the dialog event loop
    result = self.dlg.exec_()
    # See if OK was pressed
    if result:
        # Do something useful here - delete the line containing pass and
        # substitute with your code.
        pass

As i said everthing works fine but I get two strange behaviours:

  • If I click apply without entering anything in the QLineEdit the QgsMessageBar appears correctly but if I close the QDialog and I do the same the QgisMessageBar has two items (1 more, see picture)

enter image description here

  • If I insert a word in the QLineEdit after opening the plugin for the second time, clicking on apply Qgis crashes.

It seems that closing the Dialog it is not deleted and all the changing done during the first opening are preserved.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.