From 100ee613be35dbb38a366021bab23e2c36f4f0cd Mon Sep 17 00:00:00 2001 From: Nuno Bispo Date: Tue, 29 Sep 2026 09:45:05 +0200 Subject: [PATCH 1/6] Materials for NiceGUI article --- .../README.md | 40 +++++++++++++++++++ .../elements_and_actions.py | 11 +++++ .../hello_nicegui_browser.py | 5 +++ .../hello_nicegui_native.py | 5 +++ .../layout_and_styling.py | 12 ++++++ .../requirements.txt | 4 ++ 6 files changed, 77 insertions(+) create mode 100644 nicegui-a-browser-frontend-for-your-python-code/README.md create mode 100644 nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/requirements.txt diff --git a/nicegui-a-browser-frontend-for-your-python-code/README.md b/nicegui-a-browser-frontend-for-your-python-code/README.md new file mode 100644 index 0000000000..690edf83f8 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/README.md @@ -0,0 +1,40 @@ +# NiceGUI: A Browser Frontend for Your Python Code + +Sample scripts that build small user interfaces with [NiceGUI](https://nicegui.io/). Each file is a complete app. + +## Setup + +These examples need Python 3.10 or newer. Create a virtual environment and install the dependencies: + +```console +$ python -m venv venv +$ source venv/bin/activate +$ python -m pip install -r requirements.txt +``` + +On Windows, activate the environment with `venv\Scripts\activate`. + +`requirements.txt` installs NiceGUI 3.17.0 with the Plotly extra, along with pinned versions of Plotly, pandas, and [pywebview](https://pywebview.flowrl.com/). `hello_nicegui_native.py` uses pywebview to open a desktop window. On Linux, pywebview also needs a system WebKit or Qt backend. The [pywebview installation guide](https://pywebview.flowrl.com/guide/installation.html) lists the packages for each platform. + +## Examples + +| Script | What it shows | +| --- | --- | +| `hello_nicegui_browser.py` | A label and a button that shows a notification in the browser | +| `hello_nicegui_native.py` | The same interface in a native desktop window | +| `elements_and_actions.py` | Text and number inputs that update a label when you click Run | +| `layout_and_styling.py` | A row, a card, an icon, a badge, and Quasar utility classes | + +Run an example from this folder: + +```console +$ python hello_nicegui_browser.py +``` + +NiceGUI prints a local URL, usually `http://127.0.0.1:8080`. Open that address in a browser. Stop the app with Ctrl+C. + +The native example opens its own window: + +```console +$ python hello_nicegui_native.py +``` diff --git a/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py b/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py new file mode 100644 index 0000000000..9906ed7e0f --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py @@ -0,0 +1,11 @@ +from nicegui import ui + +name = ui.input("Name", value="Ada") +count = ui.number("Copies", value=2, min=1, max=10) +out = ui.label() + +def run() -> None: + out.set_text(f"Hello, {name.value}! ({int(count.value)} copies)") + +ui.button("Run", on_click=run) +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py new file mode 100644 index 0000000000..dd28c6dd10 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py @@ -0,0 +1,5 @@ +from nicegui import ui + +ui.label("Hello, NiceGUI!") +ui.button("Click me", on_click=lambda: ui.notify("It works!")) +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py new file mode 100644 index 0000000000..daa2c56fc8 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py @@ -0,0 +1,5 @@ +from nicegui import ui + +ui.label("Hello, NiceGUI!") +ui.button("Click me", on_click=lambda: ui.notify("It works!")) +ui.run(native=True) diff --git a/nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py b/nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py new file mode 100644 index 0000000000..85c181d357 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py @@ -0,0 +1,12 @@ +from nicegui import ui + +with ui.row().classes("w-full items-center gap-4 p-4"): + ui.icon("dashboard", size="md").props("color=primary") + ui.label("Ops Panel").classes("text-h6 text-weight-bold") + +with ui.card().classes("w-80"): + ui.label("Status").classes("text-subtitle2") + ui.badge("Ready").props("color=positive") + ui.button("Ping", on_click=lambda: ui.notify("Ready")) + +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/requirements.txt b/nicegui-a-browser-frontend-for-your-python-code/requirements.txt new file mode 100644 index 0000000000..3fc4963672 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/requirements.txt @@ -0,0 +1,4 @@ +nicegui[plotly]==3.17.0 +plotly==6.9.0 +pandas==3.0.5 +pywebview==6.2.1 From c4abef7ef8ee7bdd92627a40a1fc1543df557e40 Mon Sep 17 00:00:00 2001 From: Nuno Bispo Date: Tue, 29 Sep 2026 09:49:14 +0200 Subject: [PATCH 2/6] Add spacing for improved readability in elements_and_actions.py --- .../elements_and_actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py b/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py index 9906ed7e0f..f6e5fdac38 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py +++ b/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py @@ -4,8 +4,10 @@ count = ui.number("Copies", value=2, min=1, max=10) out = ui.label() + def run() -> None: out.set_text(f"Hello, {name.value}! ({int(count.value)} copies)") + ui.button("Run", on_click=run) ui.run() From 577955e2aa8e6577ce9612455f36a3c2a5e71691 Mon Sep 17 00:00:00 2001 From: Nuno Bispo Date: Thu, 1 Oct 2026 12:41:51 +0200 Subject: [PATCH 3/6] Updates from TR#1 --- .../README.md | 28 +++++--- .../csv_to_markdown.py | 68 +++++++++++++++++++ .../csv_to_markdown_ui.py | 50 ++++++++++++++ .../element_catalog.py | 28 ++++++++ .../elements_and_actions.py | 13 ---- .../estimate_card.py | 28 ++++++++ .../hello_nicegui_browser.py | 5 -- .../hello_nicegui_native.py | 5 -- .../layout_and_styling.py | 12 ---- .../password_generator.py | 40 +++++++++++ .../password_generator_native.py | 40 +++++++++++ .../people.csv | 3 + .../plotly_chart.py | 24 +++++++ .../request_chart.py | 28 ++++++++ 14 files changed, 328 insertions(+), 44 deletions(-) create mode 100644 nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/element_catalog.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/estimate_card.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/password_generator.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/people.csv create mode 100644 nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/request_chart.py diff --git a/nicegui-a-browser-frontend-for-your-python-code/README.md b/nicegui-a-browser-frontend-for-your-python-code/README.md index 690edf83f8..fef53992d7 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/README.md +++ b/nicegui-a-browser-frontend-for-your-python-code/README.md @@ -1,6 +1,6 @@ # NiceGUI: A Browser Frontend for Your Python Code -Sample scripts that build small user interfaces with [NiceGUI](https://nicegui.io/). Each file is a complete app. +Sample scripts that build small user interfaces with [NiceGUI](https://nicegui.io/). Each NiceGUI file is a complete app. `csv_to_markdown.py` is a plain CLI helper used by the UI example. ## Setup @@ -14,21 +14,25 @@ $ python -m pip install -r requirements.txt On Windows, activate the environment with `venv\Scripts\activate`. -`requirements.txt` installs NiceGUI 3.17.0 with the Plotly extra, along with pinned versions of Plotly, pandas, and [pywebview](https://pywebview.flowrl.com/). `hello_nicegui_native.py` uses pywebview to open a desktop window. On Linux, pywebview also needs a system WebKit or Qt backend. The [pywebview installation guide](https://pywebview.flowrl.com/guide/installation.html) lists the packages for each platform. +`requirements.txt` installs NiceGUI 3.17.0 with the Plotly extra, along with pinned versions of Plotly, pandas, and [pywebview](https://pywebview.flowrl.com/). `password_generator_native.py` uses pywebview to open a desktop window. On Linux, pywebview also needs a system WebKit or Qt backend. The [pywebview installation guide](https://pywebview.flowrl.com/guide/installation.html) lists the packages for each platform. ## Examples | Script | What it shows | | --- | --- | -| `hello_nicegui_browser.py` | A label and a button that shows a notification in the browser | -| `hello_nicegui_native.py` | The same interface in a native desktop window | -| `elements_and_actions.py` | Text and number inputs that update a label when you click Run | -| `layout_and_styling.py` | A row, a card, an icon, a badge, and Quasar utility classes | +| `element_catalog.py` | Common inputs (text, number, select, checkbox, radio, slider, switch) and a summary label | +| `estimate_card.py` | A card with quantity, unit price, optional tax, and a calculated total | +| `password_generator.py` | A password generator in the browser | +| `password_generator_native.py` | The same password generator in a native desktop window | +| `csv_to_markdown.py` | CLI that converts a CSV file to a Markdown table | +| `csv_to_markdown_ui.py` | Browser UI for the converter, with progress and `run.io_bound` | +| `request_chart.py` | An ECharts bar chart and a table of request counts | +| `plotly_chart.py` | A Plotly bar chart from a pandas DataFrame | -Run an example from this folder: +Run a NiceGUI example from this folder: ```console -$ python hello_nicegui_browser.py +$ python password_generator.py ``` NiceGUI prints a local URL, usually `http://127.0.0.1:8080`. Open that address in a browser. Stop the app with Ctrl+C. @@ -36,5 +40,11 @@ NiceGUI prints a local URL, usually `http://127.0.0.1:8080`. Open that address i The native example opens its own window: ```console -$ python hello_nicegui_native.py +$ python password_generator_native.py +``` + +The CLI converter works without starting a server: + +```console +$ python csv_to_markdown.py people.csv ``` diff --git a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py new file mode 100644 index 0000000000..9ba02e6a1d --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py @@ -0,0 +1,68 @@ +import argparse +import csv +from io import StringIO +from pathlib import Path + + +def csv_to_markdown( + text: str, + *, + delimiter: str = ",", + header: bool = True, +) -> str: + raw = text.strip() + if not raw: + raise ValueError("CSV text is empty") + + rows = list(csv.reader(StringIO(raw), delimiter=delimiter)) + if not rows: + raise ValueError("CSV text has no rows") + if any(len(row) != len(rows[0]) for row in rows): + raise ValueError("CSV rows do not all have the same number of columns") + + if header: + headings = [cell.strip() or " " for cell in rows[0]] + body = rows[1:] + else: + headings = [f"Col {index}" for index in range(1, len(rows[0]) + 1)] + body = rows + + lines = [ + "| " + " | ".join(headings) + " |", + "| " + " | ".join("---" for _ in headings) + " |", + ] + for row in body: + lines.append("| " + " | ".join(cell.strip() for cell in row) + " |") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Convert a CSV file to a Markdown table." + ) + parser.add_argument("csv_file", type=Path, help="path to a CSV file") + parser.add_argument( + "-d", + "--delimiter", + default=",", + help="field delimiter (default: comma)", + ) + parser.add_argument( + "--no-header", + action="store_true", + help="treat the first row as data", + ) + args = parser.parse_args() + text = args.csv_file.read_text(encoding="utf-8") + print( + csv_to_markdown( + text, + delimiter=args.delimiter, + header=not args.no_header, + ), + end="", + ) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py new file mode 100644 index 0000000000..d89dbd4db3 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py @@ -0,0 +1,50 @@ +from nicegui import run, ui + +from csv_to_markdown import csv_to_markdown + +SAMPLE = "name,role\nAda,Engineer\nGrace,Scientist\n" + +source = ui.textarea("CSV", value=SAMPLE).classes("w-full").props("rows=8") +delimiter = ui.select( + {",": "Comma", ";": "Semicolon", "\t": "Tab"}, + value=",", + label="Delimiter", +) +header = ui.checkbox("First row is the header", value=True) +status = ui.label("Paste CSV, then click Convert.") +progress = ui.linear_progress(show_value=False, size="8px").props( + "indeterminate" +) +progress.set_visibility(False) +markdown = ui.textarea("Markdown").classes("w-full").props("rows=8") + + +async def convert() -> None: + raw = source.value or "" + convert_button.disable() + progress.set_visibility(True) + status.set_text("Converting") + try: + result = await run.io_bound( + csv_to_markdown, + raw, + delimiter=delimiter.value, + header=header.value, + ) + except ValueError as exc: + markdown.set_value("") + status.set_text(str(exc)) + ui.notify(str(exc), type="negative") + return + finally: + progress.set_visibility(False) + convert_button.enable() + + markdown.set_value(result) + status.set_text("Conversion complete.") + ui.notify("Markdown table ready", type="positive") + + +convert_button = ui.button("Convert", on_click=convert) + +ui.run() \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py b/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py new file mode 100644 index 0000000000..b2b8122777 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py @@ -0,0 +1,28 @@ +from nicegui import ui + +name = ui.input("Project", value="notes") +notes = ui.textarea("Notes", value="Ship the export") +copies = ui.number("Copies", value=2, min=1, max=10) +kind = ui.select(["Draft", "Final"], value="Draft", label="Kind") +urgent = ui.checkbox("Urgent") +ui.label("Extension") +extension = ui.radio(["txt", "csv"], value="txt") +ui.label("Priority") +priority = ui.slider(min=0, max=10, value=3) +archive = ui.switch("Archive after") +summary = ui.label("No choices yet") + + +def show() -> None: + summary.set_text( + f"{name.value}: {kind.value}.{extension.value}, " + f"copies={copies.value}, urgent={urgent.value}, " + f"priority={priority.value}, archive={archive.value}" + ) + if notes.value: + ui.notify(notes.value) + + +ui.button("Show choices", on_click=show) + +ui.run() \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py b/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py deleted file mode 100644 index f6e5fdac38..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/elements_and_actions.py +++ /dev/null @@ -1,13 +0,0 @@ -from nicegui import ui - -name = ui.input("Name", value="Ada") -count = ui.number("Copies", value=2, min=1, max=10) -out = ui.label() - - -def run() -> None: - out.set_text(f"Hello, {name.value}! ({int(count.value)} copies)") - - -ui.button("Run", on_click=run) -ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py new file mode 100644 index 0000000000..ff16f4c93e --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py @@ -0,0 +1,28 @@ +from nicegui import ui + +with ui.row().classes("w-full items-center gap-4 p-4"): + ui.icon("request_quote", size="md").props("color=primary") + ui.label("Estimate").classes("text-h6 text-weight-bold") + +with ui.card().classes("w-80"): + quantity = ui.number("Quantity", value=2, min=1) + unit_price = ui.number("Unit price", value=40.0, min=0, format="%.2f") + taxed = ui.checkbox("Add 20% tax") + total = ui.label("Total: —").classes("text-subtitle1") + + def calculate() -> None: + qty = quantity.value + price = unit_price.value + if qty is None or price is None or qty < 1 or price < 0: + total.set_text("Total: —") + ui.notify("Enter a quantity and a unit price", type="warning") + return + + amount = float(qty) * float(price) + if taxed.value: + amount *= 1.2 + total.set_text(f"Total: {amount:.2f}") + + ui.button("Calculate", on_click=calculate).props("color=primary") + +ui.run() \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py deleted file mode 100644 index dd28c6dd10..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_browser.py +++ /dev/null @@ -1,5 +0,0 @@ -from nicegui import ui - -ui.label("Hello, NiceGUI!") -ui.button("Click me", on_click=lambda: ui.notify("It works!")) -ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py deleted file mode 100644 index daa2c56fc8..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py +++ /dev/null @@ -1,5 +0,0 @@ -from nicegui import ui - -ui.label("Hello, NiceGUI!") -ui.button("Click me", on_click=lambda: ui.notify("It works!")) -ui.run(native=True) diff --git a/nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py b/nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py deleted file mode 100644 index 85c181d357..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/layout_and_styling.py +++ /dev/null @@ -1,12 +0,0 @@ -from nicegui import ui - -with ui.row().classes("w-full items-center gap-4 p-4"): - ui.icon("dashboard", size="md").props("color=primary") - ui.label("Ops Panel").classes("text-h6 text-weight-bold") - -with ui.card().classes("w-80"): - ui.label("Status").classes("text-subtitle2") - ui.badge("Ready").props("color=positive") - ui.button("Ping", on_click=lambda: ui.notify("Ready")) - -ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/password_generator.py b/nicegui-a-browser-frontend-for-your-python-code/password_generator.py new file mode 100644 index 0000000000..ddae5d30e4 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/password_generator.py @@ -0,0 +1,40 @@ +import secrets +import string + +from nicegui import ui + +length = ui.number("Length", value=16, min=8, max=64) +digits = ui.checkbox("Include digits", value=True) +symbols = ui.checkbox("Include symbols") +password = ui.input("Password").props("readonly") +status = ui.label("Click Generate for a new password.") + + +def generate() -> None: + size = length.value + if ( + size is None + or size != int(size) + or size < 8 + or size > 64 + ): + password.set_value("") + status.set_text("Choose a whole length from 8 to 64.") + ui.notify("Length must be a whole number from 8 to 64", type="warning") + return + + alphabet = string.ascii_letters + if digits.value: + alphabet += string.digits + if symbols.value: + alphabet += "!@#$%^&*()-_=+" + + size = int(size) + result = "".join(secrets.choice(alphabet) for _ in range(size)) + password.set_value(result) + status.set_text(f"Generated a {size}-character password.") + + +ui.button("Generate", on_click=generate) + +ui.run() \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py b/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py new file mode 100644 index 0000000000..3c99474622 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py @@ -0,0 +1,40 @@ +import secrets +import string + +from nicegui import ui + +length = ui.number("Length", value=16, min=8, max=64) +digits = ui.checkbox("Include digits", value=True) +symbols = ui.checkbox("Include symbols") +password = ui.input("Password").props("readonly") +status = ui.label("Click Generate for a new password.") + + +def generate() -> None: + size = length.value + if ( + size is None + or size != int(size) + or size < 8 + or size > 64 + ): + password.set_value("") + status.set_text("Choose a whole length from 8 to 64.") + ui.notify("Length must be a whole number from 8 to 64", type="warning") + return + + alphabet = string.ascii_letters + if digits.value: + alphabet += string.digits + if symbols.value: + alphabet += "!@#$%^&*()-_=+" + + size = int(size) + result = "".join(secrets.choice(alphabet) for _ in range(size)) + password.set_value(result) + status.set_text(f"Generated a {size}-character password.") + + +ui.button("Generate", on_click=generate) + +ui.run(native=True) \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/people.csv b/nicegui-a-browser-frontend-for-your-python-code/people.csv new file mode 100644 index 0000000000..78ed284e8a --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/people.csv @@ -0,0 +1,3 @@ +name,role +Ada,Engineer +Grace,Scientist diff --git a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py new file mode 100644 index 0000000000..e11e9d196d --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py @@ -0,0 +1,24 @@ +import pandas as pd +import plotly.express as px +from nicegui import ui + +traffic = pd.DataFrame({ + "Endpoint": [ + "/login", + "/api/items", + "/api/search", + "/checkout", + "/health", + "/admin", + ], + "Requests": [1280, 2540, 1875, 940, 4120, 210], +}) +fig = px.bar( + traffic, + x="Endpoint", + y="Requests", + title="Requests by Endpoint", +) +ui.plotly(fig).classes("w-full") + +ui.run() \ No newline at end of file diff --git a/nicegui-a-browser-frontend-for-your-python-code/request_chart.py b/nicegui-a-browser-frontend-for-your-python-code/request_chart.py new file mode 100644 index 0000000000..723c5b7bbb --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/request_chart.py @@ -0,0 +1,28 @@ +from nicegui import ui + +traffic = [ + {"endpoint": "/login", "requests": 1280}, + {"endpoint": "/api/items", "requests": 2540}, + {"endpoint": "/api/search", "requests": 1875}, + {"endpoint": "/checkout", "requests": 940}, + {"endpoint": "/health", "requests": 4120}, + {"endpoint": "/admin", "requests": 210}, +] + +ui.echart( + { + "title": {"text": "Requests by Endpoint"}, + "xAxis": { + "type": "category", + "data": [row["endpoint"] for row in traffic], + }, + "yAxis": {"type": "value"}, + "series": [ + {"type": "bar", "data": [row["requests"] for row in traffic]} + ], + } +).classes("w-full") + +ui.table(rows=traffic) + +ui.run() \ No newline at end of file From 0be83ec24c27ee5f83514b4adc9947535640efd6 Mon Sep 17 00:00:00 2001 From: Nuno Bispo Date: Thu, 1 Oct 2026 12:45:55 +0200 Subject: [PATCH 4/6] Formatting --- .../csv_to_markdown.py | 2 +- .../csv_to_markdown_ui.py | 2 +- .../element_catalog.py | 2 +- .../estimate_card.py | 2 +- .../password_generator.py | 9 ++----- .../password_generator_native.py | 9 ++----- .../plotly_chart.py | 26 ++++++++++--------- .../request_chart.py | 2 +- 8 files changed, 23 insertions(+), 31 deletions(-) diff --git a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py index 9ba02e6a1d..12c5edaf56 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py +++ b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py @@ -65,4 +65,4 @@ def main() -> None: if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py index d89dbd4db3..0faeef366c 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py +++ b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py @@ -47,4 +47,4 @@ async def convert() -> None: convert_button = ui.button("Convert", on_click=convert) -ui.run() \ No newline at end of file +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py b/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py index b2b8122777..e6d83709f1 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py +++ b/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py @@ -25,4 +25,4 @@ def show() -> None: ui.button("Show choices", on_click=show) -ui.run() \ No newline at end of file +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py index ff16f4c93e..b2ea707e12 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py +++ b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py @@ -25,4 +25,4 @@ def calculate() -> None: ui.button("Calculate", on_click=calculate).props("color=primary") -ui.run() \ No newline at end of file +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/password_generator.py b/nicegui-a-browser-frontend-for-your-python-code/password_generator.py index ddae5d30e4..789a67d685 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/password_generator.py +++ b/nicegui-a-browser-frontend-for-your-python-code/password_generator.py @@ -12,12 +12,7 @@ def generate() -> None: size = length.value - if ( - size is None - or size != int(size) - or size < 8 - or size > 64 - ): + if size is None or size != int(size) or size < 8 or size > 64: password.set_value("") status.set_text("Choose a whole length from 8 to 64.") ui.notify("Length must be a whole number from 8 to 64", type="warning") @@ -37,4 +32,4 @@ def generate() -> None: ui.button("Generate", on_click=generate) -ui.run() \ No newline at end of file +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py b/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py index 3c99474622..bef186958f 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py +++ b/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py @@ -12,12 +12,7 @@ def generate() -> None: size = length.value - if ( - size is None - or size != int(size) - or size < 8 - or size > 64 - ): + if size is None or size != int(size) or size < 8 or size > 64: password.set_value("") status.set_text("Choose a whole length from 8 to 64.") ui.notify("Length must be a whole number from 8 to 64", type="warning") @@ -37,4 +32,4 @@ def generate() -> None: ui.button("Generate", on_click=generate) -ui.run(native=True) \ No newline at end of file +ui.run(native=True) diff --git a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py index e11e9d196d..b904160b83 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py +++ b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py @@ -2,17 +2,19 @@ import plotly.express as px from nicegui import ui -traffic = pd.DataFrame({ - "Endpoint": [ - "/login", - "/api/items", - "/api/search", - "/checkout", - "/health", - "/admin", - ], - "Requests": [1280, 2540, 1875, 940, 4120, 210], -}) +traffic = pd.DataFrame( + { + "Endpoint": [ + "/login", + "/api/items", + "/api/search", + "/checkout", + "/health", + "/admin", + ], + "Requests": [1280, 2540, 1875, 940, 4120, 210], + } +) fig = px.bar( traffic, x="Endpoint", @@ -21,4 +23,4 @@ ) ui.plotly(fig).classes("w-full") -ui.run() \ No newline at end of file +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/request_chart.py b/nicegui-a-browser-frontend-for-your-python-code/request_chart.py index 723c5b7bbb..7f4327ebc4 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/request_chart.py +++ b/nicegui-a-browser-frontend-for-your-python-code/request_chart.py @@ -25,4 +25,4 @@ ui.table(rows=traffic) -ui.run() \ No newline at end of file +ui.run() From 52a823ed3b592006034d60cd20aede8afa8dd27e Mon Sep 17 00:00:00 2001 From: Nuno Bispo Date: Fri, 9 Oct 2026 13:15:31 +0200 Subject: [PATCH 5/6] Updates from TR#2 --- .../README.md | 30 ++++---- .../csv_to_markdown.py | 68 ------------------- .../csv_to_markdown_ui.py | 50 -------------- .../element_catalog.py | 23 +++---- .../estimate_card.py | 25 +++++-- .../generate_password_cli.py | 64 +++++++++++++++++ .../generate_password_ui.py | 50 ++++++++++++++ .../hello_nicegui.py | 6 ++ .../hello_nicegui_native.py | 5 ++ .../password_generator.py | 35 ---------- .../password_generator_native.py | 35 ---------- .../people.csv | 3 - .../plotly_chart.py | 24 +++---- 13 files changed, 182 insertions(+), 236 deletions(-) delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/hello_nicegui.py create mode 100644 nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/password_generator.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py delete mode 100644 nicegui-a-browser-frontend-for-your-python-code/people.csv diff --git a/nicegui-a-browser-frontend-for-your-python-code/README.md b/nicegui-a-browser-frontend-for-your-python-code/README.md index fef53992d7..7a50366962 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/README.md +++ b/nicegui-a-browser-frontend-for-your-python-code/README.md @@ -1,10 +1,10 @@ # NiceGUI: A Browser Frontend for Your Python Code -Sample scripts that build small user interfaces with [NiceGUI](https://nicegui.io/). Each NiceGUI file is a complete app. `csv_to_markdown.py` is a plain CLI helper used by the UI example. +Sample scripts for the Real Python NiceGUI tutorial. Each NiceGUI file is a complete app. `generate_password_cli.py` is a plain CLI helper used by the password UI example. ## Setup -These examples need Python 3.10 or newer. Create a virtual environment and install the dependencies: +These examples need **Python 3.11 or later**. Create a virtual environment and install the dependencies: ```console $ python -m venv venv @@ -14,25 +14,25 @@ $ python -m pip install -r requirements.txt On Windows, activate the environment with `venv\Scripts\activate`. -`requirements.txt` installs NiceGUI 3.17.0 with the Plotly extra, along with pinned versions of Plotly, pandas, and [pywebview](https://pywebview.flowrl.com/). `password_generator_native.py` uses pywebview to open a desktop window. On Linux, pywebview also needs a system WebKit or Qt backend. The [pywebview installation guide](https://pywebview.flowrl.com/guide/installation.html) lists the packages for each platform. +`requirements.txt` installs NiceGUI 3.17.0 with the Plotly extra, along with pinned versions of Plotly, pandas, and [pywebview](https://pywebview.flowrl.com/). `hello_nicegui_native.py` uses pywebview to open a desktop window. On Linux, install a GTK backend as in the tutorial (`pywebview[gtk]` plus the system libraries from the [pywebview installation guide](https://pywebview.flowrl.com/guide/installation.html)). ## Examples | Script | What it shows | | --- | --- | -| `element_catalog.py` | Common inputs (text, number, select, checkbox, radio, slider, switch) and a summary label | -| `estimate_card.py` | A card with quantity, unit price, optional tax, and a calculated total | -| `password_generator.py` | A password generator in the browser | -| `password_generator_native.py` | The same password generator in a native desktop window | -| `csv_to_markdown.py` | CLI that converts a CSV file to a Markdown table | -| `csv_to_markdown_ui.py` | Browser UI for the converter, with progress and `run.io_bound` | -| `request_chart.py` | An ECharts bar chart and a table of request counts | -| `plotly_chart.py` | A Plotly bar chart from a pandas DataFrame | +| `hello_nicegui.py` | Minimal label and button in the browser | +| `hello_nicegui_native.py` | Same app in a native desktop window (`native=True`) | +| `element_catalog.py` | Common inputs and a summary label | +| `generate_password_cli.py` | CLI password generator | +| `generate_password_ui.py` | Browser UI that calls the CLI function | +| `estimate_card.py` | Layout, styling, and a custom color palette | +| `request_chart.py` | Built-in ECharts bar chart and table | +| `plotly_chart.py` | Plotly bar chart from a pandas DataFrame | Run a NiceGUI example from this folder: ```console -$ python password_generator.py +$ python hello_nicegui.py ``` NiceGUI prints a local URL, usually `http://127.0.0.1:8080`. Open that address in a browser. Stop the app with Ctrl+C. @@ -40,11 +40,11 @@ NiceGUI prints a local URL, usually `http://127.0.0.1:8080`. Open that address i The native example opens its own window: ```console -$ python password_generator_native.py +$ python hello_nicegui_native.py ``` -The CLI converter works without starting a server: +The password CLI works without starting a server: ```console -$ python csv_to_markdown.py people.csv +$ python generate_password_cli.py -l 24 --symbols ``` diff --git a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py deleted file mode 100644 index 12c5edaf56..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown.py +++ /dev/null @@ -1,68 +0,0 @@ -import argparse -import csv -from io import StringIO -from pathlib import Path - - -def csv_to_markdown( - text: str, - *, - delimiter: str = ",", - header: bool = True, -) -> str: - raw = text.strip() - if not raw: - raise ValueError("CSV text is empty") - - rows = list(csv.reader(StringIO(raw), delimiter=delimiter)) - if not rows: - raise ValueError("CSV text has no rows") - if any(len(row) != len(rows[0]) for row in rows): - raise ValueError("CSV rows do not all have the same number of columns") - - if header: - headings = [cell.strip() or " " for cell in rows[0]] - body = rows[1:] - else: - headings = [f"Col {index}" for index in range(1, len(rows[0]) + 1)] - body = rows - - lines = [ - "| " + " | ".join(headings) + " |", - "| " + " | ".join("---" for _ in headings) + " |", - ] - for row in body: - lines.append("| " + " | ".join(cell.strip() for cell in row) + " |") - return "\n".join(lines) + "\n" - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Convert a CSV file to a Markdown table." - ) - parser.add_argument("csv_file", type=Path, help="path to a CSV file") - parser.add_argument( - "-d", - "--delimiter", - default=",", - help="field delimiter (default: comma)", - ) - parser.add_argument( - "--no-header", - action="store_true", - help="treat the first row as data", - ) - args = parser.parse_args() - text = args.csv_file.read_text(encoding="utf-8") - print( - csv_to_markdown( - text, - delimiter=args.delimiter, - header=not args.no_header, - ), - end="", - ) - - -if __name__ == "__main__": - main() diff --git a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py b/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py deleted file mode 100644 index 0faeef366c..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/csv_to_markdown_ui.py +++ /dev/null @@ -1,50 +0,0 @@ -from nicegui import run, ui - -from csv_to_markdown import csv_to_markdown - -SAMPLE = "name,role\nAda,Engineer\nGrace,Scientist\n" - -source = ui.textarea("CSV", value=SAMPLE).classes("w-full").props("rows=8") -delimiter = ui.select( - {",": "Comma", ";": "Semicolon", "\t": "Tab"}, - value=",", - label="Delimiter", -) -header = ui.checkbox("First row is the header", value=True) -status = ui.label("Paste CSV, then click Convert.") -progress = ui.linear_progress(show_value=False, size="8px").props( - "indeterminate" -) -progress.set_visibility(False) -markdown = ui.textarea("Markdown").classes("w-full").props("rows=8") - - -async def convert() -> None: - raw = source.value or "" - convert_button.disable() - progress.set_visibility(True) - status.set_text("Converting") - try: - result = await run.io_bound( - csv_to_markdown, - raw, - delimiter=delimiter.value, - header=header.value, - ) - except ValueError as exc: - markdown.set_value("") - status.set_text(str(exc)) - ui.notify(str(exc), type="negative") - return - finally: - progress.set_visibility(False) - convert_button.enable() - - markdown.set_value(result) - status.set_text("Conversion complete.") - ui.notify("Markdown table ready", type="positive") - - -convert_button = ui.button("Convert", on_click=convert) - -ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py b/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py index e6d83709f1..51a40ecf68 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py +++ b/nicegui-a-browser-frontend-for-your-python-code/element_catalog.py @@ -1,17 +1,5 @@ from nicegui import ui -name = ui.input("Project", value="notes") -notes = ui.textarea("Notes", value="Ship the export") -copies = ui.number("Copies", value=2, min=1, max=10) -kind = ui.select(["Draft", "Final"], value="Draft", label="Kind") -urgent = ui.checkbox("Urgent") -ui.label("Extension") -extension = ui.radio(["txt", "csv"], value="txt") -ui.label("Priority") -priority = ui.slider(min=0, max=10, value=3) -archive = ui.switch("Archive after") -summary = ui.label("No choices yet") - def show() -> None: summary.set_text( @@ -23,6 +11,17 @@ def show() -> None: ui.notify(notes.value) +name = ui.input("Project", value="notes") +notes = ui.textarea("Notes", value="Ship the export") +copies = ui.number("Copies", value=2, min=1, max=10) +kind = ui.select(["Draft", "Final"], value="Draft", label="Kind") +urgent = ui.checkbox("Urgent") +ui.label("Extension") +extension = ui.radio(["txt", "csv"], value="txt") +ui.label("Priority") +priority = ui.slider(min=0, max=10, value=3) +archive = ui.switch("Archive after") +summary = ui.label("No choices yet") ui.button("Show choices", on_click=show) ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py index b2ea707e12..9894defdab 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py +++ b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py @@ -1,14 +1,27 @@ from nicegui import ui +ui.colors( + primary="#0F766E", + secondary="#334155", + accent="#C2410C", +) + with ui.row().classes("w-full items-center gap-4 p-4"): ui.icon("request_quote", size="md").props("color=primary") ui.label("Estimate").classes("text-h6 text-weight-bold") + ui.space() + ui.badge("Draft").props("color=secondary") -with ui.card().classes("w-80"): - quantity = ui.number("Quantity", value=2, min=1) - unit_price = ui.number("Unit price", value=40.0, min=0, format="%.2f") +with ui.card().classes("w-80 shadow-2"): + ui.label("Line item").classes("text-subtitle2 text-weight-medium") + quantity = ui.number("Quantity", value=2, min=1).classes("w-full") + unit_price = ui.number( + "Unit price", value=40.0, min=0, format="%.2f" + ).classes("w-full") taxed = ui.checkbox("Add 20% tax") - total = ui.label("Total: —").classes("text-subtitle1") + ui.separator() + total = ui.label("Total: —").classes("text-h6 text-weight-bold") + ui.label("Tax is off by default.").classes("text-caption text-grey-7") def calculate() -> None: qty = quantity.value @@ -23,6 +36,8 @@ def calculate() -> None: amount *= 1.2 total.set_text(f"Total: {amount:.2f}") - ui.button("Calculate", on_click=calculate).props("color=primary") + ui.button("Calculate", on_click=calculate).props( + "color=primary" + ).classes("w-full q-mt-md") ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py b/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py new file mode 100644 index 0000000000..c7826f0f00 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py @@ -0,0 +1,64 @@ +import argparse +import secrets +import string + +MIN_LENGTH = 8 +MAX_LENGTH = 64 +DEFAULT_LENGTH = 16 +SYMBOLS = "!@#$%^&*()-_=+" + + +def generate_password( + length: int, + digits: bool = True, + symbols: bool = False, +) -> str: + if not MIN_LENGTH <= length <= MAX_LENGTH: + raise ValueError( + f"length must be from {MIN_LENGTH} to {MAX_LENGTH}" + ) + + alphabet = string.ascii_letters + if digits: + alphabet += string.digits + if symbols: + alphabet += SYMBOLS + return "".join(secrets.choice(alphabet) for _ in range(length)) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate a random password." + ) + parser.add_argument( + "-l", + "--length", + type=int, + default=DEFAULT_LENGTH, + help=( + f"number of characters, {MIN_LENGTH} to {MAX_LENGTH} " + f"(default: {DEFAULT_LENGTH})" + ), + ) + parser.add_argument( + "--digits", + action=argparse.BooleanOptionalAction, + default=True, + help="allow digits (default: on)", + ) + parser.add_argument( + "--symbols", + action=argparse.BooleanOptionalAction, + default=False, + help="allow symbols (default: off)", + ) + args = parser.parse_args() + + try: + print(generate_password(args.length, args.digits, args.symbols)) + except ValueError as error: + parser.error(str(error)) + + +if __name__ == "__main__": + main() diff --git a/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py b/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py new file mode 100644 index 0000000000..83572e1456 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py @@ -0,0 +1,50 @@ +from nicegui import ui + +from generate_password_cli import ( + DEFAULT_LENGTH, + MAX_LENGTH, + MIN_LENGTH, + generate_password, +) + + +def generate() -> None: + size = length.value + if size is None: + show_error("Enter a length.") + return + if not float(size).is_integer(): + show_error("Enter a whole number for the length.") + return + + try: + result = generate_password(int(size), digits.value, symbols.value) + except ValueError as error: + show_error(f"{str(error).capitalize()}.") + return + + password.set_value(result) + status.set_text( + f"Generated a new password with {int(size)} characters." + ) + + +def show_error(message: str) -> None: + password.set_value("") + status.set_text(message) + ui.notify(message, type="warning") + + +length = ui.number( + "Length", + value=DEFAULT_LENGTH, + min=MIN_LENGTH, + max=MAX_LENGTH, +) +digits = ui.checkbox("Allow digits", value=True) +symbols = ui.checkbox("Allow symbols") +password = ui.input("Password").props("readonly").classes("w-full") +status = ui.label("Click Generate for a new password.") +ui.button("Generate", on_click=generate) + +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui.py b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui.py new file mode 100644 index 0000000000..f835bbb52d --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui.py @@ -0,0 +1,6 @@ +from nicegui import ui + +ui.label("Hello, NiceGUI!") +ui.button("Click me", on_click=lambda: ui.notify("It works!")) + +ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py new file mode 100644 index 0000000000..daa2c56fc8 --- /dev/null +++ b/nicegui-a-browser-frontend-for-your-python-code/hello_nicegui_native.py @@ -0,0 +1,5 @@ +from nicegui import ui + +ui.label("Hello, NiceGUI!") +ui.button("Click me", on_click=lambda: ui.notify("It works!")) +ui.run(native=True) diff --git a/nicegui-a-browser-frontend-for-your-python-code/password_generator.py b/nicegui-a-browser-frontend-for-your-python-code/password_generator.py deleted file mode 100644 index 789a67d685..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/password_generator.py +++ /dev/null @@ -1,35 +0,0 @@ -import secrets -import string - -from nicegui import ui - -length = ui.number("Length", value=16, min=8, max=64) -digits = ui.checkbox("Include digits", value=True) -symbols = ui.checkbox("Include symbols") -password = ui.input("Password").props("readonly") -status = ui.label("Click Generate for a new password.") - - -def generate() -> None: - size = length.value - if size is None or size != int(size) or size < 8 or size > 64: - password.set_value("") - status.set_text("Choose a whole length from 8 to 64.") - ui.notify("Length must be a whole number from 8 to 64", type="warning") - return - - alphabet = string.ascii_letters - if digits.value: - alphabet += string.digits - if symbols.value: - alphabet += "!@#$%^&*()-_=+" - - size = int(size) - result = "".join(secrets.choice(alphabet) for _ in range(size)) - password.set_value(result) - status.set_text(f"Generated a {size}-character password.") - - -ui.button("Generate", on_click=generate) - -ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py b/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py deleted file mode 100644 index bef186958f..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/password_generator_native.py +++ /dev/null @@ -1,35 +0,0 @@ -import secrets -import string - -from nicegui import ui - -length = ui.number("Length", value=16, min=8, max=64) -digits = ui.checkbox("Include digits", value=True) -symbols = ui.checkbox("Include symbols") -password = ui.input("Password").props("readonly") -status = ui.label("Click Generate for a new password.") - - -def generate() -> None: - size = length.value - if size is None or size != int(size) or size < 8 or size > 64: - password.set_value("") - status.set_text("Choose a whole length from 8 to 64.") - ui.notify("Length must be a whole number from 8 to 64", type="warning") - return - - alphabet = string.ascii_letters - if digits.value: - alphabet += string.digits - if symbols.value: - alphabet += "!@#$%^&*()-_=+" - - size = int(size) - result = "".join(secrets.choice(alphabet) for _ in range(size)) - password.set_value(result) - status.set_text(f"Generated a {size}-character password.") - - -ui.button("Generate", on_click=generate) - -ui.run(native=True) diff --git a/nicegui-a-browser-frontend-for-your-python-code/people.csv b/nicegui-a-browser-frontend-for-your-python-code/people.csv deleted file mode 100644 index 78ed284e8a..0000000000 --- a/nicegui-a-browser-frontend-for-your-python-code/people.csv +++ /dev/null @@ -1,3 +0,0 @@ -name,role -Ada,Engineer -Grace,Scientist diff --git a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py index b904160b83..3569687099 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py +++ b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py @@ -2,19 +2,17 @@ import plotly.express as px from nicegui import ui -traffic = pd.DataFrame( - { - "Endpoint": [ - "/login", - "/api/items", - "/api/search", - "/checkout", - "/health", - "/admin", - ], - "Requests": [1280, 2540, 1875, 940, 4120, 210], - } -) +traffic = pd.DataFrame({ + "Endpoint": [ + "/login", + "/api/items", + "/api/search", + "/checkout", + "/health", + "/admin", + ], + "Requests": [1280, 2540, 1875, 940, 4120, 210], +}) fig = px.bar( traffic, x="Endpoint", From 539b17938a598d6b75d6ece41515e7cd4522d945 Mon Sep 17 00:00:00 2001 From: Nuno Bispo Date: Fri, 9 Oct 2026 13:19:44 +0200 Subject: [PATCH 6/6] Formatting files --- .../estimate_card.py | 6 ++--- .../generate_password_cli.py | 8 ++----- .../generate_password_ui.py | 4 +--- .../plotly_chart.py | 24 ++++++++++--------- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py index 9894defdab..a7dd8ba198 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py +++ b/nicegui-a-browser-frontend-for-your-python-code/estimate_card.py @@ -36,8 +36,8 @@ def calculate() -> None: amount *= 1.2 total.set_text(f"Total: {amount:.2f}") - ui.button("Calculate", on_click=calculate).props( - "color=primary" - ).classes("w-full q-mt-md") + ui.button("Calculate", on_click=calculate).props("color=primary").classes( + "w-full q-mt-md" + ) ui.run() diff --git a/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py b/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py index c7826f0f00..62b798d626 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py +++ b/nicegui-a-browser-frontend-for-your-python-code/generate_password_cli.py @@ -14,9 +14,7 @@ def generate_password( symbols: bool = False, ) -> str: if not MIN_LENGTH <= length <= MAX_LENGTH: - raise ValueError( - f"length must be from {MIN_LENGTH} to {MAX_LENGTH}" - ) + raise ValueError(f"length must be from {MIN_LENGTH} to {MAX_LENGTH}") alphabet = string.ascii_letters if digits: @@ -27,9 +25,7 @@ def generate_password( def main() -> None: - parser = argparse.ArgumentParser( - description="Generate a random password." - ) + parser = argparse.ArgumentParser(description="Generate a random password.") parser.add_argument( "-l", "--length", diff --git a/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py b/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py index 83572e1456..af44e016f7 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py +++ b/nicegui-a-browser-frontend-for-your-python-code/generate_password_ui.py @@ -24,9 +24,7 @@ def generate() -> None: return password.set_value(result) - status.set_text( - f"Generated a new password with {int(size)} characters." - ) + status.set_text(f"Generated a new password with {int(size)} characters.") def show_error(message: str) -> None: diff --git a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py index 3569687099..b904160b83 100644 --- a/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py +++ b/nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py @@ -2,17 +2,19 @@ import plotly.express as px from nicegui import ui -traffic = pd.DataFrame({ - "Endpoint": [ - "/login", - "/api/items", - "/api/search", - "/checkout", - "/health", - "/admin", - ], - "Requests": [1280, 2540, 1875, 940, 4120, 210], -}) +traffic = pd.DataFrame( + { + "Endpoint": [ + "/login", + "/api/items", + "/api/search", + "/checkout", + "/health", + "/admin", + ], + "Requests": [1280, 2540, 1875, 940, 4120, 210], + } +) fig = px.bar( traffic, x="Endpoint",