/[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 2511 by russell, Mon Dec 27 16:31:32 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, \
27       wxBeginBusyCursor, wxEndBusyCursor, wxFileDialog, wxSAVE, \       EVT_MIDDLE_DOWN, EVT_MIDDLE_UP, \
28       wxOVERWRITE_PROMPT, wxID_OK       wxOVERWRITE_PROMPT, wxID_OK
29    
30  # Export related stuff  # Export related stuff
# Line 32  if wxPlatform == '__WXMSW__': Line 33  if wxPlatform == '__WXMSW__':
33    
34  from wxPython import wx  from wxPython import wx
35    
36  from wxproj import point_in_polygon_shape, shape_centroid  from Thuban import _
37    
38  from Thuban.Model.messages import \  from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
39       MAP_PROJECTION_CHANGED, MAP_LAYERS_CHANGED, \       LAYER_VISIBILITY_CHANGED
      LAYER_PROJECTION_CHANGED, LAYER_CHANGED, LAYER_VISIBILITY_CHANGED  
40    
41  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
42    
43  import labeldialog  import labeldialog
44    
45  from viewport import ViewPort, PanTool  from viewport import ViewPort, PanTool, output_transform
46    
47  class CanvasPanTool(PanTool):  class CanvasPanTool(PanTool):
48    
# Line 56  class CanvasPanTool(PanTool): Line 56  class CanvasPanTool(PanTool):
56              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
57    
58              bitmapdc = wx.wxMemoryDC()              bitmapdc = wx.wxMemoryDC()
59              bitmapdc.SelectObject(self.view.bitmap)              bitmapdc.SelectObject(self.view.PreviewBitmap())
60    
61              dc = self.view.drag_dc              dc = self.view.drag_dc
62              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 87  class MapPrintout(wx.wxPrintout):
87    
88      def draw_on_dc(self, dc):      def draw_on_dc(self, dc):
89          width, height = self.GetPageSizePixels()          width, height = self.GetPageSizePixels()
90          scale, offset, mapregion = OutputTransform(self.canvas.scale,          scale, offset, mapregion = output_transform(self.canvas.scale,
91                                                     self.canvas.offset,                                                      self.canvas.offset,
92                                                     self.canvas.GetSizeTuple(),                                                      self.canvas.GetSizeTuple(),
93                                                     self.GetPageSizePixels())                                                      self.GetPageSizePixels())
94          resx, resy = self.GetPPIPrinter()          resx, resy = self.GetPPIPrinter()
         renderer = PrinterRenderer(dc, scale, offset, resolution = resy)  
         x, y, width, height = self.region  
95          canvas_scale = self.canvas.scale          canvas_scale = self.canvas.scale
96          renderer.RenderMap(self.map,          x, y, width, height = self.region
97                             (0,0,          renderer = PrinterRenderer(dc, self.map, scale, offset,
98                                  (width/canvas_scale)*scale,                                     region = (mapregion[0], mapregion[1],
99                                  (height/canvas_scale)*scale),                                               (width/canvas_scale)*scale,
100                                  mapregion,                                               (height/canvas_scale)*scale),
101                             self.selected_layer, self.selected_shapes)                                     resolution = resy,
102                                       destination_region = mapregion)
103            renderer.RenderMap(self.selected_layer, self.selected_shapes)
104          return True          return True
105    
106    
107  class MapCanvas(wxWindow, ViewPort):  class MapCanvas(wxWindow, ViewPort):
108    
109      """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 116  class MapCanvas(wxWindow, ViewPort):
116    
117          # the bitmap serving as backing store          # the bitmap serving as backing store
118          self.bitmap = None          self.bitmap = None
119            # the monochrome bitmap with the selection if any
120            self.selection_bitmap = None
121    
122          self.backgroundColor = wx.wxWHITE_BRUSH          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          # subscribe the WX events we're interested in
129          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
130          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
131          EVT_LEFT_UP(self, self.OnLeftUp)          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)          EVT_MOTION(self, self.OnMotion)
135          EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)          EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
136          wx.EVT_SIZE(self, self.OnSize)          wx.EVT_SIZE(self, self.OnSize)
# Line 131  class MapCanvas(wxWindow, ViewPort): Line 140  class MapCanvas(wxWindow, ViewPort):
140          wxWindow.__del__(self)          wxWindow.__del__(self)
141          ViewPort.__del__(self)          ViewPort.__del__(self)
142    
143        def PreviewBitmap(self):
144            return self.bitmap
145    
146      def PanTool(self):      def PanTool(self):
147          """Start the canvas pan tool"""          """Start the canvas pan tool"""
148          self.SelectTool(CanvasPanTool(self))          self.SelectTool(CanvasPanTool(self))
# Line 155  class MapCanvas(wxWindow, ViewPort): Line 167  class MapCanvas(wxWindow, ViewPort):
167    
168      def OnPaint(self, event):      def OnPaint(self, event):
169          dc = wxPaintDC(self)          dc = wxPaintDC(self)
   
170          if self.Map() is not None and self.Map().HasLayers():          if self.Map() is not None and self.Map().HasLayers():
171              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  
172                  dc.BeginDrawing()                  dc.BeginDrawing()
173                  dc.DrawBitmap(self.bitmap, 0, 0)                  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()                  dc.EndDrawing()
177          else:          else:
178              # 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 188  class MapCanvas(wxWindow, ViewPort):
188              dc.EndDrawing()              dc.EndDrawing()
189    
190      def OnIdle(self, event):      def OnIdle(self, event):
191          # render the screen if necessary          """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          if self.bitmap != -1:      def _render_iterator(self):
242              return          width, height = self.GetSizeTuple()
243            dc = wx.wxMemoryDC()
244    
245          wxBeginBusyCursor()          render_start = time.time()
         try:  
             width, height = self.GetSizeTuple()  
246    
247              bitmap = wx.wxEmptyBitmap(width, height)          if self.bitmap is None:
248              dc = wx.wxMemoryDC()              self.bitmap = wx.wxEmptyBitmap(width, height)
249              dc.SelectObject(bitmap)              dc.SelectObject(self.bitmap)
250              dc.BeginDrawing()              dc.BeginDrawing()
251    
252              dc.SetBackground(self.backgroundColor)              dc.SetBackground(self.backgroundColor)
253              dc.Clear()              dc.Clear()
254    
             selected_layer = self.selection.SelectedLayer()  
             selected_shapes = self.selection.SelectedShapes()  
   
255              # draw the map into the bitmap              # draw the map into the bitmap
256              renderer = ScreenRenderer(dc, self.scale, self.offset)              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              # Pass the entire bitmap as update region to the renderer.              dc.EndDrawing()
262              # We're redrawing the whole bitmap, after all.              dc.SelectObject(wx.wxNullBitmap)
263              renderer.RenderMap(self.Map(), (0, 0, width, height),  
264                                 selected_layer, selected_shapes)          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()              dc.EndDrawing()
279              dc.SelectObject(wx.wxNullBitmap)              dc.SelectObject(wx.wxNullBitmap)
280    
281              self.bitmap = bitmap              bitmap.SetMask(wx.wxMaskColour(bitmap, wx.wxWHITE))
282          finally:              self.selection_bitmap = bitmap
             wxEndBusyCursor()  
             pass  
283    
284          # This causes a paint event that then draws the bitmap          yield False
         self.redraw()  
285    
286      def Export(self):      def Export(self):
287    
# Line 232  class MapCanvas(wxWindow, ViewPort): Line 296  class MapCanvas(wxWindow, ViewPort):
296              self.export_path = os.path.dirname(dlg.GetPath())              self.export_path = os.path.dirname(dlg.GetPath())
297              dc = wxMetaFileDC(dlg.GetPath())              dc = wxMetaFileDC(dlg.GetPath())
298            
299              scale, offset, mapregion = OutputTransform(self.scale,              scale, offset, mapregion = output_transform(self.scale,
300                                                         self.offset,                                                          self.offset,
301                                                         self.GetSizeTuple(),                                                          self.GetSizeTuple(),
302                                                         dc.GetSizeTuple())                                                          dc.GetSizeTuple())
303    
304              selected_layer = self.selection.SelectedLayer()              selected_layer = self.selection.SelectedLayer()
305              selected_shapes = self.selection.SelectedShapes()              selected_shapes = self.selection.SelectedShapes()
306    
             renderer = ExportRenderer(dc, scale, offset)  
   
             # Pass the entire bitmap as update region to the renderer.  
             # We're redrawing the whole bitmap, after all.  
307              width, height = self.GetSizeTuple()              width, height = self.GetSizeTuple()
308              renderer.RenderMap(self.Map(),              renderer = ExportRenderer(dc, self.Map(), scale, offset,
309                                  (0,0,                                        region = (0, 0,
310                                      (width/self.scale)*scale,                                                  (width/self.scale)*scale,
311                                      (height/self.scale)*scale),                                                  (height/self.scale)*scale),
312                                  mapregion,                                        destination_region = mapregion)
313                                  selected_layer, selected_shapes)              renderer.RenderMap(selected_layer, selected_shapes)
314    
315              dc.EndDrawing()              dc.EndDrawing()
316              dc.Close()              dc.Close()
317          dlg.Destroy()          dlg.Destroy()
# Line 271  class MapCanvas(wxWindow, ViewPort): Line 332  class MapCanvas(wxWindow, ViewPort):
332    
333      def full_redraw(self, *args):      def full_redraw(self, *args):
334          self.bitmap = None          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()          self.redraw()
343    
344      def map_projection_changed(self, map, old_proj):      def map_projection_changed(self, map, old_proj):
# Line 288  class MapCanvas(wxWindow, ViewPort): Line 356  class MapCanvas(wxWindow, ViewPort):
356      def GetPortSizeTuple(self):      def GetPortSizeTuple(self):
357          return self.GetSizeTuple()          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):      def OnLeftDown(self, event):
371          self.MouseLeftDown(event)          self.MouseLeftDown(event)
372          if self.tool is not None:          if self.tool is not None:
# Line 295  class MapCanvas(wxWindow, ViewPort): Line 374  class MapCanvas(wxWindow, ViewPort):
374              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
375              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
376              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
377                self.CaptureMouse()
378              self.dragging = 1              self.dragging = 1
379    
380      def OnLeftUp(self, event):      def OnLeftUp(self, event):
381          self.MouseLeftUp(event)          """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:          if self.dragging:
392              self.ReleaseMouse()              if self.HasCapture():
393                    self.ReleaseMouse()
394              try:              try:
395                  self.tool.Hide(self.drag_dc)                  self.tool.Hide(self.drag_dc)
396              finally:              finally:
397                  self.drag_dc = None                  self.drag_dc = None
398                  self.dragging = 0                  self.dragging = 0
399            self.MouseLeftUp(event)
400    
401      def OnMotion(self, event):      def OnMotion(self, event):
402          if self.dragging:          if self.dragging:
# Line 332  class MapCanvas(wxWindow, ViewPort): Line 423  class MapCanvas(wxWindow, ViewPort):
423          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
424          # The selection object takes care that it only issues          # The selection object takes care that it only issues
425          # SHAPES_SELECTED messages when the set of selected shapes has          # SHAPES_SELECTED messages when the set of selected shapes has
426          # actually changed, so we can do a full redraw unconditionally.          # actually changed, so we can do a full redraw of the
427          # 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.  
428          ViewPort.shape_selected(self, layer, shape)          ViewPort.shape_selected(self, layer, shape)
429          self.full_redraw()          self.redraw_selection()
430    
431      def GetTextExtent(self, text):      def GetTextExtent(self, text):
432          dc = wxClientDC(self)          dc = wxClientDC(self)
# Line 347  class MapCanvas(wxWindow, ViewPort): Line 436  class MapCanvas(wxWindow, ViewPort):
436    
437      def LabelShapeAt(self, x, y, text=None):      def LabelShapeAt(self, x, y, text=None):
438          """Add or remove a label at window position x, y.          """Add or remove a label at window position x, y.
439                                                                                    
440          If there's a label at the given position, remove it. Otherwise          If there's a label at the given position, remove it. Otherwise
441          determine the shape at the position, run the label dialog and          determine the shape at the position, run the label dialog and
442          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 450  class MapCanvas(wxWindow, ViewPort):
450                                                  layer.ShapeStore().Table(),                                                  layer.ShapeStore().Table(),
451                                                  shape_index)                                                  shape_index)
452              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.2511

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26