How to color a single row in QTableView
-
Hi everybody,
I need to color a QTableView row with a different color than the one set as style.void MyClass::show(QString filter) { model = new QSqlRelationalTableModel(this); model->setTable(QString("mytable")); model->setFilter(filter); model->setRelation(2, QSqlRelation("customer", "id_customer", "name")); model->setHeaderData(3, Qt::Horizontal, tr("Number")); model->setHeaderData(4, Qt::Horizontal, tr("Date")); model->setHeaderData(5, Qt::Horizontal, tr("Customer")); model->setHeaderData(6, Qt::Horizontal, tr("Total")); model->select(); ui->view->setModel(model); ui->view->resizeColumnsToContents(); ui->view->horizontalHeader()->setStretchLastSection(true); ui->view->setItemDelegateForColumn(3, new Custom_Num_Delegate(this)); // Center aligned numbers ui->view->setItemDelegateForColumn(4, new Custom_Date_Delegate(this)); // Date Format dd/MM/yyyy ui->view->setItemDelegateForColumn(5, new Custom_Two_Delegate(this)); // Total formatted in € 102,25 }
So far everything works. I have all the data visible on the QTableView.
Using my Custom Delegates I get all the required formatting. Numbers, Date and Total.
Using color_row() I wanted to paint only a few red lines.void MyClass::color_row() { for(int row = 0; row < model->rowCount(); row++) { QSqlRecord record = model->record(i); if (mycheck) { for(int column = 0; column < model->columnCount(); column++) { model->setData(model->index(row,column),QColor(Qt::red), Qt::BackgroundRole); // Not Work } } } }
But it does not work.
Would anyone know how to give me some advice on how I could solve?
Thank you in advance
blackout69 -
@blackout69 As I suspected: your model only supports role display. A possible solution is that the delegate has the rows that should have the color and then apply it. I have implemented a delegate that should replace all delegates:
#ifndef DELEGATE_H #define DELEGATE_H #include <QStyledItemDelegate> #include <QDateTime> #include <QVector> class Delegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; QVector<int> rows() const { return m_rows; } void setRows(const QVector<int> &rows) { m_rows = rows; } protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option, index); QVariant value = index.data(); if(index.column() == 3){ option->displayAlignment = Qt::AlignCenter; } else if(index.column() == 4){ option->text = option->locale.toString(value.toDateTime(), "dd/MM/yyyy"); } else if(index.column() == 5){ option->text = option->locale.toString(value.toDouble(), 'f', 2); option->displayAlignment = Qt::AlignRight | Qt::AlignVCenter; } if(m_rows.contains(index.row())){ option->backgroundBrush = QBrush(QColor("red")); } } private: QVector<int> m_rows; }; #endif // DELEGATE_H
Then:
*.h
Delegate *m_delegate = nullptr;
*.cpp
m_delegate = new Delegate; ui->view->setItemDelegate(m_delegate);
and:
m_delegate->setRows({1, 2}); ui->view->update();
-
Hi,
Are you doing custom painting in your delegates ?
-
Hi SGaist,
Yes. This one my custom delegates:class Custom_Two_Delegate : public QItemDelegate { Q_OBJECT public: Custom_Two_Delegate (QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; Custom_Two_Delegate::Custom_Two_Delegate(QObject *parent) : QItemDelegate(parent) { } void Custom_Two_Delegate::paint(QPainter *painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { double number = index.model()->data(index, Qt::DisplayRole).toDouble(); QString text = QString("%L1").arg(number,13,'f',2); QStyleOptionViewItem myOption = option; myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; drawDisplay(painter, myOption, myOption.rect, text); drawFocus(painter, myOption, myOption.rect); }
-
Then you have to also handle the BackgroundRole as well there. Usually it would be the first thing you paint and then the rest on top of it.
-
I tried to create a custom delegate for coloring, but applying the coloring, I lose the formatting.
I understood that if I apply a custom delegate to the columns, if I apply another one to the rows, it overrides the one applied to the columnsclass Color_Delegate_Red : public QItemDelegate { Q_OBJECT public: Color_Delegate_Red (QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawBackground(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; Color_Delegate_Red::Color_Delegate_Red(QObject *parent) : QItemDelegate(parent) { } void Color_Delegate_Red::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { drawBackground(painter, option, index); QItemDelegate::paint(painter, option, index); } void Color_Delegate_Red::drawBackground(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); painter->fillRect(option.rect, QBrush(QColor(Qt::red))); }
if (mycheck) { ...... ui->view->setItemDelegateForRow(i, new Color_Delegate_Red(this)); // It works but I lose the formatting }
-
@blackout69 You cannot have 2 delegates for the same item, the last one will replace the previous one. A possible solution is not to modify the paint method but initStyleOption and displayText:
class Custom_Two_Delegate: public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; QString displayText(const QVariant &value, const QLocale &locale) const override{ return locale.toString(value.toDouble(), 'f', 2); } protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option, index); option->displayAlignment = Qt::AlignRight | Qt::AlignVCenter; } };
-
You should subclass QStyledItemDelegate. That said, you can't stack them on top of each other. Do the background handling in each of your delegates.
-
@eyllanesc
Hi eyllanesc
As you write, column formatting works.
But if I apply the coloring, I lose the formatting.ui->view->setItemDelegateForRow(i, new Color_Delegate_Red(this));
How do you paint the entire line that interests me?
-
@blackout69 It seems that you have not understood my answer, with my solution you should not change anything of your initial logic(
model->setData(model->index(row,column),QColor(Qt::red), Qt::BackgroundRole);
) only change the implementation of the delegate that I proposed. Don't use setItemDelegateForRow!!! -
@eyllanesc
I write:ui->view->setItemDelegateForColumn(5, new Custom_Two_Delegate(this)); void MyClass::color_row() { for(int row = 0; row < model->rowCount(); row++) { QSqlRecord record = model->record(i); if (mycheck) { for(int column = 0; column < model->columnCount(); column++) { model->setData(model->index(row,column),QColor(Qt::red), Qt::BackgroundRole); } } } }
Formatting works, but the lines I am interested in are not colored.
mycheck is true! -
I must insiste:
@SGaist said in How to color a single row in QTableView:
Do the background handling in each of your delegates.
-
@SGaist
I didn't understand how to apply the coloring in the custom delegates. For example on Custom_Two_Delegate which but posted @eyllanesc -
@blackout69 mmm, I assumed that the model you use handled role Qt::BackgroundRole but it seems that this is not true.
- What model do you use?
- do you want to change the color of all the rows? Or just from a particular row? if it is a particular row then what does it depend on?
- Is the color fixed or does it also depend on another condition?
-
- I use QSqlRelationalTableModel
- I just want to color a few lines. The coloring depends on the value of a condition. For simplicity I called it mycheck.
- The color is fixed.
-
@blackout69
color all items in that row like the following.
item->setData( Qt::BackgroundRole, QVariant( Qt::red ) );
no delegate is needed. -
@blackout69 As I suspected: your model only supports role display. A possible solution is that the delegate has the rows that should have the color and then apply it. I have implemented a delegate that should replace all delegates:
#ifndef DELEGATE_H #define DELEGATE_H #include <QStyledItemDelegate> #include <QDateTime> #include <QVector> class Delegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; QVector<int> rows() const { return m_rows; } void setRows(const QVector<int> &rows) { m_rows = rows; } protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option, index); QVariant value = index.data(); if(index.column() == 3){ option->displayAlignment = Qt::AlignCenter; } else if(index.column() == 4){ option->text = option->locale.toString(value.toDateTime(), "dd/MM/yyyy"); } else if(index.column() == 5){ option->text = option->locale.toString(value.toDouble(), 'f', 2); option->displayAlignment = Qt::AlignRight | Qt::AlignVCenter; } if(m_rows.contains(index.row())){ option->backgroundBrush = QBrush(QColor("red")); } } private: QVector<int> m_rows; }; #endif // DELEGATE_H
Then:
*.h
Delegate *m_delegate = nullptr;
*.cpp
m_delegate = new Delegate; ui->view->setItemDelegate(m_delegate);
and:
m_delegate->setRows({1, 2}); ui->view->update();
-
@eyllanesc
Sorry I am using QTableWidget.Try this one for all items in one row:
bool QAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);in case you have issues, check this out.
https://stackoverflow.com/questions/13265992/qabstractitemmodel-setitemdata-returns-false-when-inserting-qtuserrole -
@eyllanesc
OK well. Now works!
Thanks you have been a great help.For the sake of completeness, I changed the currency formatting to:
In order to correctly write even the thousands in Euro. Example 1.473,56double value = index.model()->data(index, Qt::DisplayRole).toDouble(); QString currency = QString("%L1").arg(value,13,'f',2); option->text = currency; option->displayAlignment = Qt::AlignRight | Qt::AlignVCenter;