QFileSystemModel set item color issue
-
Hi! I want to set the item color or disable item when I cut it from
QListView
.QListView
usesQFileSystemModel
model. For example:ui->listView->model()->setData(ui->listView->currentIndex(), QBrush(Qt::gray), Qt::BackgroundRole);
But this code does not set the gray color as background. Any ideas? Thanks.
-
QAbstractItemView::setData() returns a boolean to indicate if the value was really set. Since you're using a plain QFileSystemModel I doubt it somehow implemented something in the setData() function for the BackgroundRole.
Therefore you have to derive from QFileSystemModel and implement it on your own. -
Ok. I tried it and it returns false.
bool res = ui->listView->model()->setData(ui->listView->currentIndex(), QBrush(Qt::gray), Qt::BackgroundRole); qDebug() << res;
So, no chance to set the item color by
QFileSystemModel
default behavior/methods? Should I really reimplementQFileSystemModel
model? -
@Cobra91151 said in QFileSystemModel set item color issue:
I tried it and it returns false.
The code I posted the link to shows that QFileSystemModel does not handle Qt::BackgroundRole so anything other than false would be a surprise here.
Should I really reimplement QFileSystemModel model?
No, you should derive from it
-
Ok, by derive you mean to inherit
QFileSystemModel
class and reimplement thesetData
method to accept other roles? Because from this code it only acceptsEditRole
.... if (!idx.isValid() || idx.column() != 0 || role != Qt::EditRole || (flags(idx) & Qt::ItemIsEditable) == 0) { return false; }
-
@Cobra91151
Yes that is exactly what @Christian-Ehrlicher means! Sub-class, overridesetData()
, implement your own logic forQt::BackgroundRole
role, else call the base implementation. -
Ok. I have tried it, but still it does not change the item background color.
Code:
bool TestModel::setData(const QModelIndex &index, const QVariant &varData, int role) { QStandardItem item(index.row(), index.column()); if (role == Qt::BackgroundRole) { QBrush brush = varData.value<QBrush>(); item.setBackground(brush); qDebug() << "setBackground"; } return QFileSystemModel::setData(index, varData, role); }
-
What should work here? You're creating a local QStandardItem, setting the background color and then it gets destroyed. How should work this at all? How should data() magically return the correct Background role with your code?
-
So, I changed code to:
bool TestModel::setData(const QModelIndex &index, const QVariant &varData, int role) { QStandardItem *item = new QStandardItem(index.row(), index.column()); if (role == Qt::BackgroundRole) { QBrush brush = varData.value<QBrush>(); item->setBackground(brush); qDebug() << "setBackground"; return item->data(Qt::BackgroundRole).toBool(); } return QFileSystemModel::setData(index, varData, role); }
But the issue still exists. What logic should I implement then? Thanks.
-
You really should take a look at how the ItemModels work... https://doc.qt.io/qt-5/model-view-programming.html
You set data with setData() and you return data with data() - so where did you implement your data() function? What should the (now leaking) item do at all? -
Good. I will check it. Thanks.
-
@Cobra91151
So I understand: the user clicks "cut" on yourQListView
, and you want to change item color displayed while it's cut? And you propose to do that via theQFileSystemModel
'ssetData(Qt::BackgroundRole)
? So you're going to store (at least conceptually) an "is this item presently cut" against each item?@Christian-Ehrlicher
Given the code shown forQFileSystemModel::setData()
rejects attempts to setQt::BackgroundRole
, are you going to/can you useQAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
to specify a value for roleQt::BackgroundRole
to "force" theQt::BackgroundRole
into the data, ready to picked up bydata(Qt::BackgroundRole)
? Or does that actually callsetData()
?! In which case, where will you store the "cut" data so thatdata()
retrieves it?A possible alternative approach, avoiding any
setData()
overriding: don't store "cut" status against each item. Just keep a variable for what item(s) is cut. (You could possibly just use the selection model for that, indicating whether it's a "cut" vs a "copy"; then when the selection goes away it's automatically no longer in "cut" state?) Override onlydata()
: when role isQt::BackgroundRole
look up theQModelIndex
in your variable/list of "cut" ones and returnQBrush(Qt::gray)
if there. Comment? -
I have cut option via context menu. It works using
QClipboard
and works well. The reason I want to change color of the item is to notify user that such item has been cut. So, basically it is only the style, hence I can do aQMessageBox
orQLabel
to notify. I am still figuring out what should I reimplement to just set the item color. -
-
Override
data()
to unconditionally returnQBrush(Qt::gray)
when role isQt::BackgroundRole
. Check the principle works, and visuals look OK. -
Override
setData()
to store the index (QModelindex
) in some container when you want to mark the item as being "cut". -
Alter the
data()
override code to check whether the index is marked in the "cut" container, and only returnQBrush(Qt::gray)
forQt::BackgroundRole
when it is.
Hint: container should be directly accessible by
QModelIndex
for it (#3) to be fast. -
-
Ok. I figured it out. My code:
TestModel.h
#ifndef TESTMODEL_H #define TESTMODEL_H #include <QObject> #include <QFileSystemModel> #include <QSet> #include <QBrush> class TestModel : public QFileSystemModel { Q_OBJECT public: using QFileSystemModel::QFileSystemModel; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &varData, int role) override; private: QSet<QPersistentModelIndex> indexList; }; #endif // TESTMODEL_H
TestModel.cpp
#include "testmodel.h" QVariant TestModel::data(const QModelIndex &index, int role) const { if (role == Qt::BackgroundRole) { return indexList.contains(index) ? QBrush(Qt::gray) : QBrush(Qt::transparent); } return QFileSystemModel::data(index, role); } bool TestModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::BackgroundRole) { if (value == QBrush(Qt::gray)) { indexList.insert(index); } else { indexList.remove(index); } emit dataChanged(index, index, {Qt::BackgroundRole}); return true; } return QFileSystemModel::setData(index, value, role); }
Now it works well. The issue is resolved!
-
dataChanged() should pass the modified role -> dataChanged(idx, idx, {Qt::BackgroundRole}) and the two ctors and flags() function is not needed in your class.
In the class definition data() and setData(9 should be marked with 'override' -
Ok. I improved
dataChanged
method and markeddata()
andsetData()
withoverride
. Thanks.When I removed constructors I got compiling issues. It is required to have at least one constructor. About
flags
, I think it is also required because I am implementing drag and drop with models and views. -
You don't do anything in your flags() so no need for it
wrt the ctors:
class TestModel : public QFileSystemModel { Q_OBJECT public: using QFileSystemModel::QFileSystemModel; ...
-
Good. I changed it. Thanks.