nice_gui.py 2.4 KB

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