Python Web Development with Reflex: Creating Dynamic Graphs Easily

Written by tomg | Published 2023/11/22
Tech Story Tags: python-web-development | data-visualization | reflex | dynamic-graphs | web-apps | react-components | python-programming | data-representation

TLDRvia the TL;DR App

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.

1. Getting started with your Python Web App

If you face any problems here with installation check out more docs here: https://reflex.dev/docs/getting-started/installation/

  1. $ pip install reflex

  2. $ reflex init

2. Creating an Interactive Graph

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.

3. Multiple Graphs

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()

4. Conclusion

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 documentation.

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.


Written by tomg | Working to help Python developers build and deploy Web Apps more effectively.
Published by HackerNoon on 2023/11/22