Skip to content

Commit 3c2befb

Browse files
committed
feat(gui): add interactive PySide6 GUI for domain visualization
Add a comprehensive GUI application for visualizing and manipulating Domain objects to configure Tree and Forest vascularization parameters. Features: - Interactive 3D domain visualization using PyVista/VTK - Point picking on domain surface for tree start points - Direction vector specification with normalization - Multi-network and multi-tree configuration - Real-time parameter adjustment for vessel generation - Progress tracking during tree/forest generation - Import/export domain files (.dmn) and configurations (JSON) Components: - Main window with menu bar and split layout - VTK widget with software rendering support (Linux) - Point selector for managing start points and directions - Parameter panel for tree/forest generation settings - Launch utilities and example usage scripts The GUI uses Qt's software OpenGL on Linux to ensure compatibility across different systems without requiring GPU drivers. Environment detection automatically locates Mesa DRI drivers from conda installations. Added dependencies: PySide6, pyvistaqt
1 parent 2eddbb4 commit 3c2befb

File tree

11 files changed

+1910
-1
lines changed

11 files changed

+1910
-1
lines changed

launch_gui.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
# Launcher script for svVascularize GUI with software rendering
3+
4+
# Set environment for software rendering
5+
export LIBGL_ALWAYS_SOFTWARE=1
6+
export GALLIUM_DRIVER=llvmpipe
7+
export MESA_GL_VERSION_OVERRIDE=3.3
8+
9+
# Set DRI driver path to conda's Mesa drivers
10+
if [ -n "$CONDA_PREFIX" ]; then
11+
export LIBGL_DRIVERS_PATH="$CONDA_PREFIX/x86_64-conda-linux-gnu/sysroot/usr/lib64/dri"
12+
fi
13+
14+
# Add svVascularize to Python path
15+
export PYTHONPATH="$(pwd):$PYTHONPATH"
16+
17+
# Launch the GUI
18+
python -c "
19+
import sys
20+
from qtpy.QtWidgets import QApplication
21+
from svv.visualize.gui import VascularizeGUI
22+
23+
app = QApplication(sys.argv)
24+
gui = VascularizeGUI()
25+
gui.show()
26+
sys.exit(app.exec_())
27+
"

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ pyvista~=0.44.2
1111
scikit-learn
1212
tqdm
1313
pymeshfix==0.17.0
14-
numexpr
14+
numexpr
15+
pyvistaqt
16+
pyside6

