[pitivi] checkpoint. save and load implemented, now need to be hooked in pitivi.



commit 7000d1cab5743e9c4d915cb9250898a6a476d8ca
Author: Alessandro Decina <alessandro decina collabora co uk>
Date:   Fri Mar 27 19:51:51 2009 +0100

    checkpoint. save and load implemented, now need to be hooked in pitivi.
---
 pitivi/formatters/etree.py |  217 ++++++++++++++++++++++++++++++++++----
 tests/test_formatter.py    |  252 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 441 insertions(+), 28 deletions(-)

diff --git a/pitivi/formatters/etree.py b/pitivi/formatters/etree.py
index dccf239..c8e4309 100644
--- a/pitivi/formatters/etree.py
+++ b/pitivi/formatters/etree.py
@@ -19,12 +19,17 @@
 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 # Boston, MA 02111-1307, USA.
 
+import gobject
+gobject.threads_init()
+import gst
+
 from xml.etree.ElementTree import Element, SubElement, tostring
 
-from pitivi.reflect import qual
+from pitivi.reflect import qual, namedAny
 from pitivi.factories.base import SourceFactory
 from pitivi.factories.file import FileSourceFactory
-from pitivi.timeline.track import SourceTrackObject
+from pitivi.timeline.track import Track, SourceTrackObject
+from pitivi.timeline.timeline import Timeline, TimelineObject
 from pitivi.formatters.base import Formatter
 
 version = "0.1"
@@ -44,7 +49,13 @@ def indent(elem, level=0):
         if level and (not elem.tail or not elem.tail.strip()):
             elem.tail = i
 
-class ElementTreeFormatterContext(object):
+class ElementTreeFormatterSaveContext(object):
+    def __init__(self):
+        self.streams = {}
+        self.factories = {}
+        self.track_objects = {}
+
+class ElementTreeFormatterLoadContext(object):
     def __init__(self):
         self.streams = {}
         self.factories = {}
@@ -52,6 +63,7 @@ class ElementTreeFormatterContext(object):
 
 class ElementTreeFormatter(Formatter):
     _element_id = 0
+    _our_properties = ["id", "type"]
 
     def _new_element_id(self):
         element_id = self._element_id
@@ -59,6 +71,17 @@ class ElementTreeFormatter(Formatter):
 
         return str(element_id)
 
+    def _filterElementProperties(self, element):
+        for name, value in element.attrib.iteritems():
+            if name in self._our_properties:
+                continue
+
+            yield name, value
+
+    def _parsePropertyValue(self, value):
+        # nothing to read here, move along
+        return gst.Caps("meh, name=%s" % value)[0]["name"]
+
     def _saveStream(self, stream, context):
         element = Element("stream")
         element.attrib["id"] = self._new_element_id()
@@ -69,6 +92,17 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadStream(self, element, context):
+        id_ = element.attrib["id"]
+        klass = namedAny(element.attrib["type"])
+        caps = gst.Caps(element.attrib["caps"])
+
+        stream = klass(caps)
+
+        context.streams[id_] = stream
+
+        return stream
+
     def _saveStreamRef(self, stream, context):
         stream_element = context.streams[stream]
         element = Element("stream-ref")
@@ -76,6 +110,9 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadStreamRef(self, element, context):
+        return context.streams[element.attrib["id"]]
+
     def _saveSource(self, source, context):
         element = self._saveObjectFactory(source, context)
         if isinstance(source, FileSourceFactory):
@@ -83,6 +120,11 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadFactory(self, element, context):
+        klass = namedAny(element.attrib["type"])
+
+        return self._loadObjectFactory(klass, element, context)
+
     def _saveObjectFactory(self, factory, context):
         element = Element("source")
         element.attrib["id"] = self._new_element_id()
@@ -104,6 +146,27 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadObjectFactory(self, klass, element, context):
+        # FIXME
+        if isinstance(klass, FileSourceFactory):
+            factory = FileSourceFactory(element.attrib["filename"])
+        else:
+            factory = klass()
+
+        input_streams = element.find("input-streams") or []
+        for stream_element in input_streams:
+            stream = self._loadStream(stream_element, context)
+            factory.addInputStream(stream)
+
+        output_streams = element.find("output-streams")
+        for stream_element in output_streams:
+            stream = self._loadStream(stream_element, context)
+            factory.addOutputStream(stream)
+
+        context.factories[element.attrib["id"]] = factory
+
+        return factory
+
     def _saveFileSourceFactory(self, element, source, context):
         element.attrib["filename"] = source.filename
 
