1

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!!!

abrakad
  • 23
  • 5
  • What does it mean: But it did not work? – eyllanesc Oct 05 '17 at 12:06
  • Google Search teach to me about List Nature Sort. So I make list with listitem. Listitems.append(listitem) and Listitems.sort(key=nature_sort). but error occured. – abrakad Oct 05 '17 at 12:17
  • I do not understand, that is the ordering of lists in python, and it is not only for natural ordering but for any ordering criteria. – eyllanesc Oct 05 '17 at 12:19
  • the lists and QStandarItemModel are very different things, I tried although it is not the best but it works, it orders them in a natural way. – eyllanesc Oct 05 '17 at 12:21
  • Is it work? My pycharm debug and error. I tried def __lt__(self, other): return naturalkey(self.value, float) < naturalkey(other.value, float) implemet of QStandardItemModel class but Fail... – abrakad Oct 05 '17 at 12:35
  • I do not talk about your comment, but about the code you show in your question. – eyllanesc Oct 05 '17 at 12:36
  • Why do you get complicated if QStandarItemModel already uses that sort order when you use your_model.sort(your_column)? – eyllanesc Oct 05 '17 at 13:25
  • yes. QstandardItemModel sort desending But It's not Natural Order. I want to Sort QstandardItemModel by Natural order. – abrakad Oct 05 '17 at 13:32
  • Try my answer, and if it works mark it as correct please. – eyllanesc Oct 05 '17 at 13:56
  • @abrakad. The `__lt__` is supposed to be used in a sub-class of `QStandardItem`, not a model. PS: there are some other errors in your code - you need to use `event.mimeData().hasUrls()` (note the `()` at the end). – ekhumoro Oct 05 '17 at 14:25

1 Answers1

1

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()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanky your Help. I didn't test this code yet. But I think It work well. after I test it I note my question. – abrakad Oct 06 '17 at 00:32