【问题标题】:QTreeView and QAbstractItemModelQTreeView 和 QAbstractItemModel
【发布时间】:2016-07-20 22:55:49
【问题描述】:

我是 Qt 新手,我正在尝试基于平面(或来自 sqlite 表)文件路径列表(不是来自 FS)创建一个简单的树,如下所示:

C:\Parent1\Child1\Leaf1.jpg
C:\Parent1\Child1\Leaf2.jpg
C:\Parent1\Child1\Leaf3.jpg
C:\Parent1\Child2\Leaf1.jpg
C:\Parent1\Child2\Leaf2.jpg
C:\Parent2\Child1\Leaf1.jpg
C:\Parent2\Child1\Leaf2.jpg
...
D:\....
...

我想将其显示为树视图(例如文件浏览器)。

我查看了 QAbstractItemModel,但在构建树时遇到了一些困难。 我的想法是使用'\' 拆分每个路径,并在添加它们之前检查分支是否已经存在。如果分支存在,我必须找到好的父母来添加这个新孩子。

我正在使用a simple tree example,但我很难实现我的模型。

void MyTreeModel::setupModelData(TreeItem *parent)
{
    QList<TreeItem*> parents;
    parents << parent;

    int number = 0;

    QString path = "mydb_path";
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(path);
    if(db.open())
    {
        QSqlQuery query("SELECT path FROM file");
        int idPath = query.record().indexOf("path");

        while (query.next())
        {
           QString name = query.value(idPath).toString();
           int id_file = query.value(idIdx).toInt();

           QStringList nodeString = name.split("\\", QString::SkipEmptyParts);


           for(int node = 0; node < nodeString.count(); ++node)
           {
            // I don't know what to do now... Should I build a lookup table with a parent id for each node before to populate my TreeItems ?

           }
        }
    }
    //...
}

有什么建议吗?

【问题讨论】:

  • 欢迎来到 StackOverflow!请分享您尝试过的代码,以便其他人可以更好地帮助您。

标签: qt qabstractitemmodel


【解决方案1】:

使用QAbstractModelItem 并不直观。但看起来你最大的问题实际上是建模你的路径列表到元素的中。有两个任务:

  1. 处理您的输入数据以获得数据结构,关闭您对数据的概念解释(您知道它们是路径。)
  2. 在您的 QAbstractItemModel 实现下使用此数据结构。

第 1 步:实际的树实现

您需要先实现一棵树。类似的东西

struct mytree
{
    static std::shared_ptr<mytree> frompath(QString path);
    static std::shared_ptr<mytree> merge(std::shared_ptr<mytree> t1, std::shared_ptr<mytree> t2);

    //may need helpers : is leaf, etc, or just access to children
    QString value;
    std::list<std::shared_ptr<mytree> > childrens_;

    mytree(); //construct empty tree
};

其中值是文件名或文件夹名。

  • frompath 从单个条目构建树。C:\Parent2\Child1\Leaf2.jpg 变为

    C->Parent2->Child1->Leaf2.jpg
    
  • merge 取两棵现有的树并构造一棵

    C->Parent2->Child1->Leaf1.jpg
    C->Parent2->Child2->Leaf1.jpg
    

    变成

    C->Parent2->{Child1->Leaf1.jpg, Child2->Leaf1.jpg}
    

第二步:模型

获得该列表后,您至少需要实现以下方法:

QModelIndex parent(const QModelIndex & index) const;
QModelIndex index(int row, int column, const QModelIndex & parent) const;

int columnCount(const QModelIndex & parent) const;
int rowCount(const QModelIndex & parent) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;

一些提示:

  1. 无效的模型索引是QModelIndex()。顶级项目有QModelIndex
  2. 您必须将一个或多个QModelIndex 分配给对象mytree
  3. 任何父级都只有一列,因为只有一个数据(文件名)。
  4. 行数就是子级数。
  5. QModelIndex 是使用createIndex 创建的

现在要能够实现这些方法,您需要向树中添加 3 个字段:

  • 对父级的引用
  • 当前元素在父列表中的位置
  • 一个id字段,唯一标识一个节点。您可以在路径上使用uint qHash(const QString &amp; key)