@@ -115,6 +178,9 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadFactoryRef(self, element, context):
+        return context.factories[element.attrib["id"]]
+
     def _saveFactories(self, factories, context):
         element = Element("factories")
         sources = SubElement(element, "sources")
@@ -130,8 +196,11 @@ class ElementTreeFormatter(Formatter):
         element.attrib["id"] = self._new_element_id()
         element.attrib["type"] = qual(track_object.__class__)
         for attribute in ("start", "duration",
-                "in_point", "media_duration", "priority"):
-            element.attrib[attribute] = str(getattr(track_object, attribute))
+                "in_point", "media_duration"):
+            element.attrib[attribute] = \
+                    str("(gint64)%s" % getattr(track_object, attribute))
+
+        element.attrib["priority"] = "(int)%s" % track_object.priority
 
         factory_ref = \
                 self._saveFactoryRef(track_object.factory, context)
@@ -144,26 +213,52 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadTrackObject(self, element, context):
+        klass = namedAny(element.attrib["type"])
+
+        factory_ref = element.find("factory-ref")
+        factory = self._loadFactoryRef(factory_ref, context)
+
+        stream_ref = element.find("stream-ref")
+        stream = self._loadStreamRef(stream_ref, context)
+
+        track_object = klass(factory, stream)
+        for name, value_string in self._filterElementProperties(element):
+            value = self._parsePropertyValue(value_string)
+            setattr(track_object, name, value)
+
+        return track_object
+
     def _saveTrackObjectRef(self, track_object, context):
         element = Element("track-object-ref")
         element.attrib["id"] = context.track_objects[track_object].attrib["id"]
 
         return element
 
+    def _loadTrackObjectRef(self, element, context):
+        return context.track_objects[element.attrib["id"]]
+
     def _saveTrackObjectRefs(self, track_objects, context):
         element = Element("track-object-refs")
 
         for track_object in track_objects:
-            if track_object is track_object.track.default_track_object:
-                continue
-
             track_object_ref = self._saveTrackObjectRef(track_object, context)
             element.append(track_object_ref)
 
         return element
 
+    def _loadTrackObjectRefs(self, element, context):
+        track_objects = []
+        for track_object_element in element:
+            track_object = self._loadTrackObjectRef(track_object_element, context)
+            track_objects.append(track_object)
+
+        return track_objects
+
     def _saveTrack(self, track, context):
         element = Element("track")
+        stream_element = self._saveStream(track.stream, context)
+        element.append(stream_element)
         track_objects = SubElement(element, "track-objects")
 
         for track_object in track.track_objects:
@@ -175,32 +270,77 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadTrack(self, element, context):
+        stream_element = element.find("stream")
+        stream = self._loadStream(stream_element, context)
+
+        track = Track(stream)
+
+        track_objects_element  = element.find("track-objects")
+        for track_object_element in track_objects_element:
+            track_object = self._loadTrackObject(track_object_element, context)
+            track.addTrackObject(track_object)
+
+        return track
+
+    def _saveTracks(self, tracks, context):
+        element = Element("tracks")
+        for track in tracks:
+            track_element = self._saveTrack(track, context)
+            element.append(track_element)
+
+        return element
+
+    def _loadTracks(self, element, context):
+        tracks = []
+        for track_element in element:
+            track = self._loadTrack(track_element, context)
+            tracks.append(track)
+
+        return tracks
+
     def _saveTimelineObject(self, timeline_object, context):
         element = Element("timeline-object")
         factory_ref = self._saveFactoryRef(timeline_object.factory, context)
         element.append(factory_ref)
-        track_object_refs = SubElement(element, "track-object-refs")
-        for track_object in timeline_object.track_objects:
-            track_object_ref = self._saveTrackObjectRef(track_object, context)
-            track_object_refs.append(track_object_ref)
-
+        track_object_refs = \
+                self._saveTrackObjectRefs(timeline_object.track_objects,
+                        context)
+        element.append(track_object_refs)
+        
         return element
 
