Set color to a QTableView row - qt

void MyWindow::initializeModelBySQL(QSqlQueryModel *model,QTableView *table,QString sql){
model = new QSqlQueryModel(this);
model->setQuery(sql);
}
With this method i can set a QSQlQueryModels to my QTableviews.
But How i can set color to a row based on a cell value?

The view draws the background based on the Qt::BackgroundRole role of the cell which is the QBrush value returned by QAbstractItemModel::data(index, role) for that role.
You can subclass the QSqlQueryModel to redefine data() to return your calculated color, or if you have Qt > 4.8, you can use a QIdentityProxyModel:
class MyModel : public QIdentityProxyModel
{
QColor calculateColorForRow(int row) const {
...
}
QVariant data(const QModelIndex &index, int role)
{
if (role == Qt::BackgroundRole) {
int row = index.row();
QColor color = calculateColorForRow(row);
return QBrush(color);
}
return QIdentityProxyModel::data(index, role);
}
};
And use that model in the view, with the sql model set as source with QIdentityProxyModel::setSourceModel.
OR
You can keep the model unchanged and modify the background with a delegate set on the view with QAbstractItemView::setItemDelegate:
class BackgroundColorDelegate : public QStyledItemDelegate {
public:
BackgroundColorDelegate(QObject *parent = 0)
: QStyledItemDelegate(parent)
{
}
QColor calculateColorForRow(int row) const;
void initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const
{
QStyledItemDelegate::initStyleOption(option, index);
QStyleOptionViewItemV4 *optionV4 =
qstyleoption_cast<QStyleOptionViewItemV4*>(option);
optionV4->backgroundBrush = QBrush(calculateColorForRow(index.row()));
}
};
As the last method is not always obvious to translate from C++ code, here is the equivalent in python:
def initStyleOption(self, option, index):
super(BackgroundColorDelegate,self).initStyleOption(option, index)
option.backgroundBrush = calculateColorForRow(index.row())

Your best bet is to define a custom model (QAbstractTableModel subclass). You probably want to have a QSqlQueryModel as a member in this custom class.
If it's a read-only model, you need to implement at least these methods:
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
and for well behaved models also
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
If you need the model to be able to edit/submit data, things get a bit more involved and you will also need to implement these methods:
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &index=QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index=QModelIndex());
What will actually change a row appearance lies in the return value of this method:
QVariant data(const QModelIndex &index, int role) const;
A dumb example:
QVariant MyCustomModel::data(const QModelIndex &index, int role) const
{
if ( !index.isValid() )
return QVariant();
int row = index.row();
int col = index.column();
switch ( role )
{
case Qt::BackgroundRole:
{
if(somecondition){
// background for this row,col is blue
return QVariant(QBrush (QColor(Qt::blue)));
}
// otherwise background is white
return QVariant(QBrush (QColor(Qt::white)));
}
case Qt::DisplayRole:
{
// return actual content for row,col here, ie. text, numbers
}
case Qt::TextAlignmentRole:
{
if (1==col)
return QVariant ( Qt::AlignVCenter | Qt::AlignLeft );
if (2==col)
return QVariant ( Qt::AlignVCenter | Qt::AlignTrailing );
return QVariant ( Qt::AlignVCenter | Qt::AlignHCenter );
}
}
}

Related

QListView moveRow() from model not called

