stylesheet magic: setfont + polish = wrong background



  • The code bellow is complete magic for me,
    anybody can explain magic behind the scene?
    I haveMyW with white backgroud and expect that it's child QLabel,
    also will have white background. But this not happens.
    I found three way to fix this, but I completly do not understand why this fix the issue:

    1. Do not call lbl->ensurePolish(),
    2. Set full style with one call in MyW
    3. Remove code that setFont in MyW

    But how font involve on background, or why the place of call of ensurePolish is important (if I don't call it in MyW::MyW it called later on show) , these things are completly mystery for me.

    #include <QApplication>
    #include <QLabel>
    #include <QVBoxLayout>
    #include <QtDebug>
    
    class MyW : public QWidget {
    public:
      MyW(QWidget *parent) : QWidget(parent) {
        setStyleSheet("border:none;");
    
        auto font = QApplication::font();
        if (font.pixelSize() != -1) {
          font.setPixelSize(static_cast<int>(font.pixelSize() * 1.25 + 0.5));
          setFont(font);
        }
    
        setStyleSheet(styleSheet() + "background:#ffffff;color:#000000;");
    
        auto lbl = new QLabel{"AAAA", this};
        auto lay = new QVBoxLayout;
        setLayout(lay);
        lay->addWidget(lbl);
        lbl->ensurePolished();
      }
    };
    
    class Page : public QWidget {
    public:
      Page(QWidget *parent) : QWidget{parent} {
        setStyleSheet("background:#f0f4f7;");
        auto lay = new QVBoxLayout;
        setLayout(lay);
        auto item = new MyW{this};
        lay->addWidget(item);
      }
    };
    
    int main(int argc, char *argv[]) {
      QApplication a(argc, argv);
    
      QFont font("Arial");
      font.setFixedPitch(false);
      font.setBold(true);
      font.setPixelSize(30);
      qInfo() << "font pixel size " << font.pixelSize();
      QApplication::setFont(font);
    
      Page p{nullptr};
      p.resize(400, 800);
      p.show();
      return a.exec();
    }
    

  • Qt Champions 2017

    It is because of ensurePolish your magic is happening. It ensures that all label is applied with Styles again.If you move the statement above line#22(above VLayout), label is polished then & there. After this label becomes child of MyW once you add into layout. So now styles of parent applied. It all depends when the label becomes the child of MyW. Hope magic is clear now.



  • @dheerendra

    After this label becomes child of MyW once you add into layout
    Hope magic is clear now.

    Hm, nothing is clear. label become child after this:
    auto lbl = new QLabel{"AAAA", this};

    you see I pass this as argument for QLabel::QLabel(const QString &text, QWidget *parent, Qt::WindowFlags = ...), why you think that adding to layout change parent or something?

    Also this is not explain why setFont change background, and why

    setStyleSheet("border:none;");
    setStyleSheet(styleSheet() + "background:#ffffff;color:#000000;");
    

    and setStyleSheet("border:none;background:#ffffff;color:#000000;"); gives different results


  • Qt Champions 2017

    I did not notice 'this' is passed while constructing. So parent will not change. If you put polished at the end, it will apply this. Can you look ensurePolished method(). It gives you the idea how it works. Last applied style are taking for label rather from parent. If you move up the polished, last applied styles are from parent.



  • Also from the doc:

    If you subclass from QWidget, you need to provide a paintEvent for your custom QWidget as below:
    
     void CustomWidget::paintEvent(QPaintEvent *)
     {
         QStyleOption opt;
         opt.init(this);
         QPainter p(this);
         style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
     }
    
    The above code is a no-operation if there is no stylesheet set.
    
    Warning: Make sure you define the Q_OBJECT macro for your custom widget.
    

    Source: http://doc.qt.io/qt-5/stylesheet.html



  • @NicolasS

    If you subclass from QWidget

    The problem completly reproducible with Q_OBJECT and paintEvent override for MyW and Page, so I remove this code to make example smaller and fit it to one c++ file.


  • Lifetime Qt Champion

    Hi
    Just as a note
    void CustomWidget::paintEvent(QPaintEvent *)
    {
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    }

    • The above code is a no-operation if there is no stylesheet set.

    Its not a no-operation. the QStyle::PE_Widget style is just very basic and have no background color or frame.
    most other QStyle::PE_xxxx are a bit more interesting :)

    Regarding your issue.
    You are not using any selectors in the your style sheets and since
    sheets are cascading ( affects children too) ,
    it often result in a mess assigning style sheet through the whole program.

    so what color do the label get ?
    I would guess on #f0f4f7 ?



  • @mrjj

    You are not using any selectors in the your style

    Yep, this is intented, I have only two background colors in whole program,
    and I want that all widgets have them. And I expect

    http://doc.qt.io/qt-5/stylesheet-syntax.html#cascading

    When conflicts arise, the widget's own style sheet is always preferred to any inherited style sheet, irrespective of the specificity of the conflicting rules. Likewise, the parent widget's style sheet is preferred to the grandparent's, etc.

    And sometimes and I get this behaviour for example if I remove lbl->ensurePolish()

    so what color do the label get ?
    I would guess on #f0f4f7 ?

    Yes #f0f4f7 while I expect #ffffff and can get it by three ways:

    • Do not call lbl->ensurePolish() in MyW::MyW
    • Set full style with one call of setStyleSheet instead of two in MyW::MyW
    • Remove code that setFont in MyW::MyW

  • Lifetime Qt Champion

    @DaveMilter
    Ok, Do note that QApplication also have setStyleSheet allowing to affect all widgets in app.
    Could you try
    ui->label->parent()->objectName();
    and see whom its daddy ? ( after inserted into layout)



  • @mrjj

    Ok, Do note that QApplication also have setStyleSheet allowing to affect all widgets in app.
    Could you try
    ui->label->parent()->objectName();
    and see whom its daddy ? ( after inserted into layout)

    Why you all are so suspicious about layout,
    I remove in code bellow all stuff related to layout.
    And print "family tree" of QLabel:

    font pixel size  30
    qapp style:  ""
    Page:  QWidget(0x7ffee3a13190)
    MyW:  QWidget(0x559fb05749d0)
    lbl family tree
    lbl parent  0 th gen  QLabel(0x7effb0005560)
    lbl parent  1 th gen  QWidget(0x559fb05749d0)
    lbl parent  2 th gen  QWidget(0x7ffee3a13190)
    
    #include <QApplication>
    #include <QLabel>
    #include <QtDebug>
    
    class MyW : public QWidget {
    public:
      MyW(QWidget *parent) : QWidget(parent) {
        qDebug() << "MyW: " << static_cast<QObject *>(this);
        setStyleSheet("border:none;");
    
        auto font = QApplication::font();
        if (font.pixelSize() != -1) {
          font.setPixelSize(static_cast<int>(font.pixelSize() * 1.25 + 0.5));
          setFont(font);
        }
    
        setStyleSheet(styleSheet() + "background:#ffffff;color:#000000;");
    
        auto lbl = new QLabel{"AAAA", this};
        {
          qDebug("lbl family tree");
          QObject *o = lbl;
          int i = 0;
          while (o != nullptr) {
            qDebug() << "lbl parent " << i << "th gen " << o;
            ++i;
            o = o->parent();
          }
        }
        lbl->ensurePolished();
      }
    };
    
    class Page : public QWidget {
    public:
      Page(QWidget *parent) : QWidget{parent} {
        qDebug() << "Page: " << static_cast<QObject *>(this);
        setStyleSheet("background:#f0f4f7;");
        auto item = new MyW{this};
      }
    };
    
    int main(int argc, char *argv[]) {
      QApplication a(argc, argv);
    
      QFont font("Arial");
      font.setFixedPitch(false);
      font.setBold(true);
      font.setPixelSize(30);
      qInfo() << "font pixel size " << font.pixelSize();
      QApplication::setFont(font);
      qDebug() << "qapp style: " << a.styleSheet();
      Page p{nullptr};
      p.resize(400, 800);
      p.show();
      return a.exec();
    }
    ```

  • Lifetime Qt Champion

    Hi
    I think the multiple setting of stylesheet in ctor confuses it.
    if you ensurePolished() for myW it seems to work as expected.
    (using Parent style sheet)

    class MyW : public QWidget
    {
    public:
        MyW(QWidget *parent) : QWidget(parent)
        {
            qDebug() << "MyW: " << static_cast<QObject *>(this);
            setStyleSheet("border:none;");
            ensurePolished(); // <<<<<<<<<<<<<<<<<<<<<<<<<
    
            auto font = QApplication::font();
            if (font.pixelSize() != -1) {
                font.setPixelSize(static_cast<int>(font.pixelSize() * 1.25 + 0.5));
                setFont(font);
            }
    
            setStyleSheet(styleSheet() + "background:#ffffff;color:#000000;");
    
            auto lbl = new QLabel{"AAAA", this};        
            lbl->ensurePolished(); // not needed
        }
    };
    
    


  • @mrjj

    I think the multiple setting of stylesheet in ctor confuses it.
    if you ensurePolished() for myW it seems to work as expected.

    yeah, but this is not explain why if I remove setFont from MyW::MyW
    multiple setting of stylesheet works completly fine,
    without extra call of MyW::ensurePolished,

    also if I change MyW::MyW to this all works fine (white background),
    so setFont somehow become interference

      MyW(QWidget *parent) : QWidget(parent) {
        qDebug() << "MyW: " << static_cast<QObject *>(this);
        setStyleSheet("border:none;");
        setStyleSheet(styleSheet() + "background:#ffffff;color:#000000;");
        auto font = QApplication::font();
        if (font.pixelSize() != -1) {
          font.setPixelSize(static_cast<int>(font.pixelSize() * 1.25 + 0.5));
          setFont(font);
        }
        auto lbl = new QLabel{"AAAA", this};
        lbl->ensurePolished();
    }
    



Log in to reply