Python: Call by Reference and Recursion - python

I try to parse an XML file.
The main goal is to get an specific tag with with an other specific parent tag.
I have to use an already existing file and i have to modify it.
def get_item_with_parents(self, parent, name, items=None, parents=None):
self.parents=parents
if items == None:
top_level = 1
items = []
self.parents = []
else:
top_level = 0
for child in self.children:
self._append_list(child.name)
if child.name == name and parent in self.parents:
print "inside if"
self._del_list()
items.append(child)
#print self.parents
child.get_item_with_parents(parent, name, items, self.parents)
if top_level:
return PacketList(items)
def _append_list(self, item):
self.parents.append(item)
def _del_list(self):
self.parents=[]
print "test"
print self.parents
The code should do the following (the child variable should basically mean the same as the parent thing):
I get the parent tag an the name of the tag which should be returned.
Then I check if I am the first recursion otherwise i will not return the packetlist at the end.
For each child I try to append its name to my self.parents list. Then I check if my packet has the right "name" and if yes I look if the "parent" is in self.parent.
So there occurs the problem now.
If the parent was in the list, I have to delete everything in my list. But this wont work.
The list will not be cleared. So if there is a next occurance of the tag with the name "name", it will be appended again even if it should not do, because the parent should not be in the list anymore
Can someone tell me why?

I found my misstake.
To clear a list i hav to call del parents[:]
parents=[] does not work(it seams it does simply nothing).

Related

Linked list implementation in python issue

I have been trying to implement a linked-list in python.Any call of a variable inside a function in Python is by default call by reference.I have this code:
For the list_node:
class list_node:
def __init__(self,obj,next_listnode):
self.obj = obj
self.next_listnode = next_listnode
For the linked_list:
class linked_list:
def __init__(self,list_node):
self.list_node =list_node
def add_node(self,obj):
current = self.list_node
while(current.next_listnode is not None):
current = current.next_listnode
current.next_listnode = obj;
def print_linkedlist(self):
current = self.list_node
while(current.next_listnode is not None):
print("",current.obj)
print("\n")
current = current.next_listnode
I I create 2 list_nodes 1 of which I add it as the initial list_node of the linked list and the other using the function add_node:
A = list_node("John",None)
B = list_node("Mike",None)
liste = linked_list(A)
liste.add_node(B)
liste.print_linkedlist();
However when I call the print_linkedlist function it only prints A list_node
What am I doing wrong?
However
I tried not calling the add_node function but it didnt print anything.
If you add one more node to your list, the problem becomes a bit more clear:
A = list_node("John",None)
B = list_node("Mike",None)
C = list_node("Biff",None)
liste = linked_list(A)
liste.add_node(B)
liste.add_node(C)
liste.print_linkedlist()
This prints "John" and "Mike" -- so the problem isn't that you're only printing the first node, it's that you're not printing the last node.
That's because your print_linkedlist function stops iterating when current.next_listnode is not None -- i.e. it will stop as soon as it reaches the last node (the one with no "next" node), and it won't print that node.
I'd suggest doing this instead:
def print_linkedlist(self):
current = self.list_node
while current:
print(current.obj)
current = current.next_listnode
in order to print all nodes of the list.

Trying to save node or reference to node in a list