I'm having a Listview which should show a preview of an image. For this I created a the item "ListItem"
class ListItem
{
public:
ListItem();
ListItem(QString name, QImage image);
QImage* previewIcon();
void setPreviewIcon(QImage icon);
QImage *image();
void setImage(QImage* image);
void setImage(QImage image);
void setName(QString name);
void setChecked(bool checked);
QString name();
bool checked();
private:
QImage m_preview;
QImage m_image;
QString m_name;
bool m_checked;
};
This model stores the image it self and a preview of it. This works fine for inserting and removing items:
class ListModel: public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
bool removeRow(int row, const QModelIndex &parent = QModelIndex());
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool insertRow(int row, const QModelIndex &parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool appendItem(ListItem* item);
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
bool moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild);
Qt::DropActions supportedDropActions() const override;
QHash<int, QByteArray> roleNames() const override;
private:
ListItem* getItem(const QModelIndex &index) const;
private:
QList<ListItem*> m_scannedDocuments;
};
This is the setup of the QListView:
m_scannedDocumentsModel = new ListModel();
m_scannedDocuments = new QListView();
m_scannedDocuments->setModel(m_scannedDocumentsModel);
m_scannedDocuments->setDragDropMode(QAbstractItemView::InternalMove);
m_scannedDocuments->setMovement(QListView::Snap);
m_scannedDocuments->setDefaultDropAction(Qt::MoveAction);
Dragging and droping is fine for the preview, the name and if it is checked or not. The problem is the image "m_image". When I'm doing a move in the view, the view calls insertRows() and inserts a new item and removes the old item, but does not call moveRows.
Why moveRows() is not called?
Here you can find the full implementation:
ListModel.cpp
ListModel.h
Another approach would be to create a userRole for the image it self. Here I tried to reimplement roleNames() as
QHash<int, QByteArray> ListModel::roleNames() const {
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
roles[Qt::UserRole] = "Qt::UserRole";
return roles;
}
and check the roles in setdata()/data() but this didn't work either.
What is the best way to have a complex model and moving them in a listview?
As already answered in brief on the Qt interest list:
Because moveRows was added in Qt 5.x, and drag and drop in Qt 4, and still uses insert+remove for backwards compatibility.
I'd add that "moveRows" is I guess considered an "optimization" and not really a requirement for a model, and the view has no way to know if the move methods are really implemented. Since an editable model requires remove/insert functions, it's "safer" to call those by default.
You can re-implement the model's dropMimeData() method and call moveRows() yourself. But, the caveat is you should return false from the method if you do this. If you return true to a Qt::MoveAction, the view will still try to remove the row from the old position in the model (which is obviously not what you want).

QTableView model out of memory

I am getting an out of memory crash when using QTableView + QAbstractTableModel with a large number of items.
The problem seems to come from the vertical header view. It tries to allocate a QVector with same size as the row count (150 millions).
How can i use millions of rows in my model without crashing the table?
Here is a sample to reproduce the issue.
class MiniModel : public QAbstractTableModel
{
Q_OBJECT
public:
MiniModel(QObject * parent = nullptr);
int columnCount(QModelIndex const &parent = QModelIndex()) const override;
int rowCount(QModelIndex const &parent = QModelIndex()) const override;
QVariant data(QModelIndex const &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
};
MiniModel::MiniModel(QObject * parent) : QAbstractTableModel(parent)
{
}
int MiniModel::columnCount(QModelIndex const & parent) const
{
return 1;
}
int MiniModel::rowCount(QModelIndex const & parent) const
{
return 150000000;
}
QVariant MiniModel::data(QModelIndex const & index, int role) const
{
if (role == Qt::DisplayRole)
return 23; // just return 23 in all cells
else
return QVariant();
}
QVariant MiniModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (section == 0 && role == Qt::DisplayRole && orientation == Qt::Horizontal)
return "Dummy";
else
return QVariant();
}

Qt QSortfilterproxy model virtual column

A related question has been asked by ymoreau here - but there is no decisive solution. I have subclassed QSortFilterProxyModel with the purpose to display some data in the virtual column on addition to QSqlRelationalTableModel (sourceModel) whose data is from MYSQL database.
Below is my code:
class vModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit vModel(QObject *parent): QSortFilterProxyModel(parent)
{
}
virtual QModelIndex index(int row, int column) const
{
if(column >= columnCount()-1){
return createIndex(row,column);
}
else
return QSortFilterProxyModel::index(row, column);
}
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const
{
Q_UNUSED(parent);
return sourceModel() ? (sourceModel()->columnCount() + 1) : 0;
}
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
{
if (index.isValid() && role == Qt::EditRole)
{
QSortFilterProxyModel::setData(index,value,role);
emit dataChanged(index,index);
return true;
}
else
return false;
}
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
{
int amtCol = columnCount()-1;
if (orientation == Qt::Horizontal
&& section == amtCol
&& role == Qt::DisplayRole){
return QString("Amount");
}
else
return QSortFilterProxyModel::headerData(section, orientation, role);
}
virtual QModelIndex parent(const QModelIndex &index) const {
Q_UNUSED(index);
return QModelIndex();
}
virtual QVariant data(const QModelIndex &index, int role) const
{
int amtCol = columnCount()-1;
if(index.column() == amtCol ){
if (role != Qt::DisplayRole)
return QSortFilterProxyModel::data(index, role);
else{
QString val = QString("Amount Index(%1,%2)").arg(index.row()).arg(index.column());
return val;
}
}
return QSortFilterProxyModel::data(index, role);
}
virtual QModelIndex mapFromSource(const QModelIndex &source) const
{
return index(source.row(), source.column());
}
virtual QModelIndex mapToSource(const QModelIndex &proxy) const
{
return (sourceModel()&&proxy.isValid())
? sourceModel()->index(proxy.row(), proxy.column(), proxy.parent())
: QModelIndex();
}
protected:
Qt::ItemFlags flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QSortFilterProxyModel::flags(index);
if (index.isValid())
{
flags |= Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
return flags;
}
};
The view draws the extra column with correct header as I specified, but the content is empty even alternating row background is not painted. The model returns the correct data when I query the extra column indexes.The delegates returns false when I check the validity of any index in the virtual column yet the my subclassed model returns true.
Where's my problem...Is it the model or the delegates? Is there any additional function that I need to include?
Subclassing the Sourcemodel yielded my expectations so easily. I believe QIdentityProxyModel or any other proxy can do the same task but you have to man-handle so many issues. My Sourcemodel (the QSqlRelationalTableModel) responds to data changes (calculated values in more than 4 virtual columns) and communicates with the default relational-delegate so easily.
If there's away to make proxymodels do this task, I still welcome those suggestions. Am using Qt5.9

