Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions nicegui-a-browser-frontend-for-your-python-code/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# NiceGUI: A Browser Frontend for Your Python Code

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.11 or later**. 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, 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 |
| --- | --- |
| `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 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.

The native example opens its own window:

```console
$ python hello_nicegui_native.py
```

The password CLI works without starting a server:

```console
$ python generate_password_cli.py -l 24 --symbols
```
27 changes: 27 additions & 0 deletions nicegui-a-browser-frontend-for-your-python-code/element_catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from nicegui import ui


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)


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()
43 changes: 43 additions & 0 deletions nicegui-a-browser-frontend-for-your-python-code/estimate_card.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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 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")
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
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").classes(
"w-full q-mt-md"
)

ui.run()
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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()
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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)
26 changes: 26 additions & 0 deletions nicegui-a-browser-frontend-for-your-python-code/plotly_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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()
28 changes: 28 additions & 0 deletions nicegui-a-browser-frontend-for-your-python-code/request_chart.py
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
nicegui[plotly]==3.17.0
plotly==6.9.0
pandas==3.0.5
pywebview==6.2.1
Loading