@@ -795,3 +795,191 @@ def onmove(self, event):
795795 self .rect .set_width (maxx - minx )
796796 self .update ()
797797 return False
798+
799+
800+ class RectangleSelector :
801+ """
802+ Select a min/max range of the x axes for a matplotlib Axes
803+
804+ Example usage:
805+
806+ ax = subplot(111)
807+ ax.plot(x,y)
808+
809+ def onselect(eclick, erelease):
810+ 'eclick and erelease are matplotlib events at press and release'
811+ print 'startposition : (%f,%f)'%(eclick.xdata, eclick.ydata)
812+ print 'endposition : (%f,%f)'%(erelease.xdata, erelease.ydata)
813+ print 'used button : ', eclick.button
814+
815+ span = Selector(ax, onselect,drawtype='box')
816+ show()
817+
818+ """
819+ def __init__ (self , ax , onselect , drawtype = 'box' ,
820+ minspanx = None , minspany = None , useblit = False ,
821+ lineprops = None , rectprops = None ):
822+
823+ """
824+ Create a selector in ax. When a selection is made, clear
825+ the span and call onselect with
826+
827+ onselect(pos_1, pos_2)
828+
829+ and clear the drawn box/line. There pos_i are arrays of length 2
830+ containing the x- and y-coordinate.
831+
832+ If minspanx is not None then events smaller than minspanx
833+ in x direction are ignored(it's the same for y).
834+
835+ The rect is drawn with rectprops; default
836+ rectprops = dict(facecolor='red', edgecolor = 'black',
837+ alpha=0.5, fill=False)
838+
839+ The line is drawn with lineprops; default
840+ lineprops = dict(color='black', linestyle='-',
841+ linewidth = 2, alpha=0.5)
842+
843+ Use type if you want the mouse to draw a line, a box or nothing
844+ between click and actual position ny setting
845+ drawtype = 'line', drawtype='box' or drawtype = 'none'.
846+
847+
848+ """
849+ self .ax = ax
850+ self .visible = True
851+ self .canvas = ax .figure .canvas
852+ self .canvas .mpl_connect ('motion_notify_event' , self .onmove )
853+ self .canvas .mpl_connect ('button_press_event' , self .press )
854+ self .canvas .mpl_connect ('button_release_event' , self .release )
855+ self .canvas .mpl_connect ('draw_event' , self .update_background )
856+
857+ self .to_draw = None
858+ self .background = None
859+
860+ if drawtype == 'none' :
861+ drawtype = 'line' # draw a line but make it
862+ self .visible = False # invisible
863+
864+ if drawtype == 'box' :
865+ if rectprops is None :
866+ rectprops = dict (facecolor = 'white' , edgecolor = 'black' ,
867+ alpha = 0.5 , fill = False )
868+ self .rectprops = rectprops
869+ self .to_draw = Rectangle ((0 ,0 ), 0 , 1 ,visible = False ,** self .rectprops )
870+ self .ax .add_patch (self .to_draw )
871+ if drawtype == 'line' :
872+ if lineprops is None :
873+ lineprops = dict (color = 'black' , linestyle = '-' ,
874+ linewidth = 2 , alpha = 0.5 )
875+ self .lineprops = lineprops
876+ self .to_draw = Line2D ([0 ,0 ],[0 ,0 ],visible = False ,** self .lineprops )
877+ self .ax .add_line (self .to_draw )
878+
879+ self .onselect = onselect
880+ self .useblit = useblit
881+ self .minspanx = minspanx
882+ self .minspany = minspany
883+ self .drawtype = drawtype
884+ # will save the data (position at mouseclick)
885+ self .eventpress = None
886+ # will save the data (pos. at mouserelease)
887+ self .eventrelease = None
888+
889+ def update_background (self , event ):
890+ 'force an update of the background'
891+ if self .useblit :
892+ self .background = self .canvas .copy_from_bbox (self .ax .bbox )
893+
894+
895+ def ignore (self , event ):
896+ 'return True if event should be ignored'
897+ # If no button was pressed yet ignore the event if it was out
898+ # of the axes
899+ if self .eventpress == None :
900+ return event .inaxes != self .ax
901+
902+ # If a button was pressed, check if the release-button is the
903+ # same.
904+ return (event .inaxes != self .ax or
905+ event .button != self .eventpress .button )
906+
907+ def press (self , event ):
908+ 'on button press event'
909+ # Is the correct button pressed within the correct axes?
910+ if self .ignore (event ): return
911+
912+
913+ # make the drawed box/line visible get the click-coordinates,
914+ # button, ...
915+ self .to_draw .set_visible (self .visible )
916+ self .eventpress = event
917+ return False
918+
919+
920+ def release (self , event ):
921+ 'on button release event'
922+ if self .eventpress is None or self .ignore (event ): return
923+ # make the box/line invisible again
924+ self .to_draw .set_visible (False )
925+ self .canvas .draw ()
926+ # release coordinates, button, ...
927+ self .eventrelease = event
928+ xmin , ymin = self .eventpress .xdata , self .eventpress .ydata
929+ xmax , ymax = self .eventrelease .xdata , self .eventrelease .ydata
930+ # calculate dimensions of box or line get values in the right
931+ # order
932+ if xmin > xmax : xmin , xmax = xmax , xmin
933+ if ymin > ymax : ymin , ymax = ymax , ymin
934+
935+
936+
937+ spanx = xmax - xmin
938+ spany = ymax - ymin
939+ xproblems = self .minspanx is not None and spanx < self .minspanx
940+ yproblems = self .minspany is not None and spany < self .minspany
941+ if (self .drawtype == 'box' ) and (xproblems or yproblems ):
942+ """Box to small""" # check if drawed distance (if it exists) is
943+ return # not to small in neither x nor y-direction
944+ if (self .drawtype == 'line' ) and (xproblems and yproblems ):
945+ """Line to small""" # check if drawed distance (if it exists) is
946+ return # not to small in neither x nor y-direction
947+ self .onselect (self .eventpress , self .eventrelease )
948+ # call desired function
949+ self .eventpress = None # reset the variables to their
950+ self .eventrelease = None # inital values
951+ return False
952+
953+ def update (self ):
954+ 'draw using newfangled blit or oldfangled draw depending on useblit'
955+ if self .useblit :
956+ if self .background is not None :
957+ self .canvas .restore_region (self .background )
958+ self .ax .draw_artist (self .to_draw )
959+ self .canvas .blit (self .ax .bbox )
960+ else :
961+ self .canvas .draw_idle ()
962+ return False
963+
964+
965+ def onmove (self , event ):
966+ 'on motion notify event if box/line is wanted'
967+ if self .eventpress is None or self .ignore (event ): return
968+ x ,y = event .xdata , event .ydata # actual position (with
969+ # (button still pressed)
970+ if self .drawtype == 'box' :
971+ minx , maxx = self .eventpress .xdata , x # click-x and actual mouse-x
972+ miny , maxy = self .eventpress .ydata , y # click-y and actual mouse-y
973+ if minx > maxx : minx , maxx = maxx , minx # get them in the right order
974+ if miny > maxy : miny , maxy = maxy , miny
975+ self .to_draw .xy [0 ] = minx # set lower left of box
976+ self .to_draw .xy [1 ] = miny
977+ self .to_draw .set_width (maxx - minx ) # set width and height of box
978+ self .to_draw .set_height (maxy - miny )
979+ self .update ()
980+ return False
981+ if self .drawtype == 'line' :
982+ self .to_draw .set_data ([self .eventpress .xdata , x ],
983+ [self .eventpress .ydata , y ])
984+ self .update ()
985+ return False
0 commit comments