/[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 299 - (show annotations)
Fri Aug 30 17:41:04 2002 UTC (22 years, 6 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 23926 byte(s)
(MainWindow.CanRemoveLayer): New method
for the sensitivity  of remove layer.
(_can_remove_layer): New. Sensitivity callback for remove layer
(Command layer_remove): Use _can_remove_layer

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