/[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 765 - (hide annotations)
Tue Apr 29 12:42:14 2003 UTC (21 years, 10 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/session.py
File MIME type: text/x-python
File size: 8661 byte(s)
Next step of table implementation. Introduce a transient database
using SQLite that some of the data is copied to on demand. This
allows us to do joins and other operations that require an index
for good performance with reasonable efficiency. Thuban now needs
SQLite 2.8.0 and pysqlite 0.4.1. Older versions may work but I
haven't tested that.

* Thuban/Model/transientdb.py: New. Transient database
implementation.

* test/test_transientdb.py: New. Tests for the transient DB
classes.

* Thuban/Model/session.py (AutoRemoveFile, AutoRemoveDir): New
classes to help automatically remove temporary files and
directories.
(Session.__init__): New instance variables temp_dir for the
temporary directory and transient_db for the SQLite database
(Session.temp_directory): New. Create a temporary directory if not
yet done and return its name. Use AutoRemoveDir to have it
automatically deleted
(Session.TransientDB): Instantiate the transient database if not
done yet and return it.

* Thuban/Model/data.py (ShapefileStore.__init__): Use an
AutoTransientTable so that data is copied to the transient DB on
demand.
(SimpleStore): New class that simply combines a table and a
shapefile

* Thuban/Model/table.py (Table, DBFTable): Rename Table into
DBFTable and update its doc-string to reflect the fact that this
is only the table interface to a DBF file. Table is now an alias
for DBFTable for temporary backwards compatibility.

* Thuban/UI/application.py (ThubanApplication.OnExit): Make sure
the last reference to the session goes away so that the temporary
files are removed properly.

* test/test_load.py (LoadSessionTest.tearDown): Remove the
reference to the session to make sure the temporary files are
removed.

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 765 from transientdb import TransientDatabase
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 jan 197 self.extensions = []
133 bh 765 self.temp_dir = None
134     self.transient_db = None
135 bh 6
136 bh 232 def changed(self, channel = None, *args):
137     """Like the inherited version but issue a CHANGED message as well.
138    
139     The CHANGED message is only issued if channel given is a
140     different channel than CHANGED.
141     """
142     Modifiable.changed(self, channel, *args)
143     if channel != CHANGED:
144     self.issue(CHANGED, self)
145    
146 bh 6 def SetFilename(self, filename):
147     self.filename = filename
148     self.changed(FILENAME_CHANGED)
149    
150     def Maps(self):
151     return self.maps
152    
153     def HasMaps(self):
154     return len(self.maps) > 0
155    
156     def AddMap(self, map):
157     self.maps.append(map)
158 bh 128 for channel in self.forwarded_channels:
159 bh 6 map.Subscribe(channel, self.forward, channel)
160     self.changed(MAPS_CHANGED)
161    
162 bh 241 def RemoveMap(self, map):
163     for channel in self.forwarded_channels:
164     map.Unsubscribe(channel, self.forward, channel)
165     self.maps.remove(map)
166     self.changed(MAPS_CHANGED)
167     map.Destroy()
168    
169 jan 197 def Extensions(self):
170     return self.extensions
171    
172     def HasExtensions(self):
173     return len(self.extensions) > 0
174    
175     def AddExtension(self, extension):
176     self.extensions.append(extension)
177     for channel in self.forwarded_channels:
178     extension.Subscribe(channel, self.forward, channel)
179     self.changed(EXTENSIONS_CHANGED)
180    
181 bh 765 def temp_directory(self):
182     """
183     Return the name of the directory for session specific temporary files
184    
185     Create the directory if it doesn't exist yet.
186     """
187     if self.temp_dir is None:
188     temp_dir = mktemp()
189     os.mkdir(temp_dir, 0700)
190     self.temp_dir = temp_dir
191     self.temp_dir_remover = AutoRemoveDir(self.temp_dir)
192     return self.temp_dir
193    
194 bh 723 def OpenShapefile(self, filename):
195     """Return a shapefile store object for the data in the given file"""
196     return ShapefileStore(self, filename)
197    
198 bh 765 def TransientDB(self):
199     if self.transient_db is None:
200     filename = os.path.join(self.temp_directory(), "transientdb")
201     self.transient_db = TransientDatabase(filename)
202     #print self.temp_dir_remover
203     auto_remover[self.transient_db] = AutoRemoveFile(filename,
204     self.temp_dir_remover)
205     return self.transient_db
206    
207 bh 6 def Destroy(self):
208     for map in self.maps:
209     map.Destroy()
210     self.maps = []
211     self.tables = []
212 bh 250 Modifiable.Destroy(self)
213 bh 6
214     def forward(self, *args):
215 bh 232 """Reissue events.
216    
217     If the channel the event is forwarded to is a changed-channel
218     that is not the CHANGED channel issue CHANGED as well. An
219     channel is considered to be a changed-channel if it's name ends
220     with 'CHANGED'.
221     """
222 bh 6 if len(args) > 1:
223     args = (args[-1],) + args[:-1]
224     apply(self.issue, args)
225 bh 232 channel = args[0]
226     # It's a bit of a kludge to rely on the channel name for this.
227     if channel.endswith("CHANGED") and channel != CHANGED:
228     self.issue(CHANGED, self)
229 bh 6
230     def WasModified(self):
231     """Return true if the session or one of the maps was modified"""
232     if self.modified:
233     return 1
234     else:
235     for map in self.maps:
236     if map.WasModified():
237     return 1
238     return 0
239    
240     def UnsetModified(self):
241     """Unset the modified flag of the session and the maps"""
242     Modifiable.UnsetModified(self)
243     for map in self.maps:
244     map.UnsetModified()
245    
246 bh 217 def TreeInfo(self):
247     items = []
248     if self.filename is None:
249 jan 374 items.append(_("Filename:"))
250 bh 217 else:
251 jan 374 items.append(_("Filename: %s") % self.filename)
252 bh 6
253 bh 217 if self.WasModified():
254 jan 374 items.append(_("Modified"))
255 bh 217 else:
256 jan 374 items.append(_("Unmodified"))
257 bh 6
258 bh 217 items.extend(self.maps)
259     items.extend(self.extensions)
260    
261 jan 374 return (_("Session: %s") % self.title, items)
262 bh 217
263    
264 bh 6 def create_empty_session():
265     """Return an empty session useful as a starting point"""
266 jan 103 import os
267 jan 374 session = Session(_('unnamed session'))
268 jan 103 session.SetFilename(None)
269 jan 374 session.AddMap(Map(_('unnamed map')))
270 bh 56 session.UnsetModified()
271 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