1
import tkinter,time

canvas=tkinter.Canvas(width=500,height=500,bg="white")
canvas.pack()

canvas.create_text(250,250,text="0:0:0",font="Arial 70 bold")

global b

def cl_e():
    b=False

def cl_s():
    h=0
    m=0
    s=0
    while b:
        if s<60:
            s+=1
            canvas.delete("all")
            time.sleep(1)
            canvas.create_text(250,250,text=str(h)+":"+str(m)+":"+str(s),font="Arial 70 bold")
            canvas.update()
        elif m<60:
            s=0
            m+=1
            canvas.delete("all")
            canvas.create_text(250,250,text=str(h)+":"+str(m)+":"+str(s),font="Arial 70 bold")
            canvas.update()
        else:
            s=0
            m=0
            h+=1
            canvas.delete("all")
            canvas.create_text(250,250,text=str(h)+":"+str(m)+":"+str(s),font="Arial 70 bold")
            canvas.update()



b=True

start=tkinter.Button(text="Start",command=cl_s())
end=tkinter.Button(text="End",command=cl_e())
start.pack()
end.pack()

As you can see, I am trying to start the cl_s() function only when the button "Start" is pressed and the cl_e() funkcion when the button "End" is pressed. The thing is the buttons do not appear in the canvas and the function cl_s() starts on its own. I wanted to achieve that when the "Start" button is pressed the timer will start and when the "End" button is pressed the timer will stop. I used a global variable "b" so when it is True the timer runs and when it is false the timer will stop, but it does not work. Without the "b" variable the button are visible, but with it they disappear.

5
  • 2
    You're missing tkinter.mainloop() at the end and a tkinter.Tk() at the start. Is there a particular reason for that? Are you new to the library? Commented Oct 29 at 14:36
  • in every if/elif/else you run canvas.delete("all") and canvas.update() so you could move it outside if/elif/else. You could run canvas.delete("all") before if/elif/else and canvas.update() after if/elif/else Commented Oct 29 at 14:57
  • if you want to put Button on Canvas then you should rather use canvas.createwindow(your_button) instead of .pack() Commented Oct 29 at 14:59
  • 1
    you have very common mistake - command= needs function's name without (). Went you press button then tkinter will use () to execute this function - so you need command=cl_s, command=cl_e. Maybe this makes problem because it may run cl_s() (and while-loop may block next lines) before it runs .pack() to show button. Commented Oct 29 at 15:01
  • 1
    by the way: every variable created outside function is already global and we use global b only inside function to inform it that it has to use external/global variable instead of creating local variable. Commented Oct 29 at 15:03

2 Answers 2

0

I didn't test it but real problem is command=cl_s() which is wrong.

command= needs function's name without () - command=cl_s - and when you press button then python will use () to run this cl_s.

At this moment it runs cl_s() at once and it runs while-loop which is blocking all code when you set b=True and it can't run .pack() to show button.

When you remove b=True then it skips loop while b: and it can run .pack() to show button.

If you add some print() inside cl_s() and inside while b: then you will see this.
If you add also print() after start = ... then you will see that this is not executed when you set b = True


Mininal working code (with mainloop() to work even without IDLE or Thonny)

But I would also use root.after(1000, function) instead of while-loop and sleep(1) and .update()

import tkinter as tk
import time  # PEP8: preferred to put every module in separate line

# --- classes ---  # PEP8: all classes after imports

# --- functions --- # PEP8: all functions after classes


def cl_e():
    global b  # inform function to use global variable instead of creating local one

    b = False


def cl_s():
    global b  # inform function to use global variable instead of creating local one

    b = True  # need it to run again after pressing `End` and `Start` again

    #print("running cl_s")
    h = 0
    m = 0
    s = 0
    while b:
        #print("running while b")
        if s < 60:
            s += 1
            time.sleep(1)
        elif m < 60:
            s = 0
            m += 1
        else:
            s = 0
            m = 0
            h += 1

        txt = f"{h:02}:{m:02}:{s:02}"  # format numbers with 2 digits, with leading zero

        canvas.delete("all")
        canvas.create_text(250, 250, text=txt, font="Arial 70 bold")
        canvas.update()


# --- main ---

# global b  # variables created outside functions/classes are global
b = True  # this is already global variable

root = tk.Tk()

canvas = tk.Canvas(root, width=500, height=500, bg="white")
canvas.pack()

