简体   繁体   中英

How can I input a time and have the timer count down? Python

I am trying to run a while loop so I can run code while the timer is still counting down.I also want to see the timer as it counts down. I have tried to find something similar on stack overflow, but haven't been able to get the result I'm looking for.

Python

print("Input minutes and seconds")
min = int(input("Minutes: "))
sec = int(input("Seconds: "))

while min & sec > 0:
    # do some code
    # I want the program to count down from whatever I input
    print("")

You should run your timer on a different thread. If you don't need the timer to affect the main code being run this should work:

import threading
import time

def timer_function(seconds):
    '''Countdown from number of seconds given'''
    for t in range(seconds, -1, -1):
        time.sleep(1)
        print(t)


if __name__ == "__main__":
    print("Input minutes and seconds")
    min = int(input("Minutes: "))
    sec = int(input("Seconds: "))

    x = threading.Thread(target=timer_function, args=(min * 60 + sec,))
    x.start()

    # Run some other code in parallel with the timer

    x.join() # will wait for the timer function to finish
    print('All done')

If you need the timer to stop the main thread (the code being run on the main function) then you need send some signal through a variable from the timer thread. There might be some libraries that handle thread timeouts better if you would like to look it up:)

Getting very precise timeout timing is somewhat troublesome, as the operation of reporting on the timing can affect the timing itself if not carefully written.

Something like this is often best accomplished with two threads in parallel

Making the threads "daemon threads" allows you to end them by quitting the program (otherwise the program will wait for them to be .join() ed)

You can rely on threading.Event() to clearly communicate into a thread and provide a .wait() method, which will either wait for a given timeout or immediately end when the event .is_set()

import threading 
import time

def function_to_call():  # replace me with your function
    time.sleep(1000)

def timer_fn(timeout, event, delay=5):  # 5 seconds between reports
    time_end = time.time() + timeout
    while True and not event.is_set():
        time_remaining = int(time_end - time.time())
        if time_remaining <= 0:
            return
        print(
f"{int(time_remaining)}s remaining")
        event.wait((min(delay, time_remaining)))  # wait for event

timeout = int(input("minutes: ") or 0) * 60 + int(input("seconds: ") or 0)

E = threading.Event()  # Event to kill t2
# making t1 a daemon lets it not prevent shutdown
t1 = threading.Thread(target=function_to_call, daemon=True)
t2 = threading.Thread(target=timer_fn, args=(timeout, E), daemon=True)

# begin both threads
t1.start()
t2.start()
# wait for t1 to exit (but not t2)
t1.join(timeout=timeout)
# t1 may join faster by ending, otherwise reach here after timeout
E.set()  # set the Event to quickly end t2 (timeout or t1 returned)
t2.join()  # not technically necessary, but cleans up t2

# program quits and t1 is killed if it's still running

Note that the timing display is actually separate from the thread ending, and ending the function early is done here by stopping the program. The function will continue to run if the program is not stopped!

If more advanced task control is needed, consider

  • modifying your callable function to repeatedly check another threading.Event throughout it to see if it's time is up (yet)
  • use multiprocessing , which is much more featureful than threading , though it may need to copy memory into the new process, which can be slow if you have a tremendous amount
  • use subprocess , creating a script just for the purpose of a timed end (here you can kill the subprocess with a simple .kill() or setting a timeout= argument when creating it! (though again you'll find some inefficiency in copying/streaming input into and out of the new process)
  • use os.fork() and [ os.kill()](https://docs.python.org/3/library/os.html#os.kill) to split your process (advanced, but usually much more efficient than multiprocessing` due to how memory is (or rather is not) copied)
  • if your function can be made asynchronous , which allows multiple tasks to collaborate in the same namespace similar to threading , but in a more friendly way (though how this behaves is fundamentally different from the other techniques given, it can be very efficient if you have many tasks which don't rely on local resources, such as a webserver or database)

Though further fundamentally different, you may find further or more benefit in designing your task to be a collection of work to iterate over (maybe in a list) and consider a library like tqdm to report on its status

You should ideally use threads and spawn a daemon to keep track, but that might be overkill for your case. I've made a more simple implementation which is hopefully understandable, otherwise please ask and I'll improve the comments/explanation:

import time

set_time = int(input('For how many seconds do you want to run this?'))
start_time = time.time()  # lets get the current time
inner_time = time.time()  # Seperate vars so we don't overwrite the main loop 
count = 0 #To keep track of how many seconds we've been going

while (time.time() - start_time) < set_time: #Lets run this until we've reached our time
        if(time.time() - inner_time) >= 1: #If 1 sec has passed
            inner_time = time.time() #Reset time
            count += 1 #Increase second by 1  
            print("Code has been running for "+str(count)+" seconds") #Inform the user

        #Do something here...
            

print(str(set_time)+" seconds have now elapsed") #Done and dusted

This is the output:

For how long do you want to run this?5
Code has been running for 1 seconds
Code has been running for 2 seconds
Code has been running for 3 seconds
Code has been running for 4 seconds
5 seconds have now elapsed

Try this,

Code:

import time

print("Input minutes and seconds")
min = int(input("Minutes: "))
sec = int(input("Seconds: "))
t = min*60 + sec

while t : 
    mins, secs = divmod(t, 60)
    timer = '{:02d}:{:02d}'.format(mins, secs)
    print(timer, end="\r")
    time.sleep(1)
    t -= 1

print('Timer ended !!')

Output:

Input minutes and seconds
Minutes: 1
Seconds: 5
01:05
01:04
01:03
01:02
01:01
01:00
00:59
00:58
.
.
.
00:01
Timer ended !!

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