/[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 1052 - (show annotations)
Tue May 27 09:34:28 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 36815 byte(s)
Remove the Table/Hide menu item. Its
effect can be achieved by simply closing the window showing the
table.
(MainWindow.TableHide): Removed.
(main_menu): Removed "table_hide"

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