Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Is there a better method than calling polish recursively on all children of a QWidget?



  • Question:

    Is there a better method than calling polish recursively on all children of the QWidgets?

    Context:

    I change the effective style of QLabels, QButtons, QWidgets, etc. by using setting properties and stylesheets.

    For instance, I have a few QWidgets with QLabels inside them (wParent1, wParent2, ...). Setting "active" to true on one of the widgets and not on the others will change the presentation of the QWidget and its children so that this QWidget appears as the active configuration/status to the user.

    Here is an example, setting up one QWidget:

    // Example with generic names
    wParent1 = new QWidget();
    wParent1->setObjectName(QStringLiteral("wParent1"));
    vLayout = new QVBoxLayout(wParent1);
    label1 = new QLabel(wParent1);
    label1->setText(QStringLiteral("label1"));
    
    vLayout->addWidget(label1);
    
    label2 = new QLabel(wParent1);
    label2->setText(QStringLiteral("label2"));
    
    vLayout->addWidget(label2);
    

    The QLabels are styled using a stylesheet, coloring the QLabel text as red in this example:

    QWidget[active=true] QLabel {
      color:red;
    }
    

    Using lists, vectors, hashes, I then update the "active" property on widgets.
    With the above example of a stylesheet, the QLabels inside QWidgets that are "active" should be "red".
    This works when the "active" property is set during the QFrame initialisation, but does not work later on when only polishing the QWidgets.
    I need to polish the QLabels explicitally.

    // Initialise list mapping index to Widget
    this->choiceMapping=QVector<QWidget *>(
    	{
    		this->ui->wParent1,
    		this->ui->wParent2,
    		this->ui->wParent3,
    	}
    );
    
    // Set active state of the widgets
    int i=0;
    for(auto item:this->radMapping) {
    	item->setProperty("active",(i==newRadType));
    	item->style()->unpolish(item);
    	item->style()->polish(item);
    	// Also tried item->ensurePolished();
    	i++;
    }
    

    So I fixed this by doing a recursive polish:

    static void polishRecursive(QWidget *const w) {
    	QStyle * const s=w->style();
            s->unpolish(w);
            s->polish(w);
    	for(QWidget *c:w->findChildren<QWidget *>()) {
    		s->unpolish(c); 
    		s->polish(c);
    	}
    }
    

    I was surprised that I have to implement this recursively, so I am looking for a better way (possibly more efficient) way of doing this.



  • @letop
    So far as I know, you have to polish all widgets, which you are doing.

    However, your polishRecursive() is not recursive, as it should be. It only does one layer of direct children. I would have expected it to call itself on the children. EDIT Oh, I see, you are using the findChildren() to find all descendants recursively. Might be more efficient (not generate a big list) if you only called polishRecursive() on the direct children and let that recurse. Or might make little difference.

    I'm a bit confused because in yours you only pick up QStyle * const s=w->style(); which is the style of the top-level widget and use that for polishing the descendants, I thought you would do that on the style for each widget. Also I note QStyle::polish(QWidget *widget) states:

    Note that the default implementation does nothing.

    But you probably know better than I do what you're doing... :)

    There is also a QStyle::polish(QApplication *application) which only says

    Late initialization of the given application object.

    I don't suppose that does the job for you over all widgets...?



  • @JonB Thank you for your feedback.

    Going thorugh the existing children lists and filter the widgets in specific code is surely a bit more efficient with regards to speed, but that's not the kind of optimisation I was looking for. Some frameworks have a dirty flag for instance that triggers an evalution by the graphics system when it is due for evaluation (on 20ms ticks for instance).

    The polish/unpolish functions could benefit from a better documentation. I have a stylesheet only at the QApplication level, I empty all other stylesheets that are set by QtDesigner - I use the only for that.
    I use the same "style()" because it works and it probably is a small performance gain.

    I haven't tried polishing the application itself, and I suppose that is an overkill for just updating a few widgets.

    The default implementation of (un)polish does nothing, but QStylesheetStyle does. It seems to wrap the baseStyle(), which is likely something like QWindowsStyle or QFusionStyle which do nothing.
    But that's doing exactly what is needed then, only the stylesheet impacts the layout and the QStylesheetStyle handles that.

    QWidget::ensurePolished works recursively, but polishes only "unpolished" QWidgets in the end.

    Upon examination of the QStylesheetStyle code, it seems that unpolishing may not be needed. So I am removing that from the "recursive" implementation and I'll see if it's ok. It seems to avoid quite a few unnecessary operations!

    Update: polishing "recursively" is just enough.