Python: threading.timer not respecting the interval - python

This is a followup to another question, to which I now have a solution but the implementation doesn't seem to be behaving properly for unrelated reasons.
I have the following code:
import time
import datetime
import threading
def scheduled_function(cycle):
cycle += 1
print "Cycle " + str(cycle) + " complete."
print "Next cycle at " + (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime("%l:%M%p")
threading.Timer(300, scheduled_function(cycle)).start() # New cycle every 5 mins
return
scheduled_function(1)
while(True):
command = raw_input()
print command
In general this seems to accomplish what I want - allowing the user to enter commands while in the background while a function is periodically called to do some sort of regular activity. However, the interval (300 in this case, which should equate to 5 minutes) does not seem to be doing anything, and the program reaches maximum recursion depth within a second or so. (Max recursion is not a problem for the actual script, as it likely won't be run for more than a few hours at a time).
How am I using threading.Timer wrongly?

That's because you are calling it right away and not letting the Timer call it for you.
threading.Timer(300, scheduled_function, args=[cycle,]).start()

Related

How to execute an if else statement in while loop without making the while loop stop?

Im trying to make a countdown timer with the feature to reset/stop/pause/resume. I tried implementing an if/elif/else statement to check for user input letter within the while loop while it continues looping. However, the loop stops running when the conditional statement executes. How do I implement my intended functionality into the code?
code:
import time
import datetime
def timeinput():
# Inputs for hours, minutes, seconds on timer
h = input("Enter the time in hours: ")
m = input("Enter the time in minutes: ")
s = input("Enter the time in seconds: ")
countdown(int(h), int(m), int(s))
# Create class that acts as a countdown
def countdown(h, m, s):
# Print options for user to do
print("Press w to reset \nPress s to stop \nPress a to pause \nPress d to resume")
# Calculate the total number of seconds
total_seconds = h * 3600 + m * 60 + s
# While loop that checks if total_seconds reaches zero
# If not zero, decrement total time by one second
while total_seconds > 0:
# Timer represents time left on countdown
timer = datetime.timedelta(seconds = total_seconds)
# Prints the time left on the timer
print(timer, end="\r")
# Delays the program one second
time.sleep(1)
# Reduces total time by one second
total_seconds -= 1
user=input()
if user=="w":
total_seconds=0
print("Timer will now reset")
timeinput()
print("Bzzzt! The countdown is at zero seconds!")
timeinput()
result:
outcome of code
As shown in the provided image, the loop stops at 20 secs. My intended action here was for the countdown to continue until the user presses the W key, which shall then reset the countdown back to its original set time at initialization.
Soo, I'm also kinda new to programming and python so I don't exactly how to make a timer with that functionality. What is happening is that when you use the input() function the code stops and waits for the user input.
I know you can use some libraries, like pygame, in order to check for user input inside the loop without stopping it, but without one I'm not sure how to do it.
You can also use the module keyboard, that comes inside python, and I think is great for the problem you shared. Check this post How to detect key presses?
The command input() is blocking. It waits until it get something from the console. Maybe take a look at this post and try to implement a non blocking version of input. Non-blocking console input?
The problem is that input is blocking - that is, it will stop the execution until the user presses < enter> .
Antique running environments, on 8 bit computers, using Basic, could check "what the user is pressing right now", without waiting for a confirmation with "enter" using the inkey command.
In Python programs running on terminal, terminedia, a 3rdy party library, implements inkey() as it was - but it needs the terminal input to be reconfigured for which it provides a "keyboard" context manager.
You can then run almost the same code you have -just install terminedia in your Python environment with "pip install terminedia"
import terminedia as TM
...
def countdown(h, m, s):
...
while total_seconds > 0:
# Timer represents time left on countdown
timer = datetime.timedelta(seconds = total_seconds)
# Prints the time left on the timer
print(timer, end="\r")
with TM.keyboard:
# Delays the program one second
time.sleep(1)
# Reduces total time by one second
total_seconds -= 1
user=TM.inkey()
if user=="w":
total_seconds=0
print("Timer will now reset")
timeinput()
print("Bzzzt! The countdown is at zero seconds!")
timeinput()
(disclaimer: I am terminedia author)
Taking inputs from the answers received here and subsequent follow-up questions on stack overflow and Reddit, this question has been solved using pygame. I'll provide the link below for future reference to this question.
problem solution

Function that substracts a certain number every second on Python

I have been wondering if there is a way to create a function that subtracts one number from another but only at a given amount of time. I have read the time library, but I am still trying to figure it out. For example:
def TimeFunction:
t = 60
#What should I need to put here so for every second that passes, it subtracts 1?
This is what you are literally asking for:
import time
def TimeFunction():
t = 60
while True:
time.sleep(1)
t -= 1
Although this is not very satisfying, because your program blocks on sleep() and nothing else would be able to see the value of t.
Do you want to modify your question now and ask for something slightly different?

how to terminate python script using while loop and get the result?

i would like to generate random numbers for 5 minutes. after that, i would like to know the most common numbers.
my question is:
I am not sure about how much time I need. Can i MANUALLY terminate the script at 1 minute or 1 minute 19 seconds and get the result
my code is as follows:
import random
from collections import Counter
t_end = time.time() + 60*5
numbers = []
while time.time() < t_end:
number=''.join(map(str, [random.randint(0,9)for value in range(0,4)]))
numbers.append(number)
print(Counter(numbers))
There are a myriad number of options:
print an intermediate result every 5s or so and abort the script with ctrl+C
check if space or something else is pressed, if so, abort the loop (see here for how to)
listen to signals and handle accordingly (this could also gracefully terminate the script for option 1)
open a socket and listen to a socket if a stop signal comes in
create a gui with TKinter or what and add a stop button
...
The first one is the easiest but also the most quick&dirty one. For the second option, the following script should get you started (your script was working for me after adding whitespaces and linebreaks):
import keyboard
while True:
if keyboard.read_key() == "p":
print("You pressed p")
break
Assuming python3 and windows: Note that keyboard is not shipped with python on default. You need to install it with pip3 install keyboard. Pip3.exe might not be on your path. cd to where you installed python and look around. For me it was in the Scripts-folder (my installation is pretty messed up though).
import random
from collections import Counter
import time
t_end = time.time() + 60*5
numbers = []
while time.time() < t_end:
number=''.join(map(str, [random.randint(0,9)for value in range(0,4)]))
numbers.append(number)
if time.time()==(t_end-60*4): # break a 1 min
break
print(Counter(numbers))
As luigigi already mentioned in the comment, you could simply use the number of iterations as a limit, and use a for-loop:
number_of_iterations = 10000
for i in range(number_of_iterations):
place_your_function_here()
However, if you for some reason really want to use the time, here is how you can do it: Update the current time at the bottom of your while block, and in the continuing condition of the while loop, compare it to the finish time:
import datetime
delta_in_seconds = 60
finish = datetime.datetime.now() + datetime.timedelta(0, delta_in_seconds)
while time < finish:
place_your_function_here()
time = datetime.datetime.now()

How to make Tkinter python script take variable every X seconds?

First of excused my poor coding skills. Just started learning about a week ago
Anyway, I'm trying to right now create a GUI for this weather/clock thing for a project. Right now I need it so that the GUI, I was thinking of using Tkinter, updates every second and basically pulled strings from this program. Currently, the program calculates the time of sunrise and sunset and lunar cycle.
I tried two ways so far, the first method, running the entire program under one script, but it doesn't work due to the mainloop() prevent the calculation from repeating itself for the next day.
So now I was hoping to separate the program into two parts. One with the calculations and the other as the main GUI program. So what I did was basically.
Simplified Calculation Script (Its very long so I write the important part):
its day.py
import datetime
import math
import schedule
import astral
a = Astral()
def tick():
d = datetime.datetimee.today()
second = d.second
minutes = d.minute
hour = d.hour
month = d.month
print(str(hour) + ":" + str(minute) + ":" + str(second))
def mooninfo():
global percent_illuminate
global moon_phase
moon_phase = a.moon_phase(date=datetime.date(year,month,day)
percent_illuminate = VERY LONG EQUATIONS
print("Moon is at... " + str(moon_phase))
print(percent_illuminate)
schedule.every(1).second.do(tick)
schedule.every().day.do(mooninfo)
while True:
schedule.run_pending()
And for the tkinter script I tried
from day import moon_phase
Instead of just importing the moon_phase it started to run the program printing out the time of day nonstop.
So if anyone could help me
Let the tkinter script update everytime the different variable, second, minutes, hour, day, month, and moon_phase changed.
Or is there a way to get around the mainloop() of tkinter and just make it run while True: loop too?
Or any other better way to do it.
I saw that you can let Tkinter track the time making a digital clock of the sort but I need the calculations and also I am also sending variables over to an Arduino too (But I already figured that out).
Any help is greatly appreciated, thanks
You're importing a variable from a function. I think you should be importing the class (restructure day.py as a class first), then calling the mooninfo() method to return the value of the moon_phase variable.

How to use a function when the current time equals another time: Python

I am developing a script that utilizes a function I made to control the relays of an 8 channel relay board on a Raspberry Pi 3. The function works, and calling the function works. I am trying to develop this script so when the current time equals another time, such as Zone 1 start time, the relays turn on/off depending on the status that is received by another part in the code.
I have tested it without this time equals part, and everything works. I seem to be running into some problems when I add this level of complexity. Here is a sample of my code:
while True:
from datetime import datetime
import time
import smbus
ValveStatus='00000001' #0 is closed, 1 is open.
R1_1,R2_1,R3_1,R4_1,R5_1,R6_1,R7_1,R8_1=list(map(int, ValveStatus))
currenttime=datetime.today().strftime('%Y-%m-%d %H:%M:%S')
Z1S_Timestamp='2018-07-09 10:25:11'
if(currenttime==Z1S_Timestamp):
if(R8_1==1):
SetRelayState(BoardOne,8,"ON")
else:
SetRelayState(BoardOne,8,"OFF")
No matter how many times I changed the code, it will never work with this timing method. It never enters the loop and therefore the relay never opens. Is there a better way to do this rather than simply having if equal to statements? I am open to editing it, but the relays still need to open around the time of the start time. I think a margin of 1 or 2 minutes is okay, since timing it exactly equal is not 100% necessary.
Would something like:
currenttime= '2018-07-09 12:53:55' #hard coding just for example purposes
if('2018-07-09 12:52:55' <= currenttime <= '2018-07-09 12:54:55'):
do the things
Be a more valid/correct/pythonically correct method?
Sure - I would do the opposite though - convert all times to datetime() objects and use those for comparison:
TIME_MARGIN = datetime.timedelta(seconds=120) # use a margin of 2 minutes
time_compare = datetime.datetime(2018, 7, 9, 12, 52, 55)
current_time = datetime.datetime.now()
if (time_compare - TIME_MARGIN) < current_time < (time_compare + TIME_MARGIN):
#do something

Categories