PDA

View Full Version : PyQt Tree Model: move TreeItems



matameko
26th November 2012, 13:05
Hello,

I have a document class that contains several "Ikwed Objects". Each "Ikwed Object" can have several child "Ikwed Objects".
The document has a tree model, that is used to show the hierarchy of the "Ikwed Objects" in a tee view.

I am able to create new "Ikwed Objects" in the document, append or insert childs and also to remove childs in the tree model.

However, If I try to move a "child tree item" to the root branch, I get errors in the tree view or infite loops that I do not understand.

For example I want to move the child IkwedObject11 to the start of the root branch:

-IkwedObject0
-IkwedObject1
++ IkwedObject11
++ IkwedObject12

=>

-IkwedObject11
-IkwedObject0
-IkwedObject1
++ IkwedObject12

Could you please have a look at the function "moveTreeItem" of the attached example and give me hints how to adapt it?
I wrote the example with Spyder 2.1.9 (PythonXY on Windows Platform, with QT 4.9)



def moveTreeItem(self, treeItem, newParentTreeItem, newRow):
oldParentTreeItem = treeItem.parentItem
oldRow = oldParentTreeItem.childItems.index(treeItem)

#self.beginMoveRows(self.getIndex(oldParentTreeIte m), oldRow, oldRow, self.getIndex(newParentTreeItem), newRow)

self.insertTreeItem(treeItem, newParentTreeItem, newRow)
self.removeTreeItemByRow(oldParentTreeItem, oldRow)

#self.endMoveRows()

def insertTreeItem(self, childItem, parentItem, childRow=0):
#insert a tree item as a new child of a parent item at a given row
#childItem.parentItem = parentItem
if childRow <= parentItem.childCount():
parentIndex = self.getIndex(parentItem)
self.beginInsertRows(parentIndex, childRow, childRow)
parentItem.childItems.insert(childRow, childItem)
self.endInsertRows()
else:
print "Error in insertTreeItem: row is larger row count: " + str(row) + " > " + str(parentItem.childCount())

def removeTreeItem(self, treeItem):
parentItem = treeItem.parentItem
row = treeItem.row()
self.removeTreeItemByRow(parentItem, row)

def removeTreeItemByRow(self, parentItem, row):
parentIndex = self.getIndex(parentItem)
self.beginRemoveRows(parentIndex, row, row)
del parentItem.childItems[row]
self.endRemoveRows()


The code creats a default document and displays it. Please comment the line 345

treeModel.moveTreeItem(treeObj11, rootItem, 0)

at the end of the init function of TestWidget to see the model without moved items.

The wrong result I get is:

-IkwedObject12
-IkwedObject0
-IkwedObject1
++ IkwedObject12

Why is the first item "IkwedObject12" instead of "IkwedObject11" ??!!


Sunny regards,

Stefan

matameko
27th November 2012, 14:52
One option seems to be to clone the tree item with
import copy
copy.deepcopy
and to use the cloned tree item for the insert command.

prof.ebral
28th November 2012, 17:40
You can just take the item from the tree instead of cloning it. Either use .takeChild or .takeToplevelItem which takes the item and its children.

You also run this code on line 227:


super(IkwedDocument.TreeModel.TreeItem, self).__init__(parentTreeItem)


I highly recommended you NEVER run super inside the __init__ method. Super operates with the Method Resolution Order, and if you start basing one class off multiple classes you are going to run into trouble. Also, super returns a proxy. It is best to instantiate the class __init__ method more directly, be it QtCore.QAbstractItemModel.__init__(self) or whatever other class you want to use. Trust me ... I use super every day in my code and the best way to use super is when you reimplement class methods and want to call the original unmodified version.

To be honest, I am not sure what you are doing wrong. I have a pretty good model for the QTreeWidget. Admittedly it is something of a hacked version of the Qt tree widgets, but it does what you want. The code can be found here: http://tech.madmathlabs.info/index.cgi/pious-beta/file/0c2fde4d5fa3/CoreEngine/tree_engine/tree_dock.py

matameko
30th November 2012, 05:41
Thank you for your tips. I am going to have a look at the alternative to super.

I found out that my over all structure could be more simple.
My example above uses a "TreeModel" class and the tree has child items of a "TreeItem" class.
These tree items contain the "IkwedObjects" that I want to manage.

Now I moved all Functions of the "TreeItem" class to the "TreeModel" class and directly
use the "IkwedObjects" as tree items. I do not need extra functions in the
"IkwedObjects". All can be done in the tree model. Without the extra TreeItem class
my structure is more flat and the errors did somehow disappear.

prof.ebral
1st December 2012, 07:19
You are welcome. When it comes to super, I will stare down a coder with a college degree and let them know .. they probably don't understand the usage of super. Even in PySide I see super used under the __init__ method in such a manner


class Widget(QWidget):

def __init__(self):
super(Widget, self).__init__(self)

and that is a **wrong** usage. Yes ... it is absolutely **wrong** .. even if it appears to work how you want it too.

Super works by reaching into the Method Resolution Order (MRO). You pass a module that super uses to obtain an index from the MRO, and then it returns a proxy of the index+1. So, for the above example the MRO would look like, [Widget, QWidget, etc ...] and super would return a a proxy of QWidget and then run it's __init__ method pass any arguments you send it.

The real problem is two fold. First ... you are using a proxy to run methods. The proxy is a module that has already been instantiated, it may have new objects assigned to variable names, and further more the data may have been altered in some other way during run time. Secondly ... you are reaching into the Method Resolution Order. While this has some really cool benefits when you use super correctly, if you don't understand how the MRO is itemized, what methods are in the MRO list, just basically don't understand the MRO, or any other point of misunderstanding ... you are not sure what you are doing .. only that it appears to work.

Super is an awesome built-in within Python and it has really great uses. I mainly use it so I can modify class defined methods (functions) and then run the original unmodified version if I need too. In Qt, since just about everything is based of QObject or QWidget, or has some other basic class that it inherits, it can provide even more functionality. Say for example you want to create a new QWidget class that all of your widgets will inherit.


class MyWidget(QWidget):
def __init__(self):
QWidget.__init__(self)

You can then use that class in place of QWidget, redifine any of the class methods, and if you still want to use the original QWidget method you can reach into the MRO and obtain it. If I am not mistaken it would look like this.


super(MyWidget, self).show()

And then super would find the MyWidget class and use that as index.

Here are some blog posts I made about super: http://weblog.madmathlabs.com/?cat=10 (read from the bottom up)