/[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 193 - (hide annotations)
Wed May 29 10:33:41 2002 UTC (22 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 22468 byte(s)
	* Thuban/UI/mainwindow.py (MainWindow.bind_command_events): Bind
	the events for a comnmand.
	(MainWindow.add_toolbar_command, MainWindow.add_menu_command):
	Call bind_command_events to bind the events. add_toolbar_command
	can now bind events too so that it's possible to have commands
	that are only available through the toolbar.
	(MainWindow.init_ids): New instance variable events_bound to keep
	track of for which commands events have been bound.

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