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

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

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

revision 84 by bh, Wed Apr 3 15:21:46 2002 UTC revision 963 by jonathan, Wed May 21 17:23:53 2003 UTC
# Line 1  Line 1 
1  # Copyright (C) 2001 by Intevation GmbH  # Copyright (C) 2001, 2002, 2003 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]>
5    # Frank Koormann <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 12  The main window Line 13  The main window
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16  import sys, os  __ThubanVersion__ = "0.2" #"$THUBAN_0_2$"
17    #__BuildDate__ = "$Date$"
18    
19    import os
20    
21  from wxPython.wx import *  from wxPython.wx import *
22    from wxPython.wx import __version__ as wxPython_version
23    
24  import Thuban  import Thuban
25  from Thuban.Model.session import Session, create_empty_session  import Thuban.version
26  from Thuban.Model.map import Map  
27  from Thuban.Model.layer import Layer  from Thuban import _
28    from Thuban.Model.session import create_empty_session
29    from Thuban.Model.layer import Layer, RasterLayer
30  from Thuban.Model.color import Color  from Thuban.Model.color import Color
31  from Thuban.Model.proj import Projection  from Thuban.Model.proj import Projection
32    
# Line 27  import view Line 34  import view
34  import tree  import tree
35  import proj4dialog  import proj4dialog
36  import tableview, identifyview  import tableview, identifyview
37    from Thuban.UI.classifier import Classifier
38    import legend
39    from menu import Menu
40    
41  import main  from context import Context
42  from command import registry, Command  from command import registry, Command, ToolCommand
43  from messages import SELECTED_SHAPE  from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION
44    
45    from Thuban.UI.dock import DockFrame
46    from Thuban.UI.join import JoinDialog
47    
48  # the directory where the toolbar icons are stored  import resource
 bitmapdir = os.path.join(Thuban.__path__[0], os.pardir, "Resources", "Bitmaps")  
 bitmapext = ".xpm"  
49    
50    import projdialog
51    
 class MainWindow(wxFrame):  
52    
     def __init__(self, parent, ID, interactor):  
         wxFrame.__init__(self, parent, ID, 'Thuban',  
                          wxDefaultPosition, wxSize(400, 300))  
53    
54          self.interactor = interactor  class MainWindow(DockFrame):
55    
56          self.CreateStatusBar()      # Some messages that can be subscribed/unsubscribed directly through
57          self.SetStatusText("This is the wxPython-based "      # the MapCanvas come in fact from other objects. This is a map to
58                        "Graphical User Interface for exploring geographic data")      # map those messages to the names of the instance variables they
59        # actually come from. This delegation is implemented in the
60        # Subscribe and unsubscribed methods
61        delegated_messages = {LAYER_SELECTED: "canvas",
62                              SHAPES_SELECTED: "canvas"}
63    
64          self.identify_view = None      # Methods delegated to some instance variables. The delegation is
65        # implemented in the __getattr__ method.
66        delegated_methods = {"SelectLayer": "canvas",
67                             "SelectShapes": "canvas",
68                             "SelectedShapes": "canvas",
69                             }
70    
71          self.init_ids()      def __init__(self, parent, ID, title, application, interactor,
72                     initial_message = None, size = wxSize(-1, -1)):
73            DockFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
74            #wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
75    
76          menuBar = wxMenuBar()          self.application = application
77    
78          menu = wxMenu()          self.CreateStatusBar()
79          menuBar.Append(menu, "&File");          if initial_message:
80          for name in ["new_session", "open_session", None,              self.SetStatusText(initial_message)
                      "save_session", "save_session_as", None,  
                      "exit"]:  
             self.add_menu_command(menu, name)  
   
         menu = wxMenu()  
         menuBar.Append(menu, "&Map");  
         for name in ["layer_add", "layer_remove",  
                      None,  
                      "map_projection",  
                      None,  
                      "map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",  
                      "map_identify_tool", "map_label_tool",  
                      None,  
                      "map_full_extent",  
                      None,  
                      "map_print"]:  
             self.add_menu_command(menu, name)  
   
         menu = wxMenu()  
         menuBar.Append(menu, "&Layer");  
         for name in ["layer_fill_color", "layer_transparent_fill",  
                      "layer_ourline_color", "layer_no_outline",  
                      None,  
                      "layer_raise", "layer_lower",  
                      None,  
                      "layer_show", "layer_hide",  
                      None,  
                      "layer_show_table"]:  
             self.add_menu_command(menu, name)  
   
         menu = wxMenu()  
         menuBar.Append(menu, "&Help");  
         self.add_menu_command(menu, "help_about")  
81    
82          self.SetMenuBar(menuBar)          self.identify_view = None
83    
84          # toolbar          self.init_ids()
         toolbar = self.CreateToolBar(wxTB_3DBUTTONS)  
85    
86          # set the size of the tools' bitmaps. Not needed on wxGTK, but          # creat the menubar from the main_menu description
87          # on Windows. We probably shouldn't hardwire the bitmap size          self.SetMenuBar(self.build_menu_bar(main_menu))
         # here  
         toolbar.SetToolBitmapSize(wxSize(24, 24))  
88    
89          for name in ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",          # Similarly, create the toolbar from main_toolbar
90                       "map_identify_tool", "map_label_tool"]:          toolbar = self.build_toolbar(main_toolbar)
             self.add_toolbar_command(toolbar, name)  
