How to access QStandardItemModel and it's data using a QModelIndex? - c++

I have a clicked()-signal which knows a selected index which is of the type QModelIndex.
void onListClicked(const QModelIndex & index) { /* ... */ }
No I want to access the data of the clicked item. I found out I can access the model using model():
void onListClicked(const QModelIndex & index)
{
QStandardItemModel * model {index.model()};
}
But this fails as the model() getter only allows me to return an QAbstractItemModel.
error: invalid conversion from 'const QAbstractItemModel*' to 'QStandardItemModel*' [-fpermissive]
How to access the QStandardItemModel or even better the selected QStandardItem? My unique identifier is stored in QStandardItem::data().
What I need is something like that:
void onListClicked(const QModelIndex & index)
{
QStandardItemModel * model {index.model()};
QStandardItem * item {model->itemFromIndex(index)};
qDebug() << item->data().toString();
}
But that does not work. Why is that so difficult. What do I miss here?

I think you can get the data directly from the model index:
void onListClicked(const QModelIndex & index) {
index.data(Qt::UserRole + 1);
// ...
}
You can use any other role to retrieve different kind of data.

Just cast it:
QStandardItemModel *model { static_cast<QStandardItemModel *>(model()); }

I had the same problem, as I need to retrieve my special model:
auto myModel=const_cast<MySpecialModel*>(dynamic_cast<const MySpecialModel*>(modelIndex.model()));
This was a perfectly working solution for me.
But, there seems to be an important issue doing so:
http://doc.qt.io/qt-5/qmodelindex.html
It says there:
A const pointer to the model is returned because calls to non-const functions of the model might invalidate the model index and possibly crash your application.
Unfortunately, the docs didn't say, why there might be a possible crash and what one shouldn't do.

Related

how I use the listView from QT to take and show my vector?

have two functions .getProjects() and .getEmployees(), from another class "manage.h", that basically return a vector with all the projects/employees I've added until now.
How can I show them in my QT interface?
I've created two buttons (in a stackedWidget), one to get all the projects when clicked and the other one for the employees, and I've also used a listView that should show all my data when I click one of the two buttons, but how I tell the listView to take and show my function?
void MainWindow::on_showAllProjects_pushButton_clicked()
{
ui->listView->something(manage.getProjects());
}
You first need a model for your QListView.
QListView inherits QAbstractItemView which has the method of adding a model.
QItemSelectionModel *m = ui->listview->selectionModel();
ui->listview->setModel(new model);
Then you can start inserting rows into this model, by using a method from QAbstractItemModel, which is this one:
bool insertRow(int row, const QModelIndex &parent = QModelIndex())
Then use this method to set the data of this last item you inserted (the last row), you can access it by providing the last index of the model
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
Set the first argument to ui->listview->model->rowCount()
And the second element is an element from your vector, so you gonna have to iterate through it.

Proper way to implement an editable QAbstractListModel subclass for QML ListView

