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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1016 - (hide annotations)
Fri May 23 11:05:59 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/session.py
File MIME type: text/x-python
File size: 11838 byte(s)
* Thuban/Model/session.py (Session.AddShapeStore): Define
AddShapeStore analogously to AddTable.

* test/test_session.py (TestSessionSimple.test_add_shapestore):
New. Test for AddShapeStore

1 bh 723 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4 jan 197 # Jan-Oliver Wagner <[email protected]>
5 bh 6 #
6     # This program is free software under the GPL (>=v2)
7     # Read the file COPYING coming with Thuban for details.
8    
9     __version__ = "$Revision$"
10    
11 bh 765 import os
12     from tempfile import mktemp
13     import weakref
14    
15 jan 197 from messages import MAPS_CHANGED, EXTENSIONS_CHANGED, FILENAME_CHANGED, \
16 jonathan 548 MAP_LAYERS_CHANGED, MAP_PROJECTION_CHANGED, \
17 jonathan 582 LAYER_CHANGED, LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED,\
18 bh 232 EXTENSION_CHANGED, EXTENSION_OBJECTS_CHANGED, CHANGED
19 bh 6
20 jan 374 from Thuban import _
21    
22 bh 6 from base import TitledObject, Modifiable
23     from map import Map
24 bh 723 from data import ShapefileStore
25 bh 6
26 bh 982 from transientdb import TransientDatabase, AutoTransientTable
27 bh 6
28 bh 765 class AutoRemoveFile:
29    
30     """Remove a file once all references go away."""
31    
32     def __init__(self, filename, tempdir = None):
33     """Initialize the AutoRemoveFile
34    
35     Parameters:
36     filename -- The name of the file to remove in __del__
37     tempdir -- Another object simple stored as an instance variable.
38    
39     As the name suggests the tempdir parameter is intended for a
40     temporary directory the file might be located in. The intended
41     use is that it's an instance of AutoRemoveDir.
42     """
43     self.filename = filename
44     self.tempdir = tempdir
45    
46     def __del__(self, remove = os.remove):
47     remove(self.filename)
48    
49     class AutoRemoveDir:
50    
51     """Remove a directory once all references go away
52    
53     The intended use of this class together with AutoRemoveFile is for
54     temporary directories and files containd therein. An AutoRemoveDir
55     should be instantiated for the directory and passed as the tempdir
56     parameter to every AutoRemoveFile instance created for files in the
57     directory. An AutoRemoveFile shold be instantiated for every file
58     created in the directory so that the directory is automatically
59     removed once the last file is removed.
60     """
61    
62     def __init__(self, filename):
63     """Initialize the AutoRemoveDir
64    
65     The parameter is the name of the directory.
66     """
67     self.filename = filename
68    
69     def __del__(self, rmdir = os.rmdir):
70     rmdir(self.filename)
71    
72    
73     # WeakKey dictionary mapping objects like the transient_db to
74     # AutoRemoveDir or AutoRemoveFile instances to make sure that the
75     # temporary files and the directory are deleted but not before the
76     # objects that use them go away.
77     auto_remover = weakref.WeakKeyDictionary()
78    
79 bh 6 class Session(TitledObject, Modifiable):
80    
81     """A complete session.
82    
83 jan 197 A Session consists of arbitrary numbers of maps, tables and extensions
84 bh 6
85     Session objects send the following events:
86    
87     TITLE_CHANGED -- The title has changed. Parameters: the session.
88    
89     FILENAME_CHANGED -- The filename has changed. No parameters.
90    
91     MAPS_CHANGED -- Maps were added, removed.
92    
93 jan 197 EXTENSIONS_CHANGED -- Extensions were added, removed.
94    
95 jonathan 548 MAP_LAYERS_CHANGED -- Same as the map's event of the same name.
96 bh 6 It's simply resent from the session to make
97     subscriptions easier.
98 bh 232
99     CHANGED -- Generic changed event. Parameters: the session. The
100     event is always issued when any other changed event
101     is issused. This is useful for code that needs to be
102     notified whenever something in the session has
103     changed but it's too cumbersome or error-prone to
104     subscribe to all the individual events.
105 bh 6 """
106    
107 bh 128 # message channels that have to be forwarded from maps contained in
108     # the session.
109     forwarded_channels = (
110 bh 318 # generic channels
111     CHANGED,
112    
113 bh 128 # map specific channels
114     MAP_PROJECTION_CHANGED,
115 jonathan 548 MAP_LAYERS_CHANGED,
116 bh 128
117     # layer channels forwarded by the map
118     LAYER_PROJECTION_CHANGED,
119 jonathan 582 LAYER_CHANGED,
120 jan 197 LAYER_VISIBILITY_CHANGED,
121 bh 128
122 jan 197 # channels forwarded by an extension
123     EXTENSION_CHANGED,
124     EXTENSION_OBJECTS_CHANGED)
125    
126 bh 6 def __init__(self, title):
127     TitledObject.__init__(self, title)
128     Modifiable.__init__(self)
129     self.filename = None
130     self.maps = []
131     self.tables = []
132 bh 851 self.shapestores = []
133 jan 197 self.extensions = []
134 bh 765 self.temp_dir = None
135     self.transient_db = None
136 bh 6
137 bh 232 def changed(self, channel = None, *args):
138     """Like the inherited version but issue a CHANGED message as well.
139    
140     The CHANGED message is only issued if channel given is a
141     different channel than CHANGED.
142     """
143     Modifiable.changed(self, channel, *args)
144     if channel != CHANGED:
145     self.issue(CHANGED, self)
146    
147 bh 6 def SetFilename(self, filename):
148     self.filename = filename
149     self.changed(FILENAME_CHANGED)
150    
151     def Maps(self):
152     return self.maps
153    
154     def HasMaps(self):
155     return len(self.maps) > 0
156    
157     def AddMap(self, map):
158     self.maps.append(map)
159 bh 128 for channel in self.forwarded_channels:
160 bh 6 map.Subscribe(channel, self.forward, channel)
161     self.changed(MAPS_CHANGED)
162    
163 bh 241 def RemoveMap(self, map):
164     for channel in self.forwarded_channels:
165     map.Unsubscribe(channel, self.forward, channel)
166     self.maps.remove(map)
167     self.changed(MAPS_CHANGED)
168     map.Destroy()
169    
170 jan 197 def Extensions(self):
171     return self.extensions
172    
173     def HasExtensions(self):
174     return len(self.extensions) > 0
175    
176     def AddExtension(self, extension):
177     self.extensions.append(extension)
178     for channel in self.forwarded_channels:
179     extension.Subscribe(channel, self.forward, channel)
180     self.changed(EXTENSIONS_CHANGED)
181    
182 bh 851 def ShapeStores(self):
183     """Return a list of all ShapeStore objects open in the session"""
184     return [store() for store in self.shapestores]
185    
186     def _add_shapestore(self, store):
187     """Internal: Add the shapestore to the list of shapestores"""
188     self.shapestores.append(weakref.ref(store,
189     self._clean_weak_store_refs))
190    
191     def _clean_weak_store_refs(self, weakref):
192     """Internal: Remove the weakref from the shapestores list"""
193     self.shapestores = [store for store in self.shapestores
194     if store is not weakref]
195    
196     def Tables(self):
197 bh 982 """Return a list of all table objects open in the session
198 bh 851
199 bh 982 The list includes all tables that are indirectly opened through
200     shape stores and the tables that have been opened explicitly.
201     """
202     return self.tables + [store.Table() for store in self.ShapeStores()]
203    
204     def AddTable(self, table):
205     """Add the table to the session
206    
207     All tables associated with the session that are not implicitly
208     created by the OpenShapefile method (and maybe other Open*
209     methods in the future) have to be passed to this method to make
210     sure the session knows about it. The session keeps a reference
211     to the table. Only tables managed by the session in this way
212     should be used for layers contained in one of the session's
213     maps.
214    
215     The table parameter may be any object implementing the table
216     interface. If it's not already one of the transient tables
217     instantiate an AutoTransientTable with it and use that instead
218     of the original table (note that the AutoTransientTable keeps a
219     reference to the original table).
220    
221     Return the table object actually used by the session.
222     """
223     if not hasattr(table, "transient_table"):
224     transient_table = AutoTransientTable(self.TransientDB(), table)
225     else:
226     transient_table = table
227     self.tables.append(transient_table)
228     return transient_table
229    
230 bh 987 def RemoveTable(self, table):
231     """Remove the table from the session.
232    
233     The table object must be a table object previously returned by
234     the AddTable method. If the table is not part of the session
235     raise a ValueError.
236     """
237     tables = [t for t in self.tables if t is not table]
238     if len(tables) == len(self.tables):
239     raise ValueError
240     self.tables = tables
241    
242 bh 765 def temp_directory(self):
243     """
244     Return the name of the directory for session specific temporary files
245    
246     Create the directory if it doesn't exist yet.
247     """
248     if self.temp_dir is None:
249     temp_dir = mktemp()
250     os.mkdir(temp_dir, 0700)
251     self.temp_dir = temp_dir
252     self.temp_dir_remover = AutoRemoveDir(self.temp_dir)
253     return self.temp_dir
254    
255 bh 723 def OpenShapefile(self, filename):
256     """Return a shapefile store object for the data in the given file"""
257 bh 851 store = ShapefileStore(self, filename)
258     self._add_shapestore(store)
259     return store
260 bh 723
261 bh 1016 def AddShapeStore(self, shapestore):
262     """Add the shapestore to the session.
263    
264     The session only holds a weak reference to the shapestore, so it
265     will automatically be removed from the session when the last
266     reference goes away.
267     """
268     self._add_shapestore(shapestore)
269     return shapestore
270    
271 bh 765 def TransientDB(self):
272     if self.transient_db is None:
273     filename = os.path.join(self.temp_directory(), "transientdb")
274     self.transient_db = TransientDatabase(filename)
275     #print self.temp_dir_remover
276     auto_remover[self.transient_db] = AutoRemoveFile(filename,
277     self.temp_dir_remover)
278     return self.transient_db
279    
280 bh 6 def Destroy(self):
281     for map in self.maps:
282     map.Destroy()
283     self.maps = []
284     self.tables = []
285 bh 250 Modifiable.Destroy(self)
286 bh 6
287 bh 778 # Close the transient DB explicitly so that it removes any
288     # journal files from the temporary directory
289     if self.transient_db is not None:
290     self.transient_db.close()
291    
292 bh 6 def forward(self, *args):
293 bh 232 """Reissue events.
294    
295     If the channel the event is forwarded to is a changed-channel
296     that is not the CHANGED channel issue CHANGED as well. An
297     channel is considered to be a changed-channel if it's name ends
298     with 'CHANGED'.
299     """
300 bh 6 if len(args) > 1:
301     args = (args[-1],) + args[:-1]
302     apply(self.issue, args)
303 bh 232 channel = args[0]
304     # It's a bit of a kludge to rely on the channel name for this.
305     if channel.endswith("CHANGED") and channel != CHANGED:
306     self.issue(CHANGED, self)
307 bh 6
308     def WasModified(self):
309     """Return true if the session or one of the maps was modified"""
310     if self.modified:
311     return 1
312     else:
313     for map in self.maps:
314     if map.WasModified():
315     return 1
316     return 0
317    
318     def UnsetModified(self):
319     """Unset the modified flag of the session and the maps"""
320     Modifiable.UnsetModified(self)
321     for map in self.maps:
322     map.UnsetModified()
323    
324 bh 217 def TreeInfo(self):
325     items = []
326     if self.filename is None:
327 jan 374 items.append(_("Filename:"))
328 bh 217 else:
329 jan 374 items.append(_("Filename: %s") % self.filename)
330 bh 6
331 bh 217 if self.WasModified():
332 jan 374 items.append(_("Modified"))
333 bh 217 else:
334 jan 374 items.append(_("Unmodified"))
335 bh 6
336 bh 217 items.extend(self.maps)
337     items.extend(self.extensions)
338    
339 jan 374 return (_("Session: %s") % self.title, items)
340 bh 217
341    
342 bh 6 def create_empty_session():
343     """Return an empty session useful as a starting point"""
344 jan 103 import os
345 jan 374 session = Session(_('unnamed session'))
346 jan 103 session.SetFilename(None)
347 jan 374 session.AddMap(Map(_('unnamed map')))
348 bh 56 session.UnsetModified()
349 bh 6 return session

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26