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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26