/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/view.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/view.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1385 by jonathan, Thu Jul 10 14:52:39 2003 UTC revision 2454 by bh, Mon Dec 13 18:26:11 2004 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  # Frank Koormann <[email protected]>  # Frank Koormann <[email protected]>
# Line 10  Line 10 
10  Classes for display of a map and interaction with it  Classes for display of a map and interaction with it
11  """  """
12    
13  __version__ = "$Revision$"  from __future__ import generators
14    
15  from Thuban import _  __version__ = "$Revision$"
16    # $Source$
17    # $Id$
18    
 import sys  
19  import os.path  import os.path
20    import time
21  from math import hypot  import traceback
22    
23  from wxPython.wx import wxWindow, \  from wxPython.wx import wxWindow, \
24       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
25       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
26       wxBITMAP_TYPE_XPM, wxCursor, wxPlatform, \       wxPlatform, wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \
      wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \  
27       wxOVERWRITE_PROMPT, wxID_OK       wxOVERWRITE_PROMPT, wxID_OK
28    
29  # Export related stuff  # Export related stuff
# Line 32  if wxPlatform == '__WXMSW__': Line 32  if wxPlatform == '__WXMSW__':
32    
33  from wxPython import wx  from wxPython import wx
34    
35  from wxproj import point_in_polygon_shape, shape_centroid  from Thuban import _
36    
37  from Thuban.Model.messages import \  from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
38       MAP_PROJECTION_CHANGED, MAP_LAYERS_CHANGED, \       LAYER_VISIBILITY_CHANGED
      LAYER_PROJECTION_CHANGED, LAYER_CHANGED, LAYER_VISIBILITY_CHANGED  
39    
40  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
41    
42  import labeldialog  import labeldialog
43    
44  from viewport import ViewPort, PanTool  from viewport import ViewPort, PanTool, output_transform
45    
46  class CanvasPanTool(PanTool):  class CanvasPanTool(PanTool):
47    
# Line 56  class CanvasPanTool(PanTool): Line 55  class CanvasPanTool(PanTool):
55              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
56    
57              bitmapdc = wx.wxMemoryDC()              bitmapdc = wx.wxMemoryDC()
58              bitmapdc.SelectObject(self.view.bitmap)              bitmapdc.SelectObject(self.view.PreviewBitmap())
59    
60              dc = self.view.drag_dc              dc = self.view.drag_dc
61              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
# Line 87  class MapPrintout(wx.wxPrintout): Line 86  class MapPrintout(wx.wxPrintout):
86    
87      def draw_on_dc(self, dc):      def draw_on_dc(self, dc):
88          width, height = self.GetPageSizePixels()          width, height = self.GetPageSizePixels()
89          scale, offset, mapregion = OutputTransform(self.canvas.scale,          scale, offset, mapregion = output_transform(self.canvas.scale,
90                                                     self.canvas.offset,                                                      self.canvas.offset,
91                                                     self.canvas.GetSizeTuple(),                                                      self.canvas.GetSizeTuple(),
92                                                     self.GetPageSizePixels())                                                      self.GetPageSizePixels())
93          resx, resy = self.GetPPIPrinter()          resx, resy = self.GetPPIPrinter()
         renderer = PrinterRenderer(dc, scale, offset, resolution = resy)  
         x, y, width, height = self.region  
