Welcome, guest | Sign In | My Account | Store | Cart
# Author: Miguel Martinez Lopez
#
# Version: 0.1
# Uncomment the next line to see my email
# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))

try:
   
from ttk import Treeview, Scrollbar, Frame
   
from Tkconstants import HORIZONTAL, VERTICAL, N,S,E,W, END
except ImportError:
   
from tkinter.ttk import Treeview, Scrollbar, Frame
   
from tkinter.constants import HORIZONTAL, VERTICAL, N,S,E,W, END
   
import xml.etree.ElementTree as ET

from operator import attrgetter


def autoscroll(sbar, first, last):
   
"""Hide and show scrollbar as needed."""
    first
, last = float(first), float(last)
   
if first <= 0 and last >= 1:
        sbar
.grid_remove()
   
else:
        sbar
.grid()
    sbar
.set(first, last)


class XML_Viwer(Frame):

   
def __init__(self, master, xml=None, heading_text=None, heading_anchor=None, padding=None, cursor=None, takefocus=None, style=None):
       
Frame.__init__(self, master, class_="XML_Viwer")

       
self._vsb = Scrollbar(self, orient=VERTICAL)
       
self._hsb = Scrollbar(self, orient=HORIZONTAL)

        kwargs
= {}
        kwargs
["yscrollcommand"] = lambda f, l: autoscroll(self._vsb, f, l)
        kwargs
["xscrollcommand"] = lambda f, l: autoscroll(self._hsb, f, l)
       
       
if style is not None:
            kwargs
["style"] = style
           
       
if padding is not None:
            kwargs
["padding"] = padding
           
       
if cursor is not None:
            kwargs
["cursor"] = cursor
           
       
if takefocus is not None:
            kwargs
["takefocus"] = takefocus

       
self._treeview = Treeview(self, **kwargs)
       
       
if heading_text is not None:
           
if heading_anchor is not None:
               
self._treeview.heading("#0", text=heading_text, anchor=heading_anchor)
           
else:
               
self._treeview.heading("#0", text=heading_text)

       
self._treeview.bind("<<TreeviewOpen>>", self._on_open)
       
self._treeview.bind("<<TreeviewClose>>", self._on_close)
       
       
# Without this line, horizontal scrolling doesn't work properly.
       
self._treeview.column("#0", stretch= False)

       
self._vsb['command'] = self._treeview.yview
       
self._hsb['command'] = self._treeview.xview

       
self._treeview.grid(column=0, row=0, sticky=N+S+W+E)
       
self._vsb.grid(column=1, row=0, sticky=N+S)
       
self._hsb.grid(column=0, row=1, sticky=E+W)
       
       
self.grid_columnconfigure(0, weight=1)
       
self.grid_rowconfigure(0, weight=1)

       
self._element_tree = None
       
self._item_ID_to_element = {}

       
if xml is not None:
           
self.parse_xml(xml)

   
def _on_open(self, event):
        item_ID
= self._treeview.focus()
       
if item_ID not in self._item_ID_to_element: return

        node
= self._item_ID_to_element[item_ID]

       
self._treeview.item(item_ID, text = self._repr_of_openning_tag(node))
       
   
def _on_close(self, event):
        item_ID
= self._treeview.focus()
       
if item_ID not in self._item_ID_to_element: return

        node
= self._item_ID_to_element[item_ID]
       
        text
= self._repr_of_openning_tag(node) + self._repr_of_closing_tag(node)
       
self._treeview.item(item_ID, text = text)

   
def parse_xml(self, xml):
       
self._element_tree = ET.ElementTree(ET.fromstring(xml))
       
       
self.clear()
       
self._walk_xml(self._element_tree.getroot())
       
   
@property
   
def element_tree(self):
       
return self._element_tree
   
   
@element_tree.setter
   
def element_tree(self, element_tree):
       
self._element_tree = element_tree
       
       
self.clear()
       
self._walk_xml(element_tree.getroot())  
       
   
def clear(self):
       
self._item_ID_to_element = {}
       
self._treeview.delete(*self._treeview.get_children())
       
   
def _repr_of_openning_tag(self, node):
        text
= "<" + node.tag

        attrs
= node.attrib
       
       
# list function is here necessary to provide support to Python 3
        a_names
= list(attrs.keys())
        a_names
.sort()

       
for a_name in a_names:
            text
+= ' %s="' % a_name
            text
+= attrs[a_name]
            text
+= '"'

        text
+= ">"
       
return text
       
   
def _repr_of_closing_tag(self, node):
       
return "</%s>"%node.tag

   
def _walk_xml(self, node, depth=0, parent=""):
        text
= self._repr_of_openning_tag(node) + self._repr_of_closing_tag(node)

        item
= self._treeview.insert(parent, END, text = text)
       
self._item_ID_to_element[item] = node
       
       
if node.text:
            text
= node.text.strip()
           
if text != "":
               
for line in text.splitlines():
                   
self._treeview.insert(item, END, text = line)

        child_nodes
= sorted(list(node), key=attrgetter('tag'))
       
