/[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 278 - (show annotations)
Mon Aug 26 12:50:23 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: 23392 byte(s)
* Thuban/UI/tableview.py (TableGrid, LayerTableGrid): Split the
layer table specific code from TableGrid into LayerTableGrid
(TableFrame, LayerTableFrame): Split the layer table specific code
from TableFrame into LayerTableFrame

* Thuban/UI/mainwindow.py (MainWindow.LayerShowTable): Use the
LayerTableFrame

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 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 context = Context(self.application, self.application.Session(),
201 self)
202 command.Execute(context)
203 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 context = Context(self.application, self.application.Session(), self)
209 command = registry.Command(self.id_to_name[event.GetId()])
210 if command is not None:
211 event.Enable(command.Sensitive(context))
212 event.SetText(command.DynText(context))
213 if command.IsCheckCommand():
214 event.Check(command.Checked(context))
215
216 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
217 """Run a modal message box with the given text, title and flags
218 and return the result"""
219 dlg = wxMessageDialog(self, text, title, flags)
220 result = dlg.ShowModal()
221 dlg.Destroy()
222 return result
223
224 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 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 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 if self.application.session.WasModified():
263 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 def NewSession(self):
277 self.save_modified_session()
278 self.application.SetSession(create_empty_session())
279
280 def OpenSession(self):
281 self.save_modified_session()
282 dlg = wxFileDialog(self, "Select a session file", ".", "",
283 "*.thuban", wxOPEN)
284 if dlg.ShowModal() == wxID_OK:
285 self.application.OpenSession(dlg.GetPath())
286 dlg.Destroy()
287
288 def SaveSession(self):
289 if self.application.session.filename == None:
290 self.SaveSessionAs()
291 self.application.SaveSession()
292
293 def SaveSessionAs(self):
294 dlg = wxFileDialog(self, "Enter a filename for session", ".", "",
295 "*.thuban", wxOPEN)
296 if dlg.ShowModal() == wxID_OK:
297 self.application.session.SetFilename(dlg.GetPath())
298 self.application.SaveSession()
299 dlg.Destroy()
300
301 def Exit(self):
302 self.Close(false)
303
304 def OnClose(self, event):
305 result = self.save_modified_session(can_veto = event.CanVeto())
306 if result == wxID_CANCEL:
307 event.Veto()
308 else:
309 self.Destroy()
310
311 def SetMap(self, map):
312 self.canvas.SetMap(map)
313
314 def ShowSessionTree(self):
315 name = "session_tree"
316 dialog = self.get_open_dialog(name)
317 if dialog is None:
318 dialog = tree.SessionTreeView(self, self.application, name)
319 self.add_dialog(name, dialog)
320 dialog.Show(true)
321 else:
322 # FIXME: bring dialog to front here
323 pass
324
325 def About(self):
326 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
333 def AddLayer(self):
334 dlg = wxFileDialog(self, "Select a data file", ".", "", "*.*",
335 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 map = self.canvas.Map()
341 has_layers = map.HasLayers()
342 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 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
365 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 return self.interactor.SelectedLayer()
376
377 def has_selected_layer(self):
378 """Return true if a layer is currently selected"""
379 return self.interactor.HasSelectedLayer()
380
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 table = layer.table
435 name = "table_view" + str(id(table))
436 dialog = self.get_open_dialog(name)
437 if dialog is None:
438 dialog = tableview.LayerTableFrame(self, self.interactor, name,
439 "Table: %s" % layer.Title(),
440 layer, table)
441 self.add_dialog(name, dialog)
442 dialog.Show(true)
443 else:
444 # FIXME: bring dialog to front here
445 pass
446
447 def Projection(self):
448 map = self.canvas.Map()
449 proj = map.projection
450 if proj is None:
451 proj4Dlg = proj4dialog.Proj4Dialog(NULL, None, map.BoundingBox())
452 else:
453 proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params,
454 map.BoundingBox())
455 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 self.identify_view_on_demand(None, None)
476
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 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 dialog = identifyview.IdentifyView(self, self.interactor, name)
491 self.add_dialog(name, dialog)
492 dialog.Show(true)
493 else:
494 # FIXME: bring dialog to front?
495 pass
496
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 """Call the mainwindow's method methodname with args *args"""
505 apply(getattr(context.mainwindow, methodname), args)
506
507 def _method_command(name, title, method, helptext = "",
508 icon = "", sensitive = None):
509 """Add a command implemented by a method of the mainwindow object"""
510 registry.Add(Command(name, title, call_method, args=(method,),
511 helptext = helptext, icon = icon,
512 sensitive = sensitive))
513
514 def make_check_current_tool(toolname):
515 """Return a function that tests if the currently active tool is toolname
516
517 The returned function can be called with the context and returns
518 true iff the currently active tool's name is toolname. It's directly
519 usable as the 'checked' callback of a command.
520 """
521 def check_current_tool(context, name=toolname):
522 return context.mainwindow.canvas.CurrentTool() == name
523 return check_current_tool
524
525 def _tool_command(name, title, method, toolname, helptext = "",
526 icon = ""):
527 """Add a tool command"""
528 registry.Add(Command(name, title, call_method, args=(method,),
529 helptext = helptext, icon = icon,
530 checked = make_check_current_tool(toolname)))
531
532 def _has_selected_layer(context):
533 """Return true if a layer is selected in the context"""
534 return context.mainwindow.has_selected_layer()
535
536 def _has_tree_window_shown(context):
537 """Return true if the tree window is shown"""
538 return context.mainwindow.get_open_dialog("session_tree") is None
539
540 # File menu
541 _method_command("new_session", "&New Session", "NewSession")
542 _method_command("open_session", "&Open Session", "OpenSession")
543 _method_command("save_session", "&Save Session", "SaveSession")
544 _method_command("save_session_as", "Save Session &As", "SaveSessionAs")
545 _method_command("show_session_tree", "Show Session &Tree", "ShowSessionTree",
546 sensitive = _has_tree_window_shown)
547 _method_command("exit", "&Exit", "Exit")
548
549 # Help menu
550 _method_command("help_about", "&About", "About")
551
552
553 # Map menu
554 _method_command("map_projection", "Pro&jection", "Projection")
555
556 _tool_command("map_zoom_in_tool", "&Zoom in", "ZoomInTool", "ZoomInTool",
557 helptext = "Switch to map-mode 'zoom-in'", icon = "zoom_in")
558 _tool_command("map_zoom_out_tool", "Zoom &out", "ZoomOutTool", "ZoomOutTool",
559 helptext = "Switch to map-mode 'zoom-out'", icon = "zoom_out")
560 _tool_command("map_pan_tool", "&Pan", "PanTool", "PanTool",
561 helptext = "Switch to map-mode 'pan'", icon = "pan")
562 _tool_command("map_identify_tool", "&Identify", "IdentifyTool", "IdentifyTool",
563 helptext = "Switch to map-mode 'identify'", icon = "identify")
564 _tool_command("map_label_tool", "&Label", "LabelTool", "LabelTool",
565 helptext = "Add/Remove labels", icon = "label")
566 _method_command("map_full_extent", "&Full extent", "FullExtent",
567 helptext = "Full Extent", icon = "fullextent")
568 _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")
569
570 # Layer menu
571 _method_command("layer_add", "&Add Layer", "AddLayer",
572 helptext = "Add a new layer to active map")
573 _method_command("layer_remove", "&Remove Layer", "RemoveLayer",
574 helptext = "Remove selected layer(s)",
575 sensitive = _has_selected_layer)
576 _method_command("layer_fill_color", "&Fill Color", "LayerFillColor",
577 helptext = "Set the fill color of selected layer(s)",
578 sensitive = _has_selected_layer)
579 _method_command("layer_transparent_fill", "&Transparent Fill",
580 "LayerTransparentFill",
581 helptext = "Do not fill the selected layer(s)",
582 sensitive = _has_selected_layer)
583 _method_command("layer_outline_color", "&Outline Color", "LayerOutlineColor",
584 helptext = "Set the outline color of selected layer(s)",
585 sensitive = _has_selected_layer)
586 _method_command("layer_no_outline", "&No Outline", "LayerNoOutline",
587 helptext = "Do not draw the outline of the selected layer(s)",
588 sensitive = _has_selected_layer)
589 _method_command("layer_raise", "&Raise", "RaiseLayer",
590 helptext = "Raise selected layer(s)",
591 sensitive = _has_selected_layer)
592 _method_command("layer_lower", "&Lower", "LowerLayer",
593 helptext = "Lower selected layer(s)",
594 sensitive = _has_selected_layer)
595 _method_command("layer_show", "&Show", "ShowLayer",
596 helptext = "Make selected layer(s) visible",
597 sensitive = _has_selected_layer)
598 _method_command("layer_hide", "&Hide", "HideLayer",
599 helptext = "Make selected layer(s) unvisible",
600 sensitive = _has_selected_layer)
601 _method_command("layer_show_table", "Show Ta&ble", "LayerShowTable",
602 helptext = "Show the selected layer's table",
603 sensitive = _has_selected_layer)
604
605
606 # the menu structure
607 main_menu = Menu("<main>", "<main>",
608 [Menu("file", "&File",
609 ["new_session", "open_session", None,
610 "save_session", "save_session_as", None,
611 "show_session_tree", None,
612 "exit"]),
613 Menu("map", "&Map",
614 ["layer_add", "layer_remove",
615 None,
616 "map_projection",
617 None,
618 "map_zoom_in_tool", "map_zoom_out_tool",
619 "map_pan_tool", "map_identify_tool", "map_label_tool",
620 None,
621 "map_full_extent",
622 None,
623 "map_print"]),
624 Menu("layer", "&Layer",
625 ["layer_fill_color", "layer_transparent_fill",
626 "layer_outline_color", "layer_no_outline",
627 None,
628 "layer_raise", "layer_lower",
629 None,
630 "layer_show", "layer_hide",
631 None,
632 "layer_show_table"]),
633 Menu("help", "&Help",
634 ["help_about"])])
635
636 # the main toolbar
637
638 main_toolbar = Menu("<toolbar>", "<toolbar>",
639 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
640 "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