/[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 1056 - (show annotations)
Tue May 27 11:29:46 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: 36892 byte(s)
(MainWindow.OnClose): Explicitly destroy all dialogs in the dialogs
dictionary and the canvas.

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 for dlg in self.dialogs.values():
411 dlg.Destroy()
412 self.canvas.Destroy()
413 self.Destroy()
414
415 def SetMap(self, map):
416 self.canvas.SetMap(map)
417 self.__SetTitle(map.Title())
418
419 dialog = self.FindRegisteredDock("legend")
420 if dialog is not None:
421 dialog.GetPanel().SetMap(self.Map())
422
423 def Map(self):
424 """Return the map displayed by this mainwindow"""
425
426 return self.canvas.Map()
427
428 def ToggleSessionTree(self):
429 """If the session tree is shown close it otherwise create a new tree"""
430 name = "session_tree"
431 dialog = self.get_open_dialog(name)
432 if dialog is None:
433 dialog = tree.SessionTreeView(self, self.application, name)
434 self.add_dialog(name, dialog)
435 dialog.Show(True)
436 else:
437 dialog.Close()
438
439 def SessionTreeShown(self):
440 """Return true iff the session tree is currently shown"""
441 return self.get_open_dialog("session_tree") is not None
442
443 def About(self):
444 self.RunMessageBox(_("About"),
445 _("Thuban %s\n"
446 #"Build Date: %s\n"
447 "using:\n"
448 " %s\n"
449 " %s\n\n"
450 "Thuban is a program for\n"
451 "exploring geographic data.\n"
452 "Copyright (C) 2001-2003 Intevation GmbH.\n"
453 "Thuban is licensed under the GNU GPL"
454 % (Thuban.version.longversion,
455 "wxPython %s" % wxPython_version,
456 "Python %d.%d.%d" % sys.version_info[:3]
457 )),
458 # % __ThubanVersion__), #__BuildDate__)),
459 wxOK | wxICON_INFORMATION)
460
461 def AddLayer(self):
462 dlg = wxFileDialog(self, _("Select a data file"), ".", "", "*.*",
463 wxOPEN)
464 if dlg.ShowModal() == wxID_OK:
465 filename = dlg.GetPath()
466 title = os.path.splitext(os.path.basename(filename))[0]
467 map = self.canvas.Map()
468 has_layers = map.HasLayers()
469 try:
470 store = self.application.Session().OpenShapefile(filename)
471 except IOError:
472 # the layer couldn't be opened
473 self.RunMessageBox(_("Add Layer"),
474 _("Can't open the file '%s'.") % filename)
475 else:
476 layer = Layer(title, store)
477 map.AddLayer(layer)
478 if not has_layers:
479 # if we're adding a layer to an empty map, fit the
480 # new map to the window
481 self.canvas.FitMapToWindow()
482 dlg.Destroy()
483
484 def AddRasterLayer(self):
485 dlg = wxFileDialog(self, _("Select an image file"), ".", "", "*.*",
486 wxOPEN)
487 if dlg.ShowModal() == wxID_OK:
488 filename = dlg.GetPath()
489 title = os.path.splitext(os.path.basename(filename))[0]
490 map = self.canvas.Map()
491 has_layers = map.HasLayers()
492 try:
493 layer = RasterLayer(title, filename)
494 except IOError:
495 # the layer couldn't be opened
496 self.RunMessageBox(_("Add Image Layer"),
497 _("Can't open the file '%s'.") % filename)
498 else:
499 map.AddLayer(layer)
500 if not has_layers:
501 # if we're adding a layer to an empty map, fit the
502 # new map to the window
503 self.canvas.FitMapToWindow()
504 dlg.Destroy()
505
506 def RemoveLayer(self):
507 layer = self.current_layer()
508 if layer is not None:
509 self.canvas.Map().RemoveLayer(layer)
510
511 def CanRemoveLayer(self):
512 """Return true if the currently selected layer can be deleted.
513
514 If no layer is selected return False.
515
516 The return value of this method determines whether the remove
517 layer command is sensitive in menu.
518 """
519 layer = self.current_layer()
520 if layer is not None:
521 return self.canvas.Map().CanRemoveLayer(layer)
522 return False
523
524 def RaiseLayer(self):
525 layer = self.current_layer()
526 if layer is not None:
527 self.canvas.Map().RaiseLayer(layer)
528
529 def LowerLayer(self):
530 layer = self.current_layer()
531 if layer is not None:
532 self.canvas.Map().LowerLayer(layer)
533
534 def current_layer(self):
535 """Return the currently selected layer.
536
537 If no layer is selected, return None
538 """
539 return self.canvas.SelectedLayer()
540
541 def has_selected_layer(self):
542 """Return true if a layer is currently selected"""
543 return self.canvas.HasSelectedLayer()
544
545 def has_selected_shapes(self):
546 """Return true if a shape is currently selected"""
547 return self.canvas.HasSelectedShapes()
548
549 def HideLayer(self):
550 layer = self.current_layer()
551 if layer is not None:
552 layer.SetVisible(0)
553
554 def ShowLayer(self):
555 layer = self.current_layer()
556 if layer is not None:
557 layer.SetVisible(1)
558
559 def LayerShowTable(self):
560 layer = self.current_layer()
561 if layer is not None:
562 table = layer.table
563 name = "table_view" + str(id(table))
564 dialog = self.get_open_dialog(name)
565 if dialog is None:
566 dialog = tableview.LayerTableFrame(self, name,
567 _("Layer Table: %s") % layer.Title(),
568 layer, table)
569 self.add_dialog(name, dialog)
570 dialog.Show(True)
571 else:
572 # FIXME: bring dialog to front here
573 pass
574
575 def MapProjection(self):
576
577 name = "map_projection"
578 dialog = self.get_open_dialog(name)
579
580 if dialog is None:
581 map = self.canvas.Map()
582 dialog = projdialog.ProjFrame(self, name,
583 _("Map Projection: %s") % map.Title(), map)
584 self.add_dialog(name, dialog)
585 dialog.Show()
586 dialog.Raise()
587
588 def LayerProjection(self):
589
590 layer = self.current_layer()
591
592 name = "layer_projection" + str(id(layer))
593 dialog = self.get_open_dialog(name)
594
595 if dialog is None:
596 map = self.canvas.Map()
597 dialog = projdialog.ProjFrame(self, name,
598 _("Layer Projection: %s") % layer.Title(), layer)
599 self.add_dialog(name, dialog)
600 dialog.Show()
601 dialog.Raise()
602
603 def LayerEditProperties(self):
604
605 #
606 # the menu option for this should only be available if there
607 # is a current layer, so we don't need to check if the
608 # current layer is None
609 #
610
611 layer = self.current_layer()
612 self.OpenLayerProperties(layer)
613
614 def OpenLayerProperties(self, layer, group = None):
615 name = "layer_properties" + str(id(layer))
616 dialog = self.get_open_dialog(name)
617
618 if dialog is None:
619 dialog = Classifier(self, name, layer, group)
620 self.add_dialog(name, dialog)
621 dialog.Show()
622 dialog.Raise()
623
624 def LayerJoinTable(self):
625 print "LayerJoinTable: Not implemented."
626
627 def LayerUnjoinTable(self):
628 print "LayerUnjoinTable: Not implemented."
629
630 def ShowLegend(self):
631 if not self.LegendShown():
632 self.ToggleLegend()
633
634 def ToggleLegend(self):
635 """Show the legend if it's not shown otherwise hide it again"""
636 name = "legend"
637 dialog = self.FindRegisteredDock(name)
638
639 if dialog is None:
640 dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
641 legend.LegendPanel(dialog, None, self)
642 dialog.Dock()
643 dialog.GetPanel().SetMap(self.Map())
644 dialog.Show()
645 else:
646 dialog.Show(not dialog.IsShown())
647
648 def LegendShown(self):
649 """Return true iff the legend is currently open"""
650 dialog = self.FindRegisteredDock("legend")
651 return dialog is not None and dialog.IsShown()
652
653 def TableOpen(self):
654 dlg = wxFileDialog(self, _("Open Table"), ".", "",
655 _("DBF Files (*.dbf)") + "|*.dbf|" +
656 #_("CSV Files (*.csv)") + "|*.csv|" +
657 _("All Files (*.*)") + "|*.*",
658 wxOPEN)
659 if dlg.ShowModal() == wxID_OK:
660 filename = dlg.GetPath()
661 dlg.Destroy()
662 try:
663 table = self.application.session.OpenTableFile(filename)
664 except IOError:
665 # the layer couldn't be opened
666 self.RunMessageBox(_("Open Table"),
667 _("Can't open the file '%s'.") % filename)
668 else:
669 self.ShowTableView(table)
670
671 def TableClose(self):
672 print "TableClose: not implemented"
673
674 def TableShow(self):
675 """Offer a multi-selection dialog for tables to be displayed
676
677 The windows for the selected tables are opened or brought to
678 the front.
679 """
680 tables = self.application.session.Tables()
681
682 dlg = wxMultipleChoiceDialog(self, _("Pick the table to show:"),
683 _("Show Table"),
684 [t.Title() for t in tables],
685 size = (400,300), style = wxRESIZE_BORDER)
686 if (dlg.ShowModal() == wxID_OK):
687 for i in dlg.GetValue():
688 # XXX: if the table belongs to a layer, open a
689 # LayerTableFrame instead of QueryTableFrame
690 self.ShowTableView(tables[i])
691
692 def TableJoin(self):
693 dlg = JoinDialog(self, _("Join Tables"), self.application.session)
694 dlg.ShowModal()
695
696 def ShowTableView(self, table):
697 """Open a table view for the table and optionally"""
698 name = "table_view%d" % id(table)
699 dialog = self.get_open_dialog(name)
700 if dialog is None:
701 dialog = tableview.QueryTableFrame(self, name,
702 _("Table: %s") % table.Title(),
703 table)
704 self.add_dialog(name, dialog)
705 dialog.Show(True)
706 # FIXME: else bring dialog to front
707
708 def ZoomInTool(self):
709 self.canvas.ZoomInTool()
710
711 def ZoomOutTool(self):
712 self.canvas.ZoomOutTool()
713
714 def PanTool(self):
715 self.canvas.PanTool()
716
717 def IdentifyTool(self):
718 self.canvas.IdentifyTool()
719 self.identify_view_on_demand(None, None)
720
721 def LabelTool(self):
722 self.canvas.LabelTool()
723
724 def FullExtent(self):
725 self.canvas.FitMapToWindow()
726
727 def FullLayerExtent(self):
728 self.canvas.FitLayerToWindow(self.current_layer())
729
730 def FullSelectionExtent(self):
731 self.canvas.FitSelectedToWindow()
732
733 def ExportMap(self):
734 self.canvas.Export()
735
736 def PrintMap(self):
737 self.canvas.Print()
738
739 def RenameMap(self):
740 dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
741 self.Map().Title())
742 if dlg.ShowModal() == wxID_OK:
743 title = dlg.GetValue()
744 if title != "":
745 self.Map().SetTitle(title)
746 self.__SetTitle(title)
747
748 dlg.Destroy()
749
750 def identify_view_on_demand(self, layer, shapes):
751 """Subscribed to the canvas' SHAPES_SELECTED message
752
753 If the current tool is the identify tool, at least one shape is
754 selected and the identify dialog is not shown, show the dialog.
755 """
756 # If the selection has become empty we don't need to do
757 # anything. Otherwise it could happen that the dialog was popped
758 # up when the selection became empty, e.g. when a new selection
759 # is opened while the identify tool is active and dialog had
760 # been closed
761 if not shapes:
762 return
763
764 name = "identify_view"
765 if self.canvas.CurrentTool() == "IdentifyTool":
766 if not self.dialog_open(name):
767 dialog = identifyview.IdentifyView(self, name)
768 self.add_dialog(name, dialog)
769 dialog.Show(True)
770 else:
771 # FIXME: bring dialog to front?
772 pass
773
774 def __SetTitle(self, title):
775 self.SetTitle("Thuban - " + title)
776
777 #
778 # Define all the commands available in the main window
779 #
780
781
782 # Helper functions to define common command implementations
783 def call_method(context, methodname, *args):
784 """Call the mainwindow's method methodname with args *args"""
785 apply(getattr(context.mainwindow, methodname), args)
786
787 def _method_command(name, title, method, helptext = "",
788 icon = "", sensitive = None, checked = None):
789 """Add a command implemented by a method of the mainwindow object"""
790 registry.Add(Command(name, title, call_method, args=(method,),
791 helptext = helptext, icon = icon,
792 sensitive = sensitive, checked = checked))
793
794 def make_check_current_tool(toolname):
795 """Return a function that tests if the currently active tool is toolname
796
797 The returned function can be called with the context and returns
798 true iff the currently active tool's name is toolname. It's directly
799 usable as the 'checked' callback of a command.
800 """
801 def check_current_tool(context, name=toolname):
802 return context.mainwindow.canvas.CurrentTool() == name
803 return check_current_tool
804
805 def _tool_command(name, title, method, toolname, helptext = "",
806 icon = "", sensitive = None):
807 """Add a tool command"""
808 registry.Add(ToolCommand(name, title, call_method, args=(method,),
809 helptext = helptext, icon = icon,
810 checked = make_check_current_tool(toolname),
811 sensitive = sensitive))
812
813 def _has_selected_layer(context):
814 """Return true if a layer is selected in the context"""
815 return context.mainwindow.has_selected_layer()
816
817 def _has_selected_shapes(context):
818 """Return true if a layer is selected in the context"""
819 return context.mainwindow.has_selected_shapes()
820
821 def _can_remove_layer(context):
822 return context.mainwindow.CanRemoveLayer()
823
824 def _has_tree_window_shown(context):
825 """Return true if the tree window is shown"""
826 return context.mainwindow.SessionTreeShown()
827
828 def _has_visible_map(context):
829 """Return true iff theres a visible map in the mainwindow.
830
831 A visible map is a map with at least one visible layer."""
832 map = context.mainwindow.Map()
833 if map is not None:
834 for layer in map.Layers():
835 if layer.Visible():
836 return 1
837 return 0
838
839 def _has_legend_shown(context):
840 """Return true if the legend window is shown"""
841 return context.mainwindow.LegendShown()
842
843
844 # File menu
845 _method_command("new_session", _("&New Session"), "NewSession")
846 _method_command("open_session", _("&Open Session..."), "OpenSession")
847 _method_command("save_session", _("&Save Session"), "SaveSession")
848 _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs")
849 _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
850 checked = _has_tree_window_shown)
851 _method_command("toggle_legend", _("Legend"), "ToggleLegend",
852 checked = _has_legend_shown)
853 _method_command("exit", _("E&xit"), "Exit")
854
855 # Help menu
856 _method_command("help_about", _("&About..."), "About")
857
858
859 # Map menu
860 _method_command("map_projection", _("Pro&jection..."), "MapProjection")
861
862 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
863 helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
864 sensitive = _has_visible_map)
865 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
866 helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
867 sensitive = _has_visible_map)
868 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
869 helptext = _("Switch to map-mode 'pan'"), icon = "pan",
870 sensitive = _has_visible_map)
871 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
872 "IdentifyTool",
873 helptext = _("Switch to map-mode 'identify'"), icon = "identify",
874 sensitive = _has_visible_map)
875 _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
876 helptext = _("Add/Remove labels"), icon = "label",
877 sensitive = _has_visible_map)
878 _method_command("map_full_extent", _("&Full extent"), "FullExtent",
879 helptext = _("Full Extent"), icon = "fullextent",
880 sensitive = _has_visible_map)
881 _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
882 helptext = _("Full Layer Extent"), icon = "fulllayerextent",
883 sensitive = _has_selected_layer)
884 _method_command("selected_full_extent", _("&Full selection extent"), "FullSelectionExtent",
885 helptext = _("Full Selection Extent"), icon = "fullselextent",
886 sensitive = _has_selected_shapes)
887 _method_command("map_export", _("E&xport"), "ExportMap",
888 helptext = _("Export the map to file"))
889 _method_command("map_print", _("Prin&t"), "PrintMap",
890 helptext = _("Print the map"))
891 _method_command("map_rename", _("&Rename..."), "RenameMap",
892 helptext = _("Rename the map"))
893 _method_command("layer_add", _("&Add Layer..."), "AddLayer",
894 helptext = _("Add a new layer to active map"))
895 _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
896 helptext = _("Add a new image layer to active map"))
897 _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
898 helptext = _("Remove selected layer(s)"),
899 sensitive = _can_remove_layer)
900
901 # Layer menu
902 _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
903 sensitive = _has_selected_layer)
904 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
905 helptext = _("Raise selected layer(s)"),
906 sensitive = _has_selected_layer)
907 _method_command("layer_lower", _("&Lower"), "LowerLayer",
908 helptext = _("Lower selected layer(s)"),
909 sensitive = _has_selected_layer)
910 _method_command("layer_show", _("&Show"), "ShowLayer",
911 helptext = _("Make selected layer(s) visible"),
912 sensitive = _has_selected_layer)
913 _method_command("layer_hide", _("&Hide"), "HideLayer",
914 helptext = _("Make selected layer(s) unvisible"),
915 sensitive = _has_selected_layer)
916 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
917 helptext = _("Show the selected layer's table"),
918 sensitive = _has_selected_layer)
919 _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
920 sensitive = _has_selected_layer)
921 _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
922 sensitive = _has_selected_layer)
923 _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
924 sensitive = _has_selected_layer)
925
926 # Table menu
927 _method_command("table_open", _("&Open..."), "TableOpen")
928 _method_command("table_close", _("&Close"), "TableClose")
929 _method_command("table_show", _("&Show"), "TableShow")
930 _method_command("table_join", _("&Join..."), "TableJoin")
931
932 # Export only under Windows ...
933 map_menu = ["layer_add", "rasterlayer_add", "layer_remove", "map_rename",
934 None,
935 "map_projection",
936 None,
937 "map_zoom_in_tool", "map_zoom_out_tool",
938 "map_pan_tool",
939 "map_full_extent",
940 "layer_full_extent",
941 "selected_full_extent",
942 None,
943 "map_identify_tool", "map_label_tool",
944 None,
945 "toggle_legend",
946 None]
947 if wxPlatform == '__WXMSW__':
948 map_menu.append("map_export")
949 map_menu.append("map_print")
950
951 # the menu structure
952 main_menu = Menu("<main>", "<main>",
953 [Menu("file", _("&File"),
954 ["new_session", "open_session", None,
955 "save_session", "save_session_as", None,
956 "toggle_session_tree", None,
957 "exit"]),
958 Menu("map", _("&Map"), map_menu),
959 Menu("layer", _("&Layer"),
960 ["layer_raise", "layer_lower",
961 None,
962 "layer_show", "layer_hide",
963 None,
964 "layer_projection",
965 None,
966 "layer_show_table",
967 "layer_jointable",
968 "layer_unjointable",
969 None,
970 "layer_properties"]),
971 Menu("table", _("&Table"),
972 ["table_open", "table_close",
973 None,
974 "table_show",
975 None,
976 "table_join"]),
977 Menu("help", _("&Help"),
978 ["help_about"])])
979
980 # the main toolbar
981
982 main_toolbar = Menu("<toolbar>", "<toolbar>",
983 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
984 "map_full_extent",
985 "layer_full_extent",
986 "selected_full_extent",
987 None,
988 "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