Search code examples
pythonpyqt5delegatespyside6

ProgressBarColumnDelegate with PySide6/PyQt6


I tried to implement a ProgressBar delegate for a tableview and stumbled accross this example:

How to include a column of progress bars within a QTableView?

The example for the tableview/PyQt5 works perfectly well with PyQt5.

But trying it with PySide6 produces one malfunctional progressbar in the first row, and gray boxes in all the other rows. PyQt6 does not work at all, even after adding fully qualified names.

Here is the code:

gui = "PySide6"
#gui = "PyQt5"
#gui = "PyQt6"

if gui =="PyQt5":

    from PyQt5.QtWidgets import QStyle, QStyledItemDelegate, QStyleOptionProgressBar,\
        QApplication, QTableView
    from PyQt5.QtGui import QStandardItem, QStandardItemModel
    from  PyQt5.QtCore import Qt
if gui == "PySide6":
    from PySide6.QtWidgets import QStyle, QStyledItemDelegate, QStyleOptionProgressBar,\
        QApplication, QTableView
    from PySide6.QtGui import QStandardItem, QStandardItemModel
    from PySide6.QtCore import Qt
if gui == "PyQt6":
    from PyQt6.QtWidgets import QStyle, QStyledItemDelegate, QStyleOptionProgressBar,\
        QApplication, QTableView
    from PyQt6.QtGui import QStandardItem, QStandardItemModel
    from PyQt6.QtCore import Qt


data = [("1", "abc", 10), ("2", "def", 60),
        ("3", "ghi", 20), ("4", "jkl", 80), 
        ("5", "mno", 100)]
  
        
class ProgressDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        progress = index.data(Qt.ItemDataRole.UserRole+1000)
        opt = QStyleOptionProgressBar()
        opt.rect = option.rect
        opt.minimum = 0
        opt.maximum = 100
        opt.progress = progress
        opt.text = f"{progress}%"
        opt.textVisible = True
        QApplication.style().drawControl(QStyle.CE_ProgressBar, opt, painter)


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    w = QTableView()
    delegate = ProgressDelegate(w)
    w.setItemDelegateForColumn(2, delegate)
    model = QStandardItemModel(0, 3)
    model.setHorizontalHeaderLabels(["ID", "Name", "Progress"])
    for _id, _name, _progress in data:
        it_id = QStandardItem(_id)
        it_name = QStandardItem(_name)
        it_progress = QStandardItem()
        it_progress.setData(_progress, Qt.ItemDataRole.UserRole+1000)
        model.appendRow([it_id, it_name, it_progress])
    w.setModel(model)
    w.show()
    app.exec()

Anyone has a solution to this?


Solution

  • In Qt6 you have to indicate the orientation of the progressbar using the State_Horizontal flag in the state attribute, in Qt5 the orientation property was used and by default it was horizontal:

    class ProgressDelegate(QStyledItemDelegate):
        def paint(self, painter, option, index):
            progress = index.data(Qt.ItemDataRole.UserRole + 1000)
    
            opt = QStyleOptionProgressBar()
            opt.rect = option.rect
            opt.minimum = 0
            opt.maximum = 100
            opt.progress = progress
            opt.text = f"{progress}%"
            opt.textVisible = True
            opt.state |= QStyle.StateFlag.State_Horizontal # <--
            style = (
                option.widget.style() if option.widget is not None else QApplication.style()
            )
            style.drawControl(
                QStyle.ControlElement.CE_ProgressBar, opt, painter, option.widget
            )