/[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 535 - (show annotations)
Fri Mar 14 20:42:18 2003 UTC (21 years, 11 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 29197 byte(s)
Implement multiple selected shapes

* Thuban/UI/selection.py: New module with a class to represent the
selection.

* Thuban/UI/messages.py (SELECTED_TABLE, SELECTED_MAP): Remove
these unused messages

* Thuban/UI/application.py (ThubanApplication.OnInit)
(ThubanApplication.OnExit, ThubanApplication.SetSession): The
interactor is gone now.
(ThubanApplication.CreateMainWindow): There is no interactor
anymore so we pass None as the interactor argument for now for
compatibility.

* Thuban/UI/view.py (MapCanvas.delegated_messages)
(MapCanvas.Subscribe, MapCanvas.Unsubscribe): In Subscribe and
Unsubscribe, delegate messages according to the delegated_messages
class variable.
(MapCanvas.__getattr__, MapCanvas.delegated_methods): Get some
attributes from instance variables as described with the
delegated_methods class variable.
(MapCanvas.__init__): New instance variable selection holding the
current selection
(MapCanvas.do_redraw): Deal with multiple selected shapes. Simply
pass them on to the renderer
(MapCanvas.SetMap): Clear the selection when a different map is
selected.
(MapCanvas.shape_selected): Simple force a complete redraw. The
selection class now takes care of only issueing SHAPES_SELECTED
messages when the set of selected shapes actually does change.
(MapCanvas.SelectShapeAt): The selection is now managed in
self.selection

* Thuban/UI/mainwindow.py (MainWindow.delegated_messages)
(MainWindow.Subscribe, MainWindow.Unsubscribe): In Subscribe and
Unsubscribe, delegate messages according to the delegated_messages
class variable.
(MainWindow.delegated_methods, MainWindow.__getattr__): Get some
attributes from instance variables as described with the
delegated_methods class variable.
(MainWindow.__init__): The interactor as ivar is gone. The
parameter is still there for compatibility. The selection messages
now come from the canvas.
(MainWindow.current_layer, MainWindow.has_selected_layer):
Delegate to the the canvas.
(MainWindow.LayerShowTable, MainWindow.Classify)
(MainWindow.identify_view_on_demand): The dialogs don't need the
interactor parameter anymore.

* Thuban/UI/tableview.py (TableFrame.__init__)
(LayerTableFrame.__init__, LayerTableFrame.OnClose)
(LayerTableFrame.row_selected): The interactor is gone. It's job
from the dialog's point of view is now done by the mainwindow,
i.e. the parent. Subscribe to SHAPES_SELECTED instead
of SELECTED_SHAPE

* Thuban/UI/dialogs.py (NonModalDialog.__init__): The interactor
is gone. It's job from the dialog's point of view is now done by
the mainwindow, i.e. the parent.

* Thuban/UI/classifier.py (Classifier.__init__): The interactor is
gone. It's job from the dialog's point of view is now done by the
mainwindow, i.e. the parent.

* Thuban/UI/tree.py (SessionTreeView.__init__): The interactor is
gone. It's job from the dialog's point of view is now done by the
mainwindow, i.e. the parent.
(SessionTreeCtrl.__init__): New parameter mainwindow which is
stored as self.mainwindow. The mainwindow is need so that the tree
can still subscribe to the selection messages.
(SessionTreeCtrl.__init__, SessionTreeCtrl.unsubscribe_all)
(SessionTreeCtrl.update_tree, SessionTreeCtrl.OnSelChanged): The
selection is now accessible through the mainwindow. Subscribe to
SHAPES_SELECTED instead of SELECTED_SHAPE

* Thuban/UI/identifyview.py (IdentifyView.__init__): Use the
SHAPES_SELECTED message now.
(IdentifyView.selected_shape): Now subscribed to SHAPES_SELECTED,
so deal with multiple shapes
(IdentifyView.__init__, IdentifyView.OnClose): The interactor is
gone. It's job from the dialog's point of view is now done by the
mainwindow, i.e. the parent.

* Thuban/UI/renderer.py (ScreenRenderer.RenderMap): Rename the
selected_shape parameter and ivar to selected_shapes. It's now a
list of shape ids.
(MapRenderer.draw_label_layer): Deal with multiple selected
shapes. Rearrange the code a bit so that the setup and shape type
distinctions are only executed once.

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