PDA

View Full Version : Always show QTreeview branches in the far left column



Nightfox
13th January 2010, 20:28
I'm working with a Qtreeview and I'm facing some problems having the treview shown when either the first column is hidden or when I rearrange the tree.

I'm using a custom model where I've tried to put in a 'branch column' (m_branchcolumn see below) to map the parent to the first visible column in the treeview but it doesn't work. I always see the treeview nodes (the plus and minus signs) in column zero. I'm using an internal record representation like the nodeitem from the simple treeview example shipped with Qt.


QModelIndex ViewModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();

Record *rec = recordFromIndex(child);
if (!rec)
return QModelIndex();

Record *parentRecord = rec->parent;
if (!parentRecord)
return QModelIndex();

if(parentRecord == rootRecord)
return QModelIndex();

Record *grandparentRecord = parentRecord->parent;

if (!grandparentRecord)
return QModelIndex();

int row = grandparentRecord->children.indexOf(parentRecord);
return createIndex(row, m_branchcolumn, parentRecord);
}

How can I make sure always to show the tree branches in the far left visible column even after column change and with hidden columns?

wysota
13th January 2010, 21:37
By definition QTreeView displays branches in the column with logical index '0' (or the lowest non-hidden column probably but I'm not sure). To override that you have to either provide a proxy model that will rearrange your columns or reimplement QTreeView.

Nightfox
13th January 2010, 22:06
Thanks for your reply wysota


By definition QTreeView displays branches in the column with logical index '0' (or the lowest non-hidden column probably but I'm not sure).
Yes, QTreeview displays branches in the column with logical index '0' but if that column is hidden the tree nodes will not appear. Tree effect is preserved however as the user is still able to expand/subtract with plus and minus keys.


To override that you have to either provide a proxy model that will rearrange your columns or reimplement QTreeView.
Acctually I am using a proxymodel for the treeview already. How would I go about rearranging the columns to get the effect I want?

Thanks

wysota
13th January 2010, 22:14
The easiest would be to reimplement data() and call the base class implementation of the proxy with a modified index (switched column numbers).

Nightfox
13th January 2010, 22:54
This is my reimplementation! I've tried to change the index but as it's a const function all my changes resolves in error. What would I need to change in this data function to swap say column 0 with column 1?


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

return QAbstractProxyModel::data(index, role);
}

Thank you again for your help!

wysota
13th January 2010, 23:05
Create a new index, don't change the original one (it's const...).

For example:

QModelIndex changed = index.sibling(index.row(), (index.column()+1) % 10);

Nightfox
13th January 2010, 23:56
It definitely one way to go but it gets messy. If I make changes to the 0 column the changes will be saved in the 'swap' column. Eventually I'll have to accommodate this change in index, parent, mapToSource, mapFromSource aswell right?

Below I'm substituting the branch column with 0 column and vica versa.

This is my new implementation;

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

if(index.column() == m_branchcolumn)
{
QModelIndex changed = index.sibling(index.row(), 0);
return QAbstractProxyModel::data(changed, role);
}
if(index.column() == 0)
{
QModelIndex changed = index.sibling(index.row(), m_branchcolumn);
return QAbstractProxyModel::data(changed, role);
}
return QAbstractProxyModel::data(index, role);
}

I haven't gotten it to work correctly yet but I think this might be the way forward. Thanks for you input.

wysota
14th January 2010, 08:00
It definitely one way to go but it gets messy. If I make changes to the 0 column the changes will be saved in the 'swap' column. Eventually I'll have to accommodate this change in index, parent, mapToSource, mapFromSource aswell right?

It depends on your model.

Nightfox
14th January 2010, 21:26
The model is a reimplemented QAbstractProxyModel. This shows you the functions I've reimplemented so far.



virtual int columnCount (const QModelIndex& parent = QModelIndex()) const ;
virtual int rowCount(const QModelIndex& parent) const ;

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

virtual QModelIndex mapFromSource(const QModelIndex&) const ;
virtual QModelIndex mapToSource(const QModelIndex&) const ;
virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const;
virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;

Surely I need to swao the index for all these functions?

wysota
14th January 2010, 22:00
If you are already implementing a proxy model then everything is just a matter of adjusting your mapFromSource() and mapToSource() methods. In the other methods you surely already use those two so everything should work fine.

Nightfox
14th January 2010, 22:38
This gets more and more messy! I've implemented this to swap column 0 with the branch column in order to always have the branches in the far left column, even after user column rearrange.

However after the change in mapFramSource and mapToSource no data is displayed in the tree.


QModelIndex MyProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
{
if (sourceIndex.isValid())
{
int key = sourceModel()->data(sourceModel()->index(sourceIndex.row(), m_sourcekeycolumn,QModelIndex()) ).toInt() ;
Record *rec = internaltable->m_map.value(key);
int row = 0;

if(rec->parent)
row = rec->parent->children.indexOf(rec);
const int brcol = getBranchColumn();
if( sourceIndex.column() == brcol ) {
QModelIndex changed = sourceIndex.sibling(row, 0); //new
return changed;
}
if( sourceIndex.column() == 0 ){
QModelIndex changed = sourceIndex.sibling(row, brcol ); //new
return changed;
}
return createIndex(row,sourceIndex.column(),rec) ;
}
return QModelIndex();
}
//
QModelIndex MyProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
if(proxyIndex.isValid())
{
Record *rec = recordFromIndex(proxyIndex);
const int brcol = getBranchColumn();
if(rec)
{
if( proxyIndex.column() == brcol ) {
QModelIndex changed = proxyIndex.sibling(proxyIndex.row(), 0); //new
return changed;
}
if( proxyIndex.column() == 0 ){
QModelIndex changed = proxyIndex.sibling(proxyIndex.row(), brcol ); //new
return changed;
}
}
}
return QModelIndex() ;
}

