/[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 1155 - (hide annotations)
Thu Jun 12 12:17:11 2003 UTC (21 years, 8 months ago) by jan
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 43551 byte(s)
import wxMultipleChoiceDialog from multiplechoicedialog.py rather than
from the wxPython library.

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