/[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 2734 - (hide 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 dpinte 2700 # opyright (c) 2001, 2002, 2003, 2004 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 bh 1866 from __future__ import generators
14    
15 bh 6 __version__ = "$Revision$"
16 bh 1866 # $Source$
17     # $Id$
18 bh 6
19 frank 910 import os.path
20 bh 1866 import time
21     import traceback
22 jonathan 799
23 dpinte 2700 import wx
24 bh 6
25 frank 910 # Export related stuff
26 dpinte 2700 if wx.Platform == '__WXMSW__':
27     from wx import MetaFileDC
28 bh 6
29 bh 1866 from Thuban import _
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 dpinte 2700 bitmapdc = wx.MemoryDC()
52 bh 1866 bitmapdc.SelectObject(self.view.PreviewBitmap())
53 bh 159
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 dpinte 2700 class MapPrintout(wx.Printout):
58 bh 6
59     """
60     wxPrintout class for printing Thuban maps
61     """
62    
63 frank 910 def __init__(self, canvas, map, region, selected_layer, selected_shapes):
64 dpinte 2700 wx.Printout.__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 bh 1866 canvas_scale = self.canvas.scale
89 frank 910 x, y, width, height = self.region
90 bh 1866 renderer = PrinterRenderer(dc, self.map, scale, offset,
91 bh 2454 region = (mapregion[0], mapregion[1],
92 bh 1866 (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 jan 1035 return True
98 bh 6
99 bh 1866
100 dpinte 2700 class MapCanvas(wx.Window, ViewPort):
101 bh 6
102     """A widget that displays a map and offers some interaction"""
103    
104 bh 535 def __init__(self, parent, winid):
105 dpinte 2700 wx.Window.__init__(self, parent, winid)
106 jonathan 1385 ViewPort.__init__(self)
107    
108 dpinte 2700 self.SetBackgroundColour(wx.Colour(255, 255, 255))
109 bh 125
110     # the bitmap serving as backing store
111     self.bitmap = None
112 bh 1866 # the monochrome bitmap with the selection if any
113     self.selection_bitmap = None
114 bh 125
115 dpinte 2700 self.backgroundColor = wx.WHITE_BRUSH
116 jonathan 1344
117 bh 1866 # The rendering iterator object. Used when rendering
118     # incrementally
119     self.render_iter = None
120 bh 1552
121 bh 125 # subscribe the WX events we're interested in
122 dpinte 2700 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 bh 6
132 bh 122 def __del__(self):
133 dpinte 2700 wx.Window.__del__(self)
134 jonathan 1385 ViewPort.__del__(self)
135 bh 122
136 bh 1866 def PreviewBitmap(self):
137     return self.bitmap
138    
139 jonathan 1385 def PanTool(self):
140     """Start the canvas pan tool"""
141     self.SelectTool(CanvasPanTool(self))
142 dpinte 2700
143 jonathan 1385 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 bh 535
150 jonathan 1385 ViewPort.SetMap(self, map)
151 bh 535
152 jonathan 1385 if self.Map() is not None:
153     for channel in redraw_channels:
154     self.Map().Subscribe(channel, self.full_redraw)
155 bh 535
156 jonathan 1385 # 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 bh 535
161 bh 6 def OnPaint(self, event):
162 dpinte 2700 dc = wx.PaintDC(self)
163 jonathan 1385 if self.Map() is not None and self.Map().HasLayers():
164 bh 1866 if self.bitmap is not None:
165 jonathan 1385 dc.BeginDrawing()
166     dc.DrawBitmap(self.bitmap, 0, 0)
167 bh 1866 if self.selection_bitmap is not None:
168     dc.DrawBitmap(self.selection_bitmap, 0, 0, True)
169 jonathan 1385 dc.EndDrawing()
170 jonathan 1344 else:
171     # If we've got no map or if the map is empty, simply clear
172     # the screen.
173 jonathan 799
174 jonathan 1344 # 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 bh 246
183 jonathan 1344 def OnIdle(self, event):
184 bh 1552 """Idle handler. Redraw the bitmap if necessary"""
185 bh 2072 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 bh 1866 event.RequestMore(self._do_redraw())
191 bh 125
192 bh 1866 def _do_redraw(self):
193     """Redraw a bit and return whether this method has to be called again.
194 bh 6
195 bh 1866 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 bh 1552 try:
202 bh 1866 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 dpinte 2700 client_dc = wx.ClientDC(self)
210 bh 1866 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 bh 1552 except:
226 bh 1866 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 jonathan 1344
234 bh 1866 def _render_iterator(self):
235 bh 1552 width, height = self.GetSizeTuple()
236 dpinte 2700 dc = wx.MemoryDC()
237 bh 6
238 bh 1866 render_start = time.time()
239 bh 57
240 bh 1866 if self.bitmap is None:
241 dpinte 2700 self.bitmap = wx.EmptyBitmap(width, height)
242 bh 1866 dc.SelectObject(self.bitmap)
243     dc.BeginDrawing()
244 bh 149
245 bh 1866 dc.SetBackground(self.backgroundColor)
246     dc.Clear()
247 bh 125
248 bh 1866 # 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 jonathan 1344
254 bh 1866 dc.EndDrawing()
255 dpinte 2700 dc.SelectObject(wx.NullBitmap)
256 bh 125
257 bh 1866 if self.HasSelectedShapes() and self.selection_bitmap is None:
258 dpinte 2700 bitmap = wx.EmptyBitmap(width, height)
259 bh 1866 dc.SelectObject(bitmap)
260     dc.BeginDrawing()
261 dpinte 2700 dc.SetBackground(wx.WHITE_BRUSH)
262 bh 1866 dc.Clear()
263 bh 6
264 bh 1866 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 dpinte 2700 dc.SelectObject(wx.NullBitmap)
273 bh 1866
274 dpinte 2700 bitmap.SetMask(wx.Mask(bitmap, wx.WHITE))
275 bh 1866 self.selection_bitmap = bitmap
276    
277     yield False
278    
279 frank 910 def Export(self):
280 jonathan 967
281 frank 910 if hasattr(self, "export_path"):
282     export_path = self.export_path
283     else:
284     export_path="."
285 dpinte 2700 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 frank 910 self.export_path = os.path.dirname(dlg.GetPath())
290 dpinte 2700 dc = wx.MetaFileDC(dlg.GetPath())
291    
292 bh 1454 scale, offset, mapregion = output_transform(self.scale,
293     self.offset,
294     self.GetSizeTuple(),
295     dc.GetSizeTuple())
296 frank 910
297     selected_layer = self.selection.SelectedLayer()
298     selected_shapes = self.selection.SelectedShapes()
299    
300 bh 2066 width, height = self.GetSizeTuple()
301 bh 1866 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 bh 2066 renderer.RenderMap(selected_layer, selected_shapes)
307 frank 910
308     dc.EndDrawing()
309     dc.Close()
310     dlg.Destroy()
311 dpinte 2700
312 bh 6 def Print(self):
313 dpinte 2700 printer = wx.Printer()
314 frank 910 width, height = self.GetSizeTuple()
315     selected_layer = self.selection.SelectedLayer()
316     selected_shapes = self.selection.SelectedShapes()
317 dpinte 2700
318     printout = MapPrintout(self, self.Map(), (0, 0, width, height),
319 frank 910 selected_layer, selected_shapes)
320 jan 1035 printer.Print(self, printout, True)
321 bh 6 printout.Destroy()
322 bh 246
323 bh 6 def redraw(self, *args):
324 jonathan 1344 self.Refresh(False)
325 bh 6
326 bh 125 def full_redraw(self, *args):
327     self.bitmap = None
328 bh 1866 self.selection_bitmap = None
329     self.render_iter = None
330 bh 125 self.redraw()
331    
332 bh 1866 def redraw_selection(self, *args):
333     self.selection_bitmap = None
334     self.render_iter = None
335     self.redraw()
336    
337 jonathan 1385 def map_projection_changed(self, map, old_proj):
338     ViewPort.map_projection_changed(self, map, old_proj)
339 bh 125 self.full_redraw()
340 bh 6
341 jonathan 1221 def layer_projection_changed(self, *args):
342 jonathan 1385 ViewPort.layer_projection_changed(self, args)
343 jonathan 1221 self.full_redraw()
344    
345 bh 6 def set_view_transform(self, scale, offset):
346 jonathan 1385 ViewPort.set_view_transform(self, scale, offset)
347 bh 125 self.full_redraw()
348 bh 6
349 jonathan 1385 def GetPortSizeTuple(self):
350     return self.GetSizeTuple()
351 jonathan 967
352 russell 2511 def OnMiddleDown(self, event):
353     self.remembertool = self.tool
354 russell 2599 if self.Map() is not None and self.Map().HasLayers():
355 russell 2511 self.PanTool()
356 russell 2599 self.OnLeftDown(event)
357 russell 2511
358     def OnMiddleUp(self, event):
359     self.OnLeftUp(event)
360     if self.remembertool:
361     self.SelectTool(self.remembertool)
362    
363 bh 6 def OnLeftDown(self, event):
364 jonathan 1385 self.MouseLeftDown(event)
365 bh 6 if self.tool is not None:
366 dpinte 2700 self.drag_dc = wx.ClientDC(self)
367     self.drag_dc.SetLogicalFunction(wx.INVERT)
368     self.drag_dc.SetBrush(wx.TRANSPARENT_BRUSH)
369 bh 6 self.tool.Show(self.drag_dc)
370 bh 1460 self.CaptureMouse()
371 bh 6 self.dragging = 1
372 bh 246
373 bh 6 def OnLeftUp(self, event):
374 bh 1652 """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 bh 6 if self.dragging:
385 bh 1652 if self.HasCapture():
386     self.ReleaseMouse()
387 bh 404 try:
388     self.tool.Hide(self.drag_dc)
389     finally:
390     self.drag_dc = None
391     self.dragging = 0
392 bh 1652 self.MouseLeftUp(event)
393 bh 6
394     def OnMotion(self, event):
395     if self.dragging:
396     self.tool.Hide(self.drag_dc)
397 jonathan 1385
398     self.MouseMove(event)
399    
400     if self.dragging:
401 bh 6 self.tool.Show(self.drag_dc)
402    
403 bh 122 def OnLeaveWindow(self, event):
404     self.set_current_position(None)
405    
406 bh 125 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 bh 6 def shape_selected(self, layer, shape):
416 bh 535 """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 bh 1866 # actually changed, so we can do a full redraw of the
420     # selection_bitmap unconditionally.
421 jonathan 1385 ViewPort.shape_selected(self, layer, shape)
422 bh 1866 self.redraw_selection()
423 bh 6
424 jonathan 1385 def GetTextExtent(self, text):
425 dpinte 2700 dc = wx.ClientDC(self)
426     font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
427 jonathan 1385 dc.SetFont(font)
428 dpinte 2718 return dc.GetTextExtent(text.decode('iso-8859-1'))
429 bh 159
430 jonathan 1385 def LabelShapeAt(self, x, y, text=None):
431 bh 295 """Add or remove a label at window position x, y.
432 bh 1454
433 bh 295 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 jonathan 1385 unless the user cancels the dialog, add a label.
436 bh 295 """
437 bh 6 layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
438     if layer is None and shape_index is not None:
439 jonathan 1385 ViewPort.LabelShapeAt(self, x, y)
440 bh 6 elif layer is not None:
441 bh 1219 text = labeldialog.run_label_dialog(self,
442     layer.ShapeStore().Table(),
443     shape_index)
444 jonathan 1385 ViewPort.LabelShapeAt(self, x, y, text)
445 dpinte 2709
446    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26