1
# Copyright (C) 2001, 2002, 2003 by Intevation GmbH
3
# Jan-Oliver Wagner <jan@intevation.de>
4
# Bernhard Herzog <bh@intevation.de>
6
# This program is free software under the GPL (>=v2)
7
# Read the file COPYING coming with Thuban for details.
10
Thuban's application object.
13
__version__ = "$Revision: 1.34 $"
20
from wxPython.wx import *
22
from Thuban.Lib.connector import Publisher
23
from Thuban.Lib.fileutil import get_application_dir
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
37
import exceptiondialog
39
from messages import SESSION_REPLACED
42
class ThubanApplication(wxApp, Publisher):
45
Thuban's application class.
47
All wxWindows programs have to have an instance of an application
48
class derived from wxApp. In Thuban the application class holds
49
references to the main window and the session.
53
sys.excepthook = self.ShowExceptionDialog
54
self.splash = self.splash_screen()
55
if self.splash is not None:
57
self.read_startup_files()
58
self.top = self.CreateMainWindow()
59
self.SetTopWindow(self.top)
60
if self.splash is None:
69
Extend this in derived classes if needed.
71
self.session.Destroy()
73
Publisher.Destroy(self)
75
def read_startup_files(self):
76
"""Read the startup files."""
77
# for now the startup file is ~/.thuban/thubanstart.py
78
dir = get_application_dir()
79
if os.path.isdir(dir):
84
tb = sys.exc_info()[2]
86
if tb.tb_next is not None:
87
# The ImportError exception was raised from
88
# inside the thubanstart module.
89
sys.stderr.write(_("Cannot import the thubanstart"
91
traceback.print_exc(None, sys.stderr)
93
# There's no thubanstart module.
94
sys.stderr.write(_("No thubanstart module available\n"))
96
# make sure we delete the traceback object,
97
# otherwise there's be circular references involving
98
# the current stack frame
101
sys.stderr.write(_("Cannot import the thubanstart module\n"))
102
traceback.print_exc(None, sys.stderr)
104
# There's no .thuban directory
105
sys.stderr.write(_("No ~/.thuban directory\n"))
107
def splash_screen(self):
108
"""Create and return a splash screen.
110
This method is called by OnInit to determine whether the
111
application should have a splashscreen. If the application
112
should display a splash screen override this method in a derived
113
class and have it create and return the wxSplashScreen instance.
114
The implementation of this method in the derived class should
115
also arranged for ShowMainWindow to be called.
117
The default implementation simply returns None so that no splash
118
screen is shown and ShowMainWindow will be called automatically.
122
def ShowMainWindow(self):
123
"""Show the main window
125
Normally this method is automatically called by OnInit to show
126
the main window. However, if the splash_screen method has
127
returned a splashscreen it is expected that the derived class
128
also arranges for ShowMainWindow to be called at the appropriate
133
def CreateMainWindow(self):
134
"""Create and return the main window for the application.
136
Override this in subclasses to instantiate the Thuban mainwindow
137
with different parameters or to use a different class for the
140
msg = (_("This is the wxPython-based Graphical User Interface"
141
" for exploring geographic data"))
142
return mainwindow.MainWindow(NULL, -1, "Thuban", self, None,
143
initial_message = msg,
147
"""Return the application's session object"""
150
def SetSession(self, session):
151
"""Make session the new session.
153
Issue SESSION_REPLACED after self.session has become the new
154
session. After the session has been assigned call
155
self.subscribe_session() with the new session and
156
self.unsubscribe_session with the old one.
158
oldsession = self.session
159
self.session = session
160
self.subscribe_session(self.session)
161
self.issue(SESSION_REPLACED)
163
if oldsession is not None:
164
self.unsubscribe_session(oldsession)
167
def subscribe_session(self, session):
168
"""Subscribe to some of the sessions channels.
170
Extend this method in derived classes if you need additional
173
session.Subscribe(MAPS_CHANGED, self.maps_changed)
175
def unsubscribe_session(self, session):
176
"""Unsubscribe from the sessions channels.
178
Extend this method in derived classes if you subscribed to
179
additional channels in subscribe_session().
181
session.Unsubscribe(MAPS_CHANGED, self.maps_changed)
183
def create_session(self):
184
"""Create a default session.
186
Override this method in derived classes to instantiate the
187
session differently or to use a different session class. Don't
188
subscribe to channels here yet. Do that in the
189
subscribe_session() method.
191
self.SetSession(create_empty_session())
193
def OpenSession(self, filename, db_connection_callback = None):
194
"""Open the session in the file named filename"""
195
# Make sure we deal with an absolute pathname. Otherwise we can
196
# get problems when saving because the saving code expects an
197
# absolute directory name
198
filename = os.path.abspath(filename)
199
if db_connection_callback is None:
200
db_connection_callback = self.run_db_param_dialog
202
session = load_session(filename,
203
db_connection_callback=db_connection_callback)
204
except LoadCancelled:
206
session.SetFilename(filename)
207
session.UnsetModified()
208
self.SetSession(session)
210
for map in session.Maps():
211
for layer in map.Layers():
212
if isinstance(layer, RasterLayer) \
213
and not Thuban.Model.resource.has_gdal_support():
214
msg = _("The current session contains Image layers,\n"
215
"but the GDAL library is not available to "
217
dlg = wx.wxMessageDialog(None,
219
_("Library not available"),
220
wx.wxOK | wx.wxICON_INFORMATION)
226
def run_db_param_dialog(self, parameters, message):
227
"""Implementation of the db_connection_callback for loading sessions"""
228
dlg = dbdialog.DBDialog(None, _("DB Connection Parameters"),
230
return dlg.RunDialog()
233
def SaveSession(self):
234
save_session(self.session, self.session.filename)
236
def maps_changed(self, *args):
237
"""Subscribed to the session's MAPS_CHANGED messages.
239
Set the toplevel window's map to the map in the session. This is
240
done by calling the window's SetMap method with the map as
241
argument. If the session doesn't have any maps None is used
244
Currently Thuban can only really handle at most one map in a
245
sessions so the first map in the session's list of maps as
246
returned by the Maps method is used.
248
if self.session.HasMaps():
249
self.top.SetMap(self.session.Maps()[0])
251
self.top.SetMap(None)
253
in_exception_dialog = 0 # flag: are we already inside the exception dialog?
255
def ShowExceptionDialog(self, exc_type, exc_value, exc_traceback):
256
"""Show a message box with information about an exception.
258
The parameters are the usual values describing an exception in
259
Python, the exception type, the value and the traceback.
261
This method can be used as a value for the sys.excepthook.
264
if self.in_exception_dialog:
266
self.in_exception_dialog = 1
268
wxEndBusyCursor() # reset the mouse cursor
271
lines = traceback.format_exception(exc_type, exc_value,
273
message = _("An unhandled exception occurred:\n%s\n"
275
" http://thuban.intevation.org/bugtracker.html)"
276
"\n\n%s") % (exc_value, "".join(lines))
279
# We don't use an explicit parent here because this method might
280
# be called in circumstances where the main window doesn't exist
282
exceptiondialog.run_exception_dialog(None, message)
285
self.in_exception_dialog = 0
286
# delete the last exception info that python keeps in
287
# sys.last_* because especially last_traceback keeps
288
# indirect references to all objects bound to local
289
# variables and this might prevent some object from being
290
# collected early enough.
291
sys.last_type = sys.last_value = sys.last_traceback = None