Russian Qt Forum

Qt => Model-View (MV) => Тема начата: spbcypher от Август 19, 2010, 15:18



Название: QAbstractItemModel - index и parent
Отправлено: spbcypher от Август 19, 2010, 15:18
пример из Johan Thelin - Foundations of QT Development[2007]

Код:
QModelIndex ObjectTreeModel::index(int row, int column, const QModelIndex &parent ) const
{
QObject *parentObject;
if( !parent.isValid() )
parentObject = m_root;
else
parentObject = static_cast<QObject*>( parent.internalPointer() );
if( row >= 0 && row < parentObject->children().count() )
return createIndex( row, column, parentObject->children().at( row ) );
else
return QModelIndex();
}

подскажите пожалуйста как организовать функцию index если данные разного уровня вложенности в древовидной модели имеют разные типы и нет единого родительского элемента?

т.е. структура данных приблизительно следующая

марка: [id title]
  модель: [id title]
    двигатель: [id kw year и пр.]
    двигатель
  модель
  ....
марка
  ....


Название: Re: QAbstractItemModel - index и parent
Отправлено: Авварон от Август 19, 2010, 15:43
единый родительский элемент есть всегда - узел дерева


Название: Re: QAbstractItemModel - index и parent
Отправлено: spbcypher от Август 19, 2010, 17:17
Код:
QVariant AutoModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || !(role == Qt::EditRole || role == Qt::DisplayRole)) return QVariant();

    int level = 0;
    QModelIndex temp_index = index;
    while (temp_index.parent().isValid()) {
        level++; temp_index = temp_index.parent();
    }

    // марки автомобилей
    if (level == 0) {
        if (index.row() >= brands->count()) {
            int start = brands->count(); // с какой строки подкачивать
            int count = index.row()-brands->count()+1; // сколько строк подкачивать
            brands->append(BrandsGetDB(start,count));
        }
        if (index.row() >= brands->count()) return QVariant();
        return QVariant::fromValue(brands->at(index.row()));
    } else

    // модели автомобилей
    if (level == 1) {
        int brand_id = brands->at(index.parent().row()).id;
        if (index.row() >= models->take(brand_id).count()) {
            int start = models->take(brand_id).count(); // с какой строки подкачивать
            int count = index.row()-models->take(brand_id).count()+1; // сколько строк подкачивать
            models->take(brand_id).append(ModelsGetDB(start,count,brand_id));
        }
        if (index.row() >= models->take(brand_id).count()) return QVariant();
        return QVariant::fromValue(models->take(brand_id).at(index.row()));
    }
}

Если наследовать класс AutoModel от QAbstractListModel, то выдаётся только первый уровень (марки), второй (модели) выдаёт false на index.isValid()

данные по логике приложения хранятся в следующих структурах


Код:
namespace AUTO
{
    struct Brand {
        int id;
        QString name;
    };
    struct Model {
        int id;
        QString name;
    };
    struct Engine {
        int id;
        QString name;
        int KW;
    };
}

typedef QList<AUTO::Model> QListModels;
typedef QList<AUTO::Engine> QListEngines;

brands = new QList<AUTO::Brand>;
models = new QMap<int,QListModels>;
engines = new QMap<int,QListEngines>;

подскажите пожалуйста где ошибка и как в данном случае с индексами оперировать?  ???

UPD: логика приложения подразумевает подкачку с базы при отсутсвии данных


Название: Re: QAbstractItemModel - index и parent
Отправлено: fuCtor от Август 19, 2010, 19:34
Как вариант завести вспомогательную структуру, которая будет строить дерево, т.е. содержать массив потомков и указатель на родителя.
Так же узел содержит тип узла и идентификатор объекта этого узла.
Таким образом можно грузить разнородные данные в виде дерева.

При создании индекса проверяете тип родителя и принимаете решение потомков какого типа порождать.


Название: Re: QAbstractItemModel - index и parent
Отправлено: Авварон от Август 19, 2010, 20:29
в случае, если индексы на разных уровнях хранят разные данные напрямую, нужно как-то различать эти индексы. В голову приходит: метод level который поднимается по парентам и высчитывает глубину; хранение типа в каждой структуре и выцепление по оффсету/каст к интерфейсу общему для структур. Оба способа кривые, посему надо делать как сказал fuCtor:
struct Node { Node *parent, QList<Node*> children; int type; void *data; }
Собственно дерево модели строится из нодов, а данные нод содержит исходя из своего типа.


