简体   繁体   中英

Countdown timer's speed increases as I press start and pause button one after another for few times in tkinter

I have a countdown script where seconds updates quickly as I press start and pause button few times one after another.

At first I thought clicking quickly between start and pause buttons speeds the seconds so I added time.sleep(1) to delay click between the buttons which didn't worked.

How can I fix this problem?

Is my algorithm correct? Or Is there other better way to make the countdown time? If yes, how can I do it?

CODE

from tkinter import *

PAUSE = False
HOUR, MINUTE, SECOND = 0, 0, 0


def start():
    '''Command for START button'''

    global PAUSE

    PAUSE = False
    start_button['state'] = 'disabled'
    pause_button['state'] = 'normal'
    reset_button['state'] = 'normal'
    # time.sleep(1) to delay time between clicks of pause and start buttons
    Counter()


def pause():
    '''Command for PAUSE button'''

    global PAUSE

    PAUSE = True
    start_button['state'] = 'normal'
    pause_button['state'] = 'disabled'


def reset():
    '''Command for RESET button'''

    global HOUR, MINUTE, SECOND, PAUSE

    PAUSE = True
    start_button['state'] = 'normal'
    pause_button['state'] = 'disabled'
    reset_button['state'] = 'disabled'
    Time['text'] = '00:00:00'

    HOUR, MINUTE, SECOND = 0, 0, 0


def Counter():
    '''Updating hour, minute and seconds'''

    global HOUR, MINUTE, SECOND

    if PAUSE is False:
        if SECOND == 59:
            if MINUTE == SECOND == 59:
                HOUR += 1

            if MINUTE == 59:
                MINUTE = 0

            else:
                MINUTE += 1

            SECOND = -1

        SECOND += 1

        Time.config(text='{}:{}:{}'.format(str(HOUR).zfill(2), str(MINUTE).zfill(2), str(SECOND).zfill(2)))
        root.after(1000, Counter)


root = Tk()
root.title('COUNTER')

width, height = 342, 108
pos_x = root.winfo_screenwidth() // 2 - width // 2
pos_y = root.winfo_screenheight() // 2 - height // 2

root.geometry('{}x{}+{}+{}'.format(width, height, pos_x, pos_y))

Time = Label(root, fg='Black', text='00:00:00', font=("Helvetica", 40))
Time.pack(side='bottom')

start_button = Button(root, text='START', font=("Arial", 16), fg='black', width=8, command=start)
start_button.pack(side='left')

pause_button = Button(root, text='PAUSE', font=("Arial", 16), fg='black', width=8, state='disabled', command=pause)
pause_button.pack(side='left')

reset_button = Button(root, text='RESET', font=("Arial", 16), fg='black', width=10, state='disabled', command=reset)
reset_button.pack(side='left', fill='both')

root.mainloop()

Your Counter function calls root.after(1000,Counter) each click and also modify the Time label instant upon clicking. When you click the buttons quick enough, you can schedule multiple root.after and also add up the seconds.

To modify your current script, you can keep track on the current action, and call root.after_cancel to pause the action.

from tkinter import *

PAUSE = False
HOUR, MINUTE, SECOND = 0, 0, 0
action = None #keep track on current action and also avoid click spam


def start():
    '''Command for START button'''

    global PAUSE, action

    PAUSE = False
    ...
    if not action:
        Counter()


def pause():
    '''Command for PAUSE button'''

    global PAUSE, action

    ...
    if action:
        root.after_cancel(action)
        action = None


def reset():
    '''Command for RESET button'''

    global HOUR, MINUTE, SECOND, PAUSE, action

    ...

    HOUR, MINUTE, SECOND = 0, 0, 0
    action = None


def Counter():
    '''Updating hour, minute and seconds'''

    global HOUR, MINUTE, SECOND, action

    if PAUSE is False:
        if SECOND == 59:
            if MINUTE == SECOND == 59:
                HOUR += 1

            if MINUTE == 59:
                MINUTE = 0

            else:
                MINUTE += 1

            SECOND = -1

        SECOND += 1

        Time.config(text='{}:{}:{}'.format(str(HOUR).zfill(2), str(MINUTE).zfill(2), str(SECOND).zfill(2)))
        action = root.after(1000, Counter)

root = Tk()
...
start_button = Button(root, text='START', font=("Arial", 16), fg='black', width=8, command=lambda: root.after(1000,start)) #to avoid the instant second increment

At first I thought clicking quickly between start and pause buttons speeds the seconds so I added time.sleep(1) to delay click between the buttons which didn't worked.

The problem is that because there is a delay before the .aftert() method calls back on itself, hit the start Button multiple times and create multiple loops of the .after(1000, counter) . meaning there are many .after() loops happening at once. To fix this you just need to make it so then the start button cannot be pressed if there is a .after() loop going on.

To do this you must change three of your functions

The counter() function

I have changed the code so then if the counter is running, then the start_button will be disabled, and when the counter stops running its state will be back to normal

def Counter():
    '''Updating hour, minute and seconds'''
    global HOUR, MINUTE, SECOND

    if PAUSE is False:
        start_button['state'] = 'disabled' # setting it to disabled if the counter is running
        if SECOND == 59:
            if MINUTE == SECOND == 59:
                HOUR += 1

            if MINUTE == 59:
                MINUTE = 0

            else:
                MINUTE += 1

            SECOND = -1

        SECOND += 1

        Time.config(text='{}:{}:{}'.format(str(HOUR).zfill(2), str(MINUTE).zfill(2), str(SECOND).zfill(2)))
        root.after(1000, Counter)
    else:
        start_button['state'] = 'normal' # setting it to normal if the counter stops

The pause() and reset() functions

You must remove the ability to set the state of the start_button to normal, because this will allow the start_button to be pressed before the last counter loop finished

def pause():
    '''Command for PAUSE button'''

    global PAUSE

    PAUSE = True
    pause_button['state'] = 'disabled'

def reset():
    '''Command for RESET button'''

    global HOUR, MINUTE, SECOND, PAUSE

    PAUSE = True
    pause_button['state'] = 'disabled'
    reset_button['state'] = 'disabled'
    Time['text'] = '00:00:00'

    HOUR, MINUTE, SECOND = 0, 0, 0

Updating these functions will remove this bug. Hope this is the answer you were looking for :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM