I'm a beginner in python and in my tkinter, I have an option menu with a total of 4 options.
# create tk variable
self.timervar = tk.StringVar(root)
# dropdown dictionary
self.timerDict = {"-", "5 seconds", "10 seconds", "15 seconds"}
self.timervar.set("-") # <-- set the default value
# timer dropdown menu
self.timer_option = tk.OptionMenu(root, self.timervar, *self.timerDict, command=self.req_timer)
self.timer_option.grid(row=1, column=3, columnspan=2, padx=3, pady=3)
Based on the option, a function will run at every x minutes. When I switch to another option, the apscheduler job for the function is removed and a new job will start.
def req_timer(self, option):
scheduler = apscheduler.schedulers.background.BackgroundScheduler()
if option == "15 minutes":
if 'job' in locals():
job.remove_job('option_timer')
job = scheduler.add_job(self.req_client, 'cron', second='*/15', id='option_timer')
scheduler.start()
else:
job = scheduler.add_job(self.req_client, 'cron', second='*/15', id='option_timer')
scheduler.start()
elif option == "10 minutes":
if 'job' in locals():
job.remove_job('option_timer')
job = scheduler.add_job(self.req_client, 'cron', second='*/10', id='option_timer')
scheduler.start()
else:
job = scheduler.add_job(self.req_client, 'cron', second='*/10', id='option_timer')
scheduler.start()
elif option == "5 minutes":
if 'job' in locals():
job.remove_job('option_timer')
job = scheduler.add_job(self.req_client, 'cron', second='*/5', id='option_timer')
scheduler.start()
else:
job = scheduler.add_job(self.req_client, 'cron', second='*/5', id='option_timer')
scheduler.start()
elif option == "-":
if 'job' in locals():
job.remove_job('option_timer')
else:
pass
But in my case once the apscheduler job starts running, it doesn't stop even when I switch to a different option which should have removed the job. It stacks the job. So if I select the 5 seconds
option, it would run normally. But if I switch to the 10 seconds
option, it will run the 10 seconds
option on top of the 5 seconds
option. Since both options uses the same function, it will give me the same results twice when the 5 and 10 seconds coincides with each other.
I've tried this with and without the job id with the same results.
Connecting to port... # <-- 5 seconds option
Successfully connected to port 9998
Successfully connected to port 9999
Sending request 1 ...
Received reply 1 [ Sensor: 6 :: Data: 123456789 :: Client: 9100 ]
Sending request 2 ...
Received reply 2 [ Sensor: 7 :: Data: 987654321 :: Client: 9101 ]
Connecting to port... # <-- at this point, the 10 seconds option was selected
Successfully connected to port 9998
Successfully connected to port 9999
Sending request 1 ...
Connecting to port...
Successfully connected to port 9998
Successfully connected to port 9999
Sending request 1 ...
Received reply 1 [ Sensor: 6 :: Data: 123456789 :: Client: 9100 ]
Sending request 2 ...
Received reply 1 [ Sensor: 6 :: Data: 123456789 :: Client: 9100 ]
Sending request 2 ...
Received reply 2 [ Sensor: 7 :: Data: 987654321 :: Client: 9101 ]
Received reply 2 [ Sensor: 7 :: Data: 987654321 :: Client: 9101 ]
Connecting to port...
Successfully connected to port 9998
Successfully connected to port 9999
Sending request 1 ...
Received reply 1 [ Sensor: 6 :: Data: 123456789 :: Client: 9100 ] # <-- closed tkinter window
Execution of job "MainApplication.req_client (trigger: cron[second='*/5'], next run at: 2017-12-29 16:34:30 +08)" skipped: maximum number of running instances reached (1)
Execution of job "MainApplication.req_client (trigger: cron[second='*/5'], next run at: 2017-12-29 16:34:35 +08)" skipped: maximum number of running instances reached (1)
[Cancelled]
What's wrong with my code (probably everything) and how do I get it to run as intended?
job
will never be in locals()
because it is a local variable, and you are checking it before it is set. When the function returns, all local variables are unset, so the next time you call the function it will again be unset.
Since you appear to be using classes, you should use a class variable, and initialize it to None
class Whatever():
def __init__(self, ...):
self.job = None
...
def req_timer(self, option=None):
if self.job is not None:
...
Also, you have a lot of duplicated code that can be removed. For example, you might want to consider rewriting the function like this to reduce the replication:
def req_timer(self, option):
seconds_map = {
"15 minutes": "*/15",
"10 minutes": "*/10",
"5 minutes": "*/5",
"-": None,
}
scheduler = apscheduler.schedulers.background.BackgroundScheduler()
if self.job is not None:
self.job.remove_job('option_timer')
self.job = None
seconds = seconds_map.get(option, None)
if seconds is not None:
self.job = scheduler.add_job(self.req_client, 'cron',
second=seconds,
id='option_timer')
scheduler.start()