Almost any web app that contains data needs a way to represent it succinctly and clearly to the user. However, most Python web development solutions do not provide an easy way to do this purely in Python.
Thanks to the plethora of existing React components for creating graphs, an implementation for Python can be created to easily solve this problem.
In this post, we’ll walk through how to build a web app purely in Python, which contains graphs that strike a balance between flexibility and ease of use. The graphs are easy to build but also easy to customize to your needs.
For this project, we are going to use a new open-source dev tool called Reflex.
If you face any problems here with installation check out more docs here: https://reflex.dev/docs/getting-started/installation/
$ pip install reflex
$ reflex init
In this example, we will create a live-streaming graph that updates every second with random data.
We start by defining some initial data for our chart to use, as well as some imports we will use for the project:
from typing import Any, Dict, List
import reflex as rx
import random
import asyncio
data = [
{"name": "A", "uv": 10, "pv": 110, "amt": 210},
{"name": "B", "uv": 20, "pv": 120, "amt": 230},
{"name": "C", "uv": 30, "pv": 120, "amt": 240},
{"name": "D", "uv": 30, "pv": 130, "amt": 210},
{"name": "E", "uv": 20, "pv": 140, "amt": 230},
{"name": "F", "uv": 40, "pv": 170, "amt": 250},
{"name": "G", "uv": 50, "pv": 190, "amt": 260},
]
Here uv
stands for unique visitors, pv
stands for page views and amt
stands for amount.
They are arbitrary values that we will use to populate our graph.
Next, we define a StreamingState
class that will be used to store the data and update it with an event handler:
class StreamingState(rx.State):
data: List[Dict[str, Any]] = data
stream: bool = False
def stop_stream(self):
self.stream = False
@rx.background
async def start_stream(self):
async with self:
self.stream = True
while self.stream:
async with self:
for i in range(len(self.data)):
self.data[i]["uv"] = random.randint(0, 100)
self.data[i]["pv"] = random.randint(100, 200)
self.data[i]["amt"] = random.randint(200, 300)
await asyncio.sleep(3)
Here, we define a stop_stream
method that will stop the stream when called. We also define a start_stream
method that will start the stream. We use the @rx.background
decorator to run the method in the background. This allows us to update the data without blocking the UI.
Remember to use async with self:
when updating the state in a background task.
Finally, we will define our UI using Reflex's new graphing components. We pass the data from our StreamingState
class to the area_chart
component and reference the data key we want to use in area
component. We also add a button to start and stop the stream.
The result is a live updating graph that looks like this:
The code for this graph is below:
def index():
return rx.vstack(
rx.recharts.area_chart(
rx.recharts.area(
data_key="pv",
stroke="#82ca9d",
fill="#82ca9d",
type_="natural",
),
rx.recharts.x_axis(
data_key="name",
),
rx.recharts.y_axis(),
rx.recharts.legend(),
data=StreamingState.data,
width="100%",
height=400,
),
rx.hstack(
rx.button(
"Start Stream",
on_click=StreamingState.start_stream,
disabled=StreamingState.stream,
),
rx.button(
"Stop Stream",
on_click=StreamingState.stop_stream,
),
width="100%",
),
width="100%",
)
# Add state and page to the app.
app = rx.App()
app.add_page(index)
app.compile()
The last three lines define our app, add the graph component to the base route, and then compile our app. Now, we can run it with the $ reflex run
terminal command.
We can add extra area
components to our chart to show the uv
and amt
data as well. We can also add a graphing_tooltip
an cartesian_grid
component to show the data when we hover over the chart.
Keep in mind the child coming first will be displayed in the back, so the order of the area
components matter.
The code for this graph is below:
def index():
return rx.vstack(
rx.recharts.area_chart(
rx.recharts.area(
data_key="pv",
fill="#48BB78",
stroke="#48BB78",
type_="natural",
),
rx.recharts.area(
data_key="uv",
fill="#F56565",
stroke="#F56565",
type_="natural",
),
rx.recharts.area(
data_key="amt",
fill="#4299E1",
stroke="#4299E1",
type_="natural",
),
rx.recharts.x_axis(
data_key="name",
),
rx.recharts.y_axis(),
data=StreamingState.data,
width="90%",
height=400,
),
rx.hstack(
rx.button(
"Start Stream",
on_click=StreamingState.start_stream,
is_disabled=StreamingState.stream,
width="100%",
color_scheme="green",
),
rx.button(
"Stop Stream",
on_click=StreamingState.stop_stream,
is_disabled=StreamingState.stream == False,
width="100%",
color_scheme="red",
),
width="100%",
)
)
# Add state and page to the app.
app = rx.App()
app.add_page(index)
app.compile()
So that’s it; in just a few lines of simple code, you’ve created your live streaming graphing web app in pure Python.
If you want to learn more about this and how to build graphs with Python check out this
If you have questions, please comment them below or message me on Twitter at @tgotsman12 or on LinkedIn. Share your app creations on social media and tag me, and I’ll be happy to provide feedback or help retweet!
Disclaimer: I work as a Founding Engineer at Reflex, where I build the open-source framework.