Кнопка виджета Matplotlib не отвечает после первого клика и вызова on_clicked () - PullRequest
0 голосов
/ 18 июня 2019

Я устанавливаю класс, который создает простой XY-график линии с некоторыми маркерами (X, Y).

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

Я добавил кнопку «Сброс», которая должна вернуть строку к ее исходным значениям.

Если я перетаскиваю одну точку и нажимаю кнопку «Сброс», она работает нормально, но если попробуйте снова перетащить точку, кнопка не отвечает.

Я довольно новичок в виджетах matplotlib, я, должно быть, упускаю что-то очевидное ...

Обратите внимание, что я пытался использовать разные бэкэнды, я также пробовал внутри ячейки ноутбука, используя (%matplotlib notebook)

Вот код моего класса "Путь":

import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import matplotlib as mpl
import numpy as np
from copy import copy

class Path(object):

def __init__(self, xcoords, ycoords):

    self.xcoords = xcoords
    self.ycoords = ycoords
    self.original_xcoords = copy(xcoords)
    self.original_ycoords = copy(ycoords)
    self.pind = None  # active point
    self.epsilon = 5  # max pixel distance

    # figure.subplot.right
    mpl.rcParams['figure.subplot.right'] = 0.8

    # set up a plot
    self.fig, self.ax1 = plt.subplots(1, 1, figsize=(9.0, 8.0), sharex=True)

    self.ax1.plot(self.xcoords, self.ycoords, 'k--', label='original')

    self.l, = self.ax1.plot(self.xcoords, self.ycoords,
        color='k', linestyle='none',
        marker='o', markersize=8)

    self.m, = self.ax1.plot(self.xcoords, self.ycoords, 'r-')

    self.ax1.set_yscale('linear')
    self.ax1.legend(loc=4, prop={'size': 10})

    self.axres = plt.axes([0.84, 0.8-((self.original_xcoords.shape[0])*0.05), 0.12, 0.02])
    self.bres = Button(self.axres, 'Test')
    self.bres.on_clicked(self.reset)

    self.fig.canvas.mpl_connect('button_press_event', self.button_press_callback)
    self.fig.canvas.mpl_connect('button_release_event', self.button_release_callback)
    self.fig.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)

    plt.show()

def reset(self, event):
    """ Reset the values """
    self.xcoords = self.original_xcoords
    self.ycoords = self.original_ycoords
    self.l.set_xdata(self.xcoords)
    self.m.set_xdata(self.xcoords)
    self.l.set_ydata(self.ycoords)
    self.m.set_ydata(self.ycoords)
    plt.draw()

def button_press_callback(self, event):
    if event.inaxes is None:
        return
    if event.button != 1:
        return
    self.pind = self.get_ind_under_point(event)

def button_release_callback(self, event):
    if event.button != 1:
        return
    self.pind = None

def get_ind_under_point(self, event):
    """
       Get the index of the vertex under point if within epsilon tolerance
    """
    tinv = self.ax1.transData
    xr = np.reshape(self.xcoords, (np.shape(self.xcoords)[0], 1))
    yr = np.reshape(self.ycoords, (np.shape(self.ycoords)[0], 1))
    xy_vals = np.append(xr, yr, 1)
    xyt = tinv.transform(xy_vals)
    xt, yt = xyt[:, 0], xyt[:, 1]
    d = np.hypot(xt - event.x, yt - event.y)
    indseq, = np.nonzero(d == d.min())
    ind = indseq[0]

    if d[ind] >= self.epsilon:
        ind = None

    return ind

def motion_notify_callback(self, event):
    'on mouse movement'
    if self.pind is None:
        return
    if event.inaxes is None:
        return
    if event.button != 1:
        return

    self.ycoords[self.pind] = event.ydata
    self.xcoords[self.pind] = event.xdata
    self.l.set_xdata(self.xcoords)
    self.m.set_xdata(self.xcoords)
    self.l.set_ydata(self.ycoords)
    self.m.set_ydata(self.ycoords)
    self.fig.canvas.draw_idle()


x = np.array([1,2,3,4])
y = np.array([5, 6, 2, 3])

#%matplotlib notebook
A = Path(x, y)

Ответы [ 2 ]

1 голос
/ 18 июня 2019

Минимальный пример проблемы:

import numpy as np

a = np.array([1,2,3])
b = a

b[1] = 1000
print(a)

он печатает [1, 1000, 3], даже если вы, казалось бы, только изменили b, a также будет изменено. Это потому, что они являются одним и тем же массивом.

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

import numpy as np

a = np.array([1,2,3])
b = np.copy(a)

b[1] = 1000
print(a)

В вашем случае

self.xcoords[:] = self.original_xcoords
self.ycoords[:] = self.original_ycoords

или

self.xcoords = np.copy(self.original_xcoords)
self.ycoords = np.copy(self.original_ycoords)
0 голосов
/ 18 июня 2019

Короче говоря: вы должны изменить функцию сброса:

self.xcoords = np.copy(self.original_xcoords)
self.ycoords = np.copy(self.original_ycoords)

Если быть кратким, все переменные в Python - это что-то вроде ссылок на объекты. Когда вы пишете a = 5, a получает ссылку на объект "5", когда вы пишете b = a, b получает ссылку на "5" как a. Та же самая ситуация имеет место с вашими self.coords и self.original_coords. Вы должны были скопировать их, чтобы сделать разные объекты, с которыми они связаны. Они ссылаются на один и тот же объект после первого вызова функции reset (). Вы сохранили там плохие координаты, поэтому с этого момента ваши объекты self.xcoords и self.original_coords связаны с одним и тем же объектом

...