Double event registered on mouse-click if legend is outside axes

后端 未结 3 1526
暗喜
暗喜 2021-01-20 03:21

I have to plot several data sets in one plot. It is useful to be able to highlight one or more of the plots in order to compare them. For this, I toggle the line style of th

相关标签:
3条回答
  • 2021-01-20 03:54

    My diving into the legend artist has found that a legend line is in the children tree of a legend twice when the legend has the bbox_to_anchor set.

    I asked about this here with my solution where I watched for a NEW mouseevent and kept track of the artists that had already been handled by my callback.

    I've asked for comments if anyone thinks there's a more elegant way to handle this "feature"

    I'm not sure this is a bug. But, it seems unique to legends where the children lines are held in the .lines attribute and deep in the packing boxes data structure - the get_children method finds both of these. Luckily they are the same object rather than a copy so I could check for a line already been handled.

    0 讨论(0)
  • 2021-01-20 04:05

    Another approach similar to tcaswell's solution counting events. Only 3 lines added to the original legend picking example. It uses python's function attributes.

    def onpick(event):
        if onpick.count % 2 == 0:    #### LINE ADDED ####  
            # on the pick event, find the orig line corresponding to the
            # legend proxy line, and toggle the visibility
            legline = event.artist
            origline = lined[legline]
            vis = not origline.get_visible()
            origline.set_visible(vis)
            # Change the alpha on the line in the legend so we can see what lines
            # have been toggled
            if vis:
                legline.set_alpha(1.0)
            else:
                legline.set_alpha(0.2)
            fig.canvas.draw()
        onpick.count += 1    #### LINE ADDED ####
    
    onpick.count = 0    #### LINE ADDED ####
    
    0 讨论(0)
  • 2021-01-20 04:09

    If you monkey patch Artist.pick with the following:

    matplotlib.artist.Artist.orig_pick = matplotlib.artist.Artist.pick
    def nu_pick(self, me):
        print self
        matplotlib.artist.Artist.orig_pick(self, me)
    
    matplotlib.artist.Artist.pick = nu_pick
    

    You can look at how the artists recurse on a pick event. (Each Artist object calls pick on it's self and then on all of it's children). For reasons I don't understand, there are two copies of each line in the drawing area of the legend (and it behaves differently when it is inside and outside).

    A way-hacky solution is to just count how many times the leglines have been hit, and only toggle on the odd ones:

    import pylab
    import numpy
    
    # Create data for plotting
    t = numpy.linspace(0, 1.0, 100) 
    a = numpy.sin(2*numpy.pi*t)
    
    # Set up figure
    fig = pylab.figure()
    ax = pylab.subplot(111)
    
    # Plot figures    
    lines = []    
    for i in range(5):
        line = ax.plot(t, (i+1)*a, linestyle=':', picker=5, label='line%d'%(i+1)) 
        lines.append(line[0]) # Save plot lines
    
    # Create legend
    leg = ax.legend(bbox_to_anchor=(1.01, 1), loc=2) # Does not work as expected
    #leg = ax.legend() # Works!!
    
    # Get legend lines
    leglines = leg.get_lines() 
    # Set event for legend lines
    for line in leglines:
        line.set_picker(5)
    
    # Create a 2 way mapping between legend lines <-> plot lines    
    line2leg = dict(zip(lines+leglines, leglines+lines))
    count_dict = dict((l, 0) for l in lines )
    # Define event function
    def onpick(event):
        thisline = event.artist
        print event
        print thisline
        if thisline in lines:
            print 'lines'
            count_dict[thisline] = 0
        elif thisline in leglines:
            print 'leglines'
            thisline = line2leg[thisline]
            count_dict[thisline] += 1
        print 'added'
        if (count_dict[thisline] % 2) == 1:
            print count_dict[thisline]
            return
        print 'tested'
        if thisline.get_linestyle()==':':
            print ": -> -" # For debugging
            thisline.set_linestyle('-')
            line2leg[thisline].set_linestyle('-')
        else:
            print "- -> :" # For debugging
            thisline.set_linestyle(':')
            line2leg[thisline].set_linestyle(':')
        fig.canvas.draw()
    
    # connect event function    
    fig.canvas.mpl_connect('pick_event', onpick)
    pylab.show()
    

    (I left all my de-bugging statements in).

    Pretty sure this is a bug, if you don't want to create an issue on github I will.

    0 讨论(0)
提交回复
热议问题