/[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 43 by bh, Fri Sep 7 11:55:51 2001 UTC revision 125 by bh, Thu May 2 18:55:33 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 127  class ZoomInTool(RectTool): Line 127  class ZoomInTool(RectTool):
127      def MouseUp(self, event):      def MouseUp(self, event):
128          if self.dragging:          if self.dragging:
129              Tool.MouseUp(self, event)              Tool.MouseUp(self, event)
130              self.view.FitRectToWindow(self.proj_rect())              sx, sy = self.start
131                cx, cy = self.current
132                if sx == cx and sy == cy:
133                    # Just a mouse click. Simply zoom in by a factor of two
134                    self.view.ZoomFactor(2, center = (cx, cy))
135                else:
136                    # A drag. Zoom in to the rectangle
137                    self.view.FitRectToWindow(self.proj_rect())
138    
139    
140  class ZoomOutTool(RectTool):  class ZoomOutTool(RectTool):
# Line 142  class ZoomOutTool(RectTool): Line 149  class ZoomOutTool(RectTool):
149              Tool.MouseUp(self, event)              Tool.MouseUp(self, event)
150              sx, sy = self.start              sx, sy = self.start
151              cx, cy = self.current              cx, cy = self.current
152              self.view.ZoomOutToRect((min(sx, cx), min(sy, cy),              if sx == cx and sy == cy:
153                                       max(sx, cx), max(sy, cy)))                  # Just a mouse click. Simply zoom out by a factor of two
154                    self.view.ZoomFactor(0.5, center = (cy, cy))
155                else:
156                    # A drag. Zoom out to the rectangle
157                    self.view.ZoomOutToRect((min(sx, cx), min(sy, cy),
158                                             max(sx, cx), max(sy, cy)))
159    
160    
161  class PanTool(Tool):  class PanTool(Tool):
# Line 228  class MapPrintout(wx.wxPrintout): Line 240  class MapPrintout(wx.wxPrintout):
240          return wx.true          return wx.true
241                    
242    
243  class MapCanvas(wxWindow):  class MapCanvas(wxWindow, Publisher):
244    
245      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
246    
247      def __init__(self, parent, winid, interactor):      def __init__(self, parent, winid, interactor):
248          wxWindow.__init__(self, parent, winid)          wxWindow.__init__(self, parent, winid)
249          self.SetBackgroundColour(wxColour(255, 255, 255))          self.SetBackgroundColour(wxColour(255, 255, 255))
250    
251            # the map displayed in this canvas. Set with SetMap()
252          self.map = None          self.map = None
253    
254            # scale and offset describe the transformation from projected
255            # coordinates to window coordinates.
256          self.scale = 1.0          self.scale = 1.0
257          self.offset = (0, 0)          self.offset = (0, 0)
258    
259            # whether the user is currently dragging the mouse, i.e. moving
260            # the mouse while pressing a mouse button
261          self.dragging = 0          self.dragging = 0
262    
263            # the currently active tool
264          self.tool = None          self.tool = None
265    
266            # The current mouse position of the last OnMotion event or None
267            # if the mouse is outside the window.
268            self.current_position = None
269    
270    
271            # If true, OnIdle will call do_redraw to do the actual
272            # redrawing. Set by OnPaint to avoid some unnecessary redraws.
273            # To force a redraw call full_redraw().
274          self.redraw_on_idle = 0          self.redraw_on_idle = 0
275    
276            # the bitmap serving as backing store
277            self.bitmap = None
278    
279            # the interactor
280            self.interactor = interactor
281            self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)
282    
283            # subscribe the WX events we're interested in
284          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
285          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
286          EVT_LEFT_UP(self, self.OnLeftUp)          EVT_LEFT_UP(self, self.OnLeftUp)
287          EVT_MOTION(self, self.OnMotion)          EVT_MOTION(self, self.OnMotion)
288            EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
289            wx.EVT_SIZE(self, self.OnSize)
290          wx.EVT_IDLE(self, self.OnIdle)          wx.EVT_IDLE(self, self.OnIdle)
291          self.interactor = interactor  
292          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)      def __del__(self):
293            wxWindow.__del__(self)
294            Publisher.__del__(self)
295    
296      def OnPaint(self, event):      def OnPaint(self, event):
297          dc = wxPaintDC(self)          dc = wxPaintDC(self)
298          if self.map is None or not self.map.HasLayers():          if self.map is not None and self.map.HasLayers():
299              return              # We have a non-empty map. Redraw it in idle time
300          self.redraw_on_idle = 1              self.redraw_on_idle = 1
301            else:
302                # If we've got no map or if the map is empty, simply clear
303                # the screen.
304                
305                # XXX it's probably possible to get rid of this. The
306                # background color of the window is already white and the
307                # only thing we may have to do is to call self.Refresh()
308                # with a true argument in the right places.
309                dc.BeginDrawing()
310                dc.Clear()            
311                dc.EndDrawing()
312    
313      def do_redraw(self):      def do_redraw(self):
314          width, height = self.GetSizeTuple()          # This should only be called if we have a non-empty map. We draw
315          bitmap = wx.wxEmptyBitmap(width, height)          # it into a memory DC and then blit it to the screen.
   
         dc = wx.wxMemoryDC()  
         dc.SelectObject(bitmap)  
