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 aQSpinBox
: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, ...)
(plusQLineEdit::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 theQSpinBox
one, which only seems to have plainQLineEdit
attached. -
Background color does work if I set it on the
QSpinBox
itself (and inherits down to the line edit) rather than on theQLineEdit
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.
-
-
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 comparisonQLineEdit
works correctly, screenshot:
- With parent/ancestor
// w.setStyleSheet("background-color: yellow; color: green;");
line commented out, both behave correctly, screenshot:
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 theQlineEdit
embedded in aQSpinBox
only.] - With parent/ancestor
-
@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'screateEditor
does not carry the featureQStyleOptionViewItem::Alternate
. That bit is added by the tree view indrawRow
. 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'spaint
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;
-
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 thatopenEditor
is not adding the option features thatdrawRow
adds in theQTreeView
derivation for thepaint
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 aQSpinBox
, at least so far as my coloring via stylesheet is concerned. -
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 frompaintEvent
todrawComplexControl
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 theQSpinBox
instead of the editor, but you'll have to adjust the buttons using theQSpinBox::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:
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 theseQSpinBoxes
. 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 theQSpinBox::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 standaloneQLineEdit
does behave correctly in this situation, while theQLineEdit
internal to aQSpinBox
does not in the same situation. This is aQSpinBox
-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.