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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1552 - (hide 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 bh 404 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4 frank 910 # Frank Koormann <[email protected]>
5 bh 6 #
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 frank 910 from Thuban import _
16    
17     import os.path
18 jonathan 799
19 jonathan 1285 from wxPython.wx import wxWindow, \
20 bh 6 wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
21 jonathan 822 EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
22 bh 1456 wxPlatform, wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \
23 jonathan 1385 wxOVERWRITE_PROMPT, wxID_OK
24 bh 6
25 frank 910 # Export related stuff
26     if wxPlatform == '__WXMSW__':
27     from wxPython.wx import wxMetaFileDC
28 bh 6
29     from wxPython import wx
30    
31 bh 1456 from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
32     LAYER_VISIBILITY_CHANGED
33 bh 6
34 frank 910 from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
35 bh 6
36     import labeldialog
37    
38 bh 1454 from viewport import ViewPort, PanTool, output_transform
39 bh 6
40 jonathan 1385 class CanvasPanTool(PanTool):
41 bh 6
42 jonathan 1385 """The Canvas Pan Tool"""
43 bh 6
44     def MouseMove(self, event):
45     if self.dragging:
46 jonathan 1385 PanTool.MouseMove(self, event)
47 bh 57 sx, sy = self.start
48 bh 6 x, y = self.current
49     width, height = self.view.GetSizeTuple()
50 bh 159
51     bitmapdc = wx.wxMemoryDC()
52     bitmapdc.SelectObject(self.view.bitmap)
53    
54 bh 6 dc = self.view.drag_dc
55 bh 159 dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
56 bh 6
57     class MapPrintout(wx.wxPrintout):
58    
59     """
60     wxPrintout class for printing Thuban maps
61     """
62    
63 frank 910 def __init__(self, canvas, map, region, selected_layer, selected_shapes):
64 bh 6 wx.wxPrintout.__init__(self)
65 frank 910 self.canvas = canvas
66 bh 6 self.map = map
67 frank 910 self.region = region
68     self.selected_layer = selected_layer
69     self.selected_shapes = selected_shapes
70 bh 6
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 bh 1454 scale, offset, mapregion = output_transform(self.canvas.scale,
84     self.canvas.offset,
85     self.canvas.GetSizeTuple(),
86     self.GetPageSizePixels())
87 bh 6 resx, resy = self.GetPPIPrinter()
88 frank 910 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 jan 1035 return True
98 bh 6
99 jonathan 1385 class MapCanvas(wxWindow, ViewPort):
100 bh 6
101     """A widget that displays a map and offers some interaction"""
102    
103 bh 535 def __init__(self, parent, winid):
104 bh 6 wxWindow.__init__(self, parent, winid)
105 jonathan 1385 ViewPort.__init__(self)
106    
107 bh 6 self.SetBackgroundColour(wxColour(255, 255, 255))
108 bh 125
109     # the bitmap serving as backing store
110     self.bitmap = None
111    
112 jonathan 1344 self.backgroundColor = wx.wxWHITE_BRUSH
113    
114 bh 1552 # 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 bh 125 # subscribe the WX events we're interested in
119 bh 6 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 bh 122 EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
124 bh 125 wx.EVT_SIZE(self, self.OnSize)
125 jonathan 1344 wx.EVT_IDLE(self, self.OnIdle)
126 bh 6
127 bh 122 def __del__(self):
128     wxWindow.__del__(self)
129 jonathan 1385 ViewPort.__del__(self)
130 bh 122
131 jonathan 1385 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 bh 535
142 jonathan 1385 ViewPort.SetMap(self, map)
143 bh 535
144 jonathan 1385 if self.Map() is not None:
145     for channel in redraw_channels:
146     self.Map().Subscribe(channel, self.full_redraw)
147 bh 535
148 jonathan 1385 # 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 bh 535
153 bh 6 def OnPaint(self, event):
154     dc = wxPaintDC(self)
155 jonathan 1285
156 jonathan 1385 if self.Map() is not None and self.Map().HasLayers():
157 jonathan 1344 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 jonathan 1385 else:
162     # blit the bitmap to the screen
163     dc.BeginDrawing()
164     dc.DrawBitmap(self.bitmap, 0, 0)
165     dc.EndDrawing()
166 jonathan 1344 else:
167     # If we've got no map or if the map is empty, simply clear
168     # the screen.
169 jonathan 799
170 jonathan 1344 # 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 bh 246
179 jonathan 1344 def OnIdle(self, event):
180 bh 1552 """Idle handler. Redraw the bitmap if necessary"""
181 bh 125
182 jonathan 1344 if self.bitmap != -1:
183     return
184 bh 1552 if self.error_on_redraw:
185     return
186 bh 6
187 jonathan 1344 wxBeginBusyCursor()
188 bh 1552 try:
189     try:
190     self._do_redraw()
191     except:
192     self.error_on_redraw = True
193     raise
194     finally:
195     wxEndBusyCursor()
196 jonathan 1344
197 bh 1552 def _do_redraw(self):
198     """Called by OnIdle to do the actual redraw.
199     """
200     width, height = self.GetSizeTuple()
201 bh 57
202 bh 1552 bitmap = wx.wxEmptyBitmap(width, height)
203     dc = wx.wxMemoryDC()
204     dc.SelectObject(bitmap)
205     dc.BeginDrawing()
206 bh 6
207 bh 1552 dc.SetBackground(self.backgroundColor)
208     dc.Clear()
209 bh 57
210 bh 1552 selected_layer = self.selection.SelectedLayer()
211     selected_shapes = self.selection.SelectedShapes()
212 bh 149
213 bh 1552 # draw the map into the bitmap
214     renderer = ScreenRenderer(dc, self.scale, self.offset)
215 bh 125
216 bh 1552 # 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 jonathan 1344
221 bh 1552 dc.EndDrawing()
222     dc.SelectObject(wx.wxNullBitmap)
223 bh 125
224 bh 1552 self.bitmap = bitmap
225 jonathan 1344 # This causes a paint event that then draws the bitmap
226     self.redraw()
227 bh 6
228 frank 910 def Export(self):
229 jonathan 967
230 frank 910 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 bh 1454 scale, offset, mapregion = output_transform(self.scale,
242     self.offset,
243     self.GetSizeTuple(),
244     dc.GetSizeTuple())
245 frank 910
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 jonathan 1385 renderer.RenderMap(self.Map(),
255 frank 910 (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 bh 6 def Print(self):
265     printer = wx.wxPrinter()
266 frank 910 width, height = self.GetSizeTuple()
267     selected_layer = self.selection.SelectedLayer()
268     selected_shapes = self.selection.SelectedShapes()
269    
270 jonathan 1385 printout = MapPrintout(self, self.Map(), (0, 0, width, height),
271 frank 910 selected_layer, selected_shapes)
272 jan 1035 printer.Print(self, printout, True)
273 bh 6 printout.Destroy()
274 bh 246
275 bh 6 def redraw(self, *args):
276 jonathan 1344 self.Refresh(False)
277 bh 6
278 bh 125 def full_redraw(self, *args):
279     self.bitmap = None
280     self.redraw()
281    
282 jonathan 1385 def map_projection_changed(self, map, old_proj):
283     ViewPort.map_projection_changed(self, map, old_proj)
284 bh 125 self.full_redraw()
285 bh 6
286 jonathan 1221 def layer_projection_changed(self, *args):
287 jonathan 1385 ViewPort.layer_projection_changed(self, args)
288 jonathan 1221 self.full_redraw()
289    
290 bh 6 def set_view_transform(self, scale, offset):
291 jonathan 1385 ViewPort.set_view_transform(self, scale, offset)
292 bh 125 self.full_redraw()
293 bh 6
294 jonathan 1385 def GetPortSizeTuple(self):
295     return self.GetSizeTuple()
296 jonathan 967
297 bh 6 def OnLeftDown(self, event):
298 jonathan 1385 self.MouseLeftDown(event)
299 bh 6 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 bh 1460 self.CaptureMouse()
305 bh 6 self.dragging = 1
306 bh 246
307 bh 6 def OnLeftUp(self, event):
308 jonathan 1385 self.MouseLeftUp(event)
309 bh 6 if self.dragging:
310 bh 261 self.ReleaseMouse()
311 bh 404 try:
312     self.tool.Hide(self.drag_dc)
313     finally:
314     self.drag_dc = None
315     self.dragging = 0
316 bh 6
317     def OnMotion(self, event):
318     if self.dragging:
319     self.tool.Hide(self.drag_dc)
320 jonathan 1385
321     self.MouseMove(event)
322    
323     if self.dragging:
324 bh 6 self.tool.Show(self.drag_dc)
325    
326 bh 122 def OnLeaveWindow(self, event):
327     self.set_current_position(None)
328    
329 bh 125 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 bh 6 def shape_selected(self, layer, shape):
339 bh 535 """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 jonathan 1385 ViewPort.shape_selected(self, layer, shape)
347 bh 535 self.full_redraw()
348 bh 6
349 jonathan 1385 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 bh 159
355 jonathan 1385 def LabelShapeAt(self, x, y, text=None):
356 bh 295 """Add or remove a label at window position x, y.
357 bh 1454
358 bh 295 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 jonathan 1385 unless the user cancels the dialog, add a label.
361 bh 295 """
362 bh 6 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 jonathan 1385 ViewPort.LabelShapeAt(self, x, y)
366 bh 6 elif layer is not None:
367 bh 1219 text = labeldialog.run_label_dialog(self,
368     layer.ShapeStore().Table(),
369     shape_index)
370 jonathan 1385 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