I have a tree class in which the class gets initialized with a data, left, and right attributes.
in the same class I have a "save" method.
I am using a list as a queue.
I am attempting to create a "save" method which takes only one argument "data".
The purpose of this save method is to dequeue from my list, check that node to see if its empty and if it is then it saves my data there. Otherwise it enqueues the 2 children of that node into the list.
The purpose of this is to save data in level order into the tree.
Because the class gets initialized there is always at least 1 element in the tree which is the root node.
The issue i keep running into is that whenever i append the self.data (the root node, not the data im currently trying to add) into my list at the beginning of the save method it only saves the data there.
and obviously when I then try to append the left and right child of this int i get an error because the int has no left or right attributes.
I am wondering how to save the node in the list instead of the data at the node.
class Tree():
aqueue = []
def __init__(self, item):
self.item = item
self.leftchild = None
self.rightchild = None
self.aqueue.append(self.item)
def add(self, newitem):
temp = self.myqueue.pop(0)
if temp is None:
temp = Tree(newitem)
else:
self.aqueue.append(temp.leftchild)
self.aqueue.append(temp.rightcild)
temp.add(newitem)
self.aqueue.clear() #this is meant to clear queue of all nodes after the recursions are complete
self.aqueue.append(self.item) #this is meant to return the root node to the queue so that it is the only item for next time
There are a couple of obvious issues with your code: both the if and else branch return, so the code after will never run, temp == newitem is an equality expression, but even if it was an assignment it wouldn't do anything:
def add(self, newitem):
temp = self.myqueue.pop(0)
if temp == None: # should use temp is None
temp == newitem # temp = newitem still wouldn't do anything
return True
else:
self.aqueue.append(temp.leftchild)
self.aqueue.append(temp.rightcild)
return temp.add(newitem)
# you will never get here, since both branches of the if returns
self.aqueue.clear() # delete everything in the list..?
self.aqueue.append(self.item)

How to filter a ttk.treeview in Python?