91          # call Realize to make sure that the tools appear.          # call Realize to make sure that the tools appear.
92          toolbar.Realize()          toolbar.Realize()
93    
94    
95          # Create the map canvas          # Create the map canvas
96          canvas = view.MapCanvas(self, -1, interactor)          canvas = view.MapCanvas(self, -1)
97            canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
98            canvas.Subscribe(SHAPES_SELECTED, self.identify_view_on_demand)
99          self.canvas = canvas          self.canvas = canvas
100    
101          self.init_dialogs()          self.SetMainWindow(self.canvas)
102    
103          interactor.Subscribe(SELECTED_SHAPE, self.identify_view_on_demand)          self.SetAutoLayout(True)
104    
105            self.init_dialogs()
106    
107          EVT_CLOSE(self, self.OnClose)          EVT_CLOSE(self, self.OnClose)
108    
109        def Subscribe(self, channel, *args):
110            """Subscribe a function to a message channel.
111    
112            If channel is one of the delegated messages call the appropriate
113            object's Subscribe method. Otherwise do nothing.
114            """
115            if channel in self.delegated_messages:
116                object = getattr(self, self.delegated_messages[channel])
117                object.Subscribe(channel, *args)
118            else:
119                print "Trying to subscribe to unsupported channel %s" % channel
120    
121        def Unsubscribe(self, channel, *args):
122            """Unsubscribe a function from a message channel.
123    
124            If channel is one of the delegated messages call the appropriate
125            object's Unsubscribe method. Otherwise do nothing.
126            """
127            if channel in self.delegated_messages:
128                object = getattr(self, self.delegated_messages[channel])
129                object.Unsubscribe(channel, *args)
130    
131        def __getattr__(self, attr):
132            """If attr is one of the delegated methods return that method
133    
134            Otherwise raise AttributeError.
135            """
136            if attr in self.delegated_methods:
137                return getattr(getattr(self, self.delegated_methods[attr]), attr)
138            raise AttributeError(attr)
139    
140      def init_ids(self):      def init_ids(self):
141          """Initialize the ids"""          """Initialize the ids"""
142          self.current_id = 6000          self.current_id = 6000
143          self.id_to_name = {}          self.id_to_name = {}
144          self.name_to_id = {}          self.name_to_id = {}
145            self.events_bound = {}
146    
147      def get_id(self, name):      def get_id(self, name):
148          """Return the wxWindows id for the command named name.          """Return the wxWindows id for the command named name.
# Line 136  class MainWindow(wxFrame): Line 155  class MainWindow(wxFrame):
155              self.name_to_id[name] = ID              self.name_to_id[name] = ID
156              self.id_to_name[ID] = name              self.id_to_name[ID] = name
157          return ID          return ID
158            
159        def bind_command_events(self, command, ID):
160            """Bind the necessary events for the given command and ID"""
161            if not self.events_bound.has_key(ID):
162                # the events haven't been bound yet
163                EVT_MENU(self, ID, self.invoke_command)
164                if command.IsDynamic():
165                    EVT_UPDATE_UI(self, ID, self.update_command_ui)
166    
167        def build_menu_bar(self, menudesc):
168            """Build and return the menu bar from the menu description"""
169            menu_bar = wxMenuBar()
170    
171            for item in menudesc.items:
172                # here the items must all be Menu instances themselves
173                menu_bar.Append(self.build_menu(item), item.title)
174    
175            return menu_bar
176    
177        def build_menu(self, menudesc):
178            """Return a wxMenu built from the menu description menudesc"""
179            wxmenu = wxMenu()
180            last = None
181            for item in menudesc.items:
182                if item is None:
183                    # a separator. Only add one if the last item was not a
184                    # separator
185                    if last is not None:
186                        wxmenu.AppendSeparator()
187                elif isinstance(item, Menu):
188                    # a submenu
189                    wxmenu.AppendMenu(wxNewId(), item.title, self.build_menu(item))
190                else:
191                    # must the name the name of a command
192                    self.add_menu_command(wxmenu, item)
193                last = item
194            return wxmenu
195    
196        def build_toolbar(self, toolbardesc):
197            """Build and return the main toolbar window from a toolbar description
198    
199            The parameter should be an instance of the Menu class but it
200            should not contain submenus.
201            """
202            toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
203    
204            # set the size of the tools' bitmaps. Not needed on wxGTK, but
205            # on Windows, although it doesn't work very well there. It seems
206            # that only 16x16 icons are really supported on windows.
207            # We probably shouldn't hardwire the bitmap size here.
208            toolbar.SetToolBitmapSize(wxSize(24, 24))
209    
210            for item in toolbardesc.items:
211                if item is None:
212                    toolbar.AddSeparator()
213                else:
214                    # assume it's a string.
215                    self.add_toolbar_command(toolbar, item)
216    
217            return toolbar
218    
219      def add_menu_command(self, menu, name):      def add_menu_command(self, menu, name):
220          """Add the command with name name to the menu menu.          """Add the command with name name to the menu menu.
221    
# Line 150  class MainWindow(wxFrame): Line 229  class MainWindow(wxFrame):
229                  ID = self.get_id(name)                  ID = self.get_id(name)
230                  menu.Append(ID, command.Title(), command.HelpText(),                  menu.Append(ID, command.Title(), command.HelpText(),
231                              command.IsCheckCommand())                              command.IsCheckCommand())
232                  EVT_MENU(self, ID, self.invoke_command)                  self.bind_command_events(command, ID)
                 if command.IsDynamic():  
                     EVT_UPDATE_UI(self, ID, self.update_command_ui)  
