How to change (remove) selection/active color of QListWidget - qt

In my QListWidget, there are some items that have non-default background color, I set them like so inside the custom QListWidget class:
item->setBackgroundColor(qcolor); // item is of type QListWidgetItem*
Those non-default colors that I set are distorted by the QListWidget's selection color. See an example:
Items three and four are supposed to be the same color, but they are not since the item four is selected, and thus the result color is a summation of original color and QListWidget's selection (active item?) color.
My question is how to edit or remove that selection color?
I tried inside my QListWidget (in special slot when I want to change the item's background color):
QPalette pal = this->palette();
pal.setColor(QPalette::Highlight, QColor(255,255,255,0));
this->setPalette(pal); // updated
But it did not produce any effect. what am I doing wrong? Is it a correct variable to set? Do I set it up inside QListWidget or inside its delegate?
Update: I tried using stylesheets as pointed by comment/answer, however, it will not be possible to use them for my application, because the items in my rows have 3 states (so I would need to use three colors). E.g., 3 states that correspond to three colors: pink for active, green for inactive and gray for the rest. When using stylesheets, I cannot set the custom property (let's say QListWidget::item[Inactive="true"]) to a single QListWidgetItem, but for the full QListWidget, and therefore it colors all the rows the same color.
Stylesheets were tried for similar problem here, and didn't work, therefore I make conclusion using stylesheets is not the way to go.
The background change method that I used originally works fine for my purpose, but I cannot figure out how to get rid of the default selection color (transparent light blue) which adds to the background color and produces the mixed color.

I think you'd be better served using the style sheets to do this. Here's an example
QListWidget::item
{
background: rgb(255,255,255);
}
QListWidget::item:selected
{
background: rgb(128,128,255);
}
::item indicates the individual items within the QListWidget, while :selected indicates the QListWidgetItems which are currently selected.
To then get the custom background on specific widgets, you could use custom style sheet properties. In your code, call something like this on the widget you want to apply a custom style on:
myList->setProperty( "Custom", "true" );
// Updates the style.
style->unpolish( myList );
style->polish( myList );
Then in your style sheet, define the style for your custom property like so.
QListWidget::item[Custom="true"]
{
background: rgb(128,255,128);
}

I found a suitable solution by using a delegate. So, there is no need to use QPalette; and for my problem the stylesheets will not work. This solution will also work when different rows (QListWidget or QTreeWidget) are needed to be colored in different colors, depending on some state.
The background color is set as described on the question:
item->setBackgroundColor(qcolor); // change color slot inside QListWidget class
In order to define rules how the widget is painted, I re-define the delegate:
class Delegate : public QStyledItemDelegate {};
Then I re-define Delegate's method paint(). There I define how to draw each row of my widget. More specifically, I only call custom drawing when the mouse hovering over an item, or that item is in selected state (those are the conditions when the row is selected by the light blue color which I want to avoid). The code looks like this:
void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if((option.state & QStyle::State_Selected) || (option.state & QStyle::State_MouseOver))
{
// get the color to paint with
QVariant var = index.model()->data(index, Qt::BackgroundRole);
// draw the row and its content
painter->fillRect(option.rect, var.value<QColor>());
painter->drawText(option.rect, index.model()->data(index, Qt::DisplayRole).toString());
}
else
QStyledItemDelegate::paint(painter, option, index);
// ...
}
Of course, do not forget to associate the QListWidget with Delegate:
listWidget->setItemDelegate(new Delegate());

Related

How to set stylesheet for the current item in QTableView

When QTableView edit control is visible for the current item the shylesheet of the edit takes place. When there is no active edit control in the QTableView the current item is styled using the QTableView { selection-background-color: } How to set different style only for the current item?
Qt style sheets support sub-controls and pseudo states, you can use it to improve your customization. see Qt6 docs
In this case you can use the ::item sub-control and the :focus pseudo state (the "current" pseudo state doesn't exist, but the :focus does the same).
This is an example that you can use:
QTableView::item:focus
{
selection-background-color: yellow;
}
See also Qt6 documentation of customizing a qtableview
1. As it IGHOR said you can use data() method in your model and provide a color when role is Qt::BackgroundColor. But there is a stumble here because you don't know whether index is current or not. You'll ought to set a current index in the model when it changes and then make a check like this:
if (index == m_currentIndex and role==Qt::BackgroundRole) return Qt::black;
Actually it's not the best idea to tell the model about currentIndex according to Model/View pattern, because you can have two views for one model.
2. Descendants of QAbstractItemView has method setItemDelegate. A delegate is used to draw a cell.
All you need is to inherit from QStyledItemDelegate, pass a pointer to the view to the delegate and override method initStyleOption.
Then do something like this:
void MyStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const
{
QStyledItemDelegate::initStyleOption(option, index);
QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option);
if (index == view()->currentIndex())
{
v4->backgroundBrush = QBrush(Qt::grey);
}
}
3. If you really need to use css (for example you have themes) you can do it this way:
Add something like this in your css file:
QTableView
{
qproperty-currentItemBackground: #cccccc;
}
Modify initStyleOption from the previous example to use the property:
v4->backgroundBrush = view()->property("currentItemBackground").toColor();
With this approach you can set a specific style via css for a column, a row, a single cell or a group of cells.
You need to create a new delegate, that renders itself based on the data model (custom role, for example). You need to base its style on a special control created for the purpose (that can be changed via stylesheet) . I'll post some code when I find time.
One can use variadic templates, and crtp (Coplien) to good effect to layer one's delegates

Qt Style Sheet - Different styles for same type widgets

I need to assign different styles for the same typed widget instances. Specially for QActions. Following style sheet sets QActions' background images, actualy tool buttons'.
QToolButton
{
background-image: url(bg.png);
}
But I need to assign different backgrounds for tool buttons like this.
QToolButton::actClose
{
background-image: url(close.png);
}
QToolButton::actOpen
{
background-image: url(open.png);
}
Is there any easy way like this or is it not possible?
You can set object name for instances of QToolButton
QToolButton *button = new QToolButton;
button->setObjectName("firstButton");
button = new QToolButton;
button->setObjectName("secondButton");
and next use them in Style Sheet
QToolButton#firstButton
{
background-color: gray
}
QToolButton#secondButton
{
background-color: red
}
It helps if you can post c++ code that creates the QToolButton and associates with QActions.
Cite from QToolBar's reference "Toolbar buttons are added by adding actions, using addAction() or insertAction(). " and "QWidget * QToolBar widgetForAction (QAction *action )const
Returns the widget associated with the specified action."
So if you are creating QToolBar and call QToolBar::addAction(QAction*) to fill it, it is possible to get pointer to the tool buttons. Try QToolBar::widgetForAction(), and call setObjectName("") and Blueman's method can be applied.
while applying Style Sheet to widgets, "#" is used after class name to specify object name, ":" is used after className of objectName indicating the object's status such like "enabled", "::" is used to specify the subcontrols such as "ListView::Item", unfortunately QAction is neither of QToolBar.

How to set stylesheet for a single item in the Qlistview?

I have a QListView containing QStandardItems . How to set stylesheet for a single item in the Qlistview based on the QModelIndex acquired?
If you use QListWidget instead of QListView, you can call QListWidget::setItemWidget() and you can customize how individual items look by applying a stylesheet to the items that you add. You need to make sure that your item widget class inherits from QWidget and you can apply styles to the widget using QSS like so in your constructor:
setStyleSheet("WidgetItem:pressed { background-color: #444444; }");
Here's a reference to QSS: http://qt-project.org/doc/qt-4.8/stylesheet-examples.html
I still see the Widget-classes documented in Qt Documentation for Qt 5.7 -
Qt Widgets Widgets Classes.
Reference: http://doc.qt.io/qt-5/widget-classes.html
There seems to be no way to set a stylesheet in the Model-View conecpt. But what exist is the FontRole. If you want to make an entry bold or italic or change the size then the FontRole can do that. If you want to change the color then you have to find some other solution.
Here is an example to make certain entries bold in python:
def data(self, index, role=QtCore.Qt.DisplayRole):
# use parent class to get unaltered result
res = super().data(index, role=role)
if role == QtCore.Qt.FontRole:
# modify FontRole
if index.row() = 3:
# row number 3 should be bold
if res is None:
# parent class didn't return a QFont so make one
res = QtGui.QFont()
# set bold attribute in font
res.setBold(True)
return res
The above data() method sets the 4th row bold (rows are counted from 0). I leave translating to C++ to the reader.

Resetting Qt Style Sheet

I've managed to style my QLineEdit to something like this:
alt text http://www.kimag.es/share/54278758.png
void Utilities::setFormErrorStyle(QLineEdit *lineEdit)
{
lineEdit->setStyleSheet(
"background-color: #FF8A8A;"
"background-image: url(:/resources/warning.png);"
"background-position: right center;"
"background-repeat: no-repeat;"
"");
}
I called the function using
Utilities *util = new Utilities;
util->setFormErrorStyle(lineNoStaf);
The flow should be something like this:
User open form
User fill data
User submit data
Got error
Use setFormErrorStyle()
User edit the text in the QLineEdit and the style disappear
This function should be reusable over and over again, but how can I connect QLineEdit signal such as textChanged() to a function in other class that will reset the Style Sheet and then disconnect the signal so that it won't be running continuously every time the text changed ?
Qt also allows dynamic properties in its stylesheet, that means you don't need to code your own class for every widget type in your form.
From http://qt-project.org/doc/qt-4.8/stylesheet-examples.html
Customizing Using Dynamic Properties
There are many situations where we need to present a form that has mandatory fields. To indicate to the user that the field is mandatory, one effective (albeit esthetically dubious) solution is to use yellow as the background color for those fields. It turns out this is very easy to implement using Qt Style Sheets. First, we would use the following application-wide style sheet:
*[mandatoryField="true"] { background-color: yellow }
This means that every widget whose mandatoryField Qt property is set to true would have a yellow background.
Then, for each mandatory field widget, we would simply create a mandatoryField property on the fly and set it to true. For example:
QLineEdit *nameEdit = new QLineEdit(this);
nameEdit->setProperty("mandatoryField", true);
QLineEdit *emailEdit = new QLineEdit(this);
emailEdit->setProperty("mandatoryField", true);
QSpinBox *ageSpinBox = new QSpinBox(this);
ageSpinBox->setProperty("mandatoryField", true);
Works also in Qt 4.3!
Allright, this is not compile but should work in principle, you should be able to change the look by calling editWidget->setProperty('isError',true) or editWidget->setError(false)
class ErrorTextEdit : QLineEdit
{
Q_OBJECT
QPROPERTY(bool isError, READ isError, WRITE setError);
public:
ErrorTextEdit(QWidget* parent) : QLineEdit(parent), m_isError(false)
{
m_styleSheet = "" // see below
setStyleSheet(m_styleSheet);
}
void setError(bool val)
{
if (val != m_isError)
{
m_isError = val;
setStyleSheet(m_styleSheet);
}
}
bool isError() {return m_isError;}
private:
QString m_styleSheet;
bool m_isError;
}
for the stylesheet
ErrorTextEdit[isError="false"]
{
optional ...
Style for textedit that is NOT an error
}
ErrorTextEdit[isError="true"]
{
background-color: #FF8A8A;
background-image: url(:/resources/warning.png);
background-position: right center;
background-repeat: no-repeat;
}
the term
[<property>="<value>"]
restricts the application of the stylesheet to instances of the class whose <property> has the appropriate <value> the only caveat is that the style is not changed when the property changes its' value, so the stylesheet has to be reapplied for the look of the widget to actually change, see Stylesheet Documentation -> Property Selector
This construction moves the stylesheet into the widget that uses it and makes switch internal to the widget, the widget changes in accordance to its state.
In general you have a couple of ways to handle invalid inputs in your form
a) observe every change and update the style appropriately, you should be able to use QValidator for that too, but that is a separate topic, using QValidator you will probably be able to completely internalize the state of a single QTextEdit and not have to deal with its validity from the outside
b) Do it in the submit loop that you have described above, whenever the user clicks on submit change the state of the correct and incorrect fields
it all depends the structure of your app and the view
See, the other idea is you need to override the paint evet of line edit and then set the background image and color.
here the implimentation is presetn here button, follow up the same to your line edit

