|
@@ -1,31 +1,24 @@
|
|
import asyncio
|
|
import asyncio
|
|
import time
|
|
import time
|
|
import traceback
|
|
import traceback
|
|
-from collections import namedtuple
|
|
|
|
-from typing import Callable, List, Optional
|
|
|
|
-
|
|
|
|
-from starlette.websockets import WebSocket
|
|
|
|
|
|
+from typing import Callable
|
|
|
|
|
|
from . import globals
|
|
from . import globals
|
|
-from .auto_context import Context
|
|
|
|
from .binding import BindableProperty
|
|
from .binding import BindableProperty
|
|
from .helpers import is_coroutine
|
|
from .helpers import is_coroutine
|
|
-from .page import Page, find_parent_page, find_parent_view
|
|
|
|
|
|
+from .lifecycle import on_startup
|
|
from .task_logger import create_task
|
|
from .task_logger import create_task
|
|
|
|
|
|
-NamedCoroutine = namedtuple('NamedCoroutine', ['name', 'coro'])
|
|
|
|
-
|
|
|
|
|
|
|
|
class Timer:
|
|
class Timer:
|
|
- prepared_coroutines: List[NamedCoroutine] = []
|
|
|
|
-
|
|
|
|
active = BindableProperty()
|
|
active = BindableProperty()
|
|
interval = BindableProperty()
|
|
interval = BindableProperty()
|
|
|
|
|
|
- def __init__(self, interval: float, callback: Callable, *, active: bool = True, once: bool = False):
|
|
|
|
|
|
+ def __init__(self, interval: float, callback: Callable, *, active: bool = True, once: bool = False) -> None:
|
|
"""Timer
|
|
"""Timer
|
|
|
|
|
|
- One major drive behind the creation of NiceGUI was the necessity to have a simple approach to update the interface in regular intervals, for example to show a graph with incomming measurements.
|
|
|
|
|
|
+ One major drive behind the creation of NiceGUI was the necessity to have a simple approach to update the interface in regular intervals,
|
|
|
|
+ for example to show a graph with incoming measurements.
|
|
A timer will execute a callback repeatedly with a given interval.
|
|
A timer will execute a callback repeatedly with a given interval.
|
|
|
|
|
|
:param interval: the interval in which the timer is called (can be changed during runtime)
|
|
:param interval: the interval in which the timer is called (can be changed during runtime)
|
|
@@ -33,48 +26,38 @@ class Timer:
|
|
:param active: whether the callback should be executed or not (can be changed during runtime)
|
|
:param active: whether the callback should be executed or not (can be changed during runtime)
|
|
:param once: whether the callback is only executed once after a delay specified by `interval` (default: `False`)
|
|
:param once: whether the callback is only executed once after a delay specified by `interval` (default: `False`)
|
|
"""
|
|
"""
|
|
-
|
|
|
|
- self.active = active
|
|
|
|
self.interval = interval
|
|
self.interval = interval
|
|
- self.socket: Optional[WebSocket] = None
|
|
|
|
- self.parent_page = find_parent_page()
|
|
|
|
- self.parent_view = find_parent_view()
|
|
|
|
-
|
|
|
|
- async def do_callback():
|
|
|
|
- try:
|
|
|
|
- with Context(self.parent_view) as context:
|
|
|
|
- result = callback()
|
|
|
|
- if is_coroutine(callback):
|
|
|
|
- await context.watch_asyncs(result)
|
|
|
|
- except Exception:
|
|
|
|
- traceback.print_exc()
|
|
|
|
|
|
+ self.callback = callback
|
|
|
|
+ self.active = active
|
|
|
|
|
|
- async def timeout():
|
|
|
|
- await asyncio.sleep(self.interval)
|
|
|
|
- await do_callback()
|
|
|
|
|
|
+ coroutine = self._run_once if once else self._run_in_loop
|
|
|
|
+ if globals.state == globals.State.STARTED:
|
|
|
|
+ globals.tasks.append(create_task(coroutine(), name=str(callback)))
|
|
|
|
+ else:
|
|
|
|
+ on_startup(coroutine)
|
|
|
|
|
|
- async def loop():
|
|
|
|
- while True:
|
|
|
|
- if not self.parent_page.shared:
|
|
|
|
- sockets = list(Page.sockets.get(self.parent_page.page_id, {}).values())
|
|
|
|
- if not self.socket and sockets:
|
|
|
|
- self.socket = sockets[0]
|
|
|
|
- elif self.socket and not sockets:
|
|
|
|
- return
|
|
|
|
- try:
|
|
|
|
- start = time.time()
|
|
|
|
- if self.active:
|
|
|
|
- await do_callback()
|
|
|
|
- dt = time.time() - start
|
|
|
|
- await asyncio.sleep(self.interval - dt)
|
|
|
|
- except asyncio.CancelledError:
|
|
|
|
- return
|
|
|
|
- except:
|
|
|
|
- traceback.print_exc()
|
|
|
|
- await asyncio.sleep(self.interval)
|
|
|
|
|
|
+ async def _run_once(self) -> None:
|
|
|
|
+ await asyncio.sleep(self.interval)
|
|
|
|
+ await self._invoke_callback()
|
|
|
|
|
|
- coroutine = timeout() if once else loop()
|
|
|
|
- if not (globals.loop and globals.loop.is_running()):
|
|
|
|
- self.prepared_coroutines.append(NamedCoroutine(str(callback), coroutine))
|
|
|
|
- else:
|
|
|
|
- globals.tasks.append(create_task(coroutine, name=str(callback)))
|
|
|
|
|
|
+ async def _run_in_loop(self) -> None:
|
|
|
|
+ while True:
|
|
|
|
+ try:
|
|
|
|
+ start = time.time()
|
|
|
|
+ if self.active:
|
|
|
|
+ await self._invoke_callback()
|
|
|
|
+ dt = time.time() - start
|
|
|
|
+ await asyncio.sleep(self.interval - dt)
|
|
|
|
+ except asyncio.CancelledError:
|
|
|
|
+ return
|
|
|
|
+ except:
|
|
|
|
+ traceback.print_exc()
|
|
|
|
+ await asyncio.sleep(self.interval)
|
|
|
|
+
|
|
|
|
+ async def _invoke_callback(self) -> None:
|
|
|
|
+ try:
|
|
|
|
+ result = self.callback()
|
|
|
|
+ if is_coroutine(self.callback):
|
|
|
|
+ await result
|
|
|
|
+ except Exception:
|
|
|
|
+ traceback.print_exc()
|