Skip to content

Commit

Permalink
Updates to trees #403
Browse files Browse the repository at this point in the history
Trees now have tooltips to show properties (must be enabled by calling `.showTreeAttributes()`

Started working on a right-click menu.
Strted working on modifying trees.
  • Loading branch information
jarvisteach committed May 31, 2018
1 parent 4bcc250 commit de42acc
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 30 deletions.
139 changes: 129 additions & 10 deletions appJar/appjar.py
Expand Up @@ -9025,6 +9025,8 @@ def tree(self, title, value=None, *args, **kwargs):
dblClick = kwargs.pop("dbl", None)
edit = kwargs.pop("edit", None)
editable = kwargs.pop("editable", None)
showAttr = kwargs.pop("attributes", None)
showMenu = kwargs.pop("menu", None)

fg = kwargs.pop("fg", None)
bg = kwargs.pop("bg", None)
Expand All @@ -9047,12 +9049,13 @@ def tree(self, title, value=None, *args, **kwargs):
if edit is not None: self.setTreeEditFunction(title, edit)
if dblClick is not None: self.setTreeDoubleClickFunction(title, dblClick)
if editable is not None: self.setTreeEditable(title, editable)
if showAttr is not None: self.showTreeAttributes(title, showAttr)
if showMenu is not None: self.showTreeMenu(title, showMenu)
return tree

def addTree(self, title, data, row=None, column=0, colspan=0, rowspan=0):
''' adds a navigatable tree, displaying the specified xml text '''
self.widgetManager.verify(self.Widgets.Tree, title)

self._importAjtree()
if parseString is False:
self.warn("Unable to parse xml files. .addTree() not available")
Expand All @@ -9077,11 +9080,39 @@ def _buildTree(self, title, xmlDoc, row=None, column=0, colspan=0, rowspan=0):
takefocus=1)
self._positionWidget(frame, row, column, colspan, rowspan, "NSEW")

item = self._makeAjTreeData()(xmlDoc.documentElement)
node = self._makeAjTreeNode()(frame.getPane(), None, item)
self.widgetManager.add(self.Widgets.Tree, title, node)
treeData = self._makeAjTreeData()(xmlDoc)
gui.trace("TreeData populated: %s", title)

treeNode = self._makeAjTreeNode()(frame.getPane(), None, treeData)
gui.trace("TreeNode created: %s", title)

self.widgetManager.add(self.Widgets.Tree, title, treeNode)
# update() & expand() called in go() function
return node
return treeNode

# not complete yet...
def clearTree(self, title):
tree = self.widgetManager.get(self.Widgets.Tree, title)
tree.destroy()
tree.update()

def showTreeAttributes(self, title, show=True):
tree = self.widgetManager.get(self.Widgets.Tree, title)
self._loadTooltip()
tree.showAttributes(show)

# not complete yet...
def showTreeMenu(self, title, show=True):
tree = self.widgetManager.get(self.Widgets.Tree, title)
tree.showMenu(show)

# not complete yet...
def addTreeChild(self, title, data):
tree = self.widgetManager.get(self.Widgets.Tree, title)
if isinstance(data, UNIVERSAL_STRING):
data = parseString(data)
treeData = self._makeAjTreeData()(data)
tree.addChild(treeData)

def setTreeEditable(self, title, value=True):
tree = self.widgetManager.get(self.Widgets.Tree, title)
Expand Down Expand Up @@ -9145,8 +9176,11 @@ def getTreeSelectedXML(self, title):
def generateTree(self, title):
""" displays data inside tree """
tree = self.widgetManager.get(self.Widgets.Tree, title)
gui.trace("Generating Tree: %s", title)
tree.update()
gui.trace("Tree updated: %s", title)
tree.expand()
gui.trace("Tree expanded: %s", title)

#####################################
# FUNCTIONS to add Message Box
Expand Down Expand Up @@ -11591,15 +11625,17 @@ def selectItem(self, position):
# Tree Widget Class
# https://www.safaribooksonline.com/library/view/python-cookbook-2nd/0596007973/ch11s11.html
# idlelib -> TreeWidget.py
# https://svn.python.org/projects/python/trunk/Lib/idlelib/TreeWidget.py
# modify minidom - https://wiki.python.org/moin/MiniDom
#####################################
def _makeAjTreeNode(self):
class AjTreeNode(TreeNode, object):

def __init__(self, canvas, parent, item):

super(AjTreeNode, self).__init__(canvas, parent, item)

self.hasAttr = False
self.showAttr = False
self.bgColour = None
self.fgColour = None
self.bgHColour = None
Expand All @@ -11613,6 +11649,14 @@ def __init__(self, canvas, parent, item):
self.bgHColour = self.parent.bgHColour
self.fgHColour = self.parent.fgHColour
self.editEvent = self.parent.editEvent
self.showAttr = self.parent.showAttr
else:
# set this once, in parent
self.canvas.menu = None
self.canvas.lastSelected = None

