Home > database >  How to sort one column based on another in QTableWidget?
How to sort one column based on another in QTableWidget?

Time:03-16

I have encountered a problem after I built a table with QTableWidget: I tried to sort a list of directories based on their creation time.

In the data collection function:

...
mtime = os.path.getmtime(directory)
item['mtime'] = str(mtime)
ltime = time.ctime(mtime)
item['ltime'] = ltime
...

Then in the table creation function:

...
self.table.setItem(row, 1, QtWidgets.QTableWidgetItem(item['ltime']))
self.table.setItem(row, 2, QtWidgets.QTableWidgetItem(item['mtime']))
row  = 1
...

And I enabled sorting simply by:

self.table.setSortingEnabled(True)

However when I sort the ltime column, it would only sort based on alphabetical order of strings:

Sorting based on ltime

Only when I sort with the mtime column would I get the correct result:

Sorting based on mtime

So my question is, is there a method for me to sort based on mtime when I click on the header of ltime column? I would like to hide the mtime column as well, since it would be confusing for users.

CodePudding user response:

PyQt treats the values in the table as stings so it sorted the table alphabetically (Fri, Mon, Thu, Tue, Wed...) not chronologically (by the actual date regardless of the name of the day). It seems that your solution with sorting the mtime works well. If you want to sort by date then you'll have to implement a QTableModel and use the correct sorting in the model. Best bet is to go with a pandas DataFrame, see here.

CodePudding user response:

The default sorting behavior in Qt models is based on the data type. If the type is a string, it will obviously sort items alphabetically.

Be aware that while it might seem to work, the sorting of the second column is also wrong: since the values probably have very similar values, you cannot see the difference, but that column is still sorting alphabetically: if those values had different digit counts, it wouldn't have worked, since "100" comes before "99" alphabetically.

A proper solution is to keep the item data using the original numeric value, and override the displayed result with an item delegate. Note that the default constructor of QTableWidgetItem only accepts strings, so you must use setData().

class DateDelegate(QStyledItemDelegate):
    def displayText(self, value, locale):
        try:
            return time.ctime(value)
        except TypeError:
            return super().displayText(value, locale)

Then, in your main class:

ltimeItem = QtWidgets.QTableWidgetItem()
ltimeItem.setData(Qt.DisplayRole, mtime)
self.table.setItem(row, 1, ltimeItem)
mtimeItem = QtWidgets.QTableWidgetItem()
mtimeItem.setData(Qt.DisplayRole, mtime)
self.table.setItem(row, 2, mtimeItem)

# ...
self.dateDelegate = DateDelegate(self.table)
self.table.setItemDelegateForColumn(1, self.dateDelegate)
self.table.setItemDelegateForColumn(2, self.dateDelegate)
  • Related