/[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 60 by bh, Thu Sep 13 14:47:39 2001 UTC revision 159 by bh, Wed May 8 13:46:15 2002 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # Copyright (c) 2001, 2002 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  #  #
# Line 15  from math import hypot Line 15  from math import hypot
15    
16  from wxPython.wx import wxWindow,\  from wxPython.wx import wxWindow,\
17       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
18       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW
19    
20    
21  from wxPython import wx  from wxPython import wx
# Line 29  from Thuban.Model.layer import SHAPETYPE Line 29  from Thuban.Model.layer import SHAPETYPE
29       SHAPETYPE_POINT       SHAPETYPE_POINT
30  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
31       ALIGN_LEFT, ALIGN_RIGHT       ALIGN_LEFT, ALIGN_RIGHT
32    from Thuban.Lib.connector import Publisher
33    
34  from renderer import ScreenRenderer, PrinterRender  from renderer import ScreenRenderer, PrinterRender
35    
36  import labeldialog  import labeldialog
37    
38  from messages import SELECTED_SHAPE  from messages import SELECTED_SHAPE, VIEW_POSITION
39    
40    
41  #  #
# Line 167  class PanTool(Tool): Line 167  class PanTool(Tool):
167    
168      def MouseMove(self, event):      def MouseMove(self, event):
169          if self.dragging:          if self.dragging:
             x0, y0 = self.current  
170              Tool.MouseMove(self, event)              Tool.MouseMove(self, event)
171                sx, sy = self.start
172              x, y = self.current              x, y = self.current
173              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
174    
175                bitmapdc = wx.wxMemoryDC()
176                bitmapdc.SelectObject(self.view.bitmap)
177    
178              dc = self.view.drag_dc              dc = self.view.drag_dc
179              dc.Blit(0, 0, width, height, dc, x0 - x, y0 - y)              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
180    
181      def MouseUp(self, event):      def MouseUp(self, event):
182          if self.dragging:          if self.dragging:
# Line 240  class MapPrintout(wx.wxPrintout): Line 244  class MapPrintout(wx.wxPrintout):
244          return wx.true          return wx.true
245                    
246    
247  class MapCanvas(wxWindow):  class MapCanvas(wxWindow, Publisher):
248    
249      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
250    
251      def __init__(self, parent, winid, interactor):      def __init__(self, parent, winid, interactor):
252          wxWindow.__init__(self, parent, winid)          wxWindow.__init__(self, parent, winid)
253          self.SetBackgroundColour(wxColour(255, 255, 255))          self.SetBackgroundColour(wxColour(255, 255, 255))
254    
255            # the map displayed in this canvas. Set with SetMap()
256          self.map = None          self.map = None
257    
258            # scale and offset describe the transformation from projected
259            # coordinates to window coordinates.
260          self.scale = 1.0          self.scale = 1.0
261          self.offset = (0, 0)          self.offset = (0, 0)
262    
263            # whether the user is currently dragging the mouse, i.e. moving
264            # the mouse while pressing a mouse button
265          self.dragging = 0          self.dragging = 0
266    
267            # the currently active tool
268          self.tool = None          self.tool = None
269    
270            # The current mouse position of the last OnMotion event or None
271            # if the mouse is outside the window.
272            self.current_position = None
273    
274            # If true, OnIdle will call do_redraw to do the actual
275            # redrawing. Set by OnPaint to avoid some unnecessary redraws.
276            # To force a redraw call full_redraw().
277          self.redraw_on_idle = 0          self.redraw_on_idle = 0
278    
279            # The region to update when idle
280            self.update_region = wx.wxRegion()
281    
282            # the bitmap serving as backing store
283            self.bitmap = None
284    
285            # the interactor
286            self.interactor = interactor
287            self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)
288    
289            # subscribe the WX events we're interested in
290          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
291          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
292          EVT_LEFT_UP(self, self.OnLeftUp)          EVT_LEFT_UP(self, self.OnLeftUp)
293          EVT_MOTION(self, self.OnMotion)          EVT_MOTION(self, self.OnMotion)
294            EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
295            wx.EVT_SIZE(self, self.OnSize)
296          wx.EVT_IDLE(self, self.OnIdle)          wx.EVT_IDLE(self, self.OnIdle)
297          self.interactor = interactor  
298          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)      def __del__(self):
299            wxWindow.__del__(self)
300            Publisher.__del__(self)
301    
302      def OnPaint(self, event):      def OnPaint(self, event):
303          dc = wxPaintDC(self)          dc = wxPaintDC(self)
304          if self.map is not None and self.map.HasLayers():          if self.map is not None and self.map.HasLayers():
305              # We have a non-empty map. Redraw it in idle time              # We have a non-empty map. Redraw it in idle time
306              self.redraw_on_idle = 1              self.redraw_on_idle = 1
307                # update the region that has to be redrawn
308                self.update_region.UnionRegion(self.GetUpdateRegion())
309          else:          else:
310              # 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
311              # the screen.              # the screen.
# Line 278  class MapCanvas(wxWindow): Line 318  class MapCanvas(wxWindow):
318              dc.Clear()                          dc.Clear()            
319              dc.EndDrawing()              dc.EndDrawing()
320    
321                # clear the region
322                self.update_region = wx.wxRegion()
323    
324      def do_redraw(self):      def do_redraw(self):
325          # This should only be called if we have a non-empty map. We draw          # This should only be called if we have a non-empty map.
326          # it into a memory DC and then blit it to the screen.  
327            # get the update region and reset it. We're not actually using
328            # it anymore, though.
329            update_box = self.update_region.GetBox()
330            self.update_region = wx.wxRegion()
331    
332            # Get the window size.
333          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
         bitmap = wx.wxEmptyBitmap(width, height)  
         dc = wx.wxMemoryDC()  
         dc.SelectObject(bitmap)  
         dc.BeginDrawing()  
