/[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 235 - (show 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 # 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):
45 wxFrame.__init__(self, parent, ID, title, wxDefaultPosition,
46 wxSize(400, 300))
47
48 self.application = application
49 self.interactor = interactor
50
51 self.CreateStatusBar()
52 if initial_message:
53 self.SetStatusText(initial_message)
54
55 self.identify_view = None
56
57 self.init_ids()
58
59 # creat the menubar from the main_menu description
60 self.SetMenuBar(self.build_menu_bar(main_menu))
61
62 # Similarly, create the toolbar from main_toolbar
63 toolbar = self.build_toolbar(main_toolbar)
64 # call Realize to make sure that the tools appear.
65 toolbar.Realize()
66
67 # Create the map canvas
68 canvas = view.MapCanvas(self, -1, interactor)
69 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
70 self.canvas = canvas
71
72 self.init_dialogs()
73
74 interactor.Subscribe(SELECTED_SHAPE, self.identify_view_on_demand)
75
76 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 self.events_bound = {}
84
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
97 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 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 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 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 self.bind_command_events(command, ID)
172 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 self.bind_command_events(command, ID)
194 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 context = Context(self.application, self.application.Session(),
202 self)
203 command.Execute(context)
204 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 context = Context(self.application, self.application.Session(), self)
210 command = registry.Command(self.id_to_name[event.GetId()])
211 if command is not None:
212 event.Enable(command.Sensitive(context))
213 event.SetText(command.DynText(context))
214 if command.IsCheckCommand():
215 event.Check(command.Checked(context))
216
217 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
218 """Run a modal message box with the given text, title and flags
219 and return the result"""
220 dlg = wxMessageDialog(self, text, title, flags)
221 result = dlg.ShowModal()
222 dlg.Destroy()
223 return result
224
225 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 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 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 if self.application.session.WasModified():
264 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 def NewSession(self):
278 self.save_modified_session()
279 self.application.SetSession(create_empty_session())
280
281 def OpenSession(self):
282 self.save_modified_session()
283 dlg = wxFileDialog(self, "Select a session file", ".", "",
284 "*.thuban", wxOPEN)
285 if dlg.ShowModal() == wxID_OK:
286 self.application.OpenSession(dlg.GetPath())
287 dlg.Destroy()
288
289 def SaveSession(self):
290 if self.application.session.filename == None:
291 self.SaveSessionAs()
292 self.application.SaveSession()
293
294 def SaveSessionAs(self):
295 dlg = wxFileDialog(self, "Enter a filename for session", ".", "",
296 "*.thuban", wxOPEN)
297 if dlg.ShowModal() == wxID_OK:
298 self.application.session.SetFilename(dlg.GetPath())
299 self.application.SaveSession()
300 dlg.Destroy()
301
302 def Exit(self):
303 self.Close(false)
304
305 def OnClose(self, event):
306 result = self.save_modified_session(can_veto = event.CanVeto())
307 if result == wxID_CANCEL:
308 event.Veto()
309 else:
310 self.Destroy()
311
312 def SetMap(self, map):
313 self.canvas.SetMap(map)
314
315 def ShowSessionTree(self):
316 name = "session_tree"
317 dialog = self.get_open_dialog(name)
318 if dialog is None:
319 dialog = tree.SessionTreeView(self, self.application, name)
320 self.add_dialog(name, dialog)
321 dialog.Show(true)
322 else:
323 # FIXME: bring dialog to front here
324 pass
325
326 def About(self):
327 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
334 def AddLayer(self):
335 dlg = wxFileDialog(self, "Select a data file", ".", "", "*.*",
336 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 map = self.canvas.Map()
342 has_layers = map.HasLayers()
343 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 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
366 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 return self.interactor.SelectedLayer()
377
378 def has_selected_layer(self):
379 """Return true if a layer is currently selected"""
380 return self.interactor.HasSelectedLayer()
381
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 table = layer.table
436 name = "table_view" + str(id(table))
437 dialog = self.get_open_dialog(name)
438 if dialog is None:
439 dialog = tableview.TableFrame(self, self.interactor, name,
440 "Table: %s" % layer.Title(),
441 layer, table)
442 self.add_dialog(name, dialog)
443 dialog.Show(true)
444 else:
445 # FIXME: bring dialog to front here
446 pass
447
448 def Projection(self):
449 map = self.canvas.Map()
450 proj = map.projection
451 if proj is None:
452 proj4Dlg = proj4dialog.Proj4Dialog(NULL, None, map.BoundingBox())
453 else:
454 proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params,
455 map.BoundingBox())
456 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 self.identify_view_on_demand(None, None)
477
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 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 dialog = identifyview.IdentifyView(self, self.interactor, name)
492 self.add_dialog(name, dialog)
493 dialog.Show(true)
494 else:
495 # FIXME: bring dialog to front?
496 pass
497
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 """Call the mainwindow's method methodname with args *args"""
506 apply(getattr(context.mainwindow, methodname), args)
507
508 def _method_command(name, title, method, helptext = "",
509 icon = "", sensitive = None):
510 """Add a command implemented by a method of the mainwindow object"""
511 registry.Add(Command(name, title, call_method, args=(method,),
512 helptext = helptext, icon = icon,
513 sensitive = sensitive))
514
515 def _tool_command(name, title, method, toolname, helptext = "",
516 icon = ""):
517 """Add a tool command"""
518 def check_current_tool(context, name=toolname):
519 return context.mainwindow.canvas.CurrentTool() == name
520 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 return context.mainwindow.has_selected_layer()
527
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 _method_command("show_session_tree", "Show Session &Tree", "ShowSessionTree")
534 _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 _method_command("map_full_extent", "&Full extent", "FullExtent",
554 helptext = "Full Extent", icon = "fullextent")
555 _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")
556
557 # Layer menu
558 _method_command("layer_add", "&Add Layer", "AddLayer",
559 helptext = "Add a new layer to active map")
560 _method_command("layer_remove", "&Remove Layer", "RemoveLayer",
561 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 _method_command("layer_outline_color", "&Outline Color", "LayerOutlineColor",
571 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
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
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