233              else:              else:
234                  print "Unknown command %s" % name                  print _("Unknown command %s") % name
235    
236      def add_toolbar_command(self, toolbar, name):      def add_toolbar_command(self, toolbar, name):
237          """Add the command with name name to the toolbar toolbar.          """Add the command with name name to the toolbar toolbar.
# Line 169  class MainWindow(wxFrame): Line 246  class MainWindow(wxFrame):
246              command = registry.Command(name)              command = registry.Command(name)
247              if command is not None:              if command is not None:
248                  ID = self.get_id(name)                  ID = self.get_id(name)
249                  filename = os.path.join(bitmapdir, command.Icon()) + bitmapext                  bitmap = resource.GetBitmapResource(command.Icon(),
250                  bitmap = wxBitmap(filename, wxBITMAP_TYPE_XPM)                                                      wxBITMAP_TYPE_XPM)
251                  toolbar.AddTool(ID, bitmap,                  toolbar.AddTool(ID, bitmap,
252                                  shortHelpString = command.HelpText(),                                  shortHelpString = command.HelpText(),
253                                  isToggle = command.IsCheckCommand())                                  isToggle = command.IsCheckCommand())
254                    self.bind_command_events(command, ID)
255              else:              else:
256                  print "Unknown command %s" % name                  print _("Unknown command %s") % name
257    
258        def Context(self):
259            """Return the context object for a command invoked from this window
260            """
261            return Context(self.application, self.application.Session(), self)
262    
263      def invoke_command(self, event):      def invoke_command(self, event):
264          name = self.id_to_name.get(event.GetId())          name = self.id_to_name.get(event.GetId())
265          if name is not None:          if name is not None:
266              command = registry.Command(name)              command = registry.Command(name)
267              command.Execute(self)              command.Execute(self.Context())
268          else:          else:
269              print "Unknown command ID %d" % event.GetId()              print _("Unknown command ID %d") % event.GetId()
270    
271      def update_command_ui(self, event):      def update_command_ui(self, event):
272          #print "update_command_ui", self.id_to_name[event.GetId()]          #print "update_command_ui", self.id_to_name[event.GetId()]
273            context = self.Context()
274          command = registry.Command(self.id_to_name[event.GetId()])          command = registry.Command(self.id_to_name[event.GetId()])
275          if command is not None:          if command is not None:
276              event.Enable(command.Sensitive(self))              sensitive = command.Sensitive(context)
277              event.SetText(command.DynText(self))              event.Enable(sensitive)
278                if command.IsTool() and not sensitive and command.Checked(context):
279                    # When a checked tool command is disabled deselect all
280                    # tools. Otherwise the tool would remain active but it
281                    # might lead to errors if the tools stays active. This
282                    # problem occurred in GREAT-ER and this fixes it, but
283                    # it's not clear to me whether this is really the best
284                    # way to do it (BH, 20021206).
285                    self.canvas.SelectTool(None)
286                event.SetText(command.DynText(context))
287              if command.IsCheckCommand():              if command.IsCheckCommand():
288                  event.Check(command.Checked(self))                      event.Check(command.Checked(context))
289    
290      def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):      def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
291          """Run a modla message box with the given text, title and flags          """Run a modal message box with the given text, title and flags
292          and return the result"""          and return the result"""
293          dlg = wxMessageDialog(self, text, title, flags)          dlg = wxMessageDialog(self, text, title, flags)
294            dlg.CenterOnParent()
295          result = dlg.ShowModal()          result = dlg.ShowModal()
296          dlg.Destroy()          dlg.Destroy()
297          return result          return result
# Line 211  class MainWindow(wxFrame): Line 305  class MainWindow(wxFrame):
305    
306      def add_dialog(self, name, dialog):      def add_dialog(self, name, dialog):
307          if self.dialogs.has_key(name):          if self.dialogs.has_key(name):
308              raise RuntimeError("The Dialog named %s is already open" % name)              raise RuntimeError(_("The Dialog named %s is already open") % name)
309          self.dialogs[name] = dialog          self.dialogs[name] = dialog
310    
311      def dialog_open(self, name):      def dialog_open(self, name):
# Line 223  class MainWindow(wxFrame): Line 317  class MainWindow(wxFrame):
317      def get_open_dialog(self, name):      def get_open_dialog(self, name):
318          return self.dialogs.get(name)          return self.dialogs.get(name)
319    
320        def view_position_changed(self):
321            pos = self.canvas.CurrentPosition()
322            if pos is not None:
323                text = "(%10.10g, %10.10g)" % pos
324            else:
325                text = ""
326            self.set_position_text(text)
327    
328        def set_position_text(self, text):
329            """Set the statusbar text showing the current position.
330    
331            By default the text is shown in field 0 of the status bar.
332            Override this method in derived classes to put it into a
333            different field of the statusbar.
334            """
335            self.SetStatusText(text)
336    
337      def save_modified_session(self, can_veto = 1):      def save_modified_session(self, can_veto = 1):
338          """If the current session has been modified, ask the user          """If the current session has been modified, ask the user
339          whether to save it and do so if requested. Return the outcome of          whether to save it and do so if requested. Return the outcome of
# Line 232  class MainWindow(wxFrame): Line 343  class MainWindow(wxFrame):
343          If the can_veto parameter is true (default) the dialog includes          If the can_veto parameter is true (default) the dialog includes
344          a cancel button, otherwise not.          a cancel button, otherwise not.
345          """          """
346          if main.app.session.WasModified():          if self.application.session.WasModified():
347              flags = wxYES_NO | wxICON_QUESTION              flags = wxYES_NO | wxICON_QUESTION
348              if can_veto:              if can_veto:
349                  flags = flags | wxCANCEL                  flags = flags | wxCANCEL
350              result = self.RunMessageBox("Exit",              result = self.RunMessageBox(_("Exit"),
351                                          ("The session has been modified."                                          _("The session has been modified."
352                                           " Do you want to save it?"),                                           " Do you want to save it?"),
353                                          flags)                                          flags)
354              if result == wxID_YES:              if result == wxID_YES:
# Line 246  class MainWindow(wxFrame): Line 357  class MainWindow(wxFrame):
357              result = wxID_NO              result = wxID_NO
358          return result          return result
359    
360        def prepare_new_session(self):
361            for d in self.dialogs.values():
362                if not isinstance(d, tree.SessionTreeView):
363                    d.Close()
364    
365      def NewSession(self):      def NewSession(self):
366          self.save_modified_session()          if self.save_modified_session() != wxID_CANCEL:
367          main.app.SetSession(create_empty_session())              self.prepare_new_session()
368                self.application.SetSession(create_empty_session())
369    
370      def OpenSession(self):      def OpenSession(self):
371          self.save_modified_session()          if self.save_modified_session() != wxID_CANCEL:
372          dlg = wxFileDialog(self, "Select a session file", ".", "",              dlg = wxFileDialog(self, _("Open Session"), ".", "",
373                             "*.session", wxOPEN)                                 "Thuban Session File (*.thuban)|*.thuban",
374          if dlg.ShowModal() == wxID_OK:                                 wxOPEN)
375              main.app.OpenSession(dlg.GetPath())              if dlg.ShowModal() == wxID_OK:
376          dlg.Destroy()                  self.prepare_new_session()
377                    self.application.OpenSession(dlg.GetPath())
378                dlg.Destroy()
379    
380      def SaveSession(self):      def SaveSession(self):
381          main.app.SaveSession()          if self.application.session.filename == None:
382                self.SaveSessionAs()
383            else:
384                self.application.SaveSession()
385    
386      def SaveSessionAs(self):      def SaveSessionAs(self):
387          dlg = wxFileDialog(self, "Enter a filename for session", ".", "",          dlg = wxFileDialog(self, _("Save Session As"), ".", "",
388                             "*.session", wxOPEN)                             "Thuban Session File (*.thuban)|*.thuban",
389                               wxSAVE|wxOVERWRITE_PROMPT)
390          if dlg.ShowModal() == wxID_OK:          if dlg.ShowModal() == wxID_OK:
391              main.app.session.SetFilename(dlg.GetPath())              self.application.session.SetFilename(dlg.GetPath())
392              main.app.SaveSession()              self.application.SaveSession()
393          dlg.Destroy()          dlg.Destroy()
394    
395      def Exit(self):      def Exit(self):
396          self.Close(false)          self.Close(False)
397    
398      def OnClose(self, event):      def OnClose(self, event):
399          result = self.save_modified_session(can_veto = event.CanVeto())          result = self.save_modified_session(can_veto = event.CanVeto())
400          if result == wxID_CANCEL:          if result == wxID_CANCEL:
401              event.Veto()              event.Veto()
402          else:          else:
403                # FIXME: it would be better to tie the unsubscription to
404                # wx's destroy event, but that isn't implemented for wxGTK
405                # yet.
406                self.canvas.Unsubscribe(VIEW_POSITION, self.view_position_changed)
407                DockFrame._OnClose(self, event)
408              self.Destroy()              self.Destroy()
409    
410      def SetMap(self, map):      def SetMap(self, map):
411          self.canvas.SetMap(map)          self.canvas.SetMap(map)
412            self.__SetTitle(map.Title())
413    
414            dialog = self.FindRegisteredDock("legend")
415            if dialog is not None:
416                dialog.GetPanel().SetMap(self.Map())
417    
418        def Map(self):
419            """Return the map displayed by this mainwindow"""
420    
421      def ShowSessionTree(self):          return self.canvas.Map()
422    
423        def ToggleSessionTree(self):
424            """If the session tree is shown close it otherwise create a new tree"""
425          name = "session_tree"          name = "session_tree"
426          dialog = self.get_open_dialog(name)          dialog = self.get_open_dialog(name)
427          if dialog is None:          if dialog is None:
428              dialog = tree.SessionTreeView(self, main.app, name)              dialog = tree.SessionTreeView(self, self.application, name)
429              self.add_dialog(name, dialog)              self.add_dialog(name, dialog)
430              dialog.Show(true)              dialog.Show(True)
431          else:          else:
432              # FIXME: bring dialog to front here              dialog.Close()
433              pass  
434        def SessionTreeShown(self):
435            """Return true iff the session tree is currently shown"""
436            return self.get_open_dialog("session_tree") is not None
437    
438      def About(self):      def About(self):
439          self.RunMessageBox("About",          self.RunMessageBox(_("About"),
440                             ("Thuban is a program for\n"                             _("Thuban %s\n"
441                                #"Build Date: %s\n"
442                                "using:\n"
443                                "  %s\n"
444                                "  %s\n\n"
445                                "Thuban is a program for\n"
446                              "exploring geographic data.\n"                              "exploring geographic data.\n"
447                              "Copyright (C) 2001 Intevation GmbH.\n"                              "Copyright (C) 2001-2003 Intevation GmbH.\n"
448                              "Thuban is licensed under the GPL"),                              "Thuban is licensed under the GNU GPL"
449                                % (Thuban.version.longversion,
450                                   "wxPython %s" % wxPython_version,
451                                   "Python %d.%d.%d" % sys.version_info[:3]
452                                  )),
453    #                           % __ThubanVersion__), #__BuildDate__)),
454                             wxOK | wxICON_INFORMATION)                             wxOK | wxICON_INFORMATION)
455    
456      def AddLayer(self):      def AddLayer(self):
457          dlg = wxFileDialog(self, "Select a session file", ".", "", "*.*",          dlg = wxFileDialog(self, _("Select a data file"), ".", "", "*.*",
458                             wxOPEN)                             wxOPEN)
459          if dlg.ShowModal() == wxID_OK:          if dlg.ShowModal() == wxID_OK:
460              filename = dlg.GetPath()              filename = dlg.GetPath()
461              title = os.path.splitext(os.path.basename(filename))[0]              title = os.path.splitext(os.path.basename(filename))[0]
             layer = Layer(title, filename)  
