Need help to create simple model for QTreeModelView
-
Hi,
I think I understood now how
QAbstractItemModel
work, of course I have already run few examples and now I'm trying to write my simple model with my items.
I can launch it but I can see only header of the column (like on the picture) but there also should be one item. So the problem is that I don't see my item.
I upload the project here
Please take a look at it.
-
@Please_Help_me_D
:)
Ok fair enough but it's also a bit hardcore to fiddle with QAbstractItemModel on
such a special day :)
-
Need help to create simple model for QTreeModelView
Did you look at the Simple Tree Model Example?
-
Hi
Ran your code.
You dont have any items ?
I dont see you add anything to the list and it also reports zeroAh you do add one here
void MyModel::fetchRootDirectory() { _items.push_back(new MyItem("first_item")); }
so the rowCount code seems not correct.
-
@JonB thank you for reply
Yes I saw this. It is opened in my editor but I still don't understand why I cannot run mine
@mrjj yes, I add item infetchRootDirectory
-
@Please_Help_me_D
But when view ask how many items you got via rowCount, you return zero.
as in No rows and that is what you see.
-
@mrjj Why do I return zero?
Are the comments below are true?int MyModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { // if this is a root (there is no parent) return _items.size(); // this returns 1 and the application goes here } const MyItem* parentItem = static_cast<const MyItem*>(parent.internalPointer()); // return parentItem->children.size(); //returns zero but the application never reach there }
-
@Please_Help_me_D Oh no, I'm wrong...
-
@Please_Help_me_D
well
you return parentItem->children.size()
and that is zero as it has no children :)So not sure if this is a tree or list or table model ?
-
@mrjj it should be tree model
-
@Please_Help_me_D
Then you should add more than the first item :)
So the root item does have children.
-
@mrjj Well I add few children to item:
void MyModel::fetchRootDirectory() { MyItem* item = new MyItem("first_item"); item->children.push_back(new MyItem("first_child")); item->children.push_back(new MyItem("second_child")); _items.push_back(item); }
but it still doesn't work :)
-
@Please_Help_me_D
so check with debugger what you return as rowCount ?
-
@mrjj I wrote:
int MyModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { qDebug() << "Item size: " << _items.size(); return _items.size(); } const MyItem* parentItem = static_cast<const MyItem*>(parent.internalPointer()); qDebug() << "Children size: " << parentItem->children.size(); return parentItem->children.size(); }
The output is:
Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Children size: 2 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Item size: 1 Children size: 2 Item size: 1 Item size: 1 Item size: 1
Why the program goes to
rowCount
so many times? I already suspected that when I set the breakpoint and tried to contue application run to next breakpoint. Is it normal?
-
@Please_Help_me_D said in Need help to create simple model for QTreeModelView:
Why it gives me such many outputs?
Because your
rowCount()
gets called by Qt (e.g. theQTreeView
) more times than you think it does :)
-
@JonB and is it normal? or my code looks so scarry that Qt don't know what to do whith it :)
-
@mrjj maybe I should manually insert rows somehow?
-
Hi
Im not sure what is wrong as could also be the index but you can take a look at
https://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html
which seems very similar to your code.
-
@mrjj ok
How do you think, is it possible that the problem is how I declare variables (pointers/references and const)?. Sometimes they differ from examples but I think it should be fine
-
@Please_Help_me_D
well i think its fine since app compiles etc.
Its more that if you use internalPointer as in the example so it does point to what you expect.
-
@Please_Help_me_D said in Need help to create simple model for QTreeModelView:
@JonB and is it normal? or my code looks so scarry that Qt don't know what to do whith it :)
No, it's normal that infrastructure will call your
rowCount()
a lot :)
-
@mrjj @JonB well guess what? Yes I've found the snippet of code that made me feel useless for whole day :D
Previously I had theMyModel::data()
method:QVariant MyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const MyItem* item = static_cast<MyItem*>(index.internalPointer()); return item->title; }
But when I change it to:
QVariant MyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); const MyItem* item = static_cast<MyItem*>(index.internalPointer()); return item->title; }
everything works :)
Now I'm trying to understand what the role is
Thank you for help!
-
@Please_Help_me_D said in Need help to create simple model for QTreeModelView:
that made me feel useless for whole day
Ehh, should it not make you feel happy you found it ?
So good spotted.
The roles are explained here
https://doc.qt.io/qt-5/qt.html#ItemDataRole-enum
-
@mrjj I would be if not my birthday today :D
-
@Please_Help_me_D
:)
Ok fair enough but it's also a bit hardcore to fiddle with QAbstractItemModel on
such a special day :)
-
@mrjj thank you))
hardcore is my lastname :)
-
@Please_Help_me_D said in Need help to create simple model for QTreeModelView:
Now I'm trying to understand what the role is
@mrjj has given you the documentation reference page for roles. You must read that! In Qt models, an index does not just store/return what you think of of as "the value" of that item, it also stores/returns a variety of additional data which you might want to set for each data item. Qt calls these "roles", I don't know where it gets that name from, you might think of them as "attributes".
When you implement your model's
data()
override, and alsosetData()
if you allow updates, you must make their behaviour take into account the value of therole
parameter. The Qt infrastructure (e.g. any views attached to the model) will be calling yourdata()
/setData()
methods a lot of times, with a variety of roles. For example, it will calldata(index, Qt::ForegroundRole)
for every cell to ask what color the text should be, ordata(index, Qt::ToolTipRole)
every time it wants to potentially show a tooltip.The normal pattern I follow for, say,
data()
is:QVariant MyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return somethingToDoWithWhatYouThinkOfAsTheValue; case Qt::AnotherRoleIWantToHandle: return somethingSuitable; default: break; } return BaseModel::data(index, role); }
And similarly for
setData()
. Note how we return the base class'sdata()
/setData()
method for any cases we don't handle; this allow for future updates.
-
@JonB thank you for explanation! This is useful for me to know
-
@mrjj Once again I need help
I set two items to the root item. So here should be one openable item wich after unwrapping should show two other items... but it shows itself instead:void MyModel::fetchRootDirectory() { MyItem* item = new MyItem("first_item"); MyItem* item1 = new MyItem("first_child"); MyItem* item2 = new MyItem("second_child"); item->children.push_back(item1); item->children.push_back(item2); _items.push_back(item); }
Also there is something wrong with highlighting of chosen item
-
@Please_Help_me_D
Hi
I ran the updated code and it seems to return the same Modelindex for different items so
that's why you see the top one also as a child and both get selected if you click on one.
So i think something up with internalpointer but could not spot it.It seems like the code from
https://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html
But with some difference with the items and handling.
-
@mrjj thank you!
I just created my model based on editabletreemodel and tried to make it as close as possible and it works now.That was the test model. Now I have to create my true model but here is the thing that I cannot tackle.
The main difference that was between my test model and editabletreemodel is that editabletreemodel hadTreeItem* rootItem
where were stored information about column header.
I want to have item that has a public variable that doesn't have the default constructorHighFive::File parentFile;
.
If I do my real model withrootItem
to store there information about column header then I will need to somehow initializeHighFive::File parentFile;
forrootItem
. ButrootItem
doesn't need to haveHighFive::File parentFile;
because this is a hdf5 file (I don't need it for columns header).
What would you suggest me?
I'm afraid to avoidrootItem
for header beacause I feel that this is the staff that made my test model useful.
-
Hi
If you to stuff HighFive::File parentFile into an item, you can so do that with UserRoles.
each item can have extra data stored in user roles.QStandardItem *item = new xxx
item ->setData(value, Qt::UserRole + 1)
item ->setData(value2, Qt::UserRole + 2)
etcwhere value can be anything , including HighFive::File
So you can create and setup parentFile outside model and simply store it there.
If that is what you are asking about ?
Im not 100% sure what you need.
-
@mrjj thank you for hint for userRole. That is useful.
But also if I add a publica variable of type that doesn't have the default constructor then I must initialaze this variables in constructor. Like:MyItem::MyItem(QString pathToFile) : parentFile(HighFive::File(pathToFile)){}
I need to store
parentFile
for all items exceptrootItem
(wich is the item for header). So it makes impossible to createrootItem
without suchhdf5
file.
Or maybe there is some solution how to initialize the object with public variable of type that doesn't have the default constructor?
-
@mrjj it is so hard being slowpoke as me :D
I needed to add parent item to every child :)Four days I worked on that I think but finally everything works :)
-
@mrjj I implemented
QAbstractItemModel::fetchMore(...)
to upload data when user click on triangle-button to show more items. It works.
Now I need to do process the data when user click this button to pack (or hide) items. I want to delete infomation from my model when user hides those items (see picture please).
Is there a function that should work when this event happens or a singnal which is emited when userclick on that button hide items?
I thought thatQAbstractItemModel::rowsRemoved
signal should be emited in such situation but it is not. I tried:connect(this, &QAbstractItemModel::rowsRemoved, this, &H5Model::pack);
but there is no signal when I push close-button. Or those signals are abstract and I need to emit them and only then catch?
-
void collapsed(const QModelIndex &index)
void expanded(const QModelIndex &index)
-
@mpergand thank you!
-
Now when I have some
MyModel
I would like to writeMyProxyModel
which inherited byQAbstractProxyModel
(I need to understand how they work that is why I don't want to useQSortFilterProxyModel
for now).
QAbstractProxyModel
inheritsQAbstractItemModel
so as I understand I must implelement at least the followingpure virtual
methods from these both abstract models:int QAbstractItemModel::columnCount(const QModelIndex &parent = QModelIndex()) const QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex &parent = QModelIndex()) const QModelIndex QAbstractItemModel::parent(const QModelIndex &index) const int QAbstractItemModel::rowCount(const QModelIndex &parent = QModelIndex()) const QModelIndex QAbstractProxyModel::mapFromSource(const QModelIndex &sourceIndex) const QModelIndex QAbstractProxyModel::mapToSource(const QModelIndex &proxyIndex) const
Ok, I don't want to use any kind of a sorting I just want to wrap
MyModel
toMyProxyModel
.
In proxy model I just declare the same methods that my source model has and invoke these methods:h5proxymodel.h
#ifndef H5PROXYMODEL_H #define H5PROXYMODEL_H #include <QAbstractProxyModel> #include <QSortFilterProxyModel> #include "h5model.h" class H5ProxyModel : public QAbstractProxyModel { Q_OBJECT public: explicit H5ProxyModel(QObject *parent = nullptr); ~H5ProxyModel(); QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; void setSourceModel(QAbstractItemModel *sourceModel) override; QAbstractItemModel* getSourceModel(); QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; bool insertRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override; bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; void expand(const QModelIndex &parent); void collapse(const QModelIndex &parent); Qt::ItemFlags flags(const QModelIndex &index) const override; private: QAbstractItemModel* sourceModel; }; #endif // H5PROXYMODEL_H
h5proxymodel.cpp
#include "h5proxymodel.h" H5ProxyModel::H5ProxyModel(QObject *parent) : QAbstractProxyModel(parent) { } H5ProxyModel::~H5ProxyModel() { delete sourceModel; } QModelIndex H5ProxyModel::mapFromSource(const QModelIndex &sourceIndex) const{ if (!sourceIndex.isValid()) return QModelIndex(); return sourceIndex; } QModelIndex H5ProxyModel::mapToSource(const QModelIndex &proxyIndex) const { if (!proxyIndex.isValid()) return QModelIndex(); return proxyIndex; } void H5ProxyModel::setSourceModel(QAbstractItemModel *sourceModel){ this->sourceModel = sourceModel; } QAbstractItemModel* H5ProxyModel::getSourceModel(){ return this->sourceModel; } QModelIndex H5ProxyModel::index(int row, int column, const QModelIndex &parent) const{ return this->sourceModel->index(row, column, parent); } QModelIndex H5ProxyModel::parent(const QModelIndex &index) const{ return this->sourceModel->parent(index); } int H5ProxyModel::rowCount(const QModelIndex &parent) const{ return this->sourceModel->rowCount(parent); } int H5ProxyModel::columnCount(const QModelIndex &parent) const{ return this->sourceModel->columnCount(parent); } QVariant H5ProxyModel::data(const QModelIndex &index, int role) const{ return sourceModel->data(index, role); } bool H5ProxyModel::setData(const QModelIndex &index, const QVariant &value, int role){ return sourceModel->setData(index, value, role); } QVariant H5ProxyModel::headerData(int section, Qt::Orientation orientation, int role) const{ return sourceModel->headerData(section, orientation, role); } bool H5ProxyModel::insertRows(int position, int rows, const QModelIndex &parent){ return sourceModel->insertRows(position, rows, parent); } bool H5ProxyModel::removeRows(int position, int rows, const QModelIndex &parent){ return sourceModel->removeRows(position, rows, parent); } bool H5ProxyModel::hasChildren(const QModelIndex &parent) const{ return sourceModel->hasChildren(parent); } void H5ProxyModel::expand(const QModelIndex &parent){ static_cast<H5Model*>(sourceModel)->expand(parent); } void H5ProxyModel::collapse(const QModelIndex &parent){ static_cast<H5Model*>(sourceModel)->collapse(parent); } Qt::ItemFlags H5ProxyModel::flags(const QModelIndex &index) const{ return sourceModel->flags(index); }
As you can see all that my proxy does is it redirects commands to source model. But all I see is header in tree view instead of a items (the right picture I see if I set source model to a tree view):
Why my logic doesn't work?