]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/mixins/rubberband.py
1 #---------------------------------------------------------------------------
2 # Name: wxPython.lib.mixins.rubberband
3 # Purpose: A mixin class for doing "RubberBand"-ing on a window.
5 # Author: Robb Shecter and members of wxPython-users
7 # Created: 11-September-2002
9 # Copyright: (c) 2002 by db-X Corporation
10 # Licence: wxWindows license
11 #---------------------------------------------------------------------------
14 A mixin class for doing "RubberBand"-ing on a window.
17 from wxPython
.wx
import *
21 # Some miscellaneous mathematical and geometrical functions
24 def isNegative(aNumber
):
32 def normalizeBox(box
):
34 Convert any negative measurements in the current
35 box to positive, and adjust the origin.
49 Convert a box specification to an extent specification.
50 I put this into a seperate function after I realized that
51 I had been implementing it wrong in several places.
54 return (b
[0], b
[1], b
[0]+b
[2]-1, b
[1]+b
[3]-1)
57 def pointInBox(x
, y
, box
):
59 Return True if the given point is contained in the box.
62 return x
>= e
[0] and x
<= e
[2] and y
>= e
[1] and y
<= e
[3]
65 def pointOnBox(x
, y
, box
, thickness
=1):
67 Return True if the point is on the outside edge
68 of the box. The thickness defines how thick the
69 edge should be. This is necessary for HCI reasons:
70 For example, it's normally very difficult for a user
71 to manuever the mouse onto a one pixel border.
74 innerBox
= (box
[0]+thickness
, box
[1]+thickness
, box
[2]-(thickness
*2), box
[3]-(thickness
*2))
75 return pointInBox(x
, y
, outerBox
) and not pointInBox(x
, y
, innerBox
)
78 def getCursorPosition(x
, y
, box
, thickness
=1):
80 Return a position number in the range 0 .. 7 to indicate
81 where on the box border the point is. The layout is:
87 x0
, y0
, x1
, y1
= boxToExtent(box
)
92 if pointInBox(x
, y
, (x0
, y0
, thickness
, thickness
)):
94 elif pointInBox(x
, y
, (x1
-delta
, y0
, thickness
, thickness
)):
96 elif pointInBox(x
, y
, (x1
-delta
, y1
-delta
, thickness
, thickness
)):
98 elif pointInBox(x
, y
, (x0
, y1
-delta
, thickness
, thickness
)):
100 elif pointInBox(x
, y
, (x0
+thickness
, y0
, w
-(thickness
*2), thickness
)):
102 elif pointInBox(x
, y
, (x1
-delta
, y0
+thickness
, thickness
, h
-(thickness
*2))):
104 elif pointInBox(x
, y
, (x0
+thickness
, y1
-delta
, w
-(thickness
*2), thickness
)):
106 elif pointInBox(x
, y
, (x0
, y0
+thickness
, thickness
, h
-(thickness
*2))):
116 A stretchable border which is drawn on top of an
117 image to define an area.
119 def __init__(self
, drawingSurface
, aspectRatio
=None):
121 self
.drawingSurface
= drawingSurface
122 self
.aspectRatio
= aspectRatio
124 self
.currentlyMoving
= None
125 self
.currentBox
= None
127 self
.__currentCursor
= None
128 EVT_MOUSE_EVENTS(drawingSurface
, self
.__handleMouseEvents
)
129 EVT_PAINT(drawingSurface
, self
.__handleOnPaint
)
131 def __setEnabled(self
, enabled
):
132 self
.__enabled
= enabled
134 def __isEnabled(self
):
135 return self
.__enabled
137 def __handleOnPaint(self
, event
):
141 def __isMovingCursor(self
):
143 Return True if the current cursor is one used to
144 mean moving the rubberband.
146 return self
.__currentCursor
== wxCURSOR_HAND
148 def __isSizingCursor(self
):
150 Return True if the current cursor is one of the ones
151 I may use to signify sizing.
153 sizingCursors
= [wxCURSOR_SIZENESW
,
160 sizingCursors
.index(self
.__currentCursor
)
166 def __handleMouseEvents(self
, event
):
168 React according to the new event. This is the main
169 entry point into the class. This method contains the
170 logic for the class's behavior.
175 x
, y
= event
.GetPosition()
177 # First make sure we have started a box.
178 if self
.currentBox
== None and not event
.LeftDown():
179 # No box started yet. Set cursor to the initial kind.
180 self
.__setCursor
(wxCURSOR_CROSS
)
184 if self
.currentBox
== None:
185 # No RB Box, so start a new one.
186 self
.currentBox
= (x
, y
, 0, 0)
188 elif self
.__isSizingCursor
():
189 # Starting a sizing operation. Change the origin.
190 position
= getCursorPosition(x
, y
, self
.currentBox
, thickness
=self
.__THICKNESS
)
191 self
.currentBox
= self
.__denormalizeBox
(position
, self
.currentBox
)
193 elif event
.Dragging() and event
.LeftIsDown():
194 # Use the cursor type to determine operation
195 if self
.__isMovingCursor
():
196 if self
.currentlyMoving
or pointInBox(x
, y
, self
.currentBox
):
197 if not self
.currentlyMoving
:
198 self
.currentlyMoving
= (x
- self
.currentBox
[0], y
- self
.currentBox
[1])
199 self
.__moveTo
(x
- self
.currentlyMoving
[0], y
- self
.currentlyMoving
[1])
200 elif self
.__isSizingCursor
():
201 self
.__resizeBox
(x
, y
)
205 self
.currentlyMoving
= None
206 self
.__normalizeBox
()
208 elif event
.Moving() and not event
.Dragging():
209 # Simple mouse movement event
210 self
.__mouseMoved
(x
,y
)
212 def __denormalizeBox(self
, position
, box
):
215 if position
== 2 or position
== 3:
216 b
= (x
, y
+ (h
-1), w
, h
* -1)
217 elif position
== 0 or position
== 1 or position
== 7:
218 b
= (x
+ (w
-1), y
+ (h
-1), w
* -1, h
* -1)
220 b
= (x
+ (w
-1), y
, w
* -1, h
)
223 def __resizeBox(self
, x
, y
):
225 Resize and repaint the box based on the given mouse
228 # Implement the correct behavior for dragging a side
229 # of the box: Only change one dimension.
230 if not self
.aspectRatio
:
231 if self
.__currentCursor
== wxCURSOR_SIZENS
:
233 elif self
.__currentCursor
== wxCURSOR_SIZEWE
:
236 x0
,y0
,w0
,h0
= self
.currentBox
237 currentExtent
= boxToExtent(self
.currentBox
)
251 w
, h
= abs(x1
-x0
)+1, abs(y1
-y0
)+1
253 w
= max(w
, int(h
* self
.aspectRatio
))
254 h
= int(w
/ self
.aspectRatio
)
255 w
*= [1,-1][isNegative(x1
-x0
)]
256 h
*= [1,-1][isNegative(y1
-y0
)]
257 newbox
= (x0
, y0
, w
, h
)
258 self
.__drawAndErase
(boxToDraw
=normalizeBox(newbox
), boxToErase
=normalizeBox(self
.currentBox
))
259 self
.currentBox
= (x0
, y0
, w
, h
)
261 def __normalizeBox(self
):
263 Convert any negative measurements in the current
264 box to positive, and adjust the origin.
266 self
.currentBox
= normalizeBox(self
.currentBox
)
268 def __mouseMoved(self
, x
, y
):
270 Called when the mouse moved without any buttons pressed
271 or dragging being done.
273 # Are we on the bounding box?
274 if pointOnBox(x
, y
, self
.currentBox
, thickness
=self
.__THICKNESS
):
275 position
= getCursorPosition(x
, y
, self
.currentBox
, thickness
=self
.__THICKNESS
)
286 self
.__setCursor
(cursor
)
287 elif pointInBox(x
, y
, self
.currentBox
):
288 self
.__setCursor
(wxCURSOR_HAND
)
292 def __setCursor(self
, id=None):
294 Set the mouse cursor to the given id.
296 if self
.__currentCursor
!= id: # Avoid redundant calls
298 self
.drawingSurface
.SetCursor(wxStockCursor(id))
300 self
.drawingSurface
.SetCursor(wxNullCursor
)
301 self
.__currentCursor
= id
303 def __moveCenterTo(self
, x
, y
):
305 Move the rubber band so that its center is at (x,y).
307 x0
, y0
, w
, h
= self
.currentBox
308 x2
, y2
= x
- (w
/2), y
- (h
/2)
309 self
.__moveTo
(x2
, y2
)
311 def __moveTo(self
, x
, y
):
313 Move the rubber band so that its origin is at (x,y).
315 newbox
= (x
, y
, self
.currentBox
[2], self
.currentBox
[3])
316 self
.__drawAndErase
(boxToDraw
=newbox
, boxToErase
=self
.currentBox
)
317 self
.currentBox
= newbox
319 def __drawAndErase(self
, boxToDraw
, boxToErase
=None):
321 Draw one box shape and possibly erase another.
323 dc
= wxClientDC(self
.drawingSurface
)
325 dc
.SetPen(wxPen(wxWHITE
, 1, wxDOT
))
326 dc
.SetBrush(wxTRANSPARENT_BRUSH
)
327 dc
.SetLogicalFunction(wxXOR
)
329 dc
.DrawRectangle(*boxToErase
)
330 dc
.DrawRectangle(*boxToDraw
)
333 def __dumpMouseEvent(self
, event
):
334 print 'Moving: ',event
.Moving()
335 print 'Dragging: ',event
.Dragging()
336 print 'LeftDown: ',event
.LeftDown()
337 print 'LeftisDown: ',event
.LeftIsDown()
338 print 'LeftUp: ',event
.LeftUp()
339 print 'Position: ',event
.GetPosition()
340 print 'x,y: ',event
.GetX(),event
.GetY()
348 def reset(self
, aspectRatio
=None):
350 Clear the existing rubberband
352 self
.currentBox
= None
353 self
.aspectRatio
= aspectRatio
354 self
.drawingSurface
.Refresh()
356 def getCurrentExtent(self
):
358 Return (x0, y0, x1, y1) or None if
359 no drawing has yet been done.
361 if not self
.currentBox
:
364 extent
= boxToExtent(self
.currentBox
)
367 enabled
= property(__isEnabled
, __setEnabled
, None, 'True if I am responding to mouse events')
371 if __name__
== '__main__':
372 app
= wxPySimpleApp()
373 frame
= wxFrame(None, -1, title
='RubberBand Test', size
=(300,300))
375 # Add a panel that the rubberband will work on.
376 panel
= wxPanel(frame
, -1)
377 panel
.SetBackgroundColour(wxBLUE
)
379 # Create the rubberband
380 frame
.rubberBand
= RubberBand(drawingSurface
=panel
)
381 frame
.rubberBand
.reset(aspectRatio
=0.5)
383 # Add a button that creates a new rubberband
384 def __newRubberBand(event
):
385 frame
.rubberBand
.reset()
386 button
= wxButton(frame
, 100, 'Reset Rubberband')
387 EVT_BUTTON(frame
, 100, __newRubberBand
)
390 sizer
= wxBoxSizer(wxVERTICAL
)
391 sizer
.Add(panel
, 1, wxEXPAND | wxALL
, 5)
392 sizer
.Add(button
, 0, wxALIGN_CENTER | wxALL
, 5)
393 frame
.SetAutoLayout(1)
394 frame
.SetSizer(sizer
)