timer.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import asyncio
  2. import time
  3. import traceback
  4. from typing import Callable
  5. from .. import globals
  6. from ..async_updater import AsyncUpdater
  7. from ..binding import BindableProperty
  8. from ..helpers import is_coroutine
  9. from ..lifecycle import on_startup
  10. from ..task_logger import create_task
  11. class Timer:
  12. active = BindableProperty()
  13. interval = BindableProperty()
  14. def __init__(self, interval: float, callback: Callable, *, active: bool = True, once: bool = False) -> None:
  15. """Timer
  16. One major drive behind the creation of NiceGUI was the necessity to have a simple approach to update the interface in regular intervals,
  17. for example to show a graph with incoming measurements.
  18. A timer will execute a callback repeatedly with a given interval.
  19. :param interval: the interval in which the timer is called (can be changed during runtime)
  20. :param callback: function or coroutine to execute when interval elapses
  21. :param active: whether the callback should be executed or not (can be changed during runtime)
  22. :param once: whether the callback is only executed once after a delay specified by `interval` (default: `False`)
  23. """
  24. self.interval = interval
  25. self.callback = callback
  26. self.active = active
  27. self.slot = globals.get_slot()
  28. coroutine = self._run_once if once else self._run_in_loop
  29. if globals.state == globals.State.STARTED:
  30. globals.tasks.append(create_task(coroutine(), name=str(callback)))
  31. else:
  32. on_startup(coroutine)
  33. async def _run_once(self) -> None:
  34. with self.slot:
  35. await asyncio.sleep(self.interval)
  36. await self._invoke_callback()
  37. async def _run_in_loop(self) -> None:
  38. with self.slot:
  39. while True:
  40. if self.slot.parent.client.id not in globals.clients:
  41. return
  42. try:
  43. start = time.time()
  44. if self.active:
  45. await self._invoke_callback()
  46. dt = time.time() - start
  47. await asyncio.sleep(self.interval - dt)
  48. except asyncio.CancelledError:
  49. return
  50. except:
  51. traceback.print_exc()
  52. await asyncio.sleep(self.interval)
  53. async def _invoke_callback(self) -> None:
  54. try:
  55. result = self.callback()
  56. if is_coroutine(self.callback):
  57. await AsyncUpdater(result)
  58. except Exception:
  59. traceback.print_exc()