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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2072 - (show annotations)
Tue Feb 17 13:14:49 2004 UTC (21 years ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 15720 byte(s)
Fix for RT#2245

* Thuban/UI/application.py (ThubanApplication.OnInit): Initialize
instance variables before trying to create any windows.  Creating
windows can start an event loop if e.g. message boxes are popped
up for some reason, and event handlers, especially EVT_UPDATE_UI
may want to access things from the application.
(ThubanApplication.maps_changed): The mainwindow may not have been
created yet, so check whether it has been created before calling
its methods

* Thuban/UI/view.py (MapCanvas.OnIdle): Only try to redraw if we
have a map

1 # Copyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 # Frank Koormann <[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 Classes for display of a map and interaction with it
11 """
12
13 from __future__ import generators
14
15 __version__ = "$Revision$"
16 # $Source$
17 # $Id$
18
19 import os.path
20 import time
21 import traceback
22
23 from wxPython.wx import wxWindow, \
24 wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
25 EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
26 wxPlatform, wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \
27 wxOVERWRITE_PROMPT, wxID_OK
28
29 # Export related stuff
30 if wxPlatform == '__WXMSW__':
31 from wxPython.wx import wxMetaFileDC
32
33 from wxPython import wx
34
35 from Thuban import _
36
37 from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
38 LAYER_VISIBILITY_CHANGED
39
40 from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
41
42 import labeldialog
43
44 from viewport import ViewPort, PanTool, output_transform
45
46 class CanvasPanTool(PanTool):
47
48 """The Canvas Pan Tool"""
49
50 def MouseMove(self, event):
51 if self.dragging:
52 PanTool.MouseMove(self, event)
53 sx, sy = self.start
54 x, y = self.current
55 width, height = self.view.GetSizeTuple()
56
57 bitmapdc = wx.wxMemoryDC()
58 bitmapdc.SelectObject(self.view.PreviewBitmap())
59
60 dc = self.view.drag_dc
61 dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
62
63 class MapPrintout(wx.wxPrintout):
64
65 """
66 wxPrintout class for printing Thuban maps
67 """
68
69 def __init__(self, canvas, map, region, selected_layer, selected_shapes):
70 wx.wxPrintout.__init__(self)
71 self.canvas = canvas
72 self.map = map
73 self.region = region
74 self.selected_layer = selected_layer
75 self.selected_shapes = selected_shapes
76
77 def GetPageInfo(self):
78 return (1, 1, 1, 1)
79
80 def HasPage(self, pagenum):
81 return pagenum == 1
82
83 def OnPrintPage(self, pagenum):
84 if pagenum == 1:
85 self.draw_on_dc(self.GetDC())
86
87 def draw_on_dc(self, dc):
88 width, height = self.GetPageSizePixels()
89 scale, offset, mapregion = output_transform(self.canvas.scale,
90 self.canvas.offset,
91 self.canvas.GetSizeTuple(),
92 self.GetPageSizePixels())
93 resx, resy = self.GetPPIPrinter()
94 canvas_scale = self.canvas.scale
95 x, y, width, height = self.region
96 renderer = PrinterRenderer(dc, self.map, scale, offset,
97 region = (0, 0,
98 (width/canvas_scale)*scale,
99 (height/canvas_scale)*scale),
100 resolution = resy,
101 destination_region = mapregion)
102 renderer.RenderMap(self.selected_layer, self.selected_shapes)
103 return True
104
105
106 class MapCanvas(wxWindow, ViewPort):
107
108 """A widget that displays a map and offers some interaction"""
109
110 def __init__(self, parent, winid):
111 wxWindow.__init__(self, parent, winid)
112 ViewPort.__init__(self)
113
114 self.SetBackgroundColour(wxColour(255, 255, 255))
115
116 # the bitmap serving as backing store
117 self.bitmap = None
118 # the monochrome bitmap with the selection if any
119 self.selection_bitmap = None
120
121 self.backgroundColor = wx.wxWHITE_BRUSH
122
123 # The rendering iterator object. Used when rendering
124 # incrementally
125 self.render_iter = None
126
127 # subscribe the WX events we're interested in
128 EVT_PAINT(self, self.OnPaint)
129 EVT_LEFT_DOWN(self, self.OnLeftDown)
130 EVT_LEFT_UP(self, self.OnLeftUp)
131 EVT_MOTION(self, self.OnMotion)
132 EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
133 wx.EVT_SIZE(self, self.OnSize)
134 wx.EVT_IDLE(self, self.OnIdle)
135
136 def __del__(self):
137 wxWindow.__del__(self)
138 ViewPort.__del__(self)
139
140 def PreviewBitmap(self):
141 return self.bitmap
142
143 def PanTool(self):
144 """Start the canvas pan tool"""
145 self.SelectTool(CanvasPanTool(self))
146
147 def SetMap(self, map):
148 redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
149 LAYER_VISIBILITY_CHANGED)
150 if self.Map() is not None:
151 for channel in redraw_channels:
152 self.Map().Unsubscribe(channel, self.full_redraw)
153
154 ViewPort.SetMap(self, map)
155
156 if self.Map() is not None:
157 for channel in redraw_channels:
158 self.Map().Subscribe(channel, self.full_redraw)
159
160 # force a redraw. If map is not empty, it's already been called
161 # by FitMapToWindow but if map is empty it hasn't been called
162 # yet so we have to explicitly call it.
163 self.full_redraw()
164
165 def OnPaint(self, event):
166 dc = wxPaintDC(self)
167 if self.Map() is not None and self.Map().HasLayers():
168 if self.bitmap is not None:
169 dc.BeginDrawing()
170 dc.DrawBitmap(self.bitmap, 0, 0)
171 if self.selection_bitmap is not None:
172 dc.DrawBitmap(self.selection_bitmap, 0, 0, True)
173 dc.EndDrawing()
174 else:
175 # If we've got no map or if the map is empty, simply clear
176 # the screen.
177
178 # XXX it's probably possible to get rid of this. The
179 # background color of the window is already white and the
180 # only thing we may have to do is to call self.Refresh()
181 # with a true argument in the right places.
182 dc.BeginDrawing()
183 dc.SetBackground(self.backgroundColor)
184 dc.Clear()
185 dc.EndDrawing()
186
187 def OnIdle(self, event):
188 """Idle handler. Redraw the bitmap if necessary"""
189 if (self.Map() is not None
190 and (self.bitmap is None
191 or self.render_iter is not None
192 or (self.HasSelectedShapes()
193 and self.selection_bitmap is None))):
194 event.RequestMore(self._do_redraw())
195
196 def _do_redraw(self):
197 """Redraw a bit and return whether this method has to be called again.
198
199 Called by OnIdle to handle the actual redraw. Redraw is
200 incremental for both the bitmap with the normal layers and the
201 bitmap with the selection.
202 """
203 finished = False
204 if self.render_iter is not None:
205 try:
206 if self.render_iter.next():
207 # Redraw if the last preview redraw was some time
208 # ago and the user is not currently dragging the
209 # mouse because redrawing would interfere with what
210 # the current tool is drawing on the window.
211 if not self.dragging \
212 and time.time() - self.render_last_preview > 0.5:
213 client_dc = wxClientDC(self)
214 client_dc.BeginDrawing()
215 client_dc.DrawBitmap(self.bitmap, 0, 0)
216 client_dc.EndDrawing()
217 self.render_last_preview = time.time()
218 else:
219 self.render_iter = None
220 # Redraw if not dragging because redrawing would
221 # interfere with what the current tool is drawing on
222 # the window.
223 if not self.dragging:
224 self.redraw()
225 finished = True
226 except StopIteration:
227 finished = True
228 self.render_iter = None
229 except:
230 finished = True
231 self.render_iter = None
232 traceback.print_exc()
233 else:
234 self.render_iter = self._render_iterator()
235 self.render_last_preview = time.time()
236 return not finished
237
238 def _render_iterator(self):
239 width, height = self.GetSizeTuple()
240 dc = wx.wxMemoryDC()
241
242 render_start = time.time()
243
244 if self.bitmap is None:
245 self.bitmap = wx.wxEmptyBitmap(width, height)
246 dc.SelectObject(self.bitmap)
247 dc.BeginDrawing()
248
249 dc.SetBackground(self.backgroundColor)
250 dc.Clear()
251
252 # draw the map into the bitmap
253 renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
254 (0, 0, width, height))
255 for cont in renderer.RenderMapIncrementally():
256 yield True
257
258 dc.EndDrawing()
259 dc.SelectObject(wx.wxNullBitmap)
260
261 if self.HasSelectedShapes() and self.selection_bitmap is None:
262 bitmap = wx.wxEmptyBitmap(width, height)
263 dc.SelectObject(bitmap)
264 dc.BeginDrawing()
265 dc.SetBackground(wx.wxWHITE_BRUSH)
266 dc.Clear()
267
268 renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
269 (0, 0, width, height))
270 layer = self.SelectedLayer()
271 shapes = self.selection.SelectedShapes()
272 for cont in renderer.draw_selection_incrementally(layer, shapes):
273 yield True
274
275 dc.EndDrawing()
276 dc.SelectObject(wx.wxNullBitmap)
277
278 bitmap.SetMask(wx.wxMaskColour(bitmap, wx.wxWHITE))
279 self.selection_bitmap = bitmap
280
281 yield False
282
283 def Export(self):
284
285 if hasattr(self, "export_path"):
286 export_path = self.export_path
287 else:
288 export_path="."
289 dlg = wxFileDialog(self, _("Export Map"), export_path, "",
290 "Enhanced Metafile (*.wmf)|*.wmf",
291 wxSAVE|wxOVERWRITE_PROMPT)
292 if dlg.ShowModal() == wxID_OK:
293 self.export_path = os.path.dirname(dlg.GetPath())
294 dc = wxMetaFileDC(dlg.GetPath())
295
296 scale, offset, mapregion = output_transform(self.scale,
297 self.offset,
298 self.GetSizeTuple(),
299 dc.GetSizeTuple())
300
301 selected_layer = self.selection.SelectedLayer()
302 selected_shapes = self.selection.SelectedShapes()
303
304 width, height = self.GetSizeTuple()
305 renderer = ExportRenderer(dc, self.Map(), scale, offset,
306 region = (0, 0,
307 (width/self.scale)*scale,
308 (height/self.scale)*scale),
309 destination_region = mapregion)
310 renderer.RenderMap(selected_layer, selected_shapes)
311
312 dc.EndDrawing()
313 dc.Close()
314 dlg.Destroy()
315
316 def Print(self):
317 printer = wx.wxPrinter()
318 width, height = self.GetSizeTuple()
319 selected_layer = self.selection.SelectedLayer()
320 selected_shapes = self.selection.SelectedShapes()
321
322 printout = MapPrintout(self, self.Map(), (0, 0, width, height),
323 selected_layer, selected_shapes)
324 printer.Print(self, printout, True)
325 printout.Destroy()
326
327 def redraw(self, *args):
328 self.Refresh(False)
329
330 def full_redraw(self, *args):
331 self.bitmap = None
332 self.selection_bitmap = None
333 self.render_iter = None
334 self.redraw()
335
336 def redraw_selection(self, *args):
337 self.selection_bitmap = None
338 self.render_iter = None
339 self.redraw()
340
341 def map_projection_changed(self, map, old_proj):
342 ViewPort.map_projection_changed(self, map, old_proj)
343 self.full_redraw()
344
345 def layer_projection_changed(self, *args):
346 ViewPort.layer_projection_changed(self, args)
347 self.full_redraw()
348
349 def set_view_transform(self, scale, offset):
350 ViewPort.set_view_transform(self, scale, offset)
351 self.full_redraw()
352
353 def GetPortSizeTuple(self):
354 return self.GetSizeTuple()
355
356 def OnLeftDown(self, event):
357 self.MouseLeftDown(event)
358 if self.tool is not None:
359 self.drag_dc = wxClientDC(self)
360 self.drag_dc.SetLogicalFunction(wxINVERT)
361 self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
362 self.tool.Show(self.drag_dc)
363 self.CaptureMouse()
364 self.dragging = 1
365
366 def OnLeftUp(self, event):
367 """Handle EVT_LEFT_UP
368
369 Release the mouse if it was captured, if a tool is active call
370 its Hide method and call self.MouseLeftUp.
371 """
372 # It's important that ReleaseMouse is called before MouseLeftUp.
373 # MouseLeftUp may pop up modal dialogs which leads to an
374 # effectively frozen X session because the user can only
375 # interact with the dialog but the mouse is still grabbed by the
376 # canvas.
377 if self.dragging:
378 if self.HasCapture():
379 self.ReleaseMouse()
380 try:
381 self.tool.Hide(self.drag_dc)
382 finally:
383 self.drag_dc = None
384 self.dragging = 0
385 self.MouseLeftUp(event)
386
387 def OnMotion(self, event):
388 if self.dragging:
389 self.tool.Hide(self.drag_dc)
390
391 self.MouseMove(event)
392
393 if self.dragging:
394 self.tool.Show(self.drag_dc)
395
396 def OnLeaveWindow(self, event):
397 self.set_current_position(None)
398
399 def OnSize(self, event):
400 # the window's size has changed. We have to get a new bitmap. If
401 # we want to be clever we could try to get by without throwing
402 # everything away. E.g. when the window gets smaller, we could
403 # either keep the bitmap or create the new one from the old one.
404 # Even when the window becomes larger some parts of the bitmap
405 # could be reused.
406 self.full_redraw()
407
408 def shape_selected(self, layer, shape):
409 """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
410 # The selection object takes care that it only issues
411 # SHAPES_SELECTED messages when the set of selected shapes has
412 # actually changed, so we can do a full redraw of the
413 # selection_bitmap unconditionally.
414 ViewPort.shape_selected(self, layer, shape)
415 self.redraw_selection()
416
417 def GetTextExtent(self, text):
418 dc = wxClientDC(self)
419 font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
420 dc.SetFont(font)
421 return dc.GetTextExtent(text)
422
423 def LabelShapeAt(self, x, y, text=None):
424 """Add or remove a label at window position x, y.
425
426 If there's a label at the given position, remove it. Otherwise
427 determine the shape at the position, run the label dialog and
428 unless the user cancels the dialog, add a label.
429 """
430 label_layer = self.map.LabelLayer()
431 layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
432 if layer is None and shape_index is not None:
433 ViewPort.LabelShapeAt(self, x, y)
434 elif layer is not None:
435 text = labeldialog.run_label_dialog(self,
436 layer.ShapeStore().Table(),
437 shape_index)
438 ViewPort.LabelShapeAt(self, x, y, text)

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26