Kivy Tree View how to save the user's selection? - python

I have a program with Tree View, I want to use the client selection when he presses on one of the labels. The program is based on client-server but the rest of the code is not relevant. the file_system object is CloudSystem type, the CloudSystem class creates a dictionary of the provided path.
.py file
class CloudSystem:
def __init__(self, cloud_path):
self.root = cloud_path
self.cloud_tree = {}
self.current_dict = self.cloud_tree
self.current_dict[os.path.basename(self.root)] = {}
self.cloud_tree = self.create_cloud( self.root)
def create_cloud(self, root_dir):
dir = {}
root_dir = root_dir.rstrip(os.sep)
start = root_dir.rfind(os.sep) + 1
for path, dirs, files in os.walk(root_dir):
folders = path[start:].split(os.sep)
subdir = dict.fromkeys(files)
parent = reduce(dict.get, folders[:-1], dir)
parent[folders[-1]] = subdir
return dir
def to_string(self):
print(self.cloud_tree)
def get_tree(self):
return self.cloud_tree
def populate_tree_view(tree_view, parent, node):
if node is not None:
if parent is None:
for key in node:
tree_node = tree_view.add_node(TreeViewLabel(text=key,
is_open=False))
if node[key] is not None:
populate_tree_view(tree_view, tree_node, node[key])
else:
for key in node:
tree_node = tree_view.add_node(TreeViewLabel(text=key,
is_open=False), parent)
if node[key] is not None:
populate_tree_view(tree_view, tree_node, node[key])
class CloudView(Screen, FloatLayout):
def __init__(self, **kwargs):
super(CloudView, self).__init__(**kwargs)
self.tv = TreeView(root_options=dict(text='My Cloud'),
hide_root=False,
indent_level=4, )
populate_tree_view(self.tv, None, self.show_cloud())
self.add_widget(self.tv)
def show_cloud(self):
s.send('CLOUD'.encode('utf-8'))
file_system = pickle.loads(s.recv(4096))
return file_system.get_tree()
def on_selection(self):
pass
.kv file
<CloudView>:
name: "cloud"
id: cloud_view

Related

Convert node tree to dictionary

