/[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 2068 - (show annotations)
Mon Feb 16 19:42:04 2004 UTC (21 years ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 15664 byte(s)
(MapCanvas.Export): Remove accidentally added
line

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.bitmap is None
190 or self.render_iter is not None
191 or (self.HasSelectedShapes()
192 and self.selection_bitmap is None)):
193 event.RequestMore(self._do_redraw())
194
195 def _do_redraw(self):
196 """Redraw a bit and return whether this method has to be called again.
197
198 Called by OnIdle to handle the actual redraw. Redraw is
199 incremental for both the bitmap with the normal layers and the
200 bitmap with the selection.
201 """
202 finished = False
203 if self.render_iter is not None:
204 try:
205 if self.render_iter.next():
206 # Redraw if the last preview redraw was some time
207 # ago and the user is not currently dragging the
208 # mouse because redrawing would interfere with what
209 # the current tool is drawing on the window.
210 if not self.dragging \
211 and time.time() - self.render_last_preview > 0.5:
212 client_dc = wxClientDC(self)
213 client_dc.BeginDrawing()
214 client_dc.DrawBitmap(self.bitmap, 0, 0)
215 client_dc.EndDrawing()
216 self.render_last_preview = time.time()
217 else:
218 self.render_iter = None
219 # Redraw if not dragging because redrawing would
220 # interfere with what the current tool is drawing on
221 # the window.
222 if not self.dragging:
223 self.redraw()
224 finished = True
225 except StopIteration:
226 finished = True
227 self.render_iter = None
228 except:
229 finished = True
230 self.render_iter = None
231 traceback.print_exc()
232 else:
233 self.render_iter = self._render_iterator()
234 self.render_last_preview = time.time()
235 return not finished
236
237 def _render_iterator(self):
238 width, height = self.GetSizeTuple()
239 dc = wx.wxMemoryDC()
240
241 render_start = time.time()
242
243 if self.bitmap is None:
244 self.bitmap = wx.wxEmptyBitmap(width, height)
245 dc.SelectObject(self.bitmap)
246 dc.BeginDrawing()
247
248 dc.SetBackground(self.backgroundColor)
249 dc.Clear()
250
251 # draw the map into the bitmap
252 renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
253 (0, 0, width, height))
254 for cont in renderer.RenderMapIncrementally():
255 yield True
256
257 dc.EndDrawing()
258 dc.SelectObject(wx.wxNullBitmap)
259
260 if self.HasSelectedShapes() and self.selection_bitmap is None:
261 bitmap = wx.wxEmptyBitmap(width, height)
262 dc.SelectObject(bitmap)
263 dc.BeginDrawing()
264 dc.SetBackground(wx.wxWHITE_BRUSH)
265 dc.Clear()
266
267 renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
268 (0, 0, width, height))
269 layer = self.SelectedLayer()
270 shapes = self.selection.SelectedShapes()
271 for cont in renderer.draw_selection_incrementally(layer, shapes):
272 yield True
273
274 dc.EndDrawing()
275 dc.SelectObject(wx.wxNullBitmap)
276
277 bitmap.SetMask(wx.wxMaskColour(bitmap, wx.wxWHITE))
278 self.selection_bitmap = bitmap
279
280 yield False
281
282 def Export(self):
283
284 if hasattr(self, "export_path"):
285 export_path = self.export_path
286 else:
287 export_path="."
288 dlg = wxFileDialog(self, _("Export Map"), export_path, "",
289 "Enhanced Metafile (*.wmf)|*.wmf",
290 wxSAVE|wxOVERWRITE_PROMPT)
291 if dlg.ShowModal() == wxID_OK:
292 self.export_path = os.path.dirname(dlg.GetPath())
293 dc = wxMetaFileDC(dlg.GetPath())
294
295 scale, offset, mapregion = output_transform(self.scale,
296 self.offset,
297 self.GetSizeTuple(),
298 dc.GetSizeTuple())
299
300 selected_layer = self.selection.SelectedLayer()
301 selected_shapes = self.selection.SelectedShapes()
302
303 width, height = self.GetSizeTuple()
304 renderer = ExportRenderer(dc, self.Map(), scale, offset,
305 region = (0, 0,
306 (width/self.scale)*scale,
307 (height/self.scale)*scale),
308 destination_region = mapregion)
309 renderer.RenderMap(selected_layer, selected_shapes)
310
311 dc.EndDrawing()
312 dc.Close()
313 dlg.Destroy()
314
315 def Print(self):
316 printer = wx.wxPrinter()
317 width, height = self.GetSizeTuple()
318 selected_layer = self.selection.SelectedLayer()
319 selected_shapes = self.selection.SelectedShapes()
320
321 printout = MapPrintout(self, self.Map(), (0, 0, width, height),
322 selected_layer, selected_shapes)
323 printer.Print(self, printout, True)
324 printout.Destroy()
325
326 def redraw(self, *args):
327 self.Refresh(False)
328
329 def full_redraw(self, *args):
330 self.bitmap = None
331 self.selection_bitmap = None
332 self.render_iter = None
333 self.redraw()
334
335 def redraw_selection(self, *args):
336 self.selection_bitmap = None
337 self.render_iter = None
338 self.redraw()
339
340 def map_projection_changed(self, map, old_proj):
341 ViewPort.map_projection_changed(self, map, old_proj)
342 self.full_redraw()
343
344 def layer_projection_changed(self, *args):
345 ViewPort.layer_projection_changed(self, args)
346 self.full_redraw()
347
348 def set_view_transform(self, scale, offset):
349 ViewPort.set_view_transform(self, scale, offset)
350 self.full_redraw()
351
352 def GetPortSizeTuple(self):
353 return self.GetSizeTuple()
354
355 def OnLeftDown(self, event):
356 self.MouseLeftDown(event)
357 if self.tool is not None:
358 self.drag_dc = wxClientDC(self)
359 self.drag_dc.SetLogicalFunction(wxINVERT)
360 self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
361 self.tool.Show(self.drag_dc)
362 self.CaptureMouse()
363 self.dragging = 1
364
365 def OnLeftUp(self, event):
366 """Handle EVT_LEFT_UP
367
368 Release the mouse if it was captured, if a tool is active call
369 its Hide method and call self.MouseLeftUp.
370 """
371 # It's important that ReleaseMouse is called before MouseLeftUp.
372 # MouseLeftUp may pop up modal dialogs which leads to an
373 # effectively frozen X session because the user can only
374 # interact with the dialog but the mouse is still grabbed by the
375 # canvas.
376 if self.dragging:
377 if self.HasCapture():
378 self.ReleaseMouse()
379 try:
380 self.tool.Hide(self.drag_dc)
381 finally:
382 self.drag_dc = None
383 self.dragging = 0
384 self.MouseLeftUp(event)
385
386 def OnMotion(self, event):
387 if self.dragging:
388 self.tool.Hide(self.drag_dc)
389
390 self.MouseMove(event)
391
392 if self.dragging:
393 self.tool.Show(self.drag_dc)
394
395 def OnLeaveWindow(self, event):
396 self.set_current_position(None)
397
398 def OnSize(self, event):
399 # the window's size has changed. We have to get a new bitmap. If
400 # we want to be clever we could try to get by without throwing
401 # everything away. E.g. when the window gets smaller, we could
402 # either keep the bitmap or create the new one from the old one.
403 # Even when the window becomes larger some parts of the bitmap
404 # could be reused.
405 self.full_redraw()
406
407 def shape_selected(self, layer, shape):
408 """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
409 # The selection object takes care that it only issues
410 # SHAPES_SELECTED messages when the set of selected shapes has
411 # actually changed, so we can do a full redraw of the
412 # selection_bitmap unconditionally.
413 ViewPort.shape_selected(self, layer, shape)
414 self.redraw_selection()
415
416 def GetTextExtent(self, text):
417 dc = wxClientDC(self)
418 font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
419 dc.SetFont(font)
420 return dc.GetTextExtent(text)
421
422 def LabelShapeAt(self, x, y, text=None):
423 """Add or remove a label at window position x, y.
424
425 If there's a label at the given position, remove it. Otherwise
426 determine the shape at the position, run the label dialog and
427 unless the user cancels the dialog, add a label.
428 """
429 label_layer = self.map.LabelLayer()
430 layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
431 if layer is None and shape_index is not None:
432 ViewPort.LabelShapeAt(self, x, y)
433 elif layer is not None:
434 text = labeldialog.run_label_dialog(self,
435 layer.ShapeStore().Table(),
436 shape_index)
437 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