/[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 1652 - (show annotations)
Mon Aug 25 15:59:58 2003 UTC (21 years, 6 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 13218 byte(s)
(MapCanvas.OnLeftUp): Release the the mouse
before calling MouseLeftUp. MouseLeftUp may pop up modal dialogs
which leads to an effectively frozen X session because the user
can only interact with the dialog but the mouse is still grabbed
by the canvas.
Also, call the tool's Hide method before MouseLeftUp because
MouseLeftUp may change the tool's coordinates.

1 # Copyright (c) 2001, 2002, 2003 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 __version__ = "$Revision$"
14
15 from Thuban import _
16
17 import os.path
18
19 from wxPython.wx import wxWindow, \
20 wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
21 EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
22 wxPlatform, wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \
23 wxOVERWRITE_PROMPT, wxID_OK
24
25 # Export related stuff
26 if wxPlatform == '__WXMSW__':
27 from wxPython.wx import wxMetaFileDC
28
29 from wxPython import wx
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.wxMemoryDC()
52 bitmapdc.SelectObject(self.view.bitmap)
53
54 dc = self.view.drag_dc
55 dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
56
57 class MapPrintout(wx.wxPrintout):
58
59 """
60 wxPrintout class for printing Thuban maps
61 """
62
63 def __init__(self, canvas, map, region, selected_layer, selected_shapes):
64 wx.wxPrintout.__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 renderer = PrinterRenderer(dc, scale, offset, resolution = resy)
89 x, y, width, height = self.region
90 canvas_scale = self.canvas.scale
91 renderer.RenderMap(self.map,
92 (0,0,
93 (width/canvas_scale)*scale,
94 (height/canvas_scale)*scale),
95 mapregion,
96 self.selected_layer, self.selected_shapes)
97 return True
98
99 class MapCanvas(wxWindow, ViewPort):
100
101 """A widget that displays a map and offers some interaction"""
102
103 def __init__(self, parent, winid):
104 wxWindow.__init__(self, parent, winid)
105 ViewPort.__init__(self)
106
107 self.SetBackgroundColour(wxColour(255, 255, 255))
108
109 # the bitmap serving as backing store
110 self.bitmap = None
111
112 self.backgroundColor = wx.wxWHITE_BRUSH
113
114 # Set to true if there ever is an error during redraw. There
115 # should never be errors, but unfortunately bugs happen.
116 self.error_on_redraw = 0
117
118 # subscribe the WX events we're interested in
119 EVT_PAINT(self, self.OnPaint)
120 EVT_LEFT_DOWN(self, self.OnLeftDown)
121 EVT_LEFT_UP(self, self.OnLeftUp)
122 EVT_MOTION(self, self.OnMotion)
123 EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
124 wx.EVT_SIZE(self, self.OnSize)
125 wx.EVT_IDLE(self, self.OnIdle)
126
127 def __del__(self):
128 wxWindow.__del__(self)
129 ViewPort.__del__(self)
130
131 def PanTool(self):
132 """Start the canvas pan tool"""
133 self.SelectTool(CanvasPanTool(self))
134
135 def SetMap(self, map):
136 redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
137 LAYER_VISIBILITY_CHANGED)
138 if self.Map() is not None:
139 for channel in redraw_channels:
140 self.Map().Unsubscribe(channel, self.full_redraw)
141
142 ViewPort.SetMap(self, map)
143
144 if self.Map() is not None:
145 for channel in redraw_channels:
146 self.Map().Subscribe(channel, self.full_redraw)
147
148 # force a redraw. If map is not empty, it's already been called
149 # by FitMapToWindow but if map is empty it hasn't been called
150 # yet so we have to explicitly call it.
151 self.full_redraw()
152
153 def OnPaint(self, event):
154 dc = wxPaintDC(self)
155
156 if self.Map() is not None and self.Map().HasLayers():
157 if self.bitmap in (None, -1):
158 # set the flag that we should redraw the
159 # bitmap in idle time
160 self.bitmap = -1
161 else:
162 # blit the bitmap to the screen
163 dc.BeginDrawing()
164 dc.DrawBitmap(self.bitmap, 0, 0)
165 dc.EndDrawing()
166 else:
167 # If we've got no map or if the map is empty, simply clear
168 # the screen.
169
170 # XXX it's probably possible to get rid of this. The
171 # background color of the window is already white and the
172 # only thing we may have to do is to call self.Refresh()
173 # with a true argument in the right places.
174 dc.BeginDrawing()
175 dc.SetBackground(self.backgroundColor)
176 dc.Clear()
177 dc.EndDrawing()
178
179 def OnIdle(self, event):
180 """Idle handler. Redraw the bitmap if necessary"""
181
182 if self.bitmap != -1:
183 return
184 if self.error_on_redraw:
185 return
186
187 wxBeginBusyCursor()
188 try:
189 try:
190 self._do_redraw()
191 except:
192 self.error_on_redraw = True
193 raise
194 finally:
195 wxEndBusyCursor()
196
197 def _do_redraw(self):
198 """Called by OnIdle to do the actual redraw.
199 """
200 width, height = self.GetSizeTuple()
201
202 bitmap = wx.wxEmptyBitmap(width, height)
203 dc = wx.wxMemoryDC()
204 dc.SelectObject(bitmap)
205 dc.BeginDrawing()
206
207 dc.SetBackground(self.backgroundColor)
208 dc.Clear()
209
210 selected_layer = self.selection.SelectedLayer()
211 selected_shapes = self.selection.SelectedShapes()
212
213 # draw the map into the bitmap
214 renderer = ScreenRenderer(dc, self.scale, self.offset)
215
216 # Pass the entire bitmap as update region to the renderer.
217 # We're redrawing the whole bitmap, after all.
218 renderer.RenderMap(self.Map(), (0, 0, width, height),
219 selected_layer, selected_shapes)
220
221 dc.EndDrawing()
222 dc.SelectObject(wx.wxNullBitmap)
223
224 self.bitmap = bitmap
225 # This causes a paint event that then draws the bitmap
226 self.redraw()
227
228 def Export(self):
229
230 if hasattr(self, "export_path"):
231 export_path = self.export_path
232 else:
233 export_path="."
234 dlg = wxFileDialog(self, _("Export Map"), export_path, "",
235 "Enhanced Metafile (*.wmf)|*.wmf",
236 wxSAVE|wxOVERWRITE_PROMPT)
237 if dlg.ShowModal() == wxID_OK:
238 self.export_path = os.path.dirname(dlg.GetPath())
239 dc = wxMetaFileDC(dlg.GetPath())
240
241 scale, offset, mapregion = output_transform(self.scale,
242 self.offset,
243 self.GetSizeTuple(),
244 dc.GetSizeTuple())
245
246 selected_layer = self.selection.SelectedLayer()
247 selected_shapes = self.selection.SelectedShapes()
248
249 renderer = ExportRenderer(dc, scale, offset)
250
251 # Pass the entire bitmap as update region to the renderer.
252 # We're redrawing the whole bitmap, after all.
253 width, height = self.GetSizeTuple()
254 renderer.RenderMap(self.Map(),
255 (0,0,
256 (width/self.scale)*scale,
257 (height/self.scale)*scale),
258 mapregion,
259 selected_layer, selected_shapes)
260 dc.EndDrawing()
261 dc.Close()
262 dlg.Destroy()
263
264 def Print(self):
265 printer = wx.wxPrinter()
266 width, height = self.GetSizeTuple()
267 selected_layer = self.selection.SelectedLayer()
268 selected_shapes = self.selection.SelectedShapes()
269
270 printout = MapPrintout(self, self.Map(), (0, 0, width, height),
271 selected_layer, selected_shapes)
272 printer.Print(self, printout, True)
273 printout.Destroy()
274
275 def redraw(self, *args):
276 self.Refresh(False)
277
278 def full_redraw(self, *args):
279 self.bitmap = None
280 self.redraw()
281
282 def map_projection_changed(self, map, old_proj):
283 ViewPort.map_projection_changed(self, map, old_proj)
284 self.full_redraw()
285
286 def layer_projection_changed(self, *args):
287 ViewPort.layer_projection_changed(self, args)
288 self.full_redraw()
289
290 def set_view_transform(self, scale, offset):
291 ViewPort.set_view_transform(self, scale, offset)
292 self.full_redraw()
293
294 def GetPortSizeTuple(self):
295 return self.GetSizeTuple()
296
297 def OnLeftDown(self, event):
298 self.MouseLeftDown(event)
299 if self.tool is not None:
300 self.drag_dc = wxClientDC(self)
301 self.drag_dc.SetLogicalFunction(wxINVERT)
302 self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
303 self.tool.Show(self.drag_dc)
304 self.CaptureMouse()
305 self.dragging = 1
306
307 def OnLeftUp(self, event):
308 """Handle EVT_LEFT_UP
309
310 Release the mouse if it was captured, if a tool is active call
311 its Hide method and call self.MouseLeftUp.
312 """
313 # It's important that ReleaseMouse is called before MouseLeftUp.
314 # MouseLeftUp may pop up modal dialogs which leads to an
315 # effectively frozen X session because the user can only
316 # interact with the dialog but the mouse is still grabbed by the
317 # canvas.
318 if self.dragging:
319 if self.HasCapture():
320 self.ReleaseMouse()
321 try:
322 self.tool.Hide(self.drag_dc)
323 finally:
324 self.drag_dc = None
325 self.dragging = 0
326 self.MouseLeftUp(event)
327
328 def OnMotion(self, event):
329 if self.dragging:
330 self.tool.Hide(self.drag_dc)
331
332 self.MouseMove(event)
333
334 if self.dragging:
335 self.tool.Show(self.drag_dc)
336
337 def OnLeaveWindow(self, event):
338 self.set_current_position(None)
339
340 def OnSize(self, event):
341 # the window's size has changed. We have to get a new bitmap. If
342 # we want to be clever we could try to get by without throwing
343 # everything away. E.g. when the window gets smaller, we could
344 # either keep the bitmap or create the new one from the old one.
345 # Even when the window becomes larger some parts of the bitmap
346 # could be reused.
347 self.full_redraw()
348
349 def shape_selected(self, layer, shape):
350 """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
351 # The selection object takes care that it only issues
352 # SHAPES_SELECTED messages when the set of selected shapes has
353 # actually changed, so we can do a full redraw unconditionally.
354 # FIXME: We should perhaps try to limit the redraw to the are
355 # actually covered by the shapes before and after the selection
356 # change.
357 ViewPort.shape_selected(self, layer, shape)
358 self.full_redraw()
359
360 def GetTextExtent(self, text):
361 dc = wxClientDC(self)
362 font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
363 dc.SetFont(font)
364 return dc.GetTextExtent(text)
365
366 def LabelShapeAt(self, x, y, text=None):
367 """Add or remove a label at window position x, y.
368
369 If there's a label at the given position, remove it. Otherwise
370 determine the shape at the position, run the label dialog and
371 unless the user cancels the dialog, add a label.
372 """
373 label_layer = self.map.LabelLayer()
374 layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
375 if layer is None and shape_index is not None:
376 ViewPort.LabelShapeAt(self, x, y)
377 elif layer is not None:
378 text = labeldialog.run_label_dialog(self,
379 layer.ShapeStore().Table(),
380 shape_index)
381 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