@@ -795,3 +795,191 @@ def onmove(self, event):
795
795
self .rect .set_width (maxx - minx )
796
796
self .update ()
797
797
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