Jérôme
Jérôme

Reputation: 27027

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 !

Upvotes: 6

Views: 22335

Answers (6)

Dan
Dan

Reputation: 45

I know this is an old question, but I want to improve upon @Jerome 's own answer:

  1. data(Qt::ForegroundRole) will return a non-valid QVariant already (so you don't have to convert it to a QColor to check it).

  2. The WindowText palette color is not always the same as the default color that would be used, so the check for that is not relevant here.

  3. You can test for the current Item's state before adjusting anything.

My resulting code looks the following:

void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (!option & QStyle::State_Selected))
    {
        QStyledItemDelegate::paint(painter, option, index);
        return;
    }

    QStyledOptionViewItem myOption(option);

    auto colorData = index.data(Qt::ForegroundRole);
    if (colorData.isValid())
        myOption.palette.setColor(QPalette::HighlightedText, colorData.value<QColor>());

    QStyledItemDelegate::paint(painter, myOption, index);
}

Upvotes: 1

Krsna
Krsna

Reputation: 444

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;}");
}

Upvotes: 2

Henrik Hartz
Henrik Hartz

Reputation: 3675

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;
   }

Upvotes: 0

Harald Scheirich
Harald Scheirich

Reputation: 9764

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

Upvotes: 1

J&#233;r&#244;me
J&#233;r&#244;me

Reputation: 27027

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));

Upvotes: 9

Caleb Huitt - cjhuitt
Caleb Huitt - cjhuitt

Reputation: 14941

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.

Upvotes: 0

Related Questions