self.menuBound = False

# customised config setters
def config(self, cnf=None, **kw):
self.configure(cnf, **kw)
Expand All @@ -11626,14 +11670,37 @@ def configure(self, cnf=None, **kw):
if "fg" in kw:
self.setFgColour(kw.pop("fg"))

# # propagate anything left
# super(AjTreeNode, self).config(cnf, **kw)
# # propagate anything left
# super(AjTreeNode, self).config(cnf, **kw)

# NOT COMPLETE
def addChild(self, child):
child = self.__class__(self.canvas, self, child)
self.children.append(child)
self.update()

def registerEditEvent(self, func):
self.editEvent = func
for c in self.children:
c.registerEditEvent(func)

def showAttributes(self, show):
self.showAttr = show
for c in self.children:
c.showAttributes(show)
self.update()

def showMenu(self, show):
if show:
if self.canvas.menu is None:
self.canvas.menu = Menu(self.canvas, tearoff=0)
self.canvas.menu.add_command(label="delete", command=self._delete)
self.canvas.menu.bind("<FocusOut>", lambda e: self.canvas.menu.unpost())
self._bindMenu()
else:
# need to go through and unbind...
pass

def setBgColour(self, colour):
self.canvas.config(background=colour)
self.bgColour = colour
Expand Down Expand Up @@ -11672,18 +11739,67 @@ def _updateColours(self, bgCol, bgHCol, fgCol, fgHCol):
for c in self.children:
c._updateColours(bgCol, bgHCol, fgCol, fgHCol)

def draw(self, x, y):
cy = super(AjTreeNode, self).draw(x, y)
self._bindMenu()
return cy

# override parent function, so that we can change the label's background colour
def drawtext(self):
attr=self.item.node.attributes
self.hasAttr = self.showAttr and attr is not None and len(attr) > 0

if self.hasAttr:
self.attrId = self.canvas.create_text(self.x+20-1, self.y-1, anchor="nw", text='*')
self.x += 7
super(AjTreeNode, self).drawtext()
if self.hasAttr: self.x -= 7
self.colourLabels()

# add a tooltip for attributes
if ToolTip is not False and self.hasAttr:
text = "Attributes\n"
for key, val in attr.items():
text += " " + key + ":" + val + "\n"
text = text[:-1]
ToolTip(self.label, text, delay=500, follow_mouse=1)
ToolTip(self.canvas, text, specId=self.attrId, delay=500, follow_mouse=1)

def _bindMenu(self):
if self.canvas.menu is not None and not self.menuBound:
self.menuBound = True
if gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]:
self.canvas.tag_bind(self.image_id, "<Button-3>", self._showMenu)
if self.hasAttr: self.canvas.tag_bind(self.attrId, "<Button-3>", self._showMenu)
self.label.bind("<Button-3>", self._showMenu)
else:
self.canvas.tag_bind(self.image_id, "<Button-2>", self._showMenu)
if self.hasAttr: self.canvas.tag_bind(self.attrId, "<Button-2>", self._showMenu)
self.label.bind("<Button-2>", self._showMenu)

# override parent function, so that we can change the label's background colour
def drawicon(self):
super(AjTreeNode, self).drawicon()

def _showMenu(self, event=None):
self.canvas.lastSelected = event.widget
self.canvas.menu.focus_set()
self.canvas.menu.post(event.x_root - 10, event.y_root - 10)
return "break"

def _delete(self):
self.update()
self.canvas.lastSelected.destroy()

# override parent function, so that we can generate an event on finish editing
def edit_finish(self, event=None):
super(AjTreeNode, self).edit_finish(event)
if self.editEvent is not None:
self.editEvent()

def colourLabels(self):
if self.showAttr and self.hasAttr:
self.canvas.itemconfigure(self.attrId, fill=self.fgColour)
try:
if not self.selected:
self.label.config(background=self.bgColour, fg=self.fgColour)
Expand Down Expand Up @@ -11716,8 +11832,11 @@ def _makeAjTreeData(self):
# functions implemented as specified in skeleton
class AjTreeData(TreeItem, object):

def __init__(self, node):
self.node = node
def __init__(self, document):
# handle root node
try: self.node = document.documentElement
except AttributeError: self.node = document

self.dblClickFunc = None
self.clickFunc = None
self.treeTitle = None
Expand Down
5 changes: 3 additions & 2 deletions docs/mkdocs/docs/pythonDevWidgets.md
Expand Up @@ -204,8 +204,9 @@ app.go()
Register a function to call when an item is edited
Will receive the name of the tree.

* `.setTreeEditable(title, value)`
Set whether the tree can be edited
* `.showTreeAttributes(title, show=True)`
Call this to enable indication of attributes on a tree.
Will draw a star & provide a tooltip.

