【问题标题】:Matplotlib/tkinter: Event picking on legend when opening multiple windowsMatplotlib/tkinter:打开多个窗口时在图例上选择事件
【发布时间】:2018-03-31 22:55:43
【问题描述】:

这是我对 tkinter 中 Matplotlib 功能的其他 question 的后续。我正在尝试开发一个程序,当我训练模型时,它会打开多个包含绘图的窗口。这些值作为字典存储在 aveCR 中,每个键存储多个数组。对于每个键,我在一个新窗口中绘制数组,因此使用 for 循环打开一个新窗口(不确定这是否是一个好习惯!)

我遇到的问题是每个窗口的图例。开启/关闭线条的功能仅在最终窗口中可用,我希望在每个打开的新窗口中都可以使用此功能。我知道 self.lined 被 for 循环中的最终绘图覆盖,但我不确定是否应该将其添加到 for 循环中。

我在下面为aveCR添加了虚拟数字,因此可以运行代码。任何解决此问题的建议将不胜感激!

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np

import tkinter as tk

class App:
    def __init__(self,master):
        self.master = master
        # Frame for the training values
        buttonFrame = tk.Frame(master)
        buttonFrame.pack()

        self.buttonGenerate = tk.Button(master=buttonFrame,
                                        text='Train',
                                        command=self.train)
        self.buttonGenerate.grid(column=2,row=0)


    def train(self):

        aveCR = {0:{0:np.array([.582,1.081,1.507,1.872,2.180]),1:np.array([2.876,6.731,1.132,1.305,1.217])},
            1:{0:np.array([.582,1.081,1.507,1.872,2.180]),1:np.array([2.876,6.731,1.132,1.305,1.217])}}

        legend = {0: ['A', 'AB'], 1: ['A', 'AB']}

        for i in range(len(aveCR)):
            t = tk.Toplevel(self.master)
            # Frame for the plot
            plotFrame = tk.Frame(t)
            plotFrame.pack()

            f = plt.Figure()
            self.ax = f.add_subplot(111)
            self.canvas = FigureCanvasTkAgg(f,master=plotFrame)
            self.canvas.show()
            self.canvas.get_tk_widget().pack()
            self.canvas.mpl_connect('pick_event', self.onpick)

            # Plot
            lines = [0] * len(aveCR[i])
            for j in range(len(aveCR[i])):        
                X = range(0,len(aveCR[i][j]))
                lines[j], = self.ax.plot(X,aveCR[i][j],label=legend[i][j])
            leg = self.ax.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,ncol=2, borderaxespad=0.)

        self.lined = dict()
        for legline, origline in zip(leg.get_lines(), lines):
            legline.set_picker(5)  # 5 pts tolerance
            self.lined[legline] = origline


    def onpick(self, event):
        # on the pick event, find the orig line corresponding to the
        # legend proxy line, and toggle the visibility
        legline = event.artist
        origline = self.lined[legline]
        vis = not origline.get_visible()
        origline.set_visible(vis)
        # Change the alpha on the line in the legend so we can see what lines
        # have been toggled
        if vis:
            legline.set_alpha(1.0)
        else:
            legline.set_alpha(0.2)
        self.canvas.draw()



root = tk.Tk()
root.title("hem")
app = App(root)
root.mainloop()

【问题讨论】:

    标签: python matplotlib tkinter


    【解决方案1】:

    这可能主要是一个设计问题。我建议为每个绘图窗口使用一个类。然后App 类可以根据需要实例化尽可能多的绘图窗口,并提供相应的数据作为参数。每个绘图窗口都自行管理图例和事件。

    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    import numpy as np
    
    import Tkinter as tk
    
    class Plotwindow():
        def __init__(self, master, data, legend):
            t = tk.Toplevel(master)
            # Frame for the plot
            plotFrame = tk.Frame(t)
            plotFrame.pack()
    
            f = plt.Figure()
            self.ax = f.add_subplot(111)
            self.canvas = FigureCanvasTkAgg(f,master=plotFrame)
            self.canvas.show()
            self.canvas.get_tk_widget().pack()
            self.canvas.mpl_connect('pick_event', self.onpick)
    
            # Plot
            lines = [0] * len(data)
            for j in range(len(data)):        
                X = range(0,len(data[j]))
                lines[j], = self.ax.plot(X,data[j],label=legend[j])
            leg = self.ax.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,ncol=2, borderaxespad=0.)
            self.lined = dict()
            for legline, origline in zip(leg.get_lines(), lines):
                legline.set_picker(5)  # 5 pts tolerance
                self.lined[legline] = origline
    
        def onpick(self, event):
            # on the pick event, find the orig line corresponding to the
            # legend proxy line, and toggle the visibility
            legline = event.artist
            origline = self.lined[legline]
            vis = not origline.get_visible()
            origline.set_visible(vis)
            # Change the alpha on the line in the legend so we can see what lines
            # have been toggled
            if vis:
                legline.set_alpha(1.0)
            else:
                legline.set_alpha(0.2)
            self.canvas.draw()
    
    class App:
        def __init__(self,master):
            self.master = master
            # Frame for the training values
            buttonFrame = tk.Frame(master)
            buttonFrame.pack()
    
            self.buttonGenerate = tk.Button(master=buttonFrame,
                                            text='Train',
                                            command=self.train)
            self.buttonGenerate.grid(column=2,row=0)
    
        def train(self):
    
            aveCR = {0:{0:np.array([.582,1.081,1.507,1.872,2.180]),1:np.array([2.876,6.731,1.132,1.305,1.217])},
                1:{0:np.array([.582,1.081,1.507,1.872,2.180]),1:np.array([2.876,6.731,1.132,1.305,1.217])}}
    
            legend = {0: ['A', 'AB'], 1: ['A', 'AB']}
    
            self.windows = []
            for i in range(len(aveCR)):
                data = aveCR[i]
                self.windows.append(Plotwindow(self.master,data, legend[i]))
    
    
    root = tk.Tk()
    root.title("hem")
    app = App(root)
    root.mainloop()
    

    【讨论】:

    • 感谢您的帮助!
    猜你喜欢
    • 2018-03-25
    • 2019-08-07
    • 2013-07-23
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    • 2014-11-08
    • 1970-01-01
    相关资源
    最近更新 更多