334    
335          # clear the background          # If self.bitmap's still there, reuse it. Otherwise redraw it
336          dc.SetBrush(wx.wxWHITE_BRUSH)          if self.bitmap is not None:
337          dc.SetPen(wx.wxTRANSPARENT_PEN)              bitmap = self.bitmap
         dc.DrawRectangle(0, 0, width, height)  
   
         if 1: #self.interactor.selected_map is self.map:  
             selected_layer = self.interactor.selected_layer  
             selected_shape = self.interactor.selected_shape  
338          else:          else:
339              selected_layer = None              bitmap = wx.wxEmptyBitmap(width, height)
340              selected_shape = None              dc = wx.wxMemoryDC()
341                dc.SelectObject(bitmap)
342                dc.BeginDrawing()
343    
344          # draw the map into the bitmap              # clear the background
345          renderer = ScreenRenderer(dc, self.scale, self.offset)              dc.SetBrush(wx.wxWHITE_BRUSH)
346          renderer.RenderMap(self.map, selected_layer, selected_shape)              dc.SetPen(wx.wxTRANSPARENT_PEN)
347                dc.DrawRectangle(0, 0, width, height)
348    
349                if 1: #self.interactor.selected_map is self.map:
350                    selected_layer = self.interactor.selected_layer
351                    selected_shape = self.interactor.selected_shape
352                else:
353                    selected_layer = None
354                    selected_shape = None
355    
356          dc.EndDrawing()              # draw the map into the bitmap
357                renderer = ScreenRenderer(dc, self.scale, self.offset)
358    
359                # Pass the entire bitmap as update_region to the renderer.
360                # We're redrawing the whole bitmap, after all.
361                renderer.RenderMap(self.map, (0, 0, width, height),
362                                   selected_layer, selected_shape)
363    
364                dc.EndDrawing()
365                dc.SelectObject(wx.wxNullBitmap)
366                self.bitmap = bitmap
367    
368          # blit the bitmap to the screen          # blit the bitmap to the screen
369            dc = wx.wxMemoryDC()
370            dc.SelectObject(bitmap)
371          clientdc = wxClientDC(self)          clientdc = wxClientDC(self)
372          clientdc.BeginDrawing()          clientdc.BeginDrawing()
373          clientdc.Blit(0, 0, width, height, dc, 0, 0)          clientdc.Blit(0, 0, width, height, dc, 0, 0)
# Line 322  class MapCanvas(wxWindow): Line 384  class MapCanvas(wxWindow):
384                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
385          if self.map is not None:          if self.map is not None:
386              for channel in redraw_channels:              for channel in redraw_channels:
387                  self.map.Unsubscribe(channel, self.redraw)                  self.map.Unsubscribe(channel, self.full_redraw)
388              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
389                                   self.projection_changed)                                   self.projection_changed)
390          self.map = map          self.map = map
391          if self.map is not None:          if self.map is not None:
392              for channel in redraw_channels:              for channel in redraw_channels:
393                  self.map.Subscribe(channel, self.redraw)                  self.map.Subscribe(channel, self.full_redraw)
394              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)
395          self.FitMapToWindow()          self.FitMapToWindow()
396          # force a redraw. If map is not empty, it's already been called          # force a redraw. If map is not empty, it's already been called
397          # by FitMapToWindow but if map is empty it hasn't been called          # by FitMapToWindow but if map is empty it hasn't been called
398          # yet so we have to explicitly call it.          # yet so we have to explicitly call it.
399          self.redraw()          self.full_redraw()
400    
401      def Map(self):      def Map(self):
402          return self.map          return self.map
# Line 342  class MapCanvas(wxWindow): Line 404  class MapCanvas(wxWindow):
404      def redraw(self, *args):      def redraw(self, *args):
405          self.Refresh(0)          self.Refresh(0)
406    
407        def full_redraw(self, *args):
408            self.bitmap = None
409            self.redraw()
410    
411      def projection_changed(self, *args):      def projection_changed(self, *args):
412          self.FitMapToWindow()          self.FitMapToWindow()
413          self.redraw()          self.full_redraw()
414    
415      def set_view_transform(self, scale, offset):      def set_view_transform(self, scale, offset):
416          self.scale = scale          self.scale = scale
417          self.offset = offset          self.offset = offset
418          self.redraw()          self.full_redraw()
419    
420      def proj_to_win(self, x, y):      def proj_to_win(self, x, y):
421          """\          """\
# Line 446  class MapCanvas(wxWindow): Line 512  class MapCanvas(wxWindow):
512      def CurrentTool(self):      def CurrentTool(self):
513          return self.tool and self.tool.Name() or None          return self.tool and self.tool.Name() or None
514    
515        def CurrentPosition(self):
516            """Return current position of the mouse in projected coordinates.
517    
518            The result is a 2-tuple of floats with the coordinates. If the
519            mouse is not in the window, the result is None.
520            """
521            if self.current_position is not None:
522                x, y = self.current_position
523                return self.win_to_proj(x, y)
524            else:
525                return None
526    
527        def set_current_position(self, event):
528            """Set the current position from event
529    
530            Should be called by all events that contain mouse positions
531            especially EVT_MOTION. The event paramete may be None to
532            indicate the the pointer left the window.
533            """
534            if event is not None:
535                self.current_position = (event.m_x, event.m_y)
536            else:
537                self.current_position = None
538            self.issue(VIEW_POSITION)
539    
540      def OnLeftDown(self, event):      def OnLeftDown(self, event):
541            self.set_current_position(event)
542          if self.tool is not None:          if self.tool is not None:
543              self.drag_dc = wxClientDC(self)              self.drag_dc = wxClientDC(self)
544              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
# Line 458  class MapCanvas(wxWindow): Line 550  class MapCanvas(wxWindow):
550                    
551      def OnLeftUp(self, event):      def OnLeftUp(self, event):
552          self.ReleaseMouse()          self.ReleaseMouse()
553            self.set_current_position(event)
554          if self.dragging:          if self.dragging:
555              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
556              self.tool.MouseUp(event)              self.tool.MouseUp(event)
# Line 465  class MapCanvas(wxWindow): Line 558  class MapCanvas(wxWindow):
558          self.dragging = 0          self.dragging = 0
559    
560      def OnMotion(self, event):      def OnMotion(self, event):
561            self.set_current_position(event)
562          if self.dragging:          if self.dragging:
563              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
564              self.tool.MouseMove(event)              self.tool.MouseMove(event)
565              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
566    
567        def OnLeaveWindow(self, event):
568            self.set_current_position(None)
569    
570      def OnIdle(self, event):      def OnIdle(self, event):
571          if self.redraw_on_idle:          if self.redraw_on_idle:
572              self.do_redraw()              self.do_redraw()
573          self.redraw_on_idle = 0          self.redraw_on_idle = 0
574    
575        def OnSize(self, event):
576            # the window's size has changed. We have to get a new bitmap. If
577            # we want to be clever we could try to get by without throwing
578            # everything away. E.g. when the window gets smaller, we could
579            # either keep the bitmap or create the new one from the old one.
580            # Even when the window becomes larger some parts of the bitmap
581            # could be reused.
582            self.full_redraw()
583    
584      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
585          self.redraw()          self.full_redraw()
586    
587        def unprojected_rect_around_point(self, x, y):
588            """return a rect a few pixels around (x, y) in unprojected corrdinates
589    
590            The return value is a tuple (minx, miny, maxx, maxy) suitable a
591            parameter to a layer's ShapesInRegion method.
592            """
593            map_proj = self.map.projection
594            if map_proj is not None:
595                inverse = map_proj.Inverse
596            else:
597                inverse = None
598    
599            xs = []
600            ys = []
601            for dx, dy in ((-1, -1), (1, -1), (1, 1), (-1, 1)):
602                px, py = self.win_to_proj(x + dx, y + dy)
603                if inverse:
604                    px, py = inverse(px, py)
605                xs.append(px)
606                ys.append(py)
607            return (min(xs), min(ys), max(xs), max(ys))
608    
609      def find_shape_at(self, px, py, select_labels = 0, selected_layer = 1):      def find_shape_at(self, px, py, select_labels = 0, selected_layer = 1):
610          """Determine the shape at point px, py in window coords          """Determine the shape at point px, py in window coords
# Line 500  class MapCanvas(wxWindow): Line 628  class MapCanvas(wxWindow):
628          scale = self.scale          scale = self.scale
629          offx, offy = self.offset          offx, offy = self.offset
630    
631            box = self.unprojected_rect_around_point(px, py)
632    
633          if select_labels:          if select_labels:
634              labels = self.map.LabelLayer().Labels()              labels = self.map.LabelLayer().Labels()
635                            
# Line 564  class MapCanvas(wxWindow): Line 694  class MapCanvas(wxWindow):
694              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
695    
696              select_shape = -1              select_shape = -1
697    
698                shape_ids = layer.ShapesInRegion(box)
699                shape_ids.reverse()
700    
701              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
702                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
703                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
704                                                      i,                                                      i,
705                                                      filled, stroked,                                                      filled, stroked,
# Line 576  class MapCanvas(wxWindow): Line 710  class MapCanvas(wxWindow):
710                          select_shape = i                          select_shape = i
711                          break                          break
712              elif shapetype == SHAPETYPE_ARC:              elif shapetype == SHAPETYPE_ARC:
713                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
714                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
715                                                      i, 0, 1,                                                      i, 0, 1,
716                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
# Line 586  class MapCanvas(wxWindow): Line 720  class MapCanvas(wxWindow):
720                          select_shape = i                          select_shape = i
721                          break                          break
722              elif shapetype == SHAPETYPE_POINT:              elif shapetype == SHAPETYPE_POINT:
723                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
724                      shape = layer.Shape(i)                      shape = layer.Shape(i)
725                      x, y = shape.Points()[0]                      x, y = shape.Points()[0]
726                      if inverse:                      if inverse:
# Line 604  class MapCanvas(wxWindow): Line 738  class MapCanvas(wxWindow):
738          return None, None          return None, None
739    
740      def SelectShapeAt(self, x, y):      def SelectShapeAt(self, x, y):
741          layer, shape = self.find_shape_at(x, y)          layer, shape = self.find_shape_at(x, y, selected_layer = 0)
742          # If layer is None, then shape will also be None. We don't want          # If layer is None, then shape will also be None. We don't want
743          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
744          # the already selected layer again.          # the already selected layer again.

Legend:
Removed from v.60  
changed lines
  Added in v.159

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26