2016-04-01 2 views
1

Я новичок в Qt, и я пытаюсь сделать простое дерево на основе плоский (или из таблицы SQLite) файла список путей (не из ФС), как это:QTreeView и QAbstractItemModel

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 ? 

      } 
     } 
    } 
    //... 
} 

Любые советы?

+0

Добро пожаловать в StackOverflow! Пожалуйста, поделитесь тем, что вы пробовали, чтобы другие могли помочь вам лучше. –

ответ

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} 
    

Шаг 2: Модель

Если у вас есть этот список, вам нужно реализовать по крайней мере, одним из следующих способов:

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 & 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; 
}; 

Вы должны быть в состоянии быстро получить 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); 
} 
+0

Спасибо! Да, моя главная проблема - это часть моделирования здесь ... Я точно не знаю, как реализовать ваш шаг 1. – Orkblutt

+0

Сделайте это вручную на листе бумаги. нарисуйте дерево с одного пути (подсказка, это связанный список). попробуйте перейти от полной строки к этому связанному списку ... что следующий шаг после разделения 'C: \ Parent1 \ Child1 \ Leaf1.jpg' на' C: ',' ​​Parent1', 'Child1',' Leaf1. jpg' ??? Посмотрите на алгоритмы для стандартных структур данных ... и если вы все еще застряли, задайте конкретный алгоритм на SO. Это хорошее упражнение ... – UmNyobe

1

Большого спасибо 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); 
        } 
       } 
      } 
     } 
    } 
} 

Я думаю, что его можно улучшить, но он выполняет свою работу. Я жду предложений.

Смежные вопросы