PDA

View Full Version : Change SqlQueryModel QTableView's Row Color



toadybarker
5th September 2014, 14:24
Dear All:

New to Qt - and have created a Tableview from a SqlQuery Model. Problem is - If the Validity Column is > 1 I want
the entire row to be red...
I tried something like this but nothing happened...



void Edit_Log_Dialog::chk_Valid_Log_Line()
{
QModelIndex chk_line;
QColor rowColor = Qt::red;
int validity;
for (int row = 1; row < current_Log_View->model()->rowCount(); row++)
{
chk_line = current_Log_View->model()->index(row,7);
validity = rfa_Log_Model->data(chk_line, Qt::DisplayRole).toInt();
if (validity > 1)
{
for (int i = 1; i< 8; i++)
{
chk_line = current_Log_View->model()->index(row,i);
rfa_Log_Model->setData(chk_line,rowColor,Qt::ForegroundRole);
}
}
}
}


I saw something about using QItemDelegate - but wondered if there was an easier way to do this, or do I have to create a subclass
or something - which I am a bit fuzzy about.

Thanks, Tb

d_stranz
5th September 2014, 14:52
Why are you indexing all of your rows and columns starting at "1" instead of zero?

Why are you using two different models? (current_Log_View->model() and rfa_Log_Model) A QModelIndex retrieved from one model cannot be used to access another model.

Why are you using Qt::ForegroundRole (which sets the brush used for drawing text) instead of Qt::BackgroundRole (which sets the brush used to fill the background of the table cells)?

Both foreground and background roles expect a QBrush reference to be passed in the QAbstractItemModel::setData() call, not a QColor.

toadybarker
5th September 2014, 16:37
Thanks for the response...

I'm not sure why I though I needed to start at 1, makes sense that the index's would start at zero.

The Current_Log_View model IS the rfa_Log_Model. I copied that from code where I was changing the values in the model to reflect something that was changed by the user, so
though I needed to point at the model and change it's attributes. Since I am really trying to change the view are you saying I should have Current_Log_View->model()->setData(index,..)?

Tried both Foreground Role and Background Role but neither one seemed to change what the output of the view looked like....

d_stranz
5th September 2014, 18:02
Current_Log_View model IS the rfa_Log_Model

Then use rfa_Log_Model only. It doesn't make sense to keep retrieving the model from the view when you already have a pointer to it in rfa_Log_Model.


Tried both Foreground Role and Background Role but neither one seemed to change what the output of the view looked like....

And you're setting the color on the model as a QBrush and not a QColor?

Try calling either QAbstractItemView::reset() or QAbstractItemView::update() although changing the model data should result in this being broadcast to any views.

toadybarker
8th September 2014, 19:12
Ok. so I have messed with the CustomSqlQuery examples I saw, and found that it changes the column color depending on a particular column number. I can change it to do it for a row,
by changing the if to change based on row instead of column but that only work for a specific integer number (row count).
When I try to figure out a specific value to look for (like 103 on the first column) and change to color for that row only, then it doesn't work as expected. Sometimes
it gives me a red color, but it depends on which column I am pointing in. Ie., if the last column I accessed was 104 - then it will paint something on that row normal, but If
I point at column with 103, then click in an entry on the same row , they turn red. At first they were red....

Is there an example that someone can point me to where you have a column - which when it is say greater than 1 - then this entire row should be red... otherwise it should be the default color.

Thanks.

d_stranz
9th September 2014, 22:45
Here is a very simple QAbstractTableModel-based example that will color an entire row based on the value in the cell in a particular column.



// main.cpp

#include "TableTestDlg.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TableTestDlg w;
w.show();
return a.exec();
}




// TableTestDlg.h

#ifndef TABLETEST_H
#define TABLETEST_H

#include <QtWidgets/QDialog>

class QTableView;
class TableModel;

class TableTestDlg : public QDialog
{
Q_OBJECT

public:
TableTestDlg(QWidget *parent = 0);
~TableTestDlg();

private:
TableModel * mpModel;
QTableView * mpView;
};

#endif // TABLETEST_H

// TableTestDlg.cpp

#include "TableTestDlg.h"
#include "TableModel.h"

#include <QTableView>
#include <QHBoxLayout>

TableTestDlg::TableTestDlg(QWidget *parent)
: QDialog(parent)
{
mpModel = new TableModel( this );
mpView = new QTableView( this );
mpView->setModel( mpModel );

QHBoxLayout * pLayout = new QHBoxLayout( this );
pLayout->addWidget( mpView );
setLayout( pLayout );
}

TableTestDlg::~TableTestDlg()
{
}




// TableModel.h

#ifndef TABLEMODEL_H
#define TABLEMODEL_H

#include <QAbstractTableModel>
#include <QVector>

