Tuesday, March 17, 2015

Sorting the table by my ownself

In the prior post, I said that what I expected a Qt sort routine to do was "...fetch all the key values into an array; sort the array using any available standard library routine; save a vector of indexes internally..." That notion was based in what I learned about sorting a long time ago. I learned about sorting when I was working on APL\360 in the early 1970s. APL has a built-in operator called grade up. In my day it only applied to vector types (Python: list types); in its modern incarnation it has been extended to handle arrays as well. Anyway, the result of applying grade-up to a vector of order-able values was a vector of integers, the origin-0 indexes that would sort the input.

      L ← 2 7 1 8 2 8
      ⍋ L
2 0 4 1 3 5
      L[ ⍋ L ]
1 2 2 7 8 8

As a side-note, APL programmers did lots of tricks with grade-up. For example, applying it to its own output produces a list of the rank orders of the input.

      L ← 2 7 1 8 2 8
      ⍋ L
2 0 4 1 3 5
      ⍋ ⍋ L
1 3 0 4 2 5

That is, the first element of L is the 2nd-lowest (1), the second element is 4th-lowest (3), the third element is the lowest, and so on. But I digress.

Since the QSortFilterProxyModel sort seems to be broken, maybe I should take the advice I got from "Andre" on the Qt forum, and implement sort myself. One reason I have been reluctant to do this is that I also use QSortFilterProxyModel to filter the table. If I toss it and do my own sort, I will have to find a way to do filtering myself.

The QAbstractTableModel inherits a sort() method which in the default form does nothing. It receives two arguments, the column number to sort and the sort direction, Qt.AscendingOrder or Qt.DescendingOrder. The docs don't say so but I am going to assume that the QTableView is smart enough to know that if it has to call model.sort() (because the user clicked a column head), there may be some change in the order of the rows, and it needs to repopulate the visible rows after the sort call.(edit: this turns out to be wrong.)

Sorting has to start with the model's data() method. When the table has been sorted, the data() method needs to access the database through an indirection vector. In APL terms, instead of returning values from L[row], it needs to return values from L[ (⍋ L)[row] ]. But how can data() know that a sort is in effect?

Well, why does it need to know? Let's make data() always go through an indirection. When the table is in the default sort order, which is the natural order of the valuesView, the indirection can be a simple range(DBS), a vector of integers from 0 to max. It will add a little overhead but otherwise not change the operation.

When sort() is called, it can swap in a different indirection vector. For example, if the sort is descending on the first column the indirection vector can be reversed(range(DBS)). It will be a little more complicated arranging for indirection vectors based on sorts of the other two columns, but the SortedDict type will be a help.

I'm posting this on Tuesday morning before I've written more than a few lines of the code to implement this concept. I can hardly wait to find out how (or if?) it is going to work. With any luck, next post will display some sorting code.

No comments: