Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python Start and Stop

Asif Choudhury
Asif Choudhury
2,086 Points

AttributeError: 'Pymodoro' object has no attribute 'stop_button'

When I am running the program, I am getting the following error

in start_timer self.stop_button.config(state=tkinter.NORMAL) AttributeError: 'Pymodoro' object has no attribute 'stop_button'

I think part of the reason of this is, self.start_button and self.stop_button has been used in 2 methods (start_timer() and stop_timer()) outside their parent method: build_buttons().

So, Python 3 interpreter is not recognizing the attributes.

What to do about it?

Thanks in advance!

3 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,154 Points

The issue is in the build buttons code. As shown in the stacktrace below, build_buttons is calling self.start_timer which references the stop_button that has not yet been build by the build_buttons code.

C:\Users\Chris\Documents\Treehouse>python pomodoro.py
Traceback (most recent call last):
  File "pomodoro.py", line 90, in <module>
    Pymodoro(root)
  File "pomodoro.py", line 21, in __init__
    self.build_buttons()
  File "pomodoro.py", line 59, in build_buttons
    command=self.start_timer()
  File "pomodoro.py", line 80, in start_timer
    self.stop_button.config(state=tkinter.NORMAL)
AttributeError: 'Pymodoro' object has no attribute 'stop_button'

The specific issue is that tkinter.Button command argument takes a callback function. A callback is a function that will be called when the button is pushed. So a function is needed, not the execution of a function. So leave off the parens.

        self.start_button=tkinter.Button(
            buttons_frame,
            text='Start',
            command=self.start_timer  # <-- no parens
        )

        self.stop_button=tkinter.Button(
            buttons_frame,
            text='Stop',
            command=self.stop_timer  # <-- no parens
        )

That corrects the compile error. The next step is to figure out why the timer value is not being displayed.

See description of tkinter callbacks docs.

I'll leave that to you to work on. Post back if you need more help. Good luck!!

Asif Choudhury
Asif Choudhury
2,086 Points

Here is my code

import tkinter

DEFAULT_GAP=60*25

class Pymodoro:

    def __init__(self,master):

        self.master=master
        self.mainframe=tkinter.Frame(self.master, bg="white")
        self.mainframe.pack(fill=tkinter.BOTH, expand=True)

        self.timer_text=tkinter.StringVar()
        self.time_left=tkinter.IntVar()
        self.time_left.set(DEFAULT_GAP)
        self.running=False


        self.build_grid()
        self.build_banner()
        self.build_buttons()
        self.build_timer()

    def build_grid(self):

        self.mainframe.columnconfigure(0,weight=1)
        self.mainframe.rowconfigure(0,weight=0)
        self.mainframe.rowconfigure(1,weight=1)
        self.mainframe.rowconfigure(2,weight=0)

    def build_banner(self):

        banner=tkinter.Label(
            self.mainframe,
            bg='red',
            fg='white',
            text='Pymodoro',
            #font info is a tuple
            font=('Helvetia',24)
                )
        banner.grid(
            row=0, column=0,
            #stickiness is directional info: east, west, north, south
            #the row cannot be stretched north south
            # it can only be expanded east west
            sticky='ew',
            padx=10, pady=10
        )

    def build_buttons(self):
        buttons_frame=tkinter.Frame(self.mainframe)
        buttons_frame.grid(row=2, column=0, sticky='nsew', padx=10, pady=10)
        buttons_frame.columnconfigure(0, weight=1)
        buttons_frame.columnconfigure(1,weight=1)

        self.start_button=tkinter.Button(
            buttons_frame,
            text='Start',
            command=self.start_timer()
        )

        self.stop_button=tkinter.Button(
            buttons_frame,
            text='Stop',
            command=self.stop_timer()
        )

        self.start_button.grid(row=0, column=0, sticky='ew')
        self.stop_button.grid(row=0,column=1,sticky='ew')
        self.stop_button.config(state=tkinter.DISABLED)

    def build_timer(self):
        timer=tkinter.Label(self.mainframe, text=self.timer_text.get(),font=('Helvetica',36))
        timer.grid(row=1,column=0,sticky='nsew')


    def start_timer(self):
        self.time_left.set(DEFAULT_GAP)
        self.running=True
        self.stop_button.config(state=tkinter.NORMAL)
        self.start_button.config(state=tkinter.DISABLED)

    def stop_timer(self):
        self.running=False
        self.stop_button.config(state=tkinter.DISABLED)
        self.start_button.config(state=tkinter.NORMAL)

if __name__=='__main__':
    root=tkinter.Tk()
    Pymodoro(root)
    root.mainloop()

[MOD: added ```python formatting -cf]

Asif Choudhury
Asif Choudhury
2,086 Points

Thanks a bunch for your detailed answer. It surely was helpful to identify the rookie mistake I was making.

Best Regards.