How to use datetime to calculate duration and stop time? - python

I'm using Python 2.7 and want to use the datetime functions to calculate a user defined time to run a small pump. I know I'm missing something obvious but I've searched until I can't see straight. I have a textCtrl that the user will enter the minutes he wants the pump to run. Then a button click will start the scheduler and check to see if it's time to turn off the pump:
def timedPump(val):
now = datetime.datetime.now()
timeOff = now + datetime.timedelta(minutes=int(val))
if timeOff > now:
print 'pumpOn'
else:
print 'pumpOff'
return True
The problem is that datetime.datetime.now() updates everytime. How do I make it so that now = the time that the button was clicked?

This would be my solution to the problem:
from time import sleep
def pumpcheck(timer_in_minutes):
time_clicked = datetime.datetime.now()
now = time_clicked
timeDiff = datetime.timedelta(minutes=int(timer_in_minutes))
while (time_clicked + timeDiff > now):
print 'pumpOn'
now = datetime.datetime.now()
sleep(2)
print 'pumpOff'
It saves the time when the button was pushed and then checks in a loop, if the time is rfeached, if not, it update the current time, says pump is still on and sleep for a little while to not bloack cpu recources. When the time is reached, it says pumpoff

There is some information missing as to how timedPump is evaluated. I guess it's a simple loop.
What needs to happen is there have to be two functions, one that sets a turnOff-time and one that evaluates weather to turn on / off the pump.
This can be implemented as two methods of a class:
class PumpControl(object):
def setTime(self, val):
now = datetime.now()
self.timeOff=now + timedelta(minutes=int(val))
print 'pumpOn'
def checkTime(self):
if self.timeOff < datetime.now():
print 'pumpOff'
This way you would create a PumpControl object and then repeat checkTime:
controller = PumpControl()
#inside your loop
controller.checkTime()
#when you want to set the time
controller.setTime(val)
This should work across threads as well, so you can have one thread repeating and another asking for the time.

This sounds like something outside the scope of a single function. Either split the logic into two functions, or use a class to preserve start_time

Related

How to schedule a task once a week from a date decided by the user with tkinter?

I need to download some files weekly at a time and day specified by the user as shown in the screenshot.
I am struggling with integrating an inner loop with respect to root.mainloop().
So far I have tried with .after and with a while structured like this:
def scaricaAuto(myDay, myHour, myMinute):
while True:
day = datetime.datetime.now().strftime("%A")
hour = datetime.datetime.now().strftime("%H")
minute = datetime.datetime.now().strftime("%M")
if day == myDay and str(int(hour)) == myHour and str(int(minute)) == myMinute:
DownloadButton.invoke()
break
root.after(60000, scaricaAuto(clickedDays.get(), HourSpin.get(), MinSpin.get()))
Using only .after leads to Python: maximum recursion depth exceeded while calling a Python object and the while loop doesn't behave properly, when the if is true it gets stucked there and keeps invoking DownloadButton.
Also OS X shows my script to be "not responding" when a button function last longer than a few seconds, is there a solution for this to?
Thanks in advance to those that took the time to read my question and might be able to help me!
There are a couple of problems in your code. First, you need to remove while True. That serves no purpose. Tkinter is already running an infinite loop by way of mainloop.
Second, after requires a function. You're calling a function and passing the result to after.
In other words, this:
root.after(60000, scaricaAuto(clickedDays.get(), HourSpin.get(), MinSpin.get()))
... is functionally identical to this:
result = scaricaAuto(clicedDays.get(), HourSpin.get(), MinSpin.get())
root.after(60000, result)
Instead, you either need to pass scaricaAuto as an argument, or you need to create a new function that calls your function. In the case of the former, you can pass arguments to the function by passing those arguments to after.
For example,
root.after(60000, scaricaAuto, clickedDays.get(), HourSpin.get(), MinSpin.get())
Note that this calls the .get() functions immediately rather than waiting the full 60 seconds.
A better solution is to not pass any arguments to scaricaAuto and instead have it call the .get() methods right when it needs the values:
def scaricaAuto():
myDay = clickedDays.get()
myHour = HourSpin.get()
myMinute = MinSpin.get()
now = datetime.datetime.now()
day = now.strftime("%A")
hour = now.strftime("%H")
minute = now.strftime("%M")
if day == myDay and str(int(hour)) == myHour and str(int(minute)) == myMinute:
DownloadButton.invoke()
root.after(60000, scaricaAuto)
The above code will run for as long as the GUI itself is running, assuming you've called mainloop at some point.

