/[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 1031 - (show annotations)
Mon May 26 15:33:02 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: 36985 byte(s)
(MainWindow.TableShow): Make the dialog resizeable and increase its
initial size.

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