/[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 1072 - (show annotations)
Tue May 27 16:47:48 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 37525 byte(s)
* Thuban/UI/join.py (JoinDialog): Extend to handle layer joins as
well
(JoinDialog.__init__): Use the layer parameter and only build the
left choice when a layer is given
(JoinDialog.OnJoin): Handle layer joins as well
(JoinDialog.OnLeftTable, JoinDialog.OnRightTable): Handle the case
that the user selects the "Select..." item. The sensitivitly
updating is now in update_sensitivity
(JoinDialog.y): New method to refactor the sensitivity update of
the join button into its own method.

* Thuban/UI/mainwindow.py (MainWindow.LayerJoinTable): Implement.

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 "SelectedShapes": "canvas",
68 }
69
70 def __init__(self, parent, ID, title, application, interactor,
71 initial_message = None, size = wxSize(-1, -1)):
72 DockFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
73 #wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
74
75 self.application = application
76
77 self.CreateStatusBar()
78 if initial_message:
79 self.SetStatusText(initial_message)
80
81 self.identify_view = None
82
83 self.init_ids()
84
85 # creat the menubar from the main_menu description
86 self.SetMenuBar(self.build_menu_bar(main_menu))
87
88 # Similarly, create the toolbar from main_toolbar
89 toolbar = self.build_toolbar(main_toolbar)
90 # call Realize to make sure that the tools appear.
91 toolbar.Realize()
92
93
94 # Create the map canvas
95 canvas = view.MapCanvas(self, -1)
96 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
97 canvas.Subscribe(SHAPES_SELECTED, self.identify_view_on_demand)
98 self.canvas = canvas
99
100 self.SetMainWindow(self.canvas)
101
102 self.SetAutoLayout(True)
103
104 self.init_dialogs()
105
106 EVT_CLOSE(self, self.OnClose)
107
108 def Subscribe(self, channel, *args):
109 """Subscribe a function to a message channel.
110
111 If channel is one of the delegated messages call the appropriate
112 object's Subscribe method. Otherwise do nothing.
113 """
114 if channel in self.delegated_messages:
115 object = getattr(self, self.delegated_messages[channel])
116 object.Subscribe(channel, *args)
117 else:
118 print "Trying to subscribe to unsupported channel %s" % channel
119
120 def Unsubscribe(self, channel, *args):
121 """Unsubscribe a function from a message channel.
122
123 If channel is one of the delegated messages call the appropriate
124 object's Unsubscribe method. Otherwise do nothing.
125 """
126 if channel in self.delegated_messages:
127 object = getattr(self, self.delegated_messages[channel])
128 object.Unsubscribe(channel, *args)
129
130 def __getattr__(self, attr):
131 """If attr is one of the delegated methods return that method
132
133 Otherwise raise AttributeError.
134 """
135 if attr in self.delegated_methods:
136 return getattr(getattr(self, self.delegated_methods[attr]), attr)
137 raise AttributeError(attr)
138
139 def init_ids(self):
140 """Initialize the ids"""
141 self.current_id = 6000
142 self.id_to_name = {}
143 self.name_to_id = {}
144 self.events_bound = {}
145
146 def get_id(self, name):
147 """Return the wxWindows id for the command named name.
148
149 Create a new one if there isn't one yet"""
150 ID = self.name_to_id.get(name)
151 if ID is None:
152 ID = self.current_id
153 self.current_id = self.current_id + 1
154 self.name_to_id[name] = ID
155 self.id_to_name[ID] = name
156 return ID
157
158 def bind_command_events(self, command, ID):
159 """Bind the necessary events for the given command and ID"""
160 if not self.events_bound.has_key(ID):
161 # the events haven't been bound yet
162 EVT_MENU(self, ID, self.invoke_command)
163 if command.IsDynamic():
164 EVT_UPDATE_UI(self, ID, self.update_command_ui)
165
166 def build_menu_bar(self, menudesc):
167 """Build and return the menu bar from the menu description"""
168 menu_bar = wxMenuBar()
169
170 for item in menudesc.items:
171 # here the items must all be Menu instances themselves
172 menu_bar.Append(self.build_menu(item), item.title)
173
174 return menu_bar
175
176 def build_menu(self, menudesc):
177 """Return a wxMenu built from the menu description menudesc"""
178 wxmenu = wxMenu()
179 last = None
180 for item in menudesc.items:
181 if item is None:
182 # a separator. Only add one if the last item was not a
183 # separator
184 if last is not None:
185 wxmenu.AppendSeparator()
186 elif isinstance(item, Menu):
187 # a submenu
188 wxmenu.AppendMenu(wxNewId(), item.title, self.build_menu(item))
189 else:
190 # must the name the name of a command
191 self.add_menu_command(wxmenu, item)
192 last = item
193 return wxmenu
194
195 def build_toolbar(self, toolbardesc):
196 """Build and return the main toolbar window from a toolbar description
197
198 The parameter should be an instance of the Menu class but it
199 should not contain submenus.
200 """
201 toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
202
203 # set the size of the tools' bitmaps. Not needed on wxGTK, but
204 # on Windows, although it doesn't work very well there. It seems
205 # that only 16x16 icons are really supported on windows.
206 # We probably shouldn't hardwire the bitmap size here.
207 toolbar.SetToolBitmapSize(wxSize(24, 24))
208
209 for item in toolbardesc.items:
210 if item is None:
211 toolbar.AddSeparator()
212 else:
213 # assume it's a string.
214 self.add_toolbar_command(toolbar, item)
215
216 return toolbar
217
218 def add_menu_command(self, menu, name):
219 """Add the command with name name to the menu menu.
220
221 If name is None, add a separator.
222 """
223 if name is None:
224 menu.AppendSeparator()
225 else:
226 command = registry.Command(name)
227 if command is not None:
228 ID = self.get_id(name)
229 menu.Append(ID, command.Title(), command.HelpText(),
230 command.IsCheckCommand())
231 self.bind_command_events(command, ID)
232 else:
233 print _("Unknown command %s") % name
234
235 def add_toolbar_command(self, toolbar, name):
236 """Add the command with name name to the toolbar toolbar.
237
238 If name is None, add a separator.
239 """
240 # Assume that all toolbar commands are also menu commmands so
241 # that we don't have to add the event handlers here
242 if name is None:
243 toolbar.AddSeparator()
244 else:
245 command = registry.Command(name)
246 if command is not None:
247 ID = self.get_id(name)
248 bitmap = resource.GetBitmapResource(command.Icon(),
249 wxBITMAP_TYPE_XPM)
250 toolbar.AddTool(ID, bitmap,
251 shortHelpString = command.HelpText(),
252 isToggle = command.IsCheckCommand())
253 self.bind_command_events(command, ID)
254 else:
255 print _("Unknown command %s") % name
256
257 def Context(self):
258 """Return the context object for a command invoked from this window
259 """
260 return Context(self.application, self.application.Session(), self)
261
262 def invoke_command(self, event):
263 name = self.id_to_name.get(event.GetId())
264 if name is not None:
265 command = registry.Command(name)
266 command.Execute(self.Context())
267 else:
268 print _("Unknown command ID %d") % event.GetId()
269
270 def update_command_ui(self, event):
271 #print "update_command_ui", self.id_to_name[event.GetId()]
272 context = self.Context()
273 command = registry.Command(self.id_to_name[event.GetId()])
274 if command is not None:
275 sensitive = command.Sensitive(context)
276 event.Enable(sensitive)
277 if command.IsTool() and not sensitive and command.Checked(context):
278 # When a checked tool command is disabled deselect all
279 # tools. Otherwise the tool would remain active but it
280 # might lead to errors if the tools stays active. This
281 # problem occurred in GREAT-ER and this fixes it, but
282 # it's not clear to me whether this is really the best
283 # way to do it (BH, 20021206).
284 self.canvas.SelectTool(None)
285 event.SetText(command.DynText(context))
286 if command.IsCheckCommand():
287 event.Check(command.Checked(context))
288
289 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
290 """Run a modal message box with the given text, title and flags
291 and return the result"""
292 dlg = wxMessageDialog(self, text, title, flags)
293 dlg.CenterOnParent()
294 result = dlg.ShowModal()
295 dlg.Destroy()
296 return result
297
298 def init_dialogs(self):
299 """Initialize the dialog handling"""
300 # The mainwindow maintains a dict mapping names to open
301 # non-modal dialogs. The dialogs are put into this dict when
302 # they're created and removed when they're closed
303 self.dialogs = {}
304
305 def add_dialog(self, name, dialog):
306 if self.dialogs.has_key(name):
307 raise RuntimeError(_("The Dialog named %s is already open") % name)
308 self.dialogs[name] = dialog
309
310 def dialog_open(self, name):
311 return self.dialogs.has_key(name)
312
313 def remove_dialog(self, name):
314 del self.dialogs[name]
315
316 def get_open_dialog(self, name):
317 return self.dialogs.get(name)
318
319 def view_position_changed(self):
320 pos = self.canvas.CurrentPosition()
321 if pos is not None:
322 text = "(%10.10g, %10.10g)" % pos
323 else:
324 text = ""
325 self.set_position_text(text)
326
327 def set_position_text(self, text):
328 """Set the statusbar text showing the current position.
329
330 By default the text is shown in field 0 of the status bar.
331 Override this method in derived classes to put it into a
332 different field of the statusbar.
333 """
334 self.SetStatusText(text)
335
336 def save_modified_session(self, can_veto = 1):
337 """If the current session has been modified, ask the user
338 whether to save it and do so if requested. Return the outcome of
339 the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
340 dialog wasn't run return wxID_NO.
341
342 If the can_veto parameter is true (default) the dialog includes
343 a cancel button, otherwise not.
344 """
345 if self.application.session.WasModified():
346 flags = wxYES_NO | wxICON_QUESTION
347 if can_veto:
348 flags = flags | wxCANCEL
349 result = self.RunMessageBox(_("Exit"),
350 _("The session has been modified."
351 " Do you want to save it?"),
352 flags)
353 if result == wxID_YES:
354 self.SaveSession()
355 else:
356 result = wxID_NO
357 return result
358
359 def prepare_new_session(self):
360 for d in self.dialogs.values():
361 if not isinstance(d, tree.SessionTreeView):
362 d.Close()
363
364 def NewSession(self):
365 if self.save_modified_session() != wxID_CANCEL:
366 self.prepare_new_session()
367 self.application.SetSession(create_empty_session())
368
369 def OpenSession(self):
370 if self.save_modified_session() != wxID_CANCEL:
371 dlg = wxFileDialog(self, _("Open Session"), ".", "",
372 "Thuban Session File (*.thuban)|*.thuban",
373 wxOPEN)
374 if dlg.ShowModal() == wxID_OK:
375 self.prepare_new_session()
376 self.application.OpenSession(dlg.GetPath())
377 dlg.Destroy()
378
379 def SaveSession(self):
380 if self.application.session.filename == None:
381 self.SaveSessionAs()
382 else:
383 self.application.SaveSession()
384
385 def SaveSessionAs(self):
386 dlg = wxFileDialog(self, _("Save Session As"), ".", "",
387 "Thuban Session File (*.thuban)|*.thuban",
388 wxSAVE|wxOVERWRITE_PROMPT)
389 if dlg.ShowModal() == wxID_OK:
390 self.application.session.SetFilename(dlg.GetPath())
391 self.application.SaveSession()
392 dlg.Destroy()
393
394 def Exit(self):
395 self.Close(False)
396
397 def OnClose(self, event):
398 result = self.save_modified_session(can_veto = event.CanVeto())
399 if result == wxID_CANCEL:
400 event.Veto()
401 else:
402 # FIXME: it would be better to tie the unsubscription to
403 # wx's destroy event, but that isn't implemented for wxGTK
404 # yet.
405 self.canvas.Unsubscribe(VIEW_POSITION, self.view_position_changed)
406 DockFrame.OnClose(self, event)
407 for dlg in self.dialogs.values():
408 dlg.Destroy()
409 self.canvas.Destroy()
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 layer = self.canvas.SelectedLayer()
623 if layer is not None:
624 dlg = JoinDialog(self, _("Join Layer with Table"),
625 self.application.session,
626 layer = layer)
627 dlg.ShowModal()
628
629 def LayerUnjoinTable(self):
630 print "LayerUnjoinTable: Not implemented."
631
632 def ShowLegend(self):
633 if not self.LegendShown():
634 self.ToggleLegend()
635
636 def ToggleLegend(self):
637 """Show the legend if it's not shown otherwise hide it again"""
638 name = "legend"
639 dialog = self.FindRegisteredDock(name)
640
641 if dialog is None:
642 dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
643 legend.LegendPanel(dialog, None, self)
644 dialog.Dock()
645 dialog.GetPanel().SetMap(self.Map())
646 dialog.Show()
647 else:
648 dialog.Show(not dialog.IsShown())
649
650 def LegendShown(self):
651 """Return true iff the legend is currently open"""
652 dialog = self.FindRegisteredDock("legend")
653 return dialog is not None and dialog.IsShown()
654
655 def TableOpen(self):
656 dlg = wxFileDialog(self, _("Open Table"), ".", "",
657 _("DBF Files (*.dbf)") + "|*.dbf|" +
658 #_("CSV Files (*.csv)") + "|*.csv|" +
659 _("All Files (*.*)") + "|*.*",
660 wxOPEN)
661 if dlg.ShowModal() == wxID_OK:
662 filename = dlg.GetPath()
663 dlg.Destroy()
664 try:
665 table = self.application.session.OpenTableFile(filename)
666 except IOError:
667 # the layer couldn't be opened
668 self.RunMessageBox(_("Open Table"),
669 _("Can't open the file '%s'.") % filename)
670 else:
671 self.ShowTableView(table)
672
673 def TableClose(self):
674 tables = self.application.session.UnreferencedTables()
675
676 dlg = wxMultipleChoiceDialog(self, _("Pick the tables to close:"),
677 _("Close Table"),
678 [t.Title() for t in tables],
679 size = (400, 300), style=wxRESIZE_BORDER)
680 if dlg.ShowModal() == wxID_OK:
681 for i in dlg.GetValue():
682 self.application.session.RemoveTable(tables[i])
683
684
685 def TableShow(self):
686 """Offer a multi-selection dialog for tables to be displayed
687
688 The windows for the selected tables are opened or brought to
689 the front.
690 """
691 tables = self.application.session.Tables()
692
693 dlg = wxMultipleChoiceDialog(self, _("Pick the table to show:"),
694 _("Show Table"),
695 [t.Title() for t in tables],
696 size = (400,300), style = wxRESIZE_BORDER)
697 if (dlg.ShowModal() == wxID_OK):
698 for i in dlg.GetValue():
699 # XXX: if the table belongs to a layer, open a
700 # LayerTableFrame instead of QueryTableFrame
701 self.ShowTableView(tables[i])
702
703 def TableJoin(self):
704 dlg = JoinDialog(self, _("Join Tables"), self.application.session)
705 dlg.ShowModal()
706
707 def ShowTableView(self, table):
708 """Open a table view for the table and optionally"""
709 name = "table_view%d" % id(table)
710 dialog = self.get_open_dialog(name)
711 if dialog is None:
712 dialog = tableview.QueryTableFrame(self, name,
713 _("Table: %s") % table.Title(),
714 table)
715 self.add_dialog(name, dialog)
716 dialog.Show(True)
717 # FIXME: else bring dialog to front
718
719 def ZoomInTool(self):
720 self.canvas.ZoomInTool()
721
722 def ZoomOutTool(self):
723 self.canvas.ZoomOutTool()
724
725 def PanTool(self):
726 self.canvas.PanTool()
727
728 def IdentifyTool(self):
729 self.canvas.IdentifyTool()
730 self.identify_view_on_demand(None, None)
731
732 def LabelTool(self):
733 self.canvas.LabelTool()
734
735 def FullExtent(self):
736 self.canvas.FitMapToWindow()
737
738 def FullLayerExtent(self):
739 self.canvas.FitLayerToWindow(self.current_layer())
740
741 def FullSelectionExtent(self):
742 self.canvas.FitSelectedToWindow()
743
744 def ExportMap(self):
745 self.canvas.Export()
746
747 def PrintMap(self):
748 self.canvas.Print()
749
750 def RenameMap(self):
751 dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
752 self.Map().Title())
753 if dlg.ShowModal() == wxID_OK:
754 title = dlg.GetValue()
755 if title != "":
756 self.Map().SetTitle(title)
757 self.__SetTitle(title)
758
759 dlg.Destroy()
760
761 def identify_view_on_demand(self, layer, shapes):
762 """Subscribed to the canvas' SHAPES_SELECTED message
763
764 If the current tool is the identify tool, at least one shape is
765 selected and the identify dialog is not shown, show the dialog.
766 """
767 # If the selection has become empty we don't need to do
768 # anything. Otherwise it could happen that the dialog was popped
769 # up when the selection became empty, e.g. when a new selection
770 # is opened while the identify tool is active and dialog had
771 # been closed
772 if not shapes:
773 return
774
775 name = "identify_view"
776 if self.canvas.CurrentTool() == "IdentifyTool":
777 if not self.dialog_open(name):
778 dialog = identifyview.IdentifyView(self, name)
779 self.add_dialog(name, dialog)
780 dialog.Show(True)
781 else:
782 # FIXME: bring dialog to front?
783 pass
784
785 def __SetTitle(self, title):
786 self.SetTitle("Thuban - " + title)
787
788 #
789 # Define all the commands available in the main window
790 #
791
792
793 # Helper functions to define common command implementations
794 def call_method(context, methodname, *args):
795 """Call the mainwindow's method methodname with args *args"""
796 apply(getattr(context.mainwindow, methodname), args)
797
798 def _method_command(name, title, method, helptext = "",
799 icon = "", sensitive = None, checked = None):
800 """Add a command implemented by a method of the mainwindow object"""
801 registry.Add(Command(name, title, call_method, args=(method,),
802 helptext = helptext, icon = icon,
803 sensitive = sensitive, checked = checked))
804
805 def make_check_current_tool(toolname):
806 """Return a function that tests if the currently active tool is toolname
807
808 The returned function can be called with the context and returns
809 true iff the currently active tool's name is toolname. It's directly
810 usable as the 'checked' callback of a command.
811 """
812 def check_current_tool(context, name=toolname):
813 return context.mainwindow.canvas.CurrentTool() == name
814 return check_current_tool
815
816 def _tool_command(name, title, method, toolname, helptext = "",
817 icon = "", sensitive = None):
818 """Add a tool command"""
819 registry.Add(ToolCommand(name, title, call_method, args=(method,),
820 helptext = helptext, icon = icon,
821 checked = make_check_current_tool(toolname),
822 sensitive = sensitive))
823
824 def _has_selected_layer(context):
825 """Return true if a layer is selected in the context"""
826 return context.mainwindow.has_selected_layer()
827
828 def _has_selected_shapes(context):
829 """Return true if a layer is selected in the context"""
830 return context.mainwindow.has_selected_shapes()
831
832 def _can_remove_layer(context):
833 return context.mainwindow.CanRemoveLayer()
834
835 def _has_tree_window_shown(context):
836 """Return true if the tree window is shown"""
837 return context.mainwindow.SessionTreeShown()
838
839 def _has_visible_map(context):
840 """Return true iff theres a visible map in the mainwindow.
841
842 A visible map is a map with at least one visible layer."""
843 map = context.mainwindow.Map()
844 if map is not None:
845 for layer in map.Layers():
846 if layer.Visible():
847 return 1
848 return 0
849
850 def _has_legend_shown(context):
851 """Return true if the legend window is shown"""
852 return context.mainwindow.LegendShown()
853
854
855 # File menu
856 _method_command("new_session", _("&New Session"), "NewSession")
857 _method_command("open_session", _("&Open Session..."), "OpenSession")
858 _method_command("save_session", _("&Save Session"), "SaveSession")
859 _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs")
860 _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
861 checked = _has_tree_window_shown)
862 _method_command("toggle_legend", _("Legend"), "ToggleLegend",
863 checked = _has_legend_shown)
864 _method_command("exit", _("E&xit"), "Exit")
865
866 # Help menu
867 _method_command("help_about", _("&About..."), "About")
868
869
870 # Map menu
871 _method_command("map_projection", _("Pro&jection..."), "MapProjection")
872
873 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
874 helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
875 sensitive = _has_visible_map)
876 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
877 helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
878 sensitive = _has_visible_map)
879 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
880 helptext = _("Switch to map-mode 'pan'"), icon = "pan",
881 sensitive = _has_visible_map)
882 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
883 "IdentifyTool",
884 helptext = _("Switch to map-mode 'identify'"), icon = "identify",
885 sensitive = _has_visible_map)
886 _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
887 helptext = _("Add/Remove labels"), icon = "label",
888 sensitive = _has_visible_map)
889 _method_command("map_full_extent", _("&Full extent"), "FullExtent",
890 helptext = _("Full Extent"), icon = "fullextent",
891 sensitive = _has_visible_map)
892 _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
893 helptext = _("Full Layer Extent"), icon = "fulllayerextent",
894 sensitive = _has_selected_layer)
895 _method_command("selected_full_extent", _("&Full selection extent"), "FullSelectionExtent",
896 helptext = _("Full Selection Extent"), icon = "fullselextent",
897 sensitive = _has_selected_shapes)
898 _method_command("map_export", _("E&xport"), "ExportMap",
899 helptext = _("Export the map to file"))
900 _method_command("map_print", _("Prin&t"), "PrintMap",
901 helptext = _("Print the map"))
902 _method_command("map_rename", _("&Rename..."), "RenameMap",
903 helptext = _("Rename the map"))
904 _method_command("layer_add", _("&Add Layer..."), "AddLayer",
905 helptext = _("Add a new layer to active map"))
906 _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
907 helptext = _("Add a new image layer to active map"))
908 _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
909 helptext = _("Remove selected layer(s)"),
910 sensitive = _can_remove_layer)
911
912 # Layer menu
913 _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
914 sensitive = _has_selected_layer)
915 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
916 helptext = _("Raise selected layer(s)"),
917 sensitive = _has_selected_layer)
918 _method_command("layer_lower", _("&Lower"), "LowerLayer",
919 helptext = _("Lower selected layer(s)"),
920 sensitive = _has_selected_layer)
921 _method_command("layer_show", _("&Show"), "ShowLayer",
922 helptext = _("Make selected layer(s) visible"),
923 sensitive = _has_selected_layer)
924 _method_command("layer_hide", _("&Hide"), "HideLayer",
925 helptext = _("Make selected layer(s) unvisible"),
926 sensitive = _has_selected_layer)
927 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
928 helptext = _("Show the selected layer's table"),
929 sensitive = _has_selected_layer)
930 _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
931 sensitive = _has_selected_layer)
932 _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
933 sensitive = _has_selected_layer)
934 _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
935 sensitive = _has_selected_layer)
936
937 # Table menu
938 _method_command("table_open", _("&Open..."), "TableOpen")
939 _method_command("table_close", _("&Close"), "TableClose",
940 sensitive = lambda context: bool(context.session.UnreferencedTables()))
941 _method_command("table_show", _("&Show"), "TableShow")
942 _method_command("table_join", _("&Join..."), "TableJoin")
943
944 # Export only under Windows ...
945 map_menu = ["layer_add", "rasterlayer_add", "layer_remove", "map_rename",
946 None,
947 "map_projection",
948 None,
949 "map_zoom_in_tool", "map_zoom_out_tool",
950 "map_pan_tool",
951 "map_full_extent",
952 "layer_full_extent",
953 "selected_full_extent",
954 None,
955 "map_identify_tool", "map_label_tool",
956 None,
957 "toggle_legend",
958 None]
959 if wxPlatform == '__WXMSW__':
960 map_menu.append("map_export")
961 map_menu.append("map_print")
962
963 # the menu structure
964 main_menu = Menu("<main>", "<main>",
965 [Menu("file", _("&File"),
966 ["new_session", "open_session", None,
967 "save_session", "save_session_as", None,
968 "toggle_session_tree", None,
969 "exit"]),
970 Menu("map", _("&Map"), map_menu),
971 Menu("layer", _("&Layer"),
972 ["layer_raise", "layer_lower",
973 None,
974 "layer_show", "layer_hide",
975 None,
976 "layer_projection",
977 None,
978 "layer_show_table",
979 "layer_jointable",
980 "layer_unjointable",
981 None,
982 "layer_properties"]),
983 Menu("table", _("&Table"),
984 ["table_open", "table_close",
985 None,
986 "table_show",
987 None,
988 "table_join"]),
989 Menu("help", _("&Help"),
990 ["help_about"])])
991
992 # the main toolbar
993
994 main_toolbar = Menu("<toolbar>", "<toolbar>",
995 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
996 "map_full_extent",
997 "layer_full_extent",
998 "selected_full_extent",
999 None,
1000 "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