1
0
Эх сурвалжийг харах

Merge branch 'main' of github.com:zauberzeug/nice_gui into main

# Conflicts:
#	nice_gui.py
Rodja Trappe 4 жил өмнө
parent
commit
926ace6405
3 өөрчлөгдсөн 69 нэмэгдсэн , 143 устгасан
  1. 0 116
      elements.py
  2. 23 19
      main.py
  3. 46 8
      nice_gui.py

+ 0 - 116
elements.py

@@ -1,116 +0,0 @@
-import justpy as jp
-from typing import List
-from contextlib import contextmanager
-import asyncio
-import time
-import uuid
-from utils import handle_exceptions, provide_arguments
-
-class Group:
-
-    def label(self, text) -> jp.Div:
-
-        return jp.Div(text=text, a=self.view, classes='')
-
-    def button(self, text, on_click=None) -> jp.Button:
-
-        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')
-        if on_click is not None:
-            b.on('click', handle_exceptions(provide_arguments(on_click)))
-        return b
-
-    def checkbox(self, text=None, on_change=None) -> jp.Input:
-
-        d = jp.Div(a=self.view, classes='flex gap-4')
-        c = jp.Input(a=d, type='checkbox', classes='form-checkbox mt-1 ml-4')
-        if text is not None:
-            jp.Div(text=text, a=d)
-        if on_change is not None:
-            c.on('change', handle_exceptions(provide_arguments(on_change, 'checked')))
-        return d
-    
-    def select(self, options: List[str], value=None, on_change=None) -> jp.Input:
-
-        s = jp.Select(classes='p-2 border rounded', a=self.view, value=value)
-        if on_change is not None:
-            s.on('change', handle_exceptions(provide_arguments(on_change, 'value')))
-        [ jp.Option(value=option, text=option, a=s) for option in options]
-        return s
-    
-    def radio(self, options: List[str], value=None, vertical=False, on_change=None) -> jp.Input:
-
-        flex_direction = 'flex-col' if vertical else 'flex-row'
-        name = str(uuid.uuid4())
-        outer = jp.Div(a=self.view, classes=f'flex gap-2 {flex_direction}')
-        for option in options:
-            inner = jp.Label(classes='inline-block', a=outer)
-            r = jp.Input(type='radio', name=name, value=option, checked=option==value, a=inner, classes='mx-2')
-            r.on('change', handle_exceptions(provide_arguments(on_change, 'value')))
-            jp.Span(a=inner, text=option)
-        
-        return outer
-
-    @contextmanager
-    def plot(self):
-
-        yield
-        jp.Matplotlib(a=self.view)
-
-    @contextmanager
-    def card(self):
-
-        yield Card(self)
-
-    @contextmanager
-    def column(self):
-
-        yield Column(self)
-
-    @contextmanager
-    def row(self):
-
-        yield Row(self)
-
-    def timer(self, inverval, callback):
-
-        async def loop():
-
-            while True:
-                start = time.time()
-                handle_exceptions(callback)()
-                jp.run_task(self.view.update())
-                dt = time.time() - start
-                await asyncio.sleep(inverval - dt)
-
-        jp.run_task(loop())
-
-
-class Page(Group):
-
-    def __init__(self):
-
-        self.view = jp.WebPage(delete_flag=False, body_classes='m-4', title='Nice GUI', favicon='favicon.png')
-
-
-class Card(Group):
-
-    def __init__(self, parent) -> None:
-
-        self.parent = parent
-        self.view = jp.Div(a=parent.view, classes='p-4 flex flex-col gap-4 items-start rounded shadow-lg')
-
-
-class Column(Group):
-
-    def __init__(self, parent) -> None:
-
-        self.parent = parent
-        self.view = jp.Div(a=parent.view, classes='flex flex-col gap-4 items-start')
-
-
-class Row(Group):
-
-    def __init__(self, parent) -> None:
-
-        self.parent = parent
-        self.view = jp.Div(a=parent.view, classes='flex flex-row flex-wrap gap-4 items-start')

+ 23 - 19
main.py

@@ -3,32 +3,36 @@ from nice_gui import ui
 from datetime import datetime
 from matplotlib import pyplot as plt
 
-ui.label('Hello world!')
-
-ui.button('Click me!', on_click=lambda: ui.label('Yes!'))
-
 with ui.card():
+    ui.label('Interactive elements', 'h5')
     with ui.row():
-        ui.label('A')
-        ui.label('B')
         with ui.column():
