timer.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import asyncio
  2. import time
  3. import traceback
  4. from typing import Awaitable, Callable, List, Union
  5. from collections import namedtuple
  6. from .binding import BindableProperty
  7. from .globals import tasks, view_stack
  8. from .task_logger import create_task
  9. NamedCoroutine = namedtuple('NamedCoroutine', ['name', 'coro'])
  10. class Timer:
  11. prepared_coroutines: List[NamedCoroutine] = []
  12. active = BindableProperty()
  13. def __init__(self, interval: float, callback: Union[Callable, Awaitable], *, active: bool = True, once: bool = False):
  14. """Timer
  15. 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.
  16. A timer will execute a callback repeatedly with a given interval.
  17. The parent view container will be updated automatically, as long as the callback does not return `False`.
  18. :param interval: the interval in which the timer is called
  19. :param callback: function or coroutine to execute when interval elapses (can return `False` to prevent view update)
  20. :param active: whether the callback should be executed or not
  21. :param once: whether the callback is only executed once after a delay specified by `interval` (default: `False`)
  22. """
  23. parent = view_stack[-1]
  24. self.active = active
  25. async def do_callback():
  26. try:
  27. if asyncio.iscoroutinefunction(callback):
  28. return await callback()
  29. else:
  30. return callback()
  31. except Exception:
  32. traceback.print_exc()
  33. async def timeout():
  34. await asyncio.sleep(interval)
  35. await do_callback()
  36. await parent.update()
  37. async def loop():
  38. while True:
  39. try:
  40. start = time.time()
  41. if self.active:
  42. needs_update = await do_callback()
  43. if needs_update != False:
  44. await parent.update()
  45. dt = time.time() - start
  46. await asyncio.sleep(interval - dt)
  47. except asyncio.CancelledError:
  48. return
  49. except:
  50. traceback.print_exc()
  51. await asyncio.sleep(interval)
  52. coroutine = timeout() if once else loop()
  53. event_loop = asyncio.get_event_loop()
  54. if not event_loop.is_running():
  55. self.prepared_coroutines.append(NamedCoroutine(str(callback), coroutine))
  56. else:
  57. tasks.append(create_task(coroutine, name=str(callback)))