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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2700 - (hide annotations)
Mon Sep 18 14:27:02 2006 UTC (18 years, 5 months ago) by dpinte
Original Path: trunk/thuban/Thuban/UI/application.py
File MIME type: text/x-python
File size: 14481 byte(s)
2006-09-18 Didrik Pinte <dpinte@itae.be>
    
        * wxPython 2.6 update : wx 2.4 syntax has been updated to 2.6


1 jan 2581 # Copyright (C) 2001-2005 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4 bh 189 # Bernhard Herzog <[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     """
10     Thuban's application object.
11     """
12    
13     __version__ = "$Revision$"
14    
15 bh 189 import sys, os
16 frank 1133 import os.path
17    
18 bh 189 import traceback
19    
20 dpinte 2700 import wx
21 bh 6
22     from Thuban.Lib.connector import Publisher
23 frank 1150 from Thuban.Lib.fileutil import get_application_dir
24 bh 6
25 jan 374 from Thuban import _
26 bh 6 from Thuban.Model.session import create_empty_session
27     from Thuban.Model.save import save_session
28 bh 1650 from Thuban.Model.load import load_session, LoadCancelled
29 bh 242 from Thuban.Model.messages import MAPS_CHANGED
30 jonathan 1162 from Thuban.Model.layer import RasterLayer
31     import Thuban.Model.resource
32 bh 6
33 jan 2581 from extensionregistry import ext_registry
34    
35 bh 6 import view
36     import tree
37 bh 215 import mainwindow
38 bh 1654 import dbdialog
39 frank 2446 import altpathdialog
40 jan 1704 import exceptiondialog
41 bh 6
42 jonathan 503 from messages import SESSION_REPLACED
43 bh 6
44    
45 dpinte 2700 class ThubanApplication(wx.App, Publisher):
46 bh 6
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 bh 535 references to the main window and the session.
53 bh 6 """
54    
55     def OnInit(self):
56 jonathan 1518 sys.excepthook = self.ShowExceptionDialog
57 bh 2072
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 frank 2446 self.path={"data":".", "projection":".", "alt_path":""}
66 bh 2072
67     self.session = None
68     self.top = None
69     self.create_session()
70    
71     # Create an optional splash screen and then the mainwindow
72 bh 401 self.splash = self.splash_screen()
73     if self.splash is not None:
74     self.splash.Show()
75 bh 189 self.read_startup_files()
76 jan 2581 self.init_extensions()
77 bh 401 self.top = self.CreateMainWindow()
78 bh 2076 # 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 bh 401 self.SetTopWindow(self.top)
82     if self.splash is None:
83     self.ShowMainWindow()
84 bh 2072
85 jonathan 518 return True
86 bh 6
87 bh 251 def OnExit(self):
88     """Clean up code.
89    
90     Extend this in derived classes if needed.
91     """
92     self.session.Destroy()
93 bh 765 self.session = None
94 bh 251 Publisher.Destroy(self)
95    
96 bh 189 def read_startup_files(self):
97     """Read the startup files."""
98     # for now the startup file is ~/.thuban/thubanstart.py
99 frank 1150 dir = get_application_dir()
100 bh 189 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 jan 374 sys.stderr.write(_("Cannot import the thubanstart"
111 bh 671 " module\n"))
112 bh 189 traceback.print_exc(None, sys.stderr)
113     else:
114     # There's no thubanstart module.
115 jan 374 sys.stderr.write(_("No thubanstart module available\n"))
116 bh 189 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 jan 374 sys.stderr.write(_("Cannot import the thubanstart module\n"))
123 bh 189 traceback.print_exc(None, sys.stderr)
124     else:
125     # There's no .thuban directory
126 jan 374 sys.stderr.write(_("No ~/.thuban directory\n"))
127 bh 189
128 jan 2581 def init_extensions(self):
129     """Call initialization callbacks for all registered extensions."""
130     for ext in ext_registry:
131     ext.init_ext()
132    
133 bh 401 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 jonathan 518 self.top.Show(True)
158 bh 535
159 bh 235 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 jan 374 msg = (_("This is the wxPython-based Graphical User Interface"
167     " for exploring geographic data"))
168 dpinte 2700 return mainwindow.MainWindow(None, -1, "Thuban", self, None,
169 jonathan 934 initial_message = msg,
170     size = (600, 400))
171 bh 235
172 bh 219 def Session(self):
173     """Return the application's session object"""
174     return self.session
175    
176 bh 6 def SetSession(self, session):
177 bh 219 """Make session the new session.
178    
179 jonathan 503 Issue SESSION_REPLACED after self.session has become the new
180 bh 242 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 bh 219 """
184 bh 6 oldsession = self.session
185     self.session = session
186 bh 242 self.subscribe_session(self.session)
187 jonathan 503 self.issue(SESSION_REPLACED)
188 bh 242 self.maps_changed()
189 bh 6 if oldsession is not None:
190 bh 242 self.unsubscribe_session(oldsession)
191 bh 6 oldsession.Destroy()
192    
193 frank 2051 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 bh 242 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 bh 6 def create_session(self):
220 bh 242 """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 bh 6 self.SetSession(create_empty_session())
228    
229 frank 2446 def OpenSession(self, filename, db_connection_callback = None,
230     shapefile_callback = None):
231 bh 592 """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 bh 1654 if db_connection_callback is None:
237     db_connection_callback = self.run_db_param_dialog
238 frank 2446 if shapefile_callback is None:
239     shapefile_callback = self.run_alt_path_dialog
240 bh 1650 try:
241     session = load_session(filename,
242 frank 2446 db_connection_callback=db_connection_callback,
243     shapefile_callback=shapefile_callback)
244 bh 1650 except LoadCancelled:
245     return
246 bh 6 session.SetFilename(filename)
247     session.UnsetModified()
248     self.SetSession(session)
249    
250 jonathan 1162 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 bh 1566 msg = _("The current session contains Image layers,\n"
255     "but the GDAL library is not available to "
256 jonathan 1162 "draw them.")
257 dpinte 2700 dlg = wx.MessageDialog(None,
258 bh 1566 msg,
259 jonathan 1162 _("Library not available"),
260 dpinte 2700 wx.OK | wx.ICON_INFORMATION)
261 jonathan 1162 print msg
262     dlg.ShowModal()
263     dlg.Destroy()
264     break
265    
266 bh 1654 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 frank 2446 # 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 bh 1654
292 frank 2446 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 dpinte 2700 self.SetPath('alt_path', fname)
304 frank 2446 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 bh 6 def SaveSession(self):
322     save_session(self.session, self.session.filename)
323    
324 bh 242 def maps_changed(self, *args):
325 bh 1776 """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 bh 2072 # 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 jonathan 1390
344 jonathan 1518 in_exception_dialog = 0 # flag: are we already inside the exception dialog?
345 jonathan 1390
346 jonathan 1518 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 jonathan 1390
355 jonathan 1518 if self.in_exception_dialog:
356     return
357     self.in_exception_dialog = 1
358 dpinte 2700 while wx.IsBusy():
359     wx.EndBusyCursor() # reset the mouse cursor
360 jonathan 1390
361 jonathan 1518 try:
362     lines = traceback.format_exception(exc_type, exc_value,
363     exc_traceback)
364 bh 1874 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 jonathan 1518 print message
369 jonathan 1390
370 jonathan 1518 # 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 jan 1704 exceptiondialog.run_exception_dialog(None, message)
374 jonathan 1390
375 jonathan 1518 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 jonathan 1390

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26