how can i trigger an action when a variable change it value in this case time?

I need to detect when the minutes of the clock/time change and do something,
This is mi code so far, for the clock but still can figuruate out in python how to detect the value has change and do action after. Any help will be apreciated i come from a c++ backgorund my implementations seems so far not working.
while True:
now = datetime.now()
print(now.strftime("%M), end = " ", flush = true)
time.sleep(1)
currentMin = now.srtftime("%M")
that worked for me:
from datetime import datetime
import time
past_min = None
while True:
#current min
now_min = int(datetime.now().strftime("%M"))
#first iteration
if not past_min:
past_min = now_min
if now_min != past_min:
#call your function here
print("Min change detected")
past_min = now_min
#print the seconds
print(datetime.now().strftime("%S"))
time.sleep(1.5)
I think you can create a class (in the below example Minute) with a property currenMin to store the current minute value. By using #<property>.setter function, when the property <property> is changed, it will trigger the setter function
from datetime import datetime
import time
class Minute(object):
def __init__(self):
self._currenMin = ''
#property
def currentMin(self):
return self._currenMin
#currentMin.setter
def currentMin(self, value):
if value != self._currenMin:
# ACTION CODE BELOW
print('Minute changed')
self._currenMin = value
minute = Minute()
while True:
now = datetime.now()
print(now.strftime("%M"), end=" ", flush = True)
time.sleep(1)
minute.currentMin = now.strftime("%M")
Well, for the general case with simple variables, you can't simply do it. There are two simple options to do something similar:
if you control EVERYTHING that writes it, make them trigger that action
write code that regularly checks it and triggers the action when it changes
use language tools like a custom setter (see #user696969's answer)
The first case needs you to control everything that could modify that value. At that point, you might not even need a variable, and just pass the new value (and you can reverse this by having a variable that is always updated). This is a very common pattern, called Event-driven programming, and heavily used for example in UIs, websites (client-side, see a list of DOM events for example) and game frameworks (see pygame's documentation on events)
The second-case of writing a loop or checking it regularly can also work, however, there are some downsides to it as well. You probably don't want to write an infinite loop waiting for it to change, especially not in a way that also blocks the changing of that variable, and thus dead-locking the entire program as it's preventing something it's waiting for. If you just check it regularly between other, it might be hard to ensure it will be checked regardless of what else is the program doing. You might use multiple threads for it, but that brings it's own set of problems. You also have to store and update the previous value, so you can compare it. This might be slow or memory-consuming if the variable holds too much data.
You can also use language tools with custom setters. This is clean, but can not be used for any variable, just for class attributes, so you still need some control over the rest of the program.
Generally I'd use the event-driven approach or the setter one, depending on the wider context. However, for such simple cases, the checking is also fine. The simplest solution might event be to remove the need for this entirely.

How to update a timestamp taken from server?

I have a very simple Python script in which I'm trying to print an updated timestamp (taken from a server by APIs) every n seconds.
After importing a custom module (FinexAPI) and the time module:
import FinexAPI
import time
and setting up the variable to get the server timestamp:
ticker = FinexAPI.ticker()
when = float(ticker['timestamp'])
if I perform:
print when
I'm getting the timestamp up to date. If I perform it again, I can see a new updated timestamp. Untill now there's no problem at all.
Now let's suppose I need to perform an "updated timestamp print" every 5 seconds:
def getNewTimestamp():
print when
time.sleep(5)
while True:
getNewTimestamp()
But with this code I'm getting the same timestamp every 5 seconds. I suppose the problem is that I'm defining when outside the getNewTimestamp function, so it basically keeps printing the same non-updated timestamp. But even if I define it inside the function I still get no update on the timestamp.
Another thing I'm thinking about is that while loop is not the best choice in this scenario, but that would be another story (I think...). Can someone help me figuring out what am I doing wrong and what is the best way to obtain and print the updated timestamp every 5 seconds?
You have to call the API in the loop:
def getNewTimestamp():
ticker = FinexAPI.ticker()
when = float(ticker['timestamp'])
print when
while True:
getNewTimestamp()
time.sleep(5)

End a Python function after a certain time?

For example, I have this function:
some_global_varible = ""
def bob(some_argument, some_more):
import time
for x in range(10):
print("Working")
time.sleep(1)
return "I finished and this is my result"
How can I run this function for certain amount of time, and if it's not finish in the time end it. But also if it finish get the output of the return (if the is one, because maybe there is none and it simply end).
Also this won't be run in Unix, because I had see some example for Unix that don't work in Windows. Also if you could change the global variable form inside the function it would be perfect, but that as a extra.
First the rest and if it possible (If it not possible to detect an end without the return it doesn't matter, I can include a return just fro that. Like: return "This is just for ending")
EDIT:
The "bob" funtion is an example for a function that takes 10 seconds, but the plan is to use it on other functions, that can take an undefined amount of time. And that function can't be edited to add simply something to stop it at some time seconds.
A better example could be "pep" as it wouldn't end by itself.
def pep():
import time
while True:
print("Working")
time.sleep(1)
You can use time.time() to get the current time in seconds before you start your loop and then at the end of each for loop, check how long has passed since then and if it's too long then you can return.
def bob(some_argument, some_more):
import time
start = time.time()
for x in range(10):
print("Working")
time.sleep(1)
if time.time() - start > 5:
return "I took too long"
return "I finished and this is my result"
Obviously replace 5 with the time you want to use, but this code gives me this output:
Working
Working
Working
Working
Working
'I took too long'
You may use the break;
For example if the value of the loop counter meets a following condition like this:
if condition:
break;
this will surely end the loop.

How to run a python script at a specific time(s)

I'd like to write a simple python script for doing a specific job. I'm getting some time and link information from a web site.
times=
[
('17.04.2011', '06:41:44', 'abc.php?xxx'),
('17.04.2011', '07:21:31', 'abc.php?yyy'),
('17.04.2011', '07:33:04', 'abc.php?zzz'),
('17.04.2011', '07:41:23', 'abc.php?www'),]
What is the best way to click these links at the right time? Do I need to calculate the time interval between the current and the one in list and sleep for a while?
I'm really stuck at this point and open to any ideas which could be useful.
Take a look at Python's sched module.
you can use schedule module and it is easy to use and will satisfy your requirement.
you can try something like this.
import datetime, schedule, requests, time
TIME = [('17.04.2011', '06:41:44', 'abc.php?xxx'),
('17.04.2011', '07:21:31', 'abc.php?yyy'),
('17.04.2011', '07:33:04', 'abc.php?zzz'),
('17.04.2011', '07:41:23', 'abc.php?www')]
def job():
global TIME
date = datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S")
for i in TIME:
runTime = i[0] + " " + i[1]
if i and date == str(runTime):
requests.get(str(i[2]))
schedule.every(0.01).minutes.do(job)
while True:
schedule.run_pending()
time.sleep(1)
I have use request module and get method to call those URL. You can write whichever methods suits you.
This may help: How do I get a Cron like scheduler in Python? It is about cron-like scheduling in Python. And yes, it is based on sleeps.
I finally made and used this.
import time,datetime
def sleep_till_future(f_minute):
"""The function takes the current time, and calculates for how many seconds should sleep until a user provided minute in the future."""
t = datetime.datetime.today()
future = datetime.datetime(t.year,t.month,t.day,t.hour,f_minute)
if future.minute <= t.minute:
print("ERROR! Enter a valid minute in the future.")
else:
print "Current time: " + str(t.hour)+":"+str(t.minute)
print "Sleep until : " + str(future.hour)+":"+str(future.minute)
seconds_till_future = (future-t).seconds
time.sleep( seconds_till_future )
print "I slept for "+str(seconds_till_future)+" seconds!"

Categories

Resources