Below is the node class definition with tree structure (built using node instance) of programming languages. Now how to convert hierarchical node data tree structure to python dictionary using node class method ?
See desired output at bottom.
class Node(object):
def __init__(self, name, parent=None):
self._name = name
self._children = []
self._parent = parent
if parent is not None:
parent.addChild(self)
def addChild(self, child):
self._children.append(child)
def name(self):
return self._name
def setName(self, name):
self._name = name
def child(self, row):
return self._children[row]
def childCount(self):
return len(self._children)
def parent(self):
return self._parent
rootNode = nodeData.Node("books")
web_node = nodeData.Node("web", rootNode)
database_node = nodeData.Node("database", rootNode)
front_end_node = nodeData.Node("front-end", web_node)
back_end_node = nodeData.Node("back-end", web_node)
sql_node = nodeData.Node("sql", database_node)
nosql_node = nodeData.Node("nosql", database_node)
html_node = nodeData.Node("html", front_end_node)
css_node = nodeData.Node("css", front_end_node)
js_node = nodeData.Node("js", front_end_node)
php_node = nodeData.Node("php", back_end_node)
python_node = nodeData.Node("python", back_end_node)
mysql_node = nodeData.Node("mysql", sql_node)
postgresql_node = nodeData.Node("postgresql", sql_node)
mongodb_node = nodeData.Node("mongodb", nosql_node)
cassandra_node = nodeData.Node("cassandra", nosql_node)
html_book_one_node = nodeData.Node("the missing manual", html_node)
html_book_two_node = nodeData.Node("core html5 canvas", html_node)
css_book_one_node = nodeData.Node("css pocket reference", css_node)
css_book_two_node = nodeData.Node("css in depth", css_node)
js_book_one_node = nodeData.Node("you don't know js", js_node)
js_book_two_node = nodeData.Node("eloquent javascript", js_node)
php_book_one_node = nodeData.Node("modern php", php_node)
python_book_one_node = nodeData.Node("dive into python", python_node)
python_book_two_node = nodeData.Node("python for everybody", python_node)
python_book_three_node = nodeData.Node("Think Python", python_node)
mongodb_book_one_node = nodeData.Node("mongodb in action", mongodb_node)
mongodb_two_node = nodeData.Node("scaling mongodb", mongodb_node)
Output
From node tree abstraction to python dictionary
{"books":{
"web":{
"front-end":{
"html":["the missing manual", "core html5 canvas"],
"css":["css pocket reference", "css in depth"],
"js":["you don't know js", "eloquent javascript"]
},
"back-end":{
"php":["modern php"],
"python":["dive into python", "python for everybody",
"Think Python"]
}
},
"database":{
"sql":{
"mysql":[],
"postgresql":[]
},
"nosql":{
"mongodb":["mongodb in action", "scaling mongodb"],
"cassandra":[]
}}}}
Updated Code
class Node(object):
def __init__(self, name, parent=None):
self._name = name
self._children = []
self._parent = parent
if parent is not None:
parent.addChild(self)
def addChild(self, child):
self._children.append(child)
def name(self):
return self._name
def setName(self, name):
self._name = name
def child(self, row):
return self._children[row]
def childCount(self):
return len(self._children)
def parent(self):
return self._parent
class categoryNode(Node):
def __init__(self, name, parent=None):
super(categoryNode, self).__init__(name, parent)
class subCategoryNode(Node):
def __init__(self, name, parent=None):
super(subCategoryNode, self).__init__(name, parent)
class languageNode(Node):
def __init__(self, name, parent=None):
super(languageNode, self).__init__(name, parent)
class BookNode(Node):
def __init__(self, name, parent=None):
super(BookNode, self).__init__(name, parent)
rootNode = Node("books")
web_node = categoryNode("web", rootNode)
database_node = categoryNode("database", rootNode)
front_end_node = subCategoryNode("front-end", web_node)
back_end_node = subCategoryNode("back-end", web_node)
sql_node = subCategoryNode("sql", database_node)
nosql_node = subCategoryNode("nosql", database_node)
html_node = languageNode("html", front_end_node)
css_node = languageNode("css", front_end_node)
js_node = languageNode("js", front_end_node)
php_node = languageNode("php", back_end_node)
python_node = languageNode("python", back_end_node)
mysql_node = languageNode("mysql", sql_node)
postgresql_node = languageNode("postgresql", sql_node)
mongodb_node = languageNode("mongodb", nosql_node)
cassandra_node = languageNode("cassandra", nosql_node)
html_book_one_node = BookNode("the missing manual", html_node)
html_book_two_node = BookNode("core html5 canvas", html_node)
css_book_one_node = BookNode("css pocket reference", css_node)
css_book_two_node = BookNode("css in depth", css_node)
js_book_one_node = BookNode("you don't know js", js_node)
js_book_two_node = BookNode("eloquent javascript", js_node)
php_book_one_node = BookNode("modern php", php_node)
python_book_one_node = BookNode("dive into python", python_node)
python_book_two_node = BookNode("python for everybody", python_node)
python_book_three_node = BookNode("Think Python", python_node)
mongodb_book_one_node = BookNode("mongodb in action", mongodb_node)
mongodb_two_node = BookNode("scaling mongodb", mongodb_node)
You have a bigger problem, because you're using the same class to represent both book categories and actual books. Given this, it is impossible to programmatically determine that mysql_node and postgresql_node are empty categories rather than books.
To make this work how you want, you will need to redesign the data structure. I suggest having a list _children for subcategories and another list _books for book titles (as strings). Note that this data structure is still a little ambiguous because a node with no subcategories and no books could be rendered as an empty list (i.e. a terminal category with no books) or an empty dictionary (i.e. a non-terminal category with no subcategories); I infer from the question that an empty list is the desired result.
class Node:
def __init__(self, name, parent=None):
self._name = name
self._children = []
self._parent = parent
self._books = []
if parent is not None:
parent.addChild(self)
def name(self):
return self._name
def setName(self, name):
self._name = name
def parent(self):
return self._parent
def addChild(self, child):
if self._books:
raise ValueError('Node cannot have both sub-categories and books')
self._children.append(child)
def child(self, row):
return self._children[row]
def childCount(self):
return len(self._children)
def addBook(self, book):
if self._children:
raise ValueError('Node cannot have both sub-categories and books')
self._books.append(book)
def book(self, row):
return self._books[row]
def bookCount(self):
return len(self._books)
rootNode = Node("books")
web_node = Node("web", rootNode)
database_node = Node("database", rootNode)
front_end_node = Node("front-end", web_node)
back_end_node = Node("back-end", web_node)
sql_node = Node("sql", database_node)
nosql_node = Node("nosql", database_node)
html_node = Node("html", front_end_node)
css_node = Node("css", front_end_node)
js_node = Node("js", front_end_node)
php_node = Node("php", back_end_node)
python_node = Node("python", back_end_node)
mysql_node = Node("mysql", sql_node)
postgresql_node = Node("postgresql", sql_node)
mongodb_node = Node("mongodb", nosql_node)
cassandra_node = Node("cassandra", nosql_node)
html_node.addBook("the missing manual")
html_node.addBook("core html5 canvas")
css_node.addBook("css pocket reference")
css_node.addBook("css in depth")
js_node.addBook("you don't know js")
js_node.addBook("eloquent javascript")
php_node.addBook("modern php")
python_node.addBook("dive into python")
python_node.addBook("python for everybody")
python_node.addBook("Think Python")
mongodb_node.addBook("mongodb in action")
mongodb_node.addBook("scaling mongodb")
def node_to_dict(node):
def helper(n):
if n.childCount() > 0:
return { c.name(): helper(c) for c in n._children }
else:
return list(n._books)
return { node.name(): helper(node) }
The result of node_to_dict(rootNode) does == your expected output.
Simple recursive function:
def to_dict(node):
if isinstance(node, nodeData.Node):
return {node._name:to_dict(node._children)}
if all(isinstance(i, nodeData.Node) for i in node):
return (lambda x:x if all(x.values()) else list(x))({i._name:to_dict(i._children) for i in node})
return node
print(to_dict(rootNode))
Output:
{'books': {'web': {'front-end': {'html': ['the missing manual', 'core html5 canvas'], 'css': ['css pocket reference', 'css in depth'], 'js': ["you don't know js", 'eloquent javascript']}, 'back-end': {'php': ['modern php'], 'python': ['dive into python', 'python for everybody', 'Think Python']}}, 'database': {'sql': ['mysql', 'postgresql'], 'nosql': ['mongodb', 'cassandra']}}}

