Skip to content

Commit b316f7e

Browse files
ayenpurejourdain
authored andcommitted
docs(paraview): adding tauri bundling example for ParaView app
Adding example for bundling an app that uses ParaView. To bundle paraview properly it's important to execute PyInstaller with the option `--collect-all` for ParaView. `os.write` for writing out information tauri requires. Also a custom call RemotingCore to force offscreen rendering.
1 parent f1002c7 commit b316f7e

File tree

12 files changed

+385
-0
lines changed

12 files changed

+385
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Tauri + WebSocket
2+
3+
This example leverages tauri for just its WebView and lets trame act as the full HTTP server by serving its content over HTTP and WebSocket. Additionally, this example also demonstrates using ParaView for rendering and computation.
4+
5+
## Tauri project
6+
7+
Since Tauri is written in Rust, let's get setup with its dev environment.
8+
9+
```bash
10+
# Install rust
11+
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
12+
13+
# Enable rust within shell
14+
. "$HOME/.cargo/env"
15+
16+
# Install tauri-cli
17+
cargo install tauri-cli
18+
```
19+
20+
Then, the infrastructure (./src-tauri) was generated by running the following commands.
21+
22+
__Caution__: No need to redo it since we committed the generated structure to the repo.
23+
24+
```bash
25+
$ cargo tauri init
26+
27+
✔ What is your app name? · Cone
28+
✔ What should the window title be? · Cone
29+
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ./www
30+
✔ What is the url of your dev server? · ./www
31+
✔ What is your frontend dev command? ·
32+
✔ What is your frontend build command? ·
33+
```
34+
35+
From the default content, we edited the following set of files:
36+
37+
- `./src-tauri/src/main.rs`: Generic main application designed for trame which can remain the same regardless of your application.
38+
- `./src-tauri/sidecar/*`: OS specific launcher for a trame server. The mac and linux version are just plain bash scripts while the Windows one is a simple compiled C++ application that forward the args to the actual trame python executable generated by PyInstaller. Those can be re-used as-is for any trame app. A departure from the other examples, this app uses `pvpython` to launch the app instead of the packaged binary.
39+
- `./src-tauri/www/*`: Basic content for splashscreen and temporary main window content before we apply a redirect. Those are static and could be adjusted by adding your own image as splashscreen.
40+
- `./src-tauri/icons`: Remove default ones (`rm -rf ./src-tauri/icons`)
41+
- `./src-tauri/Cargo.toml`: Added new dependencies and required feature.
42+
- `./src-tauri/tauri.conf.json`: Edited the following sections
43+
- tauri > allowlist: + shell/sidecar
44+
- tauri > bundle > externalBin : ["sidecar/trame"]
45+
- tauri > bundle > identifier : "trame.cone"
46+
- tauri > bundle > resources : ["server"] # pyinstaller generated app
47+
- tauri > bundle > targets : ["appimage", "nsis", "msi", "app", "dmg"]
48+
- tauri > security > csp : "default-src 'self' 'unsafe-inline' ws: localhost; script-src 'unsafe-eval' 'self';"
49+
- tauri > windows : main-not-visible + splashscreen
50+
51+
## Copy application to container
52+
53+
Since the app uses `pvpython` to launch the application, the python executables need to be copied over to the server directory for it to be bundled
54+
55+
```bash
56+
cp cone.py src-tauri/server
57+
```
58+
59+
## Trame example
60+
61+
We use a simple cone example since it does not have any complex python dependency. Since we're bundling a `ParaView` based application, and `ParaView` not being `pip` installable, this example uses `conda`
62+
63+
64+
```bash
65+
conda create --name tauri -c conda-forge python=3.13
66+
conda activate tauri
67+
pip install trame trame-vtk trame-vuetify pyinstaller
68+
conda install paraview=5.13.3
69+
```
70+
71+
Build bundle for tauri inside `./src-tauri/server/*` while skipping the web content.
72+
73+
```bash
74+
python -m PyInstaller
75+
--clean --noconfirm \
76+
--distpath src-tauri \
77+
--name server --hidden-import pkgutil \
78+
--collect-all paraview
79+
cone.py
80+
```
81+
82+
Generate webcontent for tauri to bundle
83+
84+
```bash
85+
python -m trame.tools.www --output ./src-tauri/www
86+
```
87+
88+
## Tauri bundle
89+
90+
In order to build and bundle the application, just run
91+
92+
```bash
93+
# Generate icon for application using ./app-icon.png
94+
cargo tauri icon
95+
96+
# Generate application
97+
cargo tauri build
98+
```
99+
100+
## Running application
101+
102+
### Linux and macOS
103+
```bash
104+
open ./src-tauri/target/release/bundle/macos/Cone.app
105+
```
213 KB
Loading
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import os
2+
from trame.app import get_server
3+
from trame.ui.vuetify3 import SinglePageLayout
4+
from trame.widgets import vuetify3 as vuetify, paraview
5+
from trame.decorators import TrameApp, change, life_cycle
6+
7+
from paraview import simple
8+
9+
# Configure ParaView for headless operation when using standard Python interpreter
10+
# (pvpython sets this automatically, but regular python requires manual configuration)
11+
from paraview.modules import vtkRemotingCore as rc
12+
13+
rc.vtkProcessModule.GetProcessModule().UpdateProcessType(
14+
rc.vtkProcessModule.PROCESS_BATCH, 0
15+
)
16+
17+
18+
@TrameApp()
19+
class Cone:
20+
"""
21+
This application uses the ParaView simple API to create a cone
22+
and display it in a web application using Tauri. This application
23+
uses remote rendering.
24+
"""
25+
26+
def __init__(self, server=None):
27+
self.server = get_server(server)
28+
self.state = self.server.state
29+
self.ctrl = self.server.controller
30+
31+
cone = simple.Cone()
32+
representation = simple.Show(cone)
33+
view = simple.Render()
34+
35+
self.cone = cone
36+
self.representation = representation
37+
self.view = view
38+
self.resolution = 6
39+
40+
self.ui = self._build_ui()
41+
42+
@change("resolution")
43+
def update_cone(self, resolution, **kwargs):
44+
self.cone.Resolution = resolution
45+
self.ctrl.view_update()
46+
47+
def _build_ui(self):
48+
with SinglePageLayout(self.server) as layout:
49+
with layout.content:
50+
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
51+
html_view = paraview.VtkRemoteView(self.view, ref="view")
52+
self.ctrl.view_reset_camera = html_view.reset_camera
53+
self.ctrl.view_update = html_view.update
54+
55+
with layout.toolbar:
56+
vuetify.VSpacer()
57+
vuetify.VSlider(
58+
v_model=("resolution", 6),
59+
min=3,
60+
max=60,
61+
step=1,
62+
hide_details=True,
63+
dense="compact",
64+
style="max-width: 300px;",
65+
)
66+
with vuetify.VBtn(icon=True, click=self.server.controller.reset_camera):
67+
vuetify.VIcon("mdi-crop-free")
68+
69+
return layout
70+
71+
# Use os.write for immediate unbuffered output to ensure Tauri
72+
# receives the port number before Python's stdout buffer flushes
73+
@life_cycle.server_ready
74+
def _tauri_ready(self, **_):
75+
os.write(1, f"tauri-server-port={self.server.port}\n".encode())
76+
77+
@life_cycle.client_connected
78+
def _tauri_show(self, **_):
79+
os.write(1, "tauri-client-ready\n".encode())
80+
81+
82+
if __name__ == "__main__":
83+
app = Cone()
84+
app.server.start()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
set -e
4+
set -x
5+
6+
python -m PyInstaller \
7+
--clean --noconfirm \
8+
--distpath src-tauri \
9+
--name server --hidden-import pkgutil \
10+
--collect-all paraview
11+
cone.py
12+
13+
python -m trame.tools.www --output ./src-tauri/www
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Generated by Cargo
2+
# will have compiled files and executables
3+
/target/
4+
icons
5+
server
6+
Cargo.lock
7+
www
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "app"
3+
version = "0.1.0"
4+
description = "A Tauri App"
5+
authors = ["Kitware"]
6+
license = ""
7+
repository = ""
8+
default-run = "app"
9+
edition = "2021"
10+
rust-version = "1.60"
11+
12+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13+
14+
[build-dependencies]
15+
tauri-build = { version = "1.5.1", features = [] }
16+
17+
[dependencies]
18+
serde_json = "1.0"
19+
serde = { version = "1.0", features = ["derive"] }
20+
tauri = { version = "1.6.1", features = ["shell-sidecar"] } # Added sidecar
21+
async-std = "1.12.0" # Added for sleep
22+
23+
[features]
24+
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
25+
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
26+
# DO NOT REMOVE!!
27+
custom-protocol = [ "tauri/custom-protocol" ]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
tauri_build::build()
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
rootdir=`dirname $0`
3+
rootdir=`cd $rootdir && cd .. && pwd`
4+
5+
$rootdir/Resources/server/server $@
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
rootdir=`dirname $0`
3+
rootdir=`cd $rootdir && cd .. && pwd`
4+
5+
$rootdir/Resources/server/server $@
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
rootdir=`dirname $0`
3+
rootdir=`cd $rootdir && cd ../lib/cone && pwd`
4+
5+
$rootdir/server/server $@

0 commit comments

Comments
 (0)