always show expand icon in tree view
-
wait to load and after you loaded it you and it empty... this is wrong..
To be fair, that is precisely what you [EDIT: I now realize that was the OP and not you] are asking for: show it as though it's expandable to start with to avoid having to look, and then maybe have to take that away when user later clicks and finds it's not expandable!
-
@JonB
this is what I've said before that one of the solutions is to subclass the qtreeview (to make an extension - in my first post) or he has any way to make a query to the database server to get the parents.. so .. why not to ask for the first child of each or for the first 10children of each parent?
but in order to give good advice on how this can be best implemented, he has to answer the questions from heresorry are your concern about network speeds those days?? how big database do we speak about? what database will you have MySQL or SQLite (the concept not the actual name)?? what device has to run this code? is it mobile??
and based on his I can think of a good and "proper" solution - or to put it even better - How I will do it myself...
-
- the application should handle databases with 40+ million records (to show in gui)
- it's SQLite
- it's a desktop application
@JonB
i tried to implement that idea but it's not only changingchildCount()
, more functions need to be overriden. don't know if it's worth it, or i should just go with making use of the dummy node.in my case i do have children items for each parent node, so it won't mislead the user
-
hasChildren() doesn't help here / is not needed to be touched - you have to reimplement rowCount()
-
@Christian-Ehrlicher
i know right, and that's what i did, only for me it's calledchildCount()
, but that led to app crash (becauseremoveChidlren()
was called somewhere) -
There is no childCount() function anywhere in a model - seems like you're mixing QTreeWidget with QTreeView... please show some code...
-
@Christian-Ehrlicher
i meant thechildCount()
method of the tree item.
okay, now i overrodeQAbstractItemModel::rowCount()
instead, but no luck. the expand icon isn't shownclass TreeItem : public TreeItemBase { public: TreeItem(const QList<QVariant> &data, TreeItemBase *parent = nullptr) : TreeItemBase(data, parent) { } TreeItem(const QString data, TreeItemBase *parent = nullptr) : TreeItemBase(data, parent) { } void setExpanded(bool bExpanded) { m_bExpanded = bExpanded; } bool isExpanded() const { return m_bExpanded; } private: bool m_bExpanded = false; }; // class TreeItem class TreeModel : public TreeModelBase { public: int rowCount(const QModelIndex &parent /* = QModelIndex() */) const { auto pNode = dynamic_cast<TreeItem *>(getItem(parent)); if (!pNode) return 0; return pNode->isExpanded() ? TreeModelBase::rowCount(parent) : 1; } }; // class TreeModel // later... void Tree::onItemExpanded(const QModelIndex &index) { // first, remove everything m_pTreeVewModel->clearChildren(index); auto pExpandedItem = m_pTreeVewModel->getItem(index); if (!pExpandedItem) return; static_cast<TreeItem *>(pExpandedItem)->setExpanded(true); // ... }
-
Again: childCount() is from QTreeWidgetItem, rowCount() from a model - these are two distinct approaches which can't be mixed.
-
@Christian-Ehrlicher
i got it, and that's why i overroderowCount()
in the above post -
This is working perfectly for me (don't take a look for the memleaks, it's a proof-of-concept):
class TreeItem { public: TreeItem(const QString &data, TreeItem *parent = nullptr) : m_data(data) , m_parent(parent) {} void setExpanded(bool bExpanded) { m_bExpanded = bExpanded; } bool isExpanded() const { return m_bExpanded; } void addChild(TreeItem *item) { m_children.push_back(item); } int childCount() const { return m_bExpanded ? m_children.size() : 1; } QVariant data() const { return m_data; } TreeItem *parent() const { return m_parent; } int indexOf(TreeItem *item) const { return m_children.indexOf(item); } TreeItem *childAt(int row) const { return (row >= 0 && row < m_children.size()) ? m_children.at(row) : nullptr; } private: QVariant m_data; TreeItem *m_parent; QVector<TreeItem*> m_children; bool m_bExpanded = true; }; class TreeModel final : public QAbstractItemModel { TreeItem *getItem(const QModelIndex &idx) const { return idx.isValid() ? static_cast<TreeItem*>(idx.internalPointer()) : m_root; } public: void setRootItem(TreeItem *root) { m_root = root; } int columnCount(const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent); return 1; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { const TreeItem *pNode = getItem(parent); int ret = pNode ? pNode->childCount() : 0; return ret; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if (role != Qt::DisplayRole) return QVariant(); TreeItem *pNode = getItem(index); return pNode ? pNode->data() : QVariant(); } QModelIndex parent(const QModelIndex &child) const override { TreeItem *pNode = getItem(child); TreeItem *pParent = pNode ? pNode->parent() : nullptr; if (pParent == m_root || pParent == nullptr) return QModelIndex(); TreeItem *pGrandParent = pParent->parent(); int row = pGrandParent->indexOf(pParent); return createIndex(row, 0, pGrandParent); } QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { const TreeItem *pParent = getItem(parent); TreeItem *pNode = pParent->childAt(row); if (!pNode) return QModelIndex(); return createIndex(row, column, pNode); } private: TreeItem *m_root = nullptr; }; // class TreeModel int main(int argc, char **argv) { QApplication app(argc, argv); TreeModel *model = new TreeModel; TreeItem *rootItem = new TreeItem(QString("root")); model->setRootItem(rootItem); for (int i = 0; i < 4; ++i) { auto item = new TreeItem(QString::number(i), rootItem); item->setExpanded(i % 2 == 0); rootItem->addChild(item); } QTreeView *tv = new QTreeView; tv->setModel(model); tv->show(); return app.exec(); }
-
@Christian-Ehrlicher
EDIT: don't bother to look at the code, i found the issue.auto pNode = dynamic_cast<TreeItem *>(getItem(parent));
always returnednullptr
. so i just removed therowCount()
override from the model.childCount()
of the item is enough.i have a base class tree and a derived one, from where i'm deleting the old tree model and using the new customized one. but in that case the view isn't populated. here's what i have:
class TreeModelBase : public QAbstractItemModel { Q_OBJECT public: TreeModelBase(const QString &data, QObject *parent = 0); TreeModelBase(const QList<QVariant> &columnsData, QObject *parent = 0); ~TreeModelBase() {delete rootItem;} virtual QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex &index) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; QModelIndex getIndex(const TreeItemBase *node, int column = 0) const; void insertChild(TreeItemBase *parent, TreeItemBase *child, bool notify = true, int row = -1); TreeItemBase* getItem(const QModelIndex &index) const; void clearChildren(const QModelIndex &parent); void resetModel(); void resetModel(const QList<QVariant> &columnsData); void updateModel(); TreeItemBase* getRootItem() const { return rootItem; } private: void setupModelData(const QStringList &lines, TreeItemBase *parent); TreeItemBase* rootItem; }; TreeModelBase::TreeModelBase(const QString &data, QObject *parent) : QAbstractItemModel(parent) { QList<QVariant> rootData; rootData << "Root"; rootItem = new TreeItemBase(rootData); setupModelData(data.split(QString("\n")), rootItem); } TreeModelBase::TreeModelBase(const QList<QVariant> &columnsData, QObject *parent) : QAbstractItemModel(parent) { rootItem = new TreeItemBase(columnsData); } int TreeModelBase::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return static_cast<TreeItemBase*>(parent.internalPointer())->columnCount(); return rootItem->columnCount(); } QVariant TreeModelBase::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); TreeItemBase *item = static_cast<TreeItemBase*>(index.internalPointer()); if (role == Qt::DisplayRole) return item->data(index.column()); if (role == Qt::ToolTipRole) return item->getToolTip(); if (item->isCheckable() && role == Qt::CheckStateRole) return item->getCheckState(); return QVariant(); } bool TreeModelBase::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; TreeItemBase *item = static_cast<TreeItemBase*>(index.internalPointer()); bool isUpdated = true; if (role == Qt::DisplayRole) { QList<QVariant> data; data << value; item->setItemData(data); } else if (role == Qt::ToolTipRole) item->setToolTip(value.toString()); else if (role == Qt::CheckStateRole) { // If role requested is the CheckStateRole. item->setCheckState((Qt::CheckState)value.toInt()); layoutChanged(); } else isUpdated = false; if (isUpdated == true) { // Emit a refresh signal. QModelIndex par = parent(index); QModelIndex topLeft = this->index(0, 0, par); int rc = rowCount(par); int cc = columnCount(par); QModelIndex bottomRight = this->index(rc - 1, cc - 1, par); emit dataChanged(topLeft, bottomRight); } return isUpdated; } Qt::ItemFlags TreeModelBase::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; TreeItemBase *item = static_cast<TreeItemBase*>(index.internalPointer()); return item->getFlags(); } QVariant TreeModelBase::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return rootItem->data(section); return QVariant(); } QModelIndex TreeModelBase::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItemBase *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItemBase*>(parent.internalPointer()); TreeItemBase *childItem = parentItem->childAt(row); if (childItem) return createIndex(row, column, childItem); return QModelIndex(); } QModelIndex TreeModelBase::parent(const QModelIndex &index) const { if (!index.isValid())return QModelIndex(); TreeItemBase *childItem = static_cast<TreeItemBase*>(index.internalPointer()); TreeItemBase *parentItem = childItem->parent(); if (parentItem == rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int TreeModelBase::rowCount(const QModelIndex &parent) const { TreeItemBase *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItemBase*>(parent.internalPointer()); return parentItem->childCount(); } QModelIndex TreeModelBase::getIndex(const TreeItemBase *pNode, int column) const { QModelIndex nodeIndex; // ... return nodeIndex; } // Insert new child to the node void TreeModelBase::insertChild(TreeItemBase *parent, TreeItemBase *child, bool notify, int row) { TreeItemBase *parentItem = parent ? parent : rootItem; if(!notify) { parentItem->insertChild(child, row); return; } QModelIndex parentIndex = getIndex(parentItem); if(row == -1) row = parentItem->childCount(); beginInsertRows(parentIndex, row, row); parentItem->insertChild(child,row); endInsertRows(); layoutChanged(); } TreeItemBase* TreeModelBase::getItem(const QModelIndex &index) const { return item = (index.isValid()) ? static_cast<TreeItemBase*>(index.internalPointer()):NULL; } void TreeModelBase::clearChildren(const QModelIndex &parent) { TreeItemBase *parentItem = parent.isValid() ? static_cast<TreeItemBase*>(parent.internalPointer()) : rootItem; int rows = parentItem->childCount(); if(rows > 0) { beginRemoveRows(parent, 0, rows - 1); parentItem->removeChildren(); endRemoveRows(); } layoutChanged(); } void TreeModelBase::resetModel() { beginResetModel(); rootItem->removeChildren(); endResetModel(); } void TreeModelBase::resetModel(const QList<QVariant> &columnsData) { beginResetModel(); rootItem->removeChildren(); delete rootItem; rootItem = new TreeItemBase(columnsData); endResetModel(); } void TreeModelBase::updateModel() { layoutChanged(); } class Tree { Q_OBJECT public: Tree(const QList<QVariant> &columnsData, QWidget *parent = nullptr); protected: layout::GUI::TreeModelBase *m_pTreeViewModel = nullptr; QTreeView *m_pTreeView = nullptr; QList<QVariant> m_lstColumnNames; }; Tree::Tree(const QList<QVariant> &columnsData, QWidget *parent /* = nullptr */) : QFrame(parent) , m_lstColumnNames(columnsData) { setContentsMargins(0, 0, 0, 0); auto mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); m_pTreeViewModel = TreeModelBase(m_lstColumnNames, this); m_pTreeView = new QTreeView(this); m_pTreeView->setUniformRowHeights(true); m_pTreeView->setModel(m_pTreeViewModel); connect(m_pTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Tree::onSelectionChanged); mainLayout->addWidget(m_pTreeView); } class TreeItem : public TreeItemBase { public: TreeItem(const QList<QVariant> &data, TreeItemBase *parent = 0, bool bCheckable = true) : TreeItemBase(data, parent, bCheckable) { } TreeItem(const QString data, TreeItemBase *parent = 0, bool bCheckable = true) : TreeItemBase(data, parent, bCheckable) { } void setExpanded(bool bExpanded) { m_bExpanded = bExpanded; } bool isExpanded() const { return m_bExpanded; } int childCount() const { return m_bExpanded ? 1 : TreeItemBase::childCount(); } private: bool m_bExpanded = false; }; // class TreeItem class TreeModel : public TreeModelBase { public: TreeModel(const QString &data, QObject *parent = 0) : TreeModelBase(data, parent) { } TreeModel(const QList<QVariant> &columnsData, QObject *parent = 0) : TreeModelBase(columnsData, parent) { } int rowCount(const QModelIndex &parent /* = QModelIndex() */) const override { auto pNode = dynamic_cast<TreeItem *>(getItem(parent)); if (!pNode) return 0; return pNode->childCount(); } }; // class TreeModel class OtherTree : public Tree { public: OtherTree(QWidget *parent = nullptr) : Tree(parent) { delete m_pTreeViewModel; m_pTreeViewModel = new TreeModel(getColumns(), this); m_pTreeView->setModel(m_pTreeViewModel); } void populateView() { m_pTreeViewModel->resetModel(getColumns()); auto pRootNode = m_pTreeViewModel->getRootItem(); while (whatever) { QList<QVariant> rowData; for (auto idx = 0; idx < getColumns().size(); ++idx) rowData << QVariant(); rowData[0] = someString; auto pNode = new TreeItem(rowData, pRootNode, false); pNode->setExpanded(true); m_pTreeViewModel->insertChild(pRootNode, pNode, true); } } };
with this code the view isn't populated. when i comment out the 3 lines in
OtherTree
constructor, the tree is populated but rows don't have expand icon.