4

I implemented a custom QAbstractTableModel and I'm using a std::vector for my data objects.

Now I wanted to implement the sort() method, to get my table sorted by column. That's basically what I do:

void SBStateTableModel::sort (int column, Qt::SortOrder order)
{
emit layoutAboutToBeChanged();

switch (column)
{
case Address:
    if (order == Qt::DescendingOrder)
        std::sort(states.begin(), states.end(), addr_comp_desc);
    else
        std::sort(states.begin(), states.end(), addr_comp_asc);

default:
    return;
}

emit layoutChanged();
}

But emitting layoutChanged() alone doesn't redraw the view. When mark a row and cycle through them, they get updated as they are being highlighted.

The documentation also speaks about updating the persistent indexes. Some people on here have suggested, that this is in fact not necessary. I'm not even sure how to go about it. Getting the list with persistentIndexList() and then I have to sort it. But std::sort is not a stable sort. I'm not sure how to match the persistent indices with my vector indices.

EDIT: There was just a "break" missing in the 'case'! So the function would return before emitting the layoutChanged signal.

RAM
  • 2,257
  • 2
  • 19
  • 41
Benjamin Maurer
  • 3,602
  • 5
  • 28
  • 49
  • Maybe you need to call `QTableView::sortByColumn(int, Qt::SortOrder)` function on your table view to sort the column? – vahancho Dec 09 '13 at 13:14
  • Clicking the column header does that. The signal and slots are connected. I can confirm that my sort routine is being called. For one, I have a qDebug() output in it and secondly if I highlight the lines in my table, I can see the new (expected) value. – Benjamin Maurer Dec 09 '13 at 13:26

2 Answers2

5

D'oh!

I was ready to dig into the Qt Source Code. But as I single stepped through my code, I saw the cursor jumping to the return statement in the 'default' case.

I had just forgotten to add a 'break' to my switch-case! It was just a simple fall-through error :(( It works now perfectly fine with "layoutChanged".

Benjamin Maurer
  • 3,602
  • 5
  • 28
  • 49
2

I just did this. First, you have to connect the header signal to the sort method you have created. This is a Python sample so you'll need to adapt it to C++:

model = SBStateTableModel()
table = QtGui.QTableView()
table.setModel(model)

table.setSortingEnabled(True)

When you sort, the entire view will change - or at least most of the view will change. So emitting the modelReset signal will cause the view to change. Model reset is one of the most inefficient signals to call, because it causes the entire view to be redrawn. However, most of the view will change anyway on a sort.

emit modelReset();

You could also emit the dataChanged signal, indicating that all of the data has changed. The dataChanged signal may be faster.

Python:

self.dataChanged.emit(self.index(0, 0), self.index(self.rowCount()-1, self.columnCount()-1))

C++:

(emitting a dataChanged signal, in a subclass of QTableView)

auto m = model();
emit dataChanged (m->index(0, 0), m->index(m->rowCount()-1, m->columnCount()-1));
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
justengel
  • 6,132
  • 4
  • 26
  • 42
  • 1
    reset() will also destroy state like user selection. I'd consider not to mess with sorting in the model but use a QSortFilterProxyModel on top of the model instead. – Frank Osterfeld Dec 09 '13 at 13:44
  • 1
    Good point. I didn't think about that. It is best to use the dataChanged signal then. The dataChanged signal will not affect the users selection. The users selection will still be on those cells, but the data to those cells will change. You may want the selection to be removed anyway. – justengel Dec 09 '13 at 13:48
  • Did try that with: emit dataChanged(this->createIndex(0,0,0), this->createIndex(states.size()-1, HEADERS_LEN-1,1)); Didn't work :( – Benjamin Maurer Dec 09 '13 at 13:56
  • @BenjaminMaurer I think it should just be: emit dataChanged(this->index(0,0), this->index(this->rowCount()-1, this->columnCount()-1)). I think that's how it should be in c++. The syntax is still basically the same. – justengel Dec 09 '13 at 14:00
  • I also get an error for modelReset: "/usr/include/qt4/QtCore/qabstractitemmodel.h:254: error: 'void QAbstractItemModel::modelReset()' is private" – Benjamin Maurer Dec 09 '13 at 14:00
  • @JustinEngel That method is overloaded. I tried the version without the id, with 2 params. Same effect. – Benjamin Maurer Dec 09 '13 at 14:03
  • @BenjaminMaurer I primarily work in python, and this works without a problem for me. Sorry I can't be of more help. – justengel Dec 09 '13 at 14:05
  • @BenjaminMaurer I do have one last resort if you really don't care about efficiency. This is considered really bad, but you could manually reset the model. You could catch your own custom signal or somehow link the table view with the model and call it. If the parent is the table view this->parent()->setModel(this). I really don't recommend this though. Try to figure out the proper syntax of the other signals. – justengel Dec 09 '13 at 14:12