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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/thuban/Thuban/UI/application.py revision 235 by bh, Tue Jul 23 10:56:29 2002 UTC branches/WIP-pyshapelib-bramz/Thuban/UI/application.py revision 2734 by bramz, Thu Mar 1 12:42:59 2007 UTC
# Line 1  Line 1 
1  # Copyright (C) 2001, 2002 by Intevation GmbH  # Copyright (C) 2001-2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jan-Oliver Wagner <[email protected]>  # Jan-Oliver Wagner <[email protected]>
4  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
# Line 13  Thuban's application object. Line 13  Thuban's application object.
13  __version__ = "$Revision$"  __version__ = "$Revision$"
14    
15  import sys, os  import sys, os
16    import os.path
17    
18  import traceback  import traceback
19    
20  from wxPython.wx import *  import wx
21    
22  from Thuban.Lib.connector import Publisher  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  from Thuban.Model.session import create_empty_session
27  from Thuban.Model.save import save_session  from Thuban.Model.save import save_session
28  from Thuban.Model.load import load_session  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  import view
36  import tree  import tree
 from interactor import Interactor  
37  import mainwindow  import mainwindow
38    import dbdialog
39    import altpathdialog
40    import exceptiondialog
41    
42  from messages import SESSION_CHANGED  from messages import SESSION_REPLACED
   
43    
44    
45  class ThubanApplication(wxApp, Publisher):  class ThubanApplication(wx.App, Publisher):
46    
47      """      """
48      Thuban's application class.      Thuban's application class.
49    
50      All wxWindows programs have to have an instance of an application      All wxWindows programs have to have an instance of an application
51      class derived from wxApp. In Thuban the application class holds      class derived from wxApp. In Thuban the application class holds
52      references to the main window, the session and the interactor.      references to the main window and the session.
53      """      """
54    
55      def OnInit(self):      def OnInit(self):
56          self.read_startup_files()          sys.excepthook = self.ShowExceptionDialog
57          self.interactor = Interactor(None)  
58          top = self.CreateMainWindow()          # Initialize instance variables before trying to create any
59          top.Show(true)          # windows.  Creating windows can start an event loop if
60          self.top = top          # e.g. message boxes are popped up for some reason, and event
61          self.SetTopWindow(top)          # 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          self.session = None
68            self.top = None
69          self.create_session()          self.create_session()
70          return true  
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):      def read_startup_files(self):
97          """Read the startup files."""          """Read the startup files."""
98          # for now the startup file is ~/.thuban/thubanstart.py          # for now the startup file is ~/.thuban/thubanstart.py
99          dir =os.path.expanduser("~/.thuban")          dir = get_application_dir()
100          if os.path.isdir(dir):          if os.path.isdir(dir):
101              sys.path.append(dir)              sys.path.append(dir)
102              try:              try:
# Line 67  class ThubanApplication(wxApp, Publisher Line 107  class ThubanApplication(wxApp, Publisher
107                      if tb.tb_next is not None:                      if tb.tb_next is not None:
108                          # The ImportError exception was raised from                          # The ImportError exception was raised from
109                          # inside the thubanstart module.                          # inside the thubanstart module.
110                          sys.stderr.write("Cannot import the thubanstart"                          sys.stderr.write(_("Cannot import the thubanstart"
111                                           "module\n")                                           " module\n"))
112                          traceback.print_exc(None, sys.stderr)                          traceback.print_exc(None, sys.stderr)
113                      else:                      else:
114                          # There's no thubanstart module.                          # There's no thubanstart module.
115                          sys.stderr.write("No thubanstart module available\n")                          sys.stderr.write(_("No thubanstart module available\n"))
116                  finally:                  finally:
117                      # make sure we delete the traceback object,                      # make sure we delete the traceback object,
118                      # otherwise there's be circular references involving                      # otherwise there's be circular references involving
119                      # the current stack frame                      # the current stack frame
120                      del tb                      del tb
121              except:              except:
122                  sys.stderr.write("Cannot import the thubanstart module\n")                  sys.stderr.write(_("Cannot import the thubanstart module\n"))
123                  traceback.print_exc(None, sys.stderr)                  traceback.print_exc(None, sys.stderr)
124          else:          else:
125              # There's no .thuban directory              # There's no .thuban directory
126              sys.stderr.write("No ~/.thuban directory\n")              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):      def CreateMainWindow(self):
160          """Create and return the main window for the application.          """Create and return the main window for the application.
# Line 91  class ThubanApplication(wxApp, Publisher Line 162  class ThubanApplication(wxApp, Publisher
162          Override this in subclasses to instantiate the Thuban mainwindow          Override this in subclasses to instantiate the Thuban mainwindow
163          with different parameters or to use a different class for the          with different parameters or to use a different class for the
164          main window.          main window.
165            """
166          when this method is called by OnInit self.interactor (to be used          msg = (_("This is the wxPython-based Graphical User Interface"
167          for the interactor argument of the standard Thuban main window                 " for exploring geographic data"))
168          class) has already been instantiated.          return mainwindow.MainWindow(None, -1, "Thuban", self, None,
169          """                                       initial_message = msg,
170          msg = ("This is the wxPython-based Graphical User Interface"                                       size = (600, 400))
                " for exploring geographic data")  
         return mainwindow.MainWindow(NULL, -1, "Thuban", self, self.interactor,  
                                      initial_message = msg)  
171    
172      def Session(self):      def Session(self):
173          """Return the application's session object"""          """Return the application's session object"""
# Line 108  class ThubanApplication(wxApp, Publisher Line 176  class ThubanApplication(wxApp, Publisher
176      def SetSession(self, session):      def SetSession(self, session):
177          """Make session the new session.          """Make session the new session.
178    
179          Issue SESSION_CHANGED.          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          oldsession = self.session
185          self.session = session          self.session = session
186          self.issue(SESSION_CHANGED)          self.subscribe_session(self.session)
187          self.interactor.SetSession(session)          self.issue(SESSION_REPLACED)
188          self.set_map()          self.maps_changed()
189          if oldsession is not None:          if oldsession is not None:
190                self.unsubscribe_session(oldsession)
191              oldsession.Destroy()              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):      def create_session(self):
220          # Create a simple default session          """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())          self.SetSession(create_empty_session())
228    
229      def OpenSession(self, filename):      def OpenSession(self, filename, db_connection_callback = None,
230          session = load_session(filename)                                      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)          session.SetFilename(filename)
247          session.UnsetModified()          session.UnsetModified()
248          self.SetSession(session)          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 = wx.MessageDialog(None,
258                                                 msg,
259                                                 _("Library not available"),
260                                                 wx.OK | wx.ICON_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):      def SaveSession(self):
322          save_session(self.session, self.session.filename)          save_session(self.session, self.session.filename)
323    
324      def set_map(self):      def maps_changed(self, *args):
325          if self.session.HasMaps():          """Subscribed to the session's MAPS_CHANGED messages.
326              self.top.SetMap(self.session.Maps()[0])  
327          else:          Set the toplevel window's map to the map in the session. This is
328              self.top.SetMap(None)          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 wx.IsBusy():
359                wx.EndBusyCursor() # 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    

Legend:
Removed from v.235  
changed lines
  Added in v.2734

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26