+    def _loadTimelineObject(self, element, context):
+        factory_ref = element.find("factory-ref")
+        factory = self._loadFactoryRef(factory_ref, context)
+
+        timeline_object = TimelineObject(factory)
+        track_object_refs_element = element.find("track-object-refs")
+        track_objects = \
+                self._loadTrackObjectRefs(track_object_refs_element, context)
+
+        for track_object in track_objects:
+            timeline_object.addTrackObject(track_object)
+
+        return timeline_object
+
     def _saveTimelineObjects(self, timeline_objects, context):
         element = Element("timeline-objects")
         for timeline_object in timeline_objects:
-            timeline_object_element = self._saveTimelineObject(timeline_object)
+            timeline_object_element = self._saveTimelineObject(timeline_object,
+                    context)
             element.append(timeline_object_element)
 
         return element
 
-    def _saveTracks(self, tracks, context):
-        element = Element("tracks")
-        for track in tracks:
-            track_element = self._saveTrack(track, context)
-            element.append(track_element)
+    def _loadTimelineObjects(self, element, context):
+        timeline_objects = []
+        for timeline_object_element in element:
+            timeline_object = \
+                    self._loadTimelineObject(timeline_object_element, context)
+            timeline_objects.append(timeline_object)
 
-        return element
+        return timeline_objects
 
     def _saveTimeline(self, timeline, context):
         element = Element("timeline")
@@ -214,6 +354,23 @@ class ElementTreeFormatter(Formatter):
 
         return element
 
+    def _loadTimeline(self, element, context):
+        tracks_element = element.find("tracks")
+        tracks = self._loadTracks(tracks_element, context)
+
+        timeline_objects_element = element.find("timeline-objects")
+        timeline_objects = \
+                self._loadTimelineObjects(timeline_objects_element, context)
+
+        timeline = Timeline()
+        for track in tracks:
+            timeline.addTrack(track)
+
+        for timeline_object in timeline_objects:
+            timeline.addTimelineObject(timeline_object)
+
+        return timeline
+
     def _saveMainTag(self, context):
         element = Element("pitivi")
         element.attrib["formatter"] = "etree"
@@ -232,3 +389,23 @@ class ElementTreeFormatter(Formatter):
         root.append(timeline_element)
 
         return root
+
+    def _loadProject(self, element, context):
+        factories_element = element.find("factories")
+        factories = self._loadFactories(factories_element, context)
+
+        timeline_element = element.find("timeline")
+        timeline = self._loadTimeline(timeline_element, context)
+
+        project = Project()
+        project.timeline = timeline
+
+        # FIXME: add factories to the sources list
+
+        for factory in factories:
+            if isinstance(factory, SourceFactory):
+                timeline.addSourceFactory(factory)
+            else:
+                raise NotImplementedError()
+
+        return project
diff --git a/tests/test_formatter.py b/tests/test_formatter.py
index 9642f25..fef6d46 100644
--- a/tests/test_formatter.py
+++ b/tests/test_formatter.py
@@ -22,10 +22,12 @@
 from unittest import TestCase
 from StringIO import StringIO
 import gst
+from xml.etree.ElementTree import Element, SubElement, tostring
 
 from pitivi.reflect import qual, namedAny
-from pitivi.formatters.etree import ElementTreeFormatter, \
-        ElementTreeFormatterContext, version
+from pitivi.formatters.etree import ElementTreeFormatter, version, \
+        ElementTreeFormatterSaveContext, ElementTreeFormatterLoadContext, \
+        indent, tostring
 from pitivi.stream import VideoStream, AudioStream
 from pitivi.factories.file import FileSourceFactory
 from pitivi.factories.test import VideoTestSourceFactory
@@ -36,10 +38,13 @@ from pitivi.project import Project
 class FakeElementTreeFormatter(ElementTreeFormatter):
     pass
 
+def ts(time):
+    return "(gint64)%s" % time
+
 class TestFormatterSave(TestCase):
     def setUp(self):
         self.formatter = FakeElementTreeFormatter()
