Welcome, guest | Sign In | My Account | Store | Cart

This recipe talks about how to implement rubberbanding code for canvas objects in a wxPython application. Canvas classes in wxPython include wxPanel, wxStaticBitmap, wxGlCanvas etc.

Python, 172 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# This class describes a generic method of drawing rubberbands
# on a wxPython canvas object (wxStaticBitmap, wxPanel etc) when
# the user presses the left mouse button and drags it over a rectangular
# area. It has methods to return the selected area by the user as 
# a rectangular 4 tuple / Clear the selected area.

# Beginning of code

class wxPyRubberBander:
    """ A class to manage mouse events/ rubberbanding of a wxPython
        canvas object """

    def __init__(self, canvas):
        
        # canvas object
        self._canvas = canvas
        # mouse selection start point
        self.m_stpoint=wxPoint(0,0)
        # mouse selection end point
        self.m_endpoint=wxPoint(0,0)
        # mouse selection cache point
        self.m_savepoint=wxPoint(0,0)
        
        # flags for left click/ selection
        self._leftclicked=false
        self._selected=false

        # Register event handlers for mouse
        self.RegisterEventHandlers()

    def RegisterEventHandlers(self):
        """ Register event handlers for this object """

        EVT_LEFT_DOWN(self._canvas, self.OnMouseEvent)
        EVT_LEFT_UP(self._canvas, self.OnMouseEvent)
        EVT_MOTION(self._canvas, self.OnMouseEvent)

    
    def OnMouseEvent(self, event):
        """ This function manages mouse events """


        if event:
            
            # set mouse cursor
            self._canvas.SetCursor(wxStockCursor(wxCURSOR_ARROW))
            # get device context of canvas
            dc= wxClientDC(self._canvas)
            
            # Set logical function to XOR for rubberbanding
            dc.SetLogicalFunction(wxXOR)
            
            # Set dc brush and pen
            # Here I set brush and pen to white and grey respectively
            # You can set it to your own choices
            
            # The brush setting is not really needed since we
            # dont do any filling of the dc. It is set just for 
            # the sake of completion.

            wbrush = wxBrush(wxColour(255,255,255), wxTRANSPARENT)
            wpen = wxPen(wxColour(200, 200, 200), 1, wxSOLID)
            dc.SetBrush(wbrush)
            dc.SetPen(wpen)

            
        if event.LeftDown():
 
           # Left mouse button down, change cursor to
           # something else to denote event capture
           self.m_stpoint = event.GetPosition()
           cur = wxStockCursor(wxCURSOR_CROSS)  
           self._canvas.SetCursor(cur)
        
           # invalidate current canvas
           self._canvas.Refresh()
           # cache current position
           self.m_savepoint = self.m_stpoint
           self._selected = false
           self._leftclicked = true

        elif event.Dragging():   
           
            # User is dragging the mouse, check if
            # left button is down
            
            if self._leftclicked:

                # reset dc bounding box
                dc.ResetBoundingBox()
                dc.BeginDrawing()
                w = (self.m_savepoint.x - self.m_stpoint.x)
                h = (self.m_savepoint.y - self.m_stpoint.y)
                
                # To erase previous rectangle
                dc.DrawRectangle(self.m_stpoint.x, self.m_stpoint.y, w, h)
                
                # Draw new rectangle
                self.m_endpoint =  event.GetPosition()
                
                w = (self.m_endpoint.x - self.m_stpoint.x)
                h = (self.m_endpoint.y - self.m_stpoint.y)
                
                # Set clipping region to rectangle corners
                dc.SetClippingRegion(self.m_stpoint.x, self.m_stpoint.y, w,h)
                dc.DrawRectangle(self.m_stpoint.x, self.m_stpoint.y, w, h) 
                dc.EndDrawing()
               
                self.m_savepoint = self.m_endpoint # cache current endpoint

        elif event.LeftUp():

            # User released left button, change cursor back
            self._canvas.SetCursor(wxSTOCK_CURSOR(wxCURSOR_ARROW))       
            self._selected = true  #selection is done
            self._leftclicked = false # end of clicking  
            
            
    def GetCurrentSelection(self):
        """ Return the current selected rectangle """
        
        # if there is no selection, selection defaults to
        # current viewport
        
        left = wxPoint(0,0)
        right = wxPoint(0,0)
        
        # user dragged mouse to right
        if self.m_endpoint.y > self.m_stpoint.y:
            right = self.m_endpoint
            left = self.m_stpoint
        # user dragged mouse to left
        elif self.m_endpoint.y < self.m_stpoint.y:
            right = self.m_stpoint
            left = self.m_endpoint
   
        return (left.x, left.y, right.x, right.y)


    def ClearCurrentSelection(self):
        """ Clear the current selected rectangle """
        
        box = self.GetCurrentSelection()
        
        dc=wxClientDC(self._canvas)
        
        w = box[2] - box[0]
        h = box[3] - box[1]
        dc.SetClippingRegion(box[0], box[1], w, h)
        dc.SetLogicalFunction(wxXOR)
        
        # The brush is not really needed since we
        # dont do any filling of the dc. It is set for 
        # sake of completion.
        
        wbrush = wxBrush(wxColour(255,255,255), wxTRANSPARENT)
        wpen = wxPen(wxColour(200, 200, 200), 1, wxSOLID)
        dc.SetBrush(wbrush)
        dc.SetPen(wpen)
        dc.DrawRectangle(box[0], box[1], w,h)
        self._selected = false 
        
        # reset selection to canvas size
        self.ResetSelection()    

    def ResetSelection(self):
        """ Resets the mouse selection to entire canvas """
    
        self.m_stpoint = wxPoint(0,0)
        sz=self._canvas.GetSize()
        w,h=sz.GetWidth(), sz.GetHeight()
        self.m_endpoint = wxPoint(w,h)

Rubberbanding is a common task in computer graphics for selecting specific areas of images, image canvases to perform functions like copy/cut/crop etc. This solution is straightforward, concise and elegant. There are no known issues and there could be many alternate implementations.

Fixed errors in indentation -30/12/2003 - Anand

2 comments

fherdom 15 years, 10 months ago  # | flag

Sugerencia Python2.4, wxPython2.6. Tenemos que poner esto:

if event.LeftDown():

   self._canvas.CaptureMouse()
   ....

elif event.LeftUp():
   ...
   self._canvas.ReleaseMouse()
John Perry 14 years, 5 months ago  # | flag

Question for implementing. Hi everyone!

Earlier I asked a question about mouse interaction with a GUI. I have found a pretty comprehensive script that is supposed to work from http://aspn.activestate.com/ASPN/Co...n/Recipe/189744 . The problem is I'm not to savy with classes yet and I don't know how to call it and use it. Can someone guide me through this, I would be pretty grateful.

The main objective would be to set the image that is loaded as the canvas of the rubberbander and output the result.

Says we start with some pseudocode:

root=Tk() im=Image.open("filename")

rubberbandscript(........)

print output

root.mainloop()

Thanks alot, JP

Created by Anand on Sun, 16 Mar 2003 (PSF)
Python recipes (4591)
Anand's recipes (38)

Required Modules

  • (none specified)

Other Information and Tasks