PDA

View Full Version : Transparent QAbstractProxyModel for tree models



adzajac
27th October 2010, 09:26
Hi everybody.

I know that similar posts were here on the forum but as I saw usually threads were finished with the conclusion that maybe different model should be used instead of QAbstractProxyModel.

The problem :
Right now I've a tree model based on QAbstractItemModel which is used to hold objects of TreeModelItem type ( obvious isn't it ? ). There is no problem to create such a model here is the model and item code.

BaseTreeModel.cpp


#include "BaseTreeModel.h"
#include "TreeModelItem.h"
#include <QDebug>

BaseTreeModel::BaseTreeModel() {
// fake structure
QList<QVariant *> fakeData1;
fakeData1.push_back(new QVariant("fakeData11"));
fakeData1.push_back(new QVariant("fakeData22"));


QList<QVariant *> fakeData2;
fakeData2.push_back(new QVariant(1111));
fakeData2.push_back(new QVariant(2222));


rootItem = new TreeModelItem(fakeData1, 0);

for (int i = 0;i<1;i++){
TreeModelItem *item = new TreeModelItem(fakeData1, rootItem);
rootItem->appendChild(item);
for (int j=0;j<1;j++)
item->appendChild( new TreeModelItem(fakeData2, item) );
}

}

BaseTreeModel::~BaseTreeModel() {
}

// Minimum implementation for ReadOnly model
QModelIndex BaseTreeModel::index ( int row, int column, const QModelIndex & parent ) const {
if (!hasIndex(row, column, parent))
return QModelIndex();

TreeModelItem *parent_item;
if (!parent.isValid()){
parent_item = rootItem;
} else {
parent_item = static_cast<TreeModelItem*>(parent.internalPointer());
}

TreeModelItem *child_item = parent_item->childAt(row);

if (child_item) {
return createIndex(row, column, child_item);
} else {
return QModelIndex();
}
}

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

TreeModelItem* child_item = static_cast<TreeModelItem*>(index.internalPointer());
TreeModelItem* parent_item = child_item->parent();

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

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

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

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

TreeModelItem* item = static_cast<TreeModelItem*>(index.internalPointer());
QModelIndex p = index.parent();

if( item->childCount() && index.column() != 0)
return QVariant();
else
return item->data( index.column() );

}

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


}

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

int BaseTreeModel::rowCount ( const QModelIndex & parent ) const {
TreeModelItem* parent_item;
if (parent.column() > 0)
return 0;

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

return parent_item->childCount();
}

bool BaseTreeModel::hasChildren ( const QModelIndex & parent ) const {
if ( parent.isValid() ) {
bool retVal = static_cast<TreeModelItem*>(parent.internalPointer())->childCount();
return retVal;
} else {
return (bool)(rootItem->childCount());
}
}

Qt::ItemFlags BaseTreeModel::flags ( const QModelIndex & index ) const {
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}


BaseTreeModel.h


class TreeModelItem;

#ifndef BASETREEMODEL_H
#define BASETREEMODEL_H

#include <QAbstractItemModel>

class BaseTreeModel : public QAbstractItemModel {
public :
BaseTreeModel();
~BaseTreeModel();

// Minimum implementation for ReadOnly model
virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const;
virtual QModelIndex parent ( const QModelIndex & index ) const ;
virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const ;
virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const ;
virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const ;
virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const ;
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const ;

private :
// internal model structure let's name it cache :)
TreeModelItem *rootItem;

};

#endif // BASETREEMODEL_H


And the model item that is stored internally
TreeModelItem.cpp


#include "TreeModelItem.h"
#include <QDebug>

TreeModelItem::TreeModelItem(QList<QVariant *> data, TreeModelItem *parent) : itemData(data), parentItem(parent) {

}

TreeModelItem::~TreeModelItem(){

}

void TreeModelItem::appendChild(TreeModelItem * child){
childItems.append(child);
}

void TreeModelItem::removeChild(TreeModelItem * child){
childItems.removeOne( child );
}

