/[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 1014 - (show annotations)
Fri May 23 09:26:23 2003 UTC (21 years, 9 months ago) by jan
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 36709 byte(s)
(MainWindow.TableShow): A very coarse implementation that still needs work.

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 _("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"
623
624 def LayerUnjoinTable(self):
625 print "LayerUnjoinTable"
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 print "TableOpen: not implemented"
652 dlg = wxFileDialog(self, _("Open Table"), ".", "",
653 "DBF Files (*.dbf)|*.dbf|" +
654 "CSV Files (*.csv)|*.csv|" +
655 "All Files (*.*)|*.*",
656 wxOPEN)
657 if dlg.ShowModal() == wxID_OK:
658 #self.application.session.OpenTable(dlg.GetPath())
659 pass
660
661 dlg.Destroy()
662
663 def TableClose(self):
664 print "TableClose: not implemented"
665
666 def TableShow(self):
667 """Offer a multi-selection dialog for tables to be displayed
668
669 The windows for the selected tables are opened or brought to
670 the front.
671 """
672 tables = self.application.session.Tables()
673 table_list = []
674 for table in tables:
675 table_list.append(table.Title())
676
677 dlg = wxMultipleChoiceDialog(self, _("Pick the table to show:"),
678 _("Show Table"), table_list)
679 if (dlg.ShowModal() == wxID_OK):
680 for i in dlg.GetValue():
681 # XXX: First check whether the dialog is already open
682 # and if so, bring it to the front.
683 dialog = tableview.QueryTableFrame(self, table_list[i],
684 _("Table: %s") % table_list[i],
685 tables[i])
686 self.add_dialog(table_list[i], dialog)
687 dialog.Show(true)
688
689 # XXX: just some analyis code, remove it when the above XXX is
690 # resolved.
691 for d in self.dialogs.values():
692 if isinstance(d, tableview.LayerTableFrame):
693 print "LayerTable:", d.GetTitle()
694 elif isinstance(d, tableview.QueryTableFrame):
695 print "QueryTable:", d.GetTitle()
696 else:
697 print "Other:", d.GetTitle()
698
699 def TableHide(self):
700 print "TableHide: not implemented"
701
702 def TableJoin(self):
703 dlg = JoinDialog(self, _("Join Tables"), self.application.session)
704 dlg.ShowModal()
705
706 def ZoomInTool(self):
707 self.canvas.ZoomInTool()
708
709 def ZoomOutTool(self):
710 self.canvas.ZoomOutTool()
711
712 def PanTool(self):
713 self.canvas.PanTool()
714
715 def IdentifyTool(self):
716 self.canvas.IdentifyTool()
717 self.identify_view_on_demand(None, None)
718
719 def LabelTool(self):
720 self.canvas.LabelTool()
721
722 def FullExtent(self):
723 self.canvas.FitMapToWindow()
724
725 def FullLayerExtent(self):
726 self.canvas.FitLayerToWindow(self.current_layer())
727
728 def FullSelectionExtent(self):
729 self.canvas.FitSelectedToWindow()
730
731 def ExportMap(self):
732 self.canvas.Export()
733
734 def PrintMap(self):
735 self.canvas.Print()
736
737 def RenameMap(self):
738 dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
739 self.Map().Title())
740 if dlg.ShowModal() == wxID_OK:
741 title = dlg.GetValue()
742 if title != "":
743 self.Map().SetTitle(title)
744 self.__SetTitle(title)
745
746 dlg.Destroy()
747
748 def identify_view_on_demand(self, layer, shapes):
749 """Subscribed to the canvas' SHAPES_SELECTED message
750
751 If the current tool is the identify tool, at least one shape is
752 selected and the identify dialog is not shown, show the dialog.
753 """
754 # If the selection has become empty we don't need to do
755 # anything. Otherwise it could happen that the dialog was popped
756 # up when the selection became empty, e.g. when a new selection
757 # is opened while the identify tool is active and dialog had
758 # been closed
759 if not shapes:
760 return
761
762 name = "identify_view"
763 if self.canvas.CurrentTool() == "IdentifyTool":
764 if not self.dialog_open(name):
765 dialog = identifyview.IdentifyView(self, name)
766 self.add_dialog(name, dialog)
767 dialog.Show(True)
768 else:
769 # FIXME: bring dialog to front?
770 pass
771
772 def __SetTitle(self, title):
773 self.SetTitle("Thuban - " + title)
774
775 #
776 # Define all the commands available in the main window
777 #
778
779
780 # Helper functions to define common command implementations
781 def call_method(context, methodname, *args):
782 """Call the mainwindow's method methodname with args *args"""
783 apply(getattr(context.mainwindow, methodname), args)
784
785 def _method_command(name, title, method, helptext = "",
786 icon = "", sensitive = None, checked = None):
787 """Add a command implemented by a method of the mainwindow object"""
788 registry.Add(Command(name, title, call_method, args=(method,),
789 helptext = helptext, icon = icon,
790 sensitive = sensitive, checked = checked))
791
792 def make_check_current_tool(toolname):
793 """Return a function that tests if the currently active tool is toolname
794
795 The returned function can be called with the context and returns
796 true iff the currently active tool's name is toolname. It's directly
797 usable as the 'checked' callback of a command.
798 """
799 def check_current_tool(context, name=toolname):
800 return context.mainwindow.canvas.CurrentTool() == name
801 return check_current_tool
802
803 def _tool_command(name, title, method, toolname, helptext = "",
804 icon = "", sensitive = None):
805 """Add a tool command"""
806 registry.Add(ToolCommand(name, title, call_method, args=(method,),
807 helptext = helptext, icon = icon,
808 checked = make_check_current_tool(toolname),
809 sensitive = sensitive))
810
811 def _has_selected_layer(context):
812 """Return true if a layer is selected in the context"""
813 return context.mainwindow.has_selected_layer()
814
815 def _has_selected_shapes(context):
816 """Return true if a layer is selected in the context"""
817 return context.mainwindow.has_selected_shapes()
818
819 def _can_remove_layer(context):
820 return context.mainwindow.CanRemoveLayer()
821
822 def _has_tree_window_shown(context):
823 """Return true if the tree window is shown"""
824 return context.mainwindow.SessionTreeShown()
825
826 def _has_visible_map(context):
827 """Return true iff theres a visible map in the mainwindow.
828
829 A visible map is a map with at least one visible layer."""
830 map = context.mainwindow.Map()
831 if map is not None:
832 for layer in map.Layers():
833 if layer.Visible():
834 return 1
835 return 0
836
837 def _has_legend_shown(context):
838 """Return true if the legend window is shown"""
839 return context.mainwindow.LegendShown()
840
841
842 # File menu
843 _method_command("new_session", _("&New Session"), "NewSession")
844 _method_command("open_session", _("&Open Session..."), "OpenSession")
845 _method_command("save_session", _("&Save Session"), "SaveSession")
846 _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs")
847 _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
848 checked = _has_tree_window_shown)
849 _method_command("toggle_legend", _("Legend"), "ToggleLegend",
850 checked = _has_legend_shown)
851 _method_command("exit", _("E&xit"), "Exit")
852
853 # Help menu
854 _method_command("help_about", _("&About..."), "About")
855
856
857 # Map menu
858 _method_command("map_projection", _("Pro&jection..."), "MapProjection")
859
860 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
861 helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
862 sensitive = _has_visible_map)
863 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
864 helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
865 sensitive = _has_visible_map)
866 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
867 helptext = _("Switch to map-mode 'pan'"), icon = "pan",
868 sensitive = _has_visible_map)
869 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
870 "IdentifyTool",
871 helptext = _("Switch to map-mode 'identify'"), icon = "identify",
872 sensitive = _has_visible_map)
873 _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
874 helptext = _("Add/Remove labels"), icon = "label",
875 sensitive = _has_visible_map)
876 _method_command("map_full_extent", _("&Full extent"), "FullExtent",
877 helptext = _("Full Extent"), icon = "fullextent",
878 sensitive = _has_visible_map)
879 _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
880 helptext = _("Full Layer Extent"), icon = "fulllayerextent",
881 sensitive = _has_selected_layer)
882 _method_command("selected_full_extent", _("&Full selection extent"), "FullSelectionExtent",
883 helptext = _("Full Selection Extent"), icon = "fullselextent",
884 sensitive = _has_selected_shapes)
885 _method_command("map_export", _("E&xport"), "ExportMap",
886 helptext = _("Export the map to file"))
887 _method_command("map_print", _("Prin&t"), "PrintMap",
888 helptext = _("Print the map"))
889 _method_command("map_rename", _("&Rename..."), "RenameMap",
890 helptext = _("Rename the map"))
891 _method_command("layer_add", _("&Add Layer..."), "AddLayer",
892 helptext = _("Add a new layer to active map"))
893 _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
894 helptext = _("Add a new image layer to active map"))
895 _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
896 helptext = _("Remove selected layer(s)"),
897 sensitive = _can_remove_layer)
898
899 # Layer menu
900 _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
901 sensitive = _has_selected_layer)
902 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
903 helptext = _("Raise selected layer(s)"),
904 sensitive = _has_selected_layer)
905 _method_command("layer_lower", _("&Lower"), "LowerLayer",
906 helptext = _("Lower selected layer(s)"),
907 sensitive = _has_selected_layer)
908 _method_command("layer_show", _("&Show"), "ShowLayer",
909 helptext = _("Make selected layer(s) visible"),
910 sensitive = _has_selected_layer)
911 _method_command("layer_hide", _("&Hide"), "HideLayer",
912 helptext = _("Make selected layer(s) unvisible"),
913 sensitive = _has_selected_layer)
914 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
915 helptext = _("Show the selected layer's table"),
916 sensitive = _has_selected_layer)
917 _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
918 sensitive = _has_selected_layer)
919 _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
920 sensitive = _has_selected_layer)
921 _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
922 sensitive = _has_selected_layer)
923
924 # Table menu
925 _method_command("table_open", _("&Open..."), "TableOpen")
926 _method_command("table_close", _("&Close"), "TableClose")
927 _method_command("table_show", _("&Show"), "TableShow")
928 _method_command("table_hide", _("&Hide"), "TableHide")
929 _method_command("table_join", _("&Join..."), "TableJoin")
930
931 # Export only under Windows ...
932 map_menu = ["layer_add", "rasterlayer_add", "layer_remove", "map_rename",
933 None,
934 "map_projection",
935 None,
936 "map_zoom_in_tool", "map_zoom_out_tool",
937 "map_pan_tool",
938 "map_full_extent",
939 "layer_full_extent",
940 "selected_full_extent",
941 None,
942 "map_identify_tool", "map_label_tool",
943 None,
944 "toggle_legend",
945 None]
946 if wxPlatform == '__WXMSW__':
947 map_menu.append("map_export")
948 map_menu.append("map_print")
949
950 # the menu structure
951 main_menu = Menu("<main>", "<main>",
952 [Menu("file", _("&File"),
953 ["new_session", "open_session", None,
954 "save_session", "save_session_as", None,
955 "toggle_session_tree", None,
956 "exit"]),
957 Menu("map", _("&Map"), map_menu),
958 Menu("layer", _("&Layer"),
959 ["layer_raise", "layer_lower",
960 None,
961 "layer_show", "layer_hide",
962 None,
963 "layer_projection",
964 None,
965 "layer_show_table",
966 "layer_jointable",
967 "layer_unjointable",
968 None,
969 "layer_properties"]),
970 Menu("table", _("&Table"),
971 ["table_open", "table_close",
972 None,
973 "table_show", "table_hide",
974 None,
975 "table_join"]),
976 Menu("help", _("&Help"),
977 ["help_about"])])
978
979 # the main toolbar
980
981 main_toolbar = Menu("<toolbar>", "<toolbar>",
982 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
983 "map_full_extent",
984 "layer_full_extent",
985 "selected_full_extent",
986 None,
987 "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