/[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 2446 - (hide annotations)
Mon Dec 13 11:52:34 2004 UTC (20 years, 3 months ago) by frank
Original Path: trunk/thuban/Thuban/UI/application.py
File MIME type: text/x-python
File size: 14275 byte(s)
Alternative Path feature:
	* test/test_load.py (TestAltPath): New, tests for alternative path feature
	in load_session()
	(Shapefile_CallBack): Helper, implements controllable callback.

	* Thuban/UI/application.py (ThubanApplication.OnInit):
	Added "alt_path" to self.path
	(ThubanApplication.OpenSession): Added shapefile_callback as second
	callback similar to db_connection_callback.
	(ThubanApplication.run_alt_path_dialog): New, implementaion of
	shapefile_callback. In addition to raising the dialog the control of
	self.path('alt_path') is implemented here.

	* Thuban/Model/load.py (SessionLoader.__init__): Added shapefile_callback.
	(SessionLoader.open_shapefile): Open shapefile, eventually with
	alternative path. This wrapps the "theSession.OpenShapefile(filename)"
	formerly used in start_fileshapesource()/start_layer().
	(SessionLoader.start_fileshapesource): Call open_shapefile().
	(SessionLoader.start_layer): Call open_shapefile().
	(load_session): Added shapefile_callback.

	* Thuban/UI/altpathdialog.py: New, implements dialogs for alternative path
	feature (search / check).

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