/[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 1068 - (show annotations)
Tue May 27 15:02:37 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: 37231 byte(s)
* Thuban/Model/messages.py (TABLE_REMOVED): New message.

* Thuban/Model/session.py (Session.UnreferencedTables): New method
to return tables that are not referenced by other tables or shape
stores and can be removed.
(Session.RemoveTable): Issue a TABLE_REMOVED message after
removing the table

* Thuban/UI/mainwindow.py: Remove unused imports
(MainWindow.TableClose): Implement.

* Thuban/UI/tableview.py (TableFrame.__init__): Subscribe to some
messages so that the frame will be automatically closed when a new
session is opened or the table is removed.
(TableFrame.OnClose): Unsubscribe the Subscriptions made in
__init__
(TableFrame.close_on_session_replaced)
(TableFrame.close_on_table_removed): New. Subscribers that close
the window

* test/test_session.py (TestSessionMessages.test_remove_table)
(TestSessionSimple.test_remove_table): Move the test to
TestSessionSimple and add test for the TABLE_REMOVED message
(TestSessionBase.setUp): Also subscribe to TABLE_REMOVED
(TestSessionSimple.test_unreferenced_tables) New. Test for the
UnreferencedTables method.
(UnreferencedTablesTests): New. Class with some more sophisticated
tests for UnreferencedTables.

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