|
@@ -23,7 +23,7 @@ def example(content: Union[Callable, type, str]):
|
|
|
|
|
|
def add_html_anchor(element: ui.html):
|
|
|
html = element.content
|
|
|
- match = re.search(r'<h3.*?>(.*?)</h3>', html)
|
|
|
+ match = re.search(r'<h4.*?>(.*?)</h4>', html)
|
|
|
if not match:
|
|
|
return
|
|
|
|
|
@@ -33,8 +33,8 @@ def example(content: Union[Callable, type, str]):
|
|
|
|
|
|
icon = '<span class="material-icons">link</span>'
|
|
|
anchor = f'<a href="#{headline_id}" class="text-gray-300 hover:text-black">{icon}</a>'
|
|
|
- html = html.replace('<h3', f'<h3 id="{headline_id}"', 1)
|
|
|
- html = html.replace('</h3>', f' {anchor}</h3>', 1)
|
|
|
+ html = html.replace('<h4', f'<h4 id="{headline_id}"', 1)
|
|
|
+ html = html.replace('</h4>', f' {anchor}</h4>', 1)
|
|
|
element.view.inner_html = html
|
|
|
|
|
|
with ui.row().classes('flex w-full'):
|
|
@@ -43,8 +43,8 @@ def example(content: Union[Callable, type, str]):
|
|
|
else:
|
|
|
doc = content.__doc__ or content.__init__.__doc__
|
|
|
html = docutils.core.publish_parts(doc, writer_name='html')['html_body']
|
|
|
- html = html.replace('<p>', '<h3>', 1)
|
|
|
- html = html.replace('</p>', '</h3>', 1)
|
|
|
+ html = html.replace('<p>', '<h4>', 1)
|
|
|
+ html = html.replace('</p>', '</h4>', 1)
|
|
|
html = ui.markdown.apply_tailwind(html)
|
|
|
add_html_anchor(ui.html(html).classes('mr-8 w-4/12'))
|
|
|
|
|
@@ -108,62 +108,18 @@ with ui.row().classes('flex w-full'):
|
|
|
|
|
|
ui.markdown('## API Documentation and Examples')
|
|
|
|
|
|
-with example(ui.label):
|
|
|
- ui.label('some label')
|
|
|
-
|
|
|
-with example(ui.image):
|
|
|
- ui.image('http://placeimg.com/640/360/tech')
|
|
|
- base64 = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAACKgAwAEAAAAAQAAACMAAAAA/8IAEQgAIwAiAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAMCBAEFAAYHCAkKC//EAMMQAAEDAwIEAwQGBAcGBAgGcwECAAMRBBIhBTETIhAGQVEyFGFxIweBIJFCFaFSM7EkYjAWwXLRQ5I0ggjhU0AlYxc18JNzolBEsoPxJlQ2ZJR0wmDShKMYcOInRTdls1V1pJXDhfLTRnaA40dWZrQJChkaKCkqODk6SElKV1hZWmdoaWp3eHl6hoeIiYqQlpeYmZqgpaanqKmqsLW2t7i5usDExcbHyMnK0NTV1tfY2drg5OXm5+jp6vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAQIAAwQFBgcICQoL/8QAwxEAAgIBAwMDAgMFAgUCBASHAQACEQMQEiEEIDFBEwUwIjJRFEAGMyNhQhVxUjSBUCSRoUOxFgdiNVPw0SVgwUThcvEXgmM2cCZFVJInotIICQoYGRooKSo3ODk6RkdISUpVVldYWVpkZWZnaGlqc3R1dnd4eXqAg4SFhoeIiYqQk5SVlpeYmZqgo6SlpqeoqaqwsrO0tba3uLm6wMLDxMXGx8jJytDT1NXW19jZ2uDi4+Tl5ufo6ery8/T19vf4+fr/2wBDAAwMDAwMDBUMDBUeFRUVHikeHh4eKTQpKSkpKTQ+NDQ0NDQ0Pj4+Pj4+Pj5LS0tLS0tXV1dXV2JiYmJiYmJiYmL/2wBDAQ8QEBkXGSsXFytnRjlGZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2f/2gAMAwEAAhEDEQAAAeqBgCIareozvbaK3avBqa52teT6He3z0TqCUZa22r//2gAIAQEAAQUCaVVKTjGnLFqSqqlGuciX+87YgM8ScWhAx5KWUJUdJClKadMye6O//9oACAEDEQE/AUxI86A0ynfb/9oACAECEQE/ASaYZBLxpKNinFh2dv8A/9oACAEBAAY/AmUniHVXxfVx7ZIP9x0GlOJdfa+BeVentkSWR66jsI1HUfF+f4l1UykiqR/CypAorg6n/hvuH5nv/8QAMxABAAMAAgICAgIDAQEAAAILAREAITFBUWFxgZGhscHw0RDh8SAwQFBgcICQoLDA0OD/2gAIAQEAAT8hrchP08Nlp8V+7MHK/wCcEXw8q94vkT4K5DD0fpsJBFkwYvy/8cJBuuX7l82UhL9HmlzVKCOfi+3/ADe6Z2jgePxcMYN/xxYQtAu8UCj/ALXDvn/sBxRB/g3/AL//2gAMAwEAAhEDEQAAEE5gPHEUEAP/xAAzEQEBAQADAAECBQUBAQABAQkBABEhMRBBUWEgcfCRgaGx0cHh8TBAUGBwgJCgsMDQ4P/aAAgBAxEBPxAN4PZaNJuOW/g//9oACAECEQE/EAGt2fwmfzBp3X8P/9oACAEBAAE/ELGubg74j5M+RuAgxMrE4g5c4qAjQh1Oh9GL3/xggJDuHs5H2fY1rQIGDISTZ3KuGYzkk8dSkh4Ah8TJ8c0SsIco+yPRD76/486QSwOdnIpjvmvjAQ8pEx4ixlVcDldAdtawTzP5CSqs1wAPeJDMz0nwvHVlRSYTI1ic6b58RUC4kuSTXmFOJuxknJgsgDQMkjQgj/gCBHee6QjzflUA4/5//9k='
|
|
|
- ui.image(base64).style('width:30px')
|
|
|
-
|
|
|
-svg = '''### SVG
|
|
|
-You can add Scalable Vector Graphics using the `ui.html` element.
|
|
|
-'''
|
|
|
-with example(svg):
|
|
|
- content = '''
|
|
|
- <svg viewBox="0 0 200 200" width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
|
|
- <circle cx="100" cy="100" r="78" fill="#ffde34" stroke="black" stroke-width="3" />
|
|
|
- <circle cx="80" cy="85" r="8" />
|
|
|
- <circle cx="120" cy="85" r="8" />
|
|
|
- <path d="m60,120 C75,150 125,150 140,120" style="fill:none; stroke:black; stroke-width:8; stroke-linecap:round" />
|
|
|
- </svg>'''
|
|
|
- ui.html(content)
|
|
|
-
|
|
|
-overlay = '''### Captions and Overlays
|
|
|
-
|
|
|
-By nesting elements inside a `ui.image` you can create augmentations.
|
|
|
-
|
|
|
-Use [Quasar classes](https://quasar.dev/vue-components/img) for positioning and styling captions.
|
|
|
-To overlay an svg, make the `viewBox` exactly the size of the image and provide `100%` width/height to match the actual rendered size.
|
|
|
-'''
|
|
|
-with example(overlay):
|
|
|
- with ui.image('http://placeimg.com/640/360/nature'):
|
|
|
- ui.label('nice').classes('absolute-bottom text-subtitle2 text-center')
|
|
|
-
|
|
|
- with ui.image('https://cdn.pixabay.com/photo/2020/07/13/12/56/mute-swan-5400675__340.jpg'):
|
|
|
- content = '''
|
|
|
- <svg viewBox="0 0 510 340" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
|
|
- <circle cx="200" cy="200" r="100" fill="none" stroke="red" stroke-width="10" />
|
|
|
- </svg>'''
|
|
|
- ui.html(content).style('background:transparent')
|
|
|
|
|
|
-with example(ui.interactive_image):
|
|
|
- from nicegui.events import MouseEventArguments
|
|
|
+def h3(text: str) -> None:
|
|
|
+ ui.label(text).style('width: 100%; border-bottom: 1px solid silver; font-size: 200%; font-weight: 200')
|
|
|
|
|
|
- def mouse_handler(e: MouseEventArguments):
|
|
|
- color = 'green' if e.type == 'mousedown' else 'orange'
|
|
|
- ii.svg_content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="10" fill="{color}"/>'
|
|
|
- ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')
|
|
|
|
|
|
- ii = ui.interactive_image('http://placeimg.com/640/360/arch',
|
|
|
- on_mouse=mouse_handler,
|
|
|
- events=['mousedown', 'mouseup'], cross=True)
|
|
|
+h3('Basic Elements')
|
|
|
|
|
|
-with example(ui.markdown):
|
|
|
- ui.markdown('### Headline\nWith hyperlink to [GitHub](https://github.com/zauberzeug/nicegui).')
|
|
|
+with example(ui.label):
|
|
|
+ ui.label('some label')
|
|
|
|
|
|
-with example(ui.html):
|
|
|
- ui.html('<p>demo paragraph in <strong>html</strong></p>')
|
|
|
+with example(ui.link):
|
|
|
+ ui.link('NiceGUI on GitHub', 'https://github.com/zauberzeug/nicegui')
|
|
|
|
|
|
with example(ui.button):
|
|
|
def button_increment():
|
|
@@ -175,18 +131,13 @@ with example(ui.button):
|
|
|
ui.button('Button', on_click=button_increment)
|
|
|
button_result = ui.label('pressed: 0')
|
|
|
|
|
|
-async_button = '''### Button with asynchronous action
|
|
|
-The button element does also support asynchronous action.
|
|
|
-
|
|
|
-Note: You can also pass a `functools.partial` into the `on_click` property to wrap async functions with parameters.
|
|
|
-'''
|
|
|
-with example(async_button):
|
|
|
- async def async_task():
|
|
|
- ui.notify('Asynchronous task started')
|
|
|
- await asyncio.sleep(5)
|
|
|
- ui.notify('Asynchronous task finished')
|
|
|
+with example(ui.toggle):
|
|
|
+ toggle = ui.toggle([1, 2, 3], value=1)
|
|
|
+ ui.toggle({1: 'A', 2: 'B', 3: 'C'}, value=1).bind_value(toggle, 'value')
|
|
|
|
|
|
- ui.button('start async task', on_click=async_task)
|
|
|
+with example(ui.radio):
|
|
|
+ radio = ui.radio([1, 2, 3], value=1).props('inline')
|
|
|
+ ui.radio({1: 'A', 2: 'B', 3: 'C'}, value=1).props('inline').bind_value(radio, 'value')
|
|
|
|
|
|
with example(ui.checkbox):
|
|
|
ui.checkbox('check me', on_change=lambda e: checkbox_state.set_text(e.value))
|
|
@@ -204,6 +155,19 @@ with example(ui.slider):
|
|
|
slider = ui.slider(min=0, max=100, value=50).props('label')
|
|
|
ui.label().bind_text_from(slider, 'value')
|
|
|
|
|
|
+with example(ui.joystick):
|
|
|
+ ui.joystick(
|
|
|
+ color='blue',
|
|
|
+ size=50,
|
|
|
+ on_move=lambda msg: coordinates.set_text(f'{msg.data.vector.x:.3f}, {msg.data.vector.y:.3f}'),
|
|
|
+ on_end=lambda _: coordinates.set_text('0, 0'))
|
|
|
+ coordinates = ui.label('0, 0')
|
|
|
+
|
|
|
+with example(ui.select):
|
|
|
+ with ui.row():
|
|
|
+ select = ui.select([1, 2, 3], value=1).props('inline')
|
|
|
+ ui.select({1: 'One', 2: 'Two', 3: 'Three'}, value=1).props('inline').bind_value(select, 'value')
|
|
|
+
|
|
|
with example(ui.input):
|
|
|
ui.input(
|
|
|
label='Text',
|
|
@@ -227,23 +191,107 @@ with example(ui.color_picker):
|
|
|
picker = ui.color_picker(on_pick=lambda e: button.style(f'background-color:{e.color}!important'))
|
|
|
button = ui.button(on_click=picker.open).props('icon=colorize')
|
|
|
|
|
|
-with example(ui.radio):
|
|
|
- radio = ui.radio([1, 2, 3], value=1).props('inline')
|
|
|
- ui.radio({1: 'A', 2: 'B', 3: 'C'}, value=1).props('inline').bind_value(radio, 'value')
|
|
|
-
|
|
|
-with example(ui.toggle):
|
|
|
- toggle = ui.toggle([1, 2, 3], value=1)
|
|
|
- ui.toggle({1: 'A', 2: 'B', 3: 'C'}, value=1).bind_value(toggle, 'value')
|
|
|
-
|
|
|
-with example(ui.select):
|
|
|
- with ui.row():
|
|
|
- select = ui.select([1, 2, 3], value=1).props('inline')
|
|
|
- ui.select({1: 'One', 2: 'Two', 3: 'Three'}, value=1).props('inline').bind_value(select, 'value')
|
|
|
-
|
|
|
with example(ui.upload):
|
|
|
ui.upload(on_upload=lambda e: content.set_text(e.files))
|
|
|
content = ui.label()
|
|
|
|
|
|
+h3('Markdown and HTML')
|
|
|
+
|
|
|
+with example(ui.markdown):
|
|
|
+ ui.markdown('### Headline\nWith hyperlink to [GitHub](https://github.com/zauberzeug/nicegui).')
|
|
|
+
|
|
|
+with example(ui.html):
|
|
|
+ ui.html('<p>demo paragraph in <strong>html</strong></p>')
|
|
|
+
|
|
|
+svg = '''#### SVG
|
|
|
+
|
|
|
+You can add Scalable Vector Graphics using the `ui.html` element.
|
|
|
+'''
|
|
|
+with example(svg):
|
|
|
+ content = '''
|
|
|
+ <svg viewBox="0 0 200 200" width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <circle cx="100" cy="100" r="78" fill="#ffde34" stroke="black" stroke-width="3" />
|
|
|
+ <circle cx="80" cy="85" r="8" />
|
|
|
+ <circle cx="120" cy="85" r="8" />
|
|
|
+ <path d="m60,120 C75,150 125,150 140,120" style="fill:none; stroke:black; stroke-width:8; stroke-linecap:round" />
|
|
|
+ </svg>'''
|
|
|
+ ui.html(content)
|
|
|
+
|
|
|
+h3('Images')
|
|
|
+
|
|
|
+with example(ui.image):
|
|
|
+ ui.image('http://placeimg.com/640/360/tech')
|
|
|
+ base64 = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAACKgAwAEAAAAAQAAACMAAAAA/8IAEQgAIwAiAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAMCBAEFAAYHCAkKC//EAMMQAAEDAwIEAwQGBAcGBAgGcwECAAMRBBIhBTETIhAGQVEyFGFxIweBIJFCFaFSM7EkYjAWwXLRQ5I0ggjhU0AlYxc18JNzolBEsoPxJlQ2ZJR0wmDShKMYcOInRTdls1V1pJXDhfLTRnaA40dWZrQJChkaKCkqODk6SElKV1hZWmdoaWp3eHl6hoeIiYqQlpeYmZqgpaanqKmqsLW2t7i5usDExcbHyMnK0NTV1tfY2drg5OXm5+jp6vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAQIAAwQFBgcICQoL/8QAwxEAAgIBAwMDAgMFAgUCBASHAQACEQMQEiEEIDFBEwUwIjJRFEAGMyNhQhVxUjSBUCSRoUOxFgdiNVPw0SVgwUThcvEXgmM2cCZFVJInotIICQoYGRooKSo3ODk6RkdISUpVVldYWVpkZWZnaGlqc3R1dnd4eXqAg4SFhoeIiYqQk5SVlpeYmZqgo6SlpqeoqaqwsrO0tba3uLm6wMLDxMXGx8jJytDT1NXW19jZ2uDi4+Tl5ufo6ery8/T19vf4+fr/2wBDAAwMDAwMDBUMDBUeFRUVHikeHh4eKTQpKSkpKTQ+NDQ0NDQ0Pj4+Pj4+Pj5LS0tLS0tXV1dXV2JiYmJiYmJiYmL/2wBDAQ8QEBkXGSsXFytnRjlGZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2f/2gAMAwEAAhEDEQAAAeqBgCIareozvbaK3avBqa52teT6He3z0TqCUZa22r//2gAIAQEAAQUCaVVKTjGnLFqSqqlGuciX+87YgM8ScWhAx5KWUJUdJClKadMye6O//9oACAEDEQE/AUxI86A0ynfb/9oACAECEQE/ASaYZBLxpKNinFh2dv8A/9oACAEBAAY/AmUniHVXxfVx7ZIP9x0GlOJdfa+BeVentkSWR66jsI1HUfF+f4l1UykiqR/CypAorg6n/hvuH5nv/8QAMxABAAMAAgICAgIDAQEAAAILAREAITFBUWFxgZGhscHw0RDh8SAwQFBgcICQoLDA0OD/2gAIAQEAAT8hrchP08Nlp8V+7MHK/wCcEXw8q94vkT4K5DD0fpsJBFkwYvy/8cJBuuX7l82UhL9HmlzVKCOfi+3/ADe6Z2jgePxcMYN/xxYQtAu8UCj/ALXDvn/sBxRB/g3/AL//2gAMAwEAAhEDEQAAEE5gPHEUEAP/xAAzEQEBAQADAAECBQUBAQABAQkBABEhMRBBUWEgcfCRgaGx0cHh8TBAUGBwgJCgsMDQ4P/aAAgBAxEBPxAN4PZaNJuOW/g//9oACAECEQE/EAGt2fwmfzBp3X8P/9oACAEBAAE/ELGubg74j5M+RuAgxMrE4g5c4qAjQh1Oh9GL3/xggJDuHs5H2fY1rQIGDISTZ3KuGYzkk8dSkh4Ah8TJ8c0SsIco+yPRD76/486QSwOdnIpjvmvjAQ8pEx4ixlVcDldAdtawTzP5CSqs1wAPeJDMz0nwvHVlRSYTI1ic6b58RUC4kuSTXmFOJuxknJgsgDQMkjQgj/gCBHee6QjzflUA4/5//9k='
|
|
|
+ ui.image(base64).style('width:30px')
|
|
|
+
|
|
|
+captions_and_overlays = '''#### Captions and Overlays
|
|
|
+
|
|
|
+By nesting elements inside a `ui.image` you can create augmentations.
|
|
|
+
|
|
|
+Use [Quasar classes](https://quasar.dev/vue-components/img) for positioning and styling captions.
|
|
|
+To overlay an svg, make the `viewBox` exactly the size of the image and provide `100%` width/height to match the actual rendered size.
|
|
|
+'''
|
|
|
+with example(captions_and_overlays):
|
|
|
+ with ui.image('http://placeimg.com/640/360/nature'):
|
|
|
+ ui.label('nice').classes('absolute-bottom text-subtitle2 text-center')
|
|
|
+
|
|
|
+ with ui.image('https://cdn.pixabay.com/photo/2020/07/13/12/56/mute-swan-5400675__340.jpg'):
|
|
|
+ content = '''
|
|
|
+ <svg viewBox="0 0 510 340" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <circle cx="200" cy="200" r="100" fill="none" stroke="red" stroke-width="10" />
|
|
|
+ </svg>'''
|
|
|
+ ui.html(content).style('background:transparent')
|
|
|
+
|
|
|
+with example(ui.interactive_image):
|
|
|
+ from nicegui.events import MouseEventArguments
|
|
|
+
|
|
|
+ def mouse_handler(e: MouseEventArguments):
|
|
|
+ color = 'green' if e.type == 'mousedown' else 'orange'
|
|
|
+ ii.svg_content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="10" fill="{color}"/>'
|
|
|
+ ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')
|
|
|
+
|
|
|
+ ii = ui.interactive_image('http://placeimg.com/640/360/arch',
|
|
|
+ on_mouse=mouse_handler,
|
|
|
+ events=['mousedown', 'mouseup'], cross=True)
|
|
|
+
|
|
|
+h3('Data Elements')
|
|
|
+
|
|
|
+with example(ui.table):
|
|
|
+ def update():
|
|
|
+ table.options.rowData[0].age += 1
|
|
|
+ table.update()
|
|
|
+
|
|
|
+ table = ui.table({
|
|
|
+ 'columnDefs': [
|
|
|
+ {'headerName': 'Name', 'field': 'name'},
|
|
|
+ {'headerName': 'Age', 'field': 'age'},
|
|
|
+ ],
|
|
|
+ 'rowData': [
|
|
|
+ {'name': 'Alice', 'age': 18},
|
|
|
+ {'name': 'Bob', 'age': 21},
|
|
|
+ {'name': 'Carol', 'age': 42},
|
|
|
+ ],
|
|
|
+ }).classes('max-h-40')
|
|
|
+ ui.button('Update', on_click=update)
|
|
|
+
|
|
|
+with example(ui.chart):
|
|
|
+ from numpy.random import random
|
|
|
+
|
|
|
+ def update():
|
|
|
+ chart.options.series[0].data[:] = random(2)
|
|
|
+ chart.update()
|
|
|
+
|
|
|
+ chart = ui.chart({
|
|
|
+ 'title': False,
|
|
|
+ 'chart': {'type': 'bar'},
|
|
|
+ 'xAxis': {'categories': ['A', 'B']},
|
|
|
+ 'series': [
|
|
|
+ {'name': 'Alpha', 'data': [0.1, 0.2]},
|
|
|
+ {'name': 'Beta', 'data': [0.3, 0.4]},
|
|
|
+ ],
|
|
|
+ }).classes('max-w-full h-64')
|
|
|
+ ui.button('Update', on_click=update)
|
|
|
+
|
|
|
with example(ui.plot):
|
|
|
import numpy as np
|
|
|
from matplotlib import pyplot as plt
|
|
@@ -263,18 +311,6 @@ with example(ui.line_plot):
|
|
|
]), active=False)
|
|
|
ui.checkbox('active').bind_value(line_updates, 'active')
|
|
|
|
|
|
-with example(ui.log):
|
|
|
- from datetime import datetime
|
|
|
-
|
|
|
- log = ui.log(max_lines=10).classes('h-16')
|
|
|
- ui.button('Log time', on_click=lambda: log.push(datetime.now().strftime("%X.%f")[:-5]))
|
|
|
-
|
|
|
-with example(ui.tree):
|
|
|
- ui.tree([
|
|
|
- {'id': 'number', 'children': [{'id': '1'}, {'id': '2'}]},
|
|
|
- {'id': 'letters', 'children': [{'id': 'A'}, {'id': 'B'}]},
|
|
|
- ], label_key='id', on_select=lambda e: ui.notify(e.value))
|
|
|
-
|
|
|
with example(ui.scene):
|
|
|
with ui.scene(width=200, height=200) as scene:
|
|
|
scene.sphere().material('#4488ff')
|
|
@@ -299,49 +335,79 @@ with example(ui.scene):
|
|
|
scene.text('2D', 'background: rgba(0, 0, 0, 0.2); border-radius: 5px; padding: 5px').move(z=2)
|
|
|
scene.text3d('3D', 'background: rgba(0, 0, 0, 0.2); border-radius: 5px; padding: 5px').move(y=-2).scale(.05)
|
|
|
|
|
|
-with example(ui.chart):
|
|
|
- from numpy.random import random
|
|
|
+with example(ui.tree):
|
|
|
+ ui.tree([
|
|
|
+ {'id': 'number', 'children': [{'id': '1'}, {'id': '2'}]},
|
|
|
+ {'id': 'letters', 'children': [{'id': 'A'}, {'id': 'B'}]},
|
|
|
+ ], label_key='id', on_select=lambda e: ui.notify(e.value))
|
|
|
|
|
|
- def update():
|
|
|
- chart.options.series[0].data[:] = random(2)
|
|
|
- chart.update()
|
|
|
+with example(ui.log):
|
|
|
+ from datetime import datetime
|
|
|
|
|
|
- chart = ui.chart({
|
|
|
- 'title': False,
|
|
|
- 'chart': {'type': 'bar'},
|
|
|
- 'xAxis': {'categories': ['A', 'B']},
|
|
|
- 'series': [
|
|
|
- {'name': 'Alpha', 'data': [0.1, 0.2]},
|
|
|
- {'name': 'Beta', 'data': [0.3, 0.4]},
|
|
|
- ],
|
|
|
- }).classes('max-w-full h-64')
|
|
|
- ui.button('Update', on_click=update)
|
|
|
+ log = ui.log(max_lines=10).classes('h-16')
|
|
|
+ ui.button('Log time', on_click=lambda: log.push(datetime.now().strftime("%X.%f")[:-5]))
|
|
|
|
|
|
-with example(ui.table):
|
|
|
- def update():
|
|
|
- table.options.rowData[0].age += 1
|
|
|
- table.update()
|
|
|
+h3('Layout')
|
|
|
|
|
|
- table = ui.table({
|
|
|
- 'columnDefs': [
|
|
|
- {'headerName': 'Name', 'field': 'name'},
|
|
|
- {'headerName': 'Age', 'field': 'age'},
|
|
|
- ],
|
|
|
- 'rowData': [
|
|
|
- {'name': 'Alice', 'age': 18},
|
|
|
- {'name': 'Bob', 'age': 21},
|
|
|
- {'name': 'Carol', 'age': 42},
|
|
|
- ],
|
|
|
- }).classes('max-h-40')
|
|
|
- ui.button('Update', on_click=update)
|
|
|
+with example(ui.card):
|
|
|
+ with ui.card().tight():
|
|
|
+ ui.image('http://placeimg.com/640/360/nature')
|
|
|
+ with ui.card_section():
|
|
|
+ ui.label('Lorem ipsum dolor sit amet, consectetur adipiscing elit, ...')
|
|
|
|
|
|
-with example(ui.joystick):
|
|
|
- ui.joystick(
|
|
|
- color='blue',
|
|
|
- size=50,
|
|
|
- on_move=lambda msg: coordinates.set_text(f'{msg.data.vector.x:.3f}, {msg.data.vector.y:.3f}'),
|
|
|
- on_end=lambda _: coordinates.set_text('0, 0'))
|
|
|
- coordinates = ui.label('0, 0')
|
|
|
+with example(ui.column):
|
|
|
+ with ui.column():
|
|
|
+ ui.label('label 1')
|
|
|
+ ui.label('label 2')
|
|
|
+ ui.label('label 3')
|
|
|
+
|
|
|
+with example(ui.row):
|
|
|
+ with ui.row():
|
|
|
+ ui.label('label 1')
|
|
|
+ ui.label('label 2')
|
|
|
+ ui.label('label 3')
|
|
|
+
|
|
|
+clear_containers = '''#### Clear Containers
|
|
|
+
|
|
|
+To remove all elements from a row, column or card container, use the `clear()` method.
|
|
|
+'''
|
|
|
+with example(clear_containers):
|
|
|
+ container = ui.row()
|
|
|
+
|
|
|
+ def add_face():
|
|
|
+ with container:
|
|
|
+ ui.icon('face')
|
|
|
+ add_face()
|
|
|
+
|
|
|
+ ui.button('Add', on_click=add_face)
|
|
|
+ ui.button('Clear', on_click=container.clear)
|
|
|
+
|
|
|
+with example(ui.expansion):
|
|
|
+ with ui.expansion('Expand!', icon='work').classes('w-full'):
|
|
|
+ ui.label('inside the expansion')
|
|
|
+
|
|
|
+with example(ui.menu):
|
|
|
+ choice = ui.label('Try the menu.')
|
|
|
+ with ui.menu() as menu:
|
|
|
+ ui.menu_item('Menu item 1', lambda: choice.set_text('Selected item 1.'))
|
|
|
+ ui.menu_item('Menu item 2', lambda: choice.set_text('Selected item 2.'))
|
|
|
+ ui.menu_item('Menu item 3 (keep open)', lambda: choice.set_text('Selected item 3.'), auto_close=False)
|
|
|
+ ui.menu_separator()
|
|
|
+ ui.menu_item('Close', on_click=menu.close)
|
|
|
+
|
|
|
+ ui.button('Open menu', on_click=menu.open).props('color=secondary')
|
|
|
+
|
|
|
+tooltips = '''#### Tooltips
|
|
|
+
|
|
|
+Simply call the `tooltip(text:str)` method on UI elements to provide a tooltip.
|
|
|
+'''
|
|
|
+with example(tooltips):
|
|
|
+ with ui.row():
|
|
|
+ ui.button().props('icon=thumb_up').tooltip('I like this')
|
|
|
+ ui.label('tooltips').classes('q-mt-sm').tooltip('tooltips are shown on mouse over')
|
|
|
+
|
|
|
+with example(ui.notify):
|
|
|
+ ui.button('Show notification', on_click=lambda: ui.notify('Some message', close_button='OK'))
|
|
|
|
|
|
with example(ui.dialog):
|
|
|
with ui.dialog() as dialog, ui.card():
|
|
@@ -350,7 +416,8 @@ with example(ui.dialog):
|
|
|
|
|
|
ui.button('Open a dialog', on_click=dialog.open)
|
|
|
|
|
|
-async_dialog = '''### Awaitable dialog
|
|
|
+async_dialog = '''#### Awaitable dialog
|
|
|
+
|
|
|
Dialogs can be awaited.
|
|
|
Use the `submit` method to close the dialog and return a result.
|
|
|
Canceling the dialog by clicking in the background or pressing the escape key yields `None`.
|
|
@@ -368,33 +435,9 @@ with example(async_dialog):
|
|
|
|
|
|
ui.button('Await a dialog', on_click=show)
|
|
|
|
|
|
-tooltip = '''### Tooltips
|
|
|
-Simply call the `tooltip(text:str)` method on UI elements to provide a tooltip.
|
|
|
-'''
|
|
|
-with example(tooltip):
|
|
|
- with ui.row():
|
|
|
- ui.button().props('icon=thumb_up').tooltip('I like this')
|
|
|
- ui.label('tooltips').classes('q-mt-sm').tooltip('tooltips are shown on mouse over')
|
|
|
-
|
|
|
-with example(ui.menu):
|
|
|
- choice = ui.label('Try the menu.')
|
|
|
- with ui.menu() as menu:
|
|
|
- ui.menu_item('Menu item 1', lambda: choice.set_text('Selected item 1.'))
|
|
|
- ui.menu_item('Menu item 2', lambda: choice.set_text('Selected item 2.'))
|
|
|
- ui.menu_item('Menu item 3 (keep open)', lambda: choice.set_text('Selected item 3.'), auto_close=False)
|
|
|
- ui.menu_separator()
|
|
|
- ui.menu_item('Close', on_click=menu.close)
|
|
|
-
|
|
|
- ui.button('Open menu', on_click=menu.open).props('color=secondary')
|
|
|
-
|
|
|
-with example(ui.expansion):
|
|
|
- with ui.expansion('Expand!', icon='work').classes('w-full'):
|
|
|
- ui.label('inside the expansion')
|
|
|
-
|
|
|
-with example(ui.notify):
|
|
|
- ui.button('Show notification', on_click=lambda: ui.notify('Some message', close_button='OK'))
|
|
|
+h3('Appearance')
|
|
|
|
|
|
-design = '''### Styling
|
|
|
+design = '''#### Styling
|
|
|
|
|
|
NiceGUI uses the [Quasar Framework](https://quasar.dev/) version 1.0 and hence has its full design power.
|
|
|
Each NiceGUI element provides a `props` method whose content is passed [to the Quasar component](https://justpy.io/quasar_tutorial/introduction/#props-of-quasar-components):
|
|
@@ -414,59 +457,33 @@ with example(ui.colors):
|
|
|
ui.button('Default', on_click=lambda: ui.colors())
|
|
|
ui.colors()
|
|
|
|
|
|
-with example(ui.card):
|
|
|
- with ui.card().tight():
|
|
|
- ui.image('http://placeimg.com/640/360/nature')
|
|
|
- with ui.card_section():
|
|
|
- ui.label('Lorem ipsum dolor sit amet, consectetur adipiscing elit, ...')
|
|
|
+h3('Action')
|
|
|
|
|
|
-with example(ui.column):
|
|
|
- with ui.column():
|
|
|
- ui.label('label 1')
|
|
|
- ui.label('label 2')
|
|
|
- ui.label('label 3')
|
|
|
+lifecycle = '''#### Lifecycle
|
|
|
|
|
|
-with example(ui.row):
|
|
|
- with ui.row():
|
|
|
- ui.label('label 1')
|
|
|
- ui.label('label 2')
|
|
|
- ui.label('label 3')
|
|
|
+You can run a function or coroutine as a parallel task by passing it to one of the following register methods:
|
|
|
|
|
|
-clear = '''### Clear Containers
|
|
|
+- `ui.on_startup`: Called when NiceGUI is started or restarted.
|
|
|
+- `ui.on_shutdown`: Called when NiceGUI is shut down or restarted.
|
|
|
+- `ui.on_connect`: Called when a client connects to NiceGUI. (Optional argument: Starlette request)
|
|
|
+- `ui.on_page_ready`: Called when the page is ready and the websocket is connected. (Optional argument: socket)
|
|
|
+- `ui.on_disconnect`: Called when a client disconnects from NiceGUI.
|
|
|
|
|
|
-To remove all elements from a row, column or card container, use the `clear()` method.
|
|
|
+When NiceGUI is shut down or restarted, the startup tasks will be automatically canceled.
|
|
|
'''
|
|
|
-with example(clear):
|
|
|
- container = ui.row()
|
|
|
-
|
|
|
- def add_face():
|
|
|
- with container:
|
|
|
- ui.icon('face')
|
|
|
- add_face()
|
|
|
-
|
|
|
- ui.button('Add', on_click=add_face)
|
|
|
- ui.button('Clear', on_click=container.clear)
|
|
|
-
|
|
|
-binding = '''### Bindings
|
|
|
+with example(lifecycle):
|
|
|
+ import asyncio
|
|
|
+ import time
|
|
|
|
|
|
-NiceGUI is able to directly bind UI elements to models.
|
|
|
-Binding is possible for UI element properties like text, value or visibility and for model properties that are (nested) class attributes.
|
|
|
+ l = ui.label()
|
|
|
|
|
|
-Each element provides methods like `bind_value` and `bind_visibility` to create a two-way binding with the corresponding property.
|
|
|
-To define a one-way binding use the `_from` and `_to` variants of these methods.
|
|
|
-Just pass a property of the model as parameter to these methods to create the binding.
|
|
|
-'''
|
|
|
-with example(binding):
|
|
|
- class Demo:
|
|
|
- def __init__(self):
|
|
|
- self.number = 1
|
|
|
+ async def run_clock():
|
|
|
+ while True:
|
|
|
+ l.text = f'unix time: {time.time():.1f}'
|
|
|
+ await asyncio.sleep(1)
|
|
|
|
|
|
- demo = Demo()
|
|
|
- v = ui.checkbox('visible', value=True)
|
|
|
- with ui.column().bind_visibility_from(v, 'value'):
|
|
|
- ui.slider(min=1, max=3).bind_value(demo, 'number')
|
|
|
- ui.toggle({1: 'a', 2: 'b', 3: 'c'}).bind_value(demo, 'number')
|
|
|
- ui.number().bind_value(demo, 'number')
|
|
|
+ ui.on_startup(run_clock)
|
|
|
+ ui.on_connect(lambda: l.set_text('new connection'))
|
|
|
|
|
|
with example(ui.timer):
|
|
|
from datetime import datetime
|
|
@@ -485,39 +502,57 @@ with example(ui.timer):
|
|
|
lazy_clock = ui.label()
|
|
|
ui.timer(interval=0.1, callback=lazy_update)
|
|
|
|
|
|
-lifecycle = '''### Lifecycle
|
|
|
+with example(ui.keyboard):
|
|
|
+ from nicegui.events import KeyEventArguments
|
|
|
|
|
|
-You can run a function or coroutine as a parallel task by passing it to one of the following register methods:
|
|
|
+ def handle_key(e: KeyEventArguments):
|
|
|
+ if e.key == 'f' and not e.action.repeat:
|
|
|
+ if e.action.keyup:
|
|
|
+ ui.notify('f was just released')
|
|
|
+ elif e.action.keydown:
|
|
|
+ ui.notify('f was just pressed')
|
|
|
+ if e.modifiers.shift and e.action.keydown:
|
|
|
+ if e.key.arrow_left:
|
|
|
+ ui.notify('going left')
|
|
|
+ elif e.key.arrow_right:
|
|
|
+ ui.notify('going right')
|
|
|
+ elif e.key.arrow_up:
|
|
|
+ ui.notify('going up')
|
|
|
+ elif e.key.arrow_down:
|
|
|
+ ui.notify('going down')
|
|
|
|
|
|
-- `ui.on_startup`: Called when NiceGUI is started or restarted.
|
|
|
-- `ui.on_shutdown`: Called when NiceGUI is shut down or restarted.
|
|
|
-- `ui.on_connect`: Called when a client connects to NiceGUI. (Optional argument: Starlette request)
|
|
|
-- `ui.on_page_ready`: Called when the page is ready and the websocket is connected. (Optional argument: socket)
|
|
|
-- `ui.on_disconnect`: Called when a client disconnects from NiceGUI.
|
|
|
+ keyboard = ui.keyboard(on_key=handle_key)
|
|
|
+ ui.label('Key events can be caught globally by using the keyboard element.')
|
|
|
+ ui.checkbox('Track key events').bind_value_to(keyboard, 'active')
|
|
|
|
|
|
-When NiceGUI is shut down or restarted, the startup tasks will be automatically canceled.
|
|
|
-'''
|
|
|
-with example(lifecycle):
|
|
|
- import asyncio
|
|
|
- import time
|
|
|
+bindings = '''#### Bindings
|
|
|
|
|
|
- l = ui.label()
|
|
|
+NiceGUI is able to directly bind UI elements to models.
|
|
|
+Binding is possible for UI element properties like text, value or visibility and for model properties that are (nested) class attributes.
|
|
|
|
|
|
- async def run_clock():
|
|
|
- while True:
|
|
|
- l.text = f'unix time: {time.time():.1f}'
|
|
|
- await asyncio.sleep(1)
|
|
|
+Each element provides methods like `bind_value` and `bind_visibility` to create a two-way binding with the corresponding property.
|
|
|
+To define a one-way binding use the `_from` and `_to` variants of these methods.
|
|
|
+Just pass a property of the model as parameter to these methods to create the binding.
|
|
|
+'''
|
|
|
+with example(bindings):
|
|
|
+ class Demo:
|
|
|
+ def __init__(self):
|
|
|
+ self.number = 1
|
|
|
|
|
|
- ui.on_startup(run_clock)
|
|
|
- ui.on_connect(lambda: l.set_text('new connection'))
|
|
|
+ demo = Demo()
|
|
|
+ v = ui.checkbox('visible', value=True)
|
|
|
+ with ui.column().bind_visibility_from(v, 'value'):
|
|
|
+ ui.slider(min=1, max=3).bind_value(demo, 'number')
|
|
|
+ ui.toggle({1: 'a', 2: 'b', 3: 'c'}).bind_value(demo, 'number')
|
|
|
+ ui.number().bind_value(demo, 'number')
|
|
|
|
|
|
-updates = '''### UI Updates
|
|
|
+ui_updates = '''#### UI Updates
|
|
|
|
|
|
NiceGUI tries to automatically synchronize the state of UI elements with the client, e.g. when a label text, an input value or style/classes/props of an element have changed.
|
|
|
In other cases, you can explicitly call `element.update()` or `ui.update(*elements)` to update.
|
|
|
The example code shows both methods for a `ui.table`, where it is difficult to automatically detect changes in the `options` dictionary.
|
|
|
'''
|
|
|
-with example(updates):
|
|
|
+with example(ui_updates):
|
|
|
from random import randint
|
|
|
|
|
|
def add():
|
|
@@ -532,8 +567,21 @@ with example(updates):
|
|
|
ui.button('Add', on_click=add)
|
|
|
ui.button('Clear', on_click=clear)
|
|
|
|
|
|
-with example(ui.link):
|
|
|
- ui.link('NiceGUI on GitHub', 'https://github.com/zauberzeug/nicegui')
|
|
|
+async_handlers = '''#### Async event handlers
|
|
|
+
|
|
|
+Most elements also support asynchronous event handlers.
|
|
|
+
|
|
|
+Note: You can also pass a `functools.partial` into the `on_click` property to wrap async functions with parameters.
|
|
|
+'''
|
|
|
+with example(async_handlers):
|
|
|
+ async def async_task():
|
|
|
+ ui.notify('Asynchronous task started')
|
|
|
+ await asyncio.sleep(5)
|
|
|
+ ui.notify('Asynchronous task finished')
|
|
|
+
|
|
|
+ ui.button('start async task', on_click=async_task)
|
|
|
+
|
|
|
+h3('Pages and Routes')
|
|
|
|
|
|
with example(ui.page):
|
|
|
with ui.page('/other_page'):
|
|
@@ -554,37 +602,11 @@ with example(ui.open):
|
|
|
|
|
|
ui.button('REDIRECT', on_click=lambda e: ui.open(other, e.socket))
|
|
|
|
|
|
-sessions = """### Sessions
|
|
|
-
|
|
|
-`ui.page` provides an optional `on_connect` argument to register a callback.
|
|
|
-It is invoked for each new connection to the page.
|
|
|
-
|
|
|
-The optional `request` argument provides insights about the clients URL parameters etc. (see [the JustPy docs](https://justpy.io/tutorial/request_object/) for more details).
|
|
|
-It also enables you to identify sessions over [longer time spans by configuring cookies](https://justpy.io/tutorial/sessions/).
|
|
|
-"""
|
|
|
-with example(sessions):
|
|
|
- from collections import Counter
|
|
|
- from datetime import datetime
|
|
|
-
|
|
|
- from starlette.requests import Request
|
|
|
-
|
|
|
- id_counter = Counter()
|
|
|
- creation = datetime.now().strftime('%H:%M, %d %B %Y')
|
|
|
-
|
|
|
- def handle_connection(request: Request):
|
|
|
- id_counter[request.session_id] += 1
|
|
|
- visits.set_text(f'{len(id_counter)} unique views ({sum(id_counter.values())} overall) since {creation}')
|
|
|
-
|
|
|
- with ui.page('/session_demo', on_connect=handle_connection) as page:
|
|
|
- visits = ui.label()
|
|
|
-
|
|
|
- ui.link('Visit session demo', page)
|
|
|
-
|
|
|
-add_route = """### Route
|
|
|
+add_route = '''#### Route
|
|
|
|
|
|
Add a new route by calling `ui.add_route` with a starlette route including a path and a function to be called.
|
|
|
Routed paths must start with a `'/'`.
|
|
|
-"""
|
|
|
+'''
|
|
|
with example(add_route):
|
|
|
import starlette
|
|
|
|
|
@@ -594,7 +616,7 @@ with example(add_route):
|
|
|
|
|
|
ui.link('Try the new route!', 'new/route')
|
|
|
|
|
|
-get_decorator = """### Get decorator
|
|
|
+get_decorator = '''#### Get decorator
|
|
|
|
|
|
Syntactic sugar to add routes.
|
|
|
Decorating a function with the `@ui.get` makes it available at the specified endpoint, e.g. `'/another/route/<id>'`.
|
|
@@ -602,7 +624,7 @@ Decorating a function with the `@ui.get` makes it available at the specified end
|
|
|
Path parameters can be passed to the request handler like with [FastAPI](https://fastapi.tiangolo.com/tutorial/path-params/).
|
|
|
If type-annotated, they are automatically converted to `bool`, `int`, `float` and `complex` values.
|
|
|
An optional `request` argument gives access to the complete request object.
|
|
|
-"""
|
|
|
+'''
|
|
|
with example(get_decorator):
|
|
|
from starlette import requests, responses
|
|
|
|
|
@@ -612,27 +634,30 @@ with example(get_decorator):
|
|
|
|
|
|
ui.link('Try yet another route!', 'another/route/42')
|
|
|
|
|
|
-with example(ui.keyboard):
|
|
|
- from nicegui.events import KeyEventArguments
|
|
|
+sessions = '''#### Sessions
|
|
|
|
|
|
- def handle_key(e: KeyEventArguments):
|
|
|
- if e.key == 'f' and not e.action.repeat:
|
|
|
- if e.action.keyup:
|
|
|
- ui.notify('f was just released')
|
|
|
- elif e.action.keydown:
|
|
|
- ui.notify('f was just pressed')
|
|
|
- if e.modifiers.shift and e.action.keydown:
|
|
|
- if e.key.arrow_left:
|
|
|
- ui.notify('going left')
|
|
|
- elif e.key.arrow_right:
|
|
|
- ui.notify('going right')
|
|
|
- elif e.key.arrow_up:
|
|
|
- ui.notify('going up')
|
|
|
- elif e.key.arrow_down:
|
|
|
- ui.notify('going down')
|
|
|
+`ui.page` provides an optional `on_connect` argument to register a callback.
|
|
|
+It is invoked for each new connection to the page.
|
|
|
|
|
|
- keyboard = ui.keyboard(on_key=handle_key)
|
|
|
- ui.label('Key events can be caught globally by using the keyboard element.')
|
|
|
- ui.checkbox('Track key events').bind_value_to(keyboard, 'active')
|
|
|
+The optional `request` argument provides insights about the clients URL parameters etc. (see [the JustPy docs](https://justpy.io/tutorial/request_object/) for more details).
|
|
|
+It also enables you to identify sessions over [longer time spans by configuring cookies](https://justpy.io/tutorial/sessions/).
|
|
|
+'''
|
|
|
+with example(sessions):
|
|
|
+ from collections import Counter
|
|
|
+ from datetime import datetime
|
|
|
+
|
|
|
+ from starlette.requests import Request
|
|
|
+
|
|
|
+ id_counter = Counter()
|
|
|
+ creation = datetime.now().strftime('%H:%M, %d %B %Y')
|
|
|
+
|
|
|
+ def handle_connection(request: Request):
|
|
|
+ id_counter[request.session_id] += 1
|
|
|
+ visits.set_text(f'{len(id_counter)} unique views ({sum(id_counter.values())} overall) since {creation}')
|
|
|
+
|
|
|
+ with ui.page('/session_demo', on_connect=handle_connection) as page:
|
|
|
+ visits = ui.label()
|
|
|
+
|
|
|
+ ui.link('Visit session demo', page)
|
|
|
|
|
|
ui.run()
|