dbader / schedule

Python job scheduling for humans.
https://schedule.readthedocs.io/
MIT License
11.8k stars 962 forks source link

How to stop a while loop in a scheduled job #203

Open ghost opened 6 years ago

ghost commented 6 years ago

Hello,

I am using a while loop to vary the brightness of my unicorn pHat controled by a raspberry pi zero.

The job is scheduled (using the schedule module) to start at a certain time, but I don't know how to stop it. I have tried to schedule another job to off the LEDs but since schedule runs jobs in series, it never gets to the ending program.

Thanks for your help, Charlotte

grandimam commented 6 years ago

If I'm able to understand the use case correctly. Then it's pretty simple.

Since the job function that you pass through the do method is blocking. You could after few variation effects just shutdown the setup within the job function itself.

You don't need another function to trigger the stop call. Since everything is running in the main thread it will be blocked until the job function completes the run.

ghost commented 6 years ago

Thanks for your answer.

The thing is that I would like that job to run until 6am let's say. But I don't know what kind of condition I could put in my "while" loop in the job function to make it stop at that time (is there a function to get the time from the pi? (To do for example put "while the time in before 6am, run the loop"). There must be since the schedule job is triggered by the time...

ghost commented 6 years ago

Maybe I'm not approaching the problem properly though.

I am making an alarm with light raising etc. I would like to run a program in the night for example, another one during the day and let's say blinking every hour.

I have started using schedule because it seemed the easiest but is there a way to run the blinking program while the day program is running for example? Or is there a better solution than schedule for this?

Thanks

jasonlcy91 commented 6 years ago

would like to know too. say, I've a job that needs to be run every 2 seconds but only for 10 times. How should we do that?

grandimam commented 6 years ago

@Chachapaslafrime and @jasonlcy91

So, this is how I'll approach the problem. Code snippet attached below:

You need to import few dependencies to make sure this works.

import schedule
import time
import datetime

Now, you write a wrapper function to control your application logic to suit the intended behavior i.e

def __runAlways():
    current_time = datetime.date.now()
    current_hour = current_time.strftime("%H")
    if current_hour == 6 #6am
        __startVariations()
    else if #turn_off_alarm_for_some_reason.:
        __stopVariations()
     print("current time is: " + current_time + " alarm not ready")

The wrapper function decides when to trigger the alarm or in my case the effect variations.

The __runAlways does two simple thing. Checks the current hour; Runs if it matches certain conditions.

def __startVariations():
    # Turn the switch ON. 
    # Do nothing.

 def __stopVariations():
   # Turn the switch OFF. 
   #  Do nothing.

__startVariations and __stopVariations is your main application logic that involves trigger the alarm and starting and stopping the variation effect.

 schedule.every(1).minutes.do(__runAlways)

 while True:
      schedule.run_pending()
       time.sleep(1)

The idea behind this approach is that let the scheduler run always. But, only start or stop the alarm if it matches the certain conditions.

Hope this approach helps...

grandimam commented 6 years ago

@Chachapaslafrime - Have you figured it out yet ? Else, let me know if you need any help.

As this is not entirely an schedule package issue and more of design/implementation one.

It seems not right to keep this issue open.

jasonlcy91 commented 6 years ago

I think I found a cleaner way

counter = 0
def some_job_with_condition(arg1, arg2):
    # do some work

    # stop the job
    global counter  #  this line is necessary to modify the global variable
    counter =+ 1
    if counter == 5:
        return schedule.CancelJob  #  use this to cancel this job

#  main code
job1 = schedule.every(15).minutes.do(some_job_with_condition, arg1, arg2)  #  assign Job instance to a variable

while job1 in schedule.jobs:   #  check if job still not cancelled
    schedule.run_pending()
    sleep(1)

If you used while True:, the program will loops infinitely. As a result if you open() a file for writing before the loop, the program will never write to the file as it will never end.

grandimam commented 6 years ago

The above implementation you've suggested solves the problem that you've posted. However, I would suggested few changes.