-        self.context = ElementTreeFormatterContext()
+        self.context = ElementTreeFormatterSaveContext()
 
     def testSaveStream(self):
         stream = VideoStream(gst.Caps("video/x-raw-rgb, blah=meh"))
@@ -122,12 +127,12 @@ class TestFormatterSave(TestCase):
         self.failUnlessEqual(element.tag, "track-object")
         self.failUnlessEqual(element.attrib["type"],
                 qual(track_object.__class__))
-        self.failUnlessEqual(element.attrib["start"], str(10 * gst.SECOND))
-        self.failUnlessEqual(element.attrib["duration"], str(20 * gst.SECOND))
-        self.failUnlessEqual(element.attrib["in_point"], str(5 * gst.SECOND))
+        self.failUnlessEqual(element.attrib["start"], ts(10 * gst.SECOND))
+        self.failUnlessEqual(element.attrib["duration"], ts(20 * gst.SECOND))
+        self.failUnlessEqual(element.attrib["in_point"], ts(5 * gst.SECOND))
         self.failUnlessEqual(element.attrib["media_duration"],
-                str(15 * gst.SECOND))
-        self.failUnlessEqual(element.attrib["priority"], str(10))
+                ts(15 * gst.SECOND))
+        self.failUnlessEqual(element.attrib["priority"], "(int)10")
 
         self.failIfEqual(element.find("factory-ref"), None)
         self.failIfEqual(element.find("stream-ref"), None)
@@ -202,6 +207,30 @@ class TestFormatterSave(TestCase):
         track_object_refs = element.find("track-object-refs")
         self.failUnlessEqual(len(track_object_refs), 1)
 
+    def testSavetimelineObjects(self):
+        video_stream = VideoStream(gst.Caps("video/x-raw-yuv"))
+        audio_stream = AudioStream(gst.Caps("audio/x-raw-int"))
+        source1 = FileSourceFactory("file1.ogg")
+
+        # these two calls are needed to populate the context for the -ref
+        # elements
+        self.formatter._saveSource(source1, self.context)
+        self.formatter._saveStream(video_stream, self.context)
+
+        track_object = SourceTrackObject(source1, video_stream,
+                start=10 * gst.SECOND, duration=20 * gst.SECOND,
+                in_point=5 * gst.SECOND, media_duration=15 * gst.SECOND,
+                priority=10)
+
+        self.formatter._saveTrackObject(track_object, self.context)
+
+        timeline_object = TimelineObject(source1)
+        timeline_object.addTrackObject(track_object)
+
+        element = self.formatter._saveTimelineObjects([timeline_object],
+                self.context)
+        self.failUnlessEqual(len(element), 1)
+
     def testSaveTimeline(self):
         video_stream = VideoStream(gst.Caps("video/x-raw-yuv"))
         audio_stream = AudioStream(gst.Caps("audio/x-raw-int"))
@@ -276,3 +305,210 @@ class TestFormatterSave(TestCase):
         self.failUnlessEqual(element.tag, "pitivi")
         self.failIfEqual(element.find("factories"), None)
         self.failIfEqual(element.find("timeline"), None)
