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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2036 - (hide annotations)
Mon Dec 22 17:49:43 2003 UTC (21 years, 2 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 24075 byte(s)
* setup.py (setup call): 1.0.0, yeah!

* Thuban/version.py (longversion): 1.0.0, yeah!

* Thuban/Model/load.py (SessionLoader.__init__): Accept the
1.0.0 namespace too

* Thuban/Model/save.py (SessionSaver.write_session): Save with
1.0.0 namespace

* test/test_load.py (LoadSessionTest.dtd)
(TestSingleLayer.file_contents)
(TestNonAsciiColumnName.file_contents)
(TestLayerVisibility.file_contents)
(TestClassification.file_contents, TestLabels.file_contents)
(TestLayerProjection.file_contents)
(TestRasterLayer.file_contents, TestJoinedTable.file_contents)
(TestLabelLayer.file_contents, TestPostGISLayer.file_contents)
(TestPostGISLayerPassword.file_contents)
(TestLoadError.file_contents, TestLoadError.test): Update for
1.0.0 namespace

* test/test_save.py (SaveSessionTest.dtd)
(SaveSessionTest.testEmptySession)
(SaveSessionTest.testSingleLayer)
(SaveSessionTest.testLayerProjection)
(SaveSessionTest.testRasterLayer)
(SaveSessionTest.testClassifiedLayer)
(SaveSessionTest.test_dbf_table)
(SaveSessionTest.test_joined_table)
(SaveSessionTest.test_save_postgis): Update for 1.0.0 namespace

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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26