/[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 2511 - (show annotations)
Mon Dec 27 16:31:32 2004 UTC (20 years, 2 months ago) by russell
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 16169 byte(s)
The middle button now pans the map view.

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