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

QSpinBox cannot set its QLineEdit background colour under Linux



  • I am Ubuntu 19.04 with the Qt 5.12.2 which was released with it. [EDIT: It now appears this has nothing to do with Linux or Qt version, nor even just background colour, see my later post + sample code. ] You cannot set the background colour of the QLineEdit in a QSpinBox:

    QSpinBox spin = new QSpinBox(whatever);
    QLineEdit lineEdit = spin->findChild<QLineEdit*>();
    Q_ASSERT(lineEdit);
    lineEdit->setStyleSheet("background-color: blue;");
    

    has no effect. Note the following:

    • I have tried QPalette::setColor(QPalette::Base, ...) (plus QLineEdit::setAutoFillBackground(true)) instead of stylesheet, same no effect.

    • You can set anything else stylesheet/palette, like the text colour, on the line edit, it's only the background color does not work.

    • Either method does work on a standalone QLineEdit, just not in the QSpinBox one, which only seems to have plain QLineEdit attached.

    • Background color does work if I set it on the QSpinBox itself (and inherits down to the line edit) rather than on the QLineEdit only, but that's not what i want.

    Most importantly, a kind soul here (thanks @mrjj) has confirmed it does work, but can only test with Qt 5.14.2 under both Windows & Linux Mint.

    There is a 10-year-old bug https://bugreports.qt.io/browse/QTBUG-8593, never addressed, reporting similar for Linux only; I do not know whether that has any relevance to now.

    I am wondering whether another kind soul can test under, say, Ubuntu with Qt > 5.12.2 and tell me whether it does work there? Many thanks. At a future point I will move to Ubuntu 20.04 + whatever later Qt comes with it.


  • Lifetime Qt Champion

    Hi,

    Can you provide a minimal compilable example ?
    That will allow people to just grab your code, build and test in the same conditions as you.



  • @SGaist
    In writing a standalone example, I have discovered what is causing this behaviour. But it's so strange I have no idea why!

    This presumably means it has nothing to do with Linux or Qt version, and the issue it not limited to background colour. Which means I could raise a slightly different topic title, but we are here, so...

    Here is a dead-simple standalone illustrating issue. Run by me under Ubuntu 19.04 with accompanying Qt 5.12.2, but now presumed to happen on any platform/Qt version?

    #include <QApplication>
    #include <QLineEdit>
    #include <QSpinBox>
    #include <QVBoxLayout>
    #include <QWidget>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QWidget w;
        QVBoxLayout layout;
        w.setGeometry(100, 100, 200, 200);
        w.setLayout(&layout);
    
        // commenting in/out next line makes `QLineEdit` in `QSpinBox` ignore/respect its own stylesheet colors
        // yet standalone `QLineEdit` respects its own stylesheet colors in either case
        w.setStyleSheet("background-color: yellow; color: green;");
    
        QLineEdit lineEdit;
        layout.addWidget(&lineEdit);
        lineEdit.setText("99999");
        // standalone `QLineEdit` always respects stylesheet, and behaves correctly
        lineEdit.setStyleSheet("background-color: cyan; color: red;");
    
        QSpinBox spinBox;
        layout.addWidget(&spinBox);
        QLineEdit *spinLineEdit = spinBox.findChild<QLineEdit *>();
        spinBox.setMaximum(99999);
        spinBox.setValue(99999);
        // `QLineEdit` in `QSpinBox` respects stylesheet, and behaves correctly, *ONLY IF ANCESTOR `QWidget` DOES NOT SPECIFY COLORS*
        // if parent `QWidget` does specify colors, next line has no effect on it!
        spinLineEdit->setStyleSheet("background-color: cyan; color: red;");
    
        w.show();
        return a.exec();
    }
    

    Comments describe behaviour.

    • With parent/ancestor w.setStyleSheet("background-color: yellow; color: green;"); line enabled, QSpinBox alone goes wrong while comparison QLineEdit works correctly, screenshot:

    Screenshot from 2020-04-17 09-01-59.png

    • With parent/ancestor // w.setStyleSheet("background-color: yellow; color: green;"); line commented out, both behave correctly, screenshot:

    Screenshot from 2020-04-17 09-02-33.png

    Can anyone explain this, please?

    [And btw if I set the stylesheet on the QSpinBox itself rather than its embedded line edit then that does work regardless of ancestor widget colours, but that is not what I want. It is an issue on the QlineEdit embedded in a QSpinBox only.]



  • I didn't get any takers here (other than @mrjj) :( Reported as Qt bug at https://bugreports.qt.io/browse/QTBUG-83961.



  • @JonB I had a similar need in terms of wanting the background of my spinbox delegate to reflect the background color of the current row when alternating row colors are used by the view. The way I solved it is below. Not using style sheets here, but rather I grab the palette of the app (which is themed elsewhere) and then adjust the palette of the spinbox's QLineEdit. Works.

    QWidget *SpinboxDelegate::createEditor(
            QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
    {
        Q_UNUSED(option);
        QSpinBox *editor = new QSpinBox(parent);
        editor->setAlignment(Qt::AlignRight);
        editor->setAutoFillBackground(true);
        // hack to grab protected QLineEdit from QSpinBox parent
        auto line_editors = editor->findChildren<QLineEdit*>();
        QLineEdit *line_editor = !line_editors.empty() ? line_editors.front() : nullptr;
        if (line_editor && index.isValid()) {
            // line editor background color set to current row background color
            const QPalette app_palette = qApp->palette();
            QPalette palette = line_editor->palette();
            palette.setColor(QPalette::Base, index.row() % 2 ?
                                 app_palette.color(QPalette::AlternateBase) :
                                 app_palette.color(QPalette::Base));
            line_editor->setPalette(palette);
        }
        editor->setFrame(false);
        editor->setMinimum(-100000);
        editor->setMaximum(100000);
        return editor;
    }
    


  • I'm amending my code snippet above so that it works properly with scroll areas and tree views. The row of the QModelIndex won't tell you the row needed for accurate alternate coloring. i.e. you need the row relative to the top of the scroll area and, for item views, the data above you might also be in various states of expand/collapse.

    In digging through the Qt code, I notice that the QStyleOptionViewItem delivered to delegate's createEditor does not carry the feature QStyleOptionViewItem::Alternate. That bit is added by the tree view in drawRow. This is too bad since that bit could directly tell us whether or not we're on an alternate row. That bit is sent into the delegate's paint function however, so you could play with it there.

    QWidget *SpinboxDelegate::createEditor(
            QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
    {
        QSpinBox *editor = new QSpinBox(parent);
        editor->setAlignment(Qt::AlignRight);
        editor->setAutoFillBackground(true);
        const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(option.widget);
        if (view) {
            // determine row that the index is sitting on relative to top of scroll area
            const QRect rect = view->visualRect(index);
            const int pos = view->verticalScrollBar()->value();
            const int row = pos + (rect.top() / rect.height());
            // hack to grab protected QLineEdit from QSpinBox parent
            auto line_editors = editor->findChildren<QLineEdit*>();
            QLineEdit *line_editor = !line_editors.empty() ? line_editors.front() : nullptr;
            if (line_editor && index.isValid()) {
                // line editor background color set to current row background color
                const QPalette app_palette = qApp->palette();
                QPalette palette = line_editor->palette();
                palette.setColor(QPalette::Base, row % 2 ?
                                     app_palette.color(QPalette::AlternateBase) :
                                     app_palette.color(QPalette::Base));
                line_editor->setPalette(palette);
            }
        }
        editor->setFrame(false);
        editor->setMinimum(-100000);
        editor->setMaximum(100000);
        return editor;
    

  • Lifetime Qt Champion

    Hi,

    I currently wonder whether it's something that should be available there. Did you check the bug report system to see if there's something related ? If not, you could open a feature request.



  • @SGaist In the context of delegates, the editor is created by the QAbstractItemView on this line of code and if you trace it back, you can see that openEditor is not adding the option features that drawRow adds in the QTreeView derivation for the paint calls. Not sure it would be easy to add that style feature on that abstract class. Perhaps.



  • @SGaist
    Are you addressing this to @Phil-K or to myself?

    I wrote earlier that I reported this bug as https://bugreports.qt.io/browse/QTBUG-83961 9 months ago, but haven't had any takers :) [Other than 2 non-expert replies, to which I responded as both were wrong!]

    I can't say for @Phil-K's response now. But, at least in my non-edit case, one should not need to write some set of code. There is just something fundamentally wrong in Qt's internal code for handling the QLineEdit part of a QSpinBox, at least so far as my coloring via stylesheet is concerned.


  • Lifetime Qt Champion

    At @Phil-K but somehow I managed to miss the link to the existing report... vertical scrolling is sometimes treacherous.

    The current answers are indeed inadequate.



  • I necro'd the thread and went off on a tangent. Scrolling is not relevant to the original problem. The style painter is painting that QSpinBox with a call from paintEvent to drawComplexControl so you would need to trace the drawing code through the style machinery to see what's happening. As a workaround you can set the stylesheet for the QSpinBox instead of the editor, but you'll have to adjust the buttons using the QSpinBox::up-button/::down-button subcontrol selectors. Too bad there isn't a subcontrol css selector for the QLineEdit itself. That would help. Also, I note that the css selectors for children (#qt_spinbox_lineedit or QSpinBox > QLineEdit) indeed don't seem to work in this context, so the drawing code is probably using the spinbox style settings and ignoring the rest.



  • @JonB Here's a fast workaround that you may like. Give the parent widget an object name and set its stylesheet using the #id selector. That will prevent the problematic propagation to the QLineEdit of the QSpinBox. Cheers.

    #include <QApplication>
    #include <QLineEdit>
    #include <QSpinBox>
    #include <QVBoxLayout>
    #include <QWidget>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QWidget w;
        QVBoxLayout layout;
        w.setGeometry(100, 100, 200, 200);
        w.setLayout(&layout);
        // Use #id selector to prevent propagation
        w.setObjectName("my_widget");
        w.setStyleSheet("#my_widget {background-color: yellow; color: green;}");
    
        QLineEdit *lineEdit = new QLineEdit;
        layout.addWidget(lineEdit);
        lineEdit->setText("50");
        lineEdit->setStyleSheet("background-color: cyan; color: red;");
    
        QSpinBox *spinBox = new QSpinBox;
        layout.addWidget(spinBox);
        QLineEdit *spinLineEdit = spinBox->findChild<QLineEdit *>();
        spinBox->setMaximum(100);
        spinBox->setMinimum(-100);
        spinBox->setValue(50);
        spinLineEdit->setStyleSheet("background-color: blue; color: red;");
    
        w.show();
        return a.exec();
    }
    


  • @Phil-K
    Hi Phil. Yes, I realize what you are doing here, but unfortunately this does not do the job correctly enough for my requirements.

    Firstly, the result of yours is:
    Screenshot from 2021-01-26 09-55-56.png

    Compare that to the correct one I show earlier above. Not surprisingly, while the line edit itself is now correct colors, the spinbox "furniture" around it has now lost the desired background yellow of the whole form and is shown in the default "grey".

    Secondly, I had to cut mine down to a minimal example. In practice I have a top-level background-colored widget, whose color I want inherited everywhere except when overridden, then I have a potentially-unknown hierarchy of further sub-widgets, and at the bottom I have a QSpinBox. Colored-widget -> widget -> widget -> ... -> spinbox. And I have loads of these QSpinBoxes. Setting the color on the top-level widget by id will limit it to that widget only, and there is no chance of picking out all the direct parent widgets of the child spinboxes for application of some distant, top-level color which may have been specified.

    The problem, it seems to me, must be addressed on the QSpinBox itself, not higher up the hierarchy. And the issue is that somewhere in the Qt code there is some "unusal" handling of the QSpinBox::lineEdit() such that you cannot get it to respect its own stylesheet rule iff some ancestor does specify colors. Tackling this somehow at a "top-level" widget cannot be the solution. I refer you again to my example which shows that a standalone QLineEdit does behave correctly in this situation, while the QLineEdit internal to a QSpinBox does not in the same situation. This is a QSpinBox-alone issue. Which is what I need resolved or at least explained!



  • @JonB Well my suggestion is to read the Qt code and find the exact areas where the problem exists and propose a patch on your bug report. I had an old boss many years ago who said "Don't come to me with a problem. Come to me with a problem, three solutions and a recommendation." Good luck.



  • @Phil-K
    Yes the solution to any question Qt can be read the code and find out for yourself. Difficult when I have never compiled Qt, can't step through the code, and am looking to try to find out why some stylesheet rule is or is not overridden by some inheritance elsewhere, which I'm pretty sure will be convoluted/a minefield to work out. Which is why I raised it as a bug at bugreports, in the hope that someone who maintains that code might post an answer. But not. Thank you for your interest.


Log in to reply