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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 951 - (show annotations)
Wed May 21 14:20:32 2003 UTC (21 years, 9 months ago) by frank
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 35306 byte(s)
(MainWindow): Renamed _OnClose() back to OnClose() to keep in sync
with extensions. Internally Thuban still uses "underscored" names.

1 # Copyright (C) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Jan-Oliver Wagner <[email protected]>
4 # Bernhard Herzog <[email protected]>
5 # Frank Koormann <[email protected]>
6 #
7 # This program is free software under the GPL (>=v2)
8 # Read the file COPYING coming with Thuban for details.
9
10 """
11 The main window
12 """
13
14 __version__ = "$Revision$"
15
16 __ThubanVersion__ = "0.2" #"$THUBAN_0_2$"
17 #__BuildDate__ = "$Date$"
18
19 import os
20
21 from wxPython.wx import *
22 from wxPython.wx import __version__ as wxPython_version
23
24 import Thuban
25 import Thuban.version
26
27 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
31 from Thuban.Model.proj import Projection
32
33 import view
34 import tree
35 import proj4dialog
36 import tableview, identifyview
37 from Thuban.UI.classifier import Classifier
38 import legend
39 from menu import Menu
40
41 from context import Context
42 from command import registry, Command, ToolCommand
43 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 import resource
49
50 import projdialog
51
52
53
54 class MainWindow(DockFrame):
55
56 # Some messages that can be subscribed/unsubscribed directly through
57 # the MapCanvas come in fact from other objects. This is a map to
58 # 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 # 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 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 self.application = application
77
78 self.CreateStatusBar()
79 if initial_message:
80 self.SetStatusText(initial_message)
81
82 self.identify_view = None
83
84 self.init_ids()
85
86 # creat the menubar from the main_menu description
87 self.SetMenuBar(self.build_menu_bar(main_menu))
88
89 # Similarly, create the toolbar from main_toolbar
90 toolbar = self.build_toolbar(main_toolbar)
91 # call Realize to make sure that the tools appear.
92 toolbar.Realize()
93
94
95 # Create the map canvas
96 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
100
101 self.SetMainWindow(self.canvas)
102
103 self.SetAutoLayout(True)
104
105 self.init_dialogs()
106
107 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):
141 """Initialize the ids"""
142 self.current_id = 6000
143 self.id_to_name = {}
144 self.name_to_id = {}
145 self.events_bound = {}
146
147 def get_id(self, name):
148 """Return the wxWindows id for the command named name.
149
150 Create a new one if there isn't one yet"""
151 ID = self.name_to_id.get(name)
152 if ID is None:
153 ID = self.current_id
154 self.current_id = self.current_id + 1
155 self.name_to_id[name] = ID
156 self.id_to_name[ID] = name
157 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):
220 """Add the command with name name to the menu menu.
221
222 If name is None, add a separator.
223 """
224 if name is None:
225 menu.AppendSeparator()
226 else:
227 command = registry.Command(name)
228 if command is not None:
229 ID = self.get_id(name)
230 menu.Append(ID, command.Title(), command.HelpText(),
231 command.IsCheckCommand())
232 self.bind_command_events(command, ID)
233 else:
234 print _("Unknown command %s") % name
235
236 def add_toolbar_command(self, toolbar, name):
237 """Add the command with name name to the toolbar toolbar.
238
239 If name is None, add a separator.
240 """
241 # Assume that all toolbar commands are also menu commmands so
242 # that we don't have to add the event handlers here
243 if name is None:
244 toolbar.AddSeparator()
245 else:
246 command = registry.Command(name)
247 if command is not None:
248 ID = self.get_id(name)
249 bitmap = resource.GetBitmapResource(command.Icon(),
250 wxBITMAP_TYPE_XPM)
251 toolbar.AddTool(ID, bitmap,
252 shortHelpString = command.HelpText(),
253 isToggle = command.IsCheckCommand())
254 self.bind_command_events(command, ID)
255 else:
256 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):
264 name = self.id_to_name.get(event.GetId())
265 if name is not None:
266 command = registry.Command(name)
267 command.Execute(self.Context())
268 else:
269 print _("Unknown command ID %d") % event.GetId()
270
271 def update_command_ui(self, event):
272 #print "update_command_ui", self.id_to_name[event.GetId()]
273 context = self.Context()
274 command = registry.Command(self.id_to_name[event.GetId()])
275 if command is not None:
276 sensitive = command.Sensitive(context)
277 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():
288 event.Check(command.Checked(context))
289
290 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
291 """Run a modal message box with the given text, title and flags
292 and return the result"""
293 dlg = wxMessageDialog(self, text, title, flags)
294 dlg.CenterOnParent()
295 result = dlg.ShowModal()
296 dlg.Destroy()
297 return result
298
299 def init_dialogs(self):
300 """Initialize the dialog handling"""
301 # The mainwindow maintains a dict mapping names to open
302 # non-modal dialogs. The dialogs are put into this dict when
303 # they're created and removed when they're closed
304 self.dialogs = {}
305
306 def add_dialog(self, name, dialog):
307 if self.dialogs.has_key(name):
308 raise RuntimeError(_("The Dialog named %s is already open") % name)
309 self.dialogs[name] = dialog
310
311 def dialog_open(self, name):
312 return self.dialogs.has_key(name)
313
314 def remove_dialog(self, name):
315 del self.dialogs[name]
316
317 def get_open_dialog(self, name):
318 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):
338 """If the current session has been modified, ask the user
339 whether to save it and do so if requested. Return the outcome of
340 the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
341 dialog wasn't run return wxID_NO.
342
343 If the can_veto parameter is true (default) the dialog includes
344 a cancel button, otherwise not.
345 """
346 if self.application.session.WasModified():
347 flags = wxYES_NO | wxICON_QUESTION
348 if can_veto:
349 flags = flags | wxCANCEL
350 result = self.RunMessageBox(_("Exit"),
351 _("The session has been modified."
352 " Do you want to save it?"),
353 flags)
354 if result == wxID_YES:
355 self.SaveSession()
356 else:
357 result = wxID_NO
358 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):
366 if self.save_modified_session() != wxID_CANCEL:
367 self.prepare_new_session()
368 self.application.SetSession(create_empty_session())
369
370 def OpenSession(self):
371 if self.save_modified_session() != wxID_CANCEL:
372 dlg = wxFileDialog(self, _("Open Session"), ".", "",
373 "Thuban Session File (*.thuban)|*.thuban",
374 wxOPEN)
375 if dlg.ShowModal() == wxID_OK:
376 self.prepare_new_session()
377 self.application.OpenSession(dlg.GetPath())
378 dlg.Destroy()
379
380 def SaveSession(self):
381 if self.application.session.filename == None:
382 self.SaveSessionAs()
383 else:
384 self.application.SaveSession()
385
386 def SaveSessionAs(self):
387 dlg = wxFileDialog(self, _("Save Session As"), ".", "",
388 "Thuban Session File (*.thuban)|*.thuban",
389 wxSAVE|wxOVERWRITE_PROMPT)
390 if dlg.ShowModal() == wxID_OK:
391 self.application.session.SetFilename(dlg.GetPath())
392 self.application.SaveSession()
393 dlg.Destroy()
394
395 def Exit(self):
396 self.Close(False)
397
398 def OnClose(self, event):
399 result = self.save_modified_session(can_veto = event.CanVeto())
400 if result == wxID_CANCEL:
401 event.Veto()
402 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()
409
410 def SetMap(self, map):
411 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 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"
426 dialog = self.get_open_dialog(name)
427 if dialog is None:
428 dialog = tree.SessionTreeView(self, self.application, name)
429 self.add_dialog(name, dialog)
430 dialog.Show(True)
431 else:
432 dialog.Close()
433
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):
439 self.RunMessageBox(_("About"),
440 _("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"
447 "Copyright (C) 2001-2003 Intevation GmbH.\n"
448 "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)
455
456 def AddLayer(self):
457 dlg = wxFileDialog(self, _("Select a data file"), ".", "", "*.*",
458 wxOPEN)
459 if dlg.ShowModal() == wxID_OK:
460 filename = dlg.GetPath()
461 title = os.path.splitext(os.path.basename(filename))[0]
462 store = self.application.Session().OpenShapefile(filename)
463 layer = Layer(title, store)
464 map = self.canvas.Map()
465 has_layers = map.HasLayers()
466 try:
467 map.AddLayer(layer)
468 except IOError:
469 # the layer couldn't be opened
470 self.RunMessageBox(_("Add Layer"),
471 _("Can't open the file '%s'.") % filename)
472 else:
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 layer = RasterLayer(title, filename)
486 map = self.canvas.Map()
487 has_layers = map.HasLayers()
488 try:
489 map.AddLayer(layer)
490 except IOError:
491 # the layer couldn't be opened
492 self.RunMessageBox(_("Add Image Layer"),
493 _("Can't open the file '%s'.") % filename)
494 else:
495 if not has_layers:
496 # if we're adding a layer to an empty map, fit the
497 # new map to the window
498 self.canvas.FitMapToWindow()
499 dlg.Destroy()
500
501 def RemoveLayer(self):
502 layer = self.current_layer()
503 if layer is not None:
504 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):
520 layer = self.current_layer()
521 if layer is not None:
522 self.canvas.Map().RaiseLayer(layer)
523
524 def LowerLayer(self):
525 layer = self.current_layer()
526 if layer is not None:
527 self.canvas.Map().LowerLayer(layer)
528
529 def current_layer(self):
530 """Return the currently selected layer.
531
532 If no layer is selected, return None
533 """
534 return self.canvas.SelectedLayer()
535
536 def has_selected_layer(self):
537 """Return true if a layer is currently selected"""
538 return self.canvas.HasSelectedLayer()
539
540 def has_selected_shapes(self):
541 """Return true if a shape is currently selected"""
542 return self.canvas.HasSelectedShapes()
543
544 def HideLayer(self):
545 layer = self.current_layer()
546 if layer is not None:
547 layer.SetVisible(0)
548
549 def ShowLayer(self):
550 layer = self.current_layer()
551 if layer is not None:
552 layer.SetVisible(1)
553
554 def LayerShowTable(self):
555 layer = self.current_layer()
556 if layer is not None:
557 table = layer.table
558 name = "table_view" + str(id(table))
559 dialog = self.get_open_dialog(name)
560 if dialog is None:
561 dialog = tableview.LayerTableFrame(self, name,
562 _("Table: %s") % layer.Title(),
563 layer, table)
564 self.add_dialog(name, dialog)
565 dialog.Show(true)
566 else:
567 # FIXME: bring dialog to front here
568 pass
569
570 def MapProjection(self):
571
572 name = "map_projection"
573 dialog = self.get_open_dialog(name)
574
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:
641 dialog.Show(not dialog.IsShown())
642
643 def LegendShown(self):
644 """Return true iff the legend is currently open"""
645 dialog = self.FindRegisteredDock("legend")
646 return dialog is not None and dialog.IsShown()
647
648 def TableOpen(self):
649 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):
677 self.canvas.ZoomInTool()
678
679 def ZoomOutTool(self):
680 self.canvas.ZoomOutTool()
681
682 def PanTool(self):
683 self.canvas.PanTool()
684
685 def IdentifyTool(self):
686 self.canvas.IdentifyTool()
687 self.identify_view_on_demand(None, None)
688
689 def LabelTool(self):
690 self.canvas.LabelTool()
691
692 def FullExtent(self):
693 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):
705 self.canvas.Print()
706
707 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"
733 if self.canvas.CurrentTool() == "IdentifyTool":
734 if not self.dialog_open(name):
735 dialog = identifyview.IdentifyView(self, name)
736 self.add_dialog(name, dialog)
737 dialog.Show(True)
738 else:
739 # FIXME: bring dialog to front?
740 pass
741
742 def __SetTitle(self, title):
743 self.SetTitle("Thuban - " + title)
744
745 #
746 # Define all the commands available in the main window
747 #
748
749
750 # Helper functions to define common command implementations
751 def call_method(context, methodname, *args):
752 """Call the mainwindow's method methodname with args *args"""
753 apply(getattr(context.mainwindow, methodname), args)
754
755 def _method_command(name, title, method, helptext = "",
756 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,),
759 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 = "",
774 icon = "", sensitive = None):
775 """Add a tool command"""
776 registry.Add(ToolCommand(name, title, call_method, args=(method,),
777 helptext = helptext, icon = icon,
778 checked = make_check_current_tool(toolname),
779 sensitive = sensitive))
780
781 def _has_selected_layer(context):
782 """Return true if a layer is selected in the context"""
783 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
813 _method_command("new_session", _("&New Session"), "NewSession")
814 _method_command("open_session", _("&Open Session..."), "OpenSession")
815 _method_command("save_session", _("&Save Session"), "SaveSession")
816 _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs")
817 _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
824 _method_command("help_about", _("&About..."), "About")
825
826
827 # Map menu
828 _method_command("map_projection", _("Pro&jection..."), "MapProjection")
829
830 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
831 helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
832 sensitive = _has_visible_map)
833 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
834 helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
835 sensitive = _has_visible_map)
836 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
837 helptext = _("Switch to map-mode 'pan'"), icon = "pan",
838 sensitive = _has_visible_map)
839 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
840 "IdentifyTool",
841 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
870 _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
871 sensitive = _has_selected_layer)
872 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
873 helptext = _("Raise selected layer(s)"),
874 sensitive = _has_selected_layer)
875 _method_command("layer_lower", _("&Lower"), "LowerLayer",
876 helptext = _("Lower selected layer(s)"),
877 sensitive = _has_selected_layer)
878 _method_command("layer_show", _("&Show"), "ShowLayer",
879 helptext = _("Make selected layer(s) visible"),
880 sensitive = _has_selected_layer)
881 _method_command("layer_hide", _("&Hide"), "HideLayer",
882 helptext = _("Make selected layer(s) unvisible"),
883 sensitive = _has_selected_layer)
884 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
885 helptext = _("Show the selected layer's table"),
886 sensitive = _has_selected_layer)
887 _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
888 sensitive = _has_selected_layer)
889 _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
890 sensitive = _has_selected_layer)
891 _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
892 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"])

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26