Secondary sorting of a ListCtrl

I have a ListCtrl using ColumnSorterMixin to sort the columns when the user clicks on a column header. I would like it to be that, when when multiple items in a column are the same, they are kept in the same order that they were in previously. This isn’t what happens by default, as you can see from the wxPython demo. In that demo, if you start with Artist in alphabetic order and then click on Genre, the artists within a genre are not in alphabetic order (this is seen in the Rock genre).

I’ve looked at the code for the ColumnSorterMixin and I see there are functions GetSecondarySortValues and OnSortOrderChanged and I thought perhaps I could get secondary sorting to work the way I want using those functions. I have two questions: (a) Is this a sensible approach, or is there a better way? (b) In the arguments of GetSecondarySortValues, what are key1 and key2?

There are many entries in this group with headings like “ListCtrl sorting problem”. I’ve looked for a discussion of my problem there but didn’t find one; if I missed it, I apologize.

Patrick Maher
http://patrick.maher1.net

I have a ListCtrl using ColumnSorterMixin to sort the columns when the
user clicks on a column header. I would like it to be that, when when
multiple items in a column are the same, they are kept in the same order
that they were in previously. This isn't what happens by default, as you
can see from the wxPython demo. In that demo, if you start with Artist
in alphabetic order and then click on Genre, the artists within a genre
are not in alphabetic order (this is seen in the Rock genre).

I've looked at the code for the ColumnSorterMixin and I see there are
functions GetSecondarySortValues and OnSortOrderChanged and I thought
perhaps I could get secondary sorting to work the way I want using those
functions. I have two questions: (a) Is this a sensible approach, or is
there a better way?

Yep, you should be able to do it that way, for example you can use the item's current positions as the secondary sort values. You can also override GetColumnSorter to totally replace the comparison function.

(b) In the arguments of GetSecondarySortValues, what
are key1 and key2?

They are the keys to be used to fetch the data for the two rows to be compared. Take a look at __ColumnSorter for how it is using key1, key2 for getting the primary sort values.

···

On 10/26/11 3:12 PM, Patrick Maher wrote:

--
Robin Dunn
Software Craftsman

Thanks Robin, with your help I’ve solved the problem. I’ll describe my solution in case it is useful to someone else.

First, I’ll describe the solution to a simpler problem. If you want to have secondary sorting always on the second column when the first column is sorted, otherwise on the first column, you can do that by adding to your ListCtrl subclass:

def GetSecondarySortValues(self, col, key1, key2):
    sscol = 1 if col == 0 else 0
    def ss(key):
        return self.itemDataMap[key][sscol]
    return (ss(key1), ss(key2))

The above can easily be modified to get secondary sorting to work in some other fixed way.

I wanted secondary sorting to use the column previously sorted and I did it as follows. In the init method of my ListCtrl subclass I added:

self.sorthistory = [1,0]

This sorthistory is a list of the columns sorted and here it records that initially I have sorted column 1, then column 0. I also added to my subclass the following:

def GetSecondarySortValues(self, col, key1, key2):
    n = 1
    sscol = self.sorthistory[-1]
    while sscol == col:
        sscol = self.sorthistory[-(1+n)]
        n += 1
    def ss(key):
        return self.itemDataMap[key][sscol]
    return (ss(key1), ss(key2))
def OnSortOrderChanged(self):
    self.sorthistory.append(self.GetSortState()[0])

The purpose of the while clause here is to ensure that sscol (the secondary sort column) is different to the column now being sorted, even when the user clicks on the same column multiple times.

That’s all, and it seems to be working correctly.

Patrick Maher
http://patrick.maher1.net