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

Updating widget stylesheet in succession



  • Suppose I have something like the following:

    ui->lineEdit->setStyleSheet("background-color: orange");
    ui->lineEdit->setStyleSheet("background-color: grey");

    As both are added to the event loop, will lineEdit flash orange for a split second (maybe so fast it's unnoticeable) before setting to grey?

    The above is a simplified version of a problem I'm facing. I have a series of widgets which need their stylesheet updated according to the selected row of a tableview. Upon each selection, some widgets may appear orange, while others grey. I've achieved this by do the following:

    When a row is selected, set all widgets to grey (to clear). 
    
    Set relevant widgets to orange, while others remain on grey.
    

    Is there a better way to do this?


  • Moderators

    There are several levels of paranoia you can employ here, and you just need to check how much of it is enough for your case.

    AFAIK It's not documented whether the change will be instant or not. Qt is double buffered, so there's little chance you see flicker, but you may in some cases.

    So the first thing you can do is use setUpdatesEnabled(false) on the parent, apply your changes and turn updates back on which will repaint the whole thing once.

    The second thing is stylesheets are generally slow. Because of the caching they work better when set once and not changed after, but if you have to change them try to do it only where needed. So in your case if the algorithm is something like this:

    for all widgets
        set background to gray
    
    for some widgets
       set background to orange
    

    change it to

    for each widget
       if some condition
           set background to orange
       else
          set background to gray
    

    This way you only change the stylesheet once for each widget.
    Another level of optimization is checking if the widget actually needs to change its styleshet. If it's already gray there's no need to re-apply the same stylesheet, so

    for each widget
       new_state = some condition ? orange : gray
       if new_state is different than current
          set background to new_state
    

    You might think that it's a lot of extra branching in those loops, but all those checks will be nothing next to reparsing, recaching and reapplying the stylesheets.

    So as mentioned before - how much of it is enough for your case you'll have to check on your own.



  • So, originally, I was going to do something along the lines of:

    for each widget
      if widget is in selected tableview list
          set to orange
     else
          set to grey
    

    However, this changes the complexity to N^2 (instead of 2N). In reality, given the number of widgets (up to 50), this would probably have a negligible impact on performance. Regardless, I wondered whether it was acceptable given the circumstances.

    Also, I'll probably be moving to dynamic properties which I believe are more efficient. Would a flicker show in this case?


  • Moderators

    @EaccB said:

    So, originally, I was going to do something along the lines of:

    Nah, don't do that. A pre-pass to mark the selected ones in some way is a better option.

    Also, I'll probably be moving to dynamic properties

    I'm usually skeptical about those in tight loops like this, because it's a string based dictionary lookup and a type cast (variant to whatever). Not great. I usually only use them to mark a single thing that I won't be checking too often, but you'll have to measure how much it matters to you.

    Would a flicker show in this case?

    If you use that setUpdatesEnabled method I mentioned then no. Otherwise you'll have to see for yourself, but I wouldn't rely on it. My advice is to focus on modifying only what is needed to change and only doing it once.



  • Might be best if I describe my actual problem in more detail.

    I have a series custom widgets which are displayed in a grid. Each widget can be either "Active" or "Inactive". This. is achieved via 2 functions, activate() and deactivate(). Amongst other things, these functions set the widget's border to orange and grey respectively.

    Back in Mainwindow, I have a table view where each row is associated with a QList which contains a struct of data which will be passed to the widget for processing (also contains a pointer to the relevant widget). If the QList for the selected row contains a struct pointing to a widget, that widget should be "Active". Everything else should be "Inactive".

    On selection of a row, I see no other way to update the widgets than checking whether every widget in the grid is present in the QList of the selected row. An alternative is to populate each QList with a struct for every widget and specify which should be active/inactive, although not too keen on this idea.


  • Lifetime Qt Champion

    Hold the pointers to the widgets in your struct so you can directly access them when the struct is moved from active to inactive and back.



  • I think we got out wires crossed. The structs contained in the QLists are only for widgets which should be active for that row. Setting the relevant widgets to active is trivial, as you say access them directly. Setting everything else to inactive seems to be more difficult.


  • Lifetime Qt Champion

    Then hold the inactive ones in another QList and move them around.



  • How about using QWidget::setEnabled(bool) to toggle the enabled state of the widgets. The stylesheet can then have a different style for enabled and disabled states. One thing to consider here is that child widgets would inherit this state. So, this works for non-interactive widgets only (like showing text).



  • @Christian-Ehrlicher

    I'm not sure what you mean. Could you provide an example please?


  • Lifetime Qt Champion

    Hold an additional container with the inactive ones. When you move them between the two containers you know which style to set.



  • @Christian-Ehrlicher

    In which case each row would need to have 2 QLists, one for Active and Inactive. Seems a bit redundant to store empty structs for widgets which are inactive.


  • Lifetime Qt Champion

    @EaccB said in Updating widget stylesheet in succession:

    Seems a bit redundant to store empty structs for widgets which are inactive.

    I never said you should store some structs, only the pointers to the widgets. But it's up to you. If you need the information you have to store it somewhere.



  • @Christian-Ehrlicher

    How about something like this?:

    setEnabled(false);
    
    for all widgets:
       widget.deactivate();
    
    for widgets in QList:
      widget.activate();
    
    setEnabled(true);
    

    I don't think that would be terribly inefficient. What do you think?


  • Lifetime Qt Champion

    It depends on the size but yes. Still don't understand why you can't the state when it changes from active to inactive and back.


Log in to reply