Is there a way to make a QFileSystemModel add a checkbox to each of its rows? I know I can create a QStandardItemModel and add a QStandardItem with a checkbox added to it, but I need the functionality the QFileSystemModel gives me.
Is there a way to make a QFileSystemModel add a checkbox to each of its rows? I know I can create a QStandardItemModel and add a QStandardItem with a checkbox added to it, but I need the functionality the QFileSystemModel gives me.
Subclass QFileSystemModel and provide custom implementations of the data, flags and setData functions:
You will have to invent a way of keeping track of which items are checked and unchecked, so that you can access that information in data and update it in setData.Qt Code:
if (role == Qt::CheckStateRole) { /* determine whether the box should be checked or unchecked */ return shouldBeChecked ? Qt::Checked : Qt::Unchecked; } return QFileSystemModel::data(index, role); } Qt::ItemFlags CustomModel::flags(const QModelIndex& index) const { return QFileSystemModel::flags(index) | Qt::ItemIsUserCheckable; } bool CustomModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::CheckStateRole) { /* set indicator in your backing store according to whether value == Qt::Checked */ emit dataChanged(index, index); return true; } return QFileSystemModel::setData(index, value, role); }To copy to clipboard, switch view to plain text mode
If this implementation of the flags() function returns "Qt::ItemIsUserCheckable", then each rows checkbox should be checkable, right?
That doesn't happen. It would be nicer to be able to check-uncheck without having to grab the mouse-click then call on setData().
Last edited by been_1990; 15th January 2010 at 18:06.
Yes, but there’s an easy mistake to make, so I’ll take a guess that it’s relevant and describe it.
When the value returned by flags includes Qt::ItemIsUserCheckable, clicking on the checkbox causes setData to be called specifying the appropriate item index, Qt::CheckStateRole and either Qt::Checked or Qt::Unchecked according to the state to which the checkbox should change... but it doesn’t change the state for you.
You have to emit the dataChanged signal and return true, but you must also do whatever is required in your implementation to be sure that when data is called for that item index with Qt::CheckStateRole it returns the new check state.
If you don’t do those things, when you click on the checkboxes, they won’t change state. None of the Qt classes involved here will maintain the state of the checkboxes for you, not even as far as changing their appearance when you click them. The state is will always show as whatever your data function returns.
Let's see if I got it right:
When I click on a checkbox, setData() is called.
It then emits dataChanged(), that probably calls the flag() function. Which then updates all indexes.
But then:
How can I check whether the checkbox state is checked/unchecked?
And how can I change only the state of the checkbox that was clicked on?(When I click on 1, all other rows get checked also)
It seems that setData()'s int role is always the same, 10(or Qt::CheckStateRole) whenever I click the checkbox.
I haven’t checked the Qt source code, but I think that’s wrong. I think:
- When displaying an item, Qt calls the data function with Qt::CheckStateRole to determine whether to show a box with a check mark, a box without a check mark, or no checkbox at all.
- When you click on a checkbox, Qt calls the flag function to find out whether it should pay any attention.
- If the flag function returns a value including Qt::ItemIsUserCheckable, Qt calls the setData function with a value that is the opposite of the one currently displayed by the checkbox.
- The setData function now must emit dataChanged and return true; it must also do “something” that will tell the data function to return the opposite value from the one it returned before for this index.
Qt doesn’t do that for you; you have to keep track. I’d suggest getting a QPersistentModelIndex in the setData function and storing those in a QSet<QPersistentModelIndex> that lists the ones that are checked. The data function could then use this to determine the proper check state to return.
That’s expected. The value should change between checked and unchecked... if you’ve implemented data correctly.
So if it's checked it will call setData with the Qt::Unchecked value?If the flag function returns a value including Qt::ItemIsUserCheckable, Qt calls the setData function with a value that is the opposite of the one currently displayed by the checkbox.
Trough which : "QVariant& value" or "int role" ?Qt Code:
setData(const QModelIndex& index, const QVariant& value, int role)To copy to clipboard, switch view to plain text mode
So:The setData function now must emit dataChanged and return true; it must also do “something†that will tell the data function to return the opposite value from the one it returned before for this index.
Qt Code:
setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::CheckStateRole) { ///////* If the checkbox is checked, tell data() to uncheck it; If unchecked, tell data() to check it; *//////// emit dataChanged(index, index); return true; } return QFileSystemModel::setData(index, value, role); }To copy to clipboard, switch view to plain text mode
You mean QVariant& value?Qt Code:
The value should change between checked and unchecked... if you’ve implemented data correctly.To copy to clipboard, switch view to plain text mode
The value. We’re discussing only the case where role == Qt::CheckStateRole; otherwise, the call applies to something else, not the checkbox.
Right. The aggravating part of this is that nothing in any of the Qt classes will maintain the state of each item’s checkbox for you. You have to invent a way of keeping track of that, feeding the correct value to the data function, and updating it in the setData function. If you don’t return the correct state from data when it’s called on Qt::CheckStateRole, nothing else will work right.
I already can change the checkbox state from checked to unchecked. But that still checks all of them(like you said it would..)
Do you think the code of QStandardItemModel would have the implementation to check/uncheck items independently? I'm gonna look at it, but I might get lost with all the code. (if there really is a check/uncheck mechanism that I could copy)
Ok, here is the code. It aparently does what you suggested:
Qt Code:
{ role = (role == Qt::EditRole) ? Qt::DisplayRole : role; QVector<QWidgetItemData>::iterator it; for (it = d->values.begin(); it != d->values.end(); ++it) { if ((*it).role == role) { if (value.isValid()) { if ((*it).value.type() == value.type() && (*it).value == value) return; (*it).value = value; } else { d->values.erase(it); } if (d->model) d->model->d_func()->itemChanged(this); return; } } d->values.append(QWidgetItemData(role, value)); if (d->model) d->model->d_func()->itemChanged(this); }To copy to clipboard, switch view to plain text modeQt Code:
{ role = (role == Qt::EditRole) ? Qt::DisplayRole : role; QVector<QWidgetItemData>::const_iterator it; for (it = d->values.begin(); it != d->values.end(); ++it) { if ((*it).role == role) return (*it).value; } }To copy to clipboard, switch view to plain text mode
Qt Code:
{ if (!v.isValid()) return (Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable |Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled); return Qt::ItemFlags(v.toInt()); }To copy to clipboard, switch view to plain text mode
I don’t think the code it has would be very useful, since it relies on instances of QStandardItem to keep the data, and you don’t have a model built on QStandardItems, you have a QFileSystemModel, the backing store of which is not accessible to programs that use the model.
Try something like this (off the top of my head, so errors/typos are not unlikely):
Qt Code:
class CustomModel : public QFileSystemModel { //... QSet<QPersistentModelIndex> checklist; //... }; if (role == Qt::CheckStateRole) return checklist.contains(index) ? Qt::Checked : Qt::Unchecked; return QFileSystemModel::data(index, role); } Qt::ItemFlags CustomModel::flags(const QModelIndex& index) const { return QFileSystemModel::flags(index) | Qt::ItemIsUserCheckable; } bool CustomModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::CheckStateRole) { if (value == Qt::Checked) checklist.insert(index); else checklist.remove(index); emit dataChanged(index, index); return true; } return QFileSystemModel::setData(index, value, role); }To copy to clipboard, switch view to plain text mode
Note that the QSet keeps QPersistentModelIndexes, not ordinary QModelIndexes.
Seems pretty simple. I dont know why I thought it would be complicated. Should be kicking myself now...
This works like a charm, only down-side is that it gives me a checkbox per field.
I'd love to have a checkbox per row. I tried with index.column() == 0 but that only makes the first column checkbox checkable.
The other boxes are still shown.
What am I doing wrong?
take a look at the CustomModel::flags() method, and add your column test here ?
That's what I did . . they are now non-checkable but still visible.
Added after 12 minutes:
Added column check to data method.
Now works like a charm!
Last edited by the_jinx; 11th March 2011 at 15:01.
Bookmarks