94          canvas_scale = self.canvas.scale          canvas_scale = self.canvas.scale
95          renderer.RenderMap(self.map,          x, y, width, height = self.region
96                             (0,0,          renderer = PrinterRenderer(dc, self.map, scale, offset,
97                                  (width/canvas_scale)*scale,                                     region = (mapregion[0], mapregion[1],
98                                  (height/canvas_scale)*scale),                                               (width/canvas_scale)*scale,
99                                  mapregion,                                               (height/canvas_scale)*scale),
100                             self.selected_layer, self.selected_shapes)                                     resolution = resy,
101                                       destination_region = mapregion)
102            renderer.RenderMap(self.selected_layer, self.selected_shapes)
103          return True          return True
104    
105    
106  class MapCanvas(wxWindow, ViewPort):  class MapCanvas(wxWindow, ViewPort):
107    
108      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
# Line 115  class MapCanvas(wxWindow, ViewPort): Line 115  class MapCanvas(wxWindow, ViewPort):
115    
116          # the bitmap serving as backing store          # the bitmap serving as backing store
117          self.bitmap = None          self.bitmap = None
118            # the monochrome bitmap with the selection if any
119            self.selection_bitmap = None
120    
121          self.backgroundColor = wx.wxWHITE_BRUSH          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          # subscribe the WX events we're interested in
128          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
129          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
# Line 131  class MapCanvas(wxWindow, ViewPort): Line 137  class MapCanvas(wxWindow, ViewPort):
137          wxWindow.__del__(self)          wxWindow.__del__(self)
138          ViewPort.__del__(self)          ViewPort.__del__(self)
139    
140        def PreviewBitmap(self):
141            return self.bitmap
142    
143      def PanTool(self):      def PanTool(self):
144          """Start the canvas pan tool"""          """Start the canvas pan tool"""
145          self.SelectTool(CanvasPanTool(self))          self.SelectTool(CanvasPanTool(self))
# Line 155  class MapCanvas(wxWindow, ViewPort): Line 164  class MapCanvas(wxWindow, ViewPort):
164    
165      def OnPaint(self, event):      def OnPaint(self, event):
166          dc = wxPaintDC(self)          dc = wxPaintDC(self)
   
167          if self.Map() is not None and self.Map().HasLayers():          if self.Map() is not None and self.Map().HasLayers():
168              if self.bitmap in (None, -1):              if self.bitmap is not None:
                 # set the flag that we should redraw the  
                 # bitmap in idle time  
                 self.bitmap = -1  
             else:  
                 # blit the bitmap to the screen  
169                  dc.BeginDrawing()                  dc.BeginDrawing()
170                  dc.DrawBitmap(self.bitmap, 0, 0)                  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()                  dc.EndDrawing()
174          else:          else:
175              # If we've got no map or if the map is empty, simply clear              # If we've got no map or if the map is empty, simply clear
# Line 180  class MapCanvas(wxWindow, ViewPort): Line 185  class MapCanvas(wxWindow, ViewPort):
185              dc.EndDrawing()              dc.EndDrawing()
186    
187      def OnIdle(self, event):      def OnIdle(self, event):
188          # render the screen if necessary          """Idle handler. Redraw the bitmap if necessary"""
189            if (self.Map() is not None
190                and (self.bitmap is None
191                     or self.render_iter is not None
192                     or (self.HasSelectedShapes()
193                         and self.selection_bitmap is None))):
194                event.RequestMore(self._do_redraw())
195    
196        def _do_redraw(self):
197            """Redraw a bit and return whether this method has to be called again.
198    
199            Called by OnIdle to handle the actual redraw. Redraw is
200            incremental for both the bitmap with the normal layers and the
201            bitmap with the selection.
202            """
203            finished = False
204            if self.render_iter is not None:
205                try:
206                    if self.render_iter.next():
207                        # Redraw if the last preview redraw was some time
208                        # ago and the user is not currently dragging the
209                        # mouse because redrawing would interfere with what
210                        # the current tool is drawing on the window.
211                        if not self.dragging \
212                               and time.time() - self.render_last_preview > 0.5:
213                            client_dc = wxClientDC(self)
214                            client_dc.BeginDrawing()
215                            client_dc.DrawBitmap(self.bitmap, 0, 0)
216                            client_dc.EndDrawing()
217                            self.render_last_preview = time.time()
218                    else:
219                        self.render_iter = None
220                        # Redraw if not dragging because redrawing would
221                        # interfere with what the current tool is drawing on
222                        # the window.
223                        if not self.dragging:
224                            self.redraw()
225                        finished = True
226                except StopIteration:
227                    finished = True
228                    self.render_iter = None
229                except:
230                    finished = True
231                    self.render_iter = None
232                    traceback.print_exc()
233            else:
234                self.render_iter = self._render_iterator()
235                self.render_last_preview = time.time()
236            return not finished
237    
238          if self.bitmap != -1:      def _render_iterator(self):
239              return          width, height = self.GetSizeTuple()
240            dc = wx.wxMemoryDC()
241    
242          wxBeginBusyCursor()          render_start = time.time()
         try:  
             width, height = self.GetSizeTuple()  
