~ubuntu-branches/ubuntu/lucid/thuban/lucid

« back to all changes in this revision

Viewing changes to Thuban/UI/application.py

  • Committer: Bazaar Package Importer
  • Author(s): Silke Reimer
  • Date: 2004-01-28 12:47:34 UTC
  • Revision ID: james.westby@ubuntu.com-20040128124734-6xotwcqilok6ngut
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2001, 2002, 2003 by Intevation GmbH
 
2
# Authors:
 
3
# Jan-Oliver Wagner <jan@intevation.de>
 
4
# Bernhard Herzog <bh@intevation.de>
 
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: 1.34 $"
 
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
import view
 
34
import tree
 
35
import mainwindow
 
36
import dbdialog
 
37
import exceptiondialog
 
38
 
 
39
from messages import SESSION_REPLACED
 
40
 
 
41
 
 
42
class ThubanApplication(wxApp, Publisher):
 
43
 
 
44
    """
 
45
    Thuban's application class.
 
46
 
 
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.
 
50
    """
 
51
 
 
52
    def OnInit(self):
 
53
        sys.excepthook = self.ShowExceptionDialog
 
54
        self.splash = self.splash_screen()
 
55
        if self.splash is not None:
 
56
            self.splash.Show()
 
57
        self.read_startup_files()
 
58
        self.top = self.CreateMainWindow()
 
59
        self.SetTopWindow(self.top)
 
60
        if self.splash is None:
 
61
            self.ShowMainWindow()
 
62
        self.session = None
 
63
        self.create_session()
 
64
        return True
 
65
 
 
66
    def OnExit(self):
 
67
        """Clean up code.
 
68
 
 
69
        Extend this in derived classes if needed.
 
70
        """
 
71
        self.session.Destroy()
 
72
        self.session = None
 
73
        Publisher.Destroy(self)
 
74
 
 
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):
 
80
            sys.path.append(dir)
 
81
            try:
 
82
                import thubanstart
 
83
            except ImportError:
 
84
                tb = sys.exc_info()[2]
 
85
                try:
 
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"
 
90
                                         " module\n"))
 
91
                        traceback.print_exc(None, sys.stderr)
 
92
                    else:
 
93
                        # There's no thubanstart module.
 
94
                        sys.stderr.write(_("No thubanstart module available\n"))
 
95
                finally:
 
96
                    # make sure we delete the traceback object,
 
97
                    # otherwise there's be circular references involving
 
98
                    # the current stack frame
 
99
                    del tb
 
100
            except:
 
101
                sys.stderr.write(_("Cannot import the thubanstart module\n"))
 
102
                traceback.print_exc(None, sys.stderr)
 
103
        else:
 
104
            # There's no .thuban directory
 
105
            sys.stderr.write(_("No ~/.thuban directory\n"))
 
106
 
 
107
    def splash_screen(self):
 
108
        """Create and return a splash screen.
 
109
 
 
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.
 
116
 
 
117
        The default implementation simply returns None so that no splash
 
118
        screen is shown and ShowMainWindow will be called automatically.
 
119
        """
 
120
        return None
 
121
 
 
122
    def ShowMainWindow(self):
 
123
        """Show the main window
 
124
 
 
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
 
129
        time.
 
130
        """
 
131
        self.top.Show(True)
 
132
 
 
133
    def CreateMainWindow(self):
 
134
        """Create and return the main window for the application.
 
135
 
 
136
        Override this in subclasses to instantiate the Thuban mainwindow
 
137
        with different parameters or to use a different class for the
 
138
        main window.
 
139
        """
 
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,
 
144
                                     size = (600, 400))
 
145
 
 
146
    def Session(self):
 
147
        """Return the application's session object"""
 
148
        return self.session
 
149
 
 
150
    def SetSession(self, session):
 
151
        """Make session the new session.
 
152
 
 
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.
 
157
        """
 
158
        oldsession = self.session
 
159
        self.session = session
 
160
        self.subscribe_session(self.session)
 
161
        self.issue(SESSION_REPLACED)
 
162
        self.maps_changed()
 
163
        if oldsession is not None:
 
164
            self.unsubscribe_session(oldsession)
 
165
            oldsession.Destroy()
 
166
 
 
167
    def subscribe_session(self, session):
 
168
        """Subscribe to some of the sessions channels.
 
169
 
 
170
        Extend this method in derived classes if you need additional
 
171
        channels.
 
172
        """
 
173
        session.Subscribe(MAPS_CHANGED, self.maps_changed)
 
174
 
 
175
    def unsubscribe_session(self, session):
 
176
        """Unsubscribe from the sessions channels.
 
177
 
 
178
        Extend this method in derived classes if you subscribed to
 
179
        additional channels in subscribe_session().
 
180
        """
 
181
        session.Unsubscribe(MAPS_CHANGED, self.maps_changed)
 
182
 
 
183
    def create_session(self):
 
184
        """Create a default session.
 
185
 
 
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.
 
190
        """
 
191
        self.SetSession(create_empty_session())
 
192
 
 
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
 
201
        try:
 
202
            session = load_session(filename,
 
203
                               db_connection_callback=db_connection_callback)
 
204
        except LoadCancelled:
 
205
            return
 
206
        session.SetFilename(filename)
 
207
        session.UnsetModified()
 
208
        self.SetSession(session)
 
209
 
 
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 "
 
216
                            "draw them.")
 
217
                    dlg = wx.wxMessageDialog(None,
 
218
                                             msg,
 
219
                                             _("Library not available"),
 
220
                                             wx.wxOK | wx.wxICON_INFORMATION)
 
221
                    print msg
 
222
                    dlg.ShowModal()
 
223
                    dlg.Destroy()
 
224
                    break
 
225
 
 
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"),
 
229
                                parameters, message)
 
230
        return dlg.RunDialog()
 
231
 
 
232
 
 
233
    def SaveSession(self):
 
234
        save_session(self.session, self.session.filename)
 
235
 
 
236
    def maps_changed(self, *args):
 
237
        """Subscribed to the session's MAPS_CHANGED messages.
 
238
 
 
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
 
242
        instead.
 
243
 
 
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.
 
247
        """
 
248
        if self.session.HasMaps():
 
249
            self.top.SetMap(self.session.Maps()[0])
 
250
        else:
 
251
            self.top.SetMap(None)
 
252
 
 
253
    in_exception_dialog = 0 # flag: are we already inside the exception dialog?
 
254
 
 
255
    def ShowExceptionDialog(self, exc_type, exc_value, exc_traceback):
 
256
        """Show a message box with information about an exception.
 
257
    
 
258
        The parameters are the usual values describing an exception in
 
259
        Python, the exception type, the value and the traceback.
 
260
    
 
261
        This method can be used as a value for the sys.excepthook.
 
262
        """
 
263
 
 
264
        if self.in_exception_dialog:
 
265
            return
 
266
        self.in_exception_dialog = 1
 
267
        while wxIsBusy():
 
268
            wxEndBusyCursor() # reset the mouse cursor
 
269
 
 
270
        try:
 
271
            lines = traceback.format_exception(exc_type, exc_value,
 
272
                                            exc_traceback)
 
273
            message = _("An unhandled exception occurred:\n%s\n"
 
274
                        "(please report to"
 
275
                        " http://thuban.intevation.org/bugtracker.html)"
 
276
                        "\n\n%s") % (exc_value, "".join(lines))
 
277
            print message
 
278
 
 
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
 
281
            # anymore.
 
282
            exceptiondialog.run_exception_dialog(None, message)
 
283
 
 
284
        finally:
 
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
 
292