/[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 145 by bh, Tue May 7 14:58:05 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 240  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            # If true, OnIdle will call do_redraw to do the actual
271            # redrawing. Set by OnPaint to avoid some unnecessary redraws.
272            # To force a redraw call full_redraw().
273          self.redraw_on_idle = 0          self.redraw_on_idle = 0
274    
275            # The region to update when idle
276            self.update_region = wx.wxRegion()
277    
278            # the bitmap serving as backing store
279            self.bitmap = None
280    
281            # the interactor
282            self.interactor = interactor
283            self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)
284    
285            # subscribe the WX events we're interested in
286          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
287          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
288          EVT_LEFT_UP(self, self.OnLeftUp)          EVT_LEFT_UP(self, self.OnLeftUp)
289          EVT_MOTION(self, self.OnMotion)          EVT_MOTION(self, self.OnMotion)
290            EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
291            wx.EVT_SIZE(self, self.OnSize)
292          wx.EVT_IDLE(self, self.OnIdle)          wx.EVT_IDLE(self, self.OnIdle)
293          self.interactor = interactor  
294          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)      def __del__(self):
295            wxWindow.__del__(self)
296            Publisher.__del__(self)
297    
298      def OnPaint(self, event):      def OnPaint(self, event):
299          dc = wxPaintDC(self)          dc = wxPaintDC(self)
300          if self.map is not None and self.map.HasLayers():          if self.map is not None and self.map.HasLayers():
301              # We have a non-empty map. Redraw it in idle time              # We have a non-empty map. Redraw it in idle time
302              self.redraw_on_idle = 1              self.redraw_on_idle = 1
303                # update the region that has to be redrawn
304                self.update_region.UnionRegion(self.GetUpdateRegion())
305          else:          else:
306              # 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
307              # the screen.              # the screen.
# Line 278  class MapCanvas(wxWindow): Line 314  class MapCanvas(wxWindow):
314              dc.Clear()                          dc.Clear()            
315              dc.EndDrawing()              dc.EndDrawing()
316    
317                # clear the region
318                self.update_region = wx.wxRegion()
319    
320      def do_redraw(self):      def do_redraw(self):
321          # 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.
322          # it into a memory DC and then blit it to the screen.  
323            # get the update region and reset it.
324            update_box = self.update_region.GetBox()
325            self.update_region = wx.wxRegion()
326    
327            # Get the window size.
328          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
         bitmap = wx.wxEmptyBitmap(width, height)  
         dc = wx.wxMemoryDC()  
         dc.SelectObject(bitmap)  
         dc.BeginDrawing()  
