Почему панель инструментов интерактивной навигации matplotlib не позволяет выбирать легенды - PullRequest
0 голосов
/ 22 мая 2018

Мы иногда сравниваем множество системных процессов на одном графике.Я создал макет с помощью matplotlib, как показано ниже.Поскольку существует так много точек данных, легенда интерактивна со средствами выбора, которые переключают видимость линии / исполнителя по отдельности и всей группе.

Проблема, с которой я столкнулся, заключается в том, что после использования интерактивного масштабирования или панорамирования интерактивной легендыне работает, пока та же кнопка не будет нажата снова.Если я использую масштабирование и панорамирование, легенда перестает отвечать на запросы и требует перезагрузки диаграммы.

Python 3.6.0 и Matplotlib 2.0.0

import numpy as np
import random
import operator
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D

#Generate some fake data
datalength = 60
datavariance = 25
xline = np.array(range(datalength))
y1 = np.array(range(datalength))
groups = 5 #Number of groups
members = 4 #Number of members per group
grouping = {} #Nested dictionary to hold groups and member dictionaries
for g in range(groups):
    groupmembers = {}
    for m in range(members):
        groupmembers[f'SP_{g+1}_{m+1}'] = []
    grouping[f'SP_{g+1}'] = groupmembers

#Produces a nested dictionary like:
#grouping = {'PSP_1': {'PSP_1_1':[], 'PSP_1_2':[], 'PSP_1_3':[]},
#            'PSP_2': {'PSP_2_1':[], 'PSP_2_2':[], 'PSP_2_3':[]},
#            'PSP_3': {'PSP_3_1':[], 'PSP_3_2':[], 'PSP_3_3':[]}
#            }

#Establish the figure and arrange the subplots
fig = plt.figure(figsize=(10,7))
ax1 = plt.subplot2grid((3,1), (0,0), rowspan=1, colspan=1, facecolor='white')
ax2 = plt.subplot2grid((3,1), (1,0), rowspan=1, colspan=1, facecolor='white')
ax3 = plt.subplot2grid((3,1), (2,0), rowspan=1, colspan=1, facecolor='white')

# Each member is plotted in each subplot and then the artist is added to the array in the nested dictionary.
for group in grouping:
    for member in grouping[group]:
        line1 = ax1.plot(xline, y1+np.random.randint(low=0, high=datavariance, size=datalength), lw=1, label=member, marker='.')
        line2= ax2.scatter(np.random.randn(datalength)+4, y1+np.random.randint(low=0, high=datavariance, size=datalength), s=5, label=member)
        line3 = ax3.hist(np.random.randn(datalength)+4, 20, histtype='step', lw=1, label=member)
        #Dictionary entry of lines for member
        grouping[group][member].extend((line1[0], line2, line3[2]))

#Chart labeling
ax1.set_title('Process Line', fontsize=10, bbox=dict(facecolor='white'))
ax2.set_title('Process Scatter', fontsize=10, bbox=dict(facecolor='white'))
ax3.set_title('Process Histogram', fontsize=10, bbox=dict(facecolor='white'))

fig.suptitle('System Processes', fontsize=16, bbox=dict(facecolor='white'))

leghandles, leglabels = ax1.get_legend_handles_labels() #Get handels and labels from subplot 1 for use in the legend.

grouplines = {} #Dictionary of lines by group
groupvisible = {} #Dictionary for visibility control
for group in grouping:
    linearray = []
    for member in grouping[group]:
        for line in grouping[group][member]:
            linearray.append(line)
    grouplines[group] = linearray #Dictionary entry of Group:array of all lines in group
    groupvisible[group] = True #Dictionary to control Group visibility
    leghandles.append(Line2D([], [], label=group, marker='_', markersize=7, lw=4, color='black')) #Array entry of non-existent lines for Group legend
    leglabels.append(group)

leghandles, leglabels = zip(*sorted(zip(leghandles, leglabels), key=operator.itemgetter(1))) #Sort legend by name
leghandlelist = list(leghandles) #Change tuple to list for inserting
leglabellist = list(leglabels) #Change tuple to list for inserting

for i in range(groups):
    leghandlelist.insert((members+2)*i, Line2D([], [], lw=.0)) #Add fake like
    leglabellist.insert((members+2)*i, ' ') #Add blank label

leghandles = tuple(leghandlelist) #Return handles to tuple
leglabels = tuple(leglabellist) #Return labels to tuple
mylegend = fig.legend(leghandles, leglabels, fancybox=True, shadow=True, loc='upper left', ncol=1, title='Process Groups')

#Associate the legend to plotted lines and set pickers on the legend item
legenditems = {}
for legendentry, plottedline in zip(mylegend.get_lines(), leghandles):
    legendentry.set_picker(5)
    legenditems[legendentry] = plottedline

plt.subplots_adjust(left=0.2, right=0.95, top=0.90, bottom=.05, wspace=.2, hspace=.3)


def onpick(event):
    legendclick = event.artist #Legend object
    legendlabel = legendclick.get_label() #Name of selected lines or group to toggle

    #Check line visibility and toggle accordingly
    vis = not legendclick.get_visible()
    legendclick.set_visible(vis) #Toggle the clicked legend item

    if legendlabel in grouplines.keys(): #If the selected legend item is a group name
        for plottedline in grouplines[legendlabel]: #Toggle plotted lines for group members
            try: #Toggle lines and scatter points
                plottedline.set_visible(vis)
            except: #Toggle histograms
                for hi in range(len(plottedline)):
                    plottedline[hi].set_visible(vis)
        for member in grouping[legendlabel]: #Toggle legend entries for group members
            for entry in legenditems:
                if entry.get_label() == member:
                    try: #Toggle lines and scatter points
                        entry.set_visible(vis)
                    except: #Toggle histograms
                        for hi in range(len(entry)):
                            print(hi)
    else: #If a member item was selected
        for group in grouping:
            for member in grouping[group]:
                if member == legendlabel: #Toggle member lines in each chart.
                    for i in range(len(grouping[group][member])):
                        try: #Toggle lines and scatter
                            selectedline = grouping[group][member][i]
                            selectedline.set_visible(vis)
                        except: #Toggle histogram
                            for hi in range(len(grouping[group][member][i])):
                                selectedline = grouping[group][member][i][hi]
                                selectedline.set_visible(vis)

    fig.canvas.draw()

fig.canvas.mpl_connect('pick_event', onpick)

plt.show()

enter image description here

...