wxpython DataViewCtrl - correctly identify drop target

I would like to use a DataViewCtrl with a PyDataViewModel as tree-like control with drag-and-drop support. I am almost there but I cannot figure out how to distinguish between dropping an item between the lines or on top of the parent item. The two screenshots illustrate the issue. In both cases the item identified as the drop target is the parent item.
This is the code:
import wx
import wx.dataview as dv
print(wx.__version__)
class Container(list):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Container {}'.format(self.name)
class Element(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Element {}'.format(self.name)
#property
def len(self):
return str(len(self.name))
class MyTreeListModel(dv.PyDataViewModel):
def __init__(self, data):
dv.PyDataViewModel.__init__(self)
self.data = data
def GetColumnCount(self):
return 2
def GetColumnType(self, col):
mapper = { 0 : 'string',
1 : 'string'
}
return mapper[col]
def GetChildren(self, parent, children):
if not parent:
for cont in self.data:
children.append(self.ObjectToItem(cont))
return len(self.data)
node = self.ItemToObject(parent)
if isinstance(node, Container):
for ds in node:
children.append(self.ObjectToItem(ds))
return len(node)
return 0
def IsContainer(self, item):
if not item:
return True
node = self.ItemToObject(item)
if isinstance(node, Container):
return True
return False
def GetParent(self, item):
if not item:
return dv.NullDataViewItem
node = self.ItemToObject(item)
if isinstance(node, Container):
return dv.NullDataViewItem
elif isinstance(node, Element):
for g in self.data:
try:
g.index(node)
except ValueError:
continue
else:
return self.ObjectToItem(g)
def GetValue(self, item, col):
node = self.ItemToObject(item)
if isinstance(node, Container):
mapper = { 0 : node.name,
1 : '',
}
return mapper[col]
elif isinstance(node, Element):
mapper = { 0 : node.name,
1 : node.len
}
return mapper[col]
else:
raise RuntimeError("unknown node type")
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Table")
panel = wx.Panel(self)
dvcTree = dv.DataViewCtrl(panel,style=wx.BORDER_THEME|dv.DV_MULTIPLE)
self.model = MyTreeListModel(data)
dvcTree.AssociateModel(self.model)
dvcTree.AppendTextColumn("Container", 0, width=80)
dvcTree.AppendTextColumn("Element", 1, width=80)
dvcTree.Bind(dv.EVT_DATAVIEW_ITEM_BEGIN_DRAG, self._onDrag)
dvcTree.Bind(dv.EVT_DATAVIEW_ITEM_DROP, self._onEndDrag)
dvcTree.Bind(dv.EVT_DATAVIEW_ITEM_DROP_POSSIBLE, self._onDropPossible)
self.dvcTree = dvcTree
dvcTree.EnableDragSource(wx.DataFormat(wx.DF_UNICODETEXT))
dvcTree.EnableDropTarget(wx.DataFormat(wx.DF_UNICODETEXT))
box = wx.BoxSizer(wx.VERTICAL)
box.Add(dvcTree, 1, wx.EXPAND)
panel.SetSizer(box)
self.Layout()
def _onDropPossible(self, evt):
item = evt.GetItem()
mod = evt.GetModel()
if not evt.GetItem().IsOk():
return
def _onEndDrag(self, evt):
if not evt.GetItem().IsOk():
evt.Veto()
return
mod = evt.GetModel()
print('dropped at', mod.ItemToObject(evt.GetItem()))
try:
print('parent:',mod.ItemToObject(mod.GetParent(evt.GetItem())))
except TypeError:
print('parent: None')
def _onDrag(self, evt):
evt.Allow()
mod = evt.GetModel()
print('from', mod.GetValue(evt.GetItem(),0))
evt.SetDataObject(wx.TextDataObject('don\'t know how to retrieve that information in the drop handler'))
evt.SetDragFlags(wx.Drag_AllowMove)
data = [Container('eins'),Container('zwei'),Container('drei')]
for d in data:
d[:] = [Element('element {}'.format('X'*q)) for q in range(5)]
if __name__ == "__main__":
app = wx.App()
f = MyFrame(None)
f.Show()
app.MainLoop()
EDIT:
This is mostly a problem on OSX since the MSW implementation does not offer to drop between lines. I was not able to test it on linux though.

Disappearing data in extended QTreeWidgetItem

UPDATE: (SOLVED)
Thanks to eyllanesc for making me write a working example code of my problem. That made me find the problem. Here is a working code with a fixed problem:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
import os
class ExtendedTreeWidgetItem(QTreeWidgetItem):
def __init__(self, *args, **kwargs):
super(ExtendedTreeWidgetItem, self).__init__(*args, **kwargs)
self.rescueFile = None
print("created first time")
def setRescueFile(self, rFile):
self.rescueFile = rFile
print("setting rescue file", self.rescueFile, "for item", self.text(0))
def getRescueFile(self):
return self.rescueFile
class RescueFile:
def __init__(self, path):
self.path = path
self.ischecked = True
def isChecked(self):
return self.ischecked
def setChecked(self, checked):
if isinstance(checked, bool):
self.ischecked = checked
elif isinstance(checked, Qt.CheckState):
self.ischecked = True if checked == Qt.Checked else False
def getPath(self):
return self.path
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
windowSize = (800,400)
screenSize = QDesktopWidget().availableGeometry()
self.setGeometry(screenSize.width()/2 - windowSize[0]/2, screenSize.height()/2 - windowSize[1]//2, windowSize[0], windowSize[1])
self.setWindowTitle("TreeTest")
self.buildGui()
self.show()
def buildGui(self):
wgt = QWidget()
vlayout = QVBoxLayout()
vlayout.setAlignment(Qt.AlignTop)
grid_layout = QGridLayout()
self.setupTree(grid_layout)
vlayout.addLayout(grid_layout)
wgt.setLayout(vlayout)
self.setCentralWidget(wgt)
def setupTree(self, grid_layout):
self.treeWidget = QTreeWidget()
self.treeWidget.setHeaderLabel("Results")
self.treeWidget.setColumnCount(1)
#INSERTING DATA
topItem = ExtendedTreeWidgetItem()
topItem.setText(0, "top item")
topItem.setCheckState(0, Qt.Checked)
child1 = ExtendedTreeWidgetItem(topItem)
child1.setText(0, "child 1")
child1.setCheckState(0, Qt.Checked)
topItem.addChild(child1)
deeper_child = ExtendedTreeWidgetItem(child1)
deeper_child.setText(0, "deeper child 1")
deeper_child.setCheckState(0, Qt.Checked)
r_file = RescueFile("/home/user1/Desktop")
deeper_child.setRescueFile(r_file)
child1.addChild(deeper_child)
self.treeWidget.addTopLevelItem(topItem)
self.treeWidget.expandAll()
self.treeWidget.itemChanged.connect(self.singleClickTreeWidget)
grid_layout.addWidget(self.treeWidget, 1, 1, 4, 3)
def singleClickTreeWidget(self, widgetItem, column):
parent = widgetItem.parent()
if parent and parent.checkState(0) == Qt.Unchecked:
widgetItem.setCheckState(0, Qt.Unchecked)
return
checkState = widgetItem.checkState(0)
widgetItem.setCheckState(0, checkState)
rescue_file = widgetItem.getRescueFile()
if rescue_file:
rescue_file.setChecked(checkState)
print("rescue file found, path:", rescue_file.getPath(), "checked:", rescue_file.isChecked())
self.iterateThroughChildren(widgetItem, checkState)
def iterateThroughChildren(self, item, checkState):
for i in range(item.childCount()):
child = item.child(i)
print("child:", child, ",text:", child.text(0))
child.setCheckState(0, checkState)
# HERE WAS THE MISTAKE
# -- >> rescue_file = item.getRescueFile() << --
rescue_file = child.getRescueFile() # CORRECT CODE!!
print("rescue file", rescue_file, "child", child.text(0))
if rescue_file is not None:
rescue_file.setChecked(checkState)
else:
self.iterateThroughChildren(child, checkState)
def main():
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
app.exec_()
main()
BEFORE UPDATE:
I hope I can explain my problem clearly enough.
I trying to create QTreeWidget that uses my extended QTreeWidgetItems in python 3.5.3. Here is my code for the item class:
class ExtendedTreeWidgetItem(QTreeWidgetItem):
def __init__(self, *args, **kwargs):
super(ExtendedTreeWidgetItem, self).__init__(*args, **kwargs)
self.rescueFile = None
def setRescueFile(self, rFile):
self.rescueFile = rFile
def getRescueFile(self):
return self.rescueFile
class RescueFile:
def __init__(self, path):
self.path = path
self.ischecked = True
def isChecked(self):
return self.ischecked
def setChecked(self, checked):
if isinstance(checked, bool):
self.ischecked = checked
elif isinstance(checked, Qt.CheckState):
self.ischecked = True if checked == Qt.Checked else False
print(self.path, self.ischecked)
I use this code to implement checking and unchecking the rescue file:
****
...
self.treeWidget.itemChanged.connect(self.singleClickTreeWidget)
...
****
def singleClickTreeWidget(self, widgetItem, column):
parent = widgetItem.parent()
if parent and parent.checkState(0) == Qt.Unchecked:
widgetItem.setCheckState(0, Qt.Unchecked)
return
checkState = widgetItem.checkState(0)
widgetItem.setCheckState(0, checkState)
rescue_file = widgetItem.getRescueFile()
**# I CAN GET THE RESCUE FILE OBJECT HERE FROM**
if rescue_file:
rescue_file.setChecked(checkState)
self.iterateThroughChildren(widgetItem, checkState)
def iterateThroughChildren(self, item, checkState):
for i in range(item.childCount()):
child = item.child(i)
child.setCheckState(0, checkState)
**# I CAN'T GET ANY FIND HERE ANYMORE**
rescue_file = item.getRescueFile()
if rescue_file:
rescue_file.setChecked(checkState)
else:
self.iterateThroughChildren(child, checkState)
What my code does is that it generates a checkable tree like in the picture:
What I'm trying to achieve is that when I deselect/select an item (ExtendedTreeWidgetItem) that has children, all children are also selected/deselected as well as RescueFile objects associated with items. Only files are associated with RescueFile objects. Directories are left with self.rescueFile = None
For example if I pressed to deselect 'icons' then magnifier.png should also be deselected as well as RescueFile associated with it. Deselection of a checkboxes works like a charm but RescueFile is not affected (NOT FOUND) if I press on a parent of a file. But it works if I press directly on a file, for example magnifier.png.
I have tried to trace if it is a pointer problem but it seems like all objects point to the objects they are supposed to. I don't understand where does a rescueFile disappear if I am comming to ExtendedTreeWidgetItem recursively through it's parent.

Get child node if exists otherwise create in Python

I have a basic class containing two attributes 'Name' and 'Children.
class Node():
def __init__(self, name):
self.name = name
self.children = []
I have a core root node as follows:
# Create Master Root Node
root = Node('root')
How would I go about creating a function that I could pass a path to a specific node in the tree, and the function return that node.
However if that node doesn't exist it would create/append to the tree, and still return the node.
path = ['Leslie','Marie','Tori'] # simple example
def get_node_by_path(path=[])...
If a path failed before reaching the end of the path, it would automatically create the missing nodes in order to make the entire path complete.
path = ['Leslie','Marie','Tori','Kevin'] # more complex requires two nodes to be created
def get_node_by_path(path=[])...
I'd do something like this. It's a non recursive solution.
def get_node_by_path(path):
cur_node = root
for elem_name in path:
found = False
for child in cur_node.children:
if child.name == elem_name:
cur_node = child
found = True
break
if not found:
new_node = Node(elem_name)
cur_node.children.append(new_node)
cur_node = new_node
return cur_node
class Node:
def __init__(self, name):
self.name = name
self.children = []
def path_to(self, path):
if not path:
return self
head, *tail = path
for child in self.children:
if child.name == head:
return child.path_to(tail)
newborn = Node(head)
self.children.append(newborn)
return newborn.path_to(tail)
Here's a solution that recursively considers whether the first name in the list is a child of the current node.
For Python 2, head, *tail = path can be replaced with
head = path[0]
tail = path[1:]
start with the last dag object and go to the root
class Node():
def __init__(self, name = "", childern = []):
self.name = name
self.children = childern
print ("Node Name: {0} Childern Nodes {1}".format(self.name, self.children))
def get_node_by_path(path=[]):
for c,n in enumerate(reversed(path)):
if not c:
Node(name = n)
else:
Node(name = n, childern = path[-c:])
path = ['root', 'Leslie','Marie','Tori','Kevin']
get_node_by_path(path)

Python not returning the same object?

Here is my code:
class Node:
nodes = {}
def __init__(self, name):
self.name = name
self.parent = []
self.children = []
Node.nodes[self.name] = self
def addParent(self, parent):
print "adding parent %s for %s " % (parent, self.name)
self.parent.append(parent)
print self.parent
def addChild(self, child):
self.children.append(child)
def removeParent(self, parent):
try:
self.parent.remove(parent)
except:
pass
def removeChild(self, child):
try:
self.children.remove(child)
except:
pass
def lookup(obj):
print "calling look up"
Node.nodes.get(obj)
def create_node(obj):
return lookup(obj) or Node(obj)
# Tree has Nodes
class Tree:
trees = {}
def __init__(self, name, root):
self.name = name
self.root = root
self.size = 1
self.nodes = set() # tree has unique nodes
self.nodes.add(root)
Tree.trees[self.name] = self
def addNode(self, node):
self.nodes.add(node)
self.size += 1
def removeNode(self, node):
try:
self.nodes.remove(node)
except:
return
self.size -= 1
def setRoot(self, root):
self.root = root
def print_tree(self):
for i in self.nodes:
if i == self.root.name:
print "root: %s" % i
else:
print i
def main():
roota = create_node("a")
ta = Tree("a", roota)
childaa = create_node("a_a")
roota.addChild(childaa)
childaa.addParent(roota)
ta.addNode(childaa)
childab = create_node("a_b")
roota.addChild(childab)
childab.addParent(roota)
ta.addNode(childab)
# make one of the child of a root
rootb = create_node("a_a") # should give me a node that already exists from the above tree
tb = Tree("a_a", rootb)
childbb = create_node("a_b") # this node should have two parents now, a and a_a
rootb.addChild(childbb)
childbb.addParent(rootb)
tb.addNode(childbb)
for node in Node.nodes.itervalues():
print "Name: %s" % node.name
if node.parent:
print "Parent: %s" % [parent.name for parent in node.parent]
else:
print "Parent: %s" % node.parent
print "Children: ", [node.name for node in node.children]
print ""
if __name__ == '__main__':
main()
And the output of the script:
Name: a
Parent: []
Children: ['a_a', 'a_b']
Name: a_a
Parent: []
Children: ['a_b']
Name: a_b
Parent: ['a_a']
Children: []
a_a should have parent a. line 80 is adding a as a parent for a_a
a_b should have parent a_a and a. line 85 is adding a as a parent for a_b
can someone explain to me why that's not the case in this code?
And desired the output of the script:
Name: a
Parent: []
Children: ['a_a', 'a_b']
Name: a_a
Parent: ['a']
Children: ['a_b']
Name: a_b
Parent: ['a', 'a_a']
Children: []
A tree is a directed cyclic-free graph. Each node of a tree is itself a tree, hence you don't need two classes for tree and node (except you want to give some metainfo to your tree).
It is sufficient to keep track either of children or of the parent, but for convenience sake (e.g. transversing the tree in both directions) you can save both. But if you do, you have to take care that isParent(a,b) be tautological to isChild(b,a). In your code, if you add a node and don't manually set its parent, your tree goes haywire.
Said this, "# this node should have two parents now, a and a_a" doesn't make much sense if we are talking about trees.
A basic tree structure looks like this (not validating for cycles):
class Tree:
def __init__ (self, payload):
self.payload = payload
self.children = set ()
self.parent = None
def __iadd__ (self, child):
if child.parent: raise Exception ('Node already attached')
child.parent = self #update parent
self.children.add (child) #update children's list
return self
def detach (self):
if not self.parent: return
self.parent.children.remove (self) #update parent's children's list
self.parent = None #update self
def pprint (self, level = 0):
print (' ' * level + self.payload)
for child in self.children:
child.pprint (level + 2)
And an example is this:
root = Tree ('root')
a = Tree ('A')
b = Tree ('B')
c = Tree ('C')
root += a
root += b
b += c
root.pprint ()
c.detach ()
a += c
root.pprint ()
I hope you can take some ideas from this snippet about how to build a tree.

Categories

Resources