Question

I am unable to properly insert a QTreeWidgetItem at a specific index, in this case I am removing all QTreeWidgetItems from the tree, doing a custom sort on their Date Objects and then inserting them back into the QTreeWidget.

However, upon inserting (even one at a time) the QTreeWidgetItem is not inserted into the correct place.

The code below prints out:

index 0: 0

index 0: 1 index 1: 0

index 0: 2 index 1: 1 index 2: 0

index 0: 3 index 1: 2 index 2: 0 index 3: 1

index 0: 4 index 1: 2 index 2: 0 index 3: 1 index 4: 3

print 'index 0: ', self.indexOfTopLevelItem(childrenList[0])

self.insertTopLevelItem(0, childrenList[1])

print 'index 0: ', self.indexOfTopLevelItem(childrenList[0]), ' index 1: ',\
    self.indexOfTopLevelItem(childrenList[1])

self.insertTopLevelItem(0, childrenList[2])

print 'index 0: ', self.indexOfTopLevelItem(childrenList[0]), ' index 1: ',\
    self.indexOfTopLevelItem(childrenList[1]), ' index 2: ', \
    self.indexOfTopLevelItem(childrenList[2])

self.insertTopLevelItem(0, childrenList[3])

print 'index 0: ', self.indexOfTopLevelItem(childrenList[0]), ' index 1: ',\
    self.indexOfTopLevelItem(childrenList[1]), ' index 2: ',\
    self.indexOfTopLevelItem(childrenList[2]), 'index 3: ',\
    self.indexOfTopLevelItem(childrenList[3])

self.insertTopLevelItem(0, childrenList[4])

print 'index 0: ', self.indexOfTopLevelItem(childrenList[0]),\
    ' index 1: ', self.indexOfTopLevelItem(childrenList[1]),\
    ' index 2: ', self.indexOfTopLevelItem(childrenList[2]),\
    'index 3: ', self.indexOfTopLevelItem(childrenList[3]),\
    'index 4: ', self.indexOfTopLevelItem(childrenList[4])
Was it helpful?

Solution

You can do your custom sort without a need to refill tree. You just need to overload item's 'less' operator. Note, that QT figures out, what to draw in the cell, what size it should be, what text should it contain, by looking in item's

virtual QVariant data ( int column, int role ) const

when you create an item, you provide some string to constructor for item to show. This string is placed into data(.., QtCore.Qt.EditRole), so, the equivalent of passing data into constuctor is to set data with:

virtual void setData ( int column, int role, const QVariant & value)

Internally, data is just an array, you can place anything youn want, and indexes of it are roles (Qt.EditRole is 2, Qt.ToolTipRole is 3 etc.), so we just let some role to contain our data, tell comparison operator to compare these values, and tell setData set DisplayRole when we set our, let's say, ValueRole. Here is the sample:

class TreeItem(QtGui.QTreeWidgetItem):

    PythonValueRole = QtCore.Qt.UserRole

    #values are list of python objects, that have __str__ and can be compared
    def __init__(self, tree, values):
        QtGui.QTreeWidgetItem.__init__(self, tree)
        i = 0
        for v in values:
            self.setData(i, TreeItem.PythonValueRole, v)
            i += 1

    #overridden to simplify data assigning. When called with PythonValueRole, passes
    #that object's string representation to DisplayRole and EditRole
    def setData(self, col, role, value):
        if role == TreeItem.PythonValueRole:
            QtGui.QTreeWidgetItem.setData(self, col, TreeItem.PythonValueRole, value)
            # sets DisplayRole and EditRole
            QtGui.QTreeWidgetItem.setData(self, col, QtCore.Qt.EditRole, str(value)) 
            QtGui.QTreeWidgetItem.setData(self, col, QtCore.Qt.DisplayRole, str(value))
        else:
            QtGui.QTreeWidgetItem.setData(self, col, role, value)

    def __lt__(self, other):
        c = self.treeWidget().sortColumn()
        return self.data(c, TreeItem.PythonValueRole).toPyObject() < 
               other.data(c, TreeItem.PythonValueRole).toPyObject()

I've tested, it works fine. Of cource, you can avoid inheritance and override lt directly, since we had duck typing, use UserRole itself to hold your data and set data do display directly with setData(col, role, val), or even hold only text in edit/display role and convert it in datetime only while comparing, but it seems ugly. Note, that data() contains QVariant. When you set data, your py object automatcally converts, and you need to call toPyObject() to get your value back as it was.

OTHER TIPS

The problem must be elsewhere in your code. Maybe you aren't removing the items properly? Because the following works:

import sys

from PyQt4.QtGui import *
from PyQt4.QtCore import *

app = QApplication(sys.argv)

tree = QTreeWidget()
items = [QTreeWidgetItem(['index %i' % i]) for i in range(5)]

for item in items:
    tree.insertTopLevelItem(0, item)

print 'indexes: '
for item in items:
    print tree.indexOfTopLevelItem(item),

tree.show()

app.exec_()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top