for child_node in node:
           
self._walk_xml(child_node, depth+1, parent=item)

       
       
if node.tail:
            tail
= node.tail.strip()
           
if tail != "":
               
for line in tail.splitlines():
                   
self._treeview.insert(parent, END, text = line)

if __name__ == "__main__":
   
try:
       
from Tkinter import Tk
   
except ImportError:
       
from tkinter import Tk
   
    root
= Tk()
    xml
= """
    <messages>
      <note id="
501">
        <to>Tove</to>
        <from>Jani</from>
        <heading>Reminder</heading>
        <body>Don't forget me this weekend!</body>
      </note>
      <note id="
502">
        <to>Jani</to>
        <from>Tove</from>
        <heading>Re: Reminder</heading>
        <body>I will not</body>
      </note>
    </messages>"""

    XML_Viwer
(root, xml, heading_text="Email").pack()
    root
.mainloop()

Diff to Previous Revision

--- revision 2 2017-01-28 00:23:25
+++ revision 3 2017-02-22 23:00:21
@@ -1,5 +1,6 @@
 
# Author: Miguel Martinez Lopez
 
#
+# Version: 0.1
 
# Uncomment the next line to see my email
 
# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))
 
@@ -27,16 +28,38 @@
 
 
class XML_Viwer(Frame):
 
-    def __init__(self, master, xml=None, **kwargs):
+    def __init__(self, master, xml=None, heading_text=None, heading_anchor=None, padding=None, cursor=None, takefocus=None, style=None):
         
Frame.__init__(self, master, class_="XML_Viwer")
 
         
self._vsb = Scrollbar(self, orient=VERTICAL)
         
self._hsb = Scrollbar(self, orient=HORIZONTAL)
 
+        kwargs = {}
         kwargs
["yscrollcommand"] = lambda f, l: autoscroll(self._vsb, f, l)
         kwargs
["xscrollcommand"] = lambda f, l: autoscroll(self._hsb, f, l)
+      
+        if style is not None:
+            kwargs["style"] = style
+            
+        if padding is not None:
+            kwargs["padding"] = padding
+            
+        if cursor is not None:
+            kwargs["cursor"] = cursor
+            
+        if takefocus is not None:
+            kwargs["takefocus"] = takefocus
+
+        self._treeview = Treeview(self, **kwargs)
         
-        self._treeview = Treeview(self, **kwargs)
+        if heading_text is not None:
+            if heading_anchor is not None:
+                self._treeview.heading("#0", text=heading_text, anchor=heading_anchor)
+            else:
+                self._treeview.heading("#0", text=heading_text)
+
+        self._treeview.bind("<<TreeviewOpen>>", self._on_open)
+        self._treeview.bind("<<TreeviewClose>>", self._on_close)
         
         
# Without this line, horizontal scrolling doesn't work properly.
         
self._treeview.column("#0", stretch= False)
@@ -52,9 +75,27 @@
         
self.grid_rowconfigure(0, weight=1)
 
         
self._element_tree = None
+        self._item_ID_to_element = {}
 
         
if xml is not None:
             
self.parse_xml(xml)
+
+    def _on_open(self, event):
+        item_ID = self._treeview.focus()
+        if item_ID not in self._item_ID_to_element: return
+
+        node = self._item_ID_to_element[item_ID]
+
+        self._treeview.item(item_ID, text = self._repr_of_openning_tag(node))
+        
+    def _on_close(self, event):
+        item_ID = self._treeview.focus()
+        if item_ID not in self._item_ID_to_element: return
+
+        node = self._item_ID_to_element[item_ID]
+        
+        text = self._repr_of_openning_tag(node) + self._repr_of_closing_tag(node)
+        self._treeview.item(item_ID, text = text)
 
     
def parse_xml(self, xml):
         
self._element_tree = ET.ElementTree(ET.fromstring(xml))
@@ -74,9 +115,10 @@
         
self._walk_xml(element_tree.getroot())  
         
     
def clear(self):
+        self._item_ID_to_element = {}
         
self._treeview.delete(*self._treeview.get_children())
-
-    def _walk_xml(self, node, depth=0, parent=""):
+        
+    def _repr_of_openning_tag(self, node):
         text
= "<" + node.tag
 
         attrs
= node.attrib
@@ -91,7 +133,16 @@
             text
+= '"'
 
         text
+= ">"
+        return text
+        
+    def _repr_of_closing_tag(self, node):
+        return "</%s>"%node.tag
+
+    def _walk_xml(self, node, depth=0, parent=""):
+        text = self._repr_of_openning_tag(node) + self._repr_of_closing_tag(node)
+
         item
= self._treeview.insert(parent, END, text = text)
+        self._item_ID_to_element[item] = node
         
         
if node.text:
             text
= node.text.strip()
@@ -132,5 +183,5 @@
         
<body>I will not</body>
       </
note>
     
</messages>"""
-    XML_Viwer(root, xml).pack()
+    XML_Viwer(root, xml, heading_text="
Email").pack()
     root.mainloop()

History