In a QTableWidget, changing the text color of the selected row

I'm using a QTableWidget to display several rows. Some of these rows should reflect an error and their text color is changed :
Rows reflecting that there is no error are displayed with a default color (black text on white background on my computer).
Rows reflecting that there is an error are displayed with a red text color (which is red text on white background on my computer).
This is all fine as long as there is no selection. As soon as a row is selected, no matter of the unselected text color, the text color becomes always white (on my computer) over a blue background.
This is something I would like to change to get the following :
When a row is selected, if the row is reflecting there is no error, I would like it to be displayed with white text on blue background (default behavior).
If the row is reflecting an error and is selected, I would like it to be displayed with red text on blue background.
So far I have only been able to change the selection color for the whole QTableWidget, which is not what I want !
Answering myself, here is what I ended up doing : a delegate.
This delegate will check the foreground color role of the item. If this foreground color is not the default WindowText color of the palette, that means a specific color is set and this specific color is used for the highlighted text color.
I'm not sure if this is very robust, but at least it is working fine on Windows.
class MyItemDelegate: public QItemDelegate
{
public:
MyItemDelegate(QObject* pParent = 0) : QItemDelegate(pParent)
{
}
void paint(QPainter* pPainter, const QStyleOptionViewItem& rOption, const QModelIndex& rIndex) const
{
QStyleOptionViewItem ViewOption(rOption);
QColor ItemForegroundColor = rIndex.data(Qt::ForegroundRole).value<QColor>();
if (ItemForegroundColor.isValid())
{
if (ItemForegroundColor != rOption.palette.color(QPalette::WindowText))
{
ViewOption.palette.setColor(QPalette::HighlightedText, ItemForegroundColor);
}
}
QItemDelegate::paint(pPainter, ViewOption, rIndex);
}
};
Here is how to use it :
QTableWidget* pTable = new QTableWidget(...);
pTable->setItemDelegate(new MyItemDelegate(this));
What you'll want to do is connect the selectionChanged() signal emitted by the QTableWidget's QItemSelectionModel to a slot, say OnTableSelectionChanged(). In your slot, you could then use QStyleSheets to set the selection colours as follows:
if (noError)
{
pTable->setStyleSheet("QTableView {selection-background-color: #000000; selection-color: #FFFFFF;}");
}
else
{
pTable->setStyleSheet("QTableView {selection-background-color: #FF0000; selection-color: #0000FF;}");
}
It looks ok, but you might want to look at the documentation of QStyleOption it can tell you wether the item drawn is selected or not, you don't have to look at the draw color to do that. I would probably give the model class a user role that returns whether the data is valid or not and then make the color decision based on that. I.e. rIndex.data(ValidRole) would return wether the data at this index is valid or not.
I don't know if you tried overriding data for the BackgroundRole and returning a custom color, Qt might do the right thing if you change the color there
You could, of course, inherit from the table widget and override the paint event, but I don't think that is what you want to do.
Instead, should use the QAbstractItemDelegate functionality. You could either create one to always be used for error rows, and set the error rows to use that delegate, or make a general one that knows how to draw both types of rows. The second method is what I would recommend. Then, your delegate draws the rows appropriately, even for the selected row.
You could use e.g. a proxy model for this where you return a different color if you have an error for the specific modelindex;
QVariant MySortFilterProxyModel::data(const QModelIndex & index, int role = Qt::DisplayRole) {
// assuming error state and modelindex row match
if (role==Qt::BackgroundRole)
return Qt::red;
}

Resources