canvas.create_text(250, 250, text="00:00:00", font="Arial 70 bold")

start = tk.Button(root, text="Start", command=cl_s)
#print("after start")
start.pack()

end = tk.Button(root, text="End", command=cl_e)
#print("after end")
end.pack()

root.mainloop()

PEP 8 -- Style Guide for Python Code


The same with root.after() instead of while-loop.

I have to change if/elif/else because function is repeated always after 1000ms, but original version was running sleep(1) only in some situations.

I also use counter = canvas.text(...) and canvas.itemconfig(counter, ...) to replace text in existing widget without deleting it and creating it again.

And I also use more readable names - running instead of b, clock_start instead of cl_s, and clock_end instead of cl_e

import tkinter as tk
import time  # PEP8: preferred to put every module in separate line

# --- classes ---  # PEP8: all classes after imports

# --- functions --- # PEP8: all functions after classes


def clock_end():
    global running  # inform function to use global variable instead of creating local one

    running = False


def clock_start():
    global running

    running = True

    # clear previous text
    canvas.itemconfig(counter, text="00:00:00")

    # start after 1000ms (1s) with h=0, m=0, s=0
    root.after(1000, counting, 0, 0, 0)


def counting(h, m, s):
    if running:

        s += 1

        if s == 60:
            s = 0
            m += 1

        if m == 60:
            m = 0
            h += 1

        txt = f"{h:02}:{m:02}:{s:02}"  # format numbers with 2 digits, with leading zero

        # replace text without creating widget again
        canvas.itemconfig(counter, text=txt)

        root.after(1000, counting, h, m, s)  # repeat after 1000 ms


# --- main ---

running = True  # this is already global variable

root = tk.Tk()

canvas = tk.Canvas(root, width=500, height=500, bg="white")
canvas.pack()

counter = canvas.create_text(250, 250, text="00:00:00", font="Arial 70 bold")

start = tk.Button(root, text="Start", command=clock_start)
start.pack()

end = tk.Button(root, text="End", command=clock_end)
end.pack()

root.mainloop()
Sign up to request clarification or add additional context in comments.

Comments

-1

Thank you all. The problem was that the term "global b" shoud have been at the begining of all the funkcions.

import tkinter,time

canvas=tkinter.Canvas(width=500,height=500,bg="white")
canvas.pack()

canvas.create_text(250,250,text="00:00:00",font="Arial 70 bold")

def cl_e():
    global b
    b=False
    clock()

def cl_s():
    global b
    b=True
    clock()

def clock():
    global b
    h=0
    m=0
    s=0
    while b:
        canvas.delete("all")
        if s<60:
            s+=1
            time.sleep(1)           
        elif m<60:
            s=0
            m+=1
        else:
            s=0
            m=0
            h+=1
        if h<10 and m<10 and s<10:
            canvas.create_text(250,250,text="0"+str(h)+":0"+str(m)+":0"+str(s),font="Arial 70 bold")
        elif h<10 and m<10 and s>=10:
            canvas.create_text(250,250,text="0"+str(h)+":0"+str(m)+":"+str(s),font="Arial 70 bold")
        elif h<10 and m>=10 and s<10:
            canvas.create_text(250,250,text="0"+str(h)+":"+str(m)+":0"+str(s),font="Arial 70 bold")
        elif h<10 and m>=10 and s>=10:
            canvas.create_text(250,250,text="0"+str(h)+":"+str(m)+":"+str(s),font="Arial 70 bold")
        elif h>=10 and m<10 and s<10:
            canvas.create_text(250,250,text=str(h)+":0"+str(m)+":0"+str(s),font="Arial 70 bold")
        elif h>=10 and m<10 and s>=10:
            canvas.create_text(250,250,text=str(h)+":0"+str(m)+":"+str(s),font="Arial 70 bold")
        elif h>=10 and m>=10 and s<10:
            canvas.create_text(250,250,text=str(h)+":"+str(m)+":0"+str(s),font="Arial 70 bold")
        else:
            canvas.create_text(250,250,text=str(h)+":"+str(m)+":"+str(s),font="Arial 70 bold")
        canvas.update()

start=tkinter.Button(text="Start",command=cl_s)
end=tkinter.Button(text="End",command=cl_e)
start.pack()
end.pack()

1 Comment

more important is command=cl_s without (). In original code you had it with () but here you show code without ()- but if you add again () in command=cl_s() then again it doesn't show buttons.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.