/[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 1084 - (show annotations)
Wed May 28 10:25:09 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: 38497 byte(s)
(MainWindow.TableClose, MainWindow.TableShow): sort the selection list
for the dialog.

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