/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/mainwindow.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/Thuban/UI/mainwindow.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1074 - (hide annotations)
Tue May 27 17:39:43 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: 37877 byte(s)
* Thuban/UI/mainwindow.py (MainWindow.delegated_messages): Also
delegate SelectedLayer.
(MainWindow.LayerUnjoinTable): Implement.
(_can_unjoin): New. Helper function for the sensitivity of the
layer/unjoin command.

* Thuban/Model/data.py (ShapefileStore.OrigShapeStore)
(DerivedShapeStore.OrigShapeStore): New. Return the original
shapestore. Used to figure out how to unjoin.
(DerivedShapeStore.Shapefile): Fix a typo.

1 bh 535 # Copyright (C) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4     # Bernhard Herzog <[email protected]>
5 frank 911 # Frank Koormann <[email protected]>
6 bh 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 jonathan 517 __ThubanVersion__ = "0.2" #"$THUBAN_0_2$"
17     #__BuildDate__ = "$Date$"
18    
19 bh 188 import os
20 bh 6
21     from wxPython.wx import *
22 frank 923 from wxPython.wx import __version__ as wxPython_version
23 bh 6
24 jan 1014 from wxPython.lib.dialogs import wxMultipleChoiceDialog
25    
26 bh 6 import Thuban
27 frank 923 import Thuban.version
28    
29 jan 374 from Thuban import _
30 bh 188 from Thuban.Model.session import create_empty_session
31 jonathan 937 from Thuban.Model.layer import Layer, RasterLayer
32 bh 6
33     import view
34     import tree
35     import tableview, identifyview
36 jonathan 633 from Thuban.UI.classifier import Classifier
37 jonathan 550 import legend
38 bh 188 from menu import Menu
39 bh 6
40 bh 222 from context import Context
41 bh 357 from command import registry, Command, ToolCommand
42 bh 704 from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION
43 bh 6
44 bh 704 from Thuban.UI.dock import DockFrame
45 jonathan 879 from Thuban.UI.join import JoinDialog
46 bh 6
47 jonathan 653 import resource
48 jonathan 563
49 jonathan 713 import projdialog
50 jonathan 653
51 bh 6
52 jonathan 713
53 jonathan 573 class MainWindow(DockFrame):
54 bh 6
55 bh 535 # 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 bh 1074 "SelectedLayer": "canvas",
68 jonathan 879 "SelectedShapes": "canvas",
69 bh 535 }
70    
71 bh 235 def __init__(self, parent, ID, title, application, interactor,
72 bh 238 initial_message = None, size = wxSize(-1, -1)):
73 jonathan 573 DockFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
74     #wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
75 bh 6
76 bh 227 self.application = application
77 bh 37
78 bh 6 self.CreateStatusBar()
79 bh 235 if initial_message:
80     self.SetStatusText(initial_message)
81 bh 6
82     self.identify_view = None
83    
84     self.init_ids()
85    
86 bh 191 # creat the menubar from the main_menu description
87 bh 188 self.SetMenuBar(self.build_menu_bar(main_menu))
88 bh 6
89 bh 191 # Similarly, create the toolbar from main_toolbar
90     toolbar = self.build_toolbar(main_toolbar)
91 bh 13 # call Realize to make sure that the tools appear.
92     toolbar.Realize()
93 bh 6
94 jonathan 563
95 bh 6 # Create the map canvas
96 bh 535 canvas = view.MapCanvas(self, -1)
97 bh 123 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
98 bh 535 canvas.Subscribe(SHAPES_SELECTED, self.identify_view_on_demand)
99 bh 6 self.canvas = canvas
100    
101 jonathan 573 self.SetMainWindow(self.canvas)
102    
103 jonathan 563 self.SetAutoLayout(True)
104    
105 bh 31 self.init_dialogs()
106    
107 frank 951 EVT_CLOSE(self, self.OnClose)
108 bh 6
109 bh 535 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 bh 6 def init_ids(self):
141     """Initialize the ids"""
142     self.current_id = 6000
143     self.id_to_name = {}
144     self.name_to_id = {}
145 bh 193 self.events_bound = {}
146 bh 6
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 bh 188
159 bh 193 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 bh 188 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 bh 314 """Return a wxMenu built from the menu description menudesc"""
179 bh 188 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 bh 191 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 bh 6 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 bh 193 self.bind_command_events(command, ID)
233 bh 6 else:
234 jan 374 print _("Unknown command %s") % name
235 bh 6
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 jonathan 653 bitmap = resource.GetBitmapResource(command.Icon(),
250     wxBITMAP_TYPE_XPM)
251 bh 6 toolbar.AddTool(ID, bitmap,
252     shortHelpString = command.HelpText(),
253     isToggle = command.IsCheckCommand())
254 bh 193 self.bind_command_events(command, ID)
255 bh 6 else:
256 jan 374 print _("Unknown command %s") % name
257 bh 6
258 bh 281 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 bh 6 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 bh 281 command.Execute(self.Context())
268 bh 6 else:
269 jan 374 print _("Unknown command ID %d") % event.GetId()
270 bh 6
271     def update_command_ui(self, event):
272     #print "update_command_ui", self.id_to_name[event.GetId()]
273 bh 281 context = self.Context()
274 bh 6 command = registry.Command(self.id_to_name[event.GetId()])
275     if command is not None:
276 bh 357 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 bh 222 event.SetText(command.DynText(context))
287 bh 13 if command.IsCheckCommand():
288 bh 357 event.Check(command.Checked(context))
289 bh 6
290 bh 20 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
291 bh 181 """Run a modal message box with the given text, title and flags
292 bh 20 and return the result"""
293     dlg = wxMessageDialog(self, text, title, flags)
294 bh 316 dlg.CenterOnParent()
295 bh 20 result = dlg.ShowModal()
296     dlg.Destroy()
297     return result
298    
299 bh 31 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 jan 374 raise RuntimeError(_("The Dialog named %s is already open") % name)
309 bh 31 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 bh 123 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 bh 321 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 bh 123 self.SetStatusText(text)
336    
337 bh 58 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 bh 227 if self.application.session.WasModified():
347 bh 58 flags = wxYES_NO | wxICON_QUESTION
348     if can_veto:
349     flags = flags | wxCANCEL
350 jan 374 result = self.RunMessageBox(_("Exit"),
351     _("The session has been modified."
352 bh 58 " 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 jonathan 487 def prepare_new_session(self):
361     for d in self.dialogs.values():
362     if not isinstance(d, tree.SessionTreeView):
363 jonathan 502 d.Close()
364 jonathan 487
365 bh 6 def NewSession(self):
366 jonathan 937 if self.save_modified_session() != wxID_CANCEL:
367     self.prepare_new_session()
368     self.application.SetSession(create_empty_session())
369 bh 6
370     def OpenSession(self):
371 jonathan 937 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 bh 6
380     def SaveSession(self):
381 bh 227 if self.application.session.filename == None:
382 jan 102 self.SaveSessionAs()
383 jonathan 487 else:
384     self.application.SaveSession()
385 bh 6
386     def SaveSessionAs(self):
387 jonathan 431 dlg = wxFileDialog(self, _("Save Session As"), ".", "",
388 jonathan 879 "Thuban Session File (*.thuban)|*.thuban",
389     wxSAVE|wxOVERWRITE_PROMPT)
390 bh 6 if dlg.ShowModal() == wxID_OK:
391 bh 227 self.application.session.SetFilename(dlg.GetPath())
392     self.application.SaveSession()
393 bh 6 dlg.Destroy()
394    
395     def Exit(self):
396 jonathan 621 self.Close(False)
397 bh 6
398 frank 951 def OnClose(self, event):
399 bh 58 result = self.save_modified_session(can_veto = event.CanVeto())
400     if result == wxID_CANCEL:
401 bh 6 event.Veto()
402     else:
403 bh 307 # 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 jonathan 974 DockFrame.OnClose(self, event)
408 frank 1056 for dlg in self.dialogs.values():
409     dlg.Destroy()
410     self.canvas.Destroy()
411 bh 6 self.Destroy()
412    
413     def SetMap(self, map):
414     self.canvas.SetMap(map)
415 jonathan 653 self.__SetTitle(map.Title())
416 bh 6
417 jonathan 768 dialog = self.FindRegisteredDock("legend")
418     if dialog is not None:
419     dialog.GetPanel().SetMap(self.Map())
420    
421 bh 310 def Map(self):
422     """Return the map displayed by this mainwindow"""
423 jonathan 563
424 bh 310 return self.canvas.Map()
425    
426 bh 622 def ToggleSessionTree(self):
427     """If the session tree is shown close it otherwise create a new tree"""
428 bh 37 name = "session_tree"
429     dialog = self.get_open_dialog(name)
430     if dialog is None:
431 bh 227 dialog = tree.SessionTreeView(self, self.application, name)
432 bh 37 self.add_dialog(name, dialog)
433 jonathan 512 dialog.Show(True)
434 bh 37 else:
435 bh 622 dialog.Close()
436 bh 37
437 bh 622 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 jonathan 517
441 bh 6 def About(self):
442 jan 374 self.RunMessageBox(_("About"),
443 frank 923 _("Thuban %s\n"
444 jonathan 517 #"Build Date: %s\n"
445 frank 923 "using:\n"
446     " %s\n"
447     " %s\n\n"
448 jonathan 517 "Thuban is a program for\n"
449 bh 20 "exploring geographic data.\n"
450 jan 374 "Copyright (C) 2001-2003 Intevation GmbH.\n"
451 jonathan 517 "Thuban is licensed under the GNU GPL"
452 frank 923 % (Thuban.version.longversion,
453     "wxPython %s" % wxPython_version,
454     "Python %d.%d.%d" % sys.version_info[:3]
455     )),
456     # % __ThubanVersion__), #__BuildDate__)),
457 bh 20 wxOK | wxICON_INFORMATION)
458 bh 6
459     def AddLayer(self):
460 jan 374 dlg = wxFileDialog(self, _("Select a data file"), ".", "", "*.*",
461 bh 6 wxOPEN)
462     if dlg.ShowModal() == wxID_OK:
463     filename = dlg.GetPath()
464     title = os.path.splitext(os.path.basename(filename))[0]
465 bh 18 map = self.canvas.Map()
466     has_layers = map.HasLayers()
467 bh 20 try:
468 jonathan 963 store = self.application.Session().OpenShapefile(filename)
469 bh 20 except IOError:
470     # the layer couldn't be opened
471 jan 374 self.RunMessageBox(_("Add Layer"),
472     _("Can't open the file '%s'.") % filename)
473 bh 20 else:
474 jonathan 963 layer = Layer(title, store)
475     map.AddLayer(layer)
476 bh 20 if not has_layers:
477 bh 535 # if we're adding a layer to an empty map, fit the
478 bh 20 # new map to the window
479     self.canvas.FitMapToWindow()
480 bh 6 dlg.Destroy()
481    
482 jonathan 937 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 jonathan 963 layer = RasterLayer(title, filename)
492 jonathan 937 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 jonathan 963 map.AddLayer(layer)
498 jonathan 937 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 bh 6 def RemoveLayer(self):
505     layer = self.current_layer()
506     if layer is not None:
507     self.canvas.Map().RemoveLayer(layer)
508    
509 bh 299 def CanRemoveLayer(self):
510     """Return true if the currently selected layer can be deleted.
511    
512 jonathan 621 If no layer is selected return False.
513 bh 299
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 jonathan 621 return False
521 bh 299
522 bh 6 def RaiseLayer(self):
523     layer = self.current_layer()
524     if layer is not None:
525     self.canvas.Map().RaiseLayer(layer)
526 bh 222
527 bh 6 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 bh 535 return self.canvas.SelectedLayer()
538 bh 6
539     def has_selected_layer(self):
540     """Return true if a layer is currently selected"""
541 bh 535 return self.canvas.HasSelectedLayer()
542 bh 6
543 jonathan 829 def has_selected_shapes(self):
544     """Return true if a shape is currently selected"""
545     return self.canvas.HasSelectedShapes()
546    
547 bh 6 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 bh 31 table = layer.table
561     name = "table_view" + str(id(table))
562     dialog = self.get_open_dialog(name)
563     if dialog is None:
564 bh 535 dialog = tableview.LayerTableFrame(self, name,
565 jan 1023 _("Layer Table: %s") % layer.Title(),
566     layer, table)
567 bh 31 self.add_dialog(name, dialog)
568 jan 1035 dialog.Show(True)
569 bh 31 else:
570     # FIXME: bring dialog to front here
571     pass
572 bh 6
573 jonathan 729 def MapProjection(self):
574 bh 6
575 jonathan 729 name = "map_projection"
576 jonathan 713 dialog = self.get_open_dialog(name)
577    
578     if dialog is None:
579     map = self.canvas.Map()
580 jonathan 750 dialog = projdialog.ProjFrame(self, name,
581     _("Map Projection: %s") % map.Title(), map)
582 jonathan 713 self.add_dialog(name, dialog)
583     dialog.Show()
584     dialog.Raise()
585    
586 jonathan 729 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 jonathan 750 dialog = projdialog.ProjFrame(self, name,
596     _("Layer Projection: %s") % layer.Title(), layer)
597 jonathan 729 self.add_dialog(name, dialog)
598     dialog.Show()
599     dialog.Raise()
600    
601 jonathan 640 def LayerEditProperties(self):
602 jonathan 363
603 jonathan 487 #
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 jonathan 640 self.OpenLayerProperties(layer)
611 jonathan 550
612 jonathan 640 def OpenLayerProperties(self, layer, group = None):
613     name = "layer_properties" + str(id(layer))
614 jonathan 487 dialog = self.get_open_dialog(name)
615    
616     if dialog is None:
617 jonathan 633 dialog = Classifier(self, name, layer, group)
618 jonathan 487 self.add_dialog(name, dialog)
619     dialog.Show()
620 jonathan 573 dialog.Raise()
621 jonathan 487
622 jonathan 879 def LayerJoinTable(self):
623 bh 1072 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 jonathan 550
630 jonathan 879 def LayerUnjoinTable(self):
631 bh 1074 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 jonathan 879
637 jonathan 621 def ShowLegend(self):
638 bh 622 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 jonathan 550 name = "legend"
644 jonathan 573 dialog = self.FindRegisteredDock(name)
645 jonathan 550
646     if dialog is None:
647 jonathan 640 dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
648 jonathan 573 legend.LegendPanel(dialog, None, self)
649 jonathan 580 dialog.Dock()
650 bh 622 dialog.GetPanel().SetMap(self.Map())
651     dialog.Show()
652     else:
653     dialog.Show(not dialog.IsShown())
654 jonathan 563
655 bh 622 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 jonathan 563
660 jonathan 879 def TableOpen(self):
661     dlg = wxFileDialog(self, _("Open Table"), ".", "",
662 bh 1037 _("DBF Files (*.dbf)") + "|*.dbf|" +
663     #_("CSV Files (*.csv)") + "|*.csv|" +
664     _("All Files (*.*)") + "|*.*",
665 jonathan 879 wxOPEN)
666     if dlg.ShowModal() == wxID_OK:
667 bh 1054 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 jonathan 879
678     def TableClose(self):
679 bh 1068 tables = self.application.session.UnreferencedTables()
680 jonathan 879
681 bh 1068 dlg = wxMultipleChoiceDialog(self, _("Pick the tables to close:"),
682     _("Close Table"),
683     [t.Title() for t in tables],
684     size = (400, 300), style=wxRESIZE_BORDER)
685     if dlg.ShowModal() == wxID_OK:
686     for i in dlg.GetValue():
687     self.application.session.RemoveTable(tables[i])
688    
689    
690 jonathan 879 def TableShow(self):
691 jan 1014 """Offer a multi-selection dialog for tables to be displayed
692 bh 1054
693 jan 1014 The windows for the selected tables are opened or brought to
694     the front.
695     """
696     tables = self.application.session.Tables()
697 jonathan 879
698 jan 1014 dlg = wxMultipleChoiceDialog(self, _("Pick the table to show:"),
699 bh 1054 _("Show Table"),
700     [t.Title() for t in tables],
701 jan 1031 size = (400,300), style = wxRESIZE_BORDER)
702 jan 1014 if (dlg.ShowModal() == wxID_OK):
703     for i in dlg.GetValue():
704 jan 1023 # XXX: if the table belongs to a layer, open a
705     # LayerTableFrame instead of QueryTableFrame
706 bh 1054 self.ShowTableView(tables[i])
707 jan 1014
708 jonathan 879 def TableJoin(self):
709     dlg = JoinDialog(self, _("Join Tables"), self.application.session)
710 frank 1001 dlg.ShowModal()
711 jonathan 879
712 bh 1054 def ShowTableView(self, table):
713     """Open a table view for the table and optionally"""
714     name = "table_view%d" % id(table)
715     dialog = self.get_open_dialog(name)
716     if dialog is None:
717     dialog = tableview.QueryTableFrame(self, name,
718     _("Table: %s") % table.Title(),
719     table)
720     self.add_dialog(name, dialog)
721     dialog.Show(True)
722     # FIXME: else bring dialog to front
723    
724 bh 6 def ZoomInTool(self):
725     self.canvas.ZoomInTool()
726    
727     def ZoomOutTool(self):
728     self.canvas.ZoomOutTool()
729    
730     def PanTool(self):
731     self.canvas.PanTool()
732    
733     def IdentifyTool(self):
734     self.canvas.IdentifyTool()
735 bh 49 self.identify_view_on_demand(None, None)
736 bh 6
737     def LabelTool(self):
738     self.canvas.LabelTool()
739    
740     def FullExtent(self):
741     self.canvas.FitMapToWindow()
742    
743 jonathan 821 def FullLayerExtent(self):
744     self.canvas.FitLayerToWindow(self.current_layer())
745    
746 jonathan 829 def FullSelectionExtent(self):
747     self.canvas.FitSelectedToWindow()
748    
749 frank 911 def ExportMap(self):
750     self.canvas.Export()
751    
752 bh 6 def PrintMap(self):
753     self.canvas.Print()
754    
755 jonathan 653 def RenameMap(self):
756     dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
757     self.Map().Title())
758     if dlg.ShowModal() == wxID_OK:
759     title = dlg.GetValue()
760     if title != "":
761     self.Map().SetTitle(title)
762     self.__SetTitle(title)
763    
764     dlg.Destroy()
765    
766 bh 535 def identify_view_on_demand(self, layer, shapes):
767 bh 787 """Subscribed to the canvas' SHAPES_SELECTED message
768    
769     If the current tool is the identify tool, at least one shape is
770     selected and the identify dialog is not shown, show the dialog.
771     """
772     # If the selection has become empty we don't need to do
773     # anything. Otherwise it could happen that the dialog was popped
774     # up when the selection became empty, e.g. when a new selection
775     # is opened while the identify tool is active and dialog had
776     # been closed
777     if not shapes:
778     return
779    
780 bh 31 name = "identify_view"
781     if self.canvas.CurrentTool() == "IdentifyTool":
782     if not self.dialog_open(name):
783 bh 535 dialog = identifyview.IdentifyView(self, name)
784 bh 31 self.add_dialog(name, dialog)
785 jonathan 563 dialog.Show(True)
786 bh 31 else:
787 bh 33 # FIXME: bring dialog to front?
788 bh 31 pass
789 bh 6
790 jonathan 653 def __SetTitle(self, title):
791     self.SetTitle("Thuban - " + title)
792    
793 bh 6 #
794     # Define all the commands available in the main window
795     #
796    
797    
798     # Helper functions to define common command implementations
799     def call_method(context, methodname, *args):
800 bh 222 """Call the mainwindow's method methodname with args *args"""
801     apply(getattr(context.mainwindow, methodname), args)
802 bh 6
803 jan 110 def _method_command(name, title, method, helptext = "",
804 bh 622 icon = "", sensitive = None, checked = None):
805 bh 222 """Add a command implemented by a method of the mainwindow object"""
806 bh 6 registry.Add(Command(name, title, call_method, args=(method,),
807 jan 110 helptext = helptext, icon = icon,
808 bh 622 sensitive = sensitive, checked = checked))
809 jan 110
810 bh 270 def make_check_current_tool(toolname):
811     """Return a function that tests if the currently active tool is toolname
812    
813     The returned function can be called with the context and returns
814     true iff the currently active tool's name is toolname. It's directly
815     usable as the 'checked' callback of a command.
816     """
817     def check_current_tool(context, name=toolname):
818     return context.mainwindow.canvas.CurrentTool() == name
819     return check_current_tool
820    
821 bh 6 def _tool_command(name, title, method, toolname, helptext = "",
822 bh 310 icon = "", sensitive = None):
823 bh 6 """Add a tool command"""
824 bh 357 registry.Add(ToolCommand(name, title, call_method, args=(method,),
825     helptext = helptext, icon = icon,
826     checked = make_check_current_tool(toolname),
827     sensitive = sensitive))
828 bh 6
829     def _has_selected_layer(context):
830     """Return true if a layer is selected in the context"""
831 bh 222 return context.mainwindow.has_selected_layer()
832 bh 6
833 jonathan 829 def _has_selected_shapes(context):
834     """Return true if a layer is selected in the context"""
835     return context.mainwindow.has_selected_shapes()
836    
837 bh 299 def _can_remove_layer(context):
838     return context.mainwindow.CanRemoveLayer()
839    
840 jan 264 def _has_tree_window_shown(context):
841     """Return true if the tree window is shown"""
842 bh 622 return context.mainwindow.SessionTreeShown()
843 jan 264
844 bh 310 def _has_visible_map(context):
845     """Return true iff theres a visible map in the mainwindow.
846    
847     A visible map is a map with at least one visible layer."""
848     map = context.mainwindow.Map()
849     if map is not None:
850     for layer in map.Layers():
851     if layer.Visible():
852     return 1
853     return 0
854    
855 jonathan 550 def _has_legend_shown(context):
856     """Return true if the legend window is shown"""
857 bh 622 return context.mainwindow.LegendShown()
858 bh 310
859 jonathan 550
860 bh 6 # File menu
861 jan 374 _method_command("new_session", _("&New Session"), "NewSession")
862 jonathan 815 _method_command("open_session", _("&Open Session..."), "OpenSession")
863 jan 374 _method_command("save_session", _("&Save Session"), "SaveSession")
864 jonathan 815 _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs")
865 bh 622 _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
866     checked = _has_tree_window_shown)
867     _method_command("toggle_legend", _("Legend"), "ToggleLegend",
868     checked = _has_legend_shown)
869 jan 374 _method_command("exit", _("E&xit"), "Exit")
870 bh 6
871     # Help menu
872 jonathan 815 _method_command("help_about", _("&About..."), "About")
873 bh 6
874    
875     # Map menu
876 jonathan 815 _method_command("map_projection", _("Pro&jection..."), "MapProjection")
877 bh 6
878 jan 374 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
879     helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
880 bh 310 sensitive = _has_visible_map)
881 jan 374 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
882     helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
883 bh 310 sensitive = _has_visible_map)
884 jan 374 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
885     helptext = _("Switch to map-mode 'pan'"), icon = "pan",
886 bh 310 sensitive = _has_visible_map)
887 jan 374 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
888     "IdentifyTool",
889     helptext = _("Switch to map-mode 'identify'"), icon = "identify",
890 bh 310 sensitive = _has_visible_map)
891 jan 374 _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
892     helptext = _("Add/Remove labels"), icon = "label",
893 bh 310 sensitive = _has_visible_map)
894 jan 374 _method_command("map_full_extent", _("&Full extent"), "FullExtent",
895     helptext = _("Full Extent"), icon = "fullextent",
896 bh 310 sensitive = _has_visible_map)
897 jonathan 821 _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
898 jonathan 829 helptext = _("Full Layer Extent"), icon = "fulllayerextent",
899 jonathan 821 sensitive = _has_selected_layer)
900 jonathan 829 _method_command("selected_full_extent", _("&Full selection extent"), "FullSelectionExtent",
901     helptext = _("Full Selection Extent"), icon = "fullselextent",
902     sensitive = _has_selected_shapes)
903 frank 911 _method_command("map_export", _("E&xport"), "ExportMap",
904     helptext = _("Export the map to file"))
905 jan 374 _method_command("map_print", _("Prin&t"), "PrintMap",
906     helptext = _("Print the map"))
907 jonathan 815 _method_command("map_rename", _("&Rename..."), "RenameMap",
908 jonathan 653 helptext = _("Rename the map"))
909 jonathan 815 _method_command("layer_add", _("&Add Layer..."), "AddLayer",
910 jan 374 helptext = _("Add a new layer to active map"))
911 jonathan 937 _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
912     helptext = _("Add a new image layer to active map"))
913 jan 374 _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
914     helptext = _("Remove selected layer(s)"),
915 bh 299 sensitive = _can_remove_layer)
916 jonathan 729
917     # Layer menu
918 jonathan 815 _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
919 jonathan 729 sensitive = _has_selected_layer)
920 jan 374 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
921     helptext = _("Raise selected layer(s)"),
922 bh 6 sensitive = _has_selected_layer)
923 jan 374 _method_command("layer_lower", _("&Lower"), "LowerLayer",
924     helptext = _("Lower selected layer(s)"),
925 bh 6 sensitive = _has_selected_layer)
926 jan 374 _method_command("layer_show", _("&Show"), "ShowLayer",
927     helptext = _("Make selected layer(s) visible"),
928 bh 6 sensitive = _has_selected_layer)
929 jan 374 _method_command("layer_hide", _("&Hide"), "HideLayer",
930     helptext = _("Make selected layer(s) unvisible"),
931 bh 6 sensitive = _has_selected_layer)
932 jan 374 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
933     helptext = _("Show the selected layer's table"),
934 bh 6 sensitive = _has_selected_layer)
935 jonathan 815 _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
936 jonathan 363 sensitive = _has_selected_layer)
937 jonathan 879 _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
938     sensitive = _has_selected_layer)
939 bh 1074
940     def _can_unjoin(context):
941     layer = context.mainwindow.SelectedLayer()
942     return bool(layer and layer.ShapeStore().OrigShapeStore() is not None)
943 jonathan 879 _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
944 bh 1074 sensitive = _can_unjoin)
945 bh 188
946 jonathan 879 # Table menu
947     _method_command("table_open", _("&Open..."), "TableOpen")
948 bh 1070 _method_command("table_close", _("&Close"), "TableClose",
949     sensitive = lambda context: bool(context.session.UnreferencedTables()))
950 jonathan 879 _method_command("table_show", _("&Show"), "TableShow")
951     _method_command("table_join", _("&Join..."), "TableJoin")
952    
953 frank 911 # Export only under Windows ...
954 jonathan 937 map_menu = ["layer_add", "rasterlayer_add", "layer_remove", "map_rename",
955 bh 188 None,
956     "map_projection",
957     None,
958     "map_zoom_in_tool", "map_zoom_out_tool",
959 jonathan 829 "map_pan_tool",
960     "map_full_extent",
961     "layer_full_extent",
962     "selected_full_extent",
963 bh 188 None,
964 jonathan 815 "map_identify_tool", "map_label_tool",
965 bh 188 None,
966 bh 624 "toggle_legend",
967 frank 911 None]
968     if wxPlatform == '__WXMSW__':
969     map_menu.append("map_export")
970     map_menu.append("map_print")
971    
972     # the menu structure
973     main_menu = Menu("<main>", "<main>",
974     [Menu("file", _("&File"),
975     ["new_session", "open_session", None,
976     "save_session", "save_session_as", None,
977     "toggle_session_tree", None,
978     "exit"]),
979     Menu("map", _("&Map"), map_menu),
980 jan 374 Menu("layer", _("&Layer"),
981 jonathan 640 ["layer_raise", "layer_lower",
982 bh 188 None,
983     "layer_show", "layer_hide",
984     None,
985 jonathan 879 "layer_projection",
986     None,
987 jonathan 363 "layer_show_table",
988 jonathan 879 "layer_jointable",
989     "layer_unjointable",
990 jonathan 363 None,
991 jonathan 815 "layer_properties"]),
992 jonathan 879 Menu("table", _("&Table"),
993     ["table_open", "table_close",
994     None,
995 bh 1052 "table_show",
996 jonathan 879 None,
997     "table_join"]),
998 jan 374 Menu("help", _("&Help"),
999 bh 188 ["help_about"])])
1000 bh 191
1001     # the main toolbar
1002    
1003     main_toolbar = Menu("<toolbar>", "<toolbar>",
1004 frank 351 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
1005 jonathan 829 "map_full_extent",
1006     "layer_full_extent",
1007     "selected_full_extent",
1008     None,
1009 frank 351 "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