/[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 2066 - (show annotations)
Mon Feb 16 19:39:28 2004 UTC (21 years ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 15728 byte(s)
(MapCanvas.Export): Avoid UnboundLocalError.

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 renderer.RenderMap(selected_layer, selected_shapes)
311
312 dc.EndDrawing()
313 dc.Close()
314 dlg.Destroy()
315
316 def Print(self):
317 printer = wx.wxPrinter()
318 width, height = self.GetSizeTuple()
319 selected_layer = self.selection.SelectedLayer()
320 selected_shapes = self.selection.SelectedShapes()
321
322 printout = MapPrintout(self, self.Map(), (0, 0, width, height),
323 selected_layer, selected_shapes)
324 printer.Print(self, printout, True)
325 printout.Destroy()
326
327 def redraw(self, *args):
328 self.Refresh(False)
329
330 def full_redraw(self, *args):
331 self.bitmap = None
332 self.selection_bitmap = None
333 self.render_iter = None
334 self.redraw()
335
336 def redraw_selection(self, *args):
337 self.selection_bitmap = None
338 self.render_iter = None
339 self.redraw()
340
341 def map_projection_changed(self, map, old_proj):
342 ViewPort.map_projection_changed(self, map, old_proj)
343 self.full_redraw()
344
345 def layer_projection_changed(self, *args):
346 ViewPort.layer_projection_changed(self, args)
347 self.full_redraw()
348
349 def set_view_transform(self, scale, offset):
350 ViewPort.set_view_transform(self, scale, offset)
351 self.full_redraw()
352
353 def GetPortSizeTuple(self):
354 return self.GetSizeTuple()
355
356 def OnLeftDown(self, event):
357 self.MouseLeftDown(event)
358 if self.tool is not None:
359 self.drag_dc = wxClientDC(self)
360 self.drag_dc.SetLogicalFunction(wxINVERT)
361 self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
362 self.tool.Show(self.drag_dc)
363 self.CaptureMouse()
364 self.dragging = 1
365
366 def OnLeftUp(self, event):
367 """Handle EVT_LEFT_UP
368
369 Release the mouse if it was captured, if a tool is active call
370 its Hide method and call self.MouseLeftUp.
371 """
372 # It's important that ReleaseMouse is called before MouseLeftUp.
373 # MouseLeftUp may pop up modal dialogs which leads to an
374 # effectively frozen X session because the user can only
375 # interact with the dialog but the mouse is still grabbed by the
376 # canvas.
377 if self.dragging:
378 if self.HasCapture():
379 self.ReleaseMouse()
380 try:
381 self.tool.Hide(self.drag_dc)
382 finally:
383 self.drag_dc = None
384 self.dragging = 0
385 self.MouseLeftUp(event)
386
387 def OnMotion(self, event):
388 if self.dragging:
389 self.tool.Hide(self.drag_dc)
390
391 self.MouseMove(event)
392
393 if self.dragging:
394 self.tool.Show(self.drag_dc)
395
396 def OnLeaveWindow(self, event):
397 self.set_current_position(None)
398
399 def OnSize(self, event):
400 # the window's size has changed. We have to get a new bitmap. If
401 # we want to be clever we could try to get by without throwing
402 # everything away. E.g. when the window gets smaller, we could
403 # either keep the bitmap or create the new one from the old one.
404 # Even when the window becomes larger some parts of the bitmap
405 # could be reused.
406 self.full_redraw()
407
408 def shape_selected(self, layer, shape):
409 """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
410 # The selection object takes care that it only issues
411 # SHAPES_SELECTED messages when the set of selected shapes has
412 # actually changed, so we can do a full redraw of the
413 # selection_bitmap unconditionally.
414 ViewPort.shape_selected(self, layer, shape)
415 self.redraw_selection()
416
417 def GetTextExtent(self, text):
418 dc = wxClientDC(self)
419 font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
420 dc.SetFont(font)
421 return dc.GetTextExtent(text)
422
423 def LabelShapeAt(self, x, y, text=None):
424 """Add or remove a label at window position x, y.
425
426 If there's a label at the given position, remove it. Otherwise
427 determine the shape at the position, run the label dialog and
428 unless the user cancels the dialog, add a label.
429 """
430 label_layer = self.map.LabelLayer()
431 layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
432 if layer is None and shape_index is not None:
433 ViewPort.LabelShapeAt(self, x, y)
434 elif layer is not None:
435 text = labeldialog.run_label_dialog(self,
436 layer.ShapeStore().Table(),
437 shape_index)
438 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