/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/load.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/load.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 105 by jan, Fri Apr 19 15:36:57 2002 UTC revision 1664 by bh, Wed Aug 27 15:20:54 2003 UTC
# Line 1  Line 1 
1  # Copyright (C) 2001, 2002 by Intevation GmbH  # Copyright (C) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jan-Oliver Wagner <[email protected]>  # Jan-Oliver Wagner <[email protected]>
4    # Bernhard Herzog <[email protected]>
5    # Jonathan Coles <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with GRASS for details.  # Read the file COPYING coming with GRASS for details.
# Line 11  Parser for thuban session files. Line 13  Parser for thuban session files.
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16  import sys, string, os  import string, os
17    
18    import xml.sax
19    import xml.sax.handler
20    from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
21    
22    from Thuban import _
23    
24    from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
25         FIELDTYPE_STRING
26    
27    from Thuban.Model.color import Color, Transparent
28    
29  from Thuban.Model.session import Session  from Thuban.Model.session import Session
30  from Thuban.Model.map import Map  from Thuban.Model.map import Map
31  from Thuban.Model.layer import Layer  from Thuban.Model.layer import Layer, RasterLayer
 from Thuban.Model.color import Color  
32  from Thuban.Model.proj import Projection  from Thuban.Model.proj import Projection
33    from Thuban.Model.range import Range
34    from Thuban.Model.classification import Classification, \
35        ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
36        ClassGroupProperties
37    from Thuban.Model.data import DerivedShapeStore, ShapefileStore
38    from Thuban.Model.table import DBFTable
39    from Thuban.Model.transientdb import TransientJoinedTable
40    
41  oldPython=0  from Thuban.Model.xmlreader import XMLReader
42    import resource
43    
44  if not sys.__dict__.has_key("version_info"):  import postgisdb
     # We can assume to have python 1.5.2 or lower here now  
     oldPython=1  
   
 if oldPython:  
     try:  
         from xml.sax.saxexts import make_parser  
         from xml.sax.saxlib import HandlerBase  
         from xml.sax import saxutils  
     except ImportError:  
         sys.stdout.write(("You need to have Python-XML installed or"  
                           " a modern Python!\n"  
                           "Check www.python.org/sigs/xml-sig/\n\n"))  
         raise  
 else:  
     # Do the python 2.0 standard xml thing and map it on the old names  
     import xml.sax  
     import xml.sax.handler  
     HandlerBase=xml.sax.handler.ContentHandler  
     from xml.sax import make_parser  
   
 class testSAXContentHandler(HandlerBase):  
 # SAX compliant  
     def characters(self, ch, start, length):  
         pass  
       
 def test_for_broken_SAX():  
     ch=testSAXContentHandler()  
     try:  
         xml.sax.parseString("""<?xml version="1.0"?>  
             <child1 name="paul">Text goes here</child1>  
         """,ch)  
     except TypeError:  
         return 1  
     return 0  
45    
46    class LoadError(Exception):
47    
48  def parse_color(color):      """Exception raised when the thuban file is corrupted
49    
50        Not all cases of corrupted thuban files will lead to this exception
51        but those that are found by checks in the loading code itself are.
52      """      """
53      Return the color object for the string color. Color may be either  
54      'None' or of the form '#RRGGBB' in the usual HTML color notation  
55    class LoadCancelled(Exception):
56    
57        """Exception raised to indicate that loading was interrupted by the user"""
58    
59    
60    def parse_color(color):
61        """Return the color object for the string color.
62    
63        Color may be either 'None' or of the form '#RRGGBB' in the usual
64        HTML color notation
65      """      """
66      color = string.strip(color)      color = string.strip(color)
67      if color == "None":      if color == "None":
68          result = None          result = Transparent
69      elif color[0] == '#':      elif color[0] == '#':
70          if len(color) == 7:          if len(color) == 7:
71              r = string.atoi(color[1:3], 16) / 255.0              r = string.atoi(color[1:3], 16) / 255.0
# Line 72  def parse_color(color): Line 73  def parse_color(color):
73              b = string.atoi(color[5:7], 16) / 255.0              b = string.atoi(color[5:7], 16) / 255.0
74              result = Color(r, g, b)              result = Color(r, g, b)
75          else:          else:
76              raise ValueError("Invalid hexadecimal color specification %s"              raise ValueError(_("Invalid hexadecimal color specification %s")
77                               % color)                               % color)
78      else:      else:
79          raise ValueError("Invalid color specification %s" % color)          raise ValueError(_("Invalid color specification %s") % color)
80      return result      return result
81    
82    class AttrDesc:
83    
84  class ProcessSession(HandlerBase):      def __init__(self, name, required = False, default = "",
85                     conversion = None):
86            if not isinstance(name, tuple):
87                fullname = (None, name)
88            else:
89                fullname = name
90                name = name[1]
91            self.name = name
92            self.fullname = fullname
93            self.required = required
94            self.default = default
95            self.conversion = conversion
96    
97      def __init__(self, directory):          # set by the SessionLoader's check_attrs method
98          """Inititialize the Sax handler.          self.value = None
99    
100    
101    class SessionLoader(XMLReader):
102    
103        def __init__(self, db_connection_callback = None):
104            """Inititialize the Sax handler."""
105            XMLReader.__init__(self)
106    
107            self.db_connection_callback = db_connection_callback
108    
         directory is the directory containing the session file. It's  
         needed to interpret embedded relative filenames  
         """  
         self.directory = directory  
         self.chars = ''  
