/[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 723 - (show annotations)
Thu Apr 24 15:31:53 2003 UTC (21 years, 10 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 29517 byte(s)
First step towards table management. Introduce a simple data
abstraction so that we replace the data a layer uses more easily
in the next step.

* Thuban/Model/data.py: New file with a simple data abstraction
that bundles shapefile and dbffile into one object.

* Thuban/Model/session.py (Session.OpenShapefile): New method to
open shapefiles and return a shape store object

* Thuban/Model/layer.py (Layer.__init__): Pass the data as a store
object instead of a shapefile filename. This introduces a new
instance variable store holding the datastore. For intermediate
backwards compatibility keep the old instance variables.
(open_shapefile): Removed. No longer needed with the shape store.
(Layer.SetShapeStore, Layer.ShapeStore): New methods to set and
get the shape store used by a layer.
(Layer.Destroy): No need to explicitly destroy the shapefile or
table anymore.

* Thuban/UI/mainwindow.py (MainWindow.AddLayer)
(MainWindow.AddLayer): Use the session's OpenShapefile method to
open shapefiles

* Thuban/Model/load.py (ProcessSession.start_layer): Use the
session's OpenShapefile method to open shapefiles

* test/test_classification.py
(TestClassification.test_classification): Use the session's
OpenShapefile method to open shapefiles and build the filename in
a more platform independed way

* test/test_layer.py (TestLayer.setUp, TestLayer.tearDown):
Implement to have a session to use in the tests
(TestLayer.test_arc_layer, TestLayer.test_polygon_layer)
(TestLayer.test_point_layer, TestLayer.test_empty_layer): Use the
session's OpenShapefile method to open shapefiles
(TestLayerLegend.setUp): Instantiate a session so that we can use
it to open shapefiles.
(TestLayerLegend.tearDown): Make sure that all references to
layers and session are removed otherwise we may get a resource
leak

* test/test_map.py (TestMapAddLayer.test_add_layer)
(TestMapWithContents.setUp): Instantiate a session so that we can
use it to open shapefiles.
(TestMapWithContents.tearDown): Make sure that all references to
layers, maps and sessions are removed otherwise we may get a
resource leak
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_save.py (SaveSessionTest.testSingleLayer): Use the
session's OpenShapefile method to open shapefiles
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_selection.py (TestSelection.tearDown): Make sure that
all references to the session and the selection are removed
otherwise we may get a resource leak
(TestSelection.get_layer): Instantiate a session so that we can
use it to open shapefiles.
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_session.py (TestSessionBase.tearDown)
(TestSessionWithContent.tearDown): Make sure that all references
to the session and layers are removed otherwise we may get a
resource leak
(TestSessionWithContent.setUp): Use the session's OpenShapefile
method to open shapefiles

1 # Copyright (C) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Jan-Oliver Wagner <[email protected]>
4 # Bernhard Herzog <[email protected]>
5 #
6 # This program is free software under the GPL (>=v2)
7 # Read the file COPYING coming with Thuban for details.
8
9 """
10 The main window
11 """
12
13 __version__ = "$Revision$"
14
15 __ThubanVersion__ = "0.2" #"$THUBAN_0_2$"
16 #__BuildDate__ = "$Date$"
17
18 import os
19
20 from wxPython.wx import *
21
22 import Thuban
23 from Thuban import _
24 from Thuban.Model.session import create_empty_session
25 from Thuban.Model.layer import Layer
26 from Thuban.Model.color import Color
27 from Thuban.Model.proj import Projection
28
29 import view
30 import tree
31 import proj4dialog
32 import tableview, identifyview
33 from Thuban.UI.classifier import Classifier
34 import legend
35 from menu import Menu
36
37 from context import Context
38 from command import registry, Command, ToolCommand
39 from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION
40
41 from Thuban.UI.dock import DockFrame
42
43 import resource
44
45 import projdialog
46
47
48
49 class MainWindow(DockFrame):
50
51 # Some messages that can be subscribed/unsubscribed directly through
52 # the MapCanvas come in fact from other objects. This is a map to
53 # map those messages to the names of the instance variables they
54 # actually come from. This delegation is implemented in the
55 # Subscribe and unsubscribed methods
56 delegated_messages = {LAYER_SELECTED: "canvas",
57 SHAPES_SELECTED: "canvas"}
58
59 # Methods delegated to some instance variables. The delegation is
60 # implemented in the __getattr__ method.
61 delegated_methods = {"SelectLayer": "canvas",
62 "SelectShapes": "canvas",
63 }
64
65 def __init__(self, parent, ID, title, application, interactor,
66 initial_message = None, size = wxSize(-1, -1)):
67 DockFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
68 #wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
69
70 self.application = application
71
72 self.CreateStatusBar()
73 if initial_message:
74 self.SetStatusText(initial_message)
75
76 self.identify_view = None
77
78 self.init_ids()
79
80 # creat the menubar from the main_menu description
81 self.SetMenuBar(self.build_menu_bar(main_menu))
82
83 # Similarly, create the toolbar from main_toolbar
84 toolbar = self.build_toolbar(main_toolbar)
85 # call Realize to make sure that the tools appear.
86 toolbar.Realize()
87
88
89 # Create the map canvas
90 canvas = view.MapCanvas(self, -1)
91 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
92 canvas.Subscribe(SHAPES_SELECTED, self.identify_view_on_demand)
93 self.canvas = canvas
94
95 self.SetMainWindow(self.canvas)
96
97 self.SetAutoLayout(True)
98
99 self.init_dialogs()
100
101 EVT_CLOSE(self, self._OnClose)
102
103 def Subscribe(self, channel, *args):
104 """Subscribe a function to a message channel.
105
106 If channel is one of the delegated messages call the appropriate
107 object's Subscribe method. Otherwise do nothing.
108 """
109 if channel in self.delegated_messages:
110 object = getattr(self, self.delegated_messages[channel])
111 object.Subscribe(channel, *args)
112 else:
113 print "Trying to subscribe to unsupported channel %s" % channel
114
115 def Unsubscribe(self, channel, *args):
116 """Unsubscribe a function from a message channel.
117
118 If channel is one of the delegated messages call the appropriate
119 object's Unsubscribe method. Otherwise do nothing.
120 """
121 if channel in self.delegated_messages:
122 object = getattr(self, self.delegated_messages[channel])
123 object.Unsubscribe(channel, *args)
124
125 def __getattr__(self, attr):
126 """If attr is one of the delegated methods return that method
127
128 Otherwise raise AttributeError.
129 """
130 if attr in self.delegated_methods:
131 return getattr(getattr(self, self.delegated_methods[attr]), attr)
132 raise AttributeError(attr)
133
134 def init_ids(self):
135 """Initialize the ids"""
136 self.current_id = 6000
137 self.id_to_name = {}
138 self.name_to_id = {}
139 self.events_bound = {}
140
141 def get_id(self, name):
142 """Return the wxWindows id for the command named name.
143
144 Create a new one if there isn't one yet"""
145 ID = self.name_to_id.get(name)
146 if ID is None:
147 ID = self.current_id
148 self.current_id = self.current_id + 1
149 self.name_to_id[name] = ID
150 self.id_to_name[ID] = name
151 return ID
152
153 def bind_command_events(self, command, ID):
154 """Bind the necessary events for the given command and ID"""
155 if not self.events_bound.has_key(ID):
156 # the events haven't been bound yet
157 EVT_MENU(self, ID, self.invoke_command)
158 if command.IsDynamic():
159 EVT_UPDATE_UI(self, ID, self.update_command_ui)
160
161 def build_menu_bar(self, menudesc):
162 """Build and return the menu bar from the menu description"""
163 menu_bar = wxMenuBar()
164
165 for item in menudesc.items:
166 # here the items must all be Menu instances themselves
167 menu_bar.Append(self.build_menu(item), item.title)
168
169 return menu_bar
170
171 def build_menu(self, menudesc):
172 """Return a wxMenu built from the menu description menudesc"""
173 wxmenu = wxMenu()
174 last = None
175 for item in menudesc.items:
176 if item is None:
177 # a separator. Only add one if the last item was not a
178 # separator
179 if last is not None:
180 wxmenu.AppendSeparator()
181 elif isinstance(item, Menu):
182 # a submenu
183 wxmenu.AppendMenu(wxNewId(), item.title, self.build_menu(item))
184 else:
185 # must the name the name of a command
186 self.add_menu_command(wxmenu, item)
187 last = item
188 return wxmenu
189
190 def build_toolbar(self, toolbardesc):
191 """Build and return the main toolbar window from a toolbar description
192
193 The parameter should be an instance of the Menu class but it
194 should not contain submenus.
195 """
196 toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
197
198 # set the size of the tools' bitmaps. Not needed on wxGTK, but
199 # on Windows, although it doesn't work very well there. It seems
200 # that only 16x16 icons are really supported on windows.
201 # We probably shouldn't hardwire the bitmap size here.
202 toolbar.SetToolBitmapSize(wxSize(24, 24))
203
204 for item in toolbardesc.items:
205 if item is None:
206 toolbar.AddSeparator()
207 else:
208 # assume it's a string.
209 self.add_toolbar_command(toolbar, item)
210
211 return toolbar
212
213 def add_menu_command(self, menu, name):
214 """Add the command with name name to the menu menu.
215
216 If name is None, add a separator.
217 """
218 if name is None:
219 menu.AppendSeparator()
220 else:
221 command = registry.Command(name)
222 if command is not None:
223 ID = self.get_id(name)
224 menu.Append(ID, command.Title(), command.HelpText(),
225 command.IsCheckCommand())
226 self.bind_command_events(command, ID)
227 else:
228 print _("Unknown command %s") % name
229
230 def add_toolbar_command(self, toolbar, name):
231 """Add the command with name name to the toolbar toolbar.
232
233 If name is None, add a separator.
234 """
235 # Assume that all toolbar commands are also menu commmands so
236 # that we don't have to add the event handlers here
237 if name is None:
238 toolbar.AddSeparator()
239 else:
240 command = registry.Command(name)
241 if command is not None:
242 ID = self.get_id(name)
243 bitmap = resource.GetBitmapResource(command.Icon(),
244 wxBITMAP_TYPE_XPM)
245 toolbar.AddTool(ID, bitmap,
246 shortHelpString = command.HelpText(),
247 isToggle = command.IsCheckCommand())
248 self.bind_command_events(command, ID)
249 else:
250 print _("Unknown command %s") % name
251
252 def Context(self):
253 """Return the context object for a command invoked from this window
254 """
255 return Context(self.application, self.application.Session(), self)
256
257 def invoke_command(self, event):
258 name = self.id_to_name.get(event.GetId())
259 if name is not None:
260 command = registry.Command(name)
261 command.Execute(self.Context())
262 else:
263 print _("Unknown command ID %d") % event.GetId()
264
265 def update_command_ui(self, event):
266 #print "update_command_ui", self.id_to_name[event.GetId()]
267 context = self.Context()
268 command = registry.Command(self.id_to_name[event.GetId()])
269 if command is not None:
270 sensitive = command.Sensitive(context)
271 event.Enable(sensitive)
272 if command.IsTool() and not sensitive and command.Checked(context):
273 # When a checked tool command is disabled deselect all
274 # tools. Otherwise the tool would remain active but it
275 # might lead to errors if the tools stays active. This
276 # problem occurred in GREAT-ER and this fixes it, but
277 # it's not clear to me whether this is really the best
278 # way to do it (BH, 20021206).
279 self.canvas.SelectTool(None)
280 event.SetText(command.DynText(context))
281 if command.IsCheckCommand():
282 event.Check(command.Checked(context))
283
284 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
285 """Run a modal message box with the given text, title and flags
286 and return the result"""
287 dlg = wxMessageDialog(self, text, title, flags)
288 dlg.CenterOnParent()
289 result = dlg.ShowModal()
290 dlg.Destroy()
291 return result
292
293 def init_dialogs(self):
294 """Initialize the dialog handling"""
295 # The mainwindow maintains a dict mapping names to open
296 # non-modal dialogs. The dialogs are put into this dict when
297 # they're created and removed when they're closed
298 self.dialogs = {}
299
300 def add_dialog(self, name, dialog):
301 if self.dialogs.has_key(name):
302 raise RuntimeError(_("The Dialog named %s is already open") % name)
303 self.dialogs[name] = dialog
304
305 def dialog_open(self, name):
306 return self.dialogs.has_key(name)
307
308 def remove_dialog(self, name):
309 del self.dialogs[name]
310
311 def get_open_dialog(self, name):
312 return self.dialogs.get(name)
313
314 def view_position_changed(self):
315 pos = self.canvas.CurrentPosition()
316 if pos is not None:
317 text = "(%10.10g, %10.10g)" % pos
318 else:
319 text = ""
320 self.set_position_text(text)
321
322 def set_position_text(self, text):
323 """Set the statusbar text showing the current position.
324
325 By default the text is shown in field 0 of the status bar.
326 Override this method in derived classes to put it into a
327 different field of the statusbar.
328 """
329 self.SetStatusText(text)
330
331 def save_modified_session(self, can_veto = 1):
332 """If the current session has been modified, ask the user
333 whether to save it and do so if requested. Return the outcome of
334 the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
335 dialog wasn't run return wxID_NO.
336
337 If the can_veto parameter is true (default) the dialog includes
338 a cancel button, otherwise not.
339 """
340 if self.application.session.WasModified():
341 flags = wxYES_NO | wxICON_QUESTION
342 if can_veto:
343 flags = flags | wxCANCEL
344 result = self.RunMessageBox(_("Exit"),
345 _("The session has been modified."
346 " Do you want to save it?"),
347 flags)
348 if result == wxID_YES:
349 self.SaveSession()
350 else:
351 result = wxID_NO
352 return result
353
354 def prepare_new_session(self):
355 for d in self.dialogs.values():
356 if not isinstance(d, tree.SessionTreeView):
357 d.Close()
358
359 def NewSession(self):
360 self.save_modified_session()
361 self.prepare_new_session()
362 self.application.SetSession(create_empty_session())
363
364 def OpenSession(self):
365 self.save_modified_session()
366 dlg = wxFileDialog(self, _("Open Session"), ".", "", "*.thuban", wxOPEN)
367 if dlg.ShowModal() == wxID_OK:
368 self.prepare_new_session()
369 self.application.OpenSession(dlg.GetPath())
370 dlg.Destroy()
371
372 def SaveSession(self):
373 if self.application.session.filename == None:
374 self.SaveSessionAs()
375 else:
376 self.application.SaveSession()
377
378 def SaveSessionAs(self):
379 dlg = wxFileDialog(self, _("Save Session As"), ".", "",
380 "*.thuban", wxOPEN)
381 if dlg.ShowModal() == wxID_OK:
382 self.application.session.SetFilename(dlg.GetPath())
383 self.application.SaveSession()
384 dlg.Destroy()
385
386 def Exit(self):
387 self.Close(False)
388
389 def _OnClose(self, event):
390 result = self.save_modified_session(can_veto = event.CanVeto())
391 if result == wxID_CANCEL:
392 event.Veto()
393 else:
394 # FIXME: it would be better to tie the unsubscription to
395 # wx's destroy event, but that isn't implemented for wxGTK
396 # yet.
397 self.canvas.Unsubscribe(VIEW_POSITION, self.view_position_changed)
398 DockFrame._OnClose(self, event)
399 self.Destroy()
400
401 def SetMap(self, map):
402 self.canvas.SetMap(map)
403 self.__SetTitle(map.Title())
404 #self.legendPanel.SetMap(map)
405
406 def Map(self):
407 """Return the map displayed by this mainwindow"""
408
409 # sanity check
410 #assert(self.canvas.Map() is self.legendPanel.GetMap())
411
412 return self.canvas.Map()
413
414 def ToggleSessionTree(self):
415 """If the session tree is shown close it otherwise create a new tree"""
416 name = "session_tree"
417 dialog = self.get_open_dialog(name)
418 if dialog is None:
419 dialog = tree.SessionTreeView(self, self.application, name)
420 self.add_dialog(name, dialog)
421 dialog.Show(True)
422 else:
423 dialog.Close()
424
425 def SessionTreeShown(self):
426 """Return true iff the session tree is currently shown"""
427 return self.get_open_dialog("session_tree") is not None
428
429 def About(self):
430 self.RunMessageBox(_("About"),
431 _("Thuban v%s\n"
432 #"Build Date: %s\n"
433 "\n"
434 "Thuban is a program for\n"
435 "exploring geographic data.\n"
436 "Copyright (C) 2001-2003 Intevation GmbH.\n"
437 "Thuban is licensed under the GNU GPL"
438 % __ThubanVersion__), #__BuildDate__)),
439 wxOK | wxICON_INFORMATION)
440
441 def AddLayer(self):
442 dlg = wxFileDialog(self, _("Select a data file"), ".", "", "*.*",
443 wxOPEN)
444 if dlg.ShowModal() == wxID_OK:
445 filename = dlg.GetPath()
446 title = os.path.splitext(os.path.basename(filename))[0]
447 store = self.application.Session().OpenShapefile(filename)
448 layer = Layer(title, store)
449 map = self.canvas.Map()
450 has_layers = map.HasLayers()
451 try:
452 map.AddLayer(layer)
453 except IOError:
454 # the layer couldn't be opened
455 self.RunMessageBox(_("Add Layer"),
456 _("Can't open the file '%s'.") % filename)
457 else:
458 if not has_layers:
459 # if we're adding a layer to an empty map, fit the
460 # new map to the window
461 self.canvas.FitMapToWindow()
462 dlg.Destroy()
463
464 def RemoveLayer(self):
465 layer = self.current_layer()
466 if layer is not None:
467 self.canvas.Map().RemoveLayer(layer)
468
469 def CanRemoveLayer(self):
470 """Return true if the currently selected layer can be deleted.
471
472 If no layer is selected return False.
473
474 The return value of this method determines whether the remove
475 layer command is sensitive in menu.
476 """
477 layer = self.current_layer()
478 if layer is not None:
479 return self.canvas.Map().CanRemoveLayer(layer)
480 return False
481
482 def RaiseLayer(self):
483 layer = self.current_layer()
484 if layer is not None:
485 self.canvas.Map().RaiseLayer(layer)
486
487 def LowerLayer(self):
488 layer = self.current_layer()
489 if layer is not None:
490 self.canvas.Map().LowerLayer(layer)
491
492 def current_layer(self):
493 """Return the currently selected layer.
494
495 If no layer is selected, return None
496 """
497 return self.canvas.SelectedLayer()
498
499 def has_selected_layer(self):
500 """Return true if a layer is currently selected"""
501 return self.canvas.HasSelectedLayer()
502
503 def choose_color(self):
504 """Run the color selection dialog and return the selected color.
505
506 If the user cancels, return None.
507 """
508 dlg = wxColourDialog(self)
509 color = None
510 if dlg.ShowModal() == wxID_OK:
511 data = dlg.GetColourData()
512 wxc = data.GetColour()
513 color = Color(wxc.Red() / 255.0,
514 wxc.Green() / 255.0,
515 wxc.Blue() / 255.0)
516 dlg.Destroy()
517 return color
518
519 def HideLayer(self):
520 layer = self.current_layer()
521 if layer is not None:
522 layer.SetVisible(0)
523
524 def ShowLayer(self):
525 layer = self.current_layer()
526 if layer is not None:
527 layer.SetVisible(1)
528
529 def LayerShowTable(self):
530 layer = self.current_layer()
531 if layer is not None:
532 table = layer.table
533 name = "table_view" + str(id(table))
534 dialog = self.get_open_dialog(name)
535 if dialog is None:
536 dialog = tableview.LayerTableFrame(self, name,
537 _("Table: %s") % layer.Title(),
538 layer, table)
539 self.add_dialog(name, dialog)
540 dialog.Show(true)
541 else:
542 # FIXME: bring dialog to front here
543 pass
544
545 def Projection(self):
546
547 name = "projection"
548 dialog = self.get_open_dialog(name)
549
550 if dialog is None:
551 map = self.canvas.Map()
552 dialog = projdialog.ProjFrame(self, name, map)
553 self.add_dialog(name, dialog)
554 dialog.Show()
555 dialog.Raise()
556
557 def LayerEditProperties(self):
558
559 #
560 # the menu option for this should only be available if there
561 # is a current layer, so we don't need to check if the
562 # current layer is None
563 #
564
565 layer = self.current_layer()
566 self.OpenLayerProperties(layer)
567
568 def OpenLayerProperties(self, layer, group = None):
569 name = "layer_properties" + str(id(layer))
570 dialog = self.get_open_dialog(name)
571
572 if dialog is None:
573 dialog = Classifier(self, name, layer, group)
574 self.add_dialog(name, dialog)
575 dialog.Show()
576 dialog.Raise()
577
578
579 def ShowLegend(self):
580 if not self.LegendShown():
581 self.ToggleLegend()
582
583 def ToggleLegend(self):
584 """Show the legend if it's not shown otherwise hide it again"""
585 name = "legend"
586 dialog = self.FindRegisteredDock(name)
587
588 if dialog is None:
589 dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
590 legend.LegendPanel(dialog, None, self)
591 dialog.Dock()
592 dialog.GetPanel().SetMap(self.Map())
593 dialog.Show()
594 else:
595 dialog.Show(not dialog.IsShown())
596
597 def LegendShown(self):
598 """Return true iff the legend is currently open"""
599 dialog = self.FindRegisteredDock("legend")
600 return dialog is not None and dialog.IsShown()
601
602 def ZoomInTool(self):
603 self.canvas.ZoomInTool()
604
605 def ZoomOutTool(self):
606 self.canvas.ZoomOutTool()
607
608 def PanTool(self):
609 self.canvas.PanTool()
610
611 def IdentifyTool(self):
612 self.canvas.IdentifyTool()
613 self.identify_view_on_demand(None, None)
614
615 def LabelTool(self):
616 self.canvas.LabelTool()
617
618 def FullExtent(self):
619 self.canvas.FitMapToWindow()
620
621 def PrintMap(self):
622 self.canvas.Print()
623
624 def RenameMap(self):
625 dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
626 self.Map().Title())
627 if dlg.ShowModal() == wxID_OK:
628 title = dlg.GetValue()
629 if title != "":
630 self.Map().SetTitle(title)
631 self.__SetTitle(title)
632
633 dlg.Destroy()
634
635 def identify_view_on_demand(self, layer, shapes):
636 name = "identify_view"
637 if self.canvas.CurrentTool() == "IdentifyTool":
638 if not self.dialog_open(name):
639 dialog = identifyview.IdentifyView(self, name)
640 self.add_dialog(name, dialog)
641 dialog.Show(True)
642 else:
643 # FIXME: bring dialog to front?
644 pass
645
646 def __SetTitle(self, title):
647 self.SetTitle("Thuban - " + title)
648
649 #
650 # Define all the commands available in the main window
651 #
652
653
654 # Helper functions to define common command implementations
655 def call_method(context, methodname, *args):
656 """Call the mainwindow's method methodname with args *args"""
657 apply(getattr(context.mainwindow, methodname), args)
658
659 def _method_command(name, title, method, helptext = "",
660 icon = "", sensitive = None, checked = None):
661 """Add a command implemented by a method of the mainwindow object"""
662 registry.Add(Command(name, title, call_method, args=(method,),
663 helptext = helptext, icon = icon,
664 sensitive = sensitive, checked = checked))
665
666 def make_check_current_tool(toolname):
667 """Return a function that tests if the currently active tool is toolname
668
669 The returned function can be called with the context and returns
670 true iff the currently active tool's name is toolname. It's directly
671 usable as the 'checked' callback of a command.
672 """
673 def check_current_tool(context, name=toolname):
674 return context.mainwindow.canvas.CurrentTool() == name
675 return check_current_tool
676
677 def _tool_command(name, title, method, toolname, helptext = "",
678 icon = "", sensitive = None):
679 """Add a tool command"""
680 registry.Add(ToolCommand(name, title, call_method, args=(method,),
681 helptext = helptext, icon = icon,
682 checked = make_check_current_tool(toolname),
683 sensitive = sensitive))
684
685 def _has_selected_layer(context):
686 """Return true if a layer is selected in the context"""
687 return context.mainwindow.has_selected_layer()
688
689 def _can_remove_layer(context):
690 return context.mainwindow.CanRemoveLayer()
691
692 def _has_tree_window_shown(context):
693 """Return true if the tree window is shown"""
694 return context.mainwindow.SessionTreeShown()
695
696 def _has_visible_map(context):
697 """Return true iff theres a visible map in the mainwindow.
698
699 A visible map is a map with at least one visible layer."""
700 map = context.mainwindow.Map()
701 if map is not None:
702 for layer in map.Layers():
703 if layer.Visible():
704 return 1
705 return 0
706
707 def _has_legend_shown(context):
708 """Return true if the legend window is shown"""
709 return context.mainwindow.LegendShown()
710
711
712 # File menu
713 _method_command("new_session", _("&New Session"), "NewSession")
714 _method_command("open_session", _("&Open Session"), "OpenSession")
715 _method_command("save_session", _("&Save Session"), "SaveSession")
716 _method_command("save_session_as", _("Save Session &As"), "SaveSessionAs")
717 _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
718 checked = _has_tree_window_shown)
719 _method_command("toggle_legend", _("Legend"), "ToggleLegend",
720 checked = _has_legend_shown)
721 _method_command("exit", _("E&xit"), "Exit")
722
723 # Help menu
724 _method_command("help_about", _("&About"), "About")
725
726
727 # Map menu
728 _method_command("map_projection", _("Pro&jection"), "Projection")
729
730 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
731 helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
732 sensitive = _has_visible_map)
733 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
734 helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
735 sensitive = _has_visible_map)
736 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
737 helptext = _("Switch to map-mode 'pan'"), icon = "pan",
738 sensitive = _has_visible_map)
739 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
740 "IdentifyTool",
741 helptext = _("Switch to map-mode 'identify'"), icon = "identify",
742 sensitive = _has_visible_map)
743 _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
744 helptext = _("Add/Remove labels"), icon = "label",
745 sensitive = _has_visible_map)
746 _method_command("map_full_extent", _("&Full extent"), "FullExtent",
747 helptext = _("Full Extent"), icon = "fullextent",
748 sensitive = _has_visible_map)
749 _method_command("map_print", _("Prin&t"), "PrintMap",
750 helptext = _("Print the map"))
751 _method_command("map_rename", _("&Rename"), "RenameMap",
752 helptext = _("Rename the map"))
753
754 # Layer menu
755 _method_command("layer_add", _("&Add Layer"), "AddLayer",
756 helptext = _("Add a new layer to active map"))
757 _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
758 helptext = _("Remove selected layer(s)"),
759 sensitive = _can_remove_layer)
760 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
761 helptext = _("Raise selected layer(s)"),
762 sensitive = _has_selected_layer)
763 _method_command("layer_lower", _("&Lower"), "LowerLayer",
764 helptext = _("Lower selected layer(s)"),
765 sensitive = _has_selected_layer)
766 _method_command("layer_show", _("&Show"), "ShowLayer",
767 helptext = _("Make selected layer(s) visible"),
768 sensitive = _has_selected_layer)
769 _method_command("layer_hide", _("&Hide"), "HideLayer",
770 helptext = _("Make selected layer(s) unvisible"),
771 sensitive = _has_selected_layer)
772 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
773 helptext = _("Show the selected layer's table"),
774 sensitive = _has_selected_layer)
775 _method_command("layer_properties", _("Properties"), "LayerEditProperties",
776 sensitive = _has_selected_layer)
777
778 # the menu structure
779 main_menu = Menu("<main>", "<main>",
780 [Menu("file", _("&File"),
781 ["new_session", "open_session", None,
782 "save_session", "save_session_as", None,
783 "toggle_session_tree", None,
784 "exit"]),
785 Menu("map", _("&Map"),
786 ["layer_add", "layer_remove",
787 None,
788 "map_projection",
789 None,
790 "map_zoom_in_tool", "map_zoom_out_tool",
791 "map_pan_tool", "map_identify_tool", "map_label_tool",
792 None,
793 "map_full_extent",
794 None,
795 "toggle_legend",
796 None,
797 "map_print",
798 None,
799 "map_rename"]),
800 Menu("layer", _("&Layer"),
801 ["layer_raise", "layer_lower",
802 None,
803 "layer_show", "layer_hide",
804 None,
805 "layer_show_table",
806 None,
807 "layer_properties"]),
808 Menu("help", _("&Help"),
809 ["help_about"])])
810
811 # the main toolbar
812
813 main_toolbar = Menu("<toolbar>", "<toolbar>",
814 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
815 "map_full_extent", None,
816 "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