/[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 238 - (hide annotations)
Wed Jul 24 10:19:46 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: 22803 byte(s)
(MainWindow.__init__): Turn the initial
window size into a parameter.

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 bh 238 initial_message = None, size = wxSize(-1, -1)):
45     wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
46 bh 6
47 bh 227 self.application = application
48 bh 37 self.interactor = interactor
49    
50 bh 6 self.CreateStatusBar()
51 bh 235 if initial_message:
52     self.SetStatusText(initial_message)
53 bh 6
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 227 context = Context(self.application, self.application.Session(),
201     self)
202     command.Execute(context)
203 bh 6 else:
204     print "Unknown command ID %d" % event.GetId()
205    
206     def update_command_ui(self, event):
207     #print "update_command_ui", self.id_to_name[event.GetId()]
208 bh 227 context = Context(self.application, self.application.Session(), self)
209 bh 6 command = registry.Command(self.id_to_name[event.GetId()])
210     if command is not None:
211 bh 222 event.Enable(command.Sensitive(context))
212     event.SetText(command.DynText(context))
213 bh 13 if command.IsCheckCommand():
214 bh 222 event.Check(command.Checked(context))
215 bh 6
216 bh 20 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
217 bh 181 """Run a modal message box with the given text, title and flags
218 bh 20 and return the result"""
219     dlg = wxMessageDialog(self, text, title, flags)
220     result = dlg.ShowModal()
221     dlg.Destroy()
222     return result
223    
224 bh 31 def init_dialogs(self):
225     """Initialize the dialog handling"""
226     # The mainwindow maintains a dict mapping names to open
227     # non-modal dialogs. The dialogs are put into this dict when
228     # they're created and removed when they're closed
229     self.dialogs = {}
230    
231     def add_dialog(self, name, dialog):
232     if self.dialogs.has_key(name):
233     raise RuntimeError("The Dialog named %s is already open" % name)
234     self.dialogs[name] = dialog
235    
236     def dialog_open(self, name):
237     return self.dialogs.has_key(name)
238    
239     def remove_dialog(self, name):
240     del self.dialogs[name]
241    
242     def get_open_dialog(self, name):
243     return self.dialogs.get(name)
244    
245 bh 123 def view_position_changed(self):
246     pos = self.canvas.CurrentPosition()
247     if pos is not None:
248     text = "(%10.10g, %10.10g)" % pos
249     else:
250     text = ""
251     self.SetStatusText(text)
252    
253 bh 58 def save_modified_session(self, can_veto = 1):
254     """If the current session has been modified, ask the user
255     whether to save it and do so if requested. Return the outcome of
256     the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
257     dialog wasn't run return wxID_NO.
258    
259     If the can_veto parameter is true (default) the dialog includes
260     a cancel button, otherwise not.
261     """
262 bh 227 if self.application.session.WasModified():
263 bh 58 flags = wxYES_NO | wxICON_QUESTION
264     if can_veto:
265     flags = flags | wxCANCEL
266     result = self.RunMessageBox("Exit",
267     ("The session has been modified."
268     " Do you want to save it?"),
269     flags)
270     if result == wxID_YES:
271     self.SaveSession()
272     else:
273     result = wxID_NO
274     return result
275    
276 bh 6 def NewSession(self):
277 bh 58 self.save_modified_session()
278 bh 227 self.application.SetSession(create_empty_session())
279 bh 6
280     def OpenSession(self):
281 bh 58 self.save_modified_session()
282 bh 6 dlg = wxFileDialog(self, "Select a session file", ".", "",
283 bh 130 "*.thuban", wxOPEN)
284 bh 6 if dlg.ShowModal() == wxID_OK:
285 bh 227 self.application.OpenSession(dlg.GetPath())
286 bh 6 dlg.Destroy()
287    
288     def SaveSession(self):
289 bh 227 if self.application.session.filename == None:
290 jan 102 self.SaveSessionAs()
291 bh 227 self.application.SaveSession()
292 bh 6
293     def SaveSessionAs(self):
294     dlg = wxFileDialog(self, "Enter a filename for session", ".", "",
295 bh 130 "*.thuban", wxOPEN)
296 bh 6 if dlg.ShowModal() == wxID_OK:
297 bh 227 self.application.session.SetFilename(dlg.GetPath())
298     self.application.SaveSession()
299 bh 6 dlg.Destroy()
300    
301     def Exit(self):
302     self.Close(false)
303    
304     def OnClose(self, event):
305 bh 58 result = self.save_modified_session(can_veto = event.CanVeto())
306     if result == wxID_CANCEL:
307 bh 6 event.Veto()
308     else:
309     self.Destroy()
310    
311     def SetMap(self, map):
312     self.canvas.SetMap(map)
313    
314 bh 37 def ShowSessionTree(self):
315     name = "session_tree"
316     dialog = self.get_open_dialog(name)
317     if dialog is None:
318 bh 227 dialog = tree.SessionTreeView(self, self.application, name)
319 bh 37 self.add_dialog(name, dialog)
320     dialog.Show(true)
321     else:
322     # FIXME: bring dialog to front here
323     pass
324    
325 bh 6 def About(self):
326 bh 20 self.RunMessageBox("About",
327     ("Thuban is a program for\n"
328     "exploring geographic data.\n"
329     "Copyright (C) 2001 Intevation GmbH.\n"
330     "Thuban is licensed under the GPL"),
331     wxOK | wxICON_INFORMATION)
332 bh 6
333     def AddLayer(self):
334 frank 119 dlg = wxFileDialog(self, "Select a data file", ".", "", "*.*",
335 bh 6 wxOPEN)
336     if dlg.ShowModal() == wxID_OK:
337     filename = dlg.GetPath()
338     title = os.path.splitext(os.path.basename(filename))[0]
339     layer = Layer(title, filename)
340 bh 18 map = self.canvas.Map()
341     has_layers = map.HasLayers()
342 bh 20 try:
343     map.AddLayer(layer)
344     except IOError:
345     # the layer couldn't be opened
346     self.RunMessageBox("Add Layer",
347     "Can't open the file '%s'." % filename)
348     else:
349     if not has_layers:
350     # if we're adding a layer to an empty map, for the
351     # new map to the window
352     self.canvas.FitMapToWindow()
353 bh 6 dlg.Destroy()
354    
355     def RemoveLayer(self):
356     layer = self.current_layer()
357     if layer is not None:
358     self.canvas.Map().RemoveLayer(layer)
359    
360     def RaiseLayer(self):
361     layer = self.current_layer()
362     if layer is not None:
363     self.canvas.Map().RaiseLayer(layer)
364 bh 222
365 bh 6 def LowerLayer(self):
366     layer = self.current_layer()
367     if layer is not None:
368     self.canvas.Map().LowerLayer(layer)
369    
370     def current_layer(self):
371     """Return the currently selected layer.
372    
373     If no layer is selected, return None
374     """
375 bh 37 return self.interactor.SelectedLayer()
376 bh 6
377     def has_selected_layer(self):
378     """Return true if a layer is currently selected"""
379 bh 37 return self.interactor.HasSelectedLayer()
380 bh 6
381     def choose_color(self):
382     """Run the color selection dialog and return the selected color.
383    
384     If the user cancels, return None.
385     """
386     dlg = wxColourDialog(self)
387     color = None
388     if dlg.ShowModal() == wxID_OK:
389     data = dlg.GetColourData()
390     wxc = data.GetColour()
391     color = Color(wxc.Red() / 255.0,
392     wxc.Green() / 255.0,
393     wxc.Blue() / 255.0)
394     dlg.Destroy()
395     return color
396    
397     def LayerFillColor(self):
398     layer = self.current_layer()
399     if layer is not None:
400     color = self.choose_color()
401     if color is not None:
402     layer.SetFill(color)
403    
404     def LayerTransparentFill(self):
405     layer = self.current_layer()
406     if layer is not None:
407     layer.SetFill(None)
408    
409     def LayerOutlineColor(self):
410     layer = self.current_layer()
411     if layer is not None:
412     color = self.choose_color()
413     if color is not None:
414     layer.SetStroke(color)
415    
416     def LayerNoOutline(self):
417     layer = self.current_layer()
418     if layer is not None:
419     layer.SetStroke(None)
420    
421     def HideLayer(self):
422     layer = self.current_layer()
423     if layer is not None:
424     layer.SetVisible(0)
425    
426     def ShowLayer(self):
427     layer = self.current_layer()
428     if layer is not None:
429     layer.SetVisible(1)
430    
431     def LayerShowTable(self):
432     layer = self.current_layer()
433     if layer is not None:
434 bh 31 table = layer.table
435     name = "table_view" + str(id(table))
436     dialog = self.get_open_dialog(name)
437     if dialog is None:
438 bh 37 dialog = tableview.TableFrame(self, self.interactor, name,
439 bh 31 "Table: %s" % layer.Title(),
440 bh 33 layer, table)
441 bh 31 self.add_dialog(name, dialog)
442     dialog.Show(true)
443     else:
444     # FIXME: bring dialog to front here
445     pass
446 bh 6
447     def Projection(self):
448     map = self.canvas.Map()
449     proj = map.projection
450     if proj is None:
451 jan 110 proj4Dlg = proj4dialog.Proj4Dialog(NULL, None, map.BoundingBox())
452 bh 6 else:
453 jan 110 proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params,
454     map.BoundingBox())
455 bh 6 if proj4Dlg.ShowModal() == wxID_OK:
456     params = proj4Dlg.GetParams()
457     if params is not None:
458     proj = Projection(params)
459     else:
460     proj = None
461     map.SetProjection(proj)
462     proj4Dlg.Destroy()
463    
464     def ZoomInTool(self):
465     self.canvas.ZoomInTool()
466    
467     def ZoomOutTool(self):
468     self.canvas.ZoomOutTool()
469    
470     def PanTool(self):
471     self.canvas.PanTool()
472    
473     def IdentifyTool(self):
474     self.canvas.IdentifyTool()
475 bh 49 self.identify_view_on_demand(None, None)
476 bh 6
477     def LabelTool(self):
478     self.canvas.LabelTool()
479    
480     def FullExtent(self):
481     self.canvas.FitMapToWindow()
482    
483     def PrintMap(self):
484     self.canvas.Print()
485    
486 bh 31 def identify_view_on_demand(self, layer, shape):
487     name = "identify_view"
488     if self.canvas.CurrentTool() == "IdentifyTool":
489     if not self.dialog_open(name):
490 bh 37 dialog = identifyview.IdentifyView(self, self.interactor, name)
491 bh 31 self.add_dialog(name, dialog)
492     dialog.Show(true)
493     else:
494 bh 33 # FIXME: bring dialog to front?
495 bh 31 pass
496 bh 6
497     #
498     # Define all the commands available in the main window
499     #
500    
501    
502     # Helper functions to define common command implementations
503     def call_method(context, methodname, *args):
504 bh 222 """Call the mainwindow's method methodname with args *args"""
505     apply(getattr(context.mainwindow, methodname), args)
506 bh 6
507 jan 110 def _method_command(name, title, method, helptext = "",
508     icon = "", sensitive = None):
509 bh 222 """Add a command implemented by a method of the mainwindow object"""
510 bh 6 registry.Add(Command(name, title, call_method, args=(method,),
511 jan 110 helptext = helptext, icon = icon,
512     sensitive = sensitive))
513    
514 bh 6 def _tool_command(name, title, method, toolname, helptext = "",
515     icon = ""):
516     """Add a tool command"""
517     def check_current_tool(context, name=toolname):
518 bh 222 return context.mainwindow.canvas.CurrentTool() == name
519 bh 6 registry.Add(Command(name, title, call_method, args=(method,),
520     helptext = helptext, icon = icon,
521     checked = check_current_tool))
522    
523     def _has_selected_layer(context):
524     """Return true if a layer is selected in the context"""
525 bh 222 return context.mainwindow.has_selected_layer()
526 bh 6
527     # File menu
528     _method_command("new_session", "&New Session", "NewSession")
529     _method_command("open_session", "&Open Session", "OpenSession")
530     _method_command("save_session", "&Save Session", "SaveSession")
531     _method_command("save_session_as", "Save Session &As", "SaveSessionAs")
532 bh 150 _method_command("show_session_tree", "Show Session &Tree", "ShowSessionTree")
533 bh 6 _method_command("exit", "&Exit", "Exit")
534    
535     # Help menu
536     _method_command("help_about", "&About", "About")
537    
538    
539     # Map menu
540     _method_command("map_projection", "Pro&jection", "Projection")
541    
542     _tool_command("map_zoom_in_tool", "&Zoom in", "ZoomInTool", "ZoomInTool",
543     helptext = "Switch to map-mode 'zoom-in'", icon = "zoom_in")
544     _tool_command("map_zoom_out_tool", "Zoom &out", "ZoomOutTool", "ZoomOutTool",
545     helptext = "Switch to map-mode 'zoom-out'", icon = "zoom_out")
546     _tool_command("map_pan_tool", "&Pan", "PanTool", "PanTool",
547     helptext = "Switch to map-mode 'pan'", icon = "pan")
548     _tool_command("map_identify_tool", "&Identify", "IdentifyTool", "IdentifyTool",
549     helptext = "Switch to map-mode 'identify'", icon = "identify")
550     _tool_command("map_label_tool", "&Label", "LabelTool", "LabelTool",
551     helptext = "Add/Remove labels", icon = "label")
552 jan 110 _method_command("map_full_extent", "&Full extent", "FullExtent",
553     helptext = "Full Extent", icon = "fullextent")
554 bh 6 _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")
555    
556     # Layer menu
557 bh 84 _method_command("layer_add", "&Add Layer", "AddLayer",
558 bh 6 helptext = "Add a new layer to active map")
559 bh 84 _method_command("layer_remove", "&Remove Layer", "RemoveLayer",
560 bh 6 helptext = "Remove selected layer(s)",
561     sensitive = _has_selected_layer)
562     _method_command("layer_fill_color", "&Fill Color", "LayerFillColor",
563     helptext = "Set the fill color of selected layer(s)",
564     sensitive = _has_selected_layer)
565     _method_command("layer_transparent_fill", "&Transparent Fill",
566     "LayerTransparentFill",
567     helptext = "Do not fill the selected layer(s)",
568     sensitive = _has_selected_layer)
569 bh 185 _method_command("layer_outline_color", "&Outline Color", "LayerOutlineColor",
570 bh 6 helptext = "Set the outline color of selected layer(s)",
571     sensitive = _has_selected_layer)
572     _method_command("layer_no_outline", "&No Outline", "LayerNoOutline",
573     helptext = "Do not draw the outline of the selected layer(s)",
574     sensitive = _has_selected_layer)
575     _method_command("layer_raise", "&Raise", "RaiseLayer",
576     helptext = "Raise selected layer(s)",
577     sensitive = _has_selected_layer)
578     _method_command("layer_lower", "&Lower", "LowerLayer",
579     helptext = "Lower selected layer(s)",
580     sensitive = _has_selected_layer)
581     _method_command("layer_show", "&Show", "ShowLayer",
582     helptext = "Make selected layer(s) visible",
583     sensitive = _has_selected_layer)
584     _method_command("layer_hide", "&Hide", "HideLayer",
585     helptext = "Make selected layer(s) unvisible",
586     sensitive = _has_selected_layer)
587     _method_command("layer_show_table", "Show Ta&ble", "LayerShowTable",
588     helptext = "Show the selected layer's table",
589     sensitive = _has_selected_layer)
590 bh 188
591    
592     # the menu structure
593     main_menu = Menu("<main>", "<main>",
594     [Menu("file", "&File",
595     ["new_session", "open_session", None,
596     "save_session", "save_session_as", None,
597     "show_session_tree", None,
598     "exit"]),
599     Menu("map", "&Map",
600     ["layer_add", "layer_remove",
601     None,
602     "map_projection",
603     None,
604     "map_zoom_in_tool", "map_zoom_out_tool",
605     "map_pan_tool", "map_identify_tool", "map_label_tool",
606     None,
607     "map_full_extent",
608     None,
609     "map_print"]),
610     Menu("layer", "&Layer",
611     ["layer_fill_color", "layer_transparent_fill",
612     "layer_outline_color", "layer_no_outline",
613     None,
614     "layer_raise", "layer_lower",
615     None,
616     "layer_show", "layer_hide",
617     None,
618     "layer_show_table"]),
619     Menu("help", "&Help",
620     ["help_about"])])
621 bh 191
622     # the main toolbar
623    
624     main_toolbar = Menu("<toolbar>", "<toolbar>",
625     ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
626     "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