Skip to content

Commit 5d0391d

Browse files
feat: pygwalker component in Reflex (#700)
* Add Reflex integration * feat: reflex api * feat(build): add reflex optional dependency (#706) * fix: reflex comm * fix: reported potential bugs
1 parent 05bf95f commit 5d0391d

File tree

11 files changed

+483
-2
lines changed

11 files changed

+483
-2
lines changed

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,11 @@ poetry.lock
140140
pygwalker/templates/graphic-walker.umd.js
141141
pygwalker/templates/graphic-walker.iife.js
142142

143-
tests/*.csv
143+
tests/*.csv
144+
145+
__pycache__/
146+
.states
147+
*.db
148+
.web
149+
*.py[cod]
150+
assets/external/

examples/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This folder contains example implementations of Pygwalker across different inter
88
- [`jupyter_demo.ipynb`](jupyter_demo.ipynb): Basic Pygwalker usage in Jupyter environments
99
- [`marimo_demo.py`](marimo_demo.py): Interactive data exploration using Pygwalker in Marimo notebooks
1010
- [`streamlit_demo.py`](streamlit_demo.py): Embedding Pygwalker visualizations in Streamlit apps
11+
- [`reflex_demo.py`](reflex_demo.py): Example of using Pygwalker in a Reflex application
1112
- [`web_server_demo.py`](web_server_demo.py): Setting up Pygwalker with a web server
1213

1314
## Running examples
@@ -17,3 +18,7 @@ pip install pygwalker
1718
```
1819

1920
Additional dependencies may be required based on the specific interface you're using (e.g., streamlit, dash, gradio).
21+
For the Reflex demo, install the optional Reflex plugin:
22+
```shell
23+
pip install "pygwalker[reflex]"
24+
```

examples/reflex_demo/.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.web
2+
.states
3+
assets/external/
4+
*.db
5+
*.py[cod]
6+
# Reflex generated files and directories
7+
.web/
8+
__pycache__/
9+
requirements.txt
10+
*.pyc
11+
*.pyo
12+
*.pyd
13+
.Python
14+
build/
15+
develop-eggs/
16+
dist/
17+
downloads/
18+
eggs/
19+
.eggs/
20+
lib/
21+
lib64/
22+
parts/
23+
sdist/
24+
var/
25+
wheels/
26+
*.egg-info/
27+
.installed.cfg
28+
*.egg
29+
MANIFEST
30+
31+
# Environment files
32+
.env
33+
.venv
34+
env/
35+
venv/
36+
ENV/
37+
env.bak/
38+
venv.bak/
39+
40+
# Database files
41+
reflex.db

examples/reflex_demo/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# PyGWalker Reflex Demo
2+
3+
This demo shows how to integrate PyGWalker with Reflex, a Python web framework for building interactive web applications.
4+
5+
## Prerequisites
6+
7+
Make sure you have PyGWalker installed with the Reflex plugin:
8+
9+
```bash
10+
pip install "pygwalker[reflex]"
11+
```
12+
13+
## Running the Demo
14+
15+
1. Navigate to this directory:
16+
```bash
17+
cd examples/reflex_demo
18+
```
19+
20+
2. Initialize Reflex (first time only):
21+
```bash
22+
reflex init
23+
```
24+
25+
3. Run the application:
26+
```bash
27+
reflex run
28+
```
29+
30+
4. Open your browser and navigate to `http://localhost:3002`
31+
32+
## What This Demo Shows
33+
34+
- **Full PyGWalker + Reflex Integration**: Complete integration with API transformer
35+
- Proper Reflex package structure (`app/app.py`)
36+
- Interactive data visualization components within Reflex
37+
- Automatic fallback when PyGWalker is not available
38+
- Proper error handling and graceful degradation
39+
40+
## Files in This Demo
41+
42+
- `app/app.py` - Main application file with PyGWalker integration
43+
- `app/__init__.py` - Package initialization
44+
- `rxconfig.py` - Reflex configuration file
45+
- `README.md` - This documentation file
46+
47+
## Package Structure
48+
49+
The demo follows the standard Reflex package structure:
50+
51+
```
52+
examples/reflex_demo/
53+
├── app/
54+
│ ├── __init__.py
55+
│ └── app.py # Main app with PyGWalker integration
56+
├── rxconfig.py # Reflex configuration
57+
├── README.md
58+
└── .gitignore
59+
```
60+
61+
This structure allows Reflex to properly import the app as `app.app` where:
62+
- `app_name="app"` in `rxconfig.py`
63+
- `app/` directory contains the Python package
64+
- `app/app.py` contains the `app = rx.App()` object
65+
66+
## Demo Features
67+
68+
- **Full Integration**: Uses PyGWalker's `register_pygwalker_api` transformer for complete functionality
69+
- **Local Data**: Uses generated sample data (weather data) to avoid network dependencies
70+
- **Error Handling**: Gracefully handles import and runtime errors with fallback mode
71+
- **Interactive UI**: Provides full PyGWalker data visualization capabilities
72+
73+
## Fixed Issues
74+
75+
This demo includes fixes for:
76+
-**ASGI Mounting Errors**: Fixed PyGWalker's `register_pygwalker_api` function to properly mount FastAPI sub-applications
77+
-**Package Structure**: Proper Reflex package structure following `app_name/app_name.py` convention
78+
-**Route Registration**: Improved API route registration using FastAPI mounting instead of direct route appending
79+
80+
The demo now provides complete PyGWalker integration within a Reflex web application with full API communication support.

examples/reflex_demo/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Pygwalker examples package."""
2+
3+
# Initialize examples package
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""PyGWalker Reflex Demo App Package."""

examples/reflex_demo/app/app.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
"""
2+
PyGWalker + Reflex integration demo.
3+
4+
This demo shows how to integrate PyGWalker with Reflex, a Python web framework.
5+
6+
To run this demo:
7+
1. Make sure you have installed PyGWalker with the Reflex plugin:
8+
pip install "pygwalker[reflex]"
9+
10+
2. Navigate to this directory:
11+
cd examples/reflex_demo
12+
13+
3. Run the app:
14+
reflex run
15+
"""
16+
17+
import os
18+
import sys
19+
import traceback
20+
import pandas as pd
21+
import reflex as rx
22+
23+
# Add the project root directory to sys.path to ensure pygwalker can be imported
24+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))
25+
if project_root not in sys.path:
26+
sys.path.insert(0, project_root)
27+
28+
# Try to import PyGWalker Reflex components, fall back to basic demo if not available
29+
try:
30+
from pygwalker.api.reflex import get_component
31+
from pygwalker.communications.reflex_comm import register_pygwalker_api
32+
PYGWALKER_AVAILABLE = True
33+
print("✅ PyGWalker Reflex integration is available!")
34+
except Exception as e:
35+
print(f"⚠️ PyGWalker Reflex integration not available: {e}")
36+
print("Running in fallback mode with basic Reflex demo.")
37+
PYGWALKER_AVAILABLE = False
38+
get_component = None
39+
register_pygwalker_api = None
40+
41+
42+
class State(rx.State):
43+
"""The app state."""
44+
pass
45+
46+
47+
def index() -> rx.Component:
48+
"""PyGWalker + Reflex demo page."""
49+
if PYGWALKER_AVAILABLE:
50+
return pygwalker_demo()
51+
else:
52+
return fallback_demo()
53+
54+
55+
def pygwalker_demo() -> rx.Component:
56+
"""PyGWalker integration demo."""
57+
try:
58+
# Use local data to avoid network issues
59+
df = pd.DataFrame({
60+
'Date': pd.date_range('2023-01-01', periods=100),
61+
'Temperature': [20 + 10 * (i % 10) / 10 for i in range(100)],
62+
'Humidity': [50 + 30 * (i % 7) / 7 for i in range(100)],
63+
'City': ['New York', 'London', 'Tokyo', 'Paris', 'Sydney'] * 20
64+
})
65+
66+
# Create a PyGWalker component
67+
pyg_component = get_component(
68+
df,
69+
theme_key="g2",
70+
appearance="media",
71+
default_tab="vis"
72+
)
73+
74+
return rx.vstack(
75+
rx.heading("Use Pygwalker In Reflex", size="3"),
76+
rx.text("This demo shows PyGWalker integrated with Reflex framework."),
77+
rx.text("✅ PyGWalker components with full API integration!", color="green"),
78+
pyg_component,
79+
spacing="4",
80+
width="100%",
81+
align_items="stretch",
82+
)
83+
except Exception as e:
84+
print(f"Error in pygwalker_demo: {e}")
85+
print(traceback.format_exc())
86+
return fallback_demo_with_error(str(e))
87+
88+
89+
def fallback_demo() -> rx.Component:
90+
"""Fallback demo when PyGWalker is not available."""
91+
# Sample data
92+
df = pd.DataFrame({
93+
'Date': pd.date_range('2023-01-01', periods=10),
94+
'Temperature': [20, 22, 25, 28, 30, 27, 24, 21, 19, 23],
95+
'Humidity': [45, 50, 55, 60, 65, 58, 52, 48, 44, 49],
96+
'City': ['New York'] * 10
97+
})
98+
99+
return rx.vstack(
100+
rx.heading("PyGWalker + Reflex Demo", size="3"),
101+
rx.text("PyGWalker Reflex integration is not available.", color="orange"),
102+
rx.text("Install it with: pip install 'pygwalker[reflex]'", font_family="monospace"),
103+
104+
rx.heading("Sample Data", size="2", margin_top="20px"),
105+
rx.text(f"Data shape: {df.shape}"),
106+
107+
rx.box(
108+
rx.text("Temperature Data:", font_weight="bold"),
109+
rx.text(f"Min: {df['Temperature'].min()}°C, Max: {df['Temperature'].max()}°C"),
110+
rx.text("Humidity Data:", font_weight="bold"),
111+
rx.text(f"Min: {df['Humidity'].min()}%, Max: {df['Humidity'].max()}%"),
112+
padding="15px",
113+
border="1px solid #ccc",
114+
border_radius="8px",
115+
margin_top="10px",
116+
),
117+
118+
rx.text(
119+
"This demo shows the basic Reflex setup working. "
120+
"When PyGWalker Reflex integration is available, "
121+
"this will display an interactive data visualization component.",
122+
margin_top="20px",
123+
font_style="italic"
124+
),
125+
126+
spacing="4",
127+
width="100%",
128+
align_items="stretch",
129+
padding="20px",
130+
)
131+
132+
133+
def fallback_demo_with_error(error_msg: str) -> rx.Component:
134+
"""Fallback demo when PyGWalker components fail."""
135+
return rx.vstack(
136+
rx.heading("PyGWalker + Reflex Demo", size="3"),
137+
rx.text("PyGWalker components encountered an error:", color="red"),
138+
rx.code(error_msg, padding="10px", background_color="gray.100"),
139+
rx.text("Falling back to basic demo.", color="orange"),
140+
141+
rx.text(
142+
"This suggests there might be a compatibility issue between "
143+
"PyGWalker and the current version of Reflex. The basic Reflex "
144+
"framework is working correctly.",
145+
margin_top="20px",
146+
font_style="italic"
147+
),
148+
149+
spacing="4",
150+
width="100%",
151+
align_items="stretch",
152+
padding="20px",
153+
)
154+
155+
156+
# Create a Reflex app with the fixed PyGWalker API transformer
157+
try:
158+
if PYGWALKER_AVAILABLE:
159+
try:
160+
app = rx.App(api_transformer=register_pygwalker_api)
161+
print("✅ Reflex app created with PyGWalker API transformer!")
162+
except Exception as e:
163+
print(f"⚠️ PyGWalker API transformer failed: {e}")
164+
print("Creating basic Reflex app without transformer...")
165+
app = rx.App()
166+
print("✅ Reflex app created without PyGWalker transformer!")
167+
else:
168+
app = rx.App()
169+
print("✅ Basic Reflex app created!")
170+
171+
app.add_page(index)
172+
print("✅ Pages added successfully!")
173+
174+
except Exception as e:
175+
print(f"❌ Error creating Reflex app: {e}")
176+
print(traceback.format_exc())
177+
raise

examples/reflex_demo/rxconfig.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import reflex as rx
2+
3+
config = rx.Config(
4+
app_name="app",
5+
backend_port=8000,
6+
loglevel="debug",
7+
)

0 commit comments

Comments
 (0)