Plotting data in Tkinter with matplotlib - switchi

2020-04-20 20:50发布

问题:

I'm working on creating a program that utilizes Tkinter and matplotlib. I have 2 lists of lists (one for x-axis, one for y-axis) and I'm looking to have a button that can switch between the lists within the list. I took much of the code from the question Interactive plot based on Tkinter and matplotlib, but I can't quite get the button to work as I like. I'm quite new to using classes and having a bit of difficulty understanding them.

tft is the x-data tf1 is the y-data

Example of data:

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]]

My code below will graph one of the lists within a list, but won't switch between the lists within that list when I click the button. I've commented out other the methods I've tried. It always says that App has no attribute 'line' or 'canvas' when I use that. Like I said, I'm very new to classes and I'm trying to understand them better.

I've updated my code so that it now recognizes event_num and it prints the correct value whenever the button is pushed. However, the graph is not updating with the new data (i.e. it continues to only show the first data set instead of switching between lists). I believe the issue to be in the functions increase and decrease.I tried using the self.line, = ax.plot(tft[self.event_num],tf1[self.event_num],'.') and self.canvas.draw() but it's not working. I'm looking for that portion to be edited so that the graph will change.

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()

回答1:

The AttributeError: App instance has no attribute 'canvas' means that your code references the canvas attribute before it has been created/assigned.

This line:

self.button_left = Button(frame,text="< Previous Event",
                                    command=self.decrease(event_num))

is calling the decrease method because you used parentheses and provided arguments instead of just binding the handler. Inside the decrease method, you're accessing self.canvas to call the draw method.

That is happening before you create the canvas attribute, which happens on this line:

self.canvas = FigureCanvasTkAgg(fig,master=master)

Make event_num an attribute of the App object; then you won't have to pass arguments to the handler when you bind it. You can do this by assigning self.event_num = 1 inside __init__.