Не удалось обновить график Tplinter matplotlib с помощью кнопок и пользовательских данных

Я работаю над созданием программы, которая использует Tkinter и matplotlib . У меня есть 2 списка списков (один для оси x, один для оси y), и у меня есть кнопка, которая может переключаться между списками в списке. Я взял большую часть кода из вопроса Интерактивный сюжет, основанный на Tkinter и matplotlib , но я не могу получить график для обновления при нажатии кнопки. Я совершенно новичок в использовании классов и немного усвоил их.

tft – x-data tf1 – это y-данные в моем коде.

Пример данных:

x-data = [[1,2,3,4,5],[10,11,13,15,12,19],[20,25,27]]

y-data = [[5.4,6,10,11,6],[4,6,8,34,20,12],[45,25,50]]

Мой код ниже отобразит один из списков в списке, но не будет переключаться между списками в этом списке при нажатии кнопки. Правильное значение для event_num также event_num в командном окне (проблема event_num была решена в предыдущих вопросах здесь ).

Не появляется никаких ошибок, программа только печатает номер (когда кнопка нажата), но не обновляет график с новыми данными из списка.

Предварительный код (никаких проблем – или их не должно быть)

 #Importing Modules import glob from Tkinter import * from PIL import Image from Text_File_breakdown import Text_File_breakdown import re import matplotlib.pyplot as plt from datetime import datetime #Initializing variables important_imgs=[] Image_dt=[] building=[] quick=[] num=0 l=0 match=[] #Getting the names of the image files image_names=glob.glob("C:\Carbonite\EL_36604.02_231694\*.jpeg") #image= Image.open(images_names[1]) #image.show() #Text_File_breakdown(file,voltage limit,pts after lim, pts before lim) tft,tf1,tf2=Text_File_breakdown('C:\Carbonite\EL_36604.02_231694.txt',3.0,5,5) #tft= time of voltages tf1=Voltage signal 1 tf2=Voltage signal 2 #Test Settings: 'C:\Carbonite\EL_36604.02_231694.txt',3.0,5,5 #Getting the Dates from the image names for m in image_names: Idt=re.search("([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}.[0-9]{2}.[0-9]{2})", m) Im_dat_tim=Idt.group(1) Im_dat_tim=datetime.strptime(Im_dat_tim, '%Y-%m-%d %H.%M.%S') Image_dt.append(Im_dat_tim) Im_dat_tim=None #Looking for the smallest difference between the voltage and image dates and associating an index number (index of the image_names variable) with each voltage time for event in range(len(tft)): for i in range(len(tft[event])): diff=[tft[event][i]-Image_dt[0]] diff.append(tft[event][i]-Image_dt[0]) while abs(diff[l])>=abs(diff[l+1]): l=l+1 diff.append(tft[event][i]-Image_dt[l]) match.append(l) l=0 #Arranging the index numbers (for the image_names variable) in a list of lists like tft variable for count in range(len(tft)): for new in range(len(tft[count])): quick.append(match[num]) num=num+1 building.append(quick) quick=[] plt.close('all') fig, ax = plt.subplots(1) ax.plot(tft[1],tf1[1],'.') # rotate and align the tick labels so they look better fig.autofmt_xdate() # use a more precise date string for the x axis locations in the # toolbar import matplotlib.dates as mdates ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d') plt.title('Single Event') 

Продолжение кода / Часть кода, в котором проблема:

 from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure import numpy as np class App: def __init__(self, master): self.event_num = 1 # Create a container frame = Frame(master) # Create 2 buttons self.button_left = Button(frame,text="< Previous Event", command=self.decrease) self.button_left.grid(row=0,column=0) self.button_right = Button(frame,text="Next Event >", command=self.increase) self.button_right.grid(row=0,column=1) fig = Figure() ax = fig.add_subplot(111) fig.autofmt_xdate() import matplotlib.dates as mdates ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d') self.line, = ax.plot(tft[self.event_num],tf1[self.event_num],'.') self.canvas = FigureCanvasTkAgg(fig,master=master) self.canvas.show() self.canvas.get_tk_widget().grid(row=1,column=0) frame.grid(row=0,column=0) def decrease(self): self.event_num -= 1 print self.event_num self.line, = ax.plot(tft[self.event_num],tf1[self.event_num],'.') self.canvas.draw() #self.canvas.draw(tft[self.event_num],tf1[self.event_num],'.') #self.line.set_xdata(tft[event_num]) #self.line.set_ydata(tf1[event_num]) def increase(self): self.event_num += 1 print self.event_num self.line, = ax.plot(tft[self.event_num],tf1[self.event_num],'.') self.canvas.draw() #self.canvas.draw(tft[self.event_num],tf1[self.event_num],'.') #self.set_xdata(tft[event_num]) #self.set_ydata(tf1[event_num]) root = Tk() app = App(root) root.mainloop() 

