[SOLVED] Weird behavior with setStyleSheet and palette



  • Hi all,
    I have an app that can use 2 "themes": the default one and one set by a stylesheet (included as a resource). You can toggle between the two via a menu/action and the one currently in use is saved in the app's .ini file (as a boolean, no fancy stuff) when you quit the app, so that next time you start it, the same "theme" is applied.

    The below is the code associated to the action that switched "theme": I need to propagate a palette color to some custom objects (that are not styleSheet or palette aware) so after applying the stylesheet, I get the color from the palette and then I apply it to my objects.
    @
    void MainWin::on_actUseDarkTheme_triggered(bool checked)
    {
    QBrush br(Qt::SolidPattern);
    if (checked) {
    QFile f(":darkstyle/darkstyle.qss");
    if (!f.exists())
    qDebug() << "Unable to set stylesheet " << f.fileName();
    else {
    f.open(QFile::ReadOnly | QFile::Text);
    QTextStream ts(&f);
    qApp->setStyleSheet(ts.readAll());
    f.close();
    br.setColor(palette().color(QPalette::Window));
    }
    } else {
    qApp->setStyleSheet("");
    br.setColor(Qt::white);
    }

    m_MaxTMPlots->setBackground(br);
    m_MaxTMPlots->replot();
    m_MaxTMTrackMap->setBackground(br);
    m_MaxTMTrackMap->replot();
    

    }
    @

    All this works fine except in one situation: if I quit the application when using the theme set by stylesheet, next time I launch the app my custom objects have the wrong color. It is as if on line 13, the palette().color() call does not get the right color (the one set by the setStyleSheet call on line 11).

    After that, if I trigger the action twice (ie. back to default theme and then back again to custom theme) everything is fine.

    The funnier thing is this: when my app reads it's /in ifile, I read the bool value that indicates which theme to sue and then I call the function above (on_actUseDarkTheme_triggered) passing that flag. If instead of calling this function only once I call it twice (with the same bollean value), then everythig works fine !

    I'm on Windows 7 x64 with Qt 5.3 on MSVC 2010.

    MaX.


  • Lifetime Qt Champion

    Hi,

    What happens if you first set the brush and only then the stylesheet ?



  • [quote author="SGaist" date="1401048878"]Hi,
    What happens if you first set the brush and only then the stylesheet ?[/quote]
    Hi SGalst,
    if I do that, the right color is not passed to my object, neither at startup (as before) nor on subsequent calls of the function above.
    But that's kind of expected: if the stylesheet is not yet loaded, the palette will have it's "old" color so I end up passing the old color to my objects.

    Or maybe I misunderstood what you meant .. did you mean put line 13 above just before line 11 ?

    MaX.



  • Trying to clarify more: I've surrounded line 13 above with a couple of qDebug prints that show the color returned by the call to palette().color(). Something like this:

    @
    void MainWin::on_actUseDarkTheme_triggered(bool checked)
    {
    QBrush br(Qt::SolidPattern);
    qDebug() << "Dark : " << checked;
    if (checked) {
    QFile f(":darkstyle/darkstyle.qss");
    if (!f.exists())
    qDebug() << "Unable to set stylesheet " << f.fileName();
    else {
    f.open(QFile::ReadOnly | QFile::Text);
    QTextStream ts(&f);
    qApp->setStyleSheet(ts.readAll());
    f.close();
    qDebug() << "Pal Bw: " << palette().color(QPalette::Window);
    br.setColor(palette().color(QPalette::Window));
    qDebug() << "Pal Aw: " << palette().color(QPalette::Window);
    }
    } else {
    qApp->setStyleSheet("");
    br.setColor(Qt::white);
    }

    m_MaxTMPlots->setBackground(br);
    m_MaxTMPlots->replot();
    m_MaxTMTrackMap->setBackground(br);
    m_MaxTMTrackMap->replot();
    

    }
    @

    Now, let's see what the above prints out in 2 situations:

    1. .ini tell to use the default theme (no stylesheet to load). app fires up, function above is called (but does essentially nothing), everything has correct colors everywhere. Then I ask to switch to the other theme (calling the function above and triggering the load of the stylesheet). The output is (lines 2-4):

    @
    Debug 1: Dark : false
    Debug 2: Dark : true
    Debug 3: Pal Bw: QColor(ARGB 1, 0.188235, 0.184314, 0.184314)
    Debug 4: Pal Aw: QColor(ARGB 1, 0.188235, 0.184314, 0.184314)
    @

    That's already weird: I was expecting the color on the 1st qDebug (line 3) to be different.

    2. .ini tell to use the "other" theme, the function above is called, colors on my objects are wrong (but they are correct on the other standard widgets). I ask to switch to default theme and then to switch to other theme and output is:

    @
    Debug 1: Dark : true
    Debug 2: Pal Bw: QColor(ARGB 1, 0.941176, 0.941176, 0.941176)
    Debug 3: Pal Aw: QColor(ARGB 1, 0.941176, 0.941176, 0.941176)
    Debug 4: Dark : false
    Debug 5: Dark : true
    Debug 6: Pal Bw: QColor(ARGB 1, 0.188235, 0.184314, 0.184314)
    Debug 7: Pal Aw: QColor(ARGB 1, 0.188235, 0.184314, 0.184314)
    @

    So lines 1-3 are for the 1st call (at startup): the color printed out is wrong (i.e. it is not the one specified by the style sheet). Line 4 is for the first switch (back to default theme), all OK here.
    Lines 6 and 7 are for the second switch (back to other theme): the color is the right one (the one in the stylesheet), but it's strange that it is so also before the call of setStyleSheet.

    There seems to be some sort of "delayed" evaluation somewhere.

    Just wondering: could the fact that the 1st call of the function above is done within the constructor of my main window have an impact ?

    MaX.


  • Lifetime Qt Champion

    That last conclusion rings a bell… (can't find exactly what but…)

    One thing you can try is to do this setup in a slot that you call using QTimer::singleShot (last line of your constructor). So you ensure that the widget is completely initialized before setting your custom styling.



  • [quote author="SGaist" date="1401055274"]That last conclusion rings a bell… (can't find exactly what but…)

    One thing you can try is to do this setup in a slot that you call using QTimer::singleShot (last line of your constructor). So you ensure that the widget is completely initialized before setting your custom styling.[/quote]

    OMG ! The QTimer thing worked ! I'm not sure if I want to know why or not :)

    Notice that I even tried to have two steps initialization of my main window (with the code calling setStyleSheet in the 2nd step) and this did not work. However, if the setStyleSheet is called after mainwin.show(), than this works (but is obviuously bad as you se the window flicker from default stylesheet to new one).

    I'm not 100% convinced everything is fine on Qt side, but this stuff is way below my understanding of Qt so I'll be happy with the workaround.

    Thanks for the help !

    MaX.


  • Lifetime Qt Champion

    This generally ensure that the widget has finished it's construction and internal setup.

    You could try asking about it on the interest mailing list, you'll find there Qt's developers/mainainers (this forum is more user oriented)

    You're welcome !

    Since you have it working now, please update the thread title prepending [solved] so other forum users may know a solution has been found :)



  • Just marked the topic solved. I had to use the very same trick elsewhere in my code (same kind of problem accessing palette() in the constructor). It even works with the timer set to 0 (zero) milliseconds, which makes a difference in my other occurrence, as anything above zero cause some flicker, but with zero it does not.

    I'll give it as try on the mailing list.

    Thanks again !

    MaX.



  • Got a (probably) better solution from somebody on the interest mailing list. I quote it here:

    [quote]
    In our Qt 4.8.6 app we use the Polish event to apply changes after the styles are set in our base window class:
    @
    bool BaseForm::event(QEvent *event)
    {
    bool result = QDialog::event( event );
    if (event->type() == QEvent::Polish)
    {
    addImagesToButtons( this );
    }
    return result;
    }
    @
    Look at the docs for ensurePolished to see when this event occurs.[/quote]

    This allows to handle properly the stylesheet change at object creation, as the QEvent::Polish event is called after objects creation (but before show).

    MaX.


  • Lifetime Qt Champion

    With 0 it's done at the next iteration in the even loop.

    The polish event ! I knew a was forgetting another solution. This ones very good indeed.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.