nice_gui.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #!/usr/bin/env python3
  2. import justpy as jp
  3. from starlette.applications import Starlette
  4. import uvicorn
  5. import inspect
  6. import time
  7. import asyncio
  8. from utils import handle_exceptions, provide_arguments
  9. import icecream
  10. icecream.install()
  11. wp = jp.WebPage(delete_flag=False, title='Nice GUI', favicon='favicon.png')
  12. main = jp.Div(a=wp, classes='m-4 flex flex-col items-start gap-4')
  13. jp.justpy(lambda: wp, start_server=False)
  14. view_stack = [main]
  15. class Element:
  16. def __init__(self, view: jp.HTMLBaseComponent):
  17. view_stack[-1].add(view)
  18. self.view = view
  19. @property
  20. def text(self):
  21. return self.view.text
  22. @text.setter
  23. def text(self, text):
  24. self.view.text = text
  25. def set_text(self, text):
  26. self.view.text = text
  27. def __enter__(self):
  28. view_stack.append(self.view)
  29. def __exit__(self, *_):
  30. view_stack.pop()
  31. class Ui(Starlette):
  32. def label(self, text=''):
  33. view = jp.Div(text=text)
  34. return Element(view)
  35. def button(self, text, on_click=None):
  36. view = jp.Button(text=text, classes='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded')
  37. if on_click is not None:
  38. view.on('click', handle_exceptions(provide_arguments(on_click)))
  39. return Element(view)
  40. def row(self):
  41. view = jp.Div(classes='flex flex-row gap-4 items-start')
  42. return Element(view)
  43. def column(self):
  44. view = jp.Div(classes='flex flex-col gap-4 items-start')
  45. return Element(view)
  46. def card(self):
  47. view = jp.Div(classes='flex flex-col gap-4 items-start p-4 rounded shadow-lg')
  48. return Element(view)
  49. def timer(self, inverval, callback):
  50. def update(view):
  51. if view.components:
  52. for v in view.components:
  53. update(v)
  54. jp.run_task(view.update())
  55. async def loop():
  56. while True:
  57. start = time.time()
  58. handle_exceptions(callback)()
  59. update(view_stack[-1])
  60. dt = time.time() - start
  61. await asyncio.sleep(inverval - dt)
  62. jp.run_task(loop())
  63. def run(self):
  64. # NOTE: prevent reloader to restart uvicorn
  65. if inspect.stack()[-2].filename.endswith('spawn.py'):
  66. return
  67. uvicorn.run('nice_gui:ui', host='0.0.0.0', port=80, lifespan='on', reload=True)
  68. # NOTE: instantiate our own ui object with all capabilities of jp.app
  69. ui = Ui()
  70. ui.__dict__.update(jp.app.__dict__)