/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/application.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/UI/application.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2697 - (show annotations)
Mon Sep 18 00:45:37 2006 UTC (18 years, 5 months ago) by bernhard
Original Path: trunk/thuban/Thuban/UI/application.py
File MIME type: text/x-python
File size: 14495 byte(s)
* Thuban/UI/application.py: Fixed warning dialog when gdal is missing.
* Fixed Changelog a bit.

1 # Copyright (C) 2001-2005 by Intevation GmbH
2 # Authors:
3 # Jan-Oliver Wagner <[email protected]>
4 # Bernhard Herzog <[email protected]>
5 #
6 # This program is free software under the GPL (>=v2)
7 # Read the file COPYING coming with Thuban for details.
8
9 """
10 Thuban's application object.
11 """
12
13 __version__ = "$Revision$"
14
15 import sys, os
16 import os.path
17
18 import traceback
19
20 from wxPython.wx import *
21
22 from Thuban.Lib.connector import Publisher
23 from Thuban.Lib.fileutil import get_application_dir
24
25 from Thuban import _
26 from Thuban.Model.session import create_empty_session
27 from Thuban.Model.save import save_session
28 from Thuban.Model.load import load_session, LoadCancelled
29 from Thuban.Model.messages import MAPS_CHANGED
30 from Thuban.Model.layer import RasterLayer
31 import Thuban.Model.resource
32
33 from extensionregistry import ext_registry
34
35 import view
36 import tree
37 import mainwindow
38 import dbdialog
39 import altpathdialog
40 import exceptiondialog
41
42 from messages import SESSION_REPLACED
43
44
45 class ThubanApplication(wxApp, Publisher):
46
47 """
48 Thuban's application class.
49
50 All wxWindows programs have to have an instance of an application
51 class derived from wxApp. In Thuban the application class holds
52 references to the main window and the session.
53 """
54
55 def OnInit(self):
56 sys.excepthook = self.ShowExceptionDialog
57
58 # Initialize instance variables before trying to create any
59 # windows. Creating windows can start an event loop if
60 # e.g. message boxes are popped up for some reason, and event
61 # handlers, especially EVT_UPDATE_UI may want to access things
62 # from the application.
63
64 # Defaults for the directories used in file dialogs
65 self.path={"data":".", "projection":".", "alt_path":""}
66
67 self.session = None
68 self.top = None
69 self.create_session()
70
71 # Create an optional splash screen and then the mainwindow
72 self.splash = self.splash_screen()
73 if self.splash is not None:
74 self.splash.Show()
75 self.read_startup_files()
76 self.init_extensions()
77 self.top = self.CreateMainWindow()
78 # The session was alredy created above and we need to get the
79 # map into the mainwindow. maps_changed does that.
80 self.maps_changed()
81 self.SetTopWindow(self.top)
82 if self.splash is None:
83 self.ShowMainWindow()
84
85 return True
86
87 def OnExit(self):
88 """Clean up code.
89
90 Extend this in derived classes if needed.
91 """
92 self.session.Destroy()
93 self.session = None
94 Publisher.Destroy(self)
95
96 def read_startup_files(self):
97 """Read the startup files."""
98 # for now the startup file is ~/.thuban/thubanstart.py
99 dir = get_application_dir()
100 if os.path.isdir(dir):
101 sys.path.append(dir)
102 try:
103 import thubanstart
104 except ImportError:
105 tb = sys.exc_info()[2]
106 try:
107 if tb.tb_next is not None:
108 # The ImportError exception was raised from
109 # inside the thubanstart module.
110 sys.stderr.write(_("Cannot import the thubanstart"
111 " module\n"))
112 traceback.print_exc(None, sys.stderr)
113 else:
114 # There's no thubanstart module.
115 sys.stderr.write(_("No thubanstart module available\n"))
116 finally:
117 # make sure we delete the traceback object,
118 # otherwise there's be circular references involving
119 # the current stack frame
120 del tb
121 except:
122 sys.stderr.write(_("Cannot import the thubanstart module\n"))
123 traceback.print_exc(None, sys.stderr)
124 else:
125 # There's no .thuban directory
126 sys.stderr.write(_("No ~/.thuban directory\n"))
127
128 def init_extensions(self):
129 """Call initialization callbacks for all registered extensions."""
130 for ext in ext_registry:
131 ext.init_ext()
132
133 def splash_screen(self):
134 """Create and return a splash screen.
135
136 This method is called by OnInit to determine whether the
137 application should have a splashscreen. If the application
138 should display a splash screen override this method in a derived
139 class and have it create and return the wxSplashScreen instance.
140 The implementation of this method in the derived class should
141 also arranged for ShowMainWindow to be called.
142
143 The default implementation simply returns None so that no splash
144 screen is shown and ShowMainWindow will be called automatically.
145 """
146 return None
147
148 def ShowMainWindow(self):
149 """Show the main window
150
151 Normally this method is automatically called by OnInit to show
152 the main window. However, if the splash_screen method has
153 returned a splashscreen it is expected that the derived class
154 also arranges for ShowMainWindow to be called at the appropriate
155 time.
156 """
157 self.top.Show(True)
158
159 def CreateMainWindow(self):
160 """Create and return the main window for the application.
161
162 Override this in subclasses to instantiate the Thuban mainwindow
163 with different parameters or to use a different class for the
164 main window.
165 """
166 msg = (_("This is the wxPython-based Graphical User Interface"
167 " for exploring geographic data"))
168 return mainwindow.MainWindow(NULL, -1, "Thuban", self, None,
169 initial_message = msg,
170 size = (600, 400))
171
172 def Session(self):
173 """Return the application's session object"""
174 return self.session
175
176 def SetSession(self, session):
177 """Make session the new session.
178
179 Issue SESSION_REPLACED after self.session has become the new
180 session. After the session has been assigned call
181 self.subscribe_session() with the new session and
182 self.unsubscribe_session with the old one.
183 """
184 oldsession = self.session
185 self.session = session
186 self.subscribe_session(self.session)
187 self.issue(SESSION_REPLACED)
188 self.maps_changed()
189 if oldsession is not None:
190 self.unsubscribe_session(oldsession)
191 oldsession.Destroy()
192
193 def SetPath(self, group, filename):
194 """Store the application's default path for file dialogs extracted
195 from a given filename.
196 """
197 self.path[group] = os.path.dirname( filename )
198
199 def Path(self, group):
200 """Return the application's default path for file dialogs."""
201 return self.path[group]
202
203 def subscribe_session(self, session):
204 """Subscribe to some of the sessions channels.
205
206 Extend this method in derived classes if you need additional
207 channels.
208 """
209 session.Subscribe(MAPS_CHANGED, self.maps_changed)
210
211 def unsubscribe_session(self, session):
212 """Unsubscribe from the sessions channels.
213
214 Extend this method in derived classes if you subscribed to
215 additional channels in subscribe_session().
216 """
217 session.Unsubscribe(MAPS_CHANGED, self.maps_changed)
218
219 def create_session(self):
220 """Create a default session.
221
222 Override this method in derived classes to instantiate the
223 session differently or to use a different session class. Don't
224 subscribe to channels here yet. Do that in the
225 subscribe_session() method.
226 """
227 self.SetSession(create_empty_session())
228
229 def OpenSession(self, filename, db_connection_callback = None,
230 shapefile_callback = None):
231 """Open the session in the file named filename"""
232 # Make sure we deal with an absolute pathname. Otherwise we can
233 # get problems when saving because the saving code expects an
234 # absolute directory name
235 filename = os.path.abspath(filename)
236 if db_connection_callback is None:
237 db_connection_callback = self.run_db_param_dialog
238 if shapefile_callback is None:
239 shapefile_callback = self.run_alt_path_dialog
240 try:
241 session = load_session(filename,
242 db_connection_callback=db_connection_callback,
243 shapefile_callback=shapefile_callback)
244 except LoadCancelled:
245 return
246 session.SetFilename(filename)
247 session.UnsetModified()
248 self.SetSession(session)
249
250 for map in session.Maps():
251 for layer in map.Layers():
252 if isinstance(layer, RasterLayer) \
253 and not Thuban.Model.resource.has_gdal_support():
254 msg = _("The current session contains Image layers,\n"
255 "but the GDAL library is not available to "
256 "draw them.")
257 dlg = wxMessageDialog(None,
258 msg,
259 _("Library not available"),
260 wxOK | wxICON_INFORMATION)
261 print msg
262 dlg.ShowModal()
263 dlg.Destroy()
264 break
265
266 def run_db_param_dialog(self, parameters, message):
267 """Implementation of the db_connection_callback for loading sessions"""
268 dlg = dbdialog.DBDialog(None, _("DB Connection Parameters"),
269 parameters, message)
270 return dlg.RunDialog()
271
272 # run_alt_path_dialog: Raise a dialog to ask for an alternative path
273 # if the shapefile couldn't be found.
274 # TODO:
275 # - Store a list of already used alternative paths and return these
276 # iteratively (using a generator)
277 # - How do we interact with the user to tell him we used a different
278 # shapefile (location), mode "check"? The current approach with the
279 # file dialog is not that comfortable.
280 #
281 def run_alt_path_dialog(self, filename, mode = None, second_try = 0):
282 """Implemetation of the shapefile_callback while loading sessions.
283
284 This implements two modes:
285 - search: Search for an alternative path. If available from a
286 list of alrady known paths, else interactivly by file dialog.
287 Currently the "second_try" is important since else the user might
288 be caught in a loop.
289 - check: Ask the user for confirmation, if a path from list has
290 been found successful.
291
292 Returns:
293 - fname: The full path to the (shape) file.
294 - from_list: Flags if the path was taken from list or entered
295 manually.
296 """
297
298 if mode == "search":
299 if self.Path('alt_path') == "" or second_try:
300 dlg = altpathdialog.AltPathFileDialog(filename)
301 fname = dlg.RunDialog()
302 if fname is not None:
303 self.SetPath('alt_path', fname)
304 from_list = 0
305 else:
306 fname = os.path.join(self.Path('alt_path'),
307 os.path.basename(filename))
308 from_list = 1
309 elif mode == "check":
310 dlg = altpathdialog.AltPathConfirmDialog(filename)
311 fname = dlg.RunDialog()
312 if fname is not None:
313 self.SetPath('alt_path', fname)
314 from_list = 0
315 else:
316 fname = None
317 from_list = 0
318 return fname, from_list
319
320
321 def SaveSession(self):
322 save_session(self.session, self.session.filename)
323
324 def maps_changed(self, *args):
325 """Subscribed to the session's MAPS_CHANGED messages.
326
327 Set the toplevel window's map to the map in the session. This is
328 done by calling the window's SetMap method with the map as
329 argument. If the session doesn't have any maps None is used
330 instead.
331
332 Currently Thuban can only really handle at most one map in a
333 sessions so the first map in the session's list of maps as
334 returned by the Maps method is used.
335 """
336 # The mainwindow may not have been created yet, so check whether
337 # it has been created before calling any of its methods
338 if self.top is not None:
339 if self.session.HasMaps():
340 self.top.SetMap(self.session.Maps()[0])
341 else:
342 self.top.SetMap(None)
343
344 in_exception_dialog = 0 # flag: are we already inside the exception dialog?
345
346 def ShowExceptionDialog(self, exc_type, exc_value, exc_traceback):
347 """Show a message box with information about an exception.
348
349 The parameters are the usual values describing an exception in
350 Python, the exception type, the value and the traceback.
351
352 This method can be used as a value for the sys.excepthook.
353 """
354
355 if self.in_exception_dialog:
356 return
357 self.in_exception_dialog = 1
358 while wxIsBusy():
359 wxEndBusyCursor() # reset the mouse cursor
360
361 try:
362 lines = traceback.format_exception(exc_type, exc_value,
363 exc_traceback)
364 message = _("An unhandled exception occurred:\n%s\n"
365 "(please report to"
366 " http://thuban.intevation.org/bugtracker.html)"
367 "\n\n%s") % (exc_value, "".join(lines))
368 print message
369
370 # We don't use an explicit parent here because this method might
371 # be called in circumstances where the main window doesn't exist
372 # anymore.
373 exceptiondialog.run_exception_dialog(None, message)
374
375 finally:
376 self.in_exception_dialog = 0
377 # delete the last exception info that python keeps in
378 # sys.last_* because especially last_traceback keeps
379 # indirect references to all objects bound to local
380 # variables and this might prevent some object from being
381 # collected early enough.
382 sys.last_type = sys.last_value = sys.last_traceback = None
383

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26