views:

304

answers:

2

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])
+1  A: 

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.

Max
The column I'm trying to sort by double clicking on is a column populated in this format: 03/29/10 22:06:53I'm trying to sort a column of Datetime objects (import datetime) and the sort I'm using for them (which proves to sort correctly) is - childrenList.sort(lambda x, y: cmp(x.dateObject, y.dateObject))First I grab the childrenList by "takeChildren" then I sort them. Then I try to reinsert them with my insert method in the initial question.Is there a way (being unfamiliar with Roles) to implement my sort of Datetime objects into the __lt__ method you were sketching out?
mleep
ok, now I see, you've sort your list, which contains Q..Items and dates by date. I will modify MyTreeWidgetItem class from my aswer to satisfy your task.
Max
+1  A: 

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_()
Jesse Aldridge
I'm clearing the tree twice -childrenList = self.rootItem.takeChildren()self.clear()although I'm not creating a new QTreeWidgetItem to be "rootItem" and adding the taken children to that, if I do that I get the same result as above. Any ideas?
mleep
hey jesse, the index thing is really screwing up my code, is there a proper way to "delete" a widget after using "takeTopLevelItem()" or similar functions? Is there something you have to do afterwards?
mleep
I think takeTopLevelItem should be all you need. Maybe you should post a more complete example? If I were you what I would do is deconstruct my program to be as simple as possible while still demonstrating the problem.
Jesse Aldridge