From 4b063063d9d7563e32001a6382b6dde9cb449a1b Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 7 Feb 2002 11:15:15 +0000 Subject: [PATCH] *** empty log message *** git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@37 a8418922-720d-0410-834f-a69b97ada669 --- doc/storage.txt | 50 ++++++++++ gaphor/UML/Element.py | 206 +++++++++++++++++++++++++++++---------- gaphor/UML/Makefile.am | 2 +- gaphor/UML/__init__.py | 1 + gaphor/UML/management.py | 128 ++++++++++++++++++++++++ gaphor/test-diagram.py | 44 ++++----- tests/diagram-destroy.py | 63 ++++++++++++ tests/test-treemodel.py | 31 ++++++ tests/test-xml.py | 8 ++ 9 files changed, 457 insertions(+), 76 deletions(-) create mode 100644 doc/storage.txt create mode 100644 gaphor/UML/management.py create mode 100644 tests/diagram-destroy.py create mode 100644 tests/test-treemodel.py create mode 100644 tests/test-xml.py diff --git a/doc/storage.txt b/doc/storage.txt new file mode 100644 index 000000000..77a427700 --- /dev/null +++ b/doc/storage.txt @@ -0,0 +1,50 @@ +Saving and loading Gaphor diagrams + +The idea is to keep the file format as simple and extensible as possible. + +This is the format as used by Gaphor. + +Everything interesting is between the `Gaphor' start and end tag. + +The file is as flat as possible: UML elements (including Diagram) are at +toplevel, no nesting. +A UML element can have two tags: `Reference' and `Value'. Reference is used to +point to other UML elements, Value has a value inside (an integer or astring). + +Diagram is a special case. Since this element contains a diagram canvas inside, +it may become pretty big (with lots of nested elements). +This is handled by the load and save function of the Diagram class. +All elements inside a canvas have a tag `Item'. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gaphor/UML/Element.py b/gaphor/UML/Element.py index 5b8c8ee57..3c14f0c0a 100644 --- a/gaphor/UML/Element.py +++ b/gaphor/UML/Element.py @@ -32,6 +32,14 @@ Geometry = types.ListType FALSE = 0 TRUE = not FALSE +elements = { } + +def lookup (id): + if elements.has_key(id): + return elements[id] + else: + return None + class Enumeration_: '''The Enumerration class is an abstract class that can be used to create enumerated types. One should inherit from Enumeration and define a variable @@ -105,7 +113,7 @@ class Element: attributes and relations are defined by a ._attrdef structure. A class does not need to define any local variables itself: Element will retrieve all information from the _attrdef structure. -Element._hash is a table containing all assigned objects as weak references. +elements is a table containing all assigned objects as weak references. This class has its own __del__() method. This means that it can not be freed by the garbage collector. Instead you should call Element::unlink() to remove all relationships with the element and then it will remove itself if @@ -120,37 +128,39 @@ object if references are lehd by the object on the undo stack. _index = 1 _attrdef = { 'presentation': ( Sequence, types.ObjectType ), 'itemsOnUndoStack': (0, types.IntType ) } - _hash = { } def __init__(self): - print "New object of type", self.__class__ + #print "New object of type", self.__class__ self.__dict__['__id'] = Element._index self.__dict__['__signals'] = [ ] #Element._hash[Element._index] = weakref.ref (self) - Element._hash[Element._index] = self + elements[Element._index] = self Element._index += 1 def unlink(self): '''Remove all references to the object.''' - #print 'Element.unlink()' + #print 'Element.unlink():', self self.emit("unlink") - if Element._hash.has_key(self.__dict__['__id']): - del Element._hash[self.__dict__['__id']] + if elements.has_key(self.__dict__['__id']): + del elements[self.__dict__['__id']] for key in self.__dict__.keys(): # In case of a cyclic reference, we should check if the element # not yet has been removed. if self.__dict__.has_key (key) and \ - key not in ( 'presentation', '__signals', '__id' ): + key not in ( 'presentation', 'itemsOnUndoStack', \ + '__signals', '__id' ): if isinstance (self.__dict__[key], Sequence): # Remove each item in the sequence, then remove # the sequence from __dict__. - for s in self.__dict__[key].list: - del self.__dict__[key][s] + list = self.__dict__[key].list + while len (list) > 0: + del self.__dict__[key][list[0]] del self.__dict__[key] else: # do a 'del self.key' - if isinstance (self.__dict__[key], Element): - self.__delattr__(key) + #if isinstance (self.__dict__[key], Element): + #print '\tunlink:', key + self.__delattr__(key) return self # Hooks for presentation elements to add themselves: @@ -167,6 +177,9 @@ object if references are lehd by the object on the undo stack. def undo_presentation (self, presentation): if not presentation in self.presentation: + # Add myself to the 'elements' hash + if len (self.presentation) == 0: + elements[self.id] = self self.presentation = presentation self.itemsOnUndoStack -= 1 assert self.itemsOnUndoStack >= 0 @@ -175,8 +188,14 @@ object if references are lehd by the object on the undo stack. if presentation in self.presentation: del self.presentation[presentation] self.itemsOnUndoStack += 1 + # Remove yourself from the 'elements' hash + if len (self.presentation) == 0: + if lookup (self.id): + del elements[self.id] def remove_undoability (self): + if not self.__dict__.has_key ('itemsOnUndoStack'): + return self.itemsOnUndoStack -= 1 assert self.itemsOnUndoStack >= 0 if len (self.presentation) == 0 and self.itemsOnUndoStack == 0: @@ -184,7 +203,7 @@ object if references are lehd by the object on the undo stack. self.unlink() def __get_attr_info(self, key, klass): - '''Find the record for 'key' in the ._attrdef map.''' + '''Find the record for 'key' in the ._attrdef map.''' done = [ ] def real_get_attr_info(key, klass): if klass in done: @@ -211,21 +230,23 @@ object if references are lehd by the object on the undo stack. self.__dict__[key] = Sequence(self, type) return self.__dict__[key] - def __del_seq_item(self, seq, val): - try: - index = seq.list.index(val) - del seq.list[index] - except ValueError: - pass + #def __del_seq_item(self, seq, val): + #try: + #index = seq.list.index(val) + #del seq.list[index] + # seq.list.remove (val) + #except ValueError: + # pass def __getattr__(self, key): - #print 'Element.__getattr__(' + key + ')' if key == 'id': return self.__dict__['__id'] elif self.__dict__.has_key(key): # Key is already in the object return self.__dict__[key] else: + #if key[0] != '_': + #print 'Unknown attr: Element.__getattr__(' + key + ')' rec = self.__get_attr_info (key, self.__class__) if rec[0] is Sequence: # We do not have a sequence here... create it and return it. @@ -264,13 +285,16 @@ object if references are lehd by the object on the undo stack. if rec[0] is not Sequence or xrec[0] is not Sequence: if rec[0] is Sequence: #print 'del-seq-item rec' - self.__del_seq_item(self.__dict__[key], xself) + #self.__del_seq_item(self.__dict__[key], xself) + if xself in self.__dict__[key].list: + self.__dict__[key].list.remove(xself) elif self.__dict__.has_key(key): #print 'del-item rec' del self.__dict__[key] if xrec[0] is Sequence: #print 'del-seq-item xrec' - xself.__del_seq_item(xself.__dict__[rec[2]], self) + #xself.__del_seq_item(xself.__dict__[rec[2]], self) + xself.__dict__[rec[2]].list.remove (self) elif xself.__dict__.has_key(rec[2]): #print 'del-item xrec' del xself.__dict__[rec[2]] @@ -297,15 +321,18 @@ object if references are lehd by the object on the undo stack. if not self.__dict__.has_key(key): return xval = self.__dict__[key] - del self.__dict__[key] if len(rec) > 2: # Bi-directional relationship xrec = xval.__get_attr_info (rec[2], rec[1]) if xrec[0] is Sequence: - xval.__del_seq_item(xval.__dict__[rec[2]], self) + #xval.__del_seq_item(xval.__dict__[rec[2]], self) + #xval.__dict__[rec[2]].list.remove (self) + # Handle it via sequence_remove() + del xval.__dict__[rec[2]][self] else: del xval.__dict__[rec[2]] - xval.emit(rec[2]) - self.emit (key) + del self.__dict__[key] + self.emit (key) + xval.emit(rec[2]) def sequence_remove(self, seq, obj): '''Remove an entry. Should only be called by Sequence's implementation. @@ -315,20 +342,24 @@ object if references are lehd by the object on the undo stack. if self.__dict__[key] is seq: break #print 'Element.sequence_remove', key + seq_len = len (seq) rec = self.__get_attr_info (key, self.__class__) if rec[0] is not Sequence: raise AttributeError, 'Element: This should be called from Sequence' seq.list.remove(obj) if len(rec) > 2: # Bi-directional relationship - xrec = obj.__get_attr_info (rec[2], rec[1]) + xrec = obj.__get_attr_info (rec[2], obj.__class__) #rec[1]) if xrec[0] is Sequence: - obj.__del_seq_item(obj.__dict__[rec[2]], self) + #obj.__del_seq_item(obj.__dict__[rec[2]], self) + #print 'sequence_remove: Sequence' + obj.__dict__[rec[2]].list.remove (self) else: - try: - del obj.__dict__[rec[2]] - except Exception, e: - print 'ERROR: (Element.sequence_remove)', e - + #try: + #print 'sequence_remove: item' + del obj.__dict__[rec[2]] + #except Exception, e: + # print 'ERROR: (Element.sequence_remove)', e + assert len (seq) == seq_len - 1 # Functions used by the signal functions def connect (self, signal_func, *data): self.__dict__['__signals'].append ((signal_func,) + data) @@ -338,24 +369,81 @@ object if references are lehd by the object on the undo stack. self.__dict__['__signals']) def emit (self, key): - if not self.__dict__.has_key ('__signals'): - print 'No __signals attribute in object', self - return + print 'emit', self, key + #if not self.__dict__.has_key ('__signals'): + # print 'No __signals attribute in object', self + # return for signal in self.__dict__['__signals']: signal_func = signal[0] data = signal[1:] + #print 'signal:', signal_func, 'data:', data signal_func (key, *data) -#def update_model(): -# '''Do a garbage collection on the hash table, also removing -# weak references that do not point to valid objects.''' -# gc.collect() -# for k in Element._hash.keys(): -# if Element._hash[k]() is None: -# del Element._hash[k] + def save(self, document, parent): + def save_children (obj): + if isinstance (obj, Element): + #subnode = document.createElement (key) + subnode = document.createElement ('Reference') + node.appendChild (subnode) + subnode.setAttribute ('name', key) + subnode.setAttribute ('refid', str(obj.__dict__['__id'])) + elif isinstance (obj, types.IntType) or \ + isinstance (obj, types.LongType) or \ + isinstance (obj, types.FloatType): + subnode = document.createElement ('Value') + node.appendChild (subnode) + subnode.setAttribute ('name', key) + text = document.createTextNode (str(obj)) + subnode.appendChild (text) + elif isinstance (obj, types.StringType): + subnode = document.createElement ('Value') + node.appendChild (subnode) + subnode.setAttribute ('name', key) + cdata = document.createCDATASection (str(obj)) + subnode.appendChild (cdata) -#Element_hash_gc = update_model + node = document.createElement (self.__class__.__name__) + parent.appendChild (node) + node.setAttribute ('id', str (self.__dict__['__id'])) + for key in self.__dict__.keys(): + if key not in ( 'presentation', 'itemsOnUndoStack', \ + '__signals', '__id' ): + obj = self.__dict__[key] + if isinstance (obj, Sequence): + for item in obj.list: + save_children (item) + else: + save_children (obj) + return node + def load(self, node): + for child in node.childNodes: + if child.tagName == 'Reference': + name = child.getAttribute ('name') + refid = int (child.getAttribute ('refid')) + refelement = lookup (refid) + attr_info = self.__get_attr_info (name, self.__class__) + if not isinstance (refelement, attr_info[1]): + raise ValueError, 'Referenced item is of the wrong type' + if attr_info[0] is Sequence: + self.__ensure_seq (name, attr_info[1]).list.append (refelement) + else: + self.__dict__[name] = refelement + elif child.tagName == 'Value': + name = child.getAttribute ('name') + subchild = child.firstChild + attr_info = self.__get_attr_info (name, self.__class__) + if issubclass (attr_info[1], types.IntType) or \ + issubclass (attr_info[1], types.LongType): + self.__dict__[name] = int (subchild.data) + elif issubclass (attr_info[1], types.FloatType): + self.__dict__[name] = float (subchild.data) + else: + self.__dict__[name] = subchild.data + + +################################### +# Testing if __name__ == '__main__': print '\n============== Starting Element tests... ============\n' @@ -389,7 +477,7 @@ if __name__ == '__main__': a.unlink() del a - assert len (Element._hash) == 0 + assert len (elements) == 0 print '\tOK ===' @@ -421,7 +509,7 @@ if __name__ == '__main__': a.unlink() - assert len (Element._hash) == 0 + assert len (elements) == 0 print '\tOK ===' @@ -468,7 +556,7 @@ if __name__ == '__main__': a.unlink() b.unlink() - assert len (Element._hash) == 0 + assert len (elements) == 0 print '\tOK ===' @@ -496,6 +584,18 @@ if __name__ == '__main__': assert a.ref is a assert a.seq.list == [ a ] + b = A() + a.seq = b + assert b.ref is a + assert a.seq.list == [ a, b ] + + + del a.seq[a] + assert a.ref is None + assert b.ref is a + assert a.seq.list == [ b ] + + b.unlink() a.unlink() a = A() b = A() @@ -539,8 +639,8 @@ if __name__ == '__main__': a.unlink() b.unlink() - #print Element._hash - assert len (Element._hash) == 0 + #print elements + assert len (elements) == 0 print '\tOK ===' @@ -603,7 +703,7 @@ if __name__ == '__main__': a.unlink() b.unlink() - assert len (Element._hash) == 0 + assert len (elements) == 0 print '\tOK ===' @@ -660,7 +760,7 @@ if __name__ == '__main__': b.unlink() del b - assert len (Element._hash) == 0 + assert len (elements) == 0 print '\tOK ===' @@ -674,14 +774,14 @@ if __name__ == '__main__': a = A() b = A() - assert len (Element._hash) == 2 + assert len (elements) == 2 a.rel = b a.unlink() b.unlink() - assert len (Element._hash) == 0 + assert len (elements) == 0 print '\tOK ===' diff --git a/gaphor/UML/Makefile.am b/gaphor/UML/Makefile.am index 4f0b478fa..24d370893 100644 --- a/gaphor/UML/Makefile.am +++ b/gaphor/UML/Makefile.am @@ -9,4 +9,4 @@ ModelElements.py: $(GEN_UML) $(METAMODEL_XMI) grep -v "PresentationElement" genModelElements.py > ModelElements.py rm -f genModelElements.py -EXTRA_DIST=Element.py ModelElements.py __init__.py +EXTRA_DIST=Element.py ModelElements.py management.py __init__.py diff --git a/gaphor/UML/__init__.py b/gaphor/UML/__init__.py index d20936a6d..715b36299 100644 --- a/gaphor/UML/__init__.py +++ b/gaphor/UML/__init__.py @@ -3,6 +3,7 @@ # from Element import * from ModelElements import * +from management import * Attribute._attrdef['rawAttribute'] = ( '', String ) Operation._attrdef['rawOperation'] = ( '', String ) diff --git a/gaphor/UML/management.py b/gaphor/UML/management.py new file mode 100644 index 000000000..64548f560 --- /dev/null +++ b/gaphor/UML/management.py @@ -0,0 +1,128 @@ +# vim: sw=4 +import UML, diagram + +from xmllib import XMLParser +from xml.dom.minidom import Document + +# Somehow I did not manage it to use the minidom/SAX stuff to create a +# decent parser, do I use this depricated (but workin ;) peace of code... +# The GaphorParser reads a Gaphor file and creates a DOM representation of +# the file. +class GaphorParser (XMLParser): + '''GaphorParser + The GaphorParser examines an inut strream and creates a Document object + using the elements found in the XML file. The only restruction that we + test is the Gaphor tag and the version (currently only 1.0)''' + def __init__ (self, **kw): + self.doit = 0 + self.doc = Document() + self.elements = { 'Gaphor': (self.start_Gaphor, self.end_Gaphor) } + self.__stack = [ ] + apply(XMLParser.__init__, (self,), kw) + + def syntax_error(self, message): + raise IOError, "XML document contains syntax errors: " + message + + def start_Gaphor (self, attrs): + if attrs['version'] != '1.0': + raise Exception, 'Wrong version of Gaphor (' + \ + attrs['version'] + ')' + else: + node = self.doc.createElement('Gaphor') + self.doc.appendChild (node) + self.__stack.append (node) + self.doit += 1 + + def end_Gaphor (self): + self.doit -= 1 + + def unknown_starttag(self, tag, attrs): + if self.doit: + node = self.doc.createElement(tag) + self.__stack[-1].appendChild (node) + self.__stack.append (node) + for key in attrs.keys(): + node.setAttribute (key, attrs[key]) + + def unknown_endtag(self, tag): + self.__stack = self.__stack[:-1] + + def handle_cdata(self, tag): + cdata = self.doc.createCDATASection (tag) + self.__stack[-1].appendChild (cdata) + + def unknown_entityref(self, ref): + raise ValueError, tag + " not supported." + + def unknown_charref(self, ref): + raise ValueError, tag + " not supported." + +def save (filename=None): + document = Document() + rootnode = document.createElement ('Gaphor') + document.appendChild (rootnode) + rootnode.setAttribute ('version', '1.0') + for e in UML.elements.values(): + print 'Saving object', e + e.save(document, rootnode) + + if not filename: + print document.toxml(indent=' ', newl='\n') + else: + file = open (filename, 'w') + if not file: + raise IOError, 'Could not open file `%s\'' % (filename) + document.writexml (file, indent='', addindent=' ', newl='\n') + file.close() + +def load (filename): + '''Load a file and create a model if possible. + Exceptions: IOError, ValueError.''' + parser = GaphorParser() + + f = open (filename, 'r') + while 1: + data = f.read(512) + parser.feed (data) + if len(data) != 512: + break; + parser.close() + f.close() + + # Now iterate over the tree and create every element in the UML.elements + # table. + rootnode = parser.doc.firstChild + for node in rootnode.childNodes: + try: + if node.tagName == 'Diagram': + cls = getattr (diagram, node.tagName) + else: + cls = getattr (UML, node.tagName) + except: + raise ValueError, 'Invalid field in Gaphor file: ' + node.tagName + id = int (node.getAttribute('id')) + old_index = UML.Element._index + UML.Element._index = id + cls() + if old_index > id: + UML.Element._index = old_index + #print node.tagName, node.getAttribute('id') + + # Second step: call Element.load() for every object in the element hash. + # We also provide the XML node, so it can recreate it's state + for node in rootnode.childNodes: + id = int (node.getAttribute('id')) + element = UML.lookup (id) + assert element != None + element.load (node) + + +def flush(): + '''Flush all elements in the UML.elements''' + while 1: + try: + (key, value) = UML.elements.popitem() + except KeyError: + break; + value.unlink() + UML.elements.clear() diff --git a/gaphor/test-diagram.py b/gaphor/test-diagram.py index 40b6b34fd..70bd536f0 100755 --- a/gaphor/test-diagram.py +++ b/gaphor/test-diagram.py @@ -10,9 +10,12 @@ import diacanvas import UML import tree +uc = getattr (UML, 'UseCase') +print 'getattr (UML, "UseCase") ->', uc + def mainquit(*args): - for k in UML.Element._hash.keys(): - print "Element", k, ":", UML.Element._hash[k].__dict__ + for k in UML.elements.keys(): + print "Element", k, ":", UML.elements[k].__dict__ print "Forcing Garbage collection:" gtk.main_quit() @@ -30,21 +33,21 @@ treemodel = tree.NamespaceModel(model) #item.move (30, 50) #item = canvas.root.add (diagram.Actor) #item.move (150, 50) +item = dia.create_item (diagram.UseCase, (50, 150)) +usecase = item.get_subject() +#dia.create_item (diagram.UseCase, (50, 200), subject=usecase) + item = dia.create_item (diagram.Actor, (150, 50)) actor = item.get_subject() actor.name = "Actor" -item = dia.create_item (diagram.UseCase, (50, 150)) -usecase = item.get_subject() -dia.create_item (diagram.UseCase, (50, 200), subject=usecase) - item = dia.create_item (diagram.Comment, (10,10)) comment = item.get_subject() -del item +del item#, actor, usecase, comment -#print "Comment.presentation:", comment.presentation.list -#print "Actor.presentation:", actor.presentation.list +print "Comment.presentation:", comment.presentation.list +print "Actor.presentation:", actor.presentation.list print "UseCase.presentation:", usecase.presentation.list #view = diacanvas.CanvasView().set_canvas (dia.canvas) #display_diagram (dia) @@ -61,16 +64,15 @@ treemodel.dump() print 'Going into main' gtk.main() +UML.save('x.xml') + del win treemodel.dump() -#print "Comment.ann.Elem.:", comment.annotatedElement.list -#print "Actor.comment:", actor.comment.list -#print "UseCase.comment:", usecase.comment.list -print "Comment.presentation:", comment.presentation.list -print "Actor.presentation:", actor.presentation.list -print "UseCase.presentation:", usecase.presentation.list +#print "Comment.presentation:", comment.presentation.list +#print "Actor.presentation:", actor.presentation.list +#print "UseCase.presentation:", usecase.presentation.list print "removing diagram..." dia.unlink() del dia @@ -78,17 +80,15 @@ del dia print "Comment.presentation:", comment.presentation.list print "Actor.presentation:", actor.presentation.list print "UseCase.presentation:", usecase.presentation.list -actor.unlink() del actor -usecase.unlink() del usecase -comment.unlink() del comment -#del dia -print "Garbage collection after gtk.main() has finished:" +UML.flush() + +print "Garbage collection after gtk.main() has finished (should be empty):", #UML.update_model() -for k in UML.Element._hash.keys(): - print "Element", k, ":", UML.Element._hash[k].__dict__ +for k in UML.elements.keys(): + print "Element", k, ":", UML.elements[k].__dict__ print "Program ended normally..." diff --git a/tests/diagram-destroy.py b/tests/diagram-destroy.py new file mode 100644 index 000000000..d6ef8b874 --- /dev/null +++ b/tests/diagram-destroy.py @@ -0,0 +1,63 @@ +# +# Test the behavior of a UML tree with a Diagram as leaf. The whole tree +# should be freed... +# + +import UML +import diagram as dia +import gc + +model = UML.Model() +model.name = "MyModel" + +package = UML.Package() +package.name = "Package" + +model.ownedElement = package +assert len(model.ownedElement.list) == 1 +assert model.ownedElement.list[0] is package +assert package.namespace is model + +actor = UML.Actor() +actor.namespace = package +assert len(package.ownedElement.list) == 1 +assert package.ownedElement.list[0] is actor +assert actor.namespace is package + +usecase = UML.UseCase() +usecase.namespace = package +assert len(package.ownedElement.list) == 2 +assert package.ownedElement.list[0] is actor +assert package.ownedElement.list[1] is usecase +assert usecase.namespace is package + +diagram = dia.Diagram() +diagram.namespace = package +assert len(package.ownedElement.list) == 3 +assert package.ownedElement.list[0] is actor +assert package.ownedElement.list[1] is usecase +assert package.ownedElement.list[2] is diagram +assert diagram.namespace is package + +diagram.create_item (dia.Actor, pos=(0, 0), subject=actor) +diagram.create_item (dia.UseCase, pos=(100, 100), subject=usecase) + +#dia.destroy_diagrams() + +model.unlink() +del model +usecase.unlink() +del usecase +actor.unlink() +del actor +package.unlink() +del package +diagram.unlink() +del diagram + +gc.collect() +gc.set_debug (gc.DEBUG_LEAK) + +print "Uncollectable objects found:", gc.garbage + + diff --git a/tests/test-treemodel.py b/tests/test-treemodel.py new file mode 100644 index 000000000..b663ba1fe --- /dev/null +++ b/tests/test-treemodel.py @@ -0,0 +1,31 @@ + +import UML, gtk +from tree.namespace import * + +import CreateModel + +window = gtk.Window() +window.connect('destroy', lambda win: gtk.main_quit()) +window.set_title('TreeView test') +window.set_default_size(250, 400) + +scrolled_window = gtk.ScrolledWindow() +scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) +window.add(scrolled_window) + +tree_model = NamespaceModel(CreateModel.model) + +tree_view = gtk.TreeView(tree_model) +cell = gtk.CellRendererText() +# the text in the column comes from column 0 +column = gtk.TreeViewColumn('', cell, text=0) +tree_view.append_column(column) + +scrolled_window.add(tree_view) +window.show_all() + +tree_model.dump() + +gtk.main() + +tree_model.dump() diff --git a/tests/test-xml.py b/tests/test-xml.py new file mode 100644 index 000000000..29f0995d9 --- /dev/null +++ b/tests/test-xml.py @@ -0,0 +1,8 @@ +# +# A simple parser +# + +import UML + +UML.load ('../gaphor/x.xml') +UML.save ('aa.xml')