# Example: BIM Dashboard with WebView2 **Demo:** Production-grade dashboard with React + TypeScript UI **Script:** [`dashboard_script.py`](https://github.com/trgiangv/RevitDevTool/blob/master/source/RevitDevTool.PythonDemo/commands/dashboard_script.py) **Time:** 5-10 minutes (first run with dependency installation) ![Python Dashboard](images/RevitDevTool_PythonDashboard.png) --- ## What It Demonstrates - **Modern web stack** - React 19 + TypeScript + TailwindCSS v4 + Semi Design - **WebView2 integration** - Embedded browser UI in Revit - **Revit data collection** - Elements, warnings, heavy families - **Real-time analytics** - Polars + NumPy data processing - **Bidirectional communication** - Python ↔ JavaScript bridge - **Excel export** - OpenPyXL with formatted worksheets - **Hot reload development** - Vite dev server for rapid iteration --- ## Architecture Overview ``` dashboard_script.py (Entry Point) ↓ revit_dashboard/runner.py (Orchestrator) ↓ ├─ data/ │ ├─ collector.py ← Revit API data extraction │ ├─ warnings.py ← Model warnings │ ├─ heavy_families.py ← Performance analysis │ └─ categories.py ← Category statistics ├─ analytics/ │ └─ engine.py ← Polars/NumPy analytics ├─ presentation/ │ ├─ webview_host.py ← WebView2 window │ └─ bridge.py ← MessageRouter (Python ↔ JS) ├─ revit_api/ │ └─ handler.py ← Revit API actions (selection, zoom, etc.) ├─ export/ │ └─ excel_exporter.py ← Excel generation └─ core/ └─ event_queue.py ← RevitActionDispatcher (thread-safe) revit_dashboard_ui/ (Frontend - React + TypeScript) ↓ ├─ src/ │ ├─ pages/ ← Multi-page app (home, health, inventory, schedule, settings) │ ├─ features/ ← Feature modules (charts, filters, tables) │ ├─ components/ ← Reusable UI components (shadcn/ui) │ ├─ providers/ ← React context (bridge, dashboard state) │ └─ lib/ │ ├─ bridge-client.ts ← JavaScript bridge client │ └─ mock-data.ts ← Mock data for browser development └─ dist/ ← Built production files ``` --- ## Entry Point Pattern ```python # dashboard_script.py # /// script # dependencies = [ # "polars==1.38.1", # "numpy==2.4.2", # "openpyxl==3.1.5", # ] # /// from revit_dashboard.runner import main if __name__ == "__main__": main() ``` **What happens:** 1. UV auto-installs dependencies (first run only) 2. Imports orchestrator from `revit_dashboard` package 3. Launches dashboard workflow --- ## Data Collection Pattern ```python # revit_dashboard/data/collector.py def collect_model_elements(doc): """Extract all elements with properties.""" collector = FilteredElementCollector(doc) elements = [] for el in collector.WhereElementIsNotElementType(): try: elements.append({ "id": el.Id.IntegerValue, "name": el.Name or "Unnamed", "category": el.Category.Name if el.Category else "Unknown", "level": get_level_name(el), "family": get_family_name(el), "type": get_type_name(el), # ... more properties }) except: continue return elements ``` **Processing:** - Scans entire model (~2-5 seconds for large projects) - Extracts properties safely (handles exceptions) - Returns list of dictionaries for analytics --- ## Analytics Pattern ```python # revit_dashboard/analytics/engine.py import polars as pl import numpy as np def build_dashboard_payload(rows, filters, model_info, warnings, heavy_families): """Transform raw data into analytics payload.""" # Convert to Polars DataFrame for fast analytics df = pl.DataFrame(rows) # Apply filters if provided if filters: df = apply_filters(df, filters) # Compute statistics category_counts = df.group_by("category").count() family_distribution = df.group_by("family").count() level_breakdown = df.group_by("level").count() # Build payload for frontend return { "model_info": model_info, "rows": df.to_dicts(), # For table display "stats": { "total_elements": len(df), "categories": len(category_counts), "families": len(family_distribution), }, "charts": { "category_counts": category_counts.to_dicts(), "family_distribution": family_distribution.to_dicts(), }, "warnings": warnings, "heavy_families": heavy_families, } ``` **Features:** - Polars for fast DataFrame operations - NumPy for numerical computations - Flexible filtering system - Structured payload for frontend --- ## WebView2 Integration Pattern ```python # revit_dashboard/presentation/webview_host.py import json from System.Windows.Forms import WebBrowser DEV_MODE = False # Set to True for hot reload development def show_dashboard(payload, handler_factory): """Launch WebView2 window with React app.""" if DEV_MODE: # Development: Connect to Vite dev server (hot reload) url = "http://localhost:5173" else: # Production: Load built files from dist/ html_path = get_dist_index_html() url = f"file:///{html_path}" # Create window with bridge window = WebViewWindow(url) # Inject payload when ready def on_ready(): window.inject_payload(payload) # Setup message handlers router = MessageRouter(window) dispatcher = RevitActionDispatcher() handlers = handler_factory(router, dispatcher) for event_type, handler in handlers.items(): router.on(event_type, handler) window.show() ``` **Development modes:** 1. **Browser mode** - `npm run dev` → Mock data for UI development 2. **Revit dev mode** - `DEV_MODE = True` → Hot reload with real data 3. **Production mode** - `DEV_MODE = False` → Built files from `dist/` --- ## Bridge Communication Pattern ### Python → JavaScript ```python # revit_dashboard/presentation/bridge.py class MessageRouter: def emit(self, event_type, payload): """Send message to JavaScript.""" message = json.dumps({ "type": event_type, "payload": payload }) self.window.execute_script(f"window.handlePythonMessage({message})") ``` ### JavaScript → Python ```typescript // revit_dashboard_ui/src/lib/bridge-client.ts export class BridgeClient { send(type: string, payload: any) { // Send to Python via WebView2 bridge window.chrome.webview.postMessage({ type, payload }); } on(type: string, handler: (payload: any) => void) { // Receive from Python window.addEventListener('message', (event) => { if (event.data.type === type) { handler(event.data.payload); } }); } } ``` ### Example: Export Flow ```typescript // Frontend: User clicks export button bridge.send('export_excel', { filters: currentFilters }); // Backend: Handle export def handle_export(message): filters = message.get('payload', {}).get('filters', {}) path = export_filtered_rows_to_excel(rows, filters) router.emit('bim-export-result', { 'ok': True, 'path': path }) // Frontend: Show success notification bridge.on('bim-export-result', (result) => { if (result.ok) { toast.success(`Exported to ${result.path}`); } }); ``` --- ## Revit API Actions Pattern ```python # revit_dashboard/revit_api/handler.py class RevitApiHandler: def handle(self, method, params): """Execute Revit API actions (thread-safe).""" if method == "select_elements": element_ids = params.get("ids", []) self._select_in_revit(element_ids) return {"ok": True} elif method == "zoom_to_elements": element_ids = params.get("ids", []) self._zoom_to_elements(element_ids) return {"ok": True} elif method == "refresh_data": # Re-collect from Revit payload = self.refresh_callback() return {"ok": True, "payload": payload} ``` **Thread safety:** - Uses `RevitActionDispatcher` to queue actions - Executes on Revit's main thread via `ExternalEvent` - Returns results asynchronously to frontend --- ## Frontend Architecture ### Multi-Page App ```typescript // revit_dashboard_ui/src/App.tsx } /> } /> } /> } /> } /> ``` ### State Management ```typescript // revit_dashboard_ui/src/providers/dashboard-provider.tsx export function DashboardProvider({ children }) { const [payload, setPayload] = useState(null); const [filters, setFilters] = useState({}); const [selection, setSelection] = useState([]); // Auto-sync with Python backend useEffect(() => { bridge.on('payload-update', setPayload); }, []); return ( {children} ); } ``` ### Components - **Semi Design** - Enterprise-grade UI component library (`@douyinfe/semi-ui`) - **Radix UI** - Headless UI primitives for custom components - **TanStack Table** - Virtualized table (handles 10k+ rows) - **Recharts** - Interactive charts - **Sonner** - Toast notifications --- ## Features ### 1. Home Page - Project overview metrics - Category distribution charts - Quick stats (elements, families, warnings) ### 2. Health Page - Model warnings matrix - Heavy families tracker - Performance recommendations ### 3. Inventory Page - Family explorer with tree view - Instance count by family - Type distribution ### 4. Schedule Page - Filterable data table (virtualized) - Export to Excel - Column sorting and filtering ### 5. Settings Page - Filter configuration - Display preferences - Export options --- ## Try It Yourself ### Quick Start (Production Mode) **Use pre-built UI - no setup required:** 1. **Load** folder: `source/RevitDevTool.PythonDemo/commands/` 2. **Execute** `dashboard_script.py` 3. **Wait** for data collection (~2-5 seconds) 4. **Explore** multi-page dashboard 5. **Click** export to generate Excel The dashboard uses pre-built files from `revit_dashboard_ui/dist/` - no Node.js or npm installation needed. --- ### Development Mode (Hot Reload) **For UI development with live updates:** #### Prerequisites 1. **Install Node.js** - Download from [nodejs.org](https://nodejs.org/) (LTS version recommended) - Verify installation: ```bash node --version # Should show v20.x or higher npm --version # Should show v10.x or higher ``` #### Setup Steps 1. **Navigate to frontend folder:** ```bash cd source/RevitDevTool.PythonDemo/revit_dashboard_ui ``` 2. **Install dependencies:** ```bash npm install ``` This installs all required packages (~5 minutes first time): - React 19, TypeScript - Semi Design, Radix UI - TailwindCSS v4, Vite - TanStack Table, Recharts, etc. 3. **Start dev server:** ```bash npm run dev ``` You should see: ``` VITE v7.3.1 ready in 1234 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ``` 4. **Enable dev mode in Python:** Edit `revit_dashboard/presentation/webview_host.py`: ```python DEV_MODE = True # Change from False to True ``` 5. **Run dashboard from Revit:** - Execute `dashboard_script.py` in RevitDevTool - Dashboard will connect to `http://localhost:5173` - Any changes to React code will hot reload instantly #### Development Workflow ``` Edit React code → Save → Browser auto-refreshes → See changes immediately ``` No need to rebuild or restart Revit! #### Build for Production When ready to deploy: ```bash npm run build ``` This creates optimized files in `dist/` folder. Set `DEV_MODE = False` to use them. --- ### Troubleshooting **"npm: command not found"** - Install Node.js from [nodejs.org](https://nodejs.org/) - Restart terminal after installation **"Port 5173 already in use"** - Stop other Vite dev servers - Or change port in `vite.config.ts` **"Module not found" errors** - Run `npm install` again - Delete `node_modules/` and `package-lock.json`, then `npm install` **Dashboard shows blank page** - Check dev server is running (`npm run dev`) - Verify `DEV_MODE = True` in `webview_host.py` - Check browser console for errors (F12) --- **Full source:** - Python: [`dashboard_script.py`](https://github.com/trgiangv/RevitDevTool/blob/master/source/RevitDevTool.PythonDemo/commands/dashboard_script.py) - Frontend: [`revit_dashboard_ui/`](https://github.com/trgiangv/RevitDevTool/tree/master/source/RevitDevTool.PythonDemo/revit_dashboard_ui) --- ## Project Structure ``` RevitDevTool.PythonDemo/ ├── commands/ │ └── dashboard_script.py ← Entry point (PEP 723) │ ├── revit_dashboard/ ← Python backend │ ├── runner.py ← Main orchestrator │ ├── context.py ← HOST_APP singleton │ ├── constants.py ← Configuration │ ├── data/ │ │ ├── collector.py ← Element collection │ │ ├── warnings.py ← Warning extraction │ │ ├── heavy_families.py ← Performance analysis │ │ └── categories.py ← Category stats │ ├── analytics/ │ │ └── engine.py ← Polars/NumPy analytics │ ├── presentation/ │ │ ├── webview_host.py ← WebView2 window │ │ └── bridge.py ← MessageRouter │ ├── revit_api/ │ │ └── handler.py ← Revit API actions │ ├── export/ │ │ └── excel_exporter.py ← Excel generation │ ├── core/ │ │ └── event_queue.py ← RevitActionDispatcher │ └── contracts/ │ └── payload.py ← Type definitions │ └── revit_dashboard_ui/ ← React frontend ├── src/ │ ├── App.tsx ← Main app component │ ├── pages/ ← Page components │ │ ├── home-page.tsx │ │ ├── health-page.tsx │ │ ├── inventory-page.tsx │ │ ├── schedule-page.tsx │ │ └── settings-page.tsx │ ├── features/ ← Feature modules │ │ ├── home/ │ │ ├── health/ │ │ ├── inventory/ │ │ └── filters/ │ ├── components/ ← Reusable components │ │ ├── ui/ ← Custom components (Radix UI primitives) │ │ ├── charts/ ← Chart components (Recharts) │ │ └── layout/ ← Layout components (Semi Design) │ ├── providers/ ← React contexts │ │ ├── bridge-provider.tsx │ │ └── dashboard-provider.tsx │ ├── hooks/ ← Custom hooks │ ├── lib/ ← Utilities │ │ ├── bridge-client.ts ← Bridge client │ │ └── mock-data.ts ← Mock data generator │ └── types/ ← TypeScript types ├── dist/ ← Built production files ├── package.json ← npm dependencies ├── vite.config.ts ← Vite configuration └── tailwind.config.js ← TailwindCSS config ``` --- ## Tech Stack ### Python Backend - **Polars** - Fast DataFrame operations - **NumPy** - Numerical computations - **OpenPyXL** - Excel generation - **Revit API** - Data extraction ### JavaScript Frontend - **React 19** - UI framework - **TypeScript** - Type safety - **Vite** - Build tool with hot reload - **TailwindCSS v4** - Styling - **Semi Design** - Enterprise UI component library - **Radix UI** - Headless UI primitives - **TanStack Table** - Virtualized tables - **Recharts** - Charts - **Sonner** - Notifications --- ## Related Examples - **Data analysis** → [`data_analysis_script.py`](https://github.com/trgiangv/RevitDevTool/blob/master/source/RevitDevTool.PythonDemo/commands/data_analysis_script.py) - **Logging** → [`logging_format_script.py`](https://github.com/trgiangv/RevitDevTool/blob/master/source/RevitDevTool.PythonDemo/commands/logging_format_script.py) - **Visualization** → [`visualization_curve_script.py`](https://github.com/trgiangv/RevitDevTool/blob/master/source/RevitDevTool.PythonDemo/commands/visualization_curve_script.py)