329    
330          # clear the background          # If self.bitmap's still there, reuse it. Otherwise redraw it
331          dc.SetBrush(wx.wxWHITE_BRUSH)          if self.bitmap is not None:
332          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  
333          else:          else:
334              selected_layer = None              bitmap = wx.wxEmptyBitmap(width, height)
335              selected_shape = None              dc = wx.wxMemoryDC()
336                dc.SelectObject(bitmap)
337                dc.BeginDrawing()
338    
339          # draw the map into the bitmap              # clear the background
340          renderer = ScreenRenderer(dc, self.scale, self.offset)              dc.SetBrush(wx.wxWHITE_BRUSH)
341          renderer.RenderMap(self.map, selected_layer, selected_shape)              dc.SetPen(wx.wxTRANSPARENT_PEN)
342                dc.DrawRectangle(0, 0, width, height)
343    
344                if 1: #self.interactor.selected_map is self.map:
345                    selected_layer = self.interactor.selected_layer
346                    selected_shape = self.interactor.selected_shape
347                else:
348                    selected_layer = None
349                    selected_shape = None
350    
351                # draw the map into the bitmap
352                renderer = ScreenRenderer(dc, self.scale, self.offset)
353                renderer.RenderMap(self.map, update_box,
354                                   selected_layer, selected_shape)
355    
356          dc.EndDrawing()              dc.EndDrawing()
357                dc.SelectObject(wx.wxNullBitmap)
358                self.bitmap = bitmap
359    
360          # blit the bitmap to the screen          # blit the bitmap to the screen
361            dc = wx.wxMemoryDC()
362            dc.SelectObject(bitmap)
363          clientdc = wxClientDC(self)          clientdc = wxClientDC(self)
364          clientdc.BeginDrawing()          clientdc.BeginDrawing()
365          clientdc.Blit(0, 0, width, height, dc, 0, 0)          clientdc.Blit(0, 0, width, height, dc, 0, 0)
# Line 322  class MapCanvas(wxWindow): Line 376  class MapCanvas(wxWindow):
376                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
377          if self.map is not None:          if self.map is not None:
378              for channel in redraw_channels:              for channel in redraw_channels:
379                  self.map.Unsubscribe(channel, self.redraw)                  self.map.Unsubscribe(channel, self.full_redraw)
380              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
381                                   self.projection_changed)                                   self.projection_changed)
382          self.map = map          self.map = map
383          if self.map is not None:          if self.map is not None:
384              for channel in redraw_channels:              for channel in redraw_channels:
385                  self.map.Subscribe(channel, self.redraw)                  self.map.Subscribe(channel, self.full_redraw)
386              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)
387          self.FitMapToWindow()          self.FitMapToWindow()
388          # 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
389          # by FitMapToWindow but if map is empty it hasn't been called          # by FitMapToWindow but if map is empty it hasn't been called
390          # yet so we have to explicitly call it.          # yet so we have to explicitly call it.
391          self.redraw()          self.full_redraw()
392    
393      def Map(self):      def Map(self):
394          return self.map          return self.map
# Line 342  class MapCanvas(wxWindow): Line 396  class MapCanvas(wxWindow):
396      def redraw(self, *args):      def redraw(self, *args):
397          self.Refresh(0)          self.Refresh(0)
398    
399        def full_redraw(self, *args):
400            self.bitmap = None
401            self.redraw()
402    
403      def projection_changed(self, *args):      def projection_changed(self, *args):
404          self.FitMapToWindow()          self.FitMapToWindow()
405          self.redraw()          self.full_redraw()
406    
407      def set_view_transform(self, scale, offset):      def set_view_transform(self, scale, offset):
408          self.scale = scale          self.scale = scale
409          self.offset = offset          self.offset = offset
410          self.redraw()          self.full_redraw()
411    
412      def proj_to_win(self, x, y):      def proj_to_win(self, x, y):
413          """\          """\
# Line 446  class MapCanvas(wxWindow): Line 504  class MapCanvas(wxWindow):
504      def CurrentTool(self):      def CurrentTool(self):
505          return self.tool and self.tool.Name() or None          return self.tool and self.tool.Name() or None
506    
507        def CurrentPosition(self):
508            """Return current position of the mouse in projected coordinates.
509    
510            The result is a 2-tuple of floats with the coordinates. If the
511            mouse is not in the window, the result is None.
512            """
513            if self.current_position is not None:
514                x, y = self.current_position
515                return self.win_to_proj(x, y)
516            else:
517                return None
518    
519        def set_current_position(self, event):
520            """Set the current position from event
521    
522            Should be called by all events that contain mouse positions
523            especially EVT_MOTION. The event paramete may be None to
524            indicate the the pointer left the window.
525            """
526            if event is not None:
527                self.current_position = (event.m_x, event.m_y)
528            else:
529                self.current_position = None
530            self.issue(VIEW_POSITION)
531    
532      def OnLeftDown(self, event):      def OnLeftDown(self, event):
533            self.set_current_position(event)
534          if self.tool is not None:          if self.tool is not None:
535              self.drag_dc = wxClientDC(self)              self.drag_dc = wxClientDC(self)
536              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
# Line 458  class MapCanvas(wxWindow): Line 542  class MapCanvas(wxWindow):
542                    
543      def OnLeftUp(self, event):      def OnLeftUp(self, event):
544          self.ReleaseMouse()          self.ReleaseMouse()
545            self.set_current_position(event)
546          if self.dragging:          if self.dragging:
547              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
548              self.tool.MouseUp(event)              self.tool.MouseUp(event)
# Line 465  class MapCanvas(wxWindow): Line 550  class MapCanvas(wxWindow):
550          self.dragging = 0          self.dragging = 0
551    
552      def OnMotion(self, event):      def OnMotion(self, event):
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.MouseMove(event)              self.tool.MouseMove(event)
557              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
558    
559        def OnLeaveWindow(self, event):
560            self.set_current_position(None)
561    
562      def OnIdle(self, event):      def OnIdle(self, event):
563          if self.redraw_on_idle:          if self.redraw_on_idle:
564              self.do_redraw()              self.do_redraw()
565          self.redraw_on_idle = 0          self.redraw_on_idle = 0
566    
567        def OnSize(self, event):
568            # the window's size has changed. We have to get a new bitmap. If
569            # we want to be clever we could try to get by without throwing
570            # everything away. E.g. when the window gets smaller, we could
571            # either keep the bitmap or create the new one from the old one.
572            # Even when the window becomes larger some parts of the bitmap
573            # could be reused.
574            self.full_redraw()
575    
576      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
577          self.redraw()          self.full_redraw()
578    
579      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):
580          """Determine the shape at point px, py in window coords          """Determine the shape at point px, py in window coords
# Line 604  class MapCanvas(wxWindow): Line 702  class MapCanvas(wxWindow):
702          return None, None          return None, None
703    
704      def SelectShapeAt(self, x, y):      def SelectShapeAt(self, x, y):
705          layer, shape = self.find_shape_at(x, y)          layer, shape = self.find_shape_at(x, y, selected_layer = 0)
706          # 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
707          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
708          # the already selected layer again.          # the already selected layer again.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26