/[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 1068 - (hide annotations)
Tue May 27 15:02:37 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 37231 byte(s)
* Thuban/Model/messages.py (TABLE_REMOVED): New message.

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

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

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

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

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