Qt / CSS: setStyleSheet is slow, suggested alternative using dynamic properties in CSS even worse


  • BINNED

    I have a large number of QProgressBars in a table1 and I want to change the color of the progress bars dynamically, depending on some state. For simplicity, let's assume it's the progress value, although my real use case is a bit more complex. My first attempt was using QPalette to change the color. That worked fine for the KDE theme, but the Windows native theming seems to not be affected by this. So next I resorted to using CSS style sheets2.
    Now, the problem is that dynamically computing a new style sheet whenever something changes, in a loop over all rows, is :doing_it_wrong: and extremely slow. For 2000 rows, the calls to setStyleSheet add up to 100ms, which is unacceptable for the GUI. So I searched for suggestions how to do it right instead. There's a page on the Qt wiki suggesting you can use dynamic properties with the stylesheet, as well as other posts refering to it.

    However, to make this work, the wiki entry suggests what looks like a complete hack:

    There are limitations to using this trick. The main one is that the style will not update itself automatically when the value of a property referenced from the style sheet changes. Instead, you must manually trigger an update of the visual appearance of a widget when a styled property changes. For example:

    myLineEdit->setProperty("urgent", true);
    myLineEdit->style()->unpolish(myLineEdit);
    myLineEdit->style()->polish(myLineEdit);

    This sounds awful, but as longs as it works. This is supposed to be faster:

    Using dynamic stylesheets in this way is very fast. I am working on an application that makes heavy use of dynamic stylesheet properties and have not noticed any performance degredation.

    But when I tried that, it's not faster at all. It's twice as slow!

    Any suggestions how I can do this "right" and get acceptable performance?

    1 The number of rows in the table depends on some specific user input. Of course, a large amount of rows can make this hard to use, so there's also a filter available to only display a much smaller number. For this, I use hideRow/showRow in a loop, which is also slow. The suggested solution for that is to instead do filtering with QSortFilterProxyModel. Supposedly, just wrapping the loop with setUpdatesEnabled does not work, but it did work for me, so I'm using that :kneeling_warthog:.

    2 I hate having to do that. I'm sure the web folks can create amazing stuff with enough CSS, but if I just change the color of the progress bar, everything else defaults to complete shit, so you need to build your way back up to something resembling acceptable.


    My real code is in C++, but I made a MWE using PyQt. This adds some overhead, but the gist is the same. The method using dynamic properties and the unpolish/polish hack is even slower than the naive version: qt_css_test.py

    Inline code
    #!/usr/bin/env python3
    
    import sys
    from PyQt5 import QtWidgets, QtCore
    
    numRows = 2000
    
    # Template for calling setStyleSheet (static CSS, dynamically changed)
    styleTemplate = """
    QProgressBar::chunk {
        background-color: %s;
        width: 1px;
    }
    QProgressBar {
        background-color: %s;
        border: none;
        text-align: center;
    }
    """
    
    # Style sheet with attributes for dynamic properties (set once, properties dynamically changed)
    dynStyle = """
    QProgressBar { border: none; text-align: center; }
    QProgressBar::chunk { width: 1px; }
    QProgressBar[state="low"] { background-color: lightyellow; }
    QProgressBar[state="low"]::chunk { background-color: yellow;}
    QProgressBar[state="high"] { background-color: orange; }
    QProgressBar[state="high"]::chunk { background-color: red; }
    """
    
    class Window(QtWidgets.QDialog):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.ui = Ui_Window()
            self.ui.setupUi(self)
            self.ui.updateBtn.clicked.connect(self.on_updateBtn_clicked)
            self.tickOfs = 0
            self.initTable(self.ui.tableCssTemplate)
            self.initTable(self.ui.tableDynProp)
            self.tickValues()
            self.initStyleDynProp(self.ui.tableDynProp)
            self.updateStyles()
    
        # Variant 1: Generates a style from a template, then sets it with setStyleSheet.
        # This is slow because of setStyleSheet
        def updateStyleCssTemplate(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                col, back = ("yellow", "lightyellow") if bar.value() < 50 else ("red", "orange")
                bar.setStyleSheet(styleTemplate % (col, back))
    
        # Variant 2: Only update a dynamic property corresponding to the CSS attribute
        # This is supposed to be faster, because it avoids setStyleSheet. However, this doesn't get
        # updates with a simple repaint. Instead, this unpolish/polish hack has to be used, which
        # is even slower.
        def updateStyleDynProp(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                bar.setProperty("state", "low" if bar.value() < 50 else "high")
                bar.style().unpolish(bar)
                bar.style().polish(bar)
    
        def on_updateBtn_clicked(self):
            self.tickValues()
            self.updateStyles()
    
        def updateStyles(self):
            t1 = QtCore.QElapsedTimer()
            t1.start()
            self.updateStyleCssTemplate(self.ui.tableCssTemplate)
            self.ui.labelCssTemplate.setText(f"Time {t1.elapsed()} ms")
            t2 = QtCore.QElapsedTimer()
            t2.start()
            self.updateStyleDynProp(self.ui.tableDynProp)
            self.ui.labelDynProp.setText(f"Time {t2.elapsed()} ms")
    
        def tickValues(self):
            for tbl in (self.ui.tableCssTemplate, self.ui.tableDynProp):
                for r in range(numRows):
                    bar = tbl.cellWidget(r, 1)
                    p = (r+self.tickOfs) % 102 - 1
                    if p < 0:
                        bar.setRange(0, 0)
                    else:
                        bar.setRange(0, 100)
                    bar.setValue(p)
            self.tickOfs += 1
    
        def initTable(self, tbl):
            tbl.setRowCount(numRows)
            for r in range(numRows):
                tbl.setItem(r, 0, QtWidgets.QTableWidgetItem(["Foo", "Bar", "Baz"][r%3]))
                tbl.setCellWidget(r, 1, QtWidgets.QProgressBar())
            tbl.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
            tbl.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
    
        def initStyleDynProp(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                bar.setStyleSheet(dynStyle)
    
    # Generated UI code
    class Ui_Window(object):
        def setupUi(self, Window):
            Window.setObjectName("Window")
            Window.resize(800, 300)
            self.gridLayout = QtWidgets.QGridLayout(Window)
            self.labelCssTemplate = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.labelCssTemplate, 2, 0, 1, 1)
            self.label_2 = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
            self.tableDynProp = QtWidgets.QTableWidget(Window)
            self.tableDynProp.setColumnCount(2)
            self.tableDynProp.setRowCount(0)
            item = QtWidgets.QTableWidgetItem()
            self.tableDynProp.setHorizontalHeaderItem(0, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableDynProp.setHorizontalHeaderItem(1, item)
            self.tableDynProp.horizontalHeader().setStretchLastSection(True)
            self.gridLayout.addWidget(self.tableDynProp, 1, 1, 1, 1)
            self.label = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
            self.tableCssTemplate = QtWidgets.QTableWidget(Window)
            self.tableCssTemplate.setColumnCount(2)
            self.tableCssTemplate.setRowCount(0)
            item = QtWidgets.QTableWidgetItem()
            self.tableCssTemplate.setHorizontalHeaderItem(0, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableCssTemplate.setHorizontalHeaderItem(1, item)
            self.tableCssTemplate.horizontalHeader().setStretchLastSection(True)
            self.gridLayout.addWidget(self.tableCssTemplate, 1, 0, 1, 1)
            self.labelDynProp = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.labelDynProp, 2, 1, 1, 1)
            self.updateBtn = QtWidgets.QPushButton(Window)
            self.gridLayout.addWidget(self.updateBtn, 3, 1, 1, 1)
    
            self.retranslateUi(Window)
            #QtCore.QMetaObject.connectSlotsByName(Window)
    
        def retranslateUi(self, Window):
            _translate = QtCore.QCoreApplication.translate
            self.label_2.setText(_translate("Window", "Static CSS with dynamic properties ([un]polish)"))
            item = self.tableDynProp.horizontalHeaderItem(0)
            item.setText(_translate("Window", "Name"))
            item = self.tableDynProp.horizontalHeaderItem(1)
            item.setText(_translate("Window", "Status"))
            self.label.setText(_translate("Window", "Dynamically set CSS from template (setStyleSheet)"))
            item = self.tableCssTemplate.horizontalHeaderItem(0)
            item.setText(_translate("Window", "Name"))
            item = self.tableCssTemplate.horizontalHeaderItem(1)
            item.setText(_translate("Window", "Status"))
            self.updateBtn.setText(_translate("Window", "Update"))
    
    def main():
        app = QtWidgets.QApplication(sys.argv)
        mainwin = Window()
        mainwin.show()
        app.exec_()
    
    if __name__ == '__main__':
        main()
    


  • times.png

    Agreed, kinda slow, but also not 100ms. Might be platform or HW dependent? The python thing is using about 4% of one CPU core for that according to top (it's not showing up on the GPU at all, so it isn't that either). Nevermind, have to mash update and then it goes crazy. Timing still the same though.


  • BINNED

    @cvi maybe this RHEL7 box isn't particularly fast. Just s/2000/10000/ then.

    Also, to give more context to "unacceptable": Well, it updates in the background, a lot. I have that rate limited, but of course 100ms is no good.



  • @topspin Yeah, I see the scaling problem with the suggested s/2000/10000.


  • Discourse touched me in a no-no place

    @topspin My initial comment is an easy one: why is it spending effort on updating the display of progress bars that aren't actively visible on the screen?


  • BINNED

    @dkf no idea how to easily implement doing that lazily.


  • Discourse touched me in a no-no place

    @topspin said in Qt / CSS: setStyleSheet is slow, suggested alternative using dynamic properties in CSS even worse:

    @dkf no idea how to easily implement doing that lazily.

    I just asked because I know the equivalent in Tk would be fast because that's pretty strict about only updating things that are actually displaying, and it is a big win in some cases. It would seem odd to me for Qt to not be using that trick too.


  • BINNED

    @dkf said in Qt / CSS: setStyleSheet is slow, suggested alternative using dynamic properties in CSS even worse:

    @topspin said in Qt / CSS: setStyleSheet is slow, suggested alternative using dynamic properties in CSS even worse:

    @dkf no idea how to easily implement doing that lazily.

    I just asked because I know the equivalent in Tk would be fast because that's pretty strict about only updating things that are actually displaying, and it is a big win in some cases. It would seem odd to me for Qt to not be using that trick too.

    It's not redrawing things that aren't visible, just updating a property. Redrawing indeed only happens for visible things.

    As I said, it already feels like a huge hack that you have to do the unpolish/polish dance to begin with, i.e., probably not designed that way. And that wiki entry seems to be more semi-offical than anything.
    But wait a second, you've got me thinking: what's the point of that polish call? Surely that should happen automatically as needed if you invalidated it with unpolish before.

    Let me try ...

    Bildschirmfoto 2024-02-26 um 21.07.14.png

    Yay! That's still slower than I'd like, but an order of magnitude faster than before (or 5x than the reference). Maybe the rest is a bit of Python slowness, too. Thanks!

    (I'd still appreciate better ideas, though, if anyone has them.)


  • BINNED

    I doubt anybody cares, but I guess I should still follow up on it:

    After taking a step back and rethinking it, I decided to scrap the whole approach. As I said in the OP, I originally wanted to use QPalette for changing the progress bar colors, but that doesn't work with native theming on Windows, so I did CSS styling instead.
    But wait, using CSS I already have a very much non-native theme. So I can just use a QStyle which supports the palette changes I want! If neither approach is using the native theme, I might as well use the one that doesn't require hacks to work around the horrible CSS performance in Qt. So that's what I did: use the built-in Fusion style QStyleFactory::create("fusion"). Problem solved.

    While I was at it, I decided to also slay the :kneeling_warthog: and change the QTableWidget with setCellWidget for every row, which had clearly outgrown its use case, to a QTableView with a corresponding table model and QStyledItemDelegate for drawing contents as progress bars.
    Now, there's one tricky bit remaining: indeterminate progress bars are drawn with an animation in this style, but the styling API doesn't actually give a way to manually do this. What happens is that, internally, the style creates its own animation timer per widget that you hand it and calls update correspondingly, or stops the animation timer again if it deems no updates are required (i.e. the progress bar isn't in indeterminate state). Since I got rid of all the cell widgets I only have one widget which paints many progress bars via the item delegate. So the trick is to pass the viewport of the table for calls with an indeterminate progress bar (opt.styleObject = self.widget.viewport()) and null otherwise (so the timer doesn't get stopped if not all cells are indeterminate).

    qt_table.png

    Still seems a bit laggy in Python (timing doesn't really work because the real work isn't done in the call), but I don't notice anything in my actual C++ code.

    Just for posterity, here's the code (including all the old stuff)

    qt_table.py

    #!/usr/bin/env python3
    
    import sys
    from PyQt5 import QtWidgets, QtCore, QtGui
    
    numRows = 5000
    
    # Variant 1: Template for calling setStyleSheet (static CSS, dynamically changed)
    styleTemplate = """
    QProgressBar::chunk {
        background-color: %s;
        width: 1px;
    }
    QProgressBar {
        background-color: %s;
        border: none;
        text-align: center;
    }
    """
    
    # Variant 2: Style sheet with attributes for dynamic properties (set once, properties dynamically changed)
    dynStyle = """
    QProgressBar { border: none; text-align: center; }
    QProgressBar::chunk { width: 1px; }
    QProgressBar[state="low"] { background-color: lightyellow; }
    QProgressBar[state="low"]::chunk { background-color: yellow;}
    QProgressBar[state="high"] { background-color: orange; }
    QProgressBar[state="high"]::chunk { background-color: red; }
    """
    
    # Variant 3: use QPalette instead of style sheets
    paletteLow = QtGui.QPalette()
    paletteLow.setColor(QtGui.QPalette.Highlight, QtGui.QColor("yellow").darker(120))
    paletteLow.setColor(QtGui.QPalette.Base, QtGui.QColor("lightyellow"))
    paletteLow.setColor(QtGui.QPalette.Text, QtGui.QColor("black"))
    paletteLow.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor("black"))
    paletteHigh = QtGui.QPalette()
    paletteHigh.setColor(QtGui.QPalette.Highlight, QtGui.QColor("red"))
    paletteHigh.setColor(QtGui.QPalette.Base, QtGui.QColor("orange"))
    paletteHigh.setColor(QtGui.QPalette.Text, QtGui.QColor("black"))
    paletteHigh.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor("black"))
    
    
    # Variant 3: Use an item delegate for a QTableView to paint the column as a progress bar
    class ProgressDelegate(QtWidgets.QStyledItemDelegate):
        def __init__(self, widget, style):
            super().__init__(widget)
            self.widget = widget
            self.style = style
    
        def paint(self, painter, option, index):
            progress = index.data(QtCore.Qt.DisplayRole)
            opt = QtWidgets.QStyleOptionProgressBar()
            opt.rect = option.rect
            opt.minimum = 0
            opt.maximum = 100
            opt.progress = progress
            if progress < 0:
                opt.progress = 0
                opt.maximum = 0
                opt.styleObject = self.widget.viewport()
            opt.textVisible = progress >= 0
            opt.palette = paletteLow if progress < 50 else paletteHigh
            opt.text = "{}%".format(progress)
            self.style.drawControl(QtWidgets.QStyle.CE_ProgressBar, opt, painter)
    
    
    class TableModel(QtCore.QAbstractTableModel):
        def __init__(self):
            super().__init__()
            self.tickOfs = -1
    
        def data(self, index, role):
            if role == QtCore.Qt.DisplayRole:
                r, c = index.row(), index.column()
                if c == 0:
                    return ["Foo", "Bar", "Baz"][r%3]
                return (r+self.tickOfs) % 102 - 1
    
        def headerData(self, section, orientation, role):
            if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
                return ["Text", "Progress"][section]
            return super().headerData(section, orientation, role)
    
        def rowCount(self, index):
            return numRows
    
        def columnCount(self, index):
            return 2
    
        def tickValues(self):
            self.tickOfs += 1
            self.dataChanged.emit(self.index(0,0), self.index(1, numRows-1))
    
    
    class Window(QtWidgets.QDialog):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.ui = Ui_Window()
            self.ui.setupUi(self)
            self.ui.updateLeftBtn.clicked.connect(self.on_updateLeftBtn_clicked)
            self.ui.updateMiddleBtn.clicked.connect(self.on_updateMiddleBtn_clicked)
            self.ui.updateRightBtn.clicked.connect(self.on_updateRightBtn_clicked)
            self.tickOfs = 0
            self.initTable(self.ui.tableCssTemplate)
            self.initTable(self.ui.tableDynProp)
            self.ui.tableView.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
            self.updateStylesLeft()
            self.updateStylesMiddle()
            self.model = TableModel()
            self.delegate = ProgressDelegate(self.ui.tableView, QtWidgets.QStyleFactory.create("fusion"))
            self.ui.tableView.setItemDelegateForColumn(1, self.delegate)
            self.ui.tableView.setModel(self.model)
            self.tickValuesLeft()
            self.tickValuesMiddle()
            self.tickValuesRight()
            self.initStyleDynProp(self.ui.tableDynProp)
    
        # Variant 1: Generates a style from a template, then sets it with setStyleSheet.
        # This is slow because of setStyleSheet
        def updateStyleCssTemplate(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                col, back = ("yellow", "lightyellow") if bar.value() < 50 else ("red", "orange")
                bar.setStyleSheet(styleTemplate % (col, back))
    
        # Variant 2: Only update a dynamic property corresponding to the CSS attribute
        # This is supposed to be faster, because it avoids setStyleSheet. However, this doesn't get
        # updates with a simple repaint. Instead, this unpolish/polish hack has to be used, which
        # is even slower.
        def updateStyleDynProp(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                bar.setProperty("state", "low" if bar.value() < 50 else "high")
                bar.style().unpolish(bar)
                #bar.style().polish(bar)
    
        def on_updateLeftBtn_clicked(self):
            self.tickValuesLeft()
            self.updateStylesLeft()
    
        def on_updateMiddleBtn_clicked(self):
            self.tickValuesMiddle()
            self.updateStylesMiddle()
    
        def on_updateRightBtn_clicked(self):
            self.tickValuesRight()
            self.updateRight()
    
        def updateStylesLeft(self):
            t = QtCore.QElapsedTimer()
            t.start()
            self.updateStyleCssTemplate(self.ui.tableCssTemplate)
            self.ui.labelCssTemplate.setText(f"Time {t.elapsed()} ms")
    
        def updateStylesMiddle(self):
            t = QtCore.QElapsedTimer()
            t.start()
            self.updateStyleDynProp(self.ui.tableDynProp)
            self.ui.labelDynProp.setText(f"Time {t.elapsed()} ms")
    
        def updateRight(self):
            t = QtCore.QElapsedTimer()
            t.start()
            self.ui.tableView.viewport().update()
            self.ui.labelView.setText(f"Time {t.elapsed()} ms")
    
        def tickValuesLeft(self):
            self.tickValues(self.ui.tableCssTemplate)
    
        def tickValuesMiddle(self):
            self.tickValues(self.ui.tableDynProp)
    
        def tickValuesRight(self):
            self.model.tickValues()
    
        def tickValues(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                p = (r+self.tickOfs) % 102 - 1
                if p < 0:
                    bar.setRange(0, 0)
                else:
                    bar.setRange(0, 100)
                bar.setValue(p)
            self.tickOfs += 1
    
        def initTable(self, tbl):
            tbl.setRowCount(numRows)
            for r in range(numRows):
                tbl.setItem(r, 0, QtWidgets.QTableWidgetItem(["Foo", "Bar", "Baz"][r%3]))
                tbl.setCellWidget(r, 1, QtWidgets.QProgressBar())
            tbl.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
            tbl.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
    
        def initStyleDynProp(self, tbl):
            for r in range(numRows):
                bar = tbl.cellWidget(r, 1)
                bar.setStyleSheet(dynStyle)
    
    # Generated UI code
    class Ui_Window(object):
        def setupUi(self, Window):
            Window.resize(950, 300)
            self.gridLayout = QtWidgets.QGridLayout(Window)
            self.labelCssTemplate = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.labelCssTemplate, 2, 0, 1, 1)
            self.label_2 = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
            self.tableDynProp = QtWidgets.QTableWidget(Window)
            self.tableDynProp.setColumnCount(2)
            self.tableDynProp.setRowCount(0)
            item = QtWidgets.QTableWidgetItem()
            self.tableDynProp.setHorizontalHeaderItem(0, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableDynProp.setHorizontalHeaderItem(1, item)
            self.tableDynProp.horizontalHeader().setStretchLastSection(True)
            self.gridLayout.addWidget(self.tableDynProp, 1, 1, 1, 1)
            self.label = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
            self.tableCssTemplate = QtWidgets.QTableWidget(Window)
            self.tableCssTemplate.setColumnCount(2)
            self.tableCssTemplate.setRowCount(0)
            item = QtWidgets.QTableWidgetItem()
            self.tableCssTemplate.setHorizontalHeaderItem(0, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableCssTemplate.setHorizontalHeaderItem(1, item)
            self.tableCssTemplate.horizontalHeader().setStretchLastSection(True)
            self.gridLayout.addWidget(self.tableCssTemplate, 1, 0, 1, 1)
            self.labelDynProp = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.labelDynProp, 2, 1, 1, 1)
            self.updateMiddleBtn = QtWidgets.QPushButton(Window)
            self.gridLayout.addWidget(self.updateMiddleBtn, 3, 1, 1, 1)
            self.tableView = QtWidgets.QTableView(Window)
            self.tableView.horizontalHeader().setStretchLastSection(True)
            self.gridLayout.addWidget(self.tableView, 1, 2, 1, 1)
            self.label_3 = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.label_3, 0, 2, 1, 1)
            self.updateRightBtn = QtWidgets.QPushButton(Window)
            self.gridLayout.addWidget(self.updateRightBtn, 3, 2, 1, 1)
            self.updateLeftBtn = QtWidgets.QPushButton(Window)
            self.gridLayout.addWidget(self.updateLeftBtn, 3, 0, 1, 1)
            self.labelView = QtWidgets.QLabel(Window)
            self.gridLayout.addWidget(self.labelView, 2, 2, 1, 1)
    
            self.retranslateUi(Window)
            #QtCore.QMetaObject.connectSlotsByName(Window)
    
        def retranslateUi(self, Window):
            _translate = QtCore.QCoreApplication.translate
            self.label_2.setText(_translate("Window", "Static CSS with dynamic properties ([un]polish)"))
            item = self.tableDynProp.horizontalHeaderItem(0)
            item.setText(_translate("Window", "Text"))
            item = self.tableDynProp.horizontalHeaderItem(1)
            item.setText(_translate("Window", "Progress"))
            self.label.setText(_translate("Window", "Dynamically set CSS from template (setStyleSheet)"))
            item = self.tableCssTemplate.horizontalHeaderItem(0)
            item.setText(_translate("Window", "Text"))
            item = self.tableCssTemplate.horizontalHeaderItem(1)
            item.setText(_translate("Window", "Progress"))
            self.updateMiddleBtn.setText(_translate("Window", "Update (middle)"))
            self.label_3.setText(_translate("Window", "TableView"))
            self.updateRightBtn.setText(_translate("Window", "Update (right)"))
            self.updateLeftBtn.setText(_translate("Window", "Update (left)"))
    
    
    
    def main():
        app = QtWidgets.QApplication(sys.argv)
        mainwin = Window()
        mainwin.show()
        app.exec_()
    
    if __name__ == '__main__':
        main()
    


  • @topspin said in Qt / CSS: setStyleSheet is slow, suggested alternative using dynamic properties in CSS even worse:

    I doubt anybody cares, but I guess I should still follow up on it:

    I'm a bit sorry for you that I didn't see your OP before, because while reading it I was thinking that playing with QStyle seemed a better idea to me than CSS (partly because, like you I guess, I'm not a web dev and I just don't grok CSS). I would also have suggested looking at item delegates to avoid doing unnecessary changes. Basically... what you found out yourself!

    Well I'm glad you did find it out in the end (plus anyway I would just have suggested the idea, not :kneeling_warthog: 🔫 which is the really hard part!).



  • @remi If I wanted to use css in the ui for a c++ thing I think I would listen to http requests and open the system browser


  • Discourse touched me in a no-no place

    @sockpuppet7 said in Qt / CSS: setStyleSheet is slow, suggested alternative using dynamic properties in CSS even worse:

    @remi If I wanted to use css in the ui for a c++ thing I think I would listen to http requests and open the system browser

    "Electron is just a browser?" 👨🚀 🔫 👩🚀 "Always was."


Log in to reply