svv/visualize/gui/README.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# svVascularize GUI
2+
3+
Interactive GUI for visualizing and manipulating Domain objects to configure Tree and Forest vascularization.
4+
5+
## Features
6+
7+
- **3D Domain Visualization**: View your domain mesh in an interactive 3D viewport
8+
- **Interactive Point Selection**: Click on the domain surface to select start points for trees
9+
- **Direction Control**: Optionally specify custom directions for tree growth
10+
- **Multi-Network Support**: Configure multiple networks with multiple trees per network
11+
- **Parameter Configuration**: Adjust tree/forest generation parameters
12+
- **Real-time Visualization**: See generated trees/forests in 3D
13+
- **Export Configuration**: Save your start points and parameters for later use
14+
15+
## Installation
16+
17+
The GUI requires PySide6 and PyVistaQt (with PyVista).
18+
19+
Recommended (conda):
20+
```bash
21+
conda install -c conda-forge pyside6 pyvista pyvistaqt
22+
```
23+
24+
Alternative (pip):
25+
```bash
26+
pip install PySide6 pyvista PyVistaQt
27+
```
28+
29+
Notes:
30+
- On Linux, the GUI prefers software rendering (Mesa llvmpipe) to avoid GPU/driver issues. These settings are applied only on Linux and do not affect Windows or macOS. You can opt out with `SVV_GUI_GL_MODE=system`.
31+
- On Windows and macOS, no Mesa-specific setup is required.
32+
33+
## Usage
34+
35+
### Quick Launch
36+
37+
Use the provided launcher script:
38+
39+
```bash
40+
./launch_gui.sh
41+
```
42+
43+
### Basic Usage (from Python)
44+
45+
```python
46+
import os
47+
import sys
48+
49+
from svv.visualize.gui import launch_gui
50+
51+
# Launch and block until closed
52+
launch_gui()
53+
```
54+
55+
### With Pre-loaded Domain
56+
57+
```python
58+
from PySide6.QtWidgets import QApplication
59+
from svv.visualize.gui import VascularizeGUI
60+
from svv.domain.domain import Domain
61+
import pyvista as pv
62+
import sys
63+
64+
# Create or load a domain
65+
sphere = pv.Sphere(radius=5.0)
66+
domain = Domain(sphere)
67+
domain.create()
68+
domain.solve()
69+
domain.build()
70+
71+
# Launch GUI with domain (blocking)
72+
from svv.visualize.gui import launch_gui
73+
launch_gui(domain=domain)
74+
```
75+
76+
### Example Script
77+
78+
Run the included example:
79+
80+
```bash
81+
python -m svv.visualize.gui.example_usage
82+
```
83+
84+
### CLI Launch
85+
86+
```bash
87+
python -m svv.visualize.gui
88+
python -m svv.visualize.gui --domain path/to/domain.dmn
89+
```
90+
91+
## Linux Troubleshooting
92+
93+
If you see libGL/Mesa warnings or a segmentation fault on launch, ensure your conda environment has Mesa userspace drivers and that the loader uses them instead of the system drivers.
94+
95+
1. Use conda-forge packages and strict priority:
96+
- `conda config --env --add channels conda-forge`
97+
- `conda config --env --set channel_priority strict`
98+
2. Install GL stack (conda-forge):
99+
- `conda install -y mesalib libglvnd libxcb xorg-libx11 xorg-libxt xorg-libxfixes`
100+
3. Point the driver loader to conda's DRI path and favor software GL:
101+
- `export SVV_LIBGL_DRIVERS_PATH="$CONDA_PREFIX/lib/dri"`
102+
- `export QT_OPENGL=software`
103+
- `export MESA_LOADER_DRIVER_OVERRIDE=llvmpipe`
104+
- If on Wayland: `export QT_QPA_PLATFORM=xcb`
105+
4. Launch again via Python or `python -m svv.visualize.gui`.
106+
107+
Opt out and try system GL:
108+
- `export SVV_GUI_GL_MODE=system` # disables Linux-specific software GL setup in the GUI
109+
110+
These settings help avoid mixing system Mesa with conda libraries, which can cause crashes.
111+
112+
## GUI Components
113+
114+
### Main Window
115+
116+
The main window is split into two panels:
117+
118+
1. **Left Panel (70%)**: 3D visualization using PyVista
119+
2. **Right Panel (30%)**: Control widgets
120+
121+
### Control Widgets
122+
123+
#### Point Selector
124+
125+
- **Networks**: Set the number of vascular networks
126+
- **Network/Tree Selection**: Choose which network and tree to add points to
127+
- **Pick Point**: Click on the domain surface to add start points
128+
- **Manual Input**: Enter point coordinates manually
129+
- **Direction Control**: Optionally specify custom growth directions
130+
- **Point List**: View and manage all added points
131+
132+
#### Parameter Panel
133+
134+
- **Generation Mode**: Choose between single tree or forest (multiple trees)
135+
- **Tree Parameters**:
136+
- Number of vessels to generate
137+
- Physical clearance between vessels
138+
- Convexity tolerance
139+
- **Forest Parameters** (when in forest mode):
140+
- Competition between trees
141+
- Decay probability
142+
- **Advanced Parameters**:
143+
- Random seed for reproducibility
144+
145+
### Menu Bar
146+
147+
- **File**:
148+
- Load Domain: Load a .dmn domain file
149+
- Save Configuration: Export start points and parameters to JSON
150+
- Exit: Close the application
151+
- **View**:
152+
- Reset Camera: Reset the 3D view
153+
- Toggle Domain Visibility: Show/hide the domain mesh
154+
- **Help**:
155+
- About: Show information about the application
156+
157+
## Workflow
158+
159+
1. **Load or Create a Domain**:
160+
- Use `File -> Load Domain` to load a .dmn file, or
161+
- Pass a domain object when creating the GUI
162+
163+
2. **Configure Start Points**:
164+
- Select the network and tree index
165+
- Click "Pick Point" and click on the domain surface, or
166+
- Use "Manual Input" to enter coordinates
167+
- Optionally check "Use Custom Direction" and specify a direction vector
168+
169+
3. **Set Parameters**:
170+
- Choose generation mode (Tree or Forest)
171+
- Set number of vessels
172+
- Adjust physical clearance and other parameters
173+
174+
4. **Generate**:
175+
- Click "Generate Tree/Forest" to start generation
176+
- View progress in the dialog
177+
- See results in the 3D viewport
178+
179+
5. **Export** (Optional):
180+
- Use "File -> Save Configuration" to save your setup
181+
182+
## API Reference
183+
184+
### VascularizeGUI
185+
186+
Main GUI window class.
187+
188+
**Constructor:**
189+
```python
190+
VascularizeGUI(domain=None)
191+
```
192+
193+
**Parameters:**
194+
- `domain` (svv.domain.Domain, optional): Initial domain to visualize
195+
196+
**Methods:**
197+
- `load_domain(domain)`: Load a domain object
198+
- `update_status(message)`: Update the status bar
199+
200+
### VTKWidget
201+
202+
3D visualization widget.
203+
204+
**Methods:**
205+
- `set_domain(domain)`: Set and visualize the domain
206+
- `add_start_point(point, index, color)`: Add a point marker
207+
- `add_direction(point, direction, length, color)`: Add a direction arrow
208+
- `add_tree(tree, color)`: Visualize a tree
209+
- `clear()`: Clear all visualizations except domain
210+
- `reset_camera()`: Reset camera view
211+
212+
### PointSelectorWidget
213+
214+
Widget for managing start points and directions.
215+
216+
**Methods:**
217+
- `set_domain(domain)`: Set the domain
218+
- `get_configuration()`: Get current configuration as dictionary
219+
220+
### ParameterPanel
221+
222+
Widget for setting tree/forest parameters.
223+
224+
**Methods:**
225+
- `get_parameters()`: Get current parameter values
226+
227+
## Notes
228+
229+
- The GUI uses PyVista's Qt integration for 3D rendering
230+
- Point picking is done by clicking directly on the domain surface
231+
- Directions are automatically normalized when using the "Normalize Direction" button
232+
- Generated trees/forests are stored in `gui.trees` or `gui.forest` for programmatic access
233+
234+
## Troubleshooting
235+
236+
### ImportError: No module named 'PySide6'
237+
238+
Install the required dependencies:
239+
```bash
240+
pip install PySide6 pyvista PyVistaQt
241+
```
242+
243+
### GUI not responding during generation
244+
245+
Large tree/forest generation may take time. A progress dialog is shown during generation. For very large generations (>10000 vessels), consider using the non-GUI API.
246+
247+
### Point picking not working
248+
249+
Ensure the domain has a valid boundary mesh. The domain must be created, solved, and built before point picking will work properly.

