Wednesday, 25 July 2018

multithreading - qgis crash using QThread in a plugin script


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.




No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...