2 Solutions collect form web for “Не удалось обновить график Tplinter matplotlib с помощью кнопок и пользовательских данных”

Проблема в том, что вы постоянно рисуете на другом наборе осей, чем вы думаете. self.line, = ax.plot(tft[self.event_num],tf1[self.event_num],'.') относится к осям, созданным вне вашего класса, а не к осям на рисунке, который вы создали в классе App , Проблема может быть устранена путем создания self.fig и self.ax :

 class App: def __init__(self, master): self.event_num = 1 # Create a container frame = Frame(master) # Create 2 buttons self.button_left = Button(frame,text="< Previous Event", command=self.decrease) self.button_left.grid(row=0,column=0) self.button_right = Button(frame,text="Next Event >", command=self.increase) self.button_right.grid(row=0,column=1) self.fig = Figure() self.ax = self.fig.add_subplot(111) self.fig.autofmt_xdate() import matplotlib.dates as mdates self.ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d') self.line, = self.ax.plot(tft[self.event_num],tf1[self.event_num],'.') self.canvas = FigureCanvasTkAgg(self.fig,master=master) self.canvas.show() self.canvas.get_tk_widget().grid(row=1,column=0) frame.grid(row=0,column=0) def decrease(self): self.event_num -= 1 print self.event_num self.line, = self.ax.plot(tft[self.event_num],tf1[self.event_num],'.') self.canvas.draw() def increase(self): self.event_num += 1 print self.event_num self.line, = self.ax.plot(tft[self.event_num],tf1[self.event_num],'.') self.canvas.draw() 

Другая (возможная) проблема заключается в том, что данные добавляются к графику вместо замены. Есть два способа исправить это:

  1. Поверните self.ax.hold(False) : self.ax.hold(False) где-нибудь в __init__() прежде чем что-либо сделать.
  2. Фактически замените данные графика: Замените линию

     self.line, = self.ax.plot(tft[self.event_num],tf1[self.event_num],'.') 

    с

     self.line.set_xdata(tft[self.event_num]) self.line.set_ydata(tf1[self.event_num]) 

Вы очерчиваете оси в глобальном пространстве имен, но перерисовываете холст на другом, несвязаном глобальном объекте. Я думаю, что ваши проблемы связаны с глобальным и локальным объемом и самообъектом в объектно-ориентированном программировании.

Обзор Python

Переменные, созданные в вашем файле без отступов, находятся в глобальной области файлов. class и def локальные области. Ссылки на имя переменной разрешаются путем проверки областей в следующем порядке:

  • Локальная область
  • Закрытие локальной области (включая только def s, а не class es)
  • Глобальный охват
  • Встроенные команды

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

Связывание локальных переменных удаляется при возврате функции. Значения, которые не связаны, в конечном итоге собираются мусором. Если присваивание связывает глобальную переменную с значением локальной переменной, значение будет сохраняться с глобальным.

Применение этого кода

В вашем коде есть два набора объектов фигуры / осей.

 # From your top chunk of code from Tkinter import * import matplotlib.pyplot as plt # This line creates a global variable ax: fig, ax = plt.subplots(1) # From your bottom chunk of code class App(self, master): def __init__(self, master): fig = Figure() # This line creates a local variable ax: ax = fig.add_subplot(111) # This line references the local variable ax: ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d') # This line references the local variable ax, and binds its value # to the attribute line of the self object, # which is the global variable app: self.line, = ax.plot(x[self.event_n],y[self.event_n]) def increase(self): self.event_num += 1 # This line references the global variable ax self.line, = ax.plot(x[self.event_n],y[self.event_n]) # This line updates the object's canvas but the plot # was made on the global axes: self.canvas.draw() app = App(root) 

Вы можете исправить это, всегда ссылаясь на оси, связанные с вашим корневым окном Tkinter:

  • self.fig, self.ax = plt.subplots(1, 1) для создания прочных осей.
  • self.ax.cla() чтобы удалить данные предыдущего выбора
  • self.ax.plot() чтобы добавить данные в текущий индекс self.event_num
  • self.ax.set_xlim(x_min*0.9, x_max*0.9) и self.ax.set_ylim(y_min*1.1, y_max*1.1) чтобы окно осей было согласованным, чтобы визуализировать перемещение среднего значения среди внутренних списков. x_max , y_max , x_min и y_min могут быть определены до цикла основного события, сглаживая списки x и y, а затем используя встроенные max() и min() .
Python - лучший язык программирования в мире.