]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/mixins/rubberband.py
f71dbf50a91b8089dc56fd4753ba8fc92fb9c234
2 A mixin class for doing "RubberBand"-ing on a window.
4 by "Robb Shecter" <rs@onsitetech.com>
10 from wxPython
.wx
import *
14 # Some miscellaneous mathematical and geometrical functions
17 def isNegative(aNumber
):
25 def normalizeBox(box
):
27 Convert any negative measurements in the current
28 box to positive, and adjust the origin.
42 Convert a box specification to an extent specification.
43 I put this into a seperate function after I realized that
44 I had been implementing it wrong in several places.
47 return (b
[0], b
[1], b
[0]+b
[2]-1, b
[1]+b
[3]-1)
50 def pointInBox(x
, y
, box
):
52 Return True if the given point is contained in the box.
55 return x
>= e
[0] and x
<= e
[2] and y
>= e
[1] and y
<= e
[3]
58 def pointOnBox(x
, y
, box
, thickness
=1):
60 Return True if the point is on the outside edge
61 of the box. The thickness defines how thick the
62 edge should be. This is necessary for HCI reasons:
63 For example, it's normally very difficult for a user
64 to manuever the mouse onto a one pixel border.
67 innerBox
= (box
[0]+thickness
, box
[1]+thickness
, box
[2]-(thickness
*2), box
[3]-(thickness
*2))
68 return pointInBox(x
, y
, outerBox
) and not pointInBox(x
, y
, innerBox
)
71 def getCursorPosition(x
, y
, box
, thickness
=1):
73 Return a position number in the range 0 .. 7 to indicate
74 where on the box border the point is. The layout is:
80 x0
, y0
, x1
, y1
= boxToExtent(box
)
85 if pointInBox(x
, y
, (x0
, y0
, thickness
, thickness
)):
87 elif pointInBox(x
, y
, (x1
-delta
, y0
, thickness
, thickness
)):
89 elif pointInBox(x
, y
, (x1
-delta
, y1
-delta
, thickness
, thickness
)):
91 elif pointInBox(x
, y
, (x0
, y1
-delta
, thickness
, thickness
)):
93 elif pointInBox(x
, y
, (x0
+thickness
, y0
, w
-(thickness
*2), thickness
)):
95 elif pointInBox(x
, y
, (x1
-delta
, y0
+thickness
, thickness
, h
-(thickness
*2))):
97 elif pointInBox(x
, y
, (x0
+thickness
, y1
-delta
, w
-(thickness
*2), thickness
)):
99 elif pointInBox(x
, y
, (x0
, y0
+thickness
, thickness
, h
-(thickness
*2))):
109 A stretchable border which is drawn on top of an
110 image to define an area.
112 def __init__(self
, drawingSurface
, aspectRatio
=None):
114 self
.drawingSurface
= drawingSurface
115 self
.aspectRatio
= aspectRatio
117 self
.currentlyMoving
= None
118 self
.currentBox
= None
120 self
.__currentCursor
= None
121 EVT_MOUSE_EVENTS(drawingSurface
, self
.__handleMouseEvents
)
122 EVT_PAINT(drawingSurface
, self
.__handleOnPaint
)
124 def __setEnabled(self
, enabled
):
125 self
.__enabled
= enabled
127 def __isEnabled(self
):
128 return self
.__enabled
130 def __handleOnPaint(self
, event
):
134 def __isMovingCursor(self
):
136 Return True if the current cursor is one used to
137 mean moving the rubberband.
139 return self
.__currentCursor
== wxCURSOR_HAND
141 def __isSizingCursor(self
):
143 Return True if the current cursor is one of the ones
144 I may use to signify sizing.
146 sizingCursors
= [wxCURSOR_SIZENESW
,
153 sizingCursors
.index(self
.__currentCursor
)
159 def __handleMouseEvents(self
, event
):
161 React according to the new event. This is the main
162 entry point into the class. This method contains the
163 logic for the class's behavior.
168 x
, y
= event
.GetPosition()
170 # First make sure we have started a box.
171 if self
.currentBox
== None and not event
.LeftDown():
172 # No box started yet. Set cursor to the initial kind.
173 self
.__setCursor
(wxCURSOR_CROSS
)
177 if self
.currentBox
== None:
178 # No RB Box, so start a new one.
179 self
.currentBox
= (x
, y
, 0, 0)
181 elif self
.__isSizingCursor
():
182 # Starting a sizing operation. Change the origin.
183 position
= getCursorPosition(x
, y
, self
.currentBox
, thickness
=self
.__THICKNESS
)
184 self
.currentBox
= self
.__denormalizeBox
(position
, self
.currentBox
)
186 elif event
.Dragging() and event
.LeftIsDown():
187 # Use the cursor type to determine operation
188 if self
.__isMovingCursor
():
189 if self
.currentlyMoving
or pointInBox(x
, y
, self
.currentBox
):
190 if not self
.currentlyMoving
:
191 self
.currentlyMoving
= (x
- self
.currentBox
[0], y
- self
.currentBox
[1])
192 self
.__moveTo
(x
- self
.currentlyMoving
[0], y
- self
.currentlyMoving
[1])
193 elif self
.__isSizingCursor
():
194 self
.__resizeBox
(x
, y
)
198 self
.currentlyMoving
= None
199 self
.__normalizeBox
()
201 elif event
.Moving() and not event
.Dragging():
202 # Simple mouse movement event
203 self
.__mouseMoved
(x
,y
)
205 def __denormalizeBox(self
, position
, box
):
208 if position
== 2 or position
== 3:
209 b
= (x
, y
+ (h
-1), w
, h
* -1)
210 elif position
== 0 or position
== 1 or position
== 7:
211 b
= (x
+ (w
-1), y
+ (h
-1), w
* -1, h
* -1)
213 b
= (x
+ (w
-1), y
, w
* -1, h
)
216 def __resizeBox(self
, x
, y
):
218 Resize and repaint the box based on the given mouse
221 # Implement the correct behavior for dragging a side
222 # of the box: Only change one dimension.
223 if not self
.aspectRatio
:
224 if self
.__currentCursor
== wxCURSOR_SIZENS
:
226 elif self
.__currentCursor
== wxCURSOR_SIZEWE
:
229 x0
,y0
,w0
,h0
= self
.currentBox
230 currentExtent
= boxToExtent(self
.currentBox
)
244 w
, h
= abs(x1
-x0
)+1, abs(y1
-y0
)+1
246 w
= max(w
, int(h
* self
.aspectRatio
))
247 h
= int(w
/ self
.aspectRatio
)
248 w
*= [1,-1][isNegative(x1
-x0
)]
249 h
*= [1,-1][isNegative(y1
-y0
)]
250 newbox
= (x0
, y0
, w
, h
)
251 self
.__drawAndErase
(boxToDraw
=normalizeBox(newbox
), boxToErase
=normalizeBox(self
.currentBox
))
252 self
.currentBox
= (x0
, y0
, w
, h
)
254 def __normalizeBox(self
):
256 Convert any negative measurements in the current
257 box to positive, and adjust the origin.
259 self
.currentBox
= normalizeBox(self
.currentBox
)
261 def __mouseMoved(self
, x
, y
):
263 Called when the mouse moved without any buttons pressed
264 or dragging being done.
266 # Are we on the bounding box?
267 if pointOnBox(x
, y
, self
.currentBox
, thickness
=self
.__THICKNESS
):
268 position
= getCursorPosition(x
, y
, self
.currentBox
, thickness
=self
.__THICKNESS
)
279 self
.__setCursor
(cursor
)
280 elif pointInBox(x
, y
, self
.currentBox
):
281 self
.__setCursor
(wxCURSOR_HAND
)
285 def __setCursor(self
, id=None):
287 Set the mouse cursor to the given id.
289 if self
.__currentCursor
!= id: # Avoid redundant calls
291 self
.drawingSurface
.SetCursor(wxStockCursor(id))
293 self
.drawingSurface
.SetCursor(wxNullCursor
)
294 self
.__currentCursor
= id
296 def __moveCenterTo(self
, x
, y
):
298 Move the rubber band so that its center is at (x,y).
300 x0
, y0
, w
, h
= self
.currentBox
301 x2
, y2
= x
- (w
/2), y
- (h
/2)
302 self
.__moveTo
(x2
, y2
)
304 def __moveTo(self
, x
, y
):
306 Move the rubber band so that its origin is at (x,y).
308 newbox
= (x
, y
, self
.currentBox
[2], self
.currentBox
[3])
309 self
.__drawAndErase
(boxToDraw
=newbox
, boxToErase
=self
.currentBox
)
310 self
.currentBox
= newbox
312 def __drawAndErase(self
, boxToDraw
, boxToErase
=None):
314 Draw one box shape and possibly erase another.
316 dc
= wxClientDC(self
.drawingSurface
)
318 dc
.SetPen(wxPen(wxWHITE
, 1, wxDOT
))
319 dc
.SetBrush(wxTRANSPARENT_BRUSH
)
320 dc
.SetLogicalFunction(wxXOR
)
322 dc
.DrawRectangle(*boxToErase
)
323 dc
.DrawRectangle(*boxToDraw
)
326 def __dumpMouseEvent(self
, event
):
327 print 'Moving: ',event
.Moving()
328 print 'Dragging: ',event
.Dragging()
329 print 'LeftDown: ',event
.LeftDown()
330 print 'LeftisDown: ',event
.LeftIsDown()
331 print 'LeftUp: ',event
.LeftUp()
332 print 'Position: ',event
.GetPosition()
333 print 'x,y: ',event
.GetX(),event
.GetY()
341 def reset(self
, aspectRatio
=None):
343 Clear the existing rubberband
345 self
.currentBox
= None
346 self
.aspectRatio
= aspectRatio
347 self
.drawingSurface
.Refresh()
349 def getCurrentExtent(self
):
351 Return (x0, y0, x1, y1) or None if
352 no drawing has yet been done.
354 if not self
.currentBox
:
357 extent
= boxToExtent(self
.currentBox
)
360 enabled
= property(__isEnabled
, __setEnabled
, None, 'True if I am responding to mouse events')
364 if __name__
== '__main__':
365 app
= wxPySimpleApp()
366 frame
= wxFrame(None, -1, title
='RubberBand Test', size
=(300,300))
368 # Add a panel that the rubberband will work on.
369 panel
= wxPanel(frame
, -1)
370 panel
.SetBackgroundColour(wxBLUE
)
372 # Create the rubberband
373 frame
.rubberBand
= RubberBand(drawingSurface
=panel
)
374 frame
.rubberBand
.reset(aspectRatio
=0.5)
376 # Add a button that creates a new rubberband
377 def __newRubberBand(event
):
378 frame
.rubberBand
.reset()
379 button
= wxButton(frame
, 100, 'Reset Rubberband')
380 EVT_BUTTON(frame
, 100, __newRubberBand
)
383 sizer
= wxBoxSizer(wxVERTICAL
)
384 sizer
.Add(panel
, 1, wxEXPAND | wxALL
, 5)
385 sizer
.Add(button
, 0, wxALIGN_CENTER | wxALL
, 5)
386 frame
.SetAutoLayout(1)
387 frame
.SetSizer(sizer
)