-            ui.label('C1')
-            ui.label('C2')
-            ui.label('C3')
+            ui.button('Click me!', on_click=lambda: output.set_text('Click'))
+            ui.checkbox('Check me!', on_change=lambda e: output.set_text('Checked' if e.value else 'Unchecked'))
+            ui.switch('Switch me!', on_change=lambda e: output.set_text('Switched' if e.value else 'Unswitched'))
+            ui.slider(0, 100, on_change=lambda e: output.set_text(e.value))
+        with ui.column():
+            ui.radio(['A', 'B', 'C'], on_change=lambda e: output.set_text(e.value))
+            ui.select(['1', '2', '3'], on_change=lambda e: output.set_text(e.value))
+    with ui.row():
+        ui.label('Output:')
+        output = ui.label()
 
-with ui.row():
-    ui.label('Time:')
+with ui.card():
+    ui.label('Timer', 'h5')
     time = ui.label()
     ui.timer(0.1, lambda: time.set_text(datetime.now().strftime("%X")))
 
-with ui.plot(close=False) as plot:
-    plt.title('Some plot')
-    plt.plot(range(10), [x**2 for x in range(10)])
+with ui.card():
+    ui.label('Matplotlib', 'h5')
+    with ui.plot(close=False) as plot:
+        plt.title('Some plot')
+        plt.plot(range(10), [x**2 for x in range(10)])
 
-def update_plot():
-    plt.title('Some plot with a second curve')
-    plt.plot(range(10), [100 - x**2 for x in range(10)])
-    plot.update()
-ui.timer(3.0, update_plot, once=True)
+    def update_plot():
+        plt.title('Some plot with a second curve')
+        plt.plot(range(10), [100 - x**2 for x in range(10)])
+        plot.update()
+    ui.timer(3.0, update_plot, once=True)
 
 ui.run()

+ 46 - 8
nice_gui.py

@@ -13,8 +13,8 @@ import icecream
 
 icecream.install()
 
-wp = jp.WebPage(delete_flag=False, title='Nice GUI', favicon='favicon.png')
-main = jp.Div(a=wp, classes='m-4 flex flex-col items-start gap-4')
+wp = jp.QuasarPage(delete_flag=False, title='Nice GUI', favicon='favicon.png')
+main = jp.Div(a=wp, classes='q-ma-md column items-start', style='row-gap: 1em')
 main.add_page(wp)
 jp.justpy(lambda: wp, start_server=False)
 
@@ -57,18 +57,56 @@ class Plot(Element):
 
 class Ui(Starlette):
 
-    def label(self, text=''):
+    def label(self, text='', typography=[]):
 
-        view = jp.Div(text=text)
+        if isinstance(typography, str):
+            typography = [typography]
+        classes = ' '.join('text-' + t for t in typography)
+        view = jp.Div(text=text, classes=classes)
         return Element(view)
 
     def button(self, text, on_click=None):
 
-        view = jp.Button(text=text, classes='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded')
+        view = jp.QBtn(text=text, color='primary')
         if on_click is not None:
             view.on('click', handle_exceptions(provide_arguments(on_click)))
         return Element(view)
 
+    def checkbox(self, text, on_change=None):
+
+        view = jp.QCheckbox(text=text)
+        if on_change is not None:
+            view.on('input', handle_exceptions(provide_arguments(on_change, 'value')))
+        return Element(view)
+
+    def switch(self, text, on_change=None):
+
+        view = jp.QToggle(text=text)
+        if on_change is not None:
+            view.on('input', handle_exceptions(provide_arguments(on_change, 'value')))
+        return Element(view)
+
+    def radio(self, options, value=None, on_change=None):
+
+        view = jp.QOptionGroup(value=value, options=[{'label': o, 'value': o} for o in options])
+        if on_change is not None:
+            view.on('input', handle_exceptions(provide_arguments(on_change, 'value')))
+        return Element(view)
+
+    def select(self, options, value=None, on_change=None):
+
+        view = jp.QSelect(value=value, options=options)
+        if on_change is not None:
+            view.on('input', handle_exceptions(provide_arguments(on_change, 'value')))
+        return Element(view)
+
+    def slider(self, min, max, on_change=None):
+
+        view = jp.QSlider(min=min, max=max)
+        if on_change is not None:
+            view.on('input', handle_exceptions(provide_arguments(on_change, 'value')))
+        return Element(view)
+
     @contextmanager
     def plot(self, close=True):
 
@@ -80,17 +118,17 @@ class Ui(Starlette):
 
     def row(self):
 
-        view = jp.Div(classes='flex flex-row gap-4 items-start')
+        view = jp.QDiv(classes='row items-start', style='column-gap: 1em')
         return Element(view)
 
     def column(self):
 
-        view = jp.Div(classes='flex flex-col gap-4 items-start')
+        view = jp.QDiv(classes='column items-start', style='row-gap: 1em')
         return Element(view)
 
     def card(self):
 
-        view = jp.Div(classes='flex flex-col gap-4 items-start p-4 rounded shadow-lg')
+        view = jp.QCard(classes='q-pa-md column items-start', style='row-gap: 1em')
         return Element(view)
 
     def timer(self, interval, callback, *, once=False):