1

我尝试执行以下步骤:
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 的经验。你怎么看?

4

1 回答 1

1

我已经稍微简化了您的代码,删除了update_view(在我看来)不是绝对必要的方法。我已经在sel_span方法中添加了所有相关内容,每次在图表中选择一个区域时都会调用该方法。

我还创建了另一种方法remove_spans来删除选定区域并清除下面的列表框。这我也绑定到Delete按钮,所以如果按下按钮,选择和文本框被清除。

我还删除了列表框与ListboxSelect事件的绑定,因为在调用该insert方法时列表框将被更新。

最后:我已将您的xminxmax值添加到列表框中,而不是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()

我希望这有帮助。

于 2018-12-05T11:03:44.817 回答