|
| 1 | +from functools import partial |
| 2 | + |
| 3 | +from pywebio import start_server, config |
| 4 | +from pywebio.input import * |
| 5 | +from pywebio.output import * |
| 6 | +from pywebio.pin import * |
| 7 | +from pywebio.session import * |
| 8 | + |
| 9 | + |
| 10 | +def pin_widgets(): |
| 11 | + put_markdown("# Pin widget") |
| 12 | + options = [ |
| 13 | + { |
| 14 | + "label": "Option one", |
| 15 | + "value": 1, |
| 16 | + "selected": True, |
| 17 | + }, |
| 18 | + { |
| 19 | + "label": "Option two", |
| 20 | + "value": 2, |
| 21 | + }, |
| 22 | + { |
| 23 | + "label": "Disabled option", |
| 24 | + "value": 3, |
| 25 | + "disabled": True |
| 26 | + } |
| 27 | + ] |
| 28 | + put_input('input', label='Text input', placeholder="Enter email", |
| 29 | + help_text="We'll never share your email with anyone else.") |
| 30 | + put_input('valid_input', label="Valid input", value="correct value") |
| 31 | + put_input('invalid_input', label="Invalid input", value="wrong value") |
| 32 | + put_textarea('textarea', label='Textarea', rows=3, maxlength=10, minlength=20, value=None, |
| 33 | + placeholder='This is placeholder message', readonly=False) |
| 34 | + put_textarea('code', label='Code area', rows=4, code={'mode': 'python'}, |
| 35 | + value='import pywebio\npywebio.output.put_text("hello world")') |
| 36 | + put_select('select', options=options, label='Select') |
| 37 | + put_select('select_multiple', options=options, label='Multiple select', multiple=True, value=None) |
| 38 | + put_checkbox('checkbox', options=options, label='Checkbox', inline=False, value=None) |
| 39 | + put_checkbox('checkbox_inline', options=options, label='Inline checkbox', inline=True, value=None) |
| 40 | + put_radio('radio', options=options, label='Radio', inline=False, value=None) |
| 41 | + put_radio('radio_inline', options=options, label='Inline radio', inline=True, value='B') |
| 42 | + put_slider('slider', label='Slider') |
| 43 | + put_actions('actions', buttons=[ |
| 44 | + {'label': 'Submit', 'value': '1'}, |
| 45 | + {'label': 'Warning', 'value': '2', 'color': 'warning'}, |
| 46 | + {'label': 'Danger', 'value': '3', 'color': 'danger'}, |
| 47 | + ], label='Actions') |
| 48 | + |
| 49 | + pin_update('valid_input', valid_status=True, valid_feedback="Success! You've done it.") |
| 50 | + pin_update('invalid_input', valid_status=False, invalid_feedback="Sorry, that username's taken. Try another?") |
| 51 | + |
| 52 | + |
| 53 | +def form(): |
| 54 | + options = [ |
| 55 | + { |
| 56 | + "label": "Option one", |
| 57 | + "value": 1, |
| 58 | + "selected": True, |
| 59 | + }, |
| 60 | + { |
| 61 | + "label": "Option two", |
| 62 | + "value": 2, |
| 63 | + }, |
| 64 | + { |
| 65 | + "label": "Disabled option", |
| 66 | + "value": 3, |
| 67 | + "disabled": True |
| 68 | + } |
| 69 | + ] |
| 70 | + |
| 71 | + input_group('Input group', [ |
| 72 | + input('Text', type=TEXT, datalist=['candidate-%s' % i for i in range(10)], name='text', required=True, |
| 73 | + help_text='Required'), |
| 74 | + input('Number', type=NUMBER, value="42", name='number'), |
| 75 | + input('Float', type=FLOAT, name='float'), |
| 76 | + input('Password', type=PASSWORD, name='password'), |
| 77 | + |
| 78 | + textarea('Textarea', rows=3, maxlength=20, name='textarea', |
| 79 | + placeholder="The maximum number of characters you can input is 20"), |
| 80 | + |
| 81 | + textarea('Code', name='code', code={'mode': 'python'}, |
| 82 | + value='import pywebio\npywebio.output.put_text("hello world")'), |
| 83 | + |
| 84 | + select('Multiple select', options, name='select-multiple', multiple=True), |
| 85 | + |
| 86 | + select('Select', options, name='select'), |
| 87 | + |
| 88 | + checkbox('Inline checkbox', options, inline=True, name='checkbox-inline'), |
| 89 | + |
| 90 | + checkbox('Checkbox', options, name='checkbox'), |
| 91 | + |
| 92 | + radio('Inline radio', options, inline=True, name='radio-inline'), |
| 93 | + |
| 94 | + radio('Radio', options, inline=False, name='radio'), |
| 95 | + |
| 96 | + file_upload('File upload', name='file_upload', max_size='10m'), |
| 97 | + |
| 98 | + actions('Actions', [ |
| 99 | + {'label': 'Submit', 'value': 'submit'}, |
| 100 | + {'label': 'Disabled', 'disabled': True}, |
| 101 | + {'label': 'Reset', 'type': 'reset', 'color': 'warning'}, |
| 102 | + {'label': 'Cancel', 'type': 'cancel', 'color': 'danger'}, |
| 103 | + ], name='actions'), |
| 104 | + ]) |
| 105 | + |
| 106 | + |
| 107 | +def output_widgets(): |
| 108 | + ########################################################################################### |
| 109 | + put_markdown("# Typography") |
| 110 | + put_row([ |
| 111 | + put_markdown(""" |
| 112 | + ## Heading 2 |
| 113 | + ### Heading 3 |
| 114 | + #### Heading 4 |
| 115 | + ##### Heading 5 |
| 116 | + |
| 117 | + [PyWebIO](https://github.com/pywebio/PyWebIO) is awesome! |
| 118 | + |
| 119 | + *This text will be italic* |
| 120 | + **This text will be bold** |
| 121 | + _You **can** combine them_ |
| 122 | + ~~Strikethrough~~ |
| 123 | + This is `inline code` |
| 124 | + |
| 125 | + As Kanye West said: |
| 126 | + |
| 127 | + > We're living the future so |
| 128 | + > the present is our past. |
| 129 | + """, strip_indent=8), |
| 130 | + |
| 131 | + put_markdown(""" |
| 132 | + ### Lists |
| 133 | + * Item 1 |
| 134 | + * Item 2 |
| 135 | + * Item 2a |
| 136 | + * Item 2b |
| 137 | + |
| 138 | + 1. Item 1 |
| 139 | + 1. Item 2 |
| 140 | + 1. Item 2a |
| 141 | + 1. Item 2b |
| 142 | + |
| 143 | + ### Task Lists |
| 144 | + - [x] [links](), **formatting**, and <del>tags</del> supported |
| 145 | + - [x] list syntax required (any unordered or ordered list supported) |
| 146 | + - [x] this is a complete item |
| 147 | + - [ ] this is an incomplete item |
| 148 | + """, strip_indent=8) |
| 149 | + ]) |
| 150 | + |
| 151 | + ########################################################################################### |
| 152 | + put_markdown(""" |
| 153 | + # Code |
| 154 | + ```python |
| 155 | + from pywebio import * |
| 156 | +
|
| 157 | + def main(): # PyWebIO application function |
| 158 | + name = input.input("what's your name") |
| 159 | + output.put_text("hello", name) |
| 160 | + |
| 161 | + start_server(main, port=8080, debug=True) |
| 162 | + ``` |
| 163 | + """, strip_indent=4) |
| 164 | + ########################################################################################### |
| 165 | + put_markdown('# Image') |
| 166 | + with use_scope('image'): |
| 167 | + put_image( |
| 168 | + "https://opengraph.githubassets.com/6bcea5272d0b5901f48a67d9d05da6c7a7c7c68da32a5327943070ff9c9a3dfb/pywebio/PyWebIO").style(""" |
| 169 | + max-height: 250px; |
| 170 | + border: 2px solid #fff; |
| 171 | + border-radius: 25px; |
| 172 | + """) |
| 173 | + ########################################################################################### |
| 174 | + put_markdown("# Buttons") |
| 175 | + # small=None, link_style=False, outline=False, group=False |
| 176 | + put_buttons([ |
| 177 | + dict(label=i, value=i, color=i) |
| 178 | + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] |
| 179 | + ], onclick=lambda b: toast(f'Clicked {b} button')) |
| 180 | + |
| 181 | + put_buttons([ |
| 182 | + dict(label=i, value=i, color=i) |
| 183 | + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] |
| 184 | + ], onclick=lambda b: toast(f'Clicked {b} button'), small=True) |
| 185 | + |
| 186 | + put_buttons([ |
| 187 | + dict(label=i, value=i, color=i) |
| 188 | + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] |
| 189 | + ], onclick=lambda b: toast(f'Clicked {b} button'), link_style=True) |
| 190 | + |
| 191 | + put_buttons([ |
| 192 | + dict(label=i, value=i, color=i) |
| 193 | + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] |
| 194 | + ], onclick=lambda b: toast(f'Clicked {b} button'), outline=True) |
| 195 | + |
| 196 | + put_buttons([ |
| 197 | + dict(label=i, value=i, color=i) |
| 198 | + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] |
| 199 | + ], onclick=lambda b: toast(f'Clicked {b} button'), group=True) |
| 200 | + ########################################################################################### |
| 201 | + put_markdown('# Tables') |
| 202 | + put_markdown(""" |
| 203 | + First Header | Second Header |
| 204 | + ------------ | ------------- |
| 205 | + Content from cell 1 | Content from cell 2 |
| 206 | + Content in the first column | Content in the second column |
| 207 | + """, strip_indent=4) |
| 208 | + |
| 209 | + put_table([ |
| 210 | + ['Type', 'Content'], |
| 211 | + ['text', '<hr/>'], |
| 212 | + ['html', put_html('X<sup>2</sup>')], |
| 213 | + ['buttons', put_buttons(['A', 'B'], onclick=toast, small=True)], |
| 214 | + ['markdown', put_markdown('`awesome PyWebIO!`\n - 1\n - 2\n - 3')], |
| 215 | + ['file', put_file('hello.text', b'')], |
| 216 | + ['table', put_table([ |
| 217 | + ['A', 'B'], |
| 218 | + [put_markdown('`C`'), put_markdown('`D`')] |
| 219 | + ])] |
| 220 | + ]) |
| 221 | + ########################################################################################### |
| 222 | + put_markdown('# Popup') |
| 223 | + |
| 224 | + def show_popup(): |
| 225 | + popup('Popup title', [ |
| 226 | + 'Popup body text goes here.', |
| 227 | + put_table([ |
| 228 | + ['Type', 'Content'], |
| 229 | + ['html', put_html('X<sup>2</sup>')], |
| 230 | + ['text', '<hr/>'], |
| 231 | + ['buttons', put_buttons(['A', 'B'], onclick=toast)], |
| 232 | + ['markdown', put_markdown('`Awesome PyWebIO!`')], |
| 233 | + ['file', put_file('hello.text', b'')], |
| 234 | + ['table', put_table([['A', 'B'], ['C', 'D']])] |
| 235 | + ]), |
| 236 | + put_button('Close', onclick=close_popup, outline=True) |
| 237 | + ], size=PopupSize.NORMAL) |
| 238 | + |
| 239 | + put_button("Click me to show a popup", onclick=show_popup) |
| 240 | + |
| 241 | + ########################################################################################### |
| 242 | + put_markdown('# Layout') |
| 243 | + put_row([ |
| 244 | + put_column([ |
| 245 | + put_code('A'), |
| 246 | + put_row([ |
| 247 | + put_code('B1'), None, |
| 248 | + put_code('B2'), None, |
| 249 | + put_code('B3'), |
| 250 | + ]), |
| 251 | + put_code('C'), |
| 252 | + ]), None, |
| 253 | + put_code('python'), None, |
| 254 | + put_code('python\n' * 20).style('max-height:200px;'), |
| 255 | + ]) |
| 256 | + |
| 257 | + ########################################################################################### |
| 258 | + put_markdown('# Loading') |
| 259 | + put_processbar('processbar', 0.3) |
| 260 | + put_text() |
| 261 | + put_grid([ |
| 262 | + [ |
| 263 | + put_loading(shape=shape, color=color) |
| 264 | + for color in ('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') |
| 265 | + ] |
| 266 | + for shape in ('border', 'grow') |
| 267 | + ], cell_width='50px', cell_height='50px') |
| 268 | + ########################################################################################### |
| 269 | + put_markdown('# Tabs') |
| 270 | + |
| 271 | + put_tabs([ |
| 272 | + {'title': 'Text', 'content': 'Hello world'}, |
| 273 | + {'title': 'Markdown', 'content': put_markdown('~~Strikethrough~~')}, |
| 274 | + {'title': 'More content', 'content': [ |
| 275 | + put_table([ |
| 276 | + ['Commodity', 'Price'], |
| 277 | + ['Apple', '5.5'], |
| 278 | + ['Banana', '7'], |
| 279 | + ]), |
| 280 | + put_link('pywebio', 'https://github.com/wang0618/PyWebIO') |
| 281 | + ]}, |
| 282 | + ]) |
| 283 | + ########################################################################################### |
| 284 | + put_markdown('# Scrollable') |
| 285 | + |
| 286 | + put_scrollable("Long text " * 200, height=200) |
| 287 | + ########################################################################################### |
| 288 | + put_markdown('# Collapse') |
| 289 | + put_collapse('Click to expand', [ |
| 290 | + 'text', |
| 291 | + put_markdown('~~Strikethrough~~'), |
| 292 | + put_table([ |
| 293 | + ['Commodity', 'Price'], |
| 294 | + ['Apple', '5.5'], |
| 295 | + ]) |
| 296 | + ]) |
| 297 | + ########################################################################################### |
| 298 | + put_markdown('# Message') |
| 299 | + |
| 300 | + put_warning( |
| 301 | + put_markdown('### Warning!'), |
| 302 | + "Best check yo self, you're not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.", |
| 303 | + closable=True) |
| 304 | + put_success("Well done! You successfully read this important alert message.") |
| 305 | + put_info("Heads up! This alert needs your attention, but it's not super important.") |
| 306 | + put_error("Oh snap! Change a few things up and try submitting again.") |
| 307 | + |
| 308 | + |
| 309 | +ALL_THEME = ('default', 'dark', 'sketchy', 'minty', 'yeti') |
| 310 | + |
| 311 | +THEME_SUMMARY = {'default': 'The default theme', 'dark': 'A theme for night', |
| 312 | + 'sketchy': 'A hand-drawn look for mockups and mirth', 'minty': 'A fresh feel', |
| 313 | + 'yeti': 'A friendly foundation'} |
| 314 | + |
| 315 | +style = """ |
| 316 | +table img:hover { |
| 317 | + transition-duration: 400ms; |
| 318 | + transform: translateY(-2px); |
| 319 | + box-shadow: 0px 2px 9px 2px rgb(0 0 0 / 27%), 0 30px 50px -30px rgb(0 0 0 / 30%) |
| 320 | +} |
| 321 | +.page-header h1 { |
| 322 | + font-size: 3em; |
| 323 | +} |
| 324 | +#pywebio-scope-image img { |
| 325 | + box-shadow: rgb(204 204 204) 3px 3px 13px; |
| 326 | +} |
| 327 | +.webio-theme-dark #pywebio-scope-image img { |
| 328 | + box-shadow: none !important; |
| 329 | +} |
| 330 | +""" |
| 331 | + |
| 332 | + |
| 333 | +@config(css_style=style) |
| 334 | +def page(): |
| 335 | + theme = eval_js("new URLSearchParams(window.location.search).get('app')") |
| 336 | + if theme not in ALL_THEME: |
| 337 | + theme = 'default' |
| 338 | + |
| 339 | + put_html(f""" |
| 340 | + <div class="page-header"> |
| 341 | + <div style="text-align: center"> |
| 342 | + <h1>{theme[0].upper() + theme[1:]}</h1> |
| 343 | + <p class="lead">{THEME_SUMMARY.get(theme, '')}</p> |
| 344 | + </div> |
| 345 | + </div> |
| 346 | + """) |
| 347 | + |
| 348 | + put_markdown('# Switch Themes') |
| 349 | + put_table( |
| 350 | + [ |
| 351 | + [ |
| 352 | + put_image(f"https://cdn.jsdelivr.net/gh/wang0618/PyWebIO/docs/assets/theme/{name}.png").onclick( |
| 353 | + partial(go_app, name=name, new_window=False)) |
| 354 | + for name in ALL_THEME if name != theme], |
| 355 | + ] |
| 356 | + ) |
| 357 | + put_markdown(""" |
| 358 | + ### Credits |
| 359 | + |
| 360 | + The dark theme is modified from ForEvolve's [bootstrap-dark](https://github.com/ForEvolve/bootstrap-dark). |
| 361 | + The sketchy, minty and yeti theme are from [bootswatch](https://bootswatch.com/4/). |
| 362 | + """, lstrip=True) |
| 363 | + |
| 364 | + output_widgets() |
| 365 | + pin_widgets() |
| 366 | + form() |
| 367 | + |
| 368 | + |
| 369 | +# bind each theme to the app |
| 370 | +main = { |
| 371 | + theme: config(theme=theme, title=f"PyWebIO {theme} theme")(page) |
| 372 | + for theme in ALL_THEME if theme != 'default' |
| 373 | +} |
| 374 | +main['index'] = page |
| 375 | + |
| 376 | +if __name__ == '__main__': |
| 377 | + start_server(main, debug=True, port=8080, cdn=False) |
0 commit comments