316    
317          dc.BeginDrawing()          width, height = self.GetSizeTuple()
318    
319          dc.SetBrush(wx.wxWHITE_BRUSH)          # If self.bitmap's still there, reuse it. Otherwise redraw it
320          dc.SetPen(wx.wxTRANSPARENT_PEN)          if self.bitmap is not None:
321          dc.DrawRectangle(0, 0, width, height)              bitmap = self.bitmap
   
         if 1: #self.interactor.selected_map is self.map:  
             selected_layer = self.interactor.selected_layer  
             selected_shape = self.interactor.selected_shape  
322          else:          else:
323              selected_layer = None              bitmap = wx.wxEmptyBitmap(width, height)
324              selected_shape = None              dc = wx.wxMemoryDC()
325                            dc.SelectObject(bitmap)
326          renderer = ScreenRenderer(dc, self.scale, self.offset)              dc.BeginDrawing()
327          renderer.RenderMap(self.map, selected_layer, selected_shape)  
328                # clear the background
329                dc.SetBrush(wx.wxWHITE_BRUSH)
330                dc.SetPen(wx.wxTRANSPARENT_PEN)
331                dc.DrawRectangle(0, 0, width, height)
332    
333                if 1: #self.interactor.selected_map is self.map:
334                    selected_layer = self.interactor.selected_layer
335                    selected_shape = self.interactor.selected_shape
336                else:
337                    selected_layer = None
338                    selected_shape = None
339    
340                # draw the map into the bitmap
341                renderer = ScreenRenderer(dc, self.scale, self.offset)
342                renderer.RenderMap(self.map, selected_layer, selected_shape)
343    
344                dc.EndDrawing()
345                dc.SelectObject(wx.wxNullBitmap)
346                self.bitmap = bitmap
347    
348            # blit the bitmap to the screen
349            dc = wx.wxMemoryDC()
350            dc.SelectObject(bitmap)
351          clientdc = wxClientDC(self)          clientdc = wxClientDC(self)
352          clientdc.BeginDrawing()          clientdc.BeginDrawing()
353          clientdc.Blit(0, 0, width, height, dc, 0, 0)          clientdc.Blit(0, 0, width, height, dc, 0, 0)
354          clientdc.EndDrawing()          clientdc.EndDrawing()
355    
   
