/[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 1219 - (hide annotations)
Mon Jun 16 17:42:54 2003 UTC (21 years, 8 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 43796 byte(s)
Update to the layer interface: Direct access to the table,
shapetable, shapefile and filename attributes is now actively
deprecated by issuing deprecation warnings for all places where
this happens.

* Thuban/Model/layer.py (Layer.__getattr__): New. Implement access
to the instance variables table, shapetable, shapefile and
filename via __getattr__ so that we can issue a deprecation
warning.
(Layer.SetShapeStore): Don't set the deprecated instance variables
any more
(Layer.SetShapeStore): Don't use deprecated layer instance
variables
(Layer.Destroy): No need to explicitly remove the instance
variables any more
(Layer.GetFieldType, Layer.Shape): Don't use deprecated layer
instance variables

* Thuban/UI/classgen.py (ClassGenDialog.__init__)
(GenUniformPanel._OnRetrieve, GenUniquePanel._OnRetrieve)
(GenQuantilesPanel.GetList, GenQuantilesPanel.OnRetrieve): Don't
use deprecated layer instance variables

* Thuban/UI/classifier.py (Classifier.__init__): Don't use
deprecated layer instance variables

* Thuban/UI/identifyview.py (IdentifyListCtrl.selected_shape)
(IdentifyGridCtrl.selected_shape): Don't set the deprecated layer
instance variables

* Thuban/UI/tableview.py (LayerTableGrid.select_shapes): Don't use
deprecated layer instance variables

* Thuban/UI/mainwindow.py (MainWindow.LayerShowTable): Don't use
deprecated layer instance variables

* Thuban/Model/save.py (SessionSaver.write_layer): Don't use
deprecated layer instance variables

* Thuban/UI/renderer.py (MapRenderer.draw_shape_layer)
(MapRenderer.polygon_render_param): Don't use deprecated layer instance
variables

* test/runtests.py (main): Turn Thuban's deprecation warnings into
errors so that they're cought by the tests

* test/test_load.py (TestSingleLayer.test): Don't use deprecated
layer instance variables

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