How to update the delegate of a QTableView from a Thread

0

I would like to know if you know how to update a progress bar, which is drawn using a Delegate, the class is called QAbstractItemDelegate, this class was overwritten to draw a QWidget in a specific QProgressBar progress bar, use a QMainWindow as a main container, in which there is a QListView and a QStandardItemModel, plus a button, when you click the button, it calls a function that uses a QThread thread class in which, in the run method, a counter, which I would like it to be updated in the QListView which in turn has a QAbstractItemDelegate mentioned above, which draws the progress bar, the counter works but the progress bar is updated only when I click on the QlistView. I am using PyQt 5.4.1, I leave the source code.

I send a cordial greeting, I hope you explained it well.

import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class Delegado(QAbstractItemDelegate):

    def __init__(self):
        super(Delegado, self).__init__()

    def paint(self, painter, option, index):
        estilo = "QProgressBar#barraDelegado{border: 0px solidgrey; border-radius: 5px; text-align: center; color: grey; font-weight: bold;} "
        estilo += "QProgressBar#barraDelegado::chunk {background-color: #CD96CD;}"
        f = open("../css/estilos.css")

        barra = QProgressBar()
        barra.setObjectName("barraDelegado")
        value = index.data()
        barra.resize(option.rect.size())
        barra.setMinimum(0)
        barra.setMaximum(100)
        barra.setAlignment(Qt.AlignCenter)
        barra.setValue(int(value))
        if barra.value() < 51:
            barra.setStyleSheet(estilo)
        elif barra.value() > 50 and barra.value() < 100:
            barra.setStyleSheet("QProgressBar#barraDelegado{font-weight: bold; border: 0px solidgrey; border-radius: 5px; text-align: center; color: white} QProgressBar#barraDelegado::chunk {background-color: orange; color:red;}")
        elif barra.value() > 99:
            barra.setStyleSheet("QProgressBar#barraDelegado{font-weight: bold; border: 0px solidgrey; border-radius: 5px; text-align: center; color: white} QProgressBar#barraDelegado::chunk {background-color: lightgreen; color:red;}")

        painter.save()
        painter.translate(option.rect.topLeft())
        barra.render(painter)
        painter.restore()
        f.close()

    def createEditor(self, parent, option, index):
        self.slider = QSlider(parent)
        self.slider.setAutoFillBackground(True)
        self.slider.setOrientation(Qt.Horizontal)
        # slider.installEventFilter(self)
        self.slider.setRange(0, 100)
        #print("return slider")

        self.slider.valueChanged.connect(self.imprime)
        return self.slider

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)
        print("update geometry")

    def setEditorData(self, editor, index):
        #print("editor data")
        #value = index.model().data(index, Qt.DisplayRole)
        editor.setValue(int(index.data()))


    def setModelData(self, widget, model, index):
        print("columna: {0} Fila: {1}".format(index.column(), index.row()))
        model.setData(model.index(index.row(), 1), str(widget.value()), Qt.DisplayRole)
        value = widget.value()
        widget.setToolTip(str(value))
        print(widget.value())
        #self.modelo.setData(self.modelo.index(0, 1), str(progress), Qt.DisplayRole)
    def imprime(self, valor):
        self.slider.setToolTip(str(valor))
        print(valor)

class Hilo(QThread):

    def __init__(self, modelo, tabla, delegado):
        QThread.__init__(self)
        self.modelo = modelo
        self.tabla = tabla
        self.Delegate = delegado

    def __del__(self):
        print("Fruncion __del__")
        self.wait()

    def run(self):
        for i in range(0, 100):
            time.sleep(0.3)  # artificial time delay
            #self.emit(SIGNAL('update(QString)'), "from work thread " + str(i))
            self.modelo.setData(self.modelo.index(0, 1), str(i), Qt.DisplayRole)
            self.tabla.update()
            #self.tabla.setItemDelegateForColumn(1, self.Delegate)
            print("ejemplo {0}".format(i))
        return
   def closeEvent(self, *args, **kwargs):
        print("saliendo..")
        try:
            self.t._stop()
        except:
            pass
        print("Salido")

def main():
    app = QApplication(sys.argv)
    obj = window()
    obj.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

    
asked by moises castro 25.11.2017 в 05:28
source

1 answer

1

A basic rule of Qt is that you should not update the GUI from a secondary thread directly, and when you use the setData () method you are doing it because of what you are generating that does not update the model correctly.

In order to update the GUI we must send the data to the main thread, a way to do it is through signals, but a much simpler way is to use QMetaObject.invokeMethod, this invokes the methods, for this we must indicate that the Connection type is Qt.QueuedConnection type, this indicates that it will communicate between threads.

class Hilo(QThread):
    def __init__(self, modelo, *args, **kwargs):
        QThread.__init__(self, *args, **kwargs)
        self.modelo = modelo

    def __del__(self):
        print("Fruncion __del__")
        self.wait()

    def run(self):
        for i in range(0, 100):
            time.sleep(0.3)  # artificial time delay
            import random 
            row = random.randint(0, self.modelo.rowCount()-1)
            ix = self.modelo.index(row, 1)
            QMetaObject.invokeMethod(self.modelo,
                "setData", Qt.QueuedConnection, 
                Q_ARG(QModelIndex, ix), 
                Q_ARG(QVariant, str(i)))
            print("ejemplo {0}".format(i))

It is neither necessary nor correct to pass the view nor the delegate.

A complete example can be found in the following link .

    
answered by 25.11.2017 / 06:26
source