Search code examples
pythonpyqtnatural-sortpyside2

python QstandardItemModel Natural sort


I'm looking for QstandardItemModel Natural Sort Method. I'm reading some relevant question and try to my code. But It didn't work

My tool accept Some File list by Drag and Drop. I want to sort my Model data by Natural sort. and put it to listview.

So Here is my code:

class VideolistView (QtWidgets.QListView):
    def __init__(self, parent):
        super(VideolistView, self).__init__(parent)
        self.setAcceptDrops(True)
        self.setObjectName("VideolistView")
        self.setGeometry(QtCore.QRect(8, 30, 250, 301))
        self.Model = QtGui.QStandardItemModel(self)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()

            for url in event.mimeData().urls():
                dropitem = str(url.toLocalFile())
                Fname = os.path.split(dropitem)
                if not self.Model.findItems(Fname[1]):
                    listitem = QtGui.QStandardItem(Fname[1])
                    self.Model.appendRow(listitem)
            self.Model.sort(0)
            self.setModel(self.Model)
        else:
            event.ignore()

Some my attemp announce.

import re

def _human_key(key):
    parts = re.split('(\d*\.\d+|\d+)', key)
    return tuple((e.swapcase() if i % 2 == 0 else float(e))
            for i, e in enumerate(parts))
   ......

    listitems=[]
         for url in event.mimeData().urls():
             dropitem = str(url.toLocalFile())
             Fname = os.path.split(dropitem)
             listitem = QtGui.QStandardItem(Fname[1])
             listitems.appent(listitem)
    listitems.sort(key=_human_key)    

Pycharm debug Error listitems.sort(key=_human_key) and I tried reimplement of QstandardItemModel class

import re

def _human_key(key):
    parts = re.split('(\d*\.\d+|\d+)', key)
    return tuple((e.swapcase() if i % 2 == 0 else float(e))
            for i, e in enumerate(parts))
   ......

Class NaturalSortModel (QtGui.QstandardItemModel)
    def __lt__(self, other):
        column = self.treeWidget().sortColumn()
        k1 = self.text(column)
        k2 = other.text(column)
        return _human_key(k1) < _human_key(k2)
               .......
Class VideolistView (QtWidgets.QListView)
    def __init__(self, parent):
       self.Model = self.NaturalSortModel(self)

               ........
            self.Model.appendRow(listitem)
        self.Model.sort(0)
        self.setModel(self.Model)
                ........

above Two code Error. How I do Natural Sort with QstandardItemModel?

link : Python - Human sort of numbers with alpha numeric, but in pyQt and a __lt__ operator

With @eyllanesc Answer, First Solution with QSortFilterProxyModel Work well. It sort data by Natural order.

But @eyllanesc second advice(Qstandarditem inherit) is not work for me. It sort Alphabetically just like original source. @eyllanesc Thank you a lot!!!


Solution

  • If you want to establish a particular criterion of order you must do it through a model that inherits from QSortFilterProxyModel and implement the method lessThan as I show below:

    class NaturalSortFilterProxyModel(QtCore.QSortFilterProxyModel):
        @staticmethod
        def _human_key(key):
            parts = re.split('(\d*\.\d+|\d+)', key)
            return tuple((e.swapcase() if i % 2 == 0 else float(e)) for i, e in enumerate(parts))
    
        def lessThan(self, left, right):
            leftData = self.sourceModel().data(left)
            rightData = self.sourceModel().data(right)
            return self._human_key(leftData) < self._human_key(rightData)
    
    class VideolistView (QtWidgets.QListView):
        def __init__(self, parent=None):
            super(VideolistView, self).__init__(parent)
            self.setAcceptDrops(True)
            self.setObjectName("VideolistView")
            self.setGeometry(QtCore.QRect(8, 30, 250, 301))
            model = QtGui.QStandardItemModel(self)
            proxyModel = NaturalSortFilterProxyModel()
            proxyModel.setSourceModel(model)
            proxyModel.sort(0, QtCore.Qt.AscendingOrder)
            self.setModel(proxyModel)
    
        def dragEnterEvent(self, event):
            if event.mimeData().hasUrls():
                event.accept()
            else:
                event.ignore()
    
        def dragMoveEvent(self, event):
            if event.mimeData().hasUrls():
                event.accept()
            else:
                event.ignore()
    
        def dropEvent(self, event):
            if event.mimeData().hasUrls():
                event.setDropAction(QtCore.Qt.CopyAction)
                event.accept()
    
                for url in event.mimeData().urls():
                    dropitem = str(url.toLocalFile())
                    Fname = os.path.split(dropitem)
                    if not self.model().sourceModel().findItems(Fname[1]):
                        listitem = QtGui.QStandardItem(Fname[1])
                        self.model().sourceModel().appendRow(listitem)
            else:
                event.ignore()
    

    As @ekhumuro says, the solution shown in the link refers to creating a class that inherits from QStandardItem, as I show below

    class NaturalStandardItem(QtGui.QStandardItem):
        @staticmethod
        def _human_key(key):
            parts = re.split('(\d*\.\d+|\d+)', key)
            return tuple((e.swapcase() if i % 2 == 0 else float(e)) for i, e in enumerate(parts))
        def __lt__(self, other):
            return self._human_key(self.text()) < self._human_key(other.text())
    
    
    class VideolistView (QtWidgets.QListView):
        def __init__(self, parent=None):
            super(VideolistView, self).__init__(parent)
            self.setAcceptDrops(True)
            self.setObjectName("VideolistView")
            self.setGeometry(QtCore.QRect(8, 30, 250, 301))
            model = QtGui.QStandardItemModel(self)
            self.setModel(model)
    
    
        def dragEnterEvent(self, event):
            if event.mimeData().hasUrls():
                event.accept()
            else:
                event.ignore()
    
        def dragMoveEvent(self, event):
            if event.mimeData().hasUrls():
                event.accept()
            else:
                event.ignore()
    
        def dropEvent(self, event):
            if event.mimeData().hasUrls():
                event.setDropAction(QtCore.Qt.CopyAction)
                event.accept()
    
                for url in event.mimeData().urls():
                    dropitem = str(url.toLocalFile())
                    Fname = os.path.split(dropitem)
                    if not self.model().findItems(Fname[1]):
                        listitem = NaturalStandardItem(Fname[1])
                        self.model().appendRow(listitem)
                self.model().sort(0)
    
            else:
                event.ignore()