Название: Re: QAbstractItemModel - index и parent
Отправлено: fuCtor от Август 19, 2010, 20:47
Еще замечу, что если данные хранятся в БД, то строить все дерево сразу нет необходимости. На каждом этапе только достраивается тот участок, что требуется, таким образом экономия времени на запросах. Некоторые запросы можно кешировать в узлах (например количество потомков, которые могут быть потом загружены).


Название: Re: QAbstractItemModel - index и parent
Отправлено: spbcypher от Август 19, 2010, 22:36
Q_DECLARE_METATYPE не позволяет определять тип из структуры с конструктором :(
в итоге перед передачей в метод setData нужно засунуть в обычную с полем-типом, а внутри засовывать в структуру дерева с конструктором, чтобы инициализировать указатели на связанные объекты..матрёшка, блин


Название: Re: QAbstractItemModel - index и parent
Отправлено: fuCtor от Август 20, 2010, 06:45
а зачем регистрировать тип? вы его куда засовывать собираетесь?

Индекс строится методом createIndex(строка, столбец, int|void*) и ни надо никаких мета типов.
Потом просто получаете указатель index.innerPointer(), кастуете к своей структуре и будет вам счастье.


Название: Re: QAbstractItemModel - index и parent
Отправлено: spbcypher от Август 20, 2010, 10:22
а метод setData, внутри него уже будет QVariant который надо будет как-то распознать что это - в конкретной ситуации марка, модель или двигатель


Название: Re: QAbstractItemModel - index и parent
Отправлено: Maquefel от Август 20, 2010, 12:00
а метод setData, внутри него уже будет QVariant который надо будет как-то распознать что это - в конкретной ситуации марка, модель или двигатель

Полиморфизм тебе в помощь:
Код:
enum TreeItemTypes {
    Root, Mark, Model
};

class TreeItem : public QObject
{
    Q_OBJECT
public:
// LIFECYCLE
   /** Default constructor.
    */
   TreeItem(TreeItemTypes type, TreeItem* parent = 0);
   virtual ~EGTreeItem(void);
// OPERATORS
// OPERATIONS
// ACCESS
// INQUIRY
   virtual int subItemsCount() const {return 0;}
   virtual int childCount() const;
   virtual int columnCount() const;

   void appendChild(TreeItem* child);
   TreeItem* takeLast();

   virtual void deleteChildren();

   virtual TreeItemTypes getType() {return type;}

   TreeItem* child(int row);
   TreeItem* parent();
   virtual QVariant data(int column) const;
   int row() const;

   virtual bool setData(int column, const QVariant &value);
   virtual void expand(){;}
public slots:
protected:
   TreeItemTypes type;
   QList<TreeItem*> childItems;
   TreeItem* parentItem;
   QList<QVariant> itemData;
};

Наследуешь, от общего класса предка и переопределяешь методы, допустим для каждого своя реализация метода setData;


Название: Re: QAbstractItemModel - index и parent
Отправлено: Авварон от Август 20, 2010, 12:54
ой, наследовать айтем от куобжекта, ужас...


Название: Re: QAbstractItemModel - index и parent
Отправлено: Maquefel от Август 20, 2010, 12:58
ой, наследовать айтем от куобжекта, ужас...

Почему это?


Название: Re: QAbstractItemModel - index и parent
Отправлено: Авварон от Август 20, 2010, 13:01
ваша модель будет весить килотонны и создаваться медленно (как минимум в 2раза медленней за счет new QObjectPrivate)


Название: Re: QAbstractItemModel - index и parent
Отправлено: Maquefel от Август 20, 2010, 13:07
ваша модель будет весить килотонны и создаваться медленно (как минимум в 2раза медленней за счет new QObjectPrivate)

а мне сигнал/слоты нужны, не нужны были бы не наследовал, это раз, во-вторых qobject_cast, который быстрее dynamic_cast.

Но в целом вы правы - в примере он не нужен.




Название: Re: QAbstractItemModel - index и parent
Отправлено: Авварон от Август 20, 2010, 13:14
а, я просто в вашем примере не увидел ни сигналов ни слотов, тогда да) + это имеет смысл если дерево маленькое а данных много и можно к ним прикрутить хитрый механизм обновления через сигналы. Однако мой опыд подсказывает что лучше всего маааленький TreeItem.
жаль в qt нет темплейт класса для деревьев


Название: Re: QAbstractItemModel - index и parent
Отправлено: spbcypher от Август 20, 2010, 13:40
данных много, а структура неочень разветвлённая. В моей реализации данные берутся из внутренних списков, а при отсутствии подкачиваются в методе data в эти же списки <хотя я у меня рождаются мысли что я слишком усложнил себе задачу - грубо говоря треба реализовать редактирование связанных данных в бд>