/[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 1393 - (hide annotations)
Thu Jul 10 14:54:41 2003 UTC (21 years, 8 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 43046 byte(s)
(MainWindow.ShowTableView): Raise
        the table window when it is selectd to be shown.

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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26