109          self.theSession = None          self.theSession = None
110          self.aMap = None          self.aMap = None
111          self.aLayer = None          self.aLayer = None
112    
113      def startElement(self, name, attrs):          # Map ids used in the thuban file to the corresponding objects
114          if name == 'session':          # in the session
115              self.theSession = Session(attrs.get('title', None))          self.idmap = {}
116          elif name == 'map':  
117              self.aMap = Map(attrs.get('title', None))          dispatchers = {
118          elif name == 'projection':              'session'       : ("start_session",        "end_session"),
119              self.ProjectionParams = [ ]  
120          elif name == 'parameter':              'dbconnection': ("start_dbconnection", None),
121              s = attrs.get('value')  
122              s = str(s) # we can't handle unicode in proj              'dbshapesource': ("start_dbshapesource", None),
123              self.ProjectionParams.append(s)              'fileshapesource': ("start_fileshapesource", None),
124          elif name == 'layer':              'derivedshapesource': ("start_derivedshapesource", None),
125              title = attrs.get('title', "")              'filetable': ("start_filetable", None),
126              filename = attrs.get('filename', "")              'jointable': ("start_jointable", None),
127              filename = os.path.join(self.directory, filename)  
128              fill = parse_color(attrs.get('fill', "None"))              'map'           : ("start_map",            "end_map"),
129              stroke = parse_color(attrs.get('stroke', "#000000"))              'projection'    : ("start_projection",     "end_projection"),
130              stroke_width = int(attrs.get("stroke_width", "1"))              'parameter'     : ("start_parameter",      None),
131              self.aLayer = Layer(title, filename, fill = fill, stroke = stroke,              'layer'         : ("start_layer",          "end_layer"),
132                                  stroke_width = stroke_width)              'rasterlayer'   : ("start_rasterlayer",    "end_rasterlayer"),
133          elif name == 'table':              'classification': ("start_classification", "end_classification"),
134              print "table title: %s" % attrs.get('title', None)              'clnull'        : ("start_clnull",         "end_clnull"),
135          elif name == 'labellayer':              'clpoint'       : ("start_clpoint",        "end_clpoint"),
136              self.aLayer = self.aMap.LabelLayer()              'clrange'       : ("start_clrange",        "end_clrange"),
137          elif name == 'label':              'cldata'        : ("start_cldata",         "end_cldata"),
138              x = float(attrs['x'])              'table'         : ("start_table",          "end_table"),
139              y = float(attrs['y'])              'labellayer'    : ("start_labellayer",     None),
140              text = attrs['text']              'label'         : ("start_label",          None)}
141              halign = attrs['halign']  
142              valign = attrs['valign']          # all dispatchers should be used for the 0.8 and 0.9 namespaces too
143              self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)          for xmlns in ("http://thuban.intevation.org/dtds/thuban-0.8.dtd",
144                                  "http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd",
145                          "http://thuban.intevation.org/dtds/thuban-0.9.dtd"):
146      if not oldPython and test_for_broken_SAX():              for key, value in dispatchers.items():
147          # works with python 2.0, but is not SAX compliant                  dispatchers[(xmlns, key)] = value
148          def characters(self, ch):  
149              self.my_characters(ch)          XMLReader.AddDispatchers(self, dispatchers)
150      else:  
151          # SAX compliant      def start_session(self, name, qname, attrs):
152          def characters(self, ch, start, length):          self.theSession = Session(self.encode(attrs.get((None, 'title'),
153              self.my_characters(ch[start:start+length])                                                          None)))
154    
155      def my_characters(self, ch):      def end_session(self, name, qname):
156          self.chars = self.chars + ch          pass
157    
158      def endElement(self, name):      def check_attrs(self, element, attrs, descr):
159          # If it's not a parameter element, ignore it          """Check and convert some of the attributes of an element
160          if name == 'session':  
161              #print "end of session"          Parameters:
162              pass             element -- The element name
163          if name == 'map':             attrs -- The attrs mapping as passed to the start_* methods
164              self.theSession.AddMap(self.aMap)             descr -- Sequence of attribute descriptions (AttrDesc instances)
165          if name == 'projection':  
166              self.aMap.SetProjection(Projection(self.ProjectionParams))          Return a dictionary containig normalized versions of the
167          if name == 'layer':          attributes described in descr. The keys of that dictionary are
168              self.aMap.AddLayer(self.aLayer)          the name attributes of the attribute descriptions. The attrs
169          if name == 'table':          dictionary will not be modified.
170              #print "end of table"  
171            If the attribute is required, i.e. the 'required' attribute of
172            the descrtiption is true, but it is not in attrs, raise a
173            LoadError.
174    
175            If the attribute has a default value and it is not present in
176            attrs, use that default value as the value in the returned dict.
177    
178            If a conversion is specified, convert the value before putting
179            it into the returned dict. The following conversions are
180            available:
181    
182               'filename' -- The attribute is a filename.
183    
184                             If the filename is a relative name, interpret
185                             it relative to the directory containing the
186                             .thuban file and make it an absolute name
187    
188               'shapestore' -- The attribute is the ID of a shapestore
189                               defined earlier in the .thuban file. Look it
190                               up self.idmap
191    
192               'table' -- The attribute is the ID of a table or shapestore
193                          defined earlier in the .thuban file. Look it up
194                          self.idmap. If it's the ID of a shapestore the
195                          value will be the table of the shapestore.
196    
197               'idref' -- The attribute is the id of an object defined
198                          earlier in the .thuban file. Look it up self.idmap
199    
200               'ascii' -- The attribute is converted to a bytestring with
201                          ascii encoding.
202            """
203            normalized = {}
204    
205            for d in descr:
206                if d.required and not attrs.has_key(d.fullname):
207                    raise LoadError("Element %s requires an attribute %r"
208                                    % (element, d.name))
209                value = attrs.get(d.fullname, d.default)
210    
211                if d.conversion in ("idref", "shapesource"):
212                    if value in self.idmap:
213                        value = self.idmap[value]
214                    else:
215                        raise LoadError("Element %s requires an already defined ID"
216                                        " in attribute %r"
217                                        % (element, d.name))
218                elif d.conversion == "table":
219                    if value in self.idmap:
220                        value = self.idmap[value]
221                        if isinstance(value, ShapefileStore):
222                            value = value.Table()
223                    else:
224                        raise LoadError("Element %s requires an already defined ID"
225                                        " in attribute %r"
226                                        % (element, d.name))
227                elif d.conversion == "filename":
228                    value = os.path.abspath(os.path.join(self.GetDirectory(),
229                                                         value))
230                elif d.conversion == "ascii":
231                    value = value.encode("ascii")
232                else:
233                    if d.conversion:
234                        raise ValueError("Unknown attribute conversion %r"
235                                         % d.conversion)
236    
237                normalized[d.name] = value
238            return normalized
239    
240        def start_dbconnection(self, name, qname, attrs):
241            attrs = self.check_attrs(name, attrs,
242                                     [AttrDesc("id", True),
243                                      AttrDesc("dbtype", True),
244                                      AttrDesc("host", False, ""),
245                                      AttrDesc("port", False, ""),
246                                      AttrDesc("user", False, ""),
247                                      AttrDesc("dbname", True)])
248            ID = attrs["id"]
249            dbtype = attrs["dbtype"]
250            if dbtype != "postgis":
251                raise LoadError("dbtype %r not supported" % filetype)
252    
253            del attrs["id"]
254            del attrs["dbtype"]
255    
256            # Try to open the connection and if it fails ask the user for
257            # the correct parameters repeatedly.
258            # FIXME: it would be better not to insist on getting a
259            # connection here. We should handle this more like the raster
260            # images where the layers etc still are created but are not
261            # drawn in case Thuban can't use the data for various reasons
262            while 1:
263                try:
264                    conn = postgisdb.PostGISConnection(**attrs)
265                    break
266                except postgisdb.ConnectionError, val:
267                    if self.db_connection_callback is not None:
268                        attrs = self.db_connection_callback(attrs, str(val))
269                        if attrs is None:
270                            raise LoadCancelled
271                    else:
272                        raise
273    
274            self.idmap[ID] = conn
275            self.theSession.AddDBConnection(conn)
276    
277        def start_dbshapesource(self, name, qname, attrs):
278            attrs = self.check_attrs(name, attrs,
279                                     [AttrDesc("id", True),
280                                      AttrDesc("dbconn", True,
281                                               conversion = "idref"),
282                                      AttrDesc("tablename", True,
283                                               conversion = "ascii")])
284            ID = attrs["id"]
285            db = attrs["dbconn"]
286            tablename = attrs["tablename"]
287            self.idmap[ID] = self.theSession.OpenDBShapeStore(db, tablename)
288    
289        def start_fileshapesource(self, name, qname, attrs):
290            attrs = self.check_attrs(name, attrs,
291                                      [AttrDesc("id", True),
292                                       AttrDesc("filename", True,
293                                                conversion = "filename"),
294                                       AttrDesc("filetype", True)])
295            ID = attrs["id"]
296            filename = attrs["filename"]
297            filetype = attrs["filetype"]
298            if filetype != "shapefile":
299                raise LoadError("shapesource filetype %r not supported" % filetype)
300            self.idmap[ID] = self.theSession.OpenShapefile(filename)
301    
302        def start_derivedshapesource(self, name, qname, attrs):
303            attrs = self.check_attrs(name, attrs,
304                                     [AttrDesc("id", True),
305                                      AttrDesc("shapesource", True,
306                                               conversion = "shapesource"),
307                                      AttrDesc("table", True, conversion="table")])
308            store = DerivedShapeStore(attrs["shapesource"], attrs["table"])
309            self.theSession.AddShapeStore(store)
310            self.idmap[attrs["id"]] = store
311    
312        def start_filetable(self, name, qname, attrs):
313            attrs = self.check_attrs(name, attrs,
314                                     [AttrDesc("id", True),
315                                      AttrDesc("title", True),
316                                      AttrDesc("filename", True,
317                                               conversion = "filename"),
318                                      AttrDesc("filetype")])
319            filetype = attrs["filetype"]
320            if filetype != "DBF":
321                raise LoadError("shapesource filetype %r not supported" % filetype)
322            table = DBFTable(attrs["filename"])
323            table.SetTitle(attrs["title"])
324            self.idmap[attrs["id"]] = self.theSession.AddTable(table)
325    
326        def start_jointable(self, name, qname, attrs):
327            attrs = self.check_attrs(name, attrs,
328                                     [AttrDesc("id", True),
329                                      AttrDesc("title", True),
330                                      AttrDesc("left", True, conversion="table"),
331                                      AttrDesc("leftcolumn", True),
332                                      AttrDesc("right", True, conversion="table"),
333                                      AttrDesc("rightcolumn", True),
334    
335                                      # jointype is required for file
336                                      # version 0.9 but this attribute
337                                      # wasn't in the 0.8 version because of
338                                      # an oversight so we assume it's
339                                      # optional since we want to handle
340                                      # both file format versions here.
341                                      AttrDesc("jointype", False,
342                                               default="INNER")])
343    
344            jointype = attrs["jointype"]
345            if jointype == "LEFT OUTER":
346                outer_join = True
347            elif jointype == "INNER":
348                outer_join = False
349            else:
350                raise LoadError("jointype %r not supported" % jointype )
351            table = TransientJoinedTable(self.theSession.TransientDB(),
352                                         attrs["left"], attrs["leftcolumn"],
353                                         attrs["right"], attrs["rightcolumn"],
354                                         outer_join = outer_join)
355            table.SetTitle(attrs["title"])
356            self.idmap[attrs["id"]] = self.theSession.AddTable(table)
357    
358        def start_map(self, name, qname, attrs):
359            """Start a map."""
360            self.aMap = Map(self.encode(attrs.get((None, 'title'), None)))
361    
362        def end_map(self, name, qname):
363            self.theSession.AddMap(self.aMap)
364            self.aMap = None
365    
366        def start_projection(self, name, qname, attrs):
367            self.ProjectionName = self.encode(attrs.get((None, 'name'), None))
368            self.ProjectionParams = [ ]
369    
370        def end_projection(self, name, qname):
371            if self.aLayer is not None:
372                obj = self.aLayer
373            elif self.aMap is not None:
374                obj = self.aMap
375            else:
376                assert False, "projection tag out of context"
377              pass              pass
378    
379  def load_session(filename):          obj.SetProjection(
380      """Load a Thuban session from the file object file"""              Projection(self.ProjectionParams, self.ProjectionName))
381      dir = os.path.dirname(filename)  
382      file = open(filename)      def start_parameter(self, name, qname, attrs):
383      handler = ProcessSession(dir)          s = attrs.get((None, 'value'))
384            s = str(s) # we can't handle unicode in proj
385      if oldPython:          self.ProjectionParams.append(s)
386          parser = make_parser()  
387          parser.setDocumentHandler(handler)      def start_layer(self, name, qname, attrs, layer_class = Layer):
388          parser.setErrorHandler(saxutils.ErrorPrinter())          """Start a layer
389          parser.parseFile(file)  
390          parser.close()          Instantiate a layer of class layer_class from the attributes in
391      else:          attrs which may be a dictionary as well as the normal SAX attrs
392          xml.sax.parse(file,handler)          object and bind it to self.aLayer.
393            """
394            title = self.encode(attrs.get((None, 'title'), ""))
395            filename = attrs.get((None, 'filename'), "")
396            filename = os.path.join(self.GetDirectory(), filename)
397            filename = self.encode(filename)
398            visible  = self.encode(attrs.get((None, 'visible'), "true")) != "false"
399            fill = parse_color(attrs.get((None, 'fill'), "None"))
400            stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
401            stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
402            if attrs.has_key((None, "shapestore")):
403                store = self.idmap[attrs[(None, "shapestore")]]
404            else:
405                store = self.theSession.OpenShapefile(filename)
406            self.aLayer = layer_class(title, store,
407                                      fill = fill, stroke = stroke,
408                                      lineWidth = stroke_width,
409                                      visible = visible)
410    
411        def end_layer(self, name, qname):
412            self.aMap.AddLayer(self.aLayer)
413            self.aLayer = None
414    
415        def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
416            title = self.encode(attrs.get((None, 'title'), ""))
417            filename = attrs.get((None, 'filename'), "")
418            filename = os.path.join(self.GetDirectory(), filename)
419            filename = self.encode(filename)
420            visible  = self.encode(attrs.get((None, 'visible'), "true")) != "false"
421    
422            self.aLayer = layer_class(title, filename, visible = visible)
423    
424        def end_rasterlayer(self, name, qname):
425            self.aMap.AddLayer(self.aLayer)
426            self.aLayer = None
427    
428        def start_classification(self, name, qname, attrs):
429            field = attrs.get((None, 'field'), None)
430    
431            fieldType = attrs.get((None, 'field_type'), None)
432            dbFieldType = self.aLayer.GetFieldType(field)
433    
434            if fieldType != dbFieldType:
435                raise ValueError(_("xml field type differs from database!"))
436    
437            # setup conversion routines depending on the kind of data
438            # we will be seeing later on
439            if fieldType == FIELDTYPE_STRING:
440                self.conv = str
441            elif fieldType == FIELDTYPE_INT:
442                self.conv = lambda p: int(float(p))
443            elif fieldType == FIELDTYPE_DOUBLE:
444                self.conv = float
445    
446            self.aLayer.SetClassificationColumn(field)
447    
448        def end_classification(self, name, qname):
449            pass
450    
451        def start_clnull(self, name, qname, attrs):
452            self.cl_group = ClassGroupDefault()
453            self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
454            self.cl_prop = ClassGroupProperties()
455    
456        def end_clnull(self, name, qname):
457            self.cl_group.SetProperties(self.cl_prop)
458            self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
459            del self.cl_group, self.cl_prop
460    
461        def start_clpoint(self, name, qname, attrs):
462            attrib_value = attrs.get((None, 'value'), "0")
463    
464            field = self.aLayer.GetClassificationColumn()
465            if self.aLayer.GetFieldType(field) == FIELDTYPE_STRING:
466                value = self.encode(attrib_value)
467            else:
468                value = self.conv(attrib_value)
469            self.cl_group = ClassGroupSingleton(value)
470            self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
471            self.cl_prop = ClassGroupProperties()
472    
473    
474        def end_clpoint(self, name, qname):
475            self.cl_group.SetProperties(self.cl_prop)
476            self.aLayer.GetClassification().AppendGroup(self.cl_group)
477            del self.cl_group, self.cl_prop
478    
479        def start_clrange(self, name, qname, attrs):
480    
481            range = attrs.get((None, 'range'), None)
482            # for backward compatibility (min/max are not saved)
483            min   = attrs.get((None, 'min'), None)
484            max   = attrs.get((None, 'max'), None)
485    
486            try:
487                if range is not None:
488                    self.cl_group = ClassGroupRange(Range(range))
489                elif min is not None and max is not None:
490                    self.cl_group = ClassGroupRange((self.conv(min),
491                                                     self.conv(max)))
492                else:
493                    self.cl_group = ClassGroupRange(Range(None))
494    
495            except ValueError:
496                raise ValueError(_("Classification range is not a number!"))
497    
498            self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
499            self.cl_prop = ClassGroupProperties()
500    
501    
502        def end_clrange(self, name, qname):
503            self.cl_group.SetProperties(self.cl_prop)
504            self.aLayer.GetClassification().AppendGroup(self.cl_group)
505            del self.cl_group, self.cl_prop
506    
507        def start_cldata(self, name, qname, attrs):
508            self.cl_prop.SetLineColor(
509                parse_color(attrs.get((None, 'stroke'), "None")))
510            self.cl_prop.SetLineWidth(
511                int(attrs.get((None, 'stroke_width'), "0")))
512            self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
513    
514        def end_cldata(self, name, qname):
515            pass
516    
517        def start_labellayer(self, name, qname, attrs):
518            self.aLayer = self.aMap.LabelLayer()
519    
520        def start_label(self, name, qname, attrs):
521            x = float(attrs[(None, 'x')])
522            y = float(attrs[(None, 'y')])
523            text = self.encode(attrs[(None, 'text')])
524            halign = attrs[(None, 'halign')]
525            valign = attrs[(None, 'valign')]
526            self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
527    
528        def characters(self, chars):
529            pass
530    
531    
532    def load_session(filename, db_connection_callback = None):
533        """Load a Thuban session from the file object file
534    
535        The db_connection_callback, if given should be a callable object
536        that can be called like this:
537           db_connection_callback(params, message)
538    
539        where params is a dictionary containing the known connection
540        parameters and message is a string with a message why the connection
541        failed. db_connection_callback should return a new dictionary with
542        corrected and perhaps additional parameters like a password or None
543        to indicate that the user cancelled.
544        """
545        handler = SessionLoader(db_connection_callback)
546        handler.read(filename)
547    
548      session = handler.theSession      session = handler.theSession
549      # Newly loaded session aren't modified      # Newly loaded session aren't modified
550      session.UnsetModified()      session.UnsetModified()
551    
552      return session      return session
553    
 if __name__ == "__main__":  
     # find out the command to run  
     if len(sys.argv) > 1:  
         print "usage: cat <file> | " + sys.argv[0]  
     else:  
         parseSession(sys.stdin)  

Legend:
Removed from v.105  
changed lines
  Added in v.1664

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26