新树有字段

struct mytree
{
    //removed methods;
    QString path;          eg C:\Parent1\Child1
    QString value;         eg Child1
    unsigned int id;       eg: qHash(path)

    int pos; //my position in the parent list. this is the row in the model

    std::shared_ptr<mytree> parent;      
    std::list<std::shared_ptr<mytree> > childrens;
};

您需要能够根据其 ID 快速获取 mytree。这意味着您在模型中的内部数据结构将是

std::map<unsigned int, std::shared_pr<mytree> > items_by_id;
std::shared_pr<mytree> root_item;

现在您已经拥有了实现上述方法所需的一切。这只是为了演示,所以不要认为这个代码是理所当然的

std::shared_pr<mytree> find_elt_helper(const QModelIndex & index) const
{   
   auto iter = items_by_id.find(index.internalId());
   if(iter == items_by_id.end())
       return std::shared_pr<mytree>();

   return iter->second;
}


QModelIndex parent(const QModelIndex & index) const
{
   if(!index.isValid())
       return QModelIndex();

   std::shared_pr<mytree> index_item = find_elt_helper(index);

   return index_item ? create_index(index_item->parent->pos, 0, index_item->parent->id) : QModelIndex();
}

QModelIndex index(int row, int column, const QModelIndex & parent) const
{
    std::shared_pr<mytree> parent_item = !parent.isValid() ? root_item : find_elt_helper(parent);

    std::shared_pr<mytree> item;
    if(row >= parent_item.children.size())
       return QModelIndex();

    item = parent_item.children[row];

    return create_index(row, 0, item->id);
}

【讨论】:

  • 谢谢!是的,我的主要问题是这里的建模部分......我不确定如何实施你的第 1 步。
  • 在一张纸上手工完成。从单个路径绘制树(提示,它是一个链表)。尝试从完整字符串转到该链接列表...在将C:\Parent1\Child1\Leaf1.jpg 拆分为C:Parent1Child1Leaf1.jpg 之后的下一步是什么?查看标准数据结构的算法......如果您仍然卡住,请询问关于 SO 的特定算法问题。这是一个很好的锻炼......
【解决方案2】:

非常感谢 UmNyobe 让我头脑清醒……我有点困惑。 这是我的实现:

treeitem.h

#ifndef TREEITEM_H
#define TREEITEM_H


#include <QList>
#include <QVariant>

class TreeItem
{
public:
    explicit TreeItem(const QList<QVariant> &data, TreeItem *parentItem = 0, unsigned int id = 0);
    ~TreeItem();

    void appendChild(TreeItem *child);

    TreeItem *child(int row);
    int childCount() const;
    int columnCount() const;
    QVariant data(int column) const;
    int row() const;

    unsigned int getIndex(){return _id;};

    TreeItem *parentItem();

private:

    QList<TreeItem*> m_childItems;
    QList<QVariant> m_itemData;
    TreeItem *m_parentItem;
    unsigned int _id;
};


#endif // TREEITEM_H

treeitem.cpp

#include <QStringList>

#include "treeitem.h"

TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent, unsigned int id)
{
    m_parentItem = parent;
    m_itemData = data;
    _id = id;
}

TreeItem::~TreeItem()
{
    qDeleteAll(m_childItems);
}

void TreeItem::appendChild(TreeItem *item)
{
    m_childItems.append(item);
}

TreeItem *TreeItem::child(int row)
{
    return m_childItems.value(row);
}

int TreeItem::childCount() const
{
    return m_childItems.count();
}

int TreeItem::columnCount() const
{
    return m_itemData.count();
}

QVariant TreeItem::data(int column) const
{
    return m_itemData.value(column);
}

TreeItem *TreeItem::parentItem()
{
    return m_parentItem;
}

int TreeItem::row() const
{
    if (m_parentItem)
        return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));

    return 0;
}

restoretreemodel.h

#ifndef RESTORETREEMODEL_H
#define RESTORETREEMODEL_H

#include "treeitem.h"
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>


class RestoreTreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    explicit RestoreTreeModel(QObject* parent=0);
    ~RestoreTreeModel();

    QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
    Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
    QVariant headerData(int section, Qt::Orientation orientation,
                            int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
    QModelIndex index(int row, int column,
                          const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
    int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;

private:
    void setupModelData(TreeItem *parent);
    int findNode(unsigned int& hash, const QList<TreeItem*>& tList);

    TreeItem *rootItem;

};

#endif // RESTORETREEMODEL_H

restoretreemodel.cpp

#include "restoretreemodel.h"
#include <QStringList>
#include <QtSql>


RestoreTreeModel::RestoreTreeModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    QList<QVariant> rootData;
    rootData << "Nom" << "Nombre d'éléments";
    rootItem = new TreeItem(rootData);
    setupModelData(rootItem);
}

RestoreTreeModel::~RestoreTreeModel()
{
    delete rootItem;
}

int RestoreTreeModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
    else
        return rootItem->columnCount();
}

QVariant RestoreTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

    return item->data(index.column());
}

Qt::ItemFlags RestoreTreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    return QAbstractItemModel::flags(index);
}

QVariant RestoreTreeModel::headerData(int section, Qt::Orientation orientation,
                               int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return rootItem->data(section);

    return QVariant();
}

QModelIndex RestoreTreeModel::index(int row, int column, const QModelIndex &parent)
            const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    TreeItem *parentItem;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());

    TreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex RestoreTreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
    TreeItem *parentItem = childItem->parentItem();

    if (parentItem == rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int RestoreTreeModel::rowCount(const QModelIndex &parent) const
{
    TreeItem *parentItem;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());

    return parentItem->childCount();
}

int RestoreTreeModel::findNode(unsigned int& hash, const QList<TreeItem*>& tList)
{
    for(int idx = 0; idx < tList.size(); ++idx)
    {
        unsigned int z = tList.at(idx)->getIndex();
        if(z == hash)
            return idx;
    }

    return -1;
}



void RestoreTreeModel::setupModelData(TreeItem *parent)
{
    QList<TreeItem*> parents;
    parents << parent;

    QString path = "my_db_path";
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(path);
    if(db.open())
    {
        QSqlQuery query("SELECT path, id_file FROM file");
        int idPath = query.record().indexOf("path");
        int idIdx = query.record().indexOf("id_file");

        while (query.next())
        {
           QString name = query.value(idPath).toString();
           int id_file = query.value(idIdx).toInt();

           QStringList nodeString = name.split("\\", QString::SkipEmptyParts);

           QString temppath = "";

           int lastidx = 0;
           for(int node = 0; node < nodeString.count(); ++node)
           {
               temppath += nodeString.at(node);
               if(node != nodeString.count() - 1)
                   temppath += "\\";

               unsigned int hash = qHash(temppath);
               QList<QVariant> columnData;

               columnData << nodeString.at(node);

               int idx = findNode(hash, parents);

               if(idx != -1)
               {
                    lastidx = idx;
               }
               else
               {
                   QString sQuery =  "";
                   if(node == nodeString.count() - 1)
                   {
                       sQuery += "SELECT count(*) FROM version WHERE id_file=";
                       sQuery += QString::number(id_file);
                       sQuery += ";";
                   }
                   else
                   {

                       sQuery += "SELECT count(*) FROM file WHERE path like '";
                       sQuery += temppath;
                       sQuery += "%';";
                   }


                   int nChild = 0;
                   QSqlQuery query2(sQuery);

                   if(query2.next())
                        nChild = query2.value(0).toInt();

                   columnData << nChild;

                   if(lastidx != -1)
                   {
                       parents.at(lastidx)->appendChild(new TreeItem(columnData, parents.at(lastidx), hash));
                       parents <<  parents.at(lastidx)->child( parents.at(lastidx)->childCount()-1);
                       lastidx = -1;
                   }
                   else
                   {
                       parents.last()->appendChild(new TreeItem(columnData, parents.last(), hash));
                       parents <<  parents.last()->child( parents.last()->childCount()-1);
                   }
               }
           }
        }
    }
}

我认为它可以改进,但确实可以。我正在等待任何建议。

【讨论】:

    猜你喜欢
    • 2011-09-29
    • 1970-01-01
    • 2014-03-26
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-16
    相关资源
    最近更新 更多