356      def Print(self):      def Print(self):
357          printer = wx.wxPrinter()          printer = wx.wxPrinter()
358          printout = MapPrintout(self.map)          printout = MapPrintout(self.map)
# Line 295  class MapCanvas(wxWindow): Line 364  class MapCanvas(wxWindow):
364                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
365          if self.map is not None:          if self.map is not None:
366              for channel in redraw_channels:              for channel in redraw_channels:
367                  self.map.Unsubscribe(channel, self.redraw)                  self.map.Unsubscribe(channel, self.full_redraw)
368              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
369                                   self.projection_changed)                                   self.projection_changed)
370          self.map = map          self.map = map
371          if self.map is not None:          if self.map is not None:
372              for channel in redraw_channels:              for channel in redraw_channels:
373                  self.map.Subscribe(channel, self.redraw)                  self.map.Subscribe(channel, self.full_redraw)
374              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)
375          self.FitMapToWindow()          self.FitMapToWindow()
376            # force a redraw. If map is not empty, it's already been called
377            # by FitMapToWindow but if map is empty it hasn't been called
378            # yet so we have to explicitly call it.
379            self.full_redraw()
380    
381      def Map(self):      def Map(self):
382          return self.map          return self.map
# Line 311  class MapCanvas(wxWindow): Line 384  class MapCanvas(wxWindow):
384      def redraw(self, *args):      def redraw(self, *args):
385          self.Refresh(0)          self.Refresh(0)
386    
387        def full_redraw(self, *args):
388            self.bitmap = None
389            self.redraw()
390    
391      def projection_changed(self, *args):      def projection_changed(self, *args):
392          self.FitMapToWindow()          self.FitMapToWindow()
393          self.redraw()          self.full_redraw()
394    
395      def set_view_transform(self, scale, offset):      def set_view_transform(self, scale, offset):
396          self.scale = scale          self.scale = scale
397          self.offset = offset          self.offset = offset
398          self.redraw()          self.full_redraw()
399    
400      def proj_to_win(self, x, y):      def proj_to_win(self, x, y):
401          """\          """\
# Line 337  class MapCanvas(wxWindow): Line 414  class MapCanvas(wxWindow):
414      def FitRectToWindow(self, rect):      def FitRectToWindow(self, rect):
415          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
416          llx, lly, urx, ury = rect          llx, lly, urx, ury = rect
417            if llx == urx or lly == ury:
418                # zero with or zero height. Do Nothing
419                return
420          scalex = width / (urx - llx)          scalex = width / (urx - llx)
421          scaley = height / (ury - lly)          scaley = height / (ury - lly)
422          scale = min(scalex, scaley)          scale = min(scalex, scaley)
# Line 353  class MapCanvas(wxWindow): Line 433  class MapCanvas(wxWindow):
433          if bbox is not None:          if bbox is not None:
434              self.FitRectToWindow(bbox)              self.FitRectToWindow(bbox)
435    
436      def ZoomFactor(self, factor):      def ZoomFactor(self, factor, center = None):
437            """Multiply the zoom by factor and center on center.
438    
439            The optional parameter center is a point in window coordinates
440            that should be centered. If it is omitted, it defaults to the
441            center of the window
442            """
443          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
444          scale = self.scale * factor          scale = self.scale * factor
445          offx, offy = self.offset          offx, offy = self.offset
446          offset = (factor * (offx - width / 2) + width / 2,          if center is not None:
447                    factor * (offy - height / 2) + height / 2)              cx, cy = center
448            else:
449                cx = width / 2
450                cy = height / 2
451            offset = (factor * (offx - cx) + width / 2,
452                      factor * (offy - cy) + height / 2)
453          self.set_view_transform(scale, offset)          self.set_view_transform(scale, offset)
454    
455      def ZoomOutToRect(self, rect):      def ZoomOutToRect(self, rect):
# Line 401  class MapCanvas(wxWindow): Line 492  class MapCanvas(wxWindow):
492      def CurrentTool(self):      def CurrentTool(self):
493          return self.tool and self.tool.Name() or None          return self.tool and self.tool.Name() or None
494    
495        def CurrentPosition(self):
496            """Return current position of the mouse in projected coordinates.
497    
498            The result is a 2-tuple of floats with the coordinates. If the
499            mouse is not in the window, the result is None.
500            """
501            if self.current_position is not None:
502                x, y = self.current_position
503                return self.win_to_proj(x, y)
504            else:
505                return None
506    
507        def set_current_position(self, event):
508            """Set the current position from event
509    
510            Should be called by all events that contain mouse positions
511            especially EVT_MOTION. The event paramete may be None to
512            indicate the the pointer left the window.
513            """
514            if event is not None:
515                self.current_position = (event.m_x, event.m_y)
516            else:
517                self.current_position = None
518            self.issue(VIEW_POSITION)
519    
520      def OnLeftDown(self, event):      def OnLeftDown(self, event):
521            self.set_current_position(event)
522          if self.tool is not None:          if self.tool is not None:
523              self.drag_dc = wxClientDC(self)              self.drag_dc = wxClientDC(self)
524              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
# Line 413  class MapCanvas(wxWindow): Line 530  class MapCanvas(wxWindow):
530                    
531      def OnLeftUp(self, event):      def OnLeftUp(self, event):
532          self.ReleaseMouse()          self.ReleaseMouse()
533            self.set_current_position(event)
534          if self.dragging:          if self.dragging:
535              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
536              self.tool.MouseUp(event)              self.tool.MouseUp(event)
# Line 420  class MapCanvas(wxWindow): Line 538  class MapCanvas(wxWindow):
538          self.dragging = 0          self.dragging = 0
539    
540      def OnMotion(self, event):      def OnMotion(self, event):
541            self.set_current_position(event)
542          if self.dragging:          if self.dragging:
543              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
544              self.tool.MouseMove(event)              self.tool.MouseMove(event)
545              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
546    
547        def OnLeaveWindow(self, event):
548            self.set_current_position(None)
549    
550      def OnIdle(self, event):      def OnIdle(self, event):
551          if self.redraw_on_idle:          if self.redraw_on_idle:
552              self.do_redraw()              self.do_redraw()
553          self.redraw_on_idle = 0          self.redraw_on_idle = 0
554    
555        def OnSize(self, event):
556            # the window's size has changed. We have to get a new bitmap. If
557            # we want to be clever we could try to get by without throwing
558            # everything away. E.g. when the window gets smaller, we could
559            # either keep the bitmap or create the new one from the old one.
560            # Even when the window becomes larger some parts of the bitmap
561            # could be reused.
562            self.full_redraw()
563    
564      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
565          self.redraw()          self.full_redraw()
566    
567      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):
568          """Determine the shape at point px, py in window coords          """Determine the shape at point px, py in window coords
# Line 462  class MapCanvas(wxWindow): Line 593  class MapCanvas(wxWindow):
593                  dc = wxClientDC(self)                  dc = wxClientDC(self)
594                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
595                  dc.SetFont(font)                  dc.SetFont(font)
596                  for i in range(len(labels)):                  for i in range(len(labels) - 1, -1, -1):
597                      label = labels[i]                      label = labels[i]
598                      x = label.x                      x = label.x
599                      y = label.y                      y = label.y
# Line 520  class MapCanvas(wxWindow): Line 651  class MapCanvas(wxWindow):
651    
652              select_shape = -1              select_shape = -1
653              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
654                  for i in range(layer.NumShapes()):                  for i in range(layer.NumShapes() - 1, -1, -1):
655                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
656                                                      i,                                                      i,
657                                                      filled, stroked,                                                      filled, stroked,
# Line 531  class MapCanvas(wxWindow): Line 662  class MapCanvas(wxWindow):
662                          select_shape = i                          select_shape = i
663                          break                          break
664              elif shapetype == SHAPETYPE_ARC:              elif shapetype == SHAPETYPE_ARC:
665                  for i in range(layer.NumShapes()):                  for i in range(layer.NumShapes() - 1, -1, -1):
666                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
667                                                      i, 0, 1,                                                      i, 0, 1,
668                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
# Line 541  class MapCanvas(wxWindow): Line 672  class MapCanvas(wxWindow):
672                          select_shape = i                          select_shape = i
673                          break                          break
674              elif shapetype == SHAPETYPE_POINT:              elif shapetype == SHAPETYPE_POINT:
675                  for i in range(layer.NumShapes()):                  for i in range(layer.NumShapes() - 1, -1, -1):
676                      shape = layer.Shape(i)                      shape = layer.Shape(i)
677                      x, y = shape.Points()[0]                      x, y = shape.Points()[0]
678                      if inverse:                      if inverse:
# Line 559  class MapCanvas(wxWindow): Line 690  class MapCanvas(wxWindow):
690          return None, None          return None, None
691    
692      def SelectShapeAt(self, x, y):      def SelectShapeAt(self, x, y):
693          layer, shape = self.find_shape_at(x, y)          layer, shape = self.find_shape_at(x, y, selected_layer = 0)
694          # 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
695          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
696          # the already selected layer again.          # the already selected layer again.

Legend:
Removed from v.43  
changed lines
  Added in v.125

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26