+
+
+class TestFormatterLoad(TestCase):
+    def setUp(self):
+        self.formatter = FakeElementTreeFormatter()
+        self.context = ElementTreeFormatterLoadContext()
+
+    def testLoadStream(self):
+        caps = gst.Caps("video/x-raw-yuv")
+        element = Element("stream")
+        element.attrib["id"] = "1"
+        element.attrib["type"] = "pitivi.stream.VideoStream"
+        element.attrib["caps"] = str(caps)
+
+        stream = self.formatter._loadStream(element, self.context)
+        self.failUnlessEqual(qual(stream.__class__), element.attrib["type"])
+        self.failUnlessEqual(str(stream.caps), str(caps))
+        self.failUnlessEqual(stream, self.context.streams["1"])
+
+    def testLoadStreamRef(self):
+        stream = VideoStream(gst.Caps("meh"))
+        self.context.streams["1"] = stream
+        element = Element("stream-ref")
+        element.attrib["id"] = "1"
+        stream1 = self.formatter._loadStreamRef(element, self.context)
+        self.failUnlessEqual(stream, stream1)
+
+    def testLoadFactory(self):
+        element = Element("source")
+        element.attrib["id"] = "1"
+        element.attrib["type"] = "pitivi.factories.test.VideoTestSourceFactory"
+        output_streams = SubElement(element, "output-streams")
+        output_stream = SubElement(output_streams, "stream")
+        caps = gst.Caps("video/x-raw-yuv")
+        output_stream.attrib["id"] = "1"
+        output_stream.attrib["type"] = "pitivi.stream.VideoStream"
+        output_stream.attrib["caps"] = str(caps)
+
+        factory = self.formatter._loadFactory(element, self.context)
+        self.failUnless(isinstance(factory, VideoTestSourceFactory))
+        self.failUnlessEqual(len(factory.output_streams), 2)
+
+        self.failUnlessEqual(self.context.factories["1"], factory)
+
+    def testLoadFactoryRef(self):
+        class Tag(object): pass
+        tag = Tag()
+        self.context.factories["1"] = tag
+        element = Element("factory-ref", id="1")
+        ret = self.formatter._loadFactoryRef(element, self.context)
+        self.failUnless(ret is tag)
+
+    def testLoadTrackObject(self):
+        element = Element("track-object",
+                type="pitivi.timeline.track.SourceTrackObject",
+                start=ts(1 * gst.SECOND), duration=ts(10 * gst.SECOND),
+                in_point=ts(5 * gst.SECOND),
+                media_duration=ts(15 * gst.SECOND), priority=ts(5))
+        factory = VideoTestSourceFactory()
+        self.context.factories["1"] = factory
+        stream = VideoStream(gst.Caps("meh"))
+        self.context.streams["1"] = stream
+        factory_ref = SubElement(element, "factory-ref", id="1")
+        stream_ref = SubElement(element, "stream-ref", id="1")
+
+        track_object = self.formatter._loadTrackObject(element, self.context)
+        self.failUnless(isinstance(track_object, SourceTrackObject))
+        self.failUnlessEqual(track_object.factory, factory)
+        self.failUnlessEqual(track_object.stream, stream)
+
+        self.failUnlessEqual(track_object.start, 1 * gst.SECOND)
+        self.failUnlessEqual(track_object.duration, 10 * gst.SECOND)
+        self.failUnlessEqual(track_object.in_point, 5 * gst.SECOND)
+        self.failUnlessEqual(track_object.media_duration, 15 * gst.SECOND)
+        self.failUnlessEqual(track_object.priority, 5)
+
+    def testLoadTrackObjectRef(self):
+        class Tag(object):
+            pass
+        tag = Tag()
+        self.context.track_objects["1"] = tag
+        element = Element("track-object-ref", id="1")
+        ret = self.formatter._loadTrackObjectRef(element, self.context)
+        self.failUnless(ret is tag)
+
+    def testLoadTrack(self):
+        element = Element("track")
+        stream_element = SubElement(element, "stream", id="1",
+                type="pitivi.stream.VideoStream", caps="video/x-raw-rgb")
+
+        track_objects_element = SubElement(element, "track-objects")
+        track_object = SubElement(track_objects_element, "track-object",
+                type="pitivi.timeline.track.SourceTrackObject",
+                start=ts(1 * gst.SECOND), duration=ts(10 * gst.SECOND),
+                in_point=ts(5 * gst.SECOND),
+                media_duration=ts(15 * gst.SECOND), priority=ts(5))
+        factory = VideoTestSourceFactory()
+        self.context.factories["1"] = factory
+        stream = VideoStream(gst.Caps("video/x-raw-rgb"))
+        self.context.streams["1"] = stream
+        factory_ref = SubElement(track_object, "factory-ref", id="1")
+        stream_ref = SubElement(track_object, "stream-ref", id="1")
+
+        track = self.formatter._loadTrack(element, self.context)
+
+        self.failUnlessEqual(len(track.track_objects), 2)
+        # FIXME: this is an hack
+        self.failUnlessEqual(str(track.stream), str(stream))
+
+    def testLoadTimelineObject(self):
+        video_stream = VideoStream(gst.Caps("video/x-raw-yuv"))
+        source1 = VideoTestSourceFactory()
+        self.context.factories["1"] = source1
+        self.context.track_objects["1"] = SourceTrackObject(source1, video_stream)
+
+        element = Element("timeline-object")
+        factory_ref = SubElement(element, "factory-ref", id="1")
+        stream_ref = SubElement(element, "stream-ref", id="1")
+        track_object_refs = SubElement(element, "track-object-refs")
+        track_object_ref = SubElement(track_object_refs,
+                "track-object-ref", id="1")
+
+        timeline_object = \
+                self.formatter._loadTimelineObject(element, self.context)
+
+        self.failUnlessEqual(timeline_object.factory, source1)
+        self.failUnlessEqual(len(timeline_object.track_objects), 1)
+
+    def testLoadTimeline(self):
+        timeline_element = Element("timeline")
+        tracks_element = SubElement(timeline_element, "tracks")
+        track_element = SubElement(tracks_element, "track")
+        stream_element = SubElement(track_element, "stream", id="1",
+                type="pitivi.stream.VideoStream", caps="video/x-raw-rgb")
+
+        track_objects_element = SubElement(track_element, "track-objects")
+        track_object = SubElement(track_objects_element, "track-object",
+                type="pitivi.timeline.track.SourceTrackObject",
+                start=ts(1 * gst.SECOND), duration=ts(10 * gst.SECOND),
+                in_point=ts(5 * gst.SECOND),
+                media_duration=ts(15 * gst.SECOND), priority=ts(5))
+        factory = VideoTestSourceFactory()
+        self.context.factories["1"] = factory
+        stream = VideoStream(gst.Caps("video/x-raw-rgb"))
+        self.context.streams["1"] = stream
+        factory_ref = SubElement(track_object, "factory-ref", id="1")
+        stream_ref = SubElement(track_object, "stream-ref", id="1")
+
+        video_stream = VideoStream(gst.Caps("video/x-raw-yuv"))
+        source1 = VideoTestSourceFactory()
+        self.context.factories["2"] = source1
+        self.context.track_objects["1"] = SourceTrackObject(source1, video_stream)
+
+        timeline_objects_element = SubElement(timeline_element,
+                "timeline-objects")
+        timeline_object_element = \
+                SubElement(timeline_objects_element, "timeline-object")
+        factory_ref = SubElement(timeline_object_element, "factory-ref", id="1")
+        stream_ref = SubElement(timeline_object_element, "stream-ref", id="1")
+        track_object_refs = SubElement(timeline_object_element, "track-object-refs")
+        track_object_ref = SubElement(track_object_refs,
+                "track-object-ref", id="1")
+        timeline = self.formatter._loadTimeline(timeline_element, self.context)
+        self.failUnlessEqual(len(timeline.tracks), 1)
+
+    def testLoadProject(self):
+        video_stream = VideoStream(gst.Caps("video/x-raw-yuv"))
+        audio_stream = AudioStream(gst.Caps("audio/x-raw-int"))
+        source1 = VideoTestSourceFactory()
+
+        self.formatter._saveSource(source1, self.context)
+        self.formatter._saveStream(video_stream, self.context)
+
+        track_object = SourceTrackObject(source1, video_stream,
+                start=10 * gst.SECOND, duration=20 * gst.SECOND,
+                in_point=5 * gst.SECOND, media_duration=15 * gst.SECOND,
+                priority=10)
+
+        self.formatter._saveTrackObject(track_object, self.context)
+
+        track = Track(video_stream)
+        track.addTrackObject(track_object)
+
+        timeline_object = TimelineObject(source1)
+        timeline_object.addTrackObject(track_object)
+
+        self.formatter._saveTimelineObject(timeline_object, self.context)
+
+        timeline = Timeline()
+        timeline.addTrack(track)
+
+        self.formatter._saveTimeline(timeline, self.context)
+
+        project = Project()
+        project.timeline = timeline
+        project.sources.addFactory("meh", source1)
+
+        element = self.formatter._saveProject(project, self.context)
+
+        self.failUnlessEqual(element.tag, "pitivi")
+        self.failIfEqual(element.find("factories"), None)
+        self.failIfEqual(element.find("timeline"), None)
+
+        indent(element)
+        f = file("/tmp/untitled.pptv", "w")
+        f.write(tostring(element))
+        f.close()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]