1

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()
Community
  • 1
  • 1
D. Gron
  • 59
  • 11

1 Answers1

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__.

Bennett Brown
  • 5,234
  • 1
  • 27
  • 35
  • If I use `self.event_num = 1` inside `__init__` will the value of `event_num` still be able to be changed (to switch between lists)? – D. Gron Oct 20 '15 at 14:24
  • I tried doing what you suggested, but it doesn't appear to let me change the value of `event_num` then – D. Gron Oct 20 '15 at 14:45
  • def decrease(): self.event_num -= 1 – Bennett Brown Oct 20 '15 at 17:38
  • It's getting very close to what I'm looking for. It is printing the correct `event_num` but now I need it to update the graph with each new `event_num` – D. Gron Oct 20 '15 at 17:59
  • Now that you've solved this question about the error you were getting, `no attribute canvas` or `no attribute line`, I encourage you to post another question. Include code and describe specific errant results or cut-and-pasted error. – Bennett Brown Oct 20 '15 at 22:26
  • Followed up with http://stackoverflow.com/questions/33262433/unable-to-update-tkinter-matplotlib-graph-with-buttons-and-custom-data – Bennett Brown Oct 22 '15 at 13:53