/[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 222 - (hide annotations)
Thu Jul 18 13:03:08 2002 UTC (22 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 22637 byte(s)
	* Thuban/UI/mainwindow.py (MainWindow.invoke_command,
	MainWindow.update_command_ui): Pass an instance of the context
	class to the command's methods
	(check_current_tool, call_method): Handle the new context
	implementation

1 bh 123 # Copyright (C) 2001, 2002 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4     # Bernhard Herzog <[email protected]>
5     #
6     # This program is free software under the GPL (>=v2)
7     # Read the file COPYING coming with Thuban for details.
8    
9     """
10     The main window
11     """
12    
13     __version__ = "$Revision$"
14    
15 bh 188 import os
16 bh 6
17     from wxPython.wx import *
18    
19     import Thuban
20 bh 188 from Thuban.Model.session import create_empty_session
21 bh 6 from Thuban.Model.layer import Layer
22     from Thuban.Model.color import Color
23     from Thuban.Model.proj import Projection
24    
25     import view
26     import tree
27     import proj4dialog
28     import tableview, identifyview
29 bh 188 from menu import Menu
30 bh 6
31     import main
32 bh 222 from context import Context
33 bh 6 from command import registry, Command
34 bh 123 from messages import SELECTED_SHAPE, VIEW_POSITION
35 bh 6
36    
37     # the directory where the toolbar icons are stored
38     bitmapdir = os.path.join(Thuban.__path__[0], os.pardir, "Resources", "Bitmaps")
39     bitmapext = ".xpm"
40    
41    
42     class MainWindow(wxFrame):
43    
44 bh 24 def __init__(self, parent, ID, interactor):
45 bh 6 wxFrame.__init__(self, parent, ID, 'Thuban',
46     wxDefaultPosition, wxSize(400, 300))
47    
48 bh 37 self.interactor = interactor
49    
50 bh 6 self.CreateStatusBar()
51     self.SetStatusText("This is the wxPython-based "
52     "Graphical User Interface for exploring geographic data")
53    
54     self.identify_view = None
55    
56     self.init_ids()
57    
58 bh 191 # creat the menubar from the main_menu description
59 bh 188 self.SetMenuBar(self.build_menu_bar(main_menu))
60 bh 6
61 bh 191 # Similarly, create the toolbar from main_toolbar
62     toolbar = self.build_toolbar(main_toolbar)
63 bh 13 # call Realize to make sure that the tools appear.
64     toolbar.Realize()
65 bh 6
66     # Create the map canvas
67 bh 24 canvas = view.MapCanvas(self, -1, interactor)
68 bh 123 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
69 bh 6 self.canvas = canvas
70    
71 bh 31 self.init_dialogs()
72    
73     interactor.Subscribe(SELECTED_SHAPE, self.identify_view_on_demand)
74    
75 bh 6 EVT_CLOSE(self, self.OnClose)
76    
77     def init_ids(self):
78     """Initialize the ids"""
79     self.current_id = 6000
80     self.id_to_name = {}
81     self.name_to_id = {}
82 bh 193 self.events_bound = {}
83 bh 6
84     def get_id(self, name):
85     """Return the wxWindows id for the command named name.
86    
87     Create a new one if there isn't one yet"""
88     ID = self.name_to_id.get(name)
89     if ID is None:
90     ID = self.current_id
91     self.current_id = self.current_id + 1
92     self.name_to_id[name] = ID
93     self.id_to_name[ID] = name
94     return ID
95 bh 188
96 bh 193 def bind_command_events(self, command, ID):
97     """Bind the necessary events for the given command and ID"""
98     if not self.events_bound.has_key(ID):
99     # the events haven't been bound yet
100     EVT_MENU(self, ID, self.invoke_command)
101     if command.IsDynamic():
102     EVT_UPDATE_UI(self, ID, self.update_command_ui)
103    
104 bh 188 def build_menu_bar(self, menudesc):
105     """Build and return the menu bar from the menu description"""
106     menu_bar = wxMenuBar()
107    
108     for item in menudesc.items:
109     # here the items must all be Menu instances themselves
110     menu_bar.Append(self.build_menu(item), item.title)
111    
112     return menu_bar
113    
114     def build_menu(self, menudesc):
115     """Build and return a wxMenu from a menudescription"""
116     wxmenu = wxMenu()
117     last = None
118     for item in menudesc.items:
119     # here the items must all be Menu instances themselves
120     if item is None:
121     # a separator. Only add one if the last item was not a
122     # separator
123     if last is not None:
124     wxmenu.AppendSeparator()
125     elif isinstance(item, Menu):
126     # a submenu
127     wxmenu.AppendMenu(wxNewId(), item.title, self.build_menu(item))
128     else:
129     # must the name the name of a command
130     self.add_menu_command(wxmenu, item)
131     last = item
132     return wxmenu
133    
134 bh 191 def build_toolbar(self, toolbardesc):
135     """Build and return the main toolbar window from a toolbar description
136    
137     The parameter should be an instance of the Menu class but it
138     should not contain submenus.
139     """
140     toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
141    
142     # set the size of the tools' bitmaps. Not needed on wxGTK, but
143     # on Windows, although it doesn't work very well there. It seems
144     # that only 16x16 icons are really supported on windows.
145     # We probably shouldn't hardwire the bitmap size here.
146     toolbar.SetToolBitmapSize(wxSize(24, 24))
147    
148     for item in toolbardesc.items:
149     if item is None:
150     toolbar.AddSeparator()
151     else:
152     # assume it's a string.
153     self.add_toolbar_command(toolbar, item)
154    
155     return toolbar
156    
157 bh 6 def add_menu_command(self, menu, name):
158     """Add the command with name name to the menu menu.
159    
160     If name is None, add a separator.
161     """
162     if name is None:
163     menu.AppendSeparator()
164     else:
165     command = registry.Command(name)
166     if command is not None:
167     ID = self.get_id(name)
168     menu.Append(ID, command.Title(), command.HelpText(),
169     command.IsCheckCommand())
170 bh 193 self.bind_command_events(command, ID)
171 bh 6 else:
172     print "Unknown command %s" % name
173    
174     def add_toolbar_command(self, toolbar, name):
175     """Add the command with name name to the toolbar toolbar.
176    
177     If name is None, add a separator.
178     """
179     # Assume that all toolbar commands are also menu commmands so
180     # that we don't have to add the event handlers here
181     if name is None:
182     toolbar.AddSeparator()
183     else:
184     command = registry.Command(name)
185     if command is not None:
186     ID = self.get_id(name)
187     filename = os.path.join(bitmapdir, command.Icon()) + bitmapext
188     bitmap = wxBitmap(filename, wxBITMAP_TYPE_XPM)
189     toolbar.AddTool(ID, bitmap,
190     shortHelpString = command.HelpText(),
191     isToggle = command.IsCheckCommand())
192 bh 193 self.bind_command_events(command, ID)
193 bh 6 else:
194     print "Unknown command %s" % name
195    
196     def invoke_command(self, event):
197     name = self.id_to_name.get(event.GetId())
198     if name is not None:
199     command = registry.Command(name)
200 bh 222 command.Execute(Context(main.app, main.app.Session(), self))
201 bh 6 else:
202     print "Unknown command ID %d" % event.GetId()
203    
204     def update_command_ui(self, event):
205     #print "update_command_ui", self.id_to_name[event.GetId()]
206 bh 222 context = Context(main.app, main.app.Session(), self)
207 bh 6 command = registry.Command(self.id_to_name[event.GetId()])
208     if command is not None:
209 bh 222 event.Enable(command.Sensitive(context))
210     event.SetText(command.DynText(context))
211 bh 13 if command.IsCheckCommand():
212 bh 222 event.Check(command.Checked(context))
213 bh 6
214 bh 20 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
215 bh 181 """Run a modal message box with the given text, title and flags
216 bh 20 and return the result"""
217     dlg = wxMessageDialog(self, text, title, flags)
218     result = dlg.ShowModal()
219     dlg.Destroy()
220     return result
221    
222 bh 31 def init_dialogs(self):
223     """Initialize the dialog handling"""
224     # The mainwindow maintains a dict mapping names to open
225     # non-modal dialogs. The dialogs are put into this dict when
226     # they're created and removed when they're closed
227     self.dialogs = {}
228    
229     def add_dialog(self, name, dialog):
230     if self.dialogs.has_key(name):
231     raise RuntimeError("The Dialog named %s is already open" % name)
232     self.dialogs[name] = dialog
233    
234     def dialog_open(self, name):
235     return self.dialogs.has_key(name)
236    
237     def remove_dialog(self, name):
238     del self.dialogs[name]
239    
240     def get_open_dialog(self, name):
241     return self.dialogs.get(name)
242    
243 bh 123 def view_position_changed(self):
244     pos = self.canvas.CurrentPosition()
245     if pos is not None:
246     text = "(%10.10g, %10.10g)" % pos
247     else:
248     text = ""
249     self.SetStatusText(text)
250    
251 bh 58 def save_modified_session(self, can_veto = 1):
252     """If the current session has been modified, ask the user
253     whether to save it and do so if requested. Return the outcome of
254     the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
255     dialog wasn't run return wxID_NO.
256    
257     If the can_veto parameter is true (default) the dialog includes
258     a cancel button, otherwise not.
259     """
260     if main.app.session.WasModified():
261     flags = wxYES_NO | wxICON_QUESTION
262     if can_veto:
263     flags = flags | wxCANCEL
264     result = self.RunMessageBox("Exit",
265     ("The session has been modified."
266     " Do you want to save it?"),
267     flags)
268     if result == wxID_YES:
269     self.SaveSession()
270     else:
271     result = wxID_NO
272     return result
273    
274 bh 6 def NewSession(self):
275 bh 58 self.save_modified_session()
276     main.app.SetSession(create_empty_session())
277 bh 6
278     def OpenSession(self):
279 bh 58 self.save_modified_session()
280 bh 6 dlg = wxFileDialog(self, "Select a session file", ".", "",
281 bh 130 "*.thuban", wxOPEN)
282 bh 6 if dlg.ShowModal() == wxID_OK:
283     main.app.OpenSession(dlg.GetPath())
284     dlg.Destroy()
285    
286     def SaveSession(self):
287 jan 102 if main.app.session.filename == None:
288     self.SaveSessionAs()
289 bh 6 main.app.SaveSession()
290    
291     def SaveSessionAs(self):
292     dlg = wxFileDialog(self, "Enter a filename for session", ".", "",
293 bh 130 "*.thuban", wxOPEN)
294 bh 6 if dlg.ShowModal() == wxID_OK:
295     main.app.session.SetFilename(dlg.GetPath())
296     main.app.SaveSession()
297     dlg.Destroy()
298    
299     def Exit(self):
300     self.Close(false)
301    
302     def OnClose(self, event):
303 bh 58 result = self.save_modified_session(can_veto = event.CanVeto())
304     if result == wxID_CANCEL:
305 bh 6 event.Veto()
306     else:
307     self.Destroy()
308    
309     def SetMap(self, map):
310     self.canvas.SetMap(map)
311    
312 bh 37 def ShowSessionTree(self):
313     name = "session_tree"
314     dialog = self.get_open_dialog(name)
315     if dialog is None:
316     dialog = tree.SessionTreeView(self, main.app, name)
317     self.add_dialog(name, dialog)
318     dialog.Show(true)
319     else:
320     # FIXME: bring dialog to front here
321     pass
322    
323 bh 6 def About(self):
324 bh 20 self.RunMessageBox("About",
325     ("Thuban is a program for\n"
326     "exploring geographic data.\n"
327     "Copyright (C) 2001 Intevation GmbH.\n"
328     "Thuban is licensed under the GPL"),
329     wxOK | wxICON_INFORMATION)
330 bh 6
331     def AddLayer(self):
332 frank 119 dlg = wxFileDialog(self, "Select a data file", ".", "", "*.*",
333 bh 6 wxOPEN)
334     if dlg.ShowModal() == wxID_OK:
335     filename = dlg.GetPath()
336     title = os.path.splitext(os.path.basename(filename))[0]
337     layer = Layer(title, filename)
338 bh 18 map = self.canvas.Map()
339     has_layers = map.HasLayers()
340 bh 20 try:
341     map.AddLayer(layer)
342     except IOError:
343     # the layer couldn't be opened
344     self.RunMessageBox("Add Layer",
345     "Can't open the file '%s'." % filename)
346     else:
347     if not has_layers:
348     # if we're adding a layer to an empty map, for the
349     # new map to the window
350     self.canvas.FitMapToWindow()
351 bh 6 dlg.Destroy()
352    
353     def RemoveLayer(self):
354     layer = self.current_layer()
355     if layer is not None:
356     self.canvas.Map().RemoveLayer(layer)
357    
358     def RaiseLayer(self):
359     layer = self.current_layer()
360     if layer is not None:
361     self.canvas.Map().RaiseLayer(layer)
362 bh 222
363 bh 6 def LowerLayer(self):
364     layer = self.current_layer()
365     if layer is not None:
366     self.canvas.Map().LowerLayer(layer)
367    
368     def current_layer(self):
369     """Return the currently selected layer.
370    
371     If no layer is selected, return None
372     """
373 bh 37 return self.interactor.SelectedLayer()
374 bh 6
375     def has_selected_layer(self):
376     """Return true if a layer is currently selected"""
377 bh 37 return self.interactor.HasSelectedLayer()
378 bh 6
379     def choose_color(self):
380     """Run the color selection dialog and return the selected color.
381    
382     If the user cancels, return None.
383     """
384     dlg = wxColourDialog(self)
385     color = None
386     if dlg.ShowModal() == wxID_OK:
387     data = dlg.GetColourData()
388     wxc = data.GetColour()
389     color = Color(wxc.Red() / 255.0,
390     wxc.Green() / 255.0,
391     wxc.Blue() / 255.0)
392     dlg.Destroy()
393     return color
394    
395     def LayerFillColor(self):
396     layer = self.current_layer()
397     if layer is not None:
398     color = self.choose_color()
399     if color is not None:
400     layer.SetFill(color)
401    
402     def LayerTransparentFill(self):
403     layer = self.current_layer()
404     if layer is not None:
405     layer.SetFill(None)
406    
407     def LayerOutlineColor(self):
408     layer = self.current_layer()
409     if layer is not None:
410     color = self.choose_color()
411     if color is not None:
412     layer.SetStroke(color)
413    
414     def LayerNoOutline(self):
415     layer = self.current_layer()
416     if layer is not None:
417     layer.SetStroke(None)
418    
419     def HideLayer(self):
420     layer = self.current_layer()
421     if layer is not None:
422     layer.SetVisible(0)
423    
424     def ShowLayer(self):
425     layer = self.current_layer()
426     if layer is not None:
427     layer.SetVisible(1)
428    
429     def LayerShowTable(self):
430     layer = self.current_layer()
431     if layer is not None:
432 bh 31 table = layer.table
433     name = "table_view" + str(id(table))
434     dialog = self.get_open_dialog(name)
435     if dialog is None:
436 bh 37 dialog = tableview.TableFrame(self, self.interactor, name,
437 bh 31 "Table: %s" % layer.Title(),
438 bh 33 layer, table)
439 bh 31 self.add_dialog(name, dialog)
440     dialog.Show(true)
441     else:
442     # FIXME: bring dialog to front here
443     pass
444 bh 6
445     def Projection(self):
446     map = self.canvas.Map()
447     proj = map.projection
448     if proj is None:
449 jan 110 proj4Dlg = proj4dialog.Proj4Dialog(NULL, None, map.BoundingBox())
450 bh 6 else:
451 jan 110 proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params,
452     map.BoundingBox())
453 bh 6 if proj4Dlg.ShowModal() == wxID_OK:
454     params = proj4Dlg.GetParams()
455     if params is not None:
456     proj = Projection(params)
457     else:
458     proj = None
459     map.SetProjection(proj)
460     proj4Dlg.Destroy()
461    
462     def ZoomInTool(self):
463     self.canvas.ZoomInTool()
464    
465     def ZoomOutTool(self):
466     self.canvas.ZoomOutTool()
467    
468     def PanTool(self):
469     self.canvas.PanTool()
470    
471     def IdentifyTool(self):
472     self.canvas.IdentifyTool()
473 bh 49 self.identify_view_on_demand(None, None)
474 bh 6
475     def LabelTool(self):
476     self.canvas.LabelTool()
477    
478     def FullExtent(self):
479     self.canvas.FitMapToWindow()
480    
481     def PrintMap(self):
482     self.canvas.Print()
483    
484 bh 31 def identify_view_on_demand(self, layer, shape):
485     name = "identify_view"
486     if self.canvas.CurrentTool() == "IdentifyTool":
487     if not self.dialog_open(name):
488 bh 37 dialog = identifyview.IdentifyView(self, self.interactor, name)
489 bh 31 self.add_dialog(name, dialog)
490     dialog.Show(true)
491     else:
492 bh 33 # FIXME: bring dialog to front?
493 bh 31 pass
494 bh 6
495     #
496     # Define all the commands available in the main window
497     #
498    
499    
500     # Helper functions to define common command implementations
501     def call_method(context, methodname, *args):
502 bh 222 """Call the mainwindow's method methodname with args *args"""
503     apply(getattr(context.mainwindow, methodname), args)
504 bh 6
505 jan 110 def _method_command(name, title, method, helptext = "",
506     icon = "", sensitive = None):
507 bh 222 """Add a command implemented by a method of the mainwindow object"""
508 bh 6 registry.Add(Command(name, title, call_method, args=(method,),
509 jan 110 helptext = helptext, icon = icon,
510     sensitive = sensitive))
511    
512 bh 6 def _tool_command(name, title, method, toolname, helptext = "",
513     icon = ""):
514     """Add a tool command"""
515     def check_current_tool(context, name=toolname):
516 bh 222 return context.mainwindow.canvas.CurrentTool() == name
517 bh 6 registry.Add(Command(name, title, call_method, args=(method,),
518     helptext = helptext, icon = icon,
519     checked = check_current_tool))
520    
521     def _has_selected_layer(context):
522     """Return true if a layer is selected in the context"""
523 bh 222 return context.mainwindow.has_selected_layer()
524 bh 6
525     # File menu
526     _method_command("new_session", "&New Session", "NewSession")
527     _method_command("open_session", "&Open Session", "OpenSession")
528     _method_command("save_session", "&Save Session", "SaveSession")
529     _method_command("save_session_as", "Save Session &As", "SaveSessionAs")
530 bh 150 _method_command("show_session_tree", "Show Session &Tree", "ShowSessionTree")
531 bh 6 _method_command("exit", "&Exit", "Exit")
532    
533     # Help menu
534     _method_command("help_about", "&About", "About")
535    
536    
537     # Map menu
538     _method_command("map_projection", "Pro&jection", "Projection")
539    
540     _tool_command("map_zoom_in_tool", "&Zoom in", "ZoomInTool", "ZoomInTool",
541     helptext = "Switch to map-mode 'zoom-in'", icon = "zoom_in")
542     _tool_command("map_zoom_out_tool", "Zoom &out", "ZoomOutTool", "ZoomOutTool",
543     helptext = "Switch to map-mode 'zoom-out'", icon = "zoom_out")
544     _tool_command("map_pan_tool", "&Pan", "PanTool", "PanTool",
545     helptext = "Switch to map-mode 'pan'", icon = "pan")
546     _tool_command("map_identify_tool", "&Identify", "IdentifyTool", "IdentifyTool",
547     helptext = "Switch to map-mode 'identify'", icon = "identify")
548     _tool_command("map_label_tool", "&Label", "LabelTool", "LabelTool",
549     helptext = "Add/Remove labels", icon = "label")
550 jan 110 _method_command("map_full_extent", "&Full extent", "FullExtent",
551     helptext = "Full Extent", icon = "fullextent")
552 bh 6 _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")
553    
554     # Layer menu
555 bh 84 _method_command("layer_add", "&Add Layer", "AddLayer",
556 bh 6 helptext = "Add a new layer to active map")
557 bh 84 _method_command("layer_remove", "&Remove Layer", "RemoveLayer",
558 bh 6 helptext = "Remove selected layer(s)",
559     sensitive = _has_selected_layer)
560     _method_command("layer_fill_color", "&Fill Color", "LayerFillColor",
561     helptext = "Set the fill color of selected layer(s)",
562     sensitive = _has_selected_layer)
563     _method_command("layer_transparent_fill", "&Transparent Fill",
564     "LayerTransparentFill",
565     helptext = "Do not fill the selected layer(s)",
566     sensitive = _has_selected_layer)
567 bh 185 _method_command("layer_outline_color", "&Outline Color", "LayerOutlineColor",
568 bh 6 helptext = "Set the outline color of selected layer(s)",
569     sensitive = _has_selected_layer)
570     _method_command("layer_no_outline", "&No Outline", "LayerNoOutline",
571     helptext = "Do not draw the outline of the selected layer(s)",
572     sensitive = _has_selected_layer)
573     _method_command("layer_raise", "&Raise", "RaiseLayer",
574     helptext = "Raise selected layer(s)",
575     sensitive = _has_selected_layer)
576     _method_command("layer_lower", "&Lower", "LowerLayer",
577     helptext = "Lower selected layer(s)",
578     sensitive = _has_selected_layer)
579     _method_command("layer_show", "&Show", "ShowLayer",
580     helptext = "Make selected layer(s) visible",
581     sensitive = _has_selected_layer)
582     _method_command("layer_hide", "&Hide", "HideLayer",
583     helptext = "Make selected layer(s) unvisible",
584     sensitive = _has_selected_layer)
585     _method_command("layer_show_table", "Show Ta&ble", "LayerShowTable",
586     helptext = "Show the selected layer's table",
587     sensitive = _has_selected_layer)
588 bh 188
589    
590     # the menu structure
591     main_menu = Menu("<main>", "<main>",
592     [Menu("file", "&File",
593     ["new_session", "open_session", None,
594     "save_session", "save_session_as", None,
595     "show_session_tree", None,
596     "exit"]),
597     Menu("map", "&Map",
598     ["layer_add", "layer_remove",
599     None,
600     "map_projection",
601     None,
602     "map_zoom_in_tool", "map_zoom_out_tool",
603     "map_pan_tool", "map_identify_tool", "map_label_tool",
604     None,
605     "map_full_extent",
606     None,
607     "map_print"]),
608     Menu("layer", "&Layer",
609     ["layer_fill_color", "layer_transparent_fill",
610     "layer_outline_color", "layer_no_outline",
611     None,
612     "layer_raise", "layer_lower",
613     None,
614     "layer_show", "layer_hide",
615     None,
616     "layer_show_table"]),
617     Menu("help", "&Help",
618     ["help_about"])])
619 bh 191
620     # the main toolbar
621    
622     main_toolbar = Menu("<toolbar>", "<toolbar>",
623     ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
624     "map_identify_tool", "map_label_tool", "map_full_extent"])

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26