* `.setTreeColours(title, fg, bg, fgH, bgH)`
Set the fg/bg/fg highlight/bg highlight colours of the tree
Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs/docs/simpleAppJar.md
Expand Up @@ -468,6 +468,7 @@ Displays the tree widget.
| dbl | function | None | A function to call when a node is double-clicked. |
| edit | function | None | A function to call when a node is edited. |
| editable | boolean | None | Determines if nodes are editable or not. |
| attributes | boolean | None | Determines if attributes should be shown in the tree. |
| fg | string | None | The foreground colour of the widget. |
| bg | string | None | The background colour of the widget. |
| fgH | string | None | The foreground colour of a selected node. |
Expand Down
2 changes: 1 addition & 1 deletion docs/mkdocs/docs/whatsNew.md
Expand Up @@ -5,7 +5,7 @@

* Issues Resolved:
* [#461](https://github.com/jarvisteach/appJar/issues/461) - Fixed issue with setting `parent` of [message boxes](/pythonDialogs/#message-boxes)
* [#457](https://github.com/jarvisteach/appJar/issues/457) - Can now create [trees](/pythonDevWidgets/#tree) with xml objects
* [#457](https://github.com/jarvisteach/appJar/issues/457) - Can now create [trees](/pythonDevWidgets/#tree) with xml objects and show attributes
* [#458](https://github.com/jarvisteach/appJar/issues/458) - Ability to set `wrap` parameter on [Tables](/pythonDevWidgets/#table)

## Version 0.93
Expand Down
33 changes: 19 additions & 14 deletions examples/issues/issue457.py
Expand Up @@ -3,9 +3,9 @@
from xml.dom.minidom import parseString

from appJar import gui
xml= """<people>
<person><name>Fred</name><age>45</age><gender>Male</gender></person>
<person><name>Tina</name><age>37</age><gender>Female</gender></person>
xml= """<people cont='europe'>
<person><name a='1' b='2' lowercase='false'>Fred</name><age>45</age><gender>Male</gender></person>
<person><favourites f='5'><a>Cheese</a><b>Ham</b></favourites><name>Tina</name><age>37</age><gender>Female</gender></person>
<person><name>CLive</name><age>28</age><gender>Male</gender></person>
<person><name>Betty</name><age>51</age><gender>Female</gender></person>
</people>"""
Expand All @@ -15,19 +15,12 @@ def press():
print(data.toxml())

def add():

element = data.createElement('name2')
data.documentElement.appendChild(element)
element.appendChild(data.createTextNode("meee"))
node.appendChild(element)

app.generateTree("t2")

with gui() as app:
app.label('hello world')
app.tree("t1", xml, fg="white", bg="blue", fgH="blue", bgH="white")

data = parseString(xml)
app.addChild("t2", element)

def _addr():
node = data.createElement("human")
data.documentElement.appendChild(node)

Expand All @@ -43,7 +36,19 @@ def add():
node.appendChild(age)
node.appendChild(gen)

app.tree("t2", data, fg="yellow", bg="red", bgH="yellow", fgH="red")
def dbl(t, a):
add()

def clear():
app.clearTree("t2")

with gui() as app:
app.label('hello world')

# app.tree("t1", xml, fg="white", bg="blue", fgH="blue", bgH="white")
data = parseString(xml)
app.tree("t2", data, menu=False, attributes=True, fg="yellow", bg="red", bgH="yellow", fgH="red")#, dbl=dbl)

app.button("SHOW", press)
app.button("ADD", add)
app.button("CLEAR", clear)
7 changes: 4 additions & 3 deletions tests/widget_test.py
Expand Up @@ -1518,9 +1518,9 @@ def test_trees():
print("\tTesting Trees")
xml_str = """<people>
<person><name>Fred</name><age>45</age><gender>Male</gender></person>
<person><name>Tina</name><age>37</age><gender>Female</gender></person>
<person a="aaa"><name>Tina</name><age>37</age><gender>Female</gender></person>
<person><name>CLive</name><age>28</age><gender>Male</gender></person>
<person><name>Betty</name><age>51</age><gender>Female</gender></person>
<person><name>Betty</name><age>51</age><gender b='bbb'>Female</gender></person>
</people>"""

app.addTree("t1", xml_str)
Expand All @@ -1531,6 +1531,7 @@ def test_trees():
app.setTreeDoubleClickFunction("t1", tester_function)
app.setTreeEditFunction("t1", tester_function)
app.setTreeEditable("t1", True)
app.showTreeAttributes("t1")
app.setTreeEditable("t1", False)
app.setTreeBg("t1", "red")
app.setTreeFg("t1", "yellow")
Expand All @@ -1545,7 +1546,7 @@ def test_trees():

from xml.dom.minidom import parseString

app.tree("t2", parseString(xml_str), click=tester_function, dbl=tester_function, edit=tester_function, editable=True,
app.tree("t2", parseString(xml_str), attributes=True, click=tester_function, dbl=tester_function, edit=tester_function, editable=True,
fg="green", bg="yellow", fgH="pink", bgH="blue")

# call generic setter functions
Expand Down

0 comments on commit de42acc

Please sign in to comment.