243    
244              bitmap = wx.wxEmptyBitmap(width, height)          if self.bitmap is None:
245              dc = wx.wxMemoryDC()              self.bitmap = wx.wxEmptyBitmap(width, height)
246              dc.SelectObject(bitmap)              dc.SelectObject(self.bitmap)
247              dc.BeginDrawing()              dc.BeginDrawing()
248    
249              dc.SetBackground(self.backgroundColor)              dc.SetBackground(self.backgroundColor)
250              dc.Clear()              dc.Clear()
251    
             selected_layer = self.selection.SelectedLayer()  
             selected_shapes = self.selection.SelectedShapes()  
   
252              # draw the map into the bitmap              # draw the map into the bitmap
253              renderer = ScreenRenderer(dc, self.scale, self.offset)              renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
254                                          (0, 0, width, height))
255                for cont in renderer.RenderMapIncrementally():
256                    yield True
257    
258              # Pass the entire bitmap as update region to the renderer.              dc.EndDrawing()
259              # We're redrawing the whole bitmap, after all.              dc.SelectObject(wx.wxNullBitmap)
260              renderer.RenderMap(self.Map(), (0, 0, width, height),  
261                                 selected_layer, selected_shapes)          if self.HasSelectedShapes() and self.selection_bitmap is None:
262                bitmap = wx.wxEmptyBitmap(width, height)
263                dc.SelectObject(bitmap)
264                dc.BeginDrawing()
265                dc.SetBackground(wx.wxWHITE_BRUSH)
266                dc.Clear()
267    
268                renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
269                                          (0, 0, width, height))
270                layer = self.SelectedLayer()
271                shapes = self.selection.SelectedShapes()
272                for cont in renderer.draw_selection_incrementally(layer, shapes):
273                    yield True
274    
275              dc.EndDrawing()              dc.EndDrawing()
276              dc.SelectObject(wx.wxNullBitmap)              dc.SelectObject(wx.wxNullBitmap)
277    
278              self.bitmap = bitmap              bitmap.SetMask(wx.wxMaskColour(bitmap, wx.wxWHITE))
279          finally:              self.selection_bitmap = bitmap
             wxEndBusyCursor()  
             pass  
280    
281          # This causes a paint event that then draws the bitmap          yield False
         self.redraw()  
282    
283      def Export(self):      def Export(self):
284    
# Line 232  class MapCanvas(wxWindow, ViewPort): Line 293  class MapCanvas(wxWindow, ViewPort):
293              self.export_path = os.path.dirname(dlg.GetPath())              self.export_path = os.path.dirname(dlg.GetPath())
294              dc = wxMetaFileDC(dlg.GetPath())              dc = wxMetaFileDC(dlg.GetPath())
295            
296              scale, offset, mapregion = OutputTransform(self.scale,              scale, offset, mapregion = output_transform(self.scale,
297                                                         self.offset,                                                          self.offset,
298                                                         self.GetSizeTuple(),                                                          self.GetSizeTuple(),
299                                                         dc.GetSizeTuple())                                                          dc.GetSizeTuple())
300    
301              selected_layer = self.selection.SelectedLayer()              selected_layer = self.selection.SelectedLayer()
302              selected_shapes = self.selection.SelectedShapes()              selected_shapes = self.selection.SelectedShapes()
303    
             renderer = ExportRenderer(dc, scale, offset)  
   
             # Pass the entire bitmap as update region to the renderer.  
             # We're redrawing the whole bitmap, after all.  
