My toga app needs to run a task every X seconds. I prefer a multi-platform solution where possible.
I asked an AI and its solution used an external dependency ("schedule") + additional loop + threading:
def create_timer(self, interval, callback):
def run_callback():
schedule.every(interval).seconds.do(callback)
while True:
schedule.run_pending()
threading.Event().wait(interval)
timer_thread = threading.Thread(target=run_callback)
timer_thread.daemon = True # Ensure thread terminates with app
timer_thread.start()
return timer_thread
I rather avoid external modules and adding loops (for performance, minimize resource usage, and simplicity).
How to implment a timer / interval based on Toga's own event loop?
I never used toga
but I found information that it uses async loop
and that it has functions
So I took some example app from documentation and I created working example which displays current time using call_later()
.
I use it in change()
to execute the same function again after some time
(similar to .after(milliseconds, callback)
in tkinter
)
I don't know how to access app.loop
(and label
) in this function
so I send app
(and label
) as parameters.
But works also loop = asyncio.get_event_loop()
import toga
#import datetime
import time
#import asyncio
def change(app, widget):
# if code needs more time then better set next execution first
loop = app.loop
#loop = asyncio.get_event_loop()
loop.call_later(1, change, app, widget)
#widget.text = datetime.datetime.now().strftime('%Y.%m.%d %H.%M.%S')
widget.text = time.strftime('%Y.%m.%d %H.%M.%S')
#app.loop.call_later(1, change, app, widget) # here it would need `1 - time_of_function_execution`
def build(app):
box = toga.Box()
label = toga.Label("?")
box.add(label)
# execute first time without delay
#app.loop.call_soon(change, app, label)
#app.loop.call_later(0, change, app, label)
change(app, label)
return box
def main():
return toga.App("Timer", "org.beeware.toga.tutorial", startup=build)
if __name__ == "__main__":
main().main_loop()
The same using class MyApp(toga.App)
like in documentation for App
because all is in class so I can use self.
to access loop
and label
and I don't have to send it as parameters.
import toga
import time
class MyApp(toga.App):
def startup(self):
self.main_window = toga.MainWindow()
self.main_window.content = toga.Box()
self.label = toga.Label("?")
self.main_window.content.add(self.label)
# execute first time without delay
#self.loop.call_soon(self.change, self.label)
#self.loop.call_later(0, self.change, self.label)
self.change()
self.main_window.show()
def change(self):
# if code needs more time then better set next execution first
self.loop.call_later(1, self.change)
self.label.text = time.strftime('%Y.%m.%d %H.%M.%S')
if __name__ == '__main__':
app = MyApp("Timer", "org.beeware.toga.tutorial")
app.main_loop()