462              map = self.canvas.Map()              map = self.canvas.Map()
463              has_layers = map.HasLayers()              has_layers = map.HasLayers()
464              try:              try:
465                    store = self.application.Session().OpenShapefile(filename)
466                except IOError:
467                    # the layer couldn't be opened
468                    self.RunMessageBox(_("Add Layer"),
469                                       _("Can't open the file '%s'.") % filename)
470                else:
471                    layer = Layer(title, store)
472                  map.AddLayer(layer)                  map.AddLayer(layer)
473                    if not has_layers:
474                        # if we're adding a layer to an empty map, fit the
475                        # new map to the window
476                        self.canvas.FitMapToWindow()
477            dlg.Destroy()
478    
479        def AddRasterLayer(self):
480            dlg = wxFileDialog(self, _("Select an image file"), ".", "", "*.*",
481                               wxOPEN)
482            if dlg.ShowModal() == wxID_OK:
483                filename = dlg.GetPath()
484                title = os.path.splitext(os.path.basename(filename))[0]
485                map = self.canvas.Map()
486                has_layers = map.HasLayers()
487                try:
488                    layer = RasterLayer(title, filename)
489              except IOError:              except IOError:
490                  # the layer couldn't be opened                  # the layer couldn't be opened
491                  self.RunMessageBox("Add Layer",                  self.RunMessageBox(_("Add Image Layer"),
492                                     "Can't open the file '%s'." % filename)                                     _("Can't open the file '%s'.") % filename)
493              else:              else:
494                    map.AddLayer(layer)
495                  if not has_layers:                  if not has_layers:
496                      # if we're adding a layer to an empty map, for the                      # if we're adding a layer to an empty map, fit the
497                      # new map to the window                      # new map to the window
498                      self.canvas.FitMapToWindow()                      self.canvas.FitMapToWindow()
499          dlg.Destroy()          dlg.Destroy()
# Line 328  class MainWindow(wxFrame): Line 503  class MainWindow(wxFrame):
503          if layer is not None:          if layer is not None:
504              self.canvas.Map().RemoveLayer(layer)              self.canvas.Map().RemoveLayer(layer)
505    
506        def CanRemoveLayer(self):
507            """Return true if the currently selected layer can be deleted.
508    
509            If no layer is selected return False.
510    
511            The return value of this method determines whether the remove
512            layer command is sensitive in menu.
513            """
514            layer = self.current_layer()
515            if layer is not None:
516                return self.canvas.Map().CanRemoveLayer(layer)
517            return False
518    
519      def RaiseLayer(self):      def RaiseLayer(self):
520          layer = self.current_layer()          layer = self.current_layer()
521          if layer is not None:          if layer is not None:
522              self.canvas.Map().RaiseLayer(layer)              self.canvas.Map().RaiseLayer(layer)
523            
524      def LowerLayer(self):      def LowerLayer(self):
525          layer = self.current_layer()          layer = self.current_layer()
526          if layer is not None:          if layer is not None:
# Line 343  class MainWindow(wxFrame): Line 531  class MainWindow(wxFrame):
531    
532          If no layer is selected, return None          If no layer is selected, return None
533          """          """
534          return self.interactor.SelectedLayer()          return self.canvas.SelectedLayer()
535    
536      def has_selected_layer(self):      def has_selected_layer(self):
537          """Return true if a layer is currently selected"""          """Return true if a layer is currently selected"""
538          return self.interactor.HasSelectedLayer()          return self.canvas.HasSelectedLayer()
539    
540      def choose_color(self):      def has_selected_shapes(self):
541          """Run the color selection dialog and return the selected color.          """Return true if a shape is currently selected"""
542            return self.canvas.HasSelectedShapes()
         If the user cancels, return None.  
         """  
         dlg = wxColourDialog(self)  
         color = None  
         if dlg.ShowModal() == wxID_OK:  
             data = dlg.GetColourData()  
             wxc = data.GetColour()  
             color = Color(wxc.Red() / 255.0,  
                           wxc.Green() / 255.0,  
                           wxc.Blue() / 255.0)  
         dlg.Destroy()  
         return color  
   
     def LayerFillColor(self):  
         layer = self.current_layer()  
         if layer is not None:  
             color = self.choose_color()  
             if color is not None:  
                 layer.SetFill(color)  
   
     def LayerTransparentFill(self):  
         layer = self.current_layer()  
         if layer is not None:  
             layer.SetFill(None)  
   
     def LayerOutlineColor(self):  
         layer = self.current_layer()  
         if layer is not None:  
             color = self.choose_color()  
             if color is not None:  
                 layer.SetStroke(color)  
   
     def LayerNoOutline(self):  
         layer = self.current_layer()  
         if layer is not None:  
             layer.SetStroke(None)  
