/[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 1552 - (show annotations)
Wed Aug 6 17:21:32 2003 UTC (21 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 12721 byte(s)
* Thuban/UI/renderer.py (MapRenderer): Most of the code/methods in
this class is now in BaseRenderer. This class is now practically
only a specialization of BaseRenderer for rendering to an actual
wx DC.
(ScreenRenderer.draw_shape_layer): Use self.low_level_renderer()
to get the shapetype specific rendering functions.

* test/test_baserenderer.py: New. Test cases for BaseRenderer

* Thuban/UI/view.py (MapCanvas.__init__): New instance variable
error_on_redraw to guard agains endless loops and stack overflows
when there's a bug in the rendering code that raises exceptions.
(MapCanvas.OnIdle, MapCanvas._do_redraw): Split the actual
rendering into a separate method _do_redraw so that error handling
is a bit easier. When an exception occurs, set error_on_redraw to
true. When it's true on entry to OnIdle do nothing and return
immediately.

1 # Copyright (c) 2001, 2002, 2003 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 __version__ = "$Revision$"
14
15 from Thuban import _
16
17 import os.path
18
19 from wxPython.wx import wxWindow, \
20 wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
21 EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
22 wxPlatform, wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \
23 wxOVERWRITE_PROMPT, wxID_OK
24
25 # Export related stuff
26 if wxPlatform == '__WXMSW__':
27 from wxPython.wx import wxMetaFileDC
28
29 from wxPython import wx
30
31 from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
32 LAYER_VISIBILITY_CHANGED
33
34 from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
35
36 import labeldialog
37
38 from viewport import ViewPort, PanTool, output_transform
39
40 class CanvasPanTool(PanTool):
41
42 """The Canvas Pan Tool"""
43
44 def MouseMove(self, event):
45 if self.dragging:
46 PanTool.MouseMove(self, event)
47 sx, sy = self.start
48 x, y = self.current
49 width, height = self.view.GetSizeTuple()
50
51 bitmapdc = wx.wxMemoryDC()
52 bitmapdc.SelectObject(self.view.bitmap)
53
54 dc = self.view.drag_dc
55 dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
56
57 class MapPrintout(wx.wxPrintout):
58
59 """
60 wxPrintout class for printing Thuban maps
61 """
62
63 def __init__(self, canvas, map, region, selected_layer, selected_shapes):
64 wx.wxPrintout.__init__(self)
65 self.canvas = canvas
66 self.map = map
67 self.region = region
68 self.selected_layer = selected_layer
69 self.selected_shapes = selected_shapes
70
71 def GetPageInfo(self):
72 return (1, 1, 1, 1)
73
74 def HasPage(self, pagenum):
75 return pagenum == 1
76
77 def OnPrintPage(self, pagenum):
78 if pagenum == 1:
79 self.draw_on_dc(self.GetDC())
80
81 def draw_on_dc(self, dc):
82 width, height = self.GetPageSizePixels()
83 scale, offset, mapregion = output_transform(self.canvas.scale,
84 self.canvas.offset,
85 self.canvas.GetSizeTuple(),
86 self.GetPageSizePixels())
87 resx, resy = self.GetPPIPrinter()
88 renderer = PrinterRenderer(dc, scale, offset, resolution = resy)
89 x, y, width, height = self.region
90 canvas_scale = self.canvas.scale
91 renderer.RenderMap(self.map,
92 (0,0,
93 (width/canvas_scale)*scale,
94 (height/canvas_scale)*scale),
95 mapregion,
96 self.selected_layer, self.selected_shapes)
97 return True
98
99 class MapCanvas(wxWindow, ViewPort):
100
101 """A widget that displays a map and offers some interaction"""
102
103 def __init__(self, parent, winid):
104 wxWindow.__init__(self, parent, winid)
105 ViewPort.__init__(self)
106
107 self.SetBackgroundColour(wxColour(255, 255, 255))
108
109 # the bitmap serving as backing store
110 self.bitmap = None
111
112 self.backgroundColor = wx.wxWHITE_BRUSH
113
114 # Set to true if there ever is an error during redraw. There
115 # should never be errors, but unfortunately bugs happen.
116 self.error_on_redraw = 0
117
118 # subscribe the WX events we're interested in
119 EVT_PAINT(self, self.OnPaint)
120 EVT_LEFT_DOWN(self, self.OnLeftDown)
121 EVT_LEFT_UP(self, self.OnLeftUp)
122 EVT_MOTION(self, self.OnMotion)
123 EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
124 wx.EVT_SIZE(self, self.OnSize)
125 wx.EVT_IDLE(self, self.OnIdle)
126
127 def __del__(self):
128 wxWindow.__del__(self)
129 ViewPort.__del__(self)
130
131 def PanTool(self):
132 """Start the canvas pan tool"""
133 self.SelectTool(CanvasPanTool(self))
134
135 def SetMap(self, map):
136 redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
137 LAYER_VISIBILITY_CHANGED)
138 if self.Map() is not None:
139 for channel in redraw_channels:
140 self.Map().Unsubscribe(channel, self.full_redraw)
141
142 ViewPort.SetMap(self, map)
143
144 if self.Map() is not None:
145 for channel in redraw_channels:
146 self.Map().Subscribe(channel, self.full_redraw)
147
148 # force a redraw. If map is not empty, it's already been called
149 # by FitMapToWindow but if map is empty it hasn't been called
150 # yet so we have to explicitly call it.
151 self.full_redraw()
152
153 def OnPaint(self, event):
154 dc = wxPaintDC(self)
155
156 if self.Map() is not None and self.Map().HasLayers():
157 if self.bitmap in (None, -1):
158 # set the flag that we should redraw the
159 # bitmap in idle time
160 self.bitmap = -1
161 else:
162 # blit the bitmap to the screen
163 dc.BeginDrawing()
164 dc.DrawBitmap(self.bitmap, 0, 0)
165 dc.EndDrawing()
166 else:
167 # If we've got no map or if the map is empty, simply clear
168 # the screen.
169
170 # XXX it's probably possible to get rid of this. The
171 # background color of the window is already white and the
172 # only thing we may have to do is to call self.Refresh()
173 # with a true argument in the right places.
174 dc.BeginDrawing()
175 dc.SetBackground(self.backgroundColor)
176 dc.Clear()
177 dc.EndDrawing()
178
179 def OnIdle(self, event):
180 """Idle handler. Redraw the bitmap if necessary"""
181
182 if self.bitmap != -1:
183 return
184 if self.error_on_redraw:
185 return
186
187 wxBeginBusyCursor()
188 try:
189 try:
190 self._do_redraw()
191 except:
192 self.error_on_redraw = True
193 raise
194 finally:
195 wxEndBusyCursor()
196
197 def _do_redraw(self):
198 """Called by OnIdle to do the actual redraw.
199 """
200 width, height = self.GetSizeTuple()
201
202 bitmap = wx.wxEmptyBitmap(width, height)
203 dc = wx.wxMemoryDC()
204 dc.SelectObject(bitmap)
205 dc.BeginDrawing()
206
207 dc.SetBackground(self.backgroundColor)
208 dc.Clear()
209
210 selected_layer = self.selection.SelectedLayer()
211 selected_shapes = self.selection.SelectedShapes()
212
213 # draw the map into the bitmap
214 renderer = ScreenRenderer(dc, self.scale, self.offset)
215
216 # Pass the entire bitmap as update region to the renderer.
217 # We're redrawing the whole bitmap, after all.
218 renderer.RenderMap(self.Map(), (0, 0, width, height),
219 selected_layer, selected_shapes)
220
221 dc.EndDrawing()
222 dc.SelectObject(wx.wxNullBitmap)
223
224 self.bitmap = bitmap
225 # This causes a paint event that then draws the bitmap
226 self.redraw()
227
228 def Export(self):
229
230 if hasattr(self, "export_path"):
231 export_path = self.export_path
232 else:
233 export_path="."
234 dlg = wxFileDialog(self, _("Export Map"), export_path, "",
235 "Enhanced Metafile (*.wmf)|*.wmf",
236 wxSAVE|wxOVERWRITE_PROMPT)
237 if dlg.ShowModal() == wxID_OK:
238 self.export_path = os.path.dirname(dlg.GetPath())
239 dc = wxMetaFileDC(dlg.GetPath())
240
241 scale, offset, mapregion = output_transform(self.scale,
242 self.offset,
243 self.GetSizeTuple(),
244 dc.GetSizeTuple())
245
246 selected_layer = self.selection.SelectedLayer()
247 selected_shapes = self.selection.SelectedShapes()
248
249 renderer = ExportRenderer(dc, scale, offset)
250
251 # Pass the entire bitmap as update region to the renderer.
252 # We're redrawing the whole bitmap, after all.
253 width, height = self.GetSizeTuple()
254 renderer.RenderMap(self.Map(),
255 (0,0,
256 (width/self.scale)*scale,
257 (height/self.scale)*scale),
258 mapregion,
259 selected_layer, selected_shapes)
260 dc.EndDrawing()
261 dc.Close()
262 dlg.Destroy()
263
264 def Print(self):
265 printer = wx.wxPrinter()
266 width, height = self.GetSizeTuple()
267 selected_layer = self.selection.SelectedLayer()
268 selected_shapes = self.selection.SelectedShapes()
269
270 printout = MapPrintout(self, self.Map(), (0, 0, width, height),
271 selected_layer, selected_shapes)
272 printer.Print(self, printout, True)
273 printout.Destroy()
274
275 def redraw(self, *args):
276 self.Refresh(False)
277
278 def full_redraw(self, *args):
279 self.bitmap = None
280 self.redraw()
281
282 def map_projection_changed(self, map, old_proj):
283 ViewPort.map_projection_changed(self, map, old_proj)
284 self.full_redraw()
285
286 def layer_projection_changed(self, *args):
287 ViewPort.layer_projection_changed(self, args)
288 self.full_redraw()
289
290 def set_view_transform(self, scale, offset):
291 ViewPort.set_view_transform(self, scale, offset)
292 self.full_redraw()
293
294 def GetPortSizeTuple(self):
295 return self.GetSizeTuple()
296
297 def OnLeftDown(self, event):
298 self.MouseLeftDown(event)
299 if self.tool is not None:
300 self.drag_dc = wxClientDC(self)
301 self.drag_dc.SetLogicalFunction(wxINVERT)
302 self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
303 self.tool.Show(self.drag_dc)
304 self.CaptureMouse()
305 self.dragging = 1
306
307 def OnLeftUp(self, event):
308 self.MouseLeftUp(event)
309 if self.dragging:
310 self.ReleaseMouse()
311 try:
312 self.tool.Hide(self.drag_dc)
313 finally:
314 self.drag_dc = None
315 self.dragging = 0
316
317 def OnMotion(self, event):
318 if self.dragging:
319 self.tool.Hide(self.drag_dc)
320
321 self.MouseMove(event)
322
323 if self.dragging:
324 self.tool.Show(self.drag_dc)
325
326 def OnLeaveWindow(self, event):
327 self.set_current_position(None)
328
329 def OnSize(self, event):
330 # the window's size has changed. We have to get a new bitmap. If
331 # we want to be clever we could try to get by without throwing
332 # everything away. E.g. when the window gets smaller, we could
333 # either keep the bitmap or create the new one from the old one.
334 # Even when the window becomes larger some parts of the bitmap
335 # could be reused.
336 self.full_redraw()
337
338 def shape_selected(self, layer, shape):
339 """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
340 # The selection object takes care that it only issues
341 # SHAPES_SELECTED messages when the set of selected shapes has
342 # actually changed, so we can do a full redraw unconditionally.
343 # FIXME: We should perhaps try to limit the redraw to the are
344 # actually covered by the shapes before and after the selection
345 # change.
346 ViewPort.shape_selected(self, layer, shape)
347 self.full_redraw()
348
349 def GetTextExtent(self, text):
350 dc = wxClientDC(self)
351 font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
352 dc.SetFont(font)
353 return dc.GetTextExtent(text)
354
355 def LabelShapeAt(self, x, y, text=None):
356 """Add or remove a label at window position x, y.
357
358 If there's a label at the given position, remove it. Otherwise
359 determine the shape at the position, run the label dialog and
360 unless the user cancels the dialog, add a label.
361 """
362 label_layer = self.map.LabelLayer()
363 layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
364 if layer is None and shape_index is not None:
365 ViewPort.LabelShapeAt(self, x, y)
366 elif layer is not None:
367 text = labeldialog.run_label_dialog(self,
368 layer.ShapeStore().Table(),
369 shape_index)
370 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