Python Tkinter - интерактивный график и список с виджетом span - PullRequest
0 голосов
/ 05 декабря 2018

Следующие шаги, которые я пытаюсь сделать:
1. вставьте список пар значений [xmin, xmax]
2. создайте начальный список объектов диапазона
3. нанесите интервалы и напишите меткив список
4. добавить интервалы на график (через событие onlick) и обновить список
5. удалить и выделить интервалы

Вот пример кода:

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib.widgets import SpanSelector
import tkinter as tk
import numpy as np

class App:
     def __init__(self, master, l_val_pair):
         # Create a container
         frame = tk.Frame(master)

         # Create fields
         self.button_left = tk.Button(frame,text="Export")
         self.button_left.pack(side="left")
         self.button_right = tk.Button(frame,text="Delete", command = 
                                       lambda: self.delete(self.l_spans))
         self.button_right.pack(side="left")

         self.listbox = tk.Listbox(master)
         self.listbox.pack(side='bottom', fill=tk.X) 

         #define figure
         self.fig = Figure()
         self.ax = self.fig.add_subplot(111)

         #sample data for a line
         self.x = np.arange(0.0, 5.0, 0.01)
         self.y = np.sin(2*np.pi*self.x) + 0.5*np.random.randn(len(self.x))

         #plot line and list of spans
         self.ax.plot(self.x,self.y)

         self.l_spans = [self.ax.axes.axvspan(
                 val_pair[0],val_pair[1], facecolor='red', alpha=0.3, label=val_pair) 
                    for val_pair in l_val_pair]

         #create a listbox      
         [self.listbox.insert(tk.END, item.get_label()) for item in self.l_spans]
         self.listbox.bind('<<ListboxSelect>>', self.highlight_span)         

         #plot figure
         self.canvas = FigureCanvasTkAgg(self.fig,master=master)
         self.canvas.show()
         self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
         frame.pack()

         #use span selector
         self.span = SpanSelector(self.ax, self.sel_span, 'horizontal', useblit=True,
                             rectprops=dict(alpha=0.5, facecolor='red'), span_stays=False)

         #connect
         self.canvas.mpl_connect('key_press_event', self.span)


     def sel_span(self,xmin, xmax):
         indmin, indmax = np.searchsorted(self.x, (xmin, xmax))
         indmax = min(len(self.x) - 1, indmax)

         span = self.ax.axes.axvspan(xmin=self.x[indmin], xmax=self.x[indmax], 
                              alpha = 0.5, facecolor='red')

         #add actual span to list of spans
         self.l_spans.append(span)

         #update view (spans and listbox) with central list of spans          
         self.update_view()


     def update_view(self):
         #clear visualisation
         self.listbox.delete(0,tk.END)
         [span.remove() for span in self.l_spans]


         #fill with new data
         [self.listbox.insert(tk.END, span.get_label()) for span in self.l_spans]
         [span.draw(self.ax) for span in self.l_spans] #HERE IS THE PROBLEM!!!

         #update view
         #...


     def highlight_span():
         pass

     def delete(self,item):
         pass

list_vals= [[1,2],[2.5,4]]
root = tk.Tk()
app = App(root, list_vals)
root.mainloop()

Проблема в функции update_view:
Что это за средство визуализации, которое мне нужно вставить в span.draw ()?

Я не уверен, что мой подход хорош, поскольку у меня нет опыта работы с tkinter.Что ты думаешь?

1 Ответ

0 голосов
/ 05 декабря 2018

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

Я также создал другой метод remove_spans для удаления выбранной области иочистите список ниже.Это я также привязал к кнопке Delete, так что если кнопка нажата, выделение и текстовое поле очищаются.

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

Последнее: я добавил ваши значения xmin и xmax в список вместо элементов span.get_label().Вот пример кода:

class App:
    def __init__(self, master, l_val_pair):
         # Create a container
        frame = tk.Frame(master)

        # Create fields
        self.button_left = tk.Button(frame,text="Export")
        self.button_left.pack(side="left")
        self.button_right = tk.Button(frame,text="Delete", command = self.remove_spans)
        self.button_right.pack(side="left")

        self.listbox = tk.Listbox(master)
        self.listbox.pack(side='bottom', fill=tk.X) 

        #define figure
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)

        #sample data for a line
        self.x = np.arange(0.0, 5.0, 0.01)
        self.y = np.sin(2*np.pi*self.x) + 0.5*np.random.randn(len(self.x))

        #plot line and list of spans
        self.ax.plot(self.x,self.y)

        self.l_spans = [self.ax.axes.axvspan(val_pair[0],val_pair[1], facecolor='red', alpha=0.3, label=val_pair) 
                    for val_pair in l_val_pair]

        #create a listbox      
        [self.listbox.insert(tk.END, item.get_label()) for item in self.l_spans]          

        #plot figure
        self.canvas = FigureCanvasTkAgg(self.fig,master=master)
        self.canvas.show()
        self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
        frame.pack()

        #use span selector
        self.span = SpanSelector(self.ax, self.sel_span, 'horizontal', useblit=True,
                             rectprops=dict(alpha=0.5, facecolor='red'), span_stays=False)

        #connect
        self.canvas.mpl_connect('key_press_event', self.span)


    def sel_span(self, xmin, xmax):
        #clear visualisation
        self.remove_spans()
        indmin, indmax = np.searchsorted(self.x, (xmin, xmax))
        indmax = min(len(self.x) - 1, indmax)
        span = self.ax.axes.axvspan(xmin=self.x[indmin], xmax=self.x[indmax], 
                              alpha = 0.5, facecolor='red')

        #add actual span to list of spans
        self.l_spans.append(span)

        #for span in self.l_spans:
        self.listbox.insert(tk.END, xmin)
        self.listbox.insert(tk.END, xmax)


    def remove_spans(self):
        self.listbox.delete(0,tk.END)

        for span in self.l_spans:
           try:
               span.remove()
           except:
               pass
        self.canvas.draw_idle()


list_vals= [[1,2],[2.5,4]]
root = tk.Tk()
app = App(root, list_vals)
root.mainloop()

РЕДАКТИРОВАТЬ: комментарий немного прояснил сценарий использования, так что вот измененная версия кода.Я повторно ввел привязку к событию <<ListboxSelect>> и написал метод highlight_span, который:

  • Проверяет, какой диапазон в списке был выбран
  • выделяет его на рисункезеленым цветом (просто чтобы сделать его более заметным)

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

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib.widgets import SpanSelector
import tkinter as tk
import numpy as np
import ast

class App:
    def __init__(self, master, l_val_pair):
         # Create a container
        frame = tk.Frame(master)

        # Create fields
        self.button_left = tk.Button(frame,text="Export")
        self.button_left.pack(side="left")
        self.button_right = tk.Button(frame,text="Delete", command = self.remove_spans)
        self.button_right.pack(side="left")

        self.listbox = tk.Listbox(master)
        self.listbox.pack(side='bottom', fill=tk.X) 
        self.listbox.bind('<<ListboxSelect>>', self.highlight_span)   

        #define figure
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)

        #sample data for a line
        self.x = np.arange(0.0, 5.0, 0.01)
        self.y = np.sin(2*np.pi*self.x) + 0.5*np.random.randn(len(self.x))

        #plot line and list of spans
        self.ax.plot(self.x,self.y)

        self.l_spans = [self.ax.axes.axvspan(val_pair[0],val_pair[1], facecolor='red', alpha=0.3, label=val_pair) 
                    for val_pair in l_val_pair]

        #create a listbox      
        [self.listbox.insert(tk.END, item.get_label()) for item in self.l_spans]

        #plot figure
        self.canvas = FigureCanvasTkAgg(self.fig,master=master)
        self.canvas.show()
        self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
        frame.pack()

        #use span selector
        self.span = SpanSelector(self.ax, self.sel_span, 'horizontal', useblit=True,
                             rectprops=dict(alpha=0.5, facecolor='red'), span_stays=False)

        #connect
        self.canvas.mpl_connect('key_press_event', self.span)


    def sel_span(self, xmin, xmax):
        #clear visualisation        
        for span in self.l_spans:
           try:
               span.remove()
           except:
               pass
        self.canvas.draw_idle()        

        indmin, indmax = np.searchsorted(self.x, (xmin, xmax))
        indmax = min(len(self.x) - 1, indmax)
        span = self.ax.axes.axvspan(xmin=self.x[indmin], 
                                    xmax=self.x[indmax], 
                                    alpha = 0.5,  
                                    facecolor='red',
                                    label = [xmin, xmax])

        #add actual span to list of spans
        self.l_spans.append(span)

        self.listbox.insert(tk.END, span.get_label())        


    def remove_spans(self):
        self.listbox.delete(0,tk.END)

        for span in self.l_spans:
           try:
               span.remove()
           except:
               pass
        self.canvas.draw_idle()

    def highlight_span(self, evt):        
        w = evt.widget
        index = w.curselection()[0]
        value = w.get(index)
        value = list(map(float, ast.literal_eval(value)))

        xmin = value[0]
        xmax = value[1]

        indmin, indmax = np.searchsorted(self.x, (xmin, xmax))
        indmax = min(len(self.x) - 1, indmax)
        span = self.ax.axes.axvspan(xmin=self.x[indmin], 
                                    xmax=self.x[indmax], 
                                    alpha = 0.5,  
                                    facecolor='green',
                                    label = [xmin, xmax])
        self.l_spans.append(span)
        self.canvas.draw_idle()



list_vals= [[1,2],[2.5,4]]
root = tk.Tk()
app = App(root, list_vals)
root.mainloop()

Надеюсь, это поможет.

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