svv/visualize/gui/__init__.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import os
2+
import sys
3+
4+
# CRITICAL: Set GL environment BEFORE any Qt/VTK imports
5+
# This must happen at module import time
6+
if sys.platform.startswith('linux'):
7+
gl_mode = os.environ.get('SVV_GUI_GL_MODE', 'software')
8+
if gl_mode != 'system':
9+
os.environ.setdefault('LIBGL_ALWAYS_SOFTWARE', '1')
10+
os.environ.setdefault('GALLIUM_DRIVER', 'llvmpipe')
11+
os.environ.setdefault('MESA_GL_VERSION_OVERRIDE', '3.3')
12+
os.environ.setdefault('MESA_LOADER_DRIVER_OVERRIDE', 'llvmpipe')
13+
os.environ.setdefault('QT_OPENGL', 'software')
14+
15+
# Set DRI drivers path
16+
override_dri = os.environ.get('SVV_LIBGL_DRIVERS_PATH')
17+
if override_dri and os.path.isdir(override_dri):
18+
os.environ.setdefault('LIBGL_DRIVERS_PATH', override_dri)
19+
else:
20+
conda_prefix = os.environ.get('CONDA_PREFIX', '')
21+
if conda_prefix:
22+
# Try environment-specific paths first
23+
candidates = [
24+
os.path.join(conda_prefix, 'lib', 'dri'),
25+
os.path.join(conda_prefix, 'x86_64-conda-linux-gnu', 'sysroot', 'usr', 'lib64', 'dri'),
26+
]
27+
28+
# Also check base conda path (for cos7 packages installed at root)
29+
# Extract base path from environment path
30+
if '/envs/' in conda_prefix:
31+
base_prefix = conda_prefix.split('/envs/')[0]
32+
candidates.append(os.path.join(base_prefix, 'x86_64-conda-linux-gnu', 'sysroot', 'usr', 'lib64', 'dri'))
33+
34+
for dri_path in candidates:
35+
if os.path.isdir(dri_path):
36+
os.environ.setdefault('LIBGL_DRIVERS_PATH', dri_path)
37+
break
38+
39+
if 'WAYLAND_DISPLAY' in os.environ:
40+
os.environ.setdefault('QT_QPA_PLATFORM', 'xcb')
41+
42+
from svv.visualize.gui.main_window import VascularizeGUI
43+
44+
45+
def launch_gui(domain=None, block=True):
46+
"""
47+
Launch the Vascularize GUI from a Python interpreter or script.
48+
49+
Parameters
50+
----------
51+
domain : optional
52+
Optional domain object to preload into the GUI.
53+
block : bool
54+
If True, start the Qt event loop and block until exit.
55+
If False, return ``(app, gui)`` without starting the loop.
56+
57+
Returns
58+
-------
59+
tuple | None
60+
``(app, gui)`` when ``block=False``, otherwise ``None``.
61+
"""
62+
import sys
63+
from PySide6.QtWidgets import QApplication
64+
from PySide6.QtCore import Qt
65+
66+
# On Linux, prefer Qt's software OpenGL to avoid GPU/driver issues
67+
# unless explicitly opting into system GL.
68+
if sys.platform.startswith('linux') and os.environ.get('SVV_GUI_GL_MODE', 'software') != 'system':
69+
try:
70+
QApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)
71+
except Exception:
72+
pass
73+
74+
app = QApplication.instance() or QApplication(sys.argv)
75+
gui = VascularizeGUI(domain=domain)
76+
gui.show()
77+
78+
if not block:
79+
return app, gui
80+
81+
try:
82+
app.exec()
83+
except AttributeError:
84+
app.exec_()
85+
return None
86+
87+
88+
__all__ = ['VascularizeGUI', 'launch_gui']

0 commit comments

Comments
 (0)