/[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 188 - (hide annotations)
Tue May 28 12:38:45 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: 21393 byte(s)
	* Thuban/UI/mainwindow.py: Remove some unused imports.
	(MainWindow.__init__, main_menu): move the definition of the main
	menu from __init__ to the Menu instance main_menu.
	(MainWindow.build_menu_bar, MainWindow.build_menu): New methods to
	build the menu bar and sub-menus from a menu description.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26