I have a python tkinter application that contains a ttk.treeview widget.
The widget displays a list of files found on a specific directory tree with a specific extension - this was trivial to build with tt.treeview widget.
There is a request to enable "on-the-fly" filtering of the tree - e.g., the user types in an Entry some string, and as he/she types, the tree removes elements that don't match the typed string so far.
I was exploring the Treeview documentation, tried the detach and reattach methods but with no luck.
detach indeed removes the non-matched elements from the tree, but if the user hit Backspace, I can no longer iterate correctly on the tree to restore those detached elements as get_children method will not return them.
def filter_tree(self):
search_by = self.search_entry.get()
self.tree_detach_leaf_by_regex(self.current_loaded_folder, search_by, "")
def tree_detach_leaf_by_regex(self, root, regex, parent):
if self.treeview.get_children(root):
for child in self.treeview.get_children(root):
self.tree_detach_leaf_by_regex(child, regex, root)
else:
if not re.match(regex, self.treeview.item(root)["text"]):
self.elements_index_within_parent[root] = self.treeview.index(root)
self.elements_parents[parent] = 1
self.treeview.detach(root)
else:
self.treeview.reattach(root, parent, self.elements_index_within_parent[root])
Looking forward to read your advice.
To make my answer reusable by anybody, I have to tell more than directly answering your question. If you directly want to see how I do to get detached items (thus without using the method get_children which cannot get detached items' id), jump to the definition of the method whose name is _columns_searcher.
Introduction
Let's first define some attributes.
#property
def _to_search(self):
key = 'to_search'
if key not in self._cache:
self._cache[key] = tk.StringVar()
return self._cache[key]
def _set_search_entry(self):
ent = ttk.Entry(
self.root, # or canvas, or frame ...
#...
textvariable=self._to_search
)
ent.grid(
#...
)
ent.bind(
'<Return>',
self._columns_searcher
)
return ent
#property
def search_entry(self):
key = 'search_entry'
if key not in self._cache:
self._cache[key] = self._set_search_entry()
return self._cache[key]
Core answer
What follows is the part which directly show how to re-attach user-detached items. First note that, as the OP mentions, get_children only return ids of attached items. Second note that the only thing you need to re-attach detached items is their id. Which implies thus to trace/save them when they are detached so as to be able to re-attach them.
_detached = set()
def _columns_searcher(self, event):
# originally a set returns a tuple
children = list(self._detached) + list(self.tree.get_children())
self._detached = set()
query = self._to_search.get()
self._brut_searcher(children, query.lower())
Note that children above contains all items, be them detached.
def _brut_searcher(self, children, query):
i_r = -1
for item_id in children:
text = self.tree.item(item_id)['text'] # already contains the strin-concatenation (over columns) of the row's values
if query in text:
i_r += 1
self.tree.reattach(item_id, '', i_r)
else:
self._detached.add(item_id)
self.tree.detach(item_id)
From what I see, detach is almost same as delete.
row is gone and you have no access to it.
You have to make a copy of "detached" items, just id, name or more, if you have advance treeview structure, and then go over elements in both lists and sort it out.
Difference is that if you detach item and check it's id with "exists" function, it should return true

Populating QTreeView from list of file paths

This question has been asked before at:
https://stackoverflow.com/questions/26538667/pyqt-populate-qtreeview-from-txt-file-that-contains-file-paths
But didn't seem to get a response.
I have a dataset of file paths that are formatted, like so:
hon_dev/Bob Dylan/Concept
hon_dev/Andromeda/Modeling
hon_dev/Andromeda/Lookdev
hon_dev/Andromeda/Rigging
hon_dev/Andromeda/Animation
hon_dev/Andromeda/FX
hon_dev/fsafasfas/production
hon_dev/Magebane: Acheron of Mana Aeacus/Model
hon_dev/Magebane: Acheron of Mana Aeacus/Concept
hon_dev/Magebane: Acheron of Mana Aeacus/Texture
hon_dev/Skrull/Modeling
hon_dev/Skrull/Lookdev
hon_dev/Skrull/Rigging
hon_dev/Skrull/Animation
hon_dev/Skrull/FX
hon_dev/Bob Mylan/Modeling
hon_dev/Bob Mylan/Lookdev
hon_dev/Bob Mylan/Rigging
hon_dev/Bob Mylan/Animation
hon_dev/Bob Mylan/FX
hon_dev/Handsome Man/Concept
hon_dev/Handsome Man/Modeling
hon_dev/Handsome Man/Lookdev
hon_dev/Handsome Man/Rigging
hon_dev/Handsome Man/Animation
hon_dev/Handsome Man/FX
demo-sync/Drone Craft/Modelling Drone Craft
demo-sync/Drone Craft/Texturing and Shading of Drone Craft
demo-sync/Drone Craft/Rigging Drone Parts
And I'm trying to get them to fill up a QTreeView (PySide). The current code I have is as such, with a simple recursive function:
def doIt(self):
self.model = QtGui.QStandardItemModel()
# self.model.setHorizontalHeaderLabels = ['test']
topLevelParentItem = self.model.invisibleRootItem()
# create all itewms first
# iterate over each string url
for item in data:
splitName = item.split('/')
# first part of string is defo parent item
# check to make sure not to add duplicate
if len(self.model.findItems(splitName[0], flags=QtCore.Qt.MatchFixedString)) == 0:
parItem = QtGui.QStandardItem(splitName[0])
topLevelParentItem.appendRow(parItem)
def addItems(parent, elements):
# check if not reached last item in the list of items to add
if len(elements) != 0:
print "currently eval addItems({0}, {1}".format(parent.text(), elements)
# check if item already exists, if so do not create
# new item and use existing item as parent
if len(self.model.findItems(elements[0], flags=QtCore.Qt.MatchFixedString)) == 0:
print "item being created for {0}".format(elements[0])
item = QtGui.QStandardItem(elements[0])
else:
print "not adding duplicate of: {0}".format(elements[0])
item = self.model.findItems(elements[0], flags=QtCore.Qt.MatchFixedString)[0]
print "the item to act as non-duplicate is: {0}".format(item.text())
child = elements[1:]
print "child is {0}".format(child)
# call recursive function to add
addItems(item, child)
print "parenting: {0} to {1}".format(item.text(), parent.text())
parent.appendRow(item)
addItems(parItem, splitName[1:])
print 'done: ' + item + '\n'
self.inst.col_taskList.setModel(self.model)
However, because I can't find any way to look through a QStandardItem for existing rows, I'm getting this in the UI as a result:
Is there a way to find duplicates rows in a QStandardItem or traverse the QStandardItemModel to find the existing QStandardItem? I've been struggling with this problem for the past 2 days and trying to find an existing example, and I can't really wrap my head around how this could be such a complication...
Any help/advice on this would be appreciated! Thanks!
Hmm, after a bit of faffing about, I've come up with something that works for now, though the file paths must be in order for this to work:
def doIt(self):
print "\n\n\n\n"
self.model = QtGui.QStandardItemModel()
topLevelParentItem = self.model.invisibleRootItem()
# iterate over each string url
for item in data:
splitName = item.split('/')
# first part of string is defo parent item
# check to make sure not to add duplicate
if len(self.model.findItems(splitName[0], flags=QtCore.Qt.MatchFixedString)) == 0:
parItem = QtGui.QStandardItem(splitName[0])
topLevelParentItem.appendRow(parItem)
def addItems(parent, elements):
"""
This method recursively adds items to a QStandardItemModel from a list of paths.
:param parent:
:param elements:
:return:
"""
for element in elements:
# first check if this element already exists in the hierarchy
noOfChildren = parent.rowCount()
# if there are child objects under specified parent
if noOfChildren != 0:
# create dict to store all child objects under parent for testing against
childObjsList = {}
# iterate over indexes and get names of all child objects
for c in range(noOfChildren):
childObj = parent.child(c)
childObjsList[childObj.text()] = childObj
if element in childObjsList.keys():
# only run recursive function if there are still elements to work on
if elements[1:]:
addItems(childObjsList[element], elements[1:])
return
else:
# item does not exist yet, create it and parent
newObj = QtGui.QStandardItem(element)
parent.appendRow(newObj)
# only run recursive function if there are still elements to work on
if elements[1:]:
addItems(newObj, elements[1:])
return
else:
# if there are no existing child objects, it's safe to create the item and parent it
newObj = QtGui.QStandardItem(element)
parent.appendRow(newObj)
# only run recursive function if there are still elements to work on
if elements[1:]:
# now run the recursive function again with the latest object as the parent and
# the rest of the elements as children
addItems(newObj, elements[1:])
return
# call proc to add remaining items after toplevel item to the hierarchy
print "### calling addItems({0}, {1})".format(parItem.text(), splitName[1:])
addItems(parItem, splitName[1:])
print 'done: ' + item + '\n'
self.inst.col_taskList.setModel(self.model)

Convert recursive generator object to list

I have been trying to implement a simple tree structure in Python. A tree begins at a single "root" node which has children, each of its children may have own children and so forth.
Now, I want to print the names of all nodes of the tree, that is I want to convert it to a list. I sought to employ recursiveness but unfortunately yielding recursively returns a sub-tree of generator objects which I cannot convert to nodes.
Could somebody help me and point out what I am doing wrong here please?
class Node:
def __init__(self,name):
self.name = name
self.children = []
self.parent = None
def appendChild(self,child):
self.children.append(child)
if child is not None:
child.parent = self
def listChildren(self):
yield self
for child in self.children:
yield child.listChildren()
raise StopIteration
# test
r = Node("root")
n = Node("name")
r.appendChild(n)
n.appendChild(Node("name2"))
n.appendChild(Node("name3"))
c = Node("child")
n.appendChild(c)
c.appendChild(Node("child2"))
c.appendChild(Node("child3"))
r.appendChild(Node("name4"))
r.appendChild(Node("name5"))
r.appendChild(Node("name6"))
for child in r.listChildren():
print child.name
Output:
Traceback (most recent call last):
File "C:/Users/User/Documents/TreeNode.py", line 40, in <module>
print child.name
AttributeError: 'generator' object has no attribute 'name'
A generator is supposed to be called when it is being iterated over, but in my case every child in r.listChildren() is, in turn, a generator object. If this is a design flaw, then I would have to look for another way of generating a list of node names.
Thank you in advance!
child.listChildren() will return a generator object and not the actual children. So you probably want to do something like:
def listChildren(self):
yield self
for child in self.children:
for c in child.listChildren():
yield c
raise StopIteration # PS: you don't need to do that explicitly
Alternatively if you use Python 3.3 you could do:
def listChildren(self):
yield self
for child in self.children:
yield from child.listChildren()

Categories

Resources