543    
544      def HideLayer(self):      def HideLayer(self):
545          layer = self.current_layer()          layer = self.current_layer()
# Line 406  class MainWindow(wxFrame): Line 558  class MainWindow(wxFrame):
558              name = "table_view" + str(id(table))              name = "table_view" + str(id(table))
559              dialog = self.get_open_dialog(name)              dialog = self.get_open_dialog(name)
560              if dialog is None:              if dialog is None:
561                  dialog = tableview.TableFrame(self, self.interactor, name,                  dialog = tableview.LayerTableFrame(self, name,
562                                                "Table: %s" % layer.Title(),                                                 _("Table: %s") % layer.Title(),
563                                                layer, table)                                                     layer, table)
564                  self.add_dialog(name, dialog)                  self.add_dialog(name, dialog)
565                  dialog.Show(true)                  dialog.Show(true)
566              else:              else:
567                  # FIXME: bring dialog to front here                  # FIXME: bring dialog to front here
568                  pass                  pass
569    
570      def Projection(self):      def MapProjection(self):
571          map = self.canvas.Map()  
572          proj = map.projection          name = "map_projection"
573          if proj is None:          dialog = self.get_open_dialog(name)
574              proj4Dlg = proj4dialog.Proj4Dialog(NULL, None)  
575            if dialog is None:
576                map = self.canvas.Map()
577                dialog = projdialog.ProjFrame(self, name,
578                         _("Map Projection: %s") % map.Title(), map)
579                self.add_dialog(name, dialog)
580                dialog.Show()
581            dialog.Raise()
582    
583        def LayerProjection(self):
584    
585            layer = self.current_layer()
586    
587            name = "layer_projection" + str(id(layer))
588            dialog = self.get_open_dialog(name)
589    
590            if dialog is None:
591                map = self.canvas.Map()
592                dialog = projdialog.ProjFrame(self, name,
593                         _("Layer Projection: %s") % layer.Title(), layer)
594                self.add_dialog(name, dialog)
595                dialog.Show()
596            dialog.Raise()
597    
598        def LayerEditProperties(self):
599    
600            #
601            # the menu option for this should only be available if there
602            # is a current layer, so we don't need to check if the
603            # current layer is None
604            #
605    
606            layer = self.current_layer()
607            self.OpenLayerProperties(layer)
608    
609        def OpenLayerProperties(self, layer, group = None):
610            name = "layer_properties" + str(id(layer))
611            dialog = self.get_open_dialog(name)
612    
613            if dialog is None:
614                dialog = Classifier(self, name, layer, group)
615                self.add_dialog(name, dialog)
616                dialog.Show()
617            dialog.Raise()
618    
619        def LayerJoinTable(self):
620            print "LayerJoinTable"
621    
622        def LayerUnjoinTable(self):
623            print "LayerUnjoinTable"
624    
625        def ShowLegend(self):
626            if not self.LegendShown():
627                self.ToggleLegend()
628    
629        def ToggleLegend(self):
630            """Show the legend if it's not shown otherwise hide it again"""
631            name = "legend"
632            dialog = self.FindRegisteredDock(name)
633    
634            if dialog is None:
635                dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
636                legend.LegendPanel(dialog, None, self)
637                dialog.Dock()
638                dialog.GetPanel().SetMap(self.Map())
639                dialog.Show()
640          else:          else:
641              proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params)              dialog.Show(not dialog.IsShown())
642          if proj4Dlg.ShowModal() == wxID_OK:  
643              params = proj4Dlg.GetParams()      def LegendShown(self):
644              if params is not None:          """Return true iff the legend is currently open"""
645                  proj = Projection(params)          dialog = self.FindRegisteredDock("legend")
646              else:          return dialog is not None and dialog.IsShown()
647                  proj = None  
648              map.SetProjection(proj)      def TableOpen(self):
649          proj4Dlg.Destroy()          print "TableOpen"
650            dlg = wxFileDialog(self, _("Open Table"), ".", "",
651                               "DBF Files (*.dbf)|*.dbf|" +
652                               "CSV Files (*.csv)|*.csv|" +
653                               "All Files (*.*)|*.*",
654                               wxOPEN)
655            if dlg.ShowModal() == wxID_OK:
656                #self.application.session.OpenTable(dlg.GetPath())
657                pass
658    
659            dlg.Destroy()
660    
661        def TableClose(self):
662            print "TableClose"
663    
664        def TableShow(self):
665            print "TableShow"
666    
667        def TableHide(self):
668            print "TableHide"
669    
670        def TableJoin(self):
671            print "TableJoin"
672            dlg = JoinDialog(self, _("Join Tables"), self.application.session)
673            if dlg.ShowModal() == wxID_OK:
674                print "OK"
675    
676      def ZoomInTool(self):      def ZoomInTool(self):
677          self.canvas.ZoomInTool()          self.canvas.ZoomInTool()
# Line 450  class MainWindow(wxFrame): Line 692  class MainWindow(wxFrame):
692      def FullExtent(self):      def FullExtent(self):
693          self.canvas.FitMapToWindow()          self.canvas.FitMapToWindow()
694    
695        def FullLayerExtent(self):
696            self.canvas.FitLayerToWindow(self.current_layer())
697    
698        def FullSelectionExtent(self):
699            self.canvas.FitSelectedToWindow()
700    
701        def ExportMap(self):
702            self.canvas.Export()
703    
704      def PrintMap(self):      def PrintMap(self):
705          self.canvas.Print()          self.canvas.Print()
706    
707      def identify_view_on_demand(self, layer, shape):      def RenameMap(self):
708            dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
709                                    self.Map().Title())
710            if dlg.ShowModal() == wxID_OK:
711                title = dlg.GetValue()
712                if title != "":
713                    self.Map().SetTitle(title)
714                    self.__SetTitle(title)
715    
716            dlg.Destroy()
717    
718        def identify_view_on_demand(self, layer, shapes):
719            """Subscribed to the canvas' SHAPES_SELECTED message
720    
721            If the current tool is the identify tool, at least one shape is
722            selected and the identify dialog is not shown, show the dialog.
723            """
724            # If the selection has become empty we don't need to do
725            # anything. Otherwise it could happen that the dialog was popped
726            # up when the selection became empty, e.g. when a new selection
727            # is opened while the identify tool is active and dialog had
728            # been closed
729            if not shapes:
730                return
731    
732          name = "identify_view"          name = "identify_view"
733          if self.canvas.CurrentTool() == "IdentifyTool":          if self.canvas.CurrentTool() == "IdentifyTool":
734              if not self.dialog_open(name):              if not self.dialog_open(name):
735                  dialog = identifyview.IdentifyView(self, self.interactor, name)                  dialog = identifyview.IdentifyView(self, name)
736                  self.add_dialog(name, dialog)                  self.add_dialog(name, dialog)
737                  dialog.Show(true)                  dialog.Show(True)
738              else:              else:
739                  # FIXME: bring dialog to front?                  # FIXME: bring dialog to front?
740                  pass                  pass
741    
742        def __SetTitle(self, title):
743            self.SetTitle("Thuban - " + title)
744    
745  #  #
746  # Define all the commands available in the main window  # Define all the commands available in the main window
747  #  #
# Line 471  class MainWindow(wxFrame): Line 749  class MainWindow(wxFrame):
749    
750  # Helper functions to define common command implementations  # Helper functions to define common command implementations
751  def call_method(context, methodname, *args):  def call_method(context, methodname, *args):
752      """Call the context's method methodname with args *args"""      """Call the mainwindow's method methodname with args *args"""
753      apply(getattr(context, methodname), args)      apply(getattr(context.mainwindow, methodname), args)
754    
755  def _method_command(name, title, method, helptext = "", sensitive = None):  def _method_command(name, title, method, helptext = "",
756      """Add a command implemented by a method of the context object"""                      icon = "", sensitive = None, checked = None):
757        """Add a command implemented by a method of the mainwindow object"""
758      registry.Add(Command(name, title, call_method, args=(method,),      registry.Add(Command(name, title, call_method, args=(method,),
759                           helptext = helptext, sensitive = sensitive))                           helptext = helptext, icon = icon,
760                             sensitive = sensitive, checked = checked))
761    
762    def make_check_current_tool(toolname):
763        """Return a function that tests if the currently active tool is toolname
764    
765        The returned function can be called with the context and returns
766        true iff the currently active tool's name is toolname. It's directly
767        usable as the 'checked' callback of a command.
768        """
769        def check_current_tool(context, name=toolname):
770            return context.mainwindow.canvas.CurrentTool() == name
771        return check_current_tool
772    
773  def _tool_command(name, title, method, toolname, helptext = "",  def _tool_command(name, title, method, toolname, helptext = "",
774                    icon = ""):                    icon = "", sensitive = None):
775      """Add a tool command"""      """Add a tool command"""
776      def check_current_tool(context, name=toolname):      registry.Add(ToolCommand(name, title, call_method, args=(method,),
777          return context.canvas.CurrentTool() == name                               helptext = helptext, icon = icon,
778      registry.Add(Command(name, title, call_method, args=(method,),                               checked = make_check_current_tool(toolname),
779                           helptext = helptext, icon = icon,                               sensitive = sensitive))
                          checked = check_current_tool))  
