1

I want to know how to ensure the sort indicator in the horizontal header of a QTableView is updated when a programmatic sort is performed on the model.

Here's the problem:

QStandardItemModel model(3,1);
QTableView view;
view.setModel( &model );

// Populate the model ensuring it is not in a sorted order
for( int row = 0; row < model.rowCount(); ++row )
{
    model.setItem( row , 0 ,
                   new QStandardItem(QString::number((row+1)%model.rowCount())));
}

view.setSortingEnabled( true );
// At this point everything is consistent since enabling the sorting
// triggers a sort that matches the indicator in the horizontalHeader (see A)

model.sort( 0 , Qt::AscendingOrder );
// However at this point the sort order has been reversed but the
// header's sort indicator remains unchanged (see B)

A: A: immediately after setSortingEnabled(true) B:Immediately after model.sort(0,Qt::AscendingOrder)

As you can see the sort indicator remains the same and therefore is inconsistent with the actual sort order.

In my application I have two views that interact with the same model and sorting can be triggered from either of them. I don't see anything in QAbstractItemModel that signals when a sort has been performed. It seems like QHeaderView/TableView assume that they are the only thing that can trigger a sort.

Does Qt provide facilities for coping with this that I'm missing? If not, what's the best way of keeping the sort indicator up-to-date without breaking the encapsulation of the multiple views on the model too much?

SimonD
  • 638
  • 5
  • 16
  • I think that the initiator of sorting should be rather the view than the model. Therefore you can use `QTableView::sortByColumn()` function that, IMO, should set the sorting indicator too. – vahancho Jun 02 '14 at 11:53
  • Thanks @vahancho. In my application, the user can trigger a sort from one of two views. I want the sort indicator in the table-view to correctly synchronise in that case. I was hoping to do this without the views having to interact with each other directly... – SimonD Jun 02 '14 at 12:03
  • If so, instead of calling `sort()` of your model you can call `QTableView::sortByColumn()` for each of your views. – vahancho Jun 02 '14 at 12:16
  • But that would result in sorting the model multiple times would it not? Also it requires each view to 'know' about the other views which breaks encapsulation somewhat: not all views will be in existence at all times. – SimonD Jun 02 '14 at 12:19

2 Answers2

3

One of the ItemDataRole enumerators available since Qt 4.8 is InitialSortOrderRole.

http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum

It should therefore be possible to transmit sort order information through the QAbstractItemModel::headerData method.

I've tried this however and found that QTableView and QHeaderView do not seem to update in response to changes in this headerData role. A customised header view would appear to be necessary...

It might be worth it because passing this information via the model allows any number of views to synchronise without any external agent having to track all the views in existence so that it can distribute notifications. It would also work seamlessly through model proxy stacks such as those built with QSortFilterModelProxy.

SimonD
  • 638
  • 5
  • 16
0

The solution I've come up with to avoid breaking encapsulation too much is

  • to have a signal on each view (on QTableView the sortIndicatorChanged signal suffices and on my custom view I have added a similar signal).
  • the manager of views connects to these signals
  • when any view emits such a signal the manager of views calls a slot on all the other views so that they can synchronise their sort indicators

I still feel like I might be missing something - surely this is a common problem? It seems to me that QAbstractItemModel should have a way of transmitting sort-order information to views...

SimonD
  • 638
  • 5
  • 16