My model hieracy is QStandarItemModel to QAbstractProxyModel to QSortFilterProxyModel so the source model for the treeview is QSortFilterProxyModel. Why can't I squeeze more functionality out of the two extra proxy models to archive the column swap? I had really hoped for more help from one of the proxies!

What an I doing wrong?

Rembobo
15th January 2010, 07:17
I am not sure if I correctly understood the task but is it not be easier to manipulate QHeaderView (http://qt.nokia.com/doc/4.6/qheaderview.html)?
For example.


QFileSystemModel *model = new QFileSystemModel(ui->m_tv);
model->setRootPath("C:\\");
ui->m_tv->setModel(model);
int sectionCount = ui->m_tv->header()->count();
qDebug() << "Section count:" << sectionCount;
QHeaderView *hv = ui->m_tv->header();
hv->moveSection(hv->visualIndex(0), hv->visualIndex(sectionCount - 1));
hv->setSectionHidden(hv->logicalIndex(1), true);

wysota
15th January 2010, 10:12
I am not sure if I correctly understood the task but is it not be easier to manipulate QHeaderView (http://qt.nokia.com/doc/4.6/qheaderview.html)?
No, because this causes tree branches to move.

Nightfox
19th January 2010, 22:39
So I just spend like five days figuring out that the indexes of the proxymodel get changed when I add an additional model on top of that. I've been debugging and debugging and debugging to I almost gave up. You out there who knew this please don't say I told you so!

@wysota: I used you suggestion on changing the mapFromSouce and mapToSource functions and now it finally works! I'm able to have the tree branches in the m_branchcolumn column (see below). I dont know if you still follow the code but I'm using a hierarchical model like the Qt's simple treeview example but instead of a Node class I'm using a Record class - but essentially it's the same. The m_branchcolumn column is the column of which I wish to swap the 0 column. Thanks again for your input and help!

So the following implementation will keep the branches in the m_branchcolumn.


QModelIndex ViewModel::mapFromSource(const QModelIndex& sourceIndex) const
{
if (sourceIndex.isValid())
{
int key = sourceModel()->data(sourceModel()->index(sourceIndex.row(), m_sourcekeycolumn,QModelIndex()) ).toInt() ;
Record *rec = internaltable->getRecord(key);
int row = 0;
int col = sourceIndex.column();
if(rec->parent)
row = rec->parent->children.indexOf(rec);

if(sourceIndex.column() == m_branchcolumn) col = 0;
if(sourceIndex.column() == 0 ) col = m_branchcolumn;
return createIndex(row,col,rec) ;
}
return QModelIndex();
}

QModelIndex ViewModel::mapToSource(const QModelIndex& proxyIndex) const
{
if(proxyIndex.isValid())
{

Record *rec = recordFromIndex(proxyIndex);
if(rec) {
int col = proxyIndex.column();
if(proxyIndex.column() == m_branchcolumn) col = 0;
if(proxyIndex.column() == 0 ) col = m_branchcolumn;
return sourceModel()->index(rec->sourcerow,col,QModelIndex());
}
}
return QModelIndex() ;
}

wysota
20th January 2010, 00:00
Great that you made it work. I was following the thread all the time - I'm very interested in every topic related to proxy models.

BTW. "I told you so" ;)

timohare
10th September 2010, 00:15
Thanks wysota & Nightfox, this helped me immensely.
There isn't much documentation on mapToSource or mapFromSource so here is the code I figured out.
This is useful for changing the order of the columns without using moveSection.
Next step is to patch in another model in to this proxyModel.



QModelIndex RoadProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
{
if (sourceIndex.isValid())
{
int col = sourceIndex.column();
int row = sourceIndex.row();
QModelIndex baseMapToSource = QSortFilterProxyModel::mapFromSource(sourceIndex);
row = baseMapToSource.row();
switch (col)
{
case WIShmemModel::ColTrkBat:
return index(row, ColTrkBat);
case WIShmemModel::ColExchgPad:
return index(row, ColExchgPad);
case WIShmemModel::ColLaneNo:
return index(row, ColLane);
...
default:
LOG_DBG("Uknown column %d", col);
return QModelIndex();
}
}
return QModelIndex();
}

QModelIndex RoadProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
if(proxyIndex.isValid())
{
int row = proxyIndex.row();
QModelIndex baseMapToSource = QSortFilterProxyModel::mapToSource(proxyIndex);
row = baseMapToSource.row();
switch (proxyIndex.column())
{
case ColTrkBat:
return sourceModel()->index(row, WIShmemModel::ColTrkBat);
case ColExchgPad:
return sourceModel()->index(row, WIShmemModel::ColExchgPad);
case ColLane:
return sourceModel()->index(row, WIShmemModel::ColLaneNo);
...
default:
LOG_DBG("Uknown column %d", proxyIndex.column());
return QModelIndex();
}
}

return QModelIndex() ;
}