View doesn't display data from a model

could someone please tell me, why this code for the freaking hell, doesn't display data in a view?
#include <QApplication>
#include <QtGui>
class File_Model : public QAbstractItemModel
{
private:
QStringList data_;
public:
File_Model()
{}
QVariant data(const QModelIndex &index, int role) const
{
return data_.at(index.row());
}
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole)
{
switch(role)
{
case Qt::DisplayRole:
data_ = value.toStringList();
emit dataChanged(index,index);
return true;
}
return false;
}
virtual QModelIndex index(int row, int column, const QModelIndex&) const
{
return createIndex(row,column);
}
virtual QModelIndex parent(const QModelIndex&) const
{
return QModelIndex();
}
virtual int rowCount(const QModelIndex&) const
{
return data_.size();
}
virtual int columnCount(const QModelIndex&) const
{
return 1;
}
};
int main(int argc,char** argv)
{
QApplication app(argc,argv);
QDir dir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
File_Model* model = new File_Model;//(dir.entryList());
bool t = model->setData(QModelIndex(),dir.entryList());
QListView* view = new QListView;
view->setModel(model);
view->show();
return app.exec();
}
The problem is on your data function. You should check the role before displaying something:
QVariant data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
return QVariant(data_.at(index.row()));
return QVariant();
}
Also note that you don't have to use the setData in your case. setData is udes for editing models, not initializing them with some values.
To enable editing in your model, you must also implement setData(),
and reimplement flags() to ensure that ItemIsEditable is returned.
Instead you could add a public function in your model and call it instead:
void setEntries(QStringList entries)
{
beginInsertRows(createIndex(0,0), 0, entries.count());
data_ = entries;
endInsertRows();
}

Subclassing QAbstractTableModel

I have subclassed a QAbstractTableModel to represent data from a QMap. This QMap has QLists of QSqlRecords and this map is modified by some other part of my code. I want to use this model with a QTableView to display the sql records in this map for each key. Here is my code.
//mymodel.h
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
void setRecordMap(QMap<int, QList<QSqlRecord>> *map);
void setSelectedSerMsgIndex(QModelIndex *index);
private:
QMap<int, QList<QSqlRecord>> *recordMap;
QModelIndex *selectedSerendibMsgIndex;
};
//mymodel.cpp
MyModel::MyModel(QObject *parent) : QAbstractTableModel(parent)
{
}
int MyModel::rowCount(const QModelIndex &parent) const
{
if(recordMap->isEmpty())
return 0;
int row = selectedSerendibMsgIndex->row();
return recordMap->value(row).size();
}
int MyModel::columnCount(const QModelIndex &parent) const
{
if(recordMap->isEmpty())
return 0;
int row = selectedSerendibMsgIndex->row();
return recordMap->value(row).at(0).count();
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if(recordMap->isEmpty())
return QVariant();
if (!index.isValid())
return QVariant();
int row = selectedSerendibMsgIndex->row();
if (index.row() >= recordMap->value(row).size())
return QVariant();
if (role == Qt::DisplayRole)
{
return recordMap->value(row).value(index.row()).value(index.column()); /* QVariant("hello");*/
}
else
{
return QVariant();
}
}
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
recordMap = map;
}
void MyModel::setSelectedSerMsgIndex(QModelIndex *index)
{
selectedSerendibMsgIndex = index;
}
Sorry for the huge post. But the problem is, I cannot see the data from the map. I am guessing it is because there's something wrong with my implementation of the data() method. But I can't figure out what it is. Please be kind enough to help me. Thank you.
try changing this:
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
recordMap = map;
}
to this:
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
beginResetModel();
recordMap = map;
endResetModel();
}

Resources