Merged the wxPy_newswig branch into the HEAD branch (main trunk)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@24541 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
		@@ -1,8 +1,396 @@
 | 
			
		||||
#---------------------------------------------------------------------------
 | 
			
		||||
# Name:        wxPython.lib.mixins.rubberband
 | 
			
		||||
# Purpose:     A mixin class for doing "RubberBand"-ing on a window.
 | 
			
		||||
#
 | 
			
		||||
# Author:      Robb Shecter and members of wxPython-users
 | 
			
		||||
#
 | 
			
		||||
# Created:     11-September-2002
 | 
			
		||||
# RCS-ID:      $Id$
 | 
			
		||||
# Copyright:   (c) 2002 by db-X Corporation
 | 
			
		||||
# Licence:     wxWindows license
 | 
			
		||||
#---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
"""Renamer stub: provides a way to drop the wx prefix from wxPython objects."""
 | 
			
		||||
"""
 | 
			
		||||
A mixin class for doing "RubberBand"-ing on a window.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from wx import _rename
 | 
			
		||||
from wxPython.lib.mixins import rubberband
 | 
			
		||||
_rename(globals(), rubberband.__dict__, modulename='lib.mixins.rubberband')
 | 
			
		||||
del rubberband
 | 
			
		||||
del _rename
 | 
			
		||||
from wxPython.wx import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Some miscellaneous mathematical and geometrical functions
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
def isNegative(aNumber):
 | 
			
		||||
    """
 | 
			
		||||
    x < 0:   1
 | 
			
		||||
    else:    0
 | 
			
		||||
    """
 | 
			
		||||
    return aNumber < 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def normalizeBox(box):
 | 
			
		||||
    """
 | 
			
		||||
    Convert any negative measurements in the current
 | 
			
		||||
    box to positive, and adjust the origin.
 | 
			
		||||
    """
 | 
			
		||||
    x, y, w, h = box
 | 
			
		||||
    if w < 0:
 | 
			
		||||
        x += (w+1)
 | 
			
		||||
        w *= -1
 | 
			
		||||
    if h < 0:
 | 
			
		||||
        y += (h+1)
 | 
			
		||||
        h *= -1
 | 
			
		||||
    return (x, y, w, h)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def boxToExtent(box):
 | 
			
		||||
    """
 | 
			
		||||
    Convert a box specification to an extent specification.
 | 
			
		||||
    I put this into a seperate function after I realized that
 | 
			
		||||
    I had been implementing it wrong in several places.
 | 
			
		||||
    """
 | 
			
		||||
    b = normalizeBox(box)
 | 
			
		||||
    return (b[0], b[1], b[0]+b[2]-1, b[1]+b[3]-1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pointInBox(x, y, box):
 | 
			
		||||
    """
 | 
			
		||||
    Return True if the given point is contained in the box.
 | 
			
		||||
    """
 | 
			
		||||
    e = boxToExtent(box)
 | 
			
		||||
    return x >= e[0] and x <= e[2] and y >= e[1] and y <= e[3]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pointOnBox(x, y, box, thickness=1):
 | 
			
		||||
    """
 | 
			
		||||
    Return True if the point is on the outside edge
 | 
			
		||||
    of the box.  The thickness defines how thick the
 | 
			
		||||
    edge should be.  This is necessary for HCI reasons:
 | 
			
		||||
    For example, it's normally very difficult for a user
 | 
			
		||||
    to manuever the mouse onto a one pixel border.
 | 
			
		||||
    """
 | 
			
		||||
    outerBox = box
 | 
			
		||||
    innerBox = (box[0]+thickness, box[1]+thickness, box[2]-(thickness*2), box[3]-(thickness*2))
 | 
			
		||||
    return pointInBox(x, y, outerBox) and not pointInBox(x, y, innerBox)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getCursorPosition(x, y, box, thickness=1):
 | 
			
		||||
    """
 | 
			
		||||
    Return a position number in the range 0 .. 7 to indicate
 | 
			
		||||
    where on the box border the point is.  The layout is:
 | 
			
		||||
 | 
			
		||||
              0    1    2
 | 
			
		||||
              7         3
 | 
			
		||||
              6    5    4
 | 
			
		||||
    """
 | 
			
		||||
    x0, y0, x1, y1 = boxToExtent(box)
 | 
			
		||||
    w, h  = box[2], box[3]
 | 
			
		||||
    delta = thickness - 1
 | 
			
		||||
    p     = None
 | 
			
		||||
 | 
			
		||||
    if pointInBox(x, y, (x0, y0, thickness, thickness)):
 | 
			
		||||
        p = 0
 | 
			
		||||
    elif pointInBox(x, y, (x1-delta, y0, thickness, thickness)):
 | 
			
		||||
        p = 2
 | 
			
		||||
    elif pointInBox(x, y, (x1-delta, y1-delta, thickness, thickness)):
 | 
			
		||||
        p = 4
 | 
			
		||||
    elif pointInBox(x, y, (x0, y1-delta, thickness, thickness)):
 | 
			
		||||
        p = 6
 | 
			
		||||
    elif pointInBox(x, y, (x0+thickness, y0, w-(thickness*2), thickness)):
 | 
			
		||||
        p = 1
 | 
			
		||||
    elif pointInBox(x, y, (x1-delta, y0+thickness, thickness, h-(thickness*2))):
 | 
			
		||||
        p = 3
 | 
			
		||||
    elif pointInBox(x, y, (x0+thickness, y1-delta, w-(thickness*2), thickness)):
 | 
			
		||||
        p = 5
 | 
			
		||||
    elif pointInBox(x, y, (x0, y0+thickness, thickness, h-(thickness*2))):
 | 
			
		||||
        p = 7
 | 
			
		||||
 | 
			
		||||
    return p
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RubberBand:
 | 
			
		||||
    """
 | 
			
		||||
    A stretchable border which is drawn on top of an
 | 
			
		||||
    image to define an area.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, drawingSurface, aspectRatio=None):
 | 
			
		||||
        self.__THICKNESS     = 5
 | 
			
		||||
        self.drawingSurface  = drawingSurface
 | 
			
		||||
        self.aspectRatio     = aspectRatio
 | 
			
		||||
        self.hasLetUp        = 0
 | 
			
		||||
        self.currentlyMoving = None
 | 
			
		||||
        self.currentBox      = None
 | 
			
		||||
        self.__enabled       = 1
 | 
			
		||||
        self.__currentCursor = None
 | 
			
		||||
        EVT_MOUSE_EVENTS(drawingSurface, self.__handleMouseEvents)
 | 
			
		||||
        EVT_PAINT(drawingSurface, self.__handleOnPaint)
 | 
			
		||||
 | 
			
		||||
    def __setEnabled(self, enabled):
 | 
			
		||||
        self.__enabled = enabled
 | 
			
		||||
 | 
			
		||||
    def __isEnabled(self):
 | 
			
		||||
        return self.__enabled
 | 
			
		||||
 | 
			
		||||
    def __handleOnPaint(self, event):
 | 
			
		||||
        #print 'paint'
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def __isMovingCursor(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return True if the current cursor is one used to
 | 
			
		||||
        mean moving the rubberband.
 | 
			
		||||
        """
 | 
			
		||||
        return self.__currentCursor == wxCURSOR_HAND
 | 
			
		||||
 | 
			
		||||
    def __isSizingCursor(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return True if the current cursor is one of the ones
 | 
			
		||||
        I may use to signify sizing.
 | 
			
		||||
        """
 | 
			
		||||
        sizingCursors = [wxCURSOR_SIZENESW,
 | 
			
		||||
                         wxCURSOR_SIZENS,
 | 
			
		||||
                         wxCURSOR_SIZENWSE,
 | 
			
		||||
                         wxCURSOR_SIZEWE,
 | 
			
		||||
                         wxCURSOR_SIZING,
 | 
			
		||||
                         wxCURSOR_CROSS]
 | 
			
		||||
        try:
 | 
			
		||||
            sizingCursors.index(self.__currentCursor)
 | 
			
		||||
            return 1
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __handleMouseEvents(self, event):
 | 
			
		||||
        """
 | 
			
		||||
        React according to the new event.  This is the main
 | 
			
		||||
        entry point into the class.  This method contains the
 | 
			
		||||
        logic for the class's behavior.
 | 
			
		||||
        """
 | 
			
		||||
        if not self.enabled:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        x, y = event.GetPosition()
 | 
			
		||||
 | 
			
		||||
        # First make sure we have started a box.
 | 
			
		||||
        if self.currentBox == None and not event.LeftDown():
 | 
			
		||||
            # No box started yet.  Set cursor to the initial kind.
 | 
			
		||||
            self.__setCursor(wxCURSOR_CROSS)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if event.LeftDown():
 | 
			
		||||
            if self.currentBox == None:
 | 
			
		||||
                # No RB Box, so start a new one.
 | 
			
		||||
                self.currentBox = (x, y, 0, 0)
 | 
			
		||||
                self.hasLetUp   = 0
 | 
			
		||||
            elif self.__isSizingCursor():
 | 
			
		||||
                # Starting a sizing operation.  Change the origin.
 | 
			
		||||
                position = getCursorPosition(x, y, self.currentBox, thickness=self.__THICKNESS)
 | 
			
		||||
                self.currentBox = self.__denormalizeBox(position, self.currentBox)
 | 
			
		||||
 | 
			
		||||
        elif event.Dragging() and event.LeftIsDown():
 | 
			
		||||
            # Use the cursor type to determine operation
 | 
			
		||||
            if self.__isMovingCursor():
 | 
			
		||||
                if self.currentlyMoving or pointInBox(x, y, self.currentBox):
 | 
			
		||||
                    if not self.currentlyMoving:
 | 
			
		||||
                        self.currentlyMoving = (x - self.currentBox[0], y - self.currentBox[1])
 | 
			
		||||
                    self.__moveTo(x - self.currentlyMoving[0], y - self.currentlyMoving[1])
 | 
			
		||||
            elif self.__isSizingCursor():
 | 
			
		||||
                self.__resizeBox(x, y)
 | 
			
		||||
 | 
			
		||||
        elif event.LeftUp():
 | 
			
		||||
            self.hasLetUp = 1
 | 
			
		||||
            self.currentlyMoving = None
 | 
			
		||||
            self.__normalizeBox()
 | 
			
		||||
 | 
			
		||||
        elif event.Moving() and not event.Dragging():
 | 
			
		||||
            # Simple mouse movement event
 | 
			
		||||
            self.__mouseMoved(x,y)
 | 
			
		||||
 | 
			
		||||
    def __denormalizeBox(self, position, box):
 | 
			
		||||
        x, y, w, h = box
 | 
			
		||||
        b = box
 | 
			
		||||
        if position == 2 or position == 3:
 | 
			
		||||
            b = (x, y + (h-1), w, h * -1)
 | 
			
		||||
        elif position == 0 or position == 1 or position == 7:
 | 
			
		||||
            b = (x + (w-1), y + (h-1), w * -1, h * -1)
 | 
			
		||||
        elif position == 6:
 | 
			
		||||
            b = (x + (w-1), y, w * -1, h)
 | 
			
		||||
        return b
 | 
			
		||||
 | 
			
		||||
    def __resizeBox(self, x, y):
 | 
			
		||||
        """
 | 
			
		||||
        Resize and repaint the box based on the given mouse
 | 
			
		||||
        coordinates.
 | 
			
		||||
        """
 | 
			
		||||
        # Implement the correct behavior for dragging a side
 | 
			
		||||
        # of the box:  Only change one dimension.
 | 
			
		||||
        if not self.aspectRatio:
 | 
			
		||||
            if self.__currentCursor == wxCURSOR_SIZENS:
 | 
			
		||||
                x = None
 | 
			
		||||
            elif self.__currentCursor == wxCURSOR_SIZEWE:
 | 
			
		||||
                y = None
 | 
			
		||||
 | 
			
		||||
        x0,y0,w0,h0 = self.currentBox
 | 
			
		||||
        currentExtent = boxToExtent(self.currentBox)
 | 
			
		||||
        if x == None:
 | 
			
		||||
            if w0 < 1:
 | 
			
		||||
                w0 += 1
 | 
			
		||||
            else:
 | 
			
		||||
                w0 -= 1
 | 
			
		||||
            x = x0 + w0
 | 
			
		||||
        if y == None:
 | 
			
		||||
            if h0 < 1:
 | 
			
		||||
                h0 += 1
 | 
			
		||||
            else:
 | 
			
		||||
                h0 -= 1
 | 
			
		||||
            y = y0 + h0
 | 
			
		||||
        x1,y1 = x, y
 | 
			
		||||
        w, h = abs(x1-x0)+1, abs(y1-y0)+1
 | 
			
		||||
        if self.aspectRatio:
 | 
			
		||||
            w = max(w, int(h * self.aspectRatio))
 | 
			
		||||
            h = int(w / self.aspectRatio)
 | 
			
		||||
        w *= [1,-1][isNegative(x1-x0)]
 | 
			
		||||
        h *= [1,-1][isNegative(y1-y0)]
 | 
			
		||||
        newbox = (x0, y0, w, h)
 | 
			
		||||
        self.__drawAndErase(boxToDraw=normalizeBox(newbox), boxToErase=normalizeBox(self.currentBox))
 | 
			
		||||
        self.currentBox = (x0, y0, w, h)
 | 
			
		||||
 | 
			
		||||
    def __normalizeBox(self):
 | 
			
		||||
        """
 | 
			
		||||
        Convert any negative measurements in the current
 | 
			
		||||
        box to positive, and adjust the origin.
 | 
			
		||||
        """
 | 
			
		||||
        self.currentBox = normalizeBox(self.currentBox)
 | 
			
		||||
 | 
			
		||||
    def __mouseMoved(self, x, y):
 | 
			
		||||
        """
 | 
			
		||||
        Called when the mouse moved without any buttons pressed
 | 
			
		||||
        or dragging being done.
 | 
			
		||||
        """
 | 
			
		||||
        # Are we on the bounding box?
 | 
			
		||||
        if pointOnBox(x, y, self.currentBox, thickness=self.__THICKNESS):
 | 
			
		||||
            position = getCursorPosition(x, y, self.currentBox, thickness=self.__THICKNESS)
 | 
			
		||||
            cursor   = [
 | 
			
		||||
                wxCURSOR_SIZENWSE,
 | 
			
		||||
                wxCURSOR_SIZENS,
 | 
			
		||||
                wxCURSOR_SIZENESW,
 | 
			
		||||
                wxCURSOR_SIZEWE,
 | 
			
		||||
                wxCURSOR_SIZENWSE,
 | 
			
		||||
                wxCURSOR_SIZENS,
 | 
			
		||||
                wxCURSOR_SIZENESW,
 | 
			
		||||
                wxCURSOR_SIZEWE
 | 
			
		||||
                ] [position]
 | 
			
		||||
            self.__setCursor(cursor)
 | 
			
		||||
        elif pointInBox(x, y, self.currentBox):
 | 
			
		||||
            self.__setCursor(wxCURSOR_HAND)
 | 
			
		||||
        else:
 | 
			
		||||
            self.__setCursor()
 | 
			
		||||
 | 
			
		||||
    def __setCursor(self, id=None):
 | 
			
		||||
        """
 | 
			
		||||
        Set the mouse cursor to the given id.
 | 
			
		||||
        """
 | 
			
		||||
        if self.__currentCursor != id:  # Avoid redundant calls
 | 
			
		||||
            if id:
 | 
			
		||||
                self.drawingSurface.SetCursor(wxStockCursor(id))
 | 
			
		||||
            else:
 | 
			
		||||
                self.drawingSurface.SetCursor(wxNullCursor)
 | 
			
		||||
            self.__currentCursor = id
 | 
			
		||||
 | 
			
		||||
    def __moveCenterTo(self, x, y):
 | 
			
		||||
        """
 | 
			
		||||
        Move the rubber band so that its center is at (x,y).
 | 
			
		||||
        """
 | 
			
		||||
        x0, y0, w, h = self.currentBox
 | 
			
		||||
        x2, y2 = x - (w/2), y - (h/2)
 | 
			
		||||
        self.__moveTo(x2, y2)
 | 
			
		||||
 | 
			
		||||
    def __moveTo(self, x, y):
 | 
			
		||||
        """
 | 
			
		||||
        Move the rubber band so that its origin is at (x,y).
 | 
			
		||||
        """
 | 
			
		||||
        newbox = (x, y, self.currentBox[2], self.currentBox[3])
 | 
			
		||||
        self.__drawAndErase(boxToDraw=newbox, boxToErase=self.currentBox)
 | 
			
		||||
        self.currentBox = newbox
 | 
			
		||||
 | 
			
		||||
    def __drawAndErase(self, boxToDraw, boxToErase=None):
 | 
			
		||||
        """
 | 
			
		||||
        Draw one box shape and possibly erase another.
 | 
			
		||||
        """
 | 
			
		||||
        dc = wxClientDC(self.drawingSurface)
 | 
			
		||||
        dc.BeginDrawing()
 | 
			
		||||
        dc.SetPen(wxPen(wxWHITE, 1, wxDOT))
 | 
			
		||||
        dc.SetBrush(wxTRANSPARENT_BRUSH)
 | 
			
		||||
        dc.SetLogicalFunction(wxXOR)
 | 
			
		||||
        if boxToErase:
 | 
			
		||||
            dc.DrawRectangle(*boxToErase)
 | 
			
		||||
        dc.DrawRectangle(*boxToDraw)
 | 
			
		||||
        dc.EndDrawing()
 | 
			
		||||
 | 
			
		||||
    def __dumpMouseEvent(self, event):
 | 
			
		||||
        print 'Moving:          ',event.Moving()
 | 
			
		||||
        print 'Dragging:        ',event.Dragging()
 | 
			
		||||
        print 'LeftDown:        ',event.LeftDown()
 | 
			
		||||
        print 'LeftisDown:      ',event.LeftIsDown()
 | 
			
		||||
        print 'LeftUp:          ',event.LeftUp()
 | 
			
		||||
        print 'Position:        ',event.GetPosition()
 | 
			
		||||
        print 'x,y:             ',event.GetX(),event.GetY()
 | 
			
		||||
        print
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # The public API:
 | 
			
		||||
    #
 | 
			
		||||
 | 
			
		||||
    def reset(self, aspectRatio=None):
 | 
			
		||||
        """
 | 
			
		||||
        Clear the existing rubberband
 | 
			
		||||
        """
 | 
			
		||||
        self.currentBox   = None
 | 
			
		||||
        self.aspectRatio  = aspectRatio
 | 
			
		||||
        self.drawingSurface.Refresh()
 | 
			
		||||
 | 
			
		||||
    def getCurrentExtent(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return (x0, y0, x1, y1) or None if
 | 
			
		||||
        no drawing has yet been done.
 | 
			
		||||
        """
 | 
			
		||||
        if not self.currentBox:
 | 
			
		||||
            extent = None
 | 
			
		||||
        else:
 | 
			
		||||
            extent = boxToExtent(self.currentBox)
 | 
			
		||||
        return extent
 | 
			
		||||
 | 
			
		||||
    enabled = property(__isEnabled, __setEnabled, None, 'True if I am responding to mouse events')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    app   = wxPySimpleApp()
 | 
			
		||||
    frame = wxFrame(None, -1, title='RubberBand Test', size=(300,300))
 | 
			
		||||
 | 
			
		||||
    # Add a panel that the rubberband will work on.
 | 
			
		||||
    panel = wxPanel(frame, -1)
 | 
			
		||||
    panel.SetBackgroundColour(wxBLUE)
 | 
			
		||||
 | 
			
		||||
    # Create the rubberband
 | 
			
		||||
    frame.rubberBand = RubberBand(drawingSurface=panel)
 | 
			
		||||
    frame.rubberBand.reset(aspectRatio=0.5)
 | 
			
		||||
 | 
			
		||||
    # Add a button that creates a new rubberband
 | 
			
		||||
    def __newRubberBand(event):
 | 
			
		||||
        frame.rubberBand.reset()
 | 
			
		||||
    button = wxButton(frame, 100, 'Reset Rubberband')
 | 
			
		||||
    EVT_BUTTON(frame, 100, __newRubberBand)
 | 
			
		||||
 | 
			
		||||
    # Layout the frame
 | 
			
		||||
    sizer = wxBoxSizer(wxVERTICAL)
 | 
			
		||||
    sizer.Add(panel,  1, wxEXPAND | wxALL, 5)
 | 
			
		||||
    sizer.Add(button, 0, wxALIGN_CENTER | wxALL, 5)
 | 
			
		||||
    frame.SetAutoLayout(1)
 | 
			
		||||
    frame.SetSizer(sizer)
 | 
			
		||||
    frame.Show(1)
 | 
			
		||||
    app.MainLoop()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user