/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/mainwindow.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/UI/mainwindow.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 188 - (show 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 # Copyright (C) 2001, 2002 by Intevation GmbH
2 # 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 import os
16
17 from wxPython.wx import *
18
19 import Thuban
20 from Thuban.Model.session import create_empty_session
21 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 from menu import Menu
30
31 import main
32 from command import registry, Command
33 from messages import SELECTED_SHAPE, VIEW_POSITION
34
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 def __init__(self, parent, ID, interactor):
44 wxFrame.__init__(self, parent, ID, 'Thuban',
45 wxDefaultPosition, wxSize(400, 300))
46
47 self.interactor = interactor
48
49 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 #main_menu.print_menu()
58 self.SetMenuBar(self.build_menu_bar(main_menu))
59
60 # toolbar
61 toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
62
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 for name in ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
69 "map_identify_tool", "map_label_tool", "map_full_extent"]:
70 self.add_toolbar_command(toolbar, name)
71 # call Realize to make sure that the tools appear.
72 toolbar.Realize()
73
74 # Create the map canvas
75 canvas = view.MapCanvas(self, -1, interactor)
76 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
77 self.canvas = canvas
78
79 self.init_dialogs()
80
81 interactor.Subscribe(SELECTED_SHAPE, self.identify_view_on_demand)
82
83 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
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 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 if command.IsCheckCommand():
188 event.Check(command.Checked(self))
189
190 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
191 """Run a modal message box with the given text, title and flags
192 and return the result"""
193 dlg = wxMessageDialog(self, text, title, flags)
194 result = dlg.ShowModal()
195 dlg.Destroy()
196 return result
197
198 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 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 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 def NewSession(self):
251 self.save_modified_session()
252 main.app.SetSession(create_empty_session())
253
254 def OpenSession(self):
255 self.save_modified_session()
256 dlg = wxFileDialog(self, "Select a session file", ".", "",
257 "*.thuban", wxOPEN)
258 if dlg.ShowModal() == wxID_OK:
259 main.app.OpenSession(dlg.GetPath())
260 dlg.Destroy()
261
262 def SaveSession(self):
263 if main.app.session.filename == None:
264 self.SaveSessionAs()
265 main.app.SaveSession()
266
267 def SaveSessionAs(self):
268 dlg = wxFileDialog(self, "Enter a filename for session", ".", "",
269 "*.thuban", wxOPEN)
270 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 result = self.save_modified_session(can_veto = event.CanVeto())
280 if result == wxID_CANCEL:
281 event.Veto()
282 else:
283 self.Destroy()
284
285 def SetMap(self, map):
286 self.canvas.SetMap(map)
287
288 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 def About(self):
300 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
307 def AddLayer(self):
308 dlg = wxFileDialog(self, "Select a data file", ".", "", "*.*",
309 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 map = self.canvas.Map()
315 has_layers = map.HasLayers()
316 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 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 return self.interactor.SelectedLayer()
350
351 def has_selected_layer(self):
352 """Return true if a layer is currently selected"""
353 return self.interactor.HasSelectedLayer()
354
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 table = layer.table
409 name = "table_view" + str(id(table))
410 dialog = self.get_open_dialog(name)
411 if dialog is None:
412 dialog = tableview.TableFrame(self, self.interactor, name,
413 "Table: %s" % layer.Title(),
414 layer, table)
415 self.add_dialog(name, dialog)
416 dialog.Show(true)
417 else:
418 # FIXME: bring dialog to front here
419 pass
420
421 def Projection(self):
422 map = self.canvas.Map()
423 proj = map.projection
424 if proj is None:
425 proj4Dlg = proj4dialog.Proj4Dialog(NULL, None, map.BoundingBox())
426 else:
427 proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params,
428 map.BoundingBox())
429 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 self.identify_view_on_demand(None, None)
450
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 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 dialog = identifyview.IdentifyView(self, self.interactor, name)
465 self.add_dialog(name, dialog)
466 dialog.Show(true)
467 else:
468 # FIXME: bring dialog to front?
469 pass
470
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 def _method_command(name, title, method, helptext = "",
482 icon = "", sensitive = None):
483 """Add a command implemented by a method of the context object"""
484 registry.Add(Command(name, title, call_method, args=(method,),
485 helptext = helptext, icon = icon,
486 sensitive = sensitive))
487
488 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 _method_command("show_session_tree", "Show Session &Tree", "ShowSessionTree")
507 _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 _method_command("map_full_extent", "&Full extent", "FullExtent",
527 helptext = "Full Extent", icon = "fullextent")
528 _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")
529
530 # Layer menu
531 _method_command("layer_add", "&Add Layer", "AddLayer",
532 helptext = "Add a new layer to active map")
533 _method_command("layer_remove", "&Remove Layer", "RemoveLayer",
534 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 _method_command("layer_outline_color", "&Outline Color", "LayerOutlineColor",
544 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
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