Timers¶
A function of your app run later, or again and again: a clock, a countdown, a search that waits for the typing to stop, a message that goes away.
from apkpy_lib import *
home = Screen(id="home")
clock = label("", id="clock", screen=home)
def tick():
clock.set_value(datetime.time())
def opened():
tick()
timer.every(1, tick)
lifecycle(home, on_mount=opened)
run(start_screen=home)
| Call | |
|---|---|
timer.after(seconds, function) |
Run the function once, that many seconds from now. |
timer.every(seconds, function) |
Run it again and again, that far apart. |
timer.cancel(function) |
Stop that function's timer. |
timer.running(function) |
Whether it has a timer waiting — for an if. |
A timer is known by its function¶
There is nothing to keep. One function has one timer on a screen, and
timer.cancel(tick) names what to stop:
def start_or_pause():
if timer.running(count_down):
timer.cancel(count_down)
start.set_value("Start")
else:
timer.every(1, count_down)
start.set_value("Pause")
Starting a timer for a function that already has one starts it over. That is all "wait until the typing stops" takes:
def typed(value):
timer.after(0.4, search) # every key puts the search off again
query = inputs("Search", id="query", on_change=typed, screen=home)
A function can stop its own timer, or start itself over, from inside:
def count_down():
seconds = int(left.get_value()) - 1
left.set_value(str(seconds))
if seconds <= 0:
timer.cancel(count_down)
The function¶
One you wrote with def at the top level of the file, that takes nothing.
Write its name, without brackets:
timer.every(1, tick) # yes
timer.every(1, tick()) # no: that runs tick now and hands over what it answers
timer.every(1, lambda: …) # no: a lambda has no name to be known by
Each of these stops the build and says which line to write instead (U2048);
the Previewer refuses them in the same words.
What the calls answer¶
after() / every() answer |
|
|---|---|
ok |
The timer was started. |
invalid |
The time is not a number of seconds, or it is below zero. Nothing was started. |
cancel() answers |
|
|---|---|
ok |
The timer was stopped. |
not_running |
That function had no timer waiting on this screen. |
Seconds are a number, with fractions (0.4), or the text of a field:
timer.after(wait.get_value(), ring). every() never runs more often than
twenty times a second, and the longest wait is a week.
While the screen is not on show¶
A timer runs only while its screen is on show. With the app in the background, the phone locked, or another screen of the app on top, nothing runs — a function that draws on a screen nobody is looking at is work for nothing, and Android stops a background app whenever it likes, which is no rule to build on.
Time keeps going by meanwhile. When the screen is back, what fell due runs
once, at once, and an every() goes on from there, a full period later —
not five ticks in a burst, and not two in a row.
So read the time rather than counting ticks where the answer has to be right:
a clock that sets datetime.time() in its tick is right the moment the app
comes back; a count of ticks is a count of what ran.
Closing the screen ends its timers. Turning the phone or changing the theme does not: Android builds the screen again, and its timers go on to the new copy with the moments they were waiting for.
Where to start one¶
From a function — a button's command, or the screen's on_mount:
A timer.every(...) at the top of the file would start before there is a
screen for it to belong to, and stops the build (U2048).
Each screen has its own timers. A timer started on one screen is not the one
timer.running() asks about on another.
Keeping a count¶
In a label, as above, or in storage:
What a timer is not for¶
Work with the app closed. That is
service.every and background_job, and a
notification at a set time. Inside a background job a timer stops the build
(J7004).
In the Previewer¶
The same rules, for the screen in the window: a timer waits while another screen is on show and runs what fell due when its own comes back.
One difference. On the phone a screen left with Back is closed, and its
timers end; the Previewer keeps every screen it has drawn, so a timer started
there waits and carries on when the screen is opened again. A screen that
starts its timers in on_mount behaves the same on both.