304              width, height = self.GetSizeTuple()              width, height = self.GetSizeTuple()
305              renderer.RenderMap(self.Map(),              renderer = ExportRenderer(dc, self.Map(), scale, offset,
306                                  (0,0,                                        region = (0, 0,
307                                      (width/self.scale)*scale,                                                  (width/self.scale)*scale,
308                                      (height/self.scale)*scale),                                                  (height/self.scale)*scale),
309                                  mapregion,                                        destination_region = mapregion)
310                                  selected_layer, selected_shapes)              renderer.RenderMap(selected_layer, selected_shapes)
311    
312              dc.EndDrawing()              dc.EndDrawing()
313              dc.Close()              dc.Close()
314          dlg.Destroy()          dlg.Destroy()
# Line 271  class MapCanvas(wxWindow, ViewPort): Line 329  class MapCanvas(wxWindow, ViewPort):
329    
330      def full_redraw(self, *args):      def full_redraw(self, *args):
331          self.bitmap = None          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()          self.redraw()
340    
341      def map_projection_changed(self, map, old_proj):      def map_projection_changed(self, map, old_proj):
# Line 295  class MapCanvas(wxWindow, ViewPort): Line 360  class MapCanvas(wxWindow, ViewPort):
360              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
361              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
362              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
363                self.CaptureMouse()
364              self.dragging = 1              self.dragging = 1
365    
366      def OnLeftUp(self, event):      def OnLeftUp(self, event):
367          self.MouseLeftUp(event)          """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:          if self.dragging:
378              self.ReleaseMouse()              if self.HasCapture():
379                    self.ReleaseMouse()
380              try:              try:
381                  self.tool.Hide(self.drag_dc)                  self.tool.Hide(self.drag_dc)
382              finally:              finally:
383                  self.drag_dc = None                  self.drag_dc = None
384                  self.dragging = 0                  self.dragging = 0
385            self.MouseLeftUp(event)
386    
387      def OnMotion(self, event):      def OnMotion(self, event):
388          if self.dragging:          if self.dragging:
# Line 332  class MapCanvas(wxWindow, ViewPort): Line 409  class MapCanvas(wxWindow, ViewPort):
409          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
410          # The selection object takes care that it only issues          # The selection object takes care that it only issues
411          # SHAPES_SELECTED messages when the set of selected shapes has          # SHAPES_SELECTED messages when the set of selected shapes has
412          # actually changed, so we can do a full redraw unconditionally.          # actually changed, so we can do a full redraw of the
413          # FIXME: We should perhaps try to limit the redraw to the are          # selection_bitmap unconditionally.
         # actually covered by the shapes before and after the selection  
         # change.  
414          ViewPort.shape_selected(self, layer, shape)          ViewPort.shape_selected(self, layer, shape)
415          self.full_redraw()          self.redraw_selection()
416    
417      def GetTextExtent(self, text):      def GetTextExtent(self, text):
418          dc = wxClientDC(self)          dc = wxClientDC(self)
# Line 347  class MapCanvas(wxWindow, ViewPort): Line 422  class MapCanvas(wxWindow, ViewPort):
422    
423      def LabelShapeAt(self, x, y, text=None):      def LabelShapeAt(self, x, y, text=None):
424          """Add or remove a label at window position x, y.          """Add or remove a label at window position x, y.
425                                                                                    
426          If there's a label at the given position, remove it. Otherwise          If there's a label at the given position, remove it. Otherwise
427          determine the shape at the position, run the label dialog and          determine the shape at the position, run the label dialog and
428          unless the user cancels the dialog, add a label.          unless the user cancels the dialog, add a label.
# Line 361  class MapCanvas(wxWindow, ViewPort): Line 436  class MapCanvas(wxWindow, ViewPort):
436                                                  layer.ShapeStore().Table(),                                                  layer.ShapeStore().Table(),
437                                                  shape_index)                                                  shape_index)
438              ViewPort.LabelShapeAt(self, x, y, text)              ViewPort.LabelShapeAt(self, x, y, text)
           
 def OutputTransform(canvas_scale, canvas_offset, canvas_size, device_extend):  
     """Calculate dimensions to transform canvas content to output device."""  
     width, height = device_extend  
   
     # Only 80 % of the with are available for the map  
     width = width * 0.8  
   
     # Define the distance of the map from DC border  
     distance = 20  
   
     if height < width:  
         # landscape  
         map_height = height - 2*distance  
         map_width = map_height  
     else:  
         # portrait, recalibrate width (usually the legend width is too  
         # small  
         width = width * 0.9  
         map_height = width - 2*distance  
         map_width = map_height  
       
     mapregion = (distance, distance,  
                  distance+map_width, distance+map_height)  
   
     canvas_width, canvas_height = canvas_size  
       
     scalex = map_width / (canvas_width/canvas_scale)  
     scaley = map_height / (canvas_height/canvas_scale)  
     scale = min(scalex, scaley)  
     canvas_offx, canvas_offy = canvas_offset  
     offx = scale*canvas_offx/canvas_scale  
     offy = scale*canvas_offy/canvas_scale  
   
     return scale, (offx, offy), mapregion  

Legend:
Removed from v.1385  
changed lines
  Added in v.2454

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26