780    
781  def _has_selected_layer(context):  def _has_selected_layer(context):
782      """Return true if a layer is selected in the context"""      """Return true if a layer is selected in the context"""
783      return context.has_selected_layer()      return context.mainwindow.has_selected_layer()
784    
785    def _has_selected_shapes(context):
786        """Return true if a layer is selected in the context"""
787        return context.mainwindow.has_selected_shapes()
788    
789    def _can_remove_layer(context):
790        return context.mainwindow.CanRemoveLayer()
791    
792    def _has_tree_window_shown(context):
793        """Return true if the tree window is shown"""
794        return context.mainwindow.SessionTreeShown()
795    
796    def _has_visible_map(context):
797        """Return true iff theres a visible map in the mainwindow.
798    
799        A visible map is a map with at least one visible layer."""
800        map = context.mainwindow.Map()
801        if map is not None:
802            for layer in map.Layers():
803                if layer.Visible():
804                    return 1
805        return 0
806    
807    def _has_legend_shown(context):
808        """Return true if the legend window is shown"""
809        return context.mainwindow.LegendShown()
810    
811    
812  # File menu  # File menu
813  _method_command("new_session", "&New Session", "NewSession")  _method_command("new_session", _("&New Session"), "NewSession")
814  _method_command("open_session", "&Open Session", "OpenSession")  _method_command("open_session", _("&Open Session..."), "OpenSession")
815  _method_command("save_session", "&Save Session", "SaveSession")  _method_command("save_session", _("&Save Session"), "SaveSession")
816  _method_command("save_session_as", "Save Session &As", "SaveSessionAs")  _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs")
817  _method_command("exit", "&Exit", "Exit")  _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
818                    checked = _has_tree_window_shown)
819    _method_command("toggle_legend", _("Legend"), "ToggleLegend",
820                    checked = _has_legend_shown)
821    _method_command("exit", _("E&xit"), "Exit")
822    
823  # Help menu  # Help menu
824  _method_command("help_about", "&About", "About")  _method_command("help_about", _("&About..."), "About")
825    
826    
827  # Map menu  # Map menu
828  _method_command("map_projection", "Pro&jection", "Projection")  _method_command("map_projection", _("Pro&jection..."), "MapProjection")
829    
830  _tool_command("map_zoom_in_tool", "&Zoom in", "ZoomInTool", "ZoomInTool",  _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
831                helptext = "Switch to map-mode 'zoom-in'", icon = "zoom_in")                helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
832  _tool_command("map_zoom_out_tool", "Zoom &out", "ZoomOutTool", "ZoomOutTool",                sensitive = _has_visible_map)
833                helptext = "Switch to map-mode 'zoom-out'", icon = "zoom_out")  _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
834  _tool_command("map_pan_tool", "&Pan", "PanTool", "PanTool",                helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
835                helptext = "Switch to map-mode 'pan'", icon = "pan")                sensitive = _has_visible_map)
836  _tool_command("map_identify_tool", "&Identify", "IdentifyTool", "IdentifyTool",  _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
837                helptext = "Switch to map-mode 'identify'", icon = "identify")                helptext = _("Switch to map-mode 'pan'"), icon = "pan",
838  _tool_command("map_label_tool", "&Label", "LabelTool", "LabelTool",                sensitive = _has_visible_map)
839                helptext = "Add/Remove labels", icon = "label")  _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
840  _method_command("map_full_extent", "&Full extent", "FullExtent")                "IdentifyTool",
841  _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")                helptext = _("Switch to map-mode 'identify'"), icon = "identify",
842                  sensitive = _has_visible_map)
843    _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
844                  helptext = _("Add/Remove labels"), icon = "label",
845                  sensitive = _has_visible_map)
846    _method_command("map_full_extent", _("&Full extent"), "FullExtent",
847                   helptext = _("Full Extent"), icon = "fullextent",
848                  sensitive = _has_visible_map)
849    _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
850                   helptext = _("Full Layer Extent"), icon = "fulllayerextent",
851                  sensitive = _has_selected_layer)
852    _method_command("selected_full_extent", _("&Full selection extent"), "FullSelectionExtent",
853                   helptext = _("Full Selection Extent"), icon = "fullselextent",
854                  sensitive = _has_selected_shapes)
855    _method_command("map_export", _("E&xport"), "ExportMap",
856                        helptext = _("Export the map to file"))
857    _method_command("map_print", _("Prin&t"), "PrintMap",
858                    helptext = _("Print the map"))
859    _method_command("map_rename", _("&Rename..."), "RenameMap",
860                    helptext = _("Rename the map"))
861    _method_command("layer_add", _("&Add Layer..."), "AddLayer",
862                    helptext = _("Add a new layer to active map"))
863    _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
864                    helptext = _("Add a new image layer to active map"))
865    _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
866                    helptext = _("Remove selected layer(s)"),
867                    sensitive = _can_remove_layer)
868    
869  # Layer menu  # Layer menu
870  _method_command("layer_add", "&Add Layer", "AddLayer",  _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
                 helptext = "Add a new layer to active map")  
 _method_command("layer_remove", "&Remove Layer", "RemoveLayer",  
                 helptext = "Remove selected layer(s)",  
871                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
872  _method_command("layer_fill_color", "&Fill Color", "LayerFillColor",  _method_command("layer_raise", _("&Raise"), "RaiseLayer",
873                  helptext = "Set the fill color of selected layer(s)",                  helptext = _("Raise selected layer(s)"),
874                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
875  _method_command("layer_transparent_fill", "&Transparent Fill",  _method_command("layer_lower", _("&Lower"), "LowerLayer",
876                  "LayerTransparentFill",                  helptext = _("Lower selected layer(s)"),
                 helptext = "Do not fill the selected layer(s)",  
877                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
878  _method_command("layer_ourline_color", "&Outline Color", "LayerOutlineColor",  _method_command("layer_show", _("&Show"), "ShowLayer",
879                  helptext = "Set the outline color of selected layer(s)",                  helptext = _("Make selected layer(s) visible"),
880                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
881  _method_command("layer_no_outline", "&No Outline", "LayerNoOutline",  _method_command("layer_hide", _("&Hide"), "HideLayer",
882                  helptext = "Do not draw the outline of the selected layer(s)",                  helptext = _("Make selected layer(s) unvisible"),
883                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
884  _method_command("layer_raise", "&Raise", "RaiseLayer",  _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
885                  helptext = "Raise selected layer(s)",                  helptext = _("Show the selected layer's table"),
886                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
887  _method_command("layer_lower", "&Lower", "LowerLayer",  _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
                 helptext = "Lower selected layer(s)",  
888                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
889  _method_command("layer_show", "&Show", "ShowLayer",  _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
                 helptext = "Make selected layer(s) visible",  
890                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
891  _method_command("layer_hide", "&Hide", "HideLayer",  _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
                 helptext = "Make selected layer(s) unvisible",  
                 sensitive = _has_selected_layer)  
 _method_command("layer_show_table", "Show Ta&ble", "LayerShowTable",  
                 helptext = "Show the selected layer's table",  
892                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
893    
894    # Table menu
895    _method_command("table_open", _("&Open..."), "TableOpen")
896    _method_command("table_close", _("&Close"), "TableClose")
897    _method_command("table_show", _("&Show"), "TableShow")
898    _method_command("table_hide", _("&Hide"), "TableHide")
899    _method_command("table_join", _("&Join..."), "TableJoin")
900    
901    #  Export only under Windows ...
902    map_menu = ["layer_add", "rasterlayer_add", "layer_remove", "map_rename",
903                            None,
904                            "map_projection",
905                            None,
906                            "map_zoom_in_tool", "map_zoom_out_tool",
907                            "map_pan_tool",
908                            "map_full_extent",
909                            "layer_full_extent",
910                            "selected_full_extent",
911                            None,
912                            "map_identify_tool", "map_label_tool",
913                            None,
914                            "toggle_legend",
915                            None]
916    if wxPlatform == '__WXMSW__':
917        map_menu.append("map_export")
918    map_menu.append("map_print")
919    
920    # the menu structure
921    main_menu = Menu("<main>", "<main>",
922                     [Menu("file", _("&File"),
923                           ["new_session", "open_session", None,
924                            "save_session", "save_session_as", None,
925                            "toggle_session_tree", None,
926                            "exit"]),
927                      Menu("map", _("&Map"), map_menu),
928                      Menu("layer", _("&Layer"),
929                            ["layer_raise", "layer_lower",
930                            None,
931                            "layer_show", "layer_hide",
932                            None,
933                            "layer_projection",
934                            None,
935                            "layer_show_table",
936                            "layer_jointable",
937                            "layer_unjointable",
938                            None,
939                            "layer_properties"]),
940                      Menu("table", _("&Table"),
941                           ["table_open", "table_close",
942                           None,
943                           "table_show", "table_hide",
944                           None,
945                           "table_join"]),
946                      Menu("help", _("&Help"),
947                           ["help_about"])])
948    
949    # the main toolbar
950    
951    main_toolbar = Menu("<toolbar>", "<toolbar>",
952                        ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
953                         "map_full_extent",
954                         "layer_full_extent",
955                         "selected_full_extent",
956                         None,
957                         "map_identify_tool", "map_label_tool"])

Legend:
Removed from v.84  
changed lines
  Added in v.963

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26