Обработка объектов аннотации в matplotlib

Я пытаюсь реализовать сюжет с интерактивными ползунками в Matplotlib. Я использовал предложения из интерактивного сюжета matplotlib с двумя ползунками для их реализации. Сейчас я пытаюсь переместить аннотацию точки, когда меняются ползунки. Кажется, есть какое-то странное поведение, которое я не могу понять.

Ниже приведен пример рабочего кода. Код создает точку в 3-м субплоте, и когда ползунок изменяется, точка перемещается. На точке есть аннотация, которая перемещается вместе с точкой. Однако в текущем примере старые аннотации не удаляются, что нежелательно.

Я пытался кое-что исправить. Например, я бы ожидал, что закомментированный код помечен '!!' чтобы сделать работу: удалите старую аннотацию, а затем добавьте новую. Однако, если это не прокомментировано, с аннотацией ничего не происходит при смене слайдера.

Я прочитал Удалить аннотацию с сохранением сюжета matplotlib, Python и Удалить аннотацию с рисунка и Удалить и повторно добавить объект в matplotlib | Переключить внешний вид объекта matplotlib. Я попытался реализовать предложения оттуда, то есть изменить координаты аннотации или добавить аннотацию в качестве объекта исполнителя. Ни один из них ничего не сделал.

Есть что-то фундаментальное, что я здесь ошибаюсь? Мне кажется, что я не понимаю, как переменные объекта для аннотаций обрабатываются в Python.

Еще один комментарий: я бы предпочел не слишком сильно менять структуру кода (особенно использование sympy). Я удалил много вещей, чтобы создать этот минимальный пример для воспроизведения ошибки, но мне нужна структура для других моих графиков.

import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.widgets import Slider, Button, RadioButtons

## Define variable symbols ##
var1 = sp.Symbol('var1')

## Define initial value ##
var1_init = 5

## Marker position functions ##
def positionY(var1):
    return var1

def positionX(var1):
    return var1

## plot ##
axis_color = 'lightgoldenrodyellow'

fig = plt.figure()
ax = fig.add_subplot(131)
ax2 = fig.add_subplot(132)
ax3 = fig.add_subplot(133)

# Adjust the subplots region to leave some space for the slider
fig.subplots_adjust(left=0.25, bottom=0.3)

# Draw the initial plot
marker_coord = (positionX(var1_init),positionY(var1_init))
[qMarker1] = ax3.plot(marker_coord[0],marker_coord[1],'ro')
ax3.set_xlim([-1.0, 11.0])
ax3.set_ylim([-1.0, 11.0])
qAnnotation = ax3.annotate('(%.2f, %.2f)' % marker_coord, xy=marker_coord, textcoords='data')

## Add sliders ##

# Define an axes area and draw sliders in it
var1_slider_ax  = fig.add_axes([0.25, 0.2, 0.65, 0.03], facecolor=axis_color)
var1_slider = Slider(var1_slider_ax, 'var1', 0, 10.0, valinit=var1_init)

# Define an action for modifying the plot1 when any slider's value changes
def sliders_on_changed(val):
    qMarker1.set_ydata(positionY(var1_slider.val))
    qMarker1.set_xdata(positionX(var1_slider.val))
    #qAnnotation.remove() ## <--------------------------- !!
    marker_coord = (positionX(var1_slider.val),positionY(var1_slider.val))
    qAnnotation = ax3.annotate('(%.2f, %.2f)' % marker_coord, xy=marker_coord, textcoords='data')
    fig.canvas.draw_idle()
var1_slider.on_changed(sliders_on_changed)

# Add a button for resetting the parameters
reset_button_ax = fig.add_axes([0.05, 0.1, 0.1, 0.04])
reset_button = Button(reset_button_ax, 'Reset', color=axis_color, hovercolor='0.975')
def reset_button_on_clicked(mouse_event):
    var1_slider.reset()
reset_button.on_clicked(reset_button_on_clicked)

plt.show()

1 ответ

Решение

Раскомментирование строки qAnnotation.remove() выдает ошибкуUnboundLocalError: local variable 'qAnnotation' referenced before assignment, который довольно объяснителен по проблеме. qAnnotation переопределяется в локальной области действия функции. Так что на самом деле речь идет о понимании локальных и глобальных областей действия в python и не имеет ничего общего с конкретными объектами matplotlib.

Ошибка может быть легко воспроизведена в случае, подобном

a = 0
def f():
    a += 1
    a=100
f()

который также бросает, UnboundLocalError: local variable 'a' referenced before assignment,

Самое простое решение: сделать qAnnotation доступны в глобальном масштабе, global qAnnotation,

def sliders_on_changed(val):
    global qAnnotation
    # ..
    qAnnotation.remove() 
    qAnnotation = ax3.annotate(...)
    fig.canvas.draw_idle()

Другое решение, избегающее явного global операторы должны были бы сделать аннотацию частью списка и получить к ней доступ локально.

qAnnotation = [ax3.annotate( ... )]

def sliders_on_changed(val):
    # ..
    qAnnotation[0].remove()
    qAnnotation[0] = ax3.annotate( ... )
    fig.canvas.draw_idle()
Другие вопросы по тегам