|
@@ -1,6 +1,5 @@
|
|
|
import inspect
|
|
|
import re
|
|
|
-from contextlib import contextmanager
|
|
|
from typing import Callable, Union
|
|
|
|
|
|
import docutils.core
|
|
@@ -14,57 +13,35 @@ REGEX_H4 = re.compile(r'<h4.*?>(.*?)</h4>')
|
|
|
SPECIAL_CHARACTERS = re.compile('[^(a-z)(A-Z)(0-9)-]')
|
|
|
|
|
|
|
|
|
-@contextmanager
|
|
|
-def example(content: Union[Callable, type, str], tight: bool = False) -> None:
|
|
|
- callFrame = inspect.currentframe().f_back.f_back
|
|
|
- begin = callFrame.f_lineno
|
|
|
+class example:
|
|
|
|
|
|
- def add_html_anchor(element: ui.html):
|
|
|
- html = element.content
|
|
|
- match = REGEX_H4.search(html)
|
|
|
- if not match:
|
|
|
- return
|
|
|
- headline = match.groups()[0].strip()
|
|
|
- headline_id = SPECIAL_CHARACTERS.sub('_', headline).lower()
|
|
|
- if not headline_id:
|
|
|
- return
|
|
|
+ def __init__(self, content: Union[Callable, type, str], tight: bool = False) -> None:
|
|
|
+ self.content = content
|
|
|
+ self.markdown_classes = f'mr-8 w-full flex-none lg:w-{48 if tight else 80} xl:w-80'
|
|
|
+ self.rendering_classes = f'w-{48 if tight else 64} flex-none lg:mt-12'
|
|
|
+ self.source_classes = f'w-80 flex-grow overflow-auto lg:mt-12'
|
|
|
|
|
|
- icon = '<span class="material-icons">link</span>'
|
|
|
- anchor = f'<a href="reference#{headline_id}" class="text-gray-300 hover:text-black">{icon}</a>'
|
|
|
- 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'):
|
|
|
- markdown_classes = f'mr-8 w-full flex-none lg:w-{48 if tight else 80} xl:w-80'
|
|
|
- rendering_classes = f'w-{48 if tight else 64} flex-none lg:mt-12'
|
|
|
- source_classes = f'w-80 flex-grow overflow-auto lg:mt-12'
|
|
|
-
|
|
|
- if isinstance(content, str):
|
|
|
- add_html_anchor(ui.markdown(content).classes(markdown_classes))
|
|
|
- else:
|
|
|
- doc = content.__doc__ or content.__init__.__doc__
|
|
|
- html = docutils.core.publish_parts(doc, writer_name='html')['html_body']
|
|
|
- html = html.replace('<p>', '<h4>', 1)
|
|
|
- html = html.replace('</p>', '</h4>', 1)
|
|
|
- html = apply_tailwind(html)
|
|
|
- add_html_anchor(ui.html(html).classes(markdown_classes))
|
|
|
-
|
|
|
- try:
|
|
|
- with ui.card().classes(rendering_classes):
|
|
|
- yield
|
|
|
- finally:
|
|
|
- code: str = open(__file__).read()
|
|
|
- end = begin + 1
|
|
|
- lines = code.splitlines()
|
|
|
- while True:
|
|
|
- end += 1
|
|
|
- if end >= len(lines):
|
|
|
- break
|
|
|
- if inspect.indentsize(lines[end]) < inspect.indentsize(lines[begin]) and lines[end]:
|
|
|
- break
|
|
|
- code = lines[begin:end]
|
|
|
+ def __call__(self, f: Callable) -> Callable:
|
|
|
+ with ui.row().classes('flex w-full'):
|
|
|
+ if isinstance(self.content, str):
|
|
|
+ self._add_html_anchor(ui.markdown(self.content).classes(self.markdown_classes))
|
|
|
+ else:
|
|
|
+ doc = self.content.__doc__ or self.content.__init__.__doc__
|
|
|
+ html: str = docutils.core.publish_parts(doc, writer_name='html')['html_body']
|
|
|
+ html = html.replace('<p>', '<h4>', 1)
|
|
|
+ html = html.replace('</p>', '</h4>', 1)
|
|
|
+ html = apply_tailwind(html)
|
|
|
+ self._add_html_anchor(ui.html(html).classes(self.markdown_classes))
|
|
|
+
|
|
|
+ with ui.card().classes(self.rendering_classes):
|
|
|
+ f()
|
|
|
+
|
|
|
+ code = inspect.getsource(f).splitlines()
|
|
|
+ while not code[0].startswith(' ' * 8):
|
|
|
+ del code[0]
|
|
|
code = [l[8:] for l in code]
|
|
|
+ while code[0].startswith('global '):
|
|
|
+ del code[0]
|
|
|
code.insert(0, '```python')
|
|
|
code.insert(1, 'from nicegui import ui')
|
|
|
if code[2].split()[0] not in ['from', 'import']:
|
|
@@ -75,29 +52,47 @@ def example(content: Union[Callable, type, str], tight: bool = False) -> None:
|
|
|
if line.startswith('# ui.run('):
|
|
|
break
|
|
|
else:
|
|
|
+ code.append('')
|
|
|
code.append('ui.run()')
|
|
|
code.append('```')
|
|
|
code = '\n'.join(code)
|
|
|
- ui.markdown(code).classes(source_classes)
|
|
|
+ ui.markdown(code).classes(self.source_classes)
|
|
|
+ return f
|
|
|
+
|
|
|
+ def _add_html_anchor(self, element: ui.html) -> None:
|
|
|
+ html = element.content
|
|
|
+ match = REGEX_H4.search(html)
|
|
|
+ if not match:
|
|
|
+ return
|
|
|
+ headline = match.groups()[0].strip()
|
|
|
+ headline_id = SPECIAL_CHARACTERS.sub('_', headline).lower()
|
|
|
+ if not headline_id:
|
|
|
+ return
|
|
|
+
|
|
|
+ icon = '<span class="material-icons">link</span>'
|
|
|
+ anchor = f'<a href="reference#{headline_id}" class="text-gray-300 hover:text-black">{icon}</a>'
|
|
|
+ html = html.replace('<h4', f'<h4 id="{headline_id}"', 1)
|
|
|
+ html = html.replace('</h4>', f' {anchor}</h4>', 1)
|
|
|
+ element.view.inner_html = html
|
|
|
|
|
|
|
|
|
def create_intro() -> None:
|
|
|
# add docutils css to webpage
|
|
|
ui.add_head_html(docutils.core.publish_parts('', writer_name='html')['stylesheet'])
|
|
|
|
|
|
- hello_world = '''#### Hello, World!
|
|
|
+ @example('''#### Hello, World!
|
|
|
|
|
|
Creating a user interface with NiceGUI is as simple as writing a single line of code.
|
|
|
-'''
|
|
|
- with example(hello_world, tight=True):
|
|
|
+''', tight=True)
|
|
|
+ def hello_world_example():
|
|
|
ui.label('Hello, world!')
|
|
|
ui.markdown('Have a look at the full <br/> [API reference](reference)!')
|
|
|
|
|
|
- common_elements = '''#### Common UI Elements
|
|
|
+ @example('''#### Common UI Elements
|
|
|
|
|
|
NiceGUI comes with a collection of commonly used UI elements.
|
|
|
-'''
|
|
|
- with example(common_elements, tight=True):
|
|
|
+''', tight=True)
|
|
|
+ def common_elements_example():
|
|
|
ui.button('Button', on_click=lambda: ui.notify('Click'))
|
|
|
ui.checkbox('Checkbox', on_change=lambda e: ui.notify('Checked' if e.value else 'Unchecked'))
|
|
|
ui.switch('Switch', on_change=lambda e: ui.notify('Switched' if e.value else 'Unswitched'))
|
|
@@ -106,16 +101,14 @@ NiceGUI comes with a collection of commonly used UI elements.
|
|
|
ui.select(['One', 'Two'], value='One', on_change=lambda e: ui.notify(e.value))
|
|
|
ui.link('And many more...', '/reference').classes('text-lg')
|
|
|
|
|
|
- binding = '''#### Value Binding
|
|
|
+ @example('''#### Value Binding
|
|
|
|
|
|
Binding values between UI elements or [to data models](http://127.0.0.1:8080/reference#bindings) is built into NiceGUI.
|
|
|
-'''
|
|
|
- with example(binding, tight=True):
|
|
|
+''', tight=True)
|
|
|
+ def binding_example():
|
|
|
slider = ui.slider(min=0, max=100, value=50)
|
|
|
ui.number('Value').bind_value(slider, 'value').classes('fit')
|
|
|
|
|
|
- # HACK: this comment prevents another blank line sneaking into the example above
|
|
|
-
|
|
|
|
|
|
def create_full() -> None:
|
|
|
# add docutils css to webpage
|
|
@@ -128,87 +121,106 @@ def create_full() -> None:
|
|
|
|
|
|
h3('Basic Elements')
|
|
|
|
|
|
- with example(ui.label):
|
|
|
+ @example(ui.label)
|
|
|
+ def label_example():
|
|
|
ui.label('some label')
|
|
|
|
|
|
- with example(ui.icon):
|
|
|
+ @example(ui.icon)
|
|
|
+ def icon_example():
|
|
|
ui.icon('thumb_up')
|
|
|
|
|
|
- with example(ui.link):
|
|
|
+ @example(ui.link)
|
|
|
+ def link_example():
|
|
|
ui.link('NiceGUI on GitHub', 'https://github.com/zauberzeug/nicegui')
|
|
|
|
|
|
- with example(ui.button):
|
|
|
+ @example(ui.button)
|
|
|
+ def button_example():
|
|
|
ui.button('Click me!', on_click=lambda: ui.notify(f'You clicked me!'))
|
|
|
|
|
|
- with example(ui.badge):
|
|
|
+ @example(ui.badge)
|
|
|
+ def badge_example():
|
|
|
with ui.button('Click me!', on_click=lambda: badge.set_text(int(badge.text) + 1)):
|
|
|
badge = ui.badge('0', color='red').props('floating')
|
|
|
|
|
|
- with example(ui.toggle):
|
|
|
+ @example(ui.toggle)
|
|
|
+ def toggle_example():
|
|
|
toggle1 = ui.toggle([1, 2, 3], value=1)
|
|
|
toggle2 = ui.toggle({1: 'A', 2: 'B', 3: 'C'}).bind_value(toggle1, 'value')
|
|
|
|
|
|
- with example(ui.radio):
|
|
|
+ @example(ui.radio)
|
|
|
+ def radio_example():
|
|
|
radio1 = ui.radio([1, 2, 3], value=1).props('inline')
|
|
|
radio2 = ui.radio({1: 'A', 2: 'B', 3: 'C'}).props('inline').bind_value(radio1, 'value')
|
|
|
|
|
|
- with example(ui.select):
|
|
|
+ @example(ui.select)
|
|
|
+ def select_example():
|
|
|
select1 = ui.select([1, 2, 3], value=1)
|
|
|
select2 = ui.select({1: 'One', 2: 'Two', 3: 'Three'}).bind_value(select1, 'value')
|
|
|
|
|
|
- with example(ui.checkbox):
|
|
|
+ @example(ui.checkbox)
|
|
|
+ def checkbox_example():
|
|
|
checkbox = ui.checkbox('check me')
|
|
|
ui.label('Check!').bind_visibility_from(checkbox, 'value')
|
|
|
|
|
|
- with example(ui.switch):
|
|
|
+ @example(ui.switch)
|
|
|
+ def switch_example():
|
|
|
switch = ui.switch('switch me')
|
|
|
ui.label('Switch!').bind_visibility_from(switch, 'value')
|
|
|
|
|
|
- with example(ui.slider):
|
|
|
+ @example(ui.slider)
|
|
|
+ def slider_example():
|
|
|
slider = ui.slider(min=0, max=100, value=50).props('label')
|
|
|
ui.label().bind_text_from(slider, 'value')
|
|
|
|
|
|
- with example(ui.joystick):
|
|
|
+ @example(ui.joystick)
|
|
|
+ def joystick_example():
|
|
|
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 msg: coordinates.set_text('0, 0'))
|
|
|
coordinates = ui.label('0, 0')
|
|
|
|
|
|
- with example(ui.input):
|
|
|
+ @example(ui.input)
|
|
|
+ def input_example():
|
|
|
ui.input(label='Text', placeholder='press ENTER to apply',
|
|
|
on_change=lambda e: input_result.set_text('you typed: ' + e.value))
|
|
|
input_result = ui.label()
|
|
|
|
|
|
- with example(ui.number):
|
|
|
+ @example(ui.number)
|
|
|
+ def number_example():
|
|
|
ui.number(label='Number', value=3.1415927, format='%.2f',
|
|
|
on_change=lambda e: number_result.set_text(f'you entered: {e.value}'))
|
|
|
number_result = ui.label()
|
|
|
|
|
|
- with example(ui.color_input):
|
|
|
+ @example(ui.color_input)
|
|
|
+ def color_input_example():
|
|
|
color_label = ui.label('Change my color!')
|
|
|
ui.color_input(label='Color', value='#000000',
|
|
|
on_change=lambda e: color_label.style(f'color:{e.value}'))
|
|
|
|
|
|
- with example(ui.color_picker):
|
|
|
+ @example(ui.color_picker)
|
|
|
+ def color_picker_example():
|
|
|
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.upload):
|
|
|
+ @example(ui.upload)
|
|
|
+ def upload_example():
|
|
|
ui.upload(on_upload=lambda e: ui.notify(f'{len(e.files[0])} bytes'))
|
|
|
|
|
|
h3('Markdown and HTML')
|
|
|
|
|
|
- with example(ui.markdown):
|
|
|
+ @example(ui.markdown)
|
|
|
+ def markdown_example():
|
|
|
ui.markdown('''This is **Markdown**.''')
|
|
|
|
|
|
- with example(ui.html):
|
|
|
+ @example(ui.html)
|
|
|
+ def html_example():
|
|
|
ui.html('This is <strong>HTML</strong>.')
|
|
|
|
|
|
- svg = '''#### SVG
|
|
|
+ @example('''#### SVG
|
|
|
|
|
|
You can add Scalable Vector Graphics using the `ui.html` element.
|
|
|
-'''
|
|
|
- with example(svg):
|
|
|
+''')
|
|
|
+ def svg_example():
|
|
|
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" />
|
|
@@ -220,17 +232,18 @@ You can add Scalable Vector Graphics using the `ui.html` element.
|
|
|
|
|
|
h3('Images')
|
|
|
|
|
|
- with example(ui.image):
|
|
|
+ @example(ui.image)
|
|
|
+ def image_example():
|
|
|
ui.image('http://placeimg.com/640/360/tech')
|
|
|
|
|
|
- captions_and_overlays = '''#### Captions and Overlays
|
|
|
+ @example('''#### 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):
|
|
|
+''')
|
|
|
+ def captions_and_overlays_example():
|
|
|
with ui.image('http://placeimg.com/640/360/nature'):
|
|
|
ui.label('Nice!').classes('absolute-bottom text-subtitle2 text-center')
|
|
|
|
|
@@ -241,7 +254,8 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
</svg>'''
|
|
|
ui.html(content).style('background:transparent')
|
|
|
|
|
|
- with example(ui.interactive_image):
|
|
|
+ @example(ui.interactive_image)
|
|
|
+ def interactive_image_example():
|
|
|
from nicegui.events import MouseEventArguments
|
|
|
|
|
|
def mouse_handler(e: MouseEventArguments):
|
|
@@ -254,7 +268,8 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
|
|
|
h3('Data Elements')
|
|
|
|
|
|
- with example(ui.table):
|
|
|
+ @example(ui.table)
|
|
|
+ def table_example():
|
|
|
table = ui.table({
|
|
|
'columnDefs': [
|
|
|
{'headerName': 'Name', 'field': 'name'},
|
|
@@ -273,7 +288,8 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
|
|
|
ui.button('Update', on_click=update)
|
|
|
|
|
|
- with example(ui.chart):
|
|
|
+ @example(ui.chart)
|
|
|
+ def chart_example():
|
|
|
from numpy.random import random
|
|
|
|
|
|
chart = ui.chart({
|
|
@@ -292,7 +308,8 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
|
|
|
ui.button('Update', on_click=update)
|
|
|
|
|
|
- with example(ui.plot):
|
|
|
+ @example(ui.plot)
|
|
|
+ def plot_example():
|
|
|
import numpy as np
|
|
|
from matplotlib import pyplot as plt
|
|
|
|
|
@@ -303,7 +320,9 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
plt.xlabel('time (s)')
|
|
|
plt.ylabel('Damped oscillation')
|
|
|
|
|
|
- with example(ui.line_plot):
|
|
|
+ @example(ui.line_plot)
|
|
|
+ def line_plot_example():
|
|
|
+ global line_checkbox
|
|
|
from datetime import datetime
|
|
|
|
|
|
import numpy as np
|
|
@@ -321,15 +340,18 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
line_updates = ui.timer(0.1, update_line_plot, active=False)
|
|
|
line_checkbox = ui.checkbox('active').bind_value(line_updates, 'active')
|
|
|
|
|
|
- with example(ui.linear_progress):
|
|
|
+ @example(ui.linear_progress)
|
|
|
+ def linear_progress_example():
|
|
|
slider = ui.slider(min=0, max=1, step=0.01, value=0.5)
|
|
|
ui.linear_progress().bind_value_from(slider, 'value')
|
|
|
|
|
|
- with example(ui.circular_progress):
|
|
|
+ @example(ui.circular_progress)
|
|
|
+ def circular_progress_example():
|
|
|
slider = ui.slider(min=0, max=1, step=0.01, value=0.5)
|
|
|
ui.circular_progress().bind_value_from(slider, 'value')
|
|
|
|
|
|
- with example(ui.scene):
|
|
|
+ @example(ui.scene)
|
|
|
+ def scene_example():
|
|
|
with ui.scene(width=225, height=225) as scene:
|
|
|
scene.sphere().material('#4488ff')
|
|
|
scene.cylinder(1, 0.5, 2, 20).material('#ff8800', opacity=0.5).move(-2, 1)
|
|
@@ -353,13 +375,15 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
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.tree):
|
|
|
+ @example(ui.tree)
|
|
|
+ def tree_example():
|
|
|
ui.tree([
|
|
|
{'id': 'numbers', '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.log):
|
|
|
+ @example(ui.log)
|
|
|
+ def log_example():
|
|
|
from datetime import datetime
|
|
|
|
|
|
log = ui.log(max_lines=10).classes('w-full h-16')
|
|
@@ -367,31 +391,34 @@ To overlay an SVG, make the `viewBox` exactly the size of the image and provide
|
|
|
|
|
|
h3('Layout')
|
|
|
|
|
|
- with example(ui.card):
|
|
|
+ @example(ui.card)
|
|
|
+ def card_example():
|
|
|
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.column):
|
|
|
+ @example(ui.column)
|
|
|
+ def column_example():
|
|
|
with ui.column():
|
|
|
ui.label('label 1')
|
|
|
ui.label('label 2')
|
|
|
ui.label('label 3')
|
|
|
|
|
|
- with example(ui.row):
|
|
|
+ @example(ui.row)
|
|
|
+ def row_example():
|
|
|
with ui.row():
|
|
|
ui.label('label 1')
|
|
|
ui.label('label 2')
|
|
|
ui.label('label 3')
|
|
|
|
|
|
- clear_containers = '''#### Clear Containers
|
|
|
+ @example('''#### Clear Containers
|
|
|
|
|
|
To remove all elements from a row, column or card container, use the `clear()` method.
|
|
|
|
|
|
Alternatively, you can remove individual elements with `remove(element)`, where `element` is an Element or an index.
|
|
|
-'''
|
|
|
- with example(clear_containers):
|
|
|
+''')
|
|
|
+ def clear_containers_example():
|
|
|
container = ui.row()
|
|
|
|
|
|
def add_face():
|
|
@@ -403,11 +430,13 @@ Alternatively, you can remove individual elements with `remove(element)`, where
|
|
|
ui.button('Remove', on_click=lambda: container.remove(0))
|
|
|
ui.button('Clear', on_click=container.clear)
|
|
|
|
|
|
- with example(ui.expansion):
|
|
|
+ @example(ui.expansion)
|
|
|
+ def expansion_example():
|
|
|
with ui.expansion('Expand!', icon='work').classes('w-full'):
|
|
|
ui.label('inside the expansion')
|
|
|
|
|
|
- with example(ui.menu):
|
|
|
+ @example(ui.menu)
|
|
|
+ def menu_example():
|
|
|
choice = ui.label('Try the menu.')
|
|
|
with ui.menu() as menu:
|
|
|
ui.menu_item('Menu item 1', lambda: choice.set_text('Selected item 1.'))
|
|
@@ -418,31 +447,33 @@ Alternatively, you can remove individual elements with `remove(element)`, where
|
|
|
|
|
|
ui.button('Open menu', on_click=menu.open)
|
|
|
|
|
|
- tooltips = '''#### Tooltips
|
|
|
+ @example('''#### Tooltips
|
|
|
|
|
|
Simply call the `tooltip(text:str)` method on UI elements to provide a tooltip.
|
|
|
-'''
|
|
|
- with example(tooltips):
|
|
|
+''')
|
|
|
+ def tooltips_example():
|
|
|
ui.label('Tooltips...').tooltip('...are shown on mouse over')
|
|
|
ui.button().props('icon=thumb_up').tooltip('I like this')
|
|
|
|
|
|
- with example(ui.notify):
|
|
|
+ @example(ui.notify)
|
|
|
+ def notify_example():
|
|
|
ui.button('Say hi!', on_click=lambda: ui.notify('Hi!', close_button='OK'))
|
|
|
|
|
|
- with example(ui.dialog):
|
|
|
+ @example(ui.dialog)
|
|
|
+ def dialog_example():
|
|
|
with ui.dialog() as dialog, ui.card():
|
|
|
ui.label('Hello world!')
|
|
|
ui.button('Close', on_click=dialog.close)
|
|
|
|
|
|
ui.button('Open a dialog', on_click=dialog.open)
|
|
|
|
|
|
- async_dialog = '''#### Awaitable dialog
|
|
|
+ @example('''#### 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`.
|
|
|
-'''
|
|
|
- with example(async_dialog):
|
|
|
+''')
|
|
|
+ def async_dialog_example():
|
|
|
with ui.dialog() as dialog, ui.card():
|
|
|
ui.label('Are you sure?')
|
|
|
with ui.row():
|
|
@@ -457,7 +488,7 @@ Canceling the dialog by clicking in the background or pressing the escape key yi
|
|
|
|
|
|
h3('Appearance')
|
|
|
|
|
|
- design = '''#### Styling
|
|
|
+ @example('''#### 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):
|
|
@@ -467,20 +498,21 @@ You can also apply [Tailwind](https://tailwindcss.com/) utility classes with the
|
|
|
If you really need to apply CSS, you can use the `styles` method. Here the delimiter is `;` instead of a blank space.
|
|
|
|
|
|
All three functions also provide `remove` and `replace` parameters in case the predefined look is not wanted in a particular styling.
|
|
|
-'''
|
|
|
- with example(design):
|
|
|
+''')
|
|
|
+ def design_example():
|
|
|
ui.radio(['x', 'y', 'z'], value='x').props('inline color=green')
|
|
|
ui.button().props('icon=touch_app outline round').classes('shadow-lg')
|
|
|
ui.label('Stylish!').style('color: #6E93D6; font-size: 200%; font-weight: 300')
|
|
|
|
|
|
- with example(ui.colors):
|
|
|
+ @example(ui.colors)
|
|
|
+ def colors_example():
|
|
|
ui.colors()
|
|
|
ui.button('Default', on_click=lambda: ui.colors())
|
|
|
ui.button('Gray', on_click=lambda: ui.colors(primary='#555'))
|
|
|
|
|
|
h3('Action')
|
|
|
|
|
|
- lifecycle = '''#### Lifecycle
|
|
|
+ @example('''#### Lifecycle
|
|
|
|
|
|
You can run a function or coroutine as a parallel task by passing it to one of the following register methods:
|
|
|
|
|
@@ -490,8 +522,9 @@ You can run a function or coroutine as a parallel task by passing it to one of t
|
|
|
- `ui.on_disconnect`: Called when a client disconnects from NiceGUI. (Optional argument: socket)
|
|
|
|
|
|
When NiceGUI is shut down or restarted, the startup tasks will be automatically canceled.
|
|
|
-'''
|
|
|
- with example(lifecycle):
|
|
|
+''')
|
|
|
+ def lifecycle_example():
|
|
|
+ global countdown
|
|
|
import asyncio
|
|
|
|
|
|
l = ui.label()
|
|
@@ -503,7 +536,8 @@ When NiceGUI is shut down or restarted, the startup tasks will be automatically
|
|
|
|
|
|
# ui.on_connect(countdown)
|
|
|
|
|
|
- with example(ui.timer):
|
|
|
+ @example(ui.timer)
|
|
|
+ def timer_example():
|
|
|
from datetime import datetime
|
|
|
|
|
|
with ui.row().classes('items-center'):
|
|
@@ -520,7 +554,8 @@ When NiceGUI is shut down or restarted, the startup tasks will be automatically
|
|
|
lazy_clock = ui.label()
|
|
|
ui.timer(interval=0.1, callback=lazy_update)
|
|
|
|
|
|
- with example(ui.keyboard):
|
|
|
+ @example(ui.keyboard)
|
|
|
+ def keyboard_example():
|
|
|
from nicegui.events import KeyEventArguments
|
|
|
|
|
|
def handle_key(e: KeyEventArguments):
|
|
@@ -543,7 +578,7 @@ When NiceGUI is shut down or restarted, the startup tasks will be automatically
|
|
|
ui.label('Key events can be caught globally by using the keyboard element.')
|
|
|
ui.checkbox('Track key events').bind_value_to(keyboard, 'active')
|
|
|
|
|
|
- bindings = '''#### Bindings
|
|
|
+ @example('''#### Bindings
|
|
|
|
|
|
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.
|
|
@@ -551,8 +586,8 @@ Binding is possible for UI element properties like text, value or visibility and
|
|
|
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):
|
|
|
+''')
|
|
|
+ def bindings_example():
|
|
|
class Demo:
|
|
|
def __init__(self):
|
|
|
self.number = 1
|
|
@@ -564,13 +599,13 @@ Just pass a property of the model as parameter to these methods to create the bi
|
|
|
ui.toggle({1: 'a', 2: 'b', 3: 'c'}).bind_value(demo, 'number')
|
|
|
ui.number().bind_value(demo, 'number')
|
|
|
|
|
|
- ui_updates = '''#### UI Updates
|
|
|
+ @example('''#### 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(ui_updates):
|
|
|
+''')
|
|
|
+ def ui_updates_example():
|
|
|
from random import randint
|
|
|
|
|
|
def add():
|
|
@@ -585,13 +620,13 @@ The example code shows both methods for a `ui.table`, where it is difficult to a
|
|
|
ui.button('Add', on_click=add)
|
|
|
ui.button('Clear', on_click=clear)
|
|
|
|
|
|
- async_handlers = '''#### Async event handlers
|
|
|
+ @example('''#### 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):
|
|
|
+''')
|
|
|
+ def async_handlers_example():
|
|
|
import asyncio
|
|
|
|
|
|
async def async_task():
|
|
@@ -603,7 +638,8 @@ Note: You can also pass a `functools.partial` into the `on_click` property to wr
|
|
|
|
|
|
h3('Pages')
|
|
|
|
|
|
- with example(ui.page):
|
|
|
+ @example(ui.page)
|
|
|
+ def page_example():
|
|
|
@ui.page('/other_page')
|
|
|
def other_page():
|
|
|
ui.label('Welcome to the other side')
|
|
@@ -617,7 +653,7 @@ Note: You can also pass a `functools.partial` into the `on_click` property to wr
|
|
|
ui.link('Visit other page', other_page)
|
|
|
ui.link('Visit dark page', dark_page)
|
|
|
|
|
|
- shared_and_private_pages = '''#### Shared and Private Pages
|
|
|
+ @example('''#### Shared and Private Pages
|
|
|
|
|
|
By default, pages created with the `@ui.page` decorator are "private".
|
|
|
Their content is re-created for each client.
|
|
@@ -631,8 +667,8 @@ Here, the displayed ID remains constant when the browser reloads the page.
|
|
|
|
|
|
All elements that are not created within a decorated page function are automatically added to a new, *shared* index page at route "/".
|
|
|
To make it "private" or to change other attributes like title, favicon etc. you can wrap it in a page function with `@ui.page('/', ...)` decorator.
|
|
|
-'''
|
|
|
- with example(shared_and_private_pages):
|
|
|
+''')
|
|
|
+ def shared_and_private_pages_example():
|
|
|
from uuid import uuid4
|
|
|
|
|
|
@ui.page('/private_page')
|
|
@@ -646,20 +682,20 @@ To make it "private" or to change other attributes like title, favicon etc. you
|
|
|
ui.link('private page', private_page)
|
|
|
ui.link('shared page', shared_page)
|
|
|
|
|
|
- page_with_path_parameters = '''#### Pages with Path Parameters
|
|
|
+ @example('''#### Pages with Path Parameters
|
|
|
|
|
|
Page routes can contain parameters like [FastAPI](https://fastapi.tiangolo.com/tutorial/path-params/>).
|
|
|
If type-annotated, they are automatically converted to bool, int, float and complex values.
|
|
|
If the page function expects a `request` argument, the request object is automatically provided.
|
|
|
-'''
|
|
|
- with example(page_with_path_parameters):
|
|
|
+''')
|
|
|
+ def page_with_path_parameters_example():
|
|
|
@ui.page('/repeat/{word}/{count}')
|
|
|
def page(word: str, count: int):
|
|
|
ui.label(word * count)
|
|
|
|
|
|
ui.link('Say hi to Santa!', 'repeat/Ho! /3')
|
|
|
|
|
|
- yield_page_ready = '''#### Yielding for Page-Ready
|
|
|
+ @example('''#### Yielding for Page-Ready
|
|
|
|
|
|
This is a handy alternative to the `on_page_ready` callback of the `@ui.page` decorator.
|
|
|
|
|
@@ -669,8 +705,8 @@ Also it is possible to do async stuff while the user already sees the content wh
|
|
|
|
|
|
The yield statement returns `nicegui.events.PageEvent`.
|
|
|
This contains the websocket of the client.
|
|
|
- '''
|
|
|
- with example(yield_page_ready):
|
|
|
+ ''')
|
|
|
+ def yield_page_ready_example():
|
|
|
import asyncio
|
|
|
from typing import Generator
|
|
|
|
|
@@ -687,7 +723,7 @@ This contains the websocket of the client.
|
|
|
|
|
|
ui.link('show page-ready code after yield', '/yield_page_ready')
|
|
|
|
|
|
- page_layout = '''#### Page Layout
|
|
|
+ @example('''#### Page Layout
|
|
|
|
|
|
With `ui.header`, `ui.footer`, `ui.left_drawer` and `ui.right_drawer` you can add additional layout elements to a page.
|
|
|
The `fixed` argument controls whether the element should scroll or stay fixed on the screen.
|
|
@@ -696,8 +732,8 @@ See <https://quasar.dev/layout/header-and-footer> and <https://quasar.dev/layout
|
|
|
`elevated`, `bordered` and many more.
|
|
|
With `ui.page_sticky` you can place an element "sticky" on the screen.
|
|
|
See <https://quasar.dev/layout/page-sticky> for more information.
|
|
|
- '''
|
|
|
- with example(page_layout):
|
|
|
+ ''')
|
|
|
+ def page_layout_example():
|
|
|
@ui.page('/page_layout')
|
|
|
async def page_layout():
|
|
|
ui.label('CONTENT')
|
|
@@ -713,7 +749,8 @@ See <https://quasar.dev/layout/page-sticky> for more information.
|
|
|
|
|
|
ui.link('show page with fancy layout', page_layout)
|
|
|
|
|
|
- with example(ui.open):
|
|
|
+ @example(ui.open)
|
|
|
+ def ui_open_example():
|
|
|
@ui.page('/yet_another_page')
|
|
|
def yet_another_page():
|
|
|
ui.label('Welcome to yet another page')
|
|
@@ -721,15 +758,15 @@ See <https://quasar.dev/layout/page-sticky> for more information.
|
|
|
|
|
|
ui.button('REDIRECT', on_click=lambda e: ui.open(yet_another_page, e.socket))
|
|
|
|
|
|
- sessions = '''#### Sessions
|
|
|
+ @example('''#### 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):
|
|
|
+''')
|
|
|
+ def sessions_example():
|
|
|
from collections import Counter
|
|
|
from datetime import datetime
|
|
|
|
|
@@ -749,14 +786,14 @@ It also enables you to identify sessions over [longer time spans by configuring
|
|
|
|
|
|
ui.link('Visit session demo', session_demo)
|
|
|
|
|
|
- javascript = '''#### JavaScript
|
|
|
+ @example('''#### JavaScript
|
|
|
|
|
|
With `ui.run_javascript()` you can run arbitrary JavaScript code on a page that is executed in the browser.
|
|
|
The asynchronous function will return after the command(s) are executed.
|
|
|
The result of the execution is returned as a dictionary containing the response string per websocket.
|
|
|
You can also set `respond=False` to send a command without waiting for a response.
|
|
|
-'''
|
|
|
- with example(javascript):
|
|
|
+''')
|
|
|
+ def javascript_example():
|
|
|
async def alert():
|
|
|
await ui.run_javascript('alert("Hello!")', respond=False)
|
|
|
|
|
@@ -770,7 +807,8 @@ You can also set `respond=False` to send a command without waiting for a respons
|
|
|
|
|
|
h3('Routes')
|
|
|
|
|
|
- with example(ui.get):
|
|
|
+ @example(ui.get)
|
|
|
+ def get_example():
|
|
|
from starlette import requests, responses
|
|
|
|
|
|
@ui.get('/another/route/{id}')
|
|
@@ -779,13 +817,15 @@ You can also set `respond=False` to send a command without waiting for a respons
|
|
|
|
|
|
ui.link('Try yet another route!', 'another/route/42')
|
|
|
|
|
|
- with example(ui.add_static_files):
|
|
|
+ @example(ui.add_static_files)
|
|
|
+ def add_static_files_example():
|
|
|
ui.add_static_files('/examples', 'examples')
|
|
|
ui.link('Slideshow Example (raw file)', 'examples/slideshow/main.py')
|
|
|
with ui.image('examples/slideshow/slides/slide1.jpg'):
|
|
|
ui.label('first image from slideshow').classes('absolute-bottom text-subtitle2')
|
|
|
|
|
|
- with example(ui.add_route):
|
|
|
+ @example(ui.add_route)
|
|
|
+ def add_route_example():
|
|
|
import starlette
|
|
|
|
|
|
ui.add_route(starlette.routing.Route(
|
|
@@ -796,7 +836,7 @@ You can also set `respond=False` to send a command without waiting for a respons
|
|
|
|
|
|
h3('Configuration')
|
|
|
|
|
|
- ui_run = '''#### ui.run
|
|
|
+ @example('''#### ui.run
|
|
|
|
|
|
You can call `ui.run()` with optional arguments:
|
|
|
|
|
@@ -820,8 +860,8 @@ The environment variables `HOST` and `PORT` can also be used to configure NiceGU
|
|
|
|
|
|
To avoid the potentially costly import of Matplotlib, you set the environment variable `MATPLOTLIB=false`.
|
|
|
This will make `ui.plot` and `ui.line_plot` unavailable.
|
|
|
-'''
|
|
|
- with example(ui_run):
|
|
|
+''')
|
|
|
+ def ui_run_example():
|
|
|
ui.label('dark page on port 7000 without reloading')
|
|
|
|
|
|
# ui.run(dark=True, port=7000, reload=False)
|