TreeModelItem * TreeModelItem::childAt(int row){
if ( childItems.count() > row )
return childItems.at( row );
else
return NULL;
}

int TreeModelItem::childCount() const {
return childItems.count();
}

int TreeModelItem::columnCount() const {
return itemData.count();
}

QVariant TreeModelItem::data(int column) const {
return *itemData.at( column );
}

int TreeModelItem::row() const {
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeModelItem*>(this));

return 0;
}

TreeModelItem * TreeModelItem::parent(){
return parentItem;
}

void TreeModelItem::clear(){
this->itemData.clear();
}


TreeModelItem.h


#ifndef TREEMODELITEM_H
#define TREEMODELITEM_H

#include <QList>
#include <QVariant>

class TreeModelItem {
public:
TreeModelItem(QList<QVariant *> data, TreeModelItem *parent = 0);
~TreeModelItem();
void appendChild(TreeModelItem * child);
void removeChild(TreeModelItem * child);

TreeModelItem *childAt(int row);

int childCount() const;
int columnCount() const;
QVariant data(int column) const;
int row() const;

TreeModelItem *parent();
void updateColumn(int column, QVariant *data);
void updateItem( const QMap<int, QVariant *> &data);
void clear();

private:
QList<TreeModelItem *> childItems;
QList<QVariant *> itemData;
TreeModelItem *parentItem;
};

#endif // TREEMODELITEM_H


This tree is working. The structure right now is build in the constructor but only for the test purposes.

I want to create the base proxy model which will be transparent. I don't to have any fancy things inside at the firs step. If I've take QSortFilterProxyModel everything is Ok. But with QAbstractProxyModel it wan't work. I've implemented all the necessary virtual methods but I think that I'm doing this wrong. So the first step is to create transparent proxy based on QAbstaractProxyModel to understand the concept of source <-> proxy mapping.
Then later I want to create proxy for different aggregation/grouping, model flatten to table, adding a virtual columns etc..

Could somebody can help me with this issue to guide how to implement mapToSource, mapFromSource and index, parent etc... in this proxy on my model arch.

wysota
27th October 2010, 10:13
For tree models these two methods are quite complex so it is really easier to base your proxy model on QSortFilterProxyModel. If you really want to implement your own mapping then you have to come up with a mapping algorithm. You will probably need some internal map that will contain enough information to map between source and target models and you would have to update this map every time anything changes in the base model. But since QSortFilterProxyModel already does that I don't see a point in repeating it especially that it's really not something you can implement in 5 minutes.

adzajac
27th October 2010, 10:38
Yes I know that.

The first issue that I want to fix is a behaviour of the branch column. It looks strange when you've a branch column somewhere in the middle in view. So my idea is to create a proxy which will be able to hole branch column always on the most left side and if the user will want to swap the column with this particular one the proxy should change the indexes in the way that data that was before in the branch column should be shown in the swapped column and the data from this column which is dropped to the branch should be presented with with column zero.

I don't know that I'm clear enough ?

Theoretically it should be possible with QSortFilterProxyModel when we reimplement index and data an I right ? Maybe this is the right way ?

wysota
27th October 2010, 10:41
I would do it by reimplementing the view that is responsible for drawing the branches but what do I know...

adzajac
27th October 2010, 10:56
Yes maybe this is the god idea, but it's even less described in doc than models sub classing :( It should be rather a combination of proxy and view. Where in view column moving policy should be changed. To not allow of the most left column moving. But only the other column will be allowed to drop in to it. Ill try.

By the way have you read a priv. message that I've sent to you this morning (this one written in Polish) ?

wysota
27th October 2010, 11:03
Yes maybe this is the god idea, but it's even less described in doc than models sub classing :( It should be rather a combination of proxy and view. Where in view column moving policy should be changed. To not allow of the most left column moving. But only the other column will be allowed to drop in to it. Ill try.
If you want to prevent a column from being moved then do just that. It's enough to subclass QHeaderView and prevent the first column from being moved.


By the way have you read a priv. message that I've sent to you this morning (this one written in Polish) ?
It's a visitor message and not a private message. I'll read it in a moment.