Создание событий щелчка легенды на нескольких дочерних участках matplotlib.pyplot - PullRequest
0 голосов
/ 23 мая 2019

У меня есть несколько matplolib.pyplot цифр.У каждого есть легенда, и я хочу, чтобы щелчок по линии на легенде скрывал ее на рисунке.Обработка событий щелчка была найдена здесь: https://matplotlib.org/examples/event_handling/legend_picking.html

Это прекрасно работает, когда есть только одна цифра, но когда их несколько, она работает только с последней цифрой.Когда я нажимаю на легенду другой фигуры, я не получаю никаких исключений или предупреждений, но ничего не происходит.

Вот пример кода, который имеет эту проблему:

import matplotlib.pyplot as plt
import numpy as np

a = np.arange(0,10,1)
    b = np.arange(0,20,2)
    c = np.arange(0,5,.5)
    d = np.arange(-1,9,1)

    lined = {}

    for var1, var2 in [(a,b), (c,d)]:
        fig, ax = plt.subplots()
        line1, = ax.plot(var1, label="l1")
        line2, = ax.plot(var2, label="l2")
        leg = fig.legend([line1, line2], ["l1", "l2"])
        legl1, legl2 = leg.get_lines()
        legl1.set_picker(5)
        lined[legl1] = line1
        legl2.set_picker(5)
        lined[legl2] = line2

        def onpick(event, figu):
            legl = event.artist
            origl = lined[legl]
            vis = not origl.get_visible()
            origl.set_visible(vis)
            if vis:
                legl.set_alpha(1.0)
            else:
                legl.set_alpha(0.2)
            figu.canvas.draw()

        fig.canvas.mpl_connect('pick_event', lambda ev: onpick(ev, fig))
    plt.show()

Как я могу сделатьсобытие click тоже работает на первом рисунке?

Ответы [ 4 ]

1 голос
/ 23 мая 2019

@ Ответ Лепорелло завершен с точки зрения диагностики и решения. Вот полное объяснение:

Строка lambda ev: onpick(ev, fig) создает функциональный объект, который ссылается на имена onpick и fig. Функция вызывается после окончания цикла, после запуска plt.show().

Имена onpick и fig не являются локальными, поэтому он ищет в пространстве имен модуля. Ко времени вызова слушателя onpick относится к последней созданной функции, а fig относится к фигуре, созданной в последней итерации цикла.

Предложение Лепорелло аргументов по умолчанию, вероятно, является наиболее элегантным способом сделать это. Это работает, потому что весь оператор def вычисляется сразу. A def создает функциональный объект с блоком кода внутри и сразу же устанавливает ссылки на значения по умолчанию. Это означает, что в конечном итоге вы устанавливаете обратный вызов для правильного функционального объекта, и то, что fig было внутри цикла, будет тем, на что figu указывает.

Любая операция, которая связывает имена onpick и fig с чем-либо в локальном пространстве имен обратного вызова или любом другом пространстве имен, созданном в цикле, решит вашу проблему.

1 голос
/ 23 мая 2019

Причина указана https://docs.python -guide.org / writing / gotchas / # late-binding-closures .Я должен признать, что сам не совсем понимаю, но это дает хитрость для его решения: используйте аргумент по умолчанию.

import matplotlib.pyplot as plt
import numpy as np

a = np.arange(0,10,1)
b = np.arange(0,20,2)
c = np.arange(0,5,.5)
d = np.arange(-1,9,1)

lined = {}

for var1, var2 in [(a,b), (c,d)]:
    fig, ax = plt.subplots()
    line1, = ax.plot(var1, label="l1")
    line2, = ax.plot(var2, label="l2")
    leg = fig.legend([line1, line2], ["l1", "l2"])
    legl1, legl2 = leg.get_lines()
    legl1.set_picker(5)
    lined[legl1] = line1
    legl2.set_picker(5)
    lined[legl2] = line2

    def onpick(event, figu=fig):
        legl = event.artist
        origl = lined[legl]
        vis = not origl.get_visible()
        origl.set_visible(vis)
        if vis:
            legl.set_alpha(1.0)
        else:
            legl.set_alpha(0.2)
        figu.canvas.draw()

    fig.canvas.mpl_connect('pick_event', onpick)  # no need for a lambda
plt.show()

Как уже упоминалось, это решение довольно хакерское.Сравните кажущийся эквивалент

# works
    def onpick(event, figu=fig):
        (...)
        figu.canvas.draw()  # using a default arg equal to fig
    fig.canvas.mpl_connect('pick_event', onpick)

с

# fails as described
    def onpick(event):
        (...)
        fig.canvas.draw()  # using fig from main loop directly
    fig.canvas.mpl_connect('pick_event', onpick)
0 голосов
/ 23 мая 2019

На самом деле я нашел решение, которое немного проще, чем ImportanceOfBeingErnest's и имеет больше смысла, чем Leporello's , потому что оно определяет функцию onpick только один раз, и то же самое длякаждая цифра.

Поскольку ссылка на рисунок необходима только для canvas, а * canvas находится в event, следующий код отлично работает:

import matplotlib.pyplot as plt
import numpy as np

a = np.arange(0,10,1)
    b = np.arange(0,20,2)
    c = np.arange(0,5,.5)
    d = np.arange(-1,9,1)

    lined = {}
    def onpick(event):
            legl = event.artist
            origl = lined[legl]
            vis = not origl.get_visible()
            origl.set_visible(vis)
            if vis:
                legl.set_alpha(1.0)
            else:
                legl.set_alpha(0.2)
            event.canvas.draw()

    for var1, var2 in [(a,b), (c,d)]:
        fig, ax = plt.subplots()
        line1, = ax.plot(var1, label="l1")
        line2, = ax.plot(var2, label="l2")
        leg = fig.legend([line1, line2], ["l1", "l2"])
        legl1, legl2 = leg.get_lines()
        legl1.set_picker(5)
        lined[legl1] = line1
        legl2.set_picker(5)
        lined[legl2] = line2

        fig.canvas.mpl_connect('pick_event', onpick)
    plt.show()
0 голосов
/ 23 мая 2019

Это идеальное приложение, где использование класса может быть полезным.Это позволило бы сохранить соответствующий рисунок в переменной экземпляра и использовать его в методах класса.

import matplotlib.pyplot as plt
import numpy as np

class MyPlot():
    def __init__(self, var1, var2):
        self.lined = {}
        self.fig, ax = plt.subplots()
        line1, = ax.plot(var1, label="l1")
        line2, = ax.plot(var2, label="l2")
        leg = self.fig.legend([line1, line2], ["l1", "l2"])
        legl1, legl2 = leg.get_lines()
        legl1.set_picker(5)
        self.lined[legl1] = line1
        legl2.set_picker(5)
        self.lined[legl2] = line2
        self.cid = self.fig.canvas.mpl_connect('pick_event', self.onpick)

    def onpick(self, event):
        legl = event.artist
        if legl in self.lined:
            origl = self.lined[legl]
            vis = not origl.get_visible()
            origl.set_visible(vis)
            if vis:
                legl.set_alpha(1.0)
            else:
                legl.set_alpha(0.2)
            self.fig.canvas.draw()


a = np.arange(0,10,1)
b = np.arange(0,20,2)
c = np.arange(0,5,.5)
d = np.arange(-1,9,1)

plots = [MyPlot(*var) for var in [(a,b), (c,d)]]

plt.show()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...