class TableModel : public QAbstractTableModel
{
Q_OBJECT

public:
TableModel(QObject *parent);
~TableModel();

virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const;
virtual int columnCount( const QModelIndex & parent = QModelIndex() ) const;
virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const;

private:
QVector< int > mRowData;
};

#endif // TABLEMODEL_H

// TableModel.cpp

#include "TableModel.h"
#include <QBrush>

static int sRowCount = 42;
static int sColumnCount = 4;
static int sNumberColumn = 1;

TableModel::TableModel(QObject *parent)
: QAbstractTableModel(parent)
{
mRowData.resize( sRowCount );

// Set data to an integer - 0, 1, or 2
for ( int nRow = 0; nRow < sRowCount; ++nRow )
mRowData[ nRow ] = int( 3.0 * double( qrand() ) / double( RAND_MAX ) );
}

TableModel::~TableModel()
{
}

int TableModel::rowCount( const QModelIndex & parent /*= QModelIndex() */ ) const
{
if ( parent.isValid() )
return 0;
return sRowCount;
}

int TableModel::columnCount( const QModelIndex & parent /*= QModelIndex() */ ) const
{
if ( parent.isValid() )
return 0;
return sColumnCount;
}

QVariant TableModel::data( const QModelIndex & index, int role /*= Qt::DisplayRole */ ) const
{
QVariant retVal;

switch( role )
{
case Qt::DisplayRole:
if ( index.column() == sNumberColumn )
retVal = QString( "%1" ).arg( mRowData[ index.row() ] );
else
retVal = QString( "foo" );
break;

case Qt::BackgroundRole:
if ( mRowData[ index.row() ] > 1 )
retVal = QBrush( Qt::red );
else if ( mRowData[ index.row() ] < 1 )
retVal = QBrush( Qt::green );
break;
}
return retVal;
}


The results look like this 10625

Since you are using a QSqlQueryModel (which is also based on QAbstractTableModel) you have two options:

1 - Derive a class from QSqlQueryModel and reimplement the data() method. In the Qt::BackgroundRole case, you probably must make a recursive call to data() with Qt::DisplayRole and the QModelIndex that corresponds to the column you want to check for the threshold value and convert the result to an int. If the value is greater than the threshold, set the return value to the appropriate QBrush as I show above, otherwise return an empty QVariant. For all other roles, return QSqlQueryModel::data() to make sure your results are displayed as they should be.

2 - Derive a class from QSortFilterProxyModel and use the QSqlQueryModel as the source (QSortFilterProxyModel::setSourceModel()). In your proxy model, implement only the data() method as described in (1) above, except substitute QSortFilterProxyModel::data() for QSqlQueryModel::data().

toadybarker
16th September 2014, 18:53
Thanks,

Yes, after I looked a bit further I was able to piece together a solution - but this confirms that I was on the right track. I came up with something like this.



CurVideoValidityModel::CurVideoValidityModel(QObje ct *parent)
:QSqlQueryModel(parent)
{
}
QVariant CurVideoValidityModel::data(const QModelIndex &item, int role) const
{
QModelIndex validityIndex = index(item.row(),5);
QString line_validity = QSqlQueryModel::data(validityIndex).toString();
if ((role == Qt::TextColorRole) && (line_validity == "Y"))
{
return QVariant::fromValue(QColor(Qt::red));
}
else
{
return QSqlQueryModel::data(item,role);
}
}


I then set my Tableview to this model. Have to refresh the model when I change things, but it seemed to work. Didn't include num rows or anything thing else...

Thank you so much for the example.

d_stranz
16th September 2014, 22:46
Didn't include num rows or anything thing else...

Right, because your QSqlQueryModel base class defines them. QAbstractTableModel defines these as pure virtual methods, and so any concrete class based on it needs to define at least the three methods: rowCount(), columnCount(), and data().

By the way, I was curious how hard it would be to turn my example into one that uses a QSortFilterProxyModel instead of deriving from the actual model, and it turns out to be pretty simple. Doing so is really what the Qt Model-View architecture is intended for - you map the same model into different views using proxies to extract or modify parts of the model to suit the requirements of the view. This is generally preferred over deriving from the model (as you and I did) to accomplish the same end.


Have to refresh the model when I change things

Not sure what you mean by that. It is usually the model that notifies the view that it has changed, and the view updates accordingly. Unless you are changing something like the column used for the comparison or the row color; that's where you lose the connection to the model. If you used a QSortFilterProxyModel, you qould simply call the invalidate() method after changing something external to the model.

anda_skoa
17th September 2014, 09:04
By the way, I was curious how hard it would be to turn my example into one that uses a QSortFilterProxyModel instead of deriving from the actual model, and it turns out to be pretty simple.

Sounds actually more like a job for QIdentityProxyModel, the proxy only changes data, not structure.

Cheers,
_

d_stranz
17th September 2014, 18:03
QIdentityProxyModel

Yes, even better. Too many darn classes in Qt to keep track of all of them. I'll keep this in mind the next time I need a proxy model.