I am trying to make an editable model for a ListView in QML, using QAbstractListModel. Since it is stated in the Qt Documentation, I tried implementing setData() for my model, but I am facing several issues.
The setData() member function is defined as such:
bool setData(const QModelIndex &index, const QVariant &value, int role)
However, in my ListView delegate I have something like this:
MouseArea
{
anchors.fill: parent
onClicked: elementClicked(index)
}
Is there a proper way to cast the index to a QModelIndex to use it in setData()? I tried to craft a QModelIndex using this:
void MainWindow::onElementClicked(int index)
{
AbstractItem x = mListModel.getItem(index); //Getting AbstractItem at specific index...
x.setStatus(); //Changing a boolean value of AbstractItem
QModelIndex idx = MIListModel.index(0, index, QModelIndex()); //using 0, since the ListView only has 1 row.
qDebug() << index;
MIListModel.setData(idx, x, Qt::EditRole); //...and then trying to pass it in setData() to replace the original one.
qDebug() << "OK";
}
But then setData() always returns false. Using qDebug() << index.row() and qDebug() << index.column() inside setData(), I found out that the value for both row and column is -1, thus causing the failure.
Also, do I have to implement Qt::ItemFlags flags(const QModelIndex &index) const? In the Qt Documentation(it's the same link as above) they state that it is needed, but I found no relevant example of it being used for QML.
Finally, I am having trouble passing AbstractItem to setData(), since I am getting this:
cannot convert argument 2 from 'const QVariant' to 'const AbstractItem &'
with
[
T=AbstractItem
]
To resolve this, I tried using Q_DECLARE_METATYPE(AbstractItem), but I still get the same error. The only way I could temporarily solve this was changing the setData() member function to use a AbstractItem instead a QVariant. Is this even good practice?
To sum up my problem, I have these questions:
How can I cast an int to a QModelIndex to use in setData().
Do I also have to reimplement flags() to get an editable model. If so, how?
Is it a good idea to change the second argument of setData() to one of my choice? If not, how do I convert my class to QVariant?
If possible, I would also like a source where I can further study using QAbstractListModel with QML.
Edit: Removed additional question as it was off-topic.

sourceModel()->createIndex() in QAbstractProxyModel sub-class

I am attempting to create a proxy model that dynamically maps items from source model.
Following the implementation of QIdentityProxyModel with the intention to go from there I have discovered that it is in fact impossible to replicate it by examining the 4 core functions:
mapFromSource()
mapToSource()
index()
parent()
Consider this based on QIdentityProxyModel:
mapFromSource()
QModelIndex ProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if(sourceIndex.isValid())
return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());
else
return QModelIndex();
}
mapToSource()
QModelIndex ProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if(proxyIndex.isValid())
return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
else
return QModelIndex();
}
index()
QModelIndex ProxyModel::index(int row, int column, const QModelIndex &parent) const
{
const QModelIndex sourceParent = mapToSource(parent);
const QModelIndex sourceIndex = sourceModel()->index(row, column, sourceParent);
return mapFromSource(sourceIndex);
}
parent()
QModelIndex ProxyModel::parent(const QModelIndex &index) const
{
const QModelIndex sourceIndex = mapToSource(index);
const QModelIndex sourceParent = sourceIndex.parent();
return mapFromSource(sourceParent);
}
THE ISSUE
Problem lies in mapToSource() line
return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
The QAbstractItemModel::createIndex is protected function and cannot be used unless the caller is declared friend. That is obviously not an option without modifying the QAbstractItemModel class directly. The only alternative is to use regular QAbstractItemModel::index but that requires parent in form of QModelIndex as one of the arguments. However doing this:
return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), proxyIndex.parent());
causes an infinite loop due to the fact that parent() function relies on mapToSource() and vice versa now.
The alternative approach shown for example here relies on the stored map of QPersistentModelIndex objects that are queried in the above mentioned functions. This approach while viable has several disadvantages:
It requires a lots of extra memory to store the indexes
All persistent indexes need to be updated by the source model upon every structure change (as opposed to on demand index lookup with dynamic models)
Queries to the map may be slow when iterating over kyes of the map (this can be remedied by making two identical maps in reverse at the expense of yet more memory)
Hence my questions:
Is there another way to handle the hierarchical proxy model dynamically without relying on createIndex() and without running into infinite loop of function calls?
Is it in fact necessary to create and maintain the structure of the proxy model in a storage vis-à-vis the source structure or is there a way to create a dynamic proxy model?
Thanks!
Perhaps this is a late reply, but I've just faced the same problem. I solved it by casting sourceModel() to my model, meanwhile I declared my ProxyModel as a friend.
This is my mapToSource definition:
QModelIndex ProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
Model* pModel = qobject_cast<Model*>(sourceModel());
if (!pModel || !proxyIndex.isValid()) return QModelIndex();
...
return pModel->createIndex(row, col, proxyIndex.internalPointer());
}
and the friend declaration:
class Model : public QAbstractTableModel
{
Q_OBJECT
friend class ProxyModel;
...
};
I understand your concern about using only QAbstractItemModel interface (believe me, I had the same), but let's face it the ProxyModel can only be used with my Model and nothing else (at least according to my implementation). Therefore I don't see anything bad in "friendship" like this. As for UB there shouldn't be any issues here as well, because qobject_cast would return 0 if other model other than mine was provided.
Building on the previous answers, I would like to add some alternate ways
First, the uglyest hack in the history of time itself is safe-ish on most compile:
class HackyDummy : public QAbstractItemModel
{
friend class ProxyModel;
};
QModelIndex ProxyModel::createSourceIndex(int r, int c, void *i) {
return ((HackyDummy*) sourceModel())->createIndex(r,c,i);
}
Now, of course, this is a terrible, terrible hack. It also wont compile in some compiler as the C++ spec isn't very clear (or at least as been mis-interpreted by some) on accessing parent class private and protected methods from sub-class friends. This lead us to a better plan. But first, a little detail: QIdentityProxyModel does it (almost). QIdentityModel own mapToSource/mapFromSource are public and re-implementable, so is its createIndex. So, it is possible do an internal/private QIdentityProxyModel with a method to create our little source indices from the row, column and internal pointers by accessing the hack contained inside of QIdentityProxyModel itself.
Edit: typo
I just inherited from QIdentityProxyModel and used it's mapToSource method in my mapToSourceMethod.
QModelIndex PropertyModel::mapToSource(const QModelIndex & proxyIndex) const
{
if(hasNoModel())
{
return QModelIndex();
}
QModelIndex remapped = createIndex( /* transform proxyIndex here */ );
return QIdentityProxyModel::mapToSource(remapped);
}
It means you don't end up with a cast that could crash your code should you ever change the source model type and forget to update your cast.

QModelIndex::data not working

I have a QTreeView in my application with a data model. I'm capturing when items are double clicked with the following slot:
void MainWindow::on_treeView_doubleClicked(const QModelIndex &index)
{
if (index.parent().isValid()) {
QSharedPointer<GMResource> resource;
resource = index.data(Qt::UserRole).value<QSharedPointer<GMResource> >();
Workspace::GetSingleton()->OpenResourceEditor(resource);
}
}
I expected the QModelIndex::data() method to (execute and) return the underlying QStandardItem::data() for the item referenced by that index, however its not returning anything. I set a breakpoint in my QStandardItem::data() method, and it's not even being called, so I might have incorrectly assumed what QModelIndex::data() actually returns.
How can I access the item data referenced by the QModelIndex (eg. Access to the original QStandardItem I added to the model).
Here is my data() method for my QStandardItem derived class:
virtual QVariant data( int role) const {
if (role==Qt::UserRole) {
return QVariant(resource);
}
return QStandardItem::data(role);
}
Any help would be much appreciated
I found the solution to the problem.
I replaced this code:
return QVariant(resource);
With this code:
QVariant r;
r.setValue<QSharedPointer<GMResource> >(resource);
return r;
Seems to be working as expected. I guess the data() method was being executed, but the breakpoints weren't being triggered for some reason.
You should add Q_DECLARE_METATYPE(QSharedPointer<GMResource>) to be abe to wrap QSharedPointer<GMResource> type in QVariant

Qt4: Read Default mimeData from QAbstractItemModel

What I want to do is very similar to this. Except that I am working with a QAbstractItemModel that has a tree structure and am interested in more than just the row and column. In fact, in my model, column is always 0. But in order to implement drag and drop, I need to get the parent, children, and the opaque pointer that internalPointer() returns. Here is some relevant code. CTreeView extends QTreeView.
void CTreeView::dragEnterEvent(QDragEnterEvent* event)
{
if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
{
event->acceptProposedAction();
}
}
void CTreeView::dropEvent(QDropEvent* event)
{
const QMimeData* mime_data = event->mimeData();
QByteArray encoded_data =
mime_data->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded_data, QIODevice::ReadOnly);
while (!stream.atEnd())
{
// I can do this.
int row, column;
stream >> row >> column;
// But how do I construct the QModelIndex to get the parent, children,
// and opaque pointer?
// I have seen other advice that mentions doing this.
QMap<int, QVariant> role_data_map;
stream >> row >> col >> role_data_map;
// Which allows you to do this.
QList<int> keys = role_data_map.keys();
BOOST_FOREACH(int key, keys)
{
QVariant variant = role_data_map[key];
// use the variant
}
// But that only gets me part of the way there.
}
}
Any ideas? I only want to support drag and drop within the tree view so I'm thinking about storing the QModelIndexList of selectedIndexes() in a member variable of my subclass and just manipulating it directly in dropEvent(). That seems like cheating somehow so I'm still interested in the Qt way. Please let me know what you think of this idea.
First it looks like from your code that you are doing dnd the wrong way: you should not overload dropEvent in your view, but instead dropMimeData in your model. The following document explains how to do dnd with the model/view framework of Qt:
http://doc.trolltech.com/latest/model-view-dnd.html
As for your specific problem, which is to access the internalPointer() of the dropped items. Storing the indexes in an index of your class is dangerous and error-prone. What you want to do is store the information you need in the mime data. I don't know what your use case is so I cannot guess what this useful data is - but if you just need the value of internalPointer (and can make sure this value will still be valid when the drop event is received), you can just store it, as you decide the format. For instance, if your data is referenced by a unique id somewhere (like the row id in a database), you can store this information and have a custom index(int rowid) method in your model that constructs a QModelIndex from this information. Normally the internalPointer of an index is set during its creation, so this would allow to fetch all the needed information.
If you tell us how you create your indexes maybe we can help further.