/[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 235 - (hide annotations)
Tue Jul 23 10:56:29 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: 22817 byte(s)
* Thuban/UI/mainwindow.py (MainWindow.__init__): Change the
parameter list a bit to allow setting the window title and the
initial message in the status bar. Update the callers.

* Thuban/UI/application.py (ThubanApplication.OnInit)
(ThubanApplication.CreateMainWindow): Put the mainwindow
instantiation into a separate method so that it can be overridden
by a subclass.

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