LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-19-2014, 07:04 PM   #1
sharky
Member
 
Registered: Oct 2002
Posts: 569

Rep: Reputation: 84
Pyside qtreewidget numeric sorting


Is there a way to get a column in a QTreeWidget to sort numerically? I have search the interweb for solutions and have not been able to get anything to work.

Tried converting the data to integer prior to populating the tree and the data would not even populate. Got no errors from this - just empty spaces in the tree.

I am open to comments, advice, or suggestions.

Using python 2.7 and Qt 4.8

Cheers,
 
Old 04-21-2014, 02:17 PM   #2
sharky
Member
 
Registered: Oct 2002
Posts: 569

Original Poster
Rep: Reputation: 84
I found a solution but I don't really understand how it works. Pretty much did a cut and paste.

Code:
# re-implement the QTreeWidgetItem
class TreeWidgetItem(QTreeWidgetItem):
    def __lt__(self, other):
        column = self.treeWidget().sortColumn()
        key1 = self.text(column)
        key2 = other.text(column)
        return self.natural_sort_key(key1) < self.natural_sort_key(key2)

    @staticmethod
    def natural_sort_key(key):
        regex = '(\d*\.\d+|\d+)'
        parts = re.split(regex, key)
        return tuple((e if i % 2 == 0 else float(e)) for i, e in enumerate(parts))
Also had to 'import re' and the part of my code where I populate the tree I replace 'QTreeWidgetItem' with the new class, "TreeWidgetItem".

I'm definitely am confused but it works and that's what I need.

If you can explain this then please feel free to do so.

Cheers,
 
Old 04-21-2014, 04:20 PM   #3
dugan
LQ Guru
 
Registered: Nov 2003
Location: Canada
Distribution: distro hopper
Posts: 11,225

Rep: Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320
Here's an example that follows current PyQt best practices. It uses a QStandardItemModel+QtreeView combination, instead of a QTreeWidget. Sorting is done with a QSortFilterProxyModel subclass.

Code:
#!/usr/bin/env python

from sip import setapi
setapi("QDate", 2)
setapi("QDateTime", 2)
setapi("QTextStream", 2)
setapi("QTime", 2)
setapi("QVariant", 2)
setapi("QString", 2)
setapi("QUrl", 2)


from PyQt4.QtCore import Qt
from PyQt4.QtGui import (QApplication, QMainWindow, QStandardItem,
                         QSortFilterProxyModel, QStandardItemModel, QTreeView)
import sys


def main():

    app = QApplication(sys.argv)
    view = TreeView()
    view.show()
    sys.exit(app.exec_())


class TreeView(QMainWindow):

    def __init__(self, parent=None):
        super(TreeView, self).__init__(parent)

        treeModel = QStandardItemModel(self)

        treeModel.appendRow(self._createRow('cc', '14', '2'))
        treeModel.appendRow(self._createRow('bb', '5', '1'))
        treeModel.appendRow(self._createRow('dd', '3', '14'))

        proxyModel = ProxyModel(self)
        proxyModel.setSourceModel(treeModel)

        treeView = QTreeView()
        treeView.setSortingEnabled(True)
        treeView.setModel(proxyModel)
        self.setCentralWidget(treeView)

    @classmethod
    def _createRow(cls, *args):
        return [cls._createItem(value) for value in args]

    @staticmethod
    def _createItem(data):
        item = QStandardItem(data)
        flags = item.flags()
        flags ^= Qt.ItemIsEditable
        item.setFlags(flags)
        return item


class ProxyModel(QSortFilterProxyModel):

    def __init__(self, parent=None):
        super(ProxyModel, self).__init__(parent)

    def lessThan(self, left, right):
        leftData = self.sourceModel().data(left)
        rightData = self.sourceModel().data(right)

        try:
            return int(leftData) < int(rightData)
        except ValueError:
            return leftData < rightData

if __name__ == '__main__':
    main()
Click the header on each column, and the column will be sorted in numerical (not lexical) order.

See, especially:

http://qt-project.org/doc/qt-4.8/qso...l.html#sorting

The solution that you found is a tremendously ugly hack that monkey-patches QTreeWidgetItem and overrides its __lt__ method. The __lt__ method is called by the implementation of the comparison operator, "<", and the comparison operator is presumably called by the QTreeWidget's internal sorting routines. And as far as I can tell, its natural_sort_key method (which has the "re" dependency) can be eliminated and replaced with float with the same effect. So:

Code:
# re-implement the QTreeWidgetItem
class TreeWidgetItem(QTreeWidgetItem):
    def __lt__(self, other):
        column = self.treeWidget().sortColumn()
        key1 = self.text(column)
        key2 = other.text(column)
        return float(key1) < float(key2)

Last edited by dugan; 04-21-2014 at 07:24 PM. Reason: Fixed syntax error. Added sortable left column of text.
 
Old 04-21-2014, 07:11 PM   #4
sharky
Member
 
Registered: Oct 2002
Posts: 569

Original Poster
Rep: Reputation: 84
Hi Dugan,
Your example returned a syntax error on line 47. I am not able to fix it.

With respect to using float instead of the natural_sort_key doesn't work on text. My first column is text and then the next two columns are integers. So column one needs alphbetical sorting and columns two and three need numerical sorting.
 
Old 04-21-2014, 07:23 PM   #5
dugan
LQ Guru
 
Registered: Nov 2003
Location: Canada
Distribution: distro hopper
Posts: 11,225

Rep: Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320
I've fixed the example. The syntax error on line 47 is gone, and there is now a sortable left column of text, in addition to two sortable columns of numbers.

And as for the example you found, here's a revision that will work with both text and numbers:

Code:
# re-implement the QTreeWidgetItem
class TreeWidgetItem(QTreeWidgetItem):
    def __lt__(self, other):
        column = self.treeWidget().sortColumn()
        key1 = self.text(column)
        key2 = other.text(column)
        try:
            return float(key1) < float(key2)
        except ValueError:
            return key1 < key2

Last edited by dugan; 04-21-2014 at 07:28 PM.
 
Old 04-22-2014, 01:43 PM   #6
sharky
Member
 
Registered: Oct 2002
Posts: 569

Original Poster
Rep: Reputation: 84
Mr. Dugan, Thanks again for your help. For now I am using your revision of my 'ugly hack'.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Installing PySide from AUR BananaPhone Arch 4 10-09-2012 06:15 PM
Pyqt vs pyside Garda Programming 1 07-13-2012 05:46 PM
Qt Python : QTreeWidget Child Problem threaderslash Programming 1 11-17-2009 08:36 PM
QtPython QtreeWidget - sortingEnabled Problem threaderslash Programming 1 11-16-2009 08:38 PM
LXer: On the PySide - interview LXer Syndicated Linux News 0 08-25-2009 12:30 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 07:58 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration