elements.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import justpy as jp
  2. from typing import List
  3. from contextlib import contextmanager
  4. import asyncio
  5. import time
  6. import uuid
  7. from utils import handle_exceptions, provide_arguments
  8. class Group:
  9. def label(self, text) -> jp.Div:
  10. return jp.Div(text=text, a=self.view, classes='')
  11. def button(self, text, on_click=None) -> jp.Button:
  12. b = jp.Button(text=text, a=self.view, classes='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded')
  13. if on_click is not None:
  14. b.on('click', handle_exceptions(provide_arguments(on_click)))
  15. return b
  16. def checkbox(self, text=None, on_change=None) -> jp.Input:
  17. d = jp.Div(a=self.view, classes='flex gap-4')
  18. c = jp.Input(a=d, type='checkbox', classes='form-checkbox mt-1 ml-4')
  19. if text is not None:
  20. jp.Div(text=text, a=d)
  21. if on_change is not None:
  22. c.on('change', handle_exceptions(provide_arguments(on_change, 'checked')))
  23. return d
  24. def select(self, options: List[str], value=None, on_change=None) -> jp.Input:
  25. s = jp.Select(classes='p-2 border rounded', a=self.view, value=value)
  26. if on_change is not None:
  27. s.on('change', handle_exceptions(provide_arguments(on_change, 'value')))
  28. [ jp.Option(value=option, text=option, a=s) for option in options]
  29. return s
  30. def radio(self, options: List[str], value=None, vertical=False, on_change=None) -> jp.Input:
  31. flex_direction = 'flex-col' if vertical else 'flex-row'
  32. name = str(uuid.uuid4())
  33. outer = jp.Div(a=self.view, classes=f'flex gap-2 {flex_direction}')
  34. for option in options:
  35. inner = jp.Label(classes='inline-block', a=outer)
  36. r = jp.Input(type='radio', name=name, value=option, checked=option==value, a=inner, classes='mx-2')
  37. r.on('change', handle_exceptions(provide_arguments(on_change, 'value')))
  38. jp.Span(a=inner, text=option)
  39. return outer
  40. @contextmanager
  41. def plot(self):
  42. yield
  43. jp.Matplotlib(a=self.view)
  44. @contextmanager
  45. def card(self):
  46. yield Card(self)
  47. @contextmanager
  48. def column(self):
  49. yield Column(self)
  50. @contextmanager
  51. def row(self):
  52. yield Row(self)
  53. def timer(self, inverval, callback):
  54. async def loop():
  55. while True:
  56. start = time.time()
  57. handle_exceptions(callback)()
  58. jp.run_task(self.view.update())
  59. dt = time.time() - start
  60. await asyncio.sleep(inverval - dt)
  61. jp.run_task(loop())
  62. class Page(Group):
  63. def __init__(self):
  64. self.view = jp.WebPage(delete_flag=False, body_classes='m-4', title='Nice GUI', favicon='favicon.png')
  65. class Card(Group):
  66. def __init__(self, parent) -> None:
  67. self.parent = parent
  68. self.view = jp.Div(a=parent.view, classes='p-4 flex flex-col gap-4 items-start rounded shadow-lg')
  69. class Column(Group):
  70. def __init__(self, parent) -> None:
  71. self.parent = parent
  72. self.view = jp.Div(a=parent.view, classes='flex flex-col gap-4 items-start')
  73. class Row(Group):
  74. def __init__(self, parent) -> None:
  75. self.parent = parent
  76. self.view = jp.Div(a=parent.view, classes='flex flex-row flex-wrap gap-4 items-start')