pyvista-quicklook 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. pyvista_quicklook-0.2.0/.github/workflows/ci.yml +133 -0
  2. pyvista_quicklook-0.2.0/.gitignore +10 -0
  3. pyvista_quicklook-0.2.0/.pre-commit-config.yaml +44 -0
  4. pyvista_quicklook-0.2.0/LICENSE +21 -0
  5. pyvista_quicklook-0.2.0/PKG-INFO +231 -0
  6. pyvista_quicklook-0.2.0/README.md +206 -0
  7. pyvista_quicklook-0.2.0/macos/App/main.swift +121 -0
  8. pyvista_quicklook-0.2.0/macos/QuickLookExtension/PreviewViewController.swift +108 -0
  9. pyvista_quicklook-0.2.0/macos/QuickLookExtension/QuickLook.entitlements +11 -0
  10. pyvista_quicklook-0.2.0/macos/Shared/Camera.swift +26 -0
  11. pyvista_quicklook-0.2.0/macos/Shared/Helper.swift +258 -0
  12. pyvista_quicklook-0.2.0/macos/Tools/RenderScene.swift +76 -0
  13. pyvista_quicklook-0.2.0/pyproject.toml +116 -0
  14. pyvista_quicklook-0.2.0/pyvista_quicklook/__init__.py +8 -0
  15. pyvista_quicklook-0.2.0/pyvista_quicklook/__main__.py +10 -0
  16. pyvista_quicklook-0.2.0/pyvista_quicklook/_scene_export.py +180 -0
  17. pyvista_quicklook-0.2.0/pyvista_quicklook/_version.py +24 -0
  18. pyvista_quicklook-0.2.0/pyvista_quicklook/_warmup.py +46 -0
  19. pyvista_quicklook-0.2.0/pyvista_quicklook/cli.py +357 -0
  20. pyvista_quicklook-0.2.0/pyvista_quicklook/config.py +118 -0
  21. pyvista_quicklook-0.2.0/pyvista_quicklook/convert.py +105 -0
  22. pyvista_quicklook-0.2.0/pyvista_quicklook/daemon.py +240 -0
  23. pyvista_quicklook-0.2.0/pyvista_quicklook/formats.py +123 -0
  24. pyvista_quicklook-0.2.0/pyvista_quicklook/plist.py +108 -0
  25. pyvista_quicklook-0.2.0/pyvista_quicklook/render.py +175 -0
  26. pyvista_quicklook-0.2.0/pyvista_quicklook/warmup.py +72 -0
  27. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/PKG-INFO +231 -0
  28. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/SOURCES.txt +40 -0
  29. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/dependency_links.txt +1 -0
  30. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/entry_points.txt +2 -0
  31. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/scm_file_list.json +36 -0
  32. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/scm_version.json +8 -0
  33. pyvista_quicklook-0.2.0/pyvista_quicklook.egg-info/top_level.txt +1 -0
  34. pyvista_quicklook-0.2.0/scripts/bootstrap.sh +57 -0
  35. pyvista_quicklook-0.2.0/scripts/build.sh +90 -0
  36. pyvista_quicklook-0.2.0/scripts/install.sh +128 -0
  37. pyvista_quicklook-0.2.0/scripts/uninstall.sh +29 -0
  38. pyvista_quicklook-0.2.0/setup.cfg +4 -0
  39. pyvista_quicklook-0.2.0/tests/test_pyvista_quicklook.py +528 -0
  40. pyvista_quicklook-0.2.0/tests/test_render.py +116 -0
  41. pyvista_quicklook-0.2.0/tests/test_scene_export.py +170 -0
  42. pyvista_quicklook-0.2.0/uv.lock +1804 -0
@@ -0,0 +1,133 @@
1
+ name: CI
2
+
3
+ # `pull_request` is deliberately unfiltered: filtering it by base branch would
4
+ # skip every pull request in a stack that does not target main.
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ tags: [v*]
9
+ pull_request:
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ pre-commit:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
+ with:
24
+ persist-credentials: false
25
+ fetch-depth: 0
26
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
27
+ with:
28
+ enable-cache: false
29
+ - run: uv sync --group dev
30
+ - run: uv run pre-commit run --all-files --show-diff-on-failure
31
+ env:
32
+ # This hook exists to stop *local* commits straight to main; it
33
+ # would always fail here since CI checks out that branch/tag directly.
34
+ SKIP: no-commit-to-branch
35
+
36
+ test:
37
+ # The helper is pure standard library, so it is tested away from macOS too.
38
+ runs-on: ubuntu-latest
39
+ strategy:
40
+ fail-fast: false
41
+ matrix:
42
+ python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
43
+ steps:
44
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
45
+ with:
46
+ persist-credentials: false
47
+ fetch-depth: 0
48
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
49
+ with:
50
+ enable-cache: false
51
+ - run: uv sync --group dev --python ${{ matrix.python-version }}
52
+ - run: uv run pytest tests/
53
+
54
+ app:
55
+ # Builds the real thing: compiles both Swift targets, generates the
56
+ # Info.plist files, and ad-hoc signs the bundle.
57
+ runs-on: macos-latest
58
+ steps:
59
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
60
+ with:
61
+ persist-credentials: false
62
+ fetch-depth: 0
63
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
64
+ with:
65
+ enable-cache: false
66
+ - run: uv sync --group dev
67
+ - run: ./scripts/build.sh --universal
68
+ - name: Check the bundle is complete and signed
69
+ run: |
70
+ app=build/PyVistaQuickLook.app
71
+ appex="$app/Contents/PlugIns/PyVistaQuickLookExtension.appex"
72
+ test -x "$app/Contents/MacOS/PyVistaQuickLook"
73
+ test -x "$appex/Contents/MacOS/PyVistaQuickLookExtension"
74
+ codesign --verify --deep --strict "$app"
75
+ # A preview extension is only registered by macOS when it is sandboxed.
76
+ codesign -d --entitlements - "$appex" 2>&1 | grep -q app-sandbox
77
+ # Both architectures, so the download runs on any Mac.
78
+ lipo -archs "$appex/Contents/MacOS/PyVistaQuickLookExtension" | grep -q x86_64
79
+ lipo -archs "$appex/Contents/MacOS/PyVistaQuickLookExtension" | grep -q arm64
80
+ - run: uv run pytest tests/
81
+ - name: Package the app
82
+ run: ditto -c -k --keepParent build/PyVistaQuickLook.app build/PyVistaQuickLook.zip
83
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
84
+ with:
85
+ name: PyVistaQuickLook.zip
86
+ path: build/PyVistaQuickLook.zip
87
+
88
+ build:
89
+ needs: [pre-commit, test, app]
90
+ runs-on: ubuntu-latest
91
+ steps:
92
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
93
+ with:
94
+ persist-credentials: false
95
+ fetch-depth: 0
96
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
97
+ with:
98
+ enable-cache: false
99
+ - run: uv build
100
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
101
+ with:
102
+ name: dist
103
+ path: dist/
104
+
105
+ publish:
106
+ needs: build
107
+ if: startsWith(github.ref, 'refs/tags/v')
108
+ runs-on: ubuntu-latest
109
+ environment: release
110
+ permissions:
111
+ id-token: write
112
+ steps:
113
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
114
+ with:
115
+ name: dist
116
+ path: dist/
117
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
118
+
119
+ release:
120
+ # Attaches the built app to the tag's release, so installing needs no compiler.
121
+ needs: [app, build]
122
+ if: startsWith(github.ref, 'refs/tags/v')
123
+ runs-on: ubuntu-latest
124
+ permissions:
125
+ contents: write
126
+ steps:
127
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
128
+ with:
129
+ name: PyVistaQuickLook.zip
130
+ - env:
131
+ GH_TOKEN: ${{ github.token }}
132
+ TAG: ${{ github.ref_name }}
133
+ run: gh release upload "$TAG" PyVistaQuickLook.zip --clobber --repo "$GITHUB_REPOSITORY"
@@ -0,0 +1,10 @@
1
+ build/
2
+ dist/
3
+ *.egg-info/
4
+ __pycache__/
5
+ .venv/
6
+ .pytest_cache/
7
+ pyvista_quicklook/_version.py
8
+ .coverage
9
+ .coverage.*
10
+ .idea/
@@ -0,0 +1,44 @@
1
+ ci:
2
+ autoupdate_commit_msg: 'chore: update pre-commit hooks'
3
+ autofix_prs: true
4
+ autoupdate_schedule: quarterly
5
+
6
+ repos:
7
+ - repo: https://github.com/pre-commit/pre-commit-hooks
8
+ rev: v5.0.0
9
+ hooks:
10
+ - id: check-merge-conflict
11
+ - id: debug-statements
12
+ - id: no-commit-to-branch
13
+ args: [--branch, main]
14
+
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ rev: v0.16.0
17
+ hooks:
18
+ - id: ruff-check
19
+ args: [--fix, --show-fixes]
20
+ - id: ruff-format
21
+
22
+ - repo: https://github.com/zizmorcore/zizmor-pre-commit
23
+ rev: v1.11.0
24
+ hooks:
25
+ - id: zizmor
26
+
27
+ - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
28
+ rev: v2.15.0
29
+ hooks:
30
+ - id: pretty-format-yaml
31
+ args: [--autofix, --indent, '2']
32
+
33
+ - repo: https://github.com/ComPWA/taplo-pre-commit
34
+ rev: v0.9.3
35
+ hooks:
36
+ - id: taplo-format
37
+ # See options: https://taplo.tamasfe.dev/configuration/formatter-options.html
38
+ args: [--option, reorder_arrays=true, --option, reorder_keys=true, --option, align_comments=false]
39
+
40
+ # The app is built and installed by shell scripts, so they are linted too.
41
+ - repo: https://github.com/shellcheck-py/shellcheck-py
42
+ rev: v0.11.0.1
43
+ hooks:
44
+ - id: shellcheck
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2026 Erik Bedard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to
7
+ deal in the Software without restriction, including without limitation the
8
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ sell copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyvista-quicklook
3
+ Version: 0.2.0
4
+ Summary: Quick Look previews for PyVista-readable mesh files.
5
+ Author: Erik Bedard
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/user27182/pyvista-quicklook
8
+ Keywords: macos,mesh,preview,pyvista,quicklook,vtk
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: MacOS X
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Scientific/Engineering :: Visualization
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # PyVista Quick Look
27
+
28
+ Press the space bar on a mesh file in the Finder and turn it with the mouse, the same
29
+ way macOS already previews `.ply` and `.usdz` models.
30
+
31
+ Selecting `flow.vtu` and pressing space shows its surface in the Quick Look panel,
32
+ coloured by the active scalars and free to rotate and zoom.
33
+
34
+ ## Requirements
35
+
36
+ - macOS 12 or newer
37
+
38
+ Neither PyVista nor a compiler needs to be installed. The installer downloads a
39
+ published build of the app and provisions its own PyVista; Xcode command line tools
40
+ (`xcode-select --install`) are only needed to build from source.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ ./scripts/install.sh
46
+ ```
47
+
48
+ Then select a `.vtu`, `.vtp`, or `.vtk` file in the Finder and press space.
49
+
50
+ The installer fetches [uv](https://docs.astral.sh/uv/) if it is missing, creates a
51
+ private environment holding PyVista, installs the `pvql` helper, builds and registers
52
+ `PyVistaQuickLook.app`, and loads PyVista once so the first preview is quick. Nothing
53
+ outside `~/Library/Application Support/PyVistaQuickLook`, `~/Applications`, and
54
+ `~/.local/bin` is touched, and no existing Python environment is used or changed.
55
+
56
+ ```bash
57
+ ./scripts/install.sh --prefix /Applications # install for all users
58
+ ./scripts/install.sh --pyvista /path/to/venv/bin/pyvista # use an existing PyVista
59
+ ./scripts/install.sh --app /path/to/PyVistaQuickLook.app # skip the build
60
+ ```
61
+
62
+ Check the result with `pvql doctor`.
63
+
64
+ `scripts/bootstrap.sh` installs the same way from a URL, once this repository is
65
+ published:
66
+
67
+ ```bash
68
+ curl -LsSf https://raw.githubusercontent.com/user27182/pyvista-quicklook/main/scripts/bootstrap.sh | sh
69
+ ```
70
+
71
+ ### What gets installed
72
+
73
+ About 270 MB, in `~/Library/Application Support/PyVistaQuickLook/venv`:
74
+
75
+ - PyVista, from git until 0.49 is released, installed with `--no-deps`
76
+ - [cvista](https://github.com/pyvista/cvista)`[all]`, a VTK fork, in place of stock VTK
77
+ - cyclopts, matplotlib, numpy, pillow, pooch, pyobjc-framework-Cocoa, scooby,
78
+ typing-extensions
79
+
80
+ Installing PyVista with `--no-deps` and swapping stock VTK for cvista is what keeps this
81
+ down; stock VTK alone is 591 MB against cvista's 136 MB.
82
+
83
+ Point `--pyvista` at an environment you already have to use that instead.
84
+
85
+ ## Supported files
86
+
87
+ 36 extensions are claimed by default, including `.vtk`, `.vti`, `.vtp`, `.vtu`, `.vtm`,
88
+ `.vtkhdf`, `.pvd`, `.case`, `.exo`, `.foam`, `.cgns`, `.segy`, and `.xdmf`.
89
+
90
+ ```bash
91
+ pvql types # what is claimed now
92
+ pvql types --all # every format pvql knows about
93
+ ```
94
+
95
+ Formats macOS already previews — `.stl`, `.obj`, `.ply`, `.png` — are deliberately not
96
+ claimed, so the built-in viewer keeps handling them. Claim them by adding them to the
97
+ config and reinstalling:
98
+
99
+ ```json
100
+ { "extensions": { "add": [".stl", ".obj"], "remove": [".pdb"] } }
101
+ ```
102
+
103
+ ## How it works
104
+
105
+ The app bundle contains a Quick Look extension that declares a uniform type identifier
106
+ for each claimed extension. When the Finder previews one of those files, the extension
107
+ hands it to a background render service, which reads it with PyVista, extracts the
108
+ surface, colours the vertices by the active scalars, and writes a PLY. The extension
109
+ shows that PLY in a SceneKit view, which is what makes the preview turnable.
110
+
111
+ Datasets with no surface to show fall back to a still image rendered by
112
+ `pyvista plot --off-screen --screenshot`, which keeps the scalar bar and axes that the
113
+ interactive view leaves out. Setting `"interactive": false` in the config always uses
114
+ that still image.
115
+
116
+ Both are cached under `~/Library/Caches/PyVistaQuickLook`, keyed by the file's path,
117
+ size, and modification time, so editing a file invalidates its preview. Later previews
118
+ of the same file come from the cache.
119
+
120
+ Every preview runs PyVista in a fresh process, and VTK is 600 MB of libraries, so the
121
+ first one after a restart is slow until macOS has those pages cached. The service loads
122
+ them in the background when it starts, which is at login and whenever it is reinstalled,
123
+ so that cost is paid before you press space. `pvql warmup` does the same on demand, and
124
+ `"warm_on_start": false` turns the automatic pass off.
125
+
126
+ Cell data is sampled onto the points before colouring. Surfaces are sent whole:
127
+ decimating them costs more time than the larger file does, so `max_scene_points` is a
128
+ safety valve for very large meshes rather than a routine step. Set it to `0` to never
129
+ decimate.
130
+
131
+ When a preview fails, the Quick Look panel shows the error text instead.
132
+
133
+ ### The render service
134
+
135
+ macOS runs Quick Look extensions in a sandbox that VTK cannot render inside, so
136
+ rendering happens in a launch agent instead. `scripts/install.sh` sets it up.
137
+
138
+ ```bash
139
+ pvql service # report whether it is loaded
140
+ pvql service --install # (re)install and start it
141
+ pvql service --uninstall
142
+ ```
143
+
144
+ Its output goes to `~/Library/Logs/pvqld.log`.
145
+
146
+ ### Files in the Desktop, Documents, and Downloads folders
147
+
148
+ macOS keeps those folders private to each program, and the render service cannot read
149
+ them. The Quick Look extension copies the file it was asked to preview into its own
150
+ container so that the service can render it anyway.
151
+
152
+ A dataset that points at neighbouring files — `.pvd`, `.vtm`, `.case`, `.foam` — needs
153
+ those neighbours, which the copy does not include. Keep such datasets outside those
154
+ three folders, or grant the render service Full Disk Access in System Settings under
155
+ Privacy & Security.
156
+
157
+ ## Configuration
158
+
159
+ `~/Library/Application Support/PyVistaQuickLook/config.json`
160
+
161
+ | Key | Default | Effect |
162
+ | --- | --- | --- |
163
+ | `python` | set at install | Interpreter of the PyVista environment |
164
+ | `pyvista` | unset | Optional `pyvista` executable, for still images |
165
+ | `pvql` | discovered | Absolute path to the `pvql` helper |
166
+ | `interactive` | `true` | Show a turnable surface instead of a still image |
167
+ | `max_scene_points` | `2000000` | Decimate only above this many points; `0` never does |
168
+ | `colormap` | `'viridis'` | Colormap used to colour the surface |
169
+ | `warm_on_start` | `true` | Load PyVista and VTK when the render service starts |
170
+ | `window_size` | `[1024, 1024]` | Rendered preview size in pixels |
171
+ | `timeout` | `60` | Seconds before a render is abandoned |
172
+ | `max_file_size_mb` | `512` | Files above this size show a notice instead of a render |
173
+ | `background` | `null` | Background color passed to `pyvista plot` |
174
+ | `extra_args` | `[]` | Extra arguments appended to `pyvista plot` |
175
+ | `cache` | `true` | Reuse previously rendered previews |
176
+ | `log` | `false` | Append render activity to `pvql.log` beside the config file |
177
+
178
+ Changing `extensions` requires a reinstall, because the claimed types are baked into the
179
+ app bundle. Every other key takes effect on the next preview.
180
+
181
+ ## Commands
182
+
183
+ ```bash
184
+ pvql preview FILE # render and print the path of the cached PNG
185
+ pvql warmup # load PyVista and VTK ahead of the first preview
186
+ pvql warm DIR # render a directory ahead of time
187
+ pvql types # list claimed extensions
188
+ pvql doctor # check every part of the integration
189
+ pvql service # manage the render service
190
+ pvql config --init # write a config file with discovered defaults
191
+ pvql cache --clear # delete cached previews
192
+ ```
193
+
194
+ ## Troubleshooting
195
+
196
+ Run `pvql doctor` first; it checks the helper, the app, the extension registration, the
197
+ service, and a real render.
198
+
199
+ - **The panel says the service is not answering.** Run `pvql service --install`.
200
+ - **Nothing happens on space bar.** Confirm the type is claimed with `pvql types`, then
201
+ check that Finder resolves it: `mdls -name kMDItemContentType yourfile.vtu` should
202
+ report an `io.github.user27182.pyvista-quicklook.*` type.
203
+ - **Previews are stale.** `pvql cache --clear`.
204
+ - **A render fails.** Set `"log": true` in the config; activity is appended to
205
+ `pvql.log` beside it. The extension's own log is in
206
+ `~/Library/Containers/io.github.user27182.PyVistaQuickLook.QuickLook/Data/tmp/`.
207
+
208
+ ## Development
209
+
210
+ ```bash
211
+ uv sync --group dev
212
+ uv run pytest tests/ # helper tests, with coverage
213
+ uv run pre-commit run --all-files
214
+ ./scripts/build.sh # compile and sign the app bundle
215
+ ```
216
+
217
+ `main` is protected by a pre-commit hook, so work on a branch.
218
+
219
+ Releases are published to PyPI by CI through trusted publishing: push a `v*` tag and
220
+ the `publish` job uploads from the `release` environment. That requires a matching
221
+ pending publisher configured on PyPI for this repository and workflow.
222
+
223
+ ## Uninstall
224
+
225
+ ```bash
226
+ ./scripts/uninstall.sh
227
+ uv tool uninstall pyvista-quicklook
228
+ ```
229
+
230
+ That removes the app, the private PyVista environment, the downloaded source, and the
231
+ cache, leaving only the config file.
@@ -0,0 +1,206 @@
1
+ # PyVista Quick Look
2
+
3
+ Press the space bar on a mesh file in the Finder and turn it with the mouse, the same
4
+ way macOS already previews `.ply` and `.usdz` models.
5
+
6
+ Selecting `flow.vtu` and pressing space shows its surface in the Quick Look panel,
7
+ coloured by the active scalars and free to rotate and zoom.
8
+
9
+ ## Requirements
10
+
11
+ - macOS 12 or newer
12
+
13
+ Neither PyVista nor a compiler needs to be installed. The installer downloads a
14
+ published build of the app and provisions its own PyVista; Xcode command line tools
15
+ (`xcode-select --install`) are only needed to build from source.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ ./scripts/install.sh
21
+ ```
22
+
23
+ Then select a `.vtu`, `.vtp`, or `.vtk` file in the Finder and press space.
24
+
25
+ The installer fetches [uv](https://docs.astral.sh/uv/) if it is missing, creates a
26
+ private environment holding PyVista, installs the `pvql` helper, builds and registers
27
+ `PyVistaQuickLook.app`, and loads PyVista once so the first preview is quick. Nothing
28
+ outside `~/Library/Application Support/PyVistaQuickLook`, `~/Applications`, and
29
+ `~/.local/bin` is touched, and no existing Python environment is used or changed.
30
+
31
+ ```bash
32
+ ./scripts/install.sh --prefix /Applications # install for all users
33
+ ./scripts/install.sh --pyvista /path/to/venv/bin/pyvista # use an existing PyVista
34
+ ./scripts/install.sh --app /path/to/PyVistaQuickLook.app # skip the build
35
+ ```
36
+
37
+ Check the result with `pvql doctor`.
38
+
39
+ `scripts/bootstrap.sh` installs the same way from a URL, once this repository is
40
+ published:
41
+
42
+ ```bash
43
+ curl -LsSf https://raw.githubusercontent.com/user27182/pyvista-quicklook/main/scripts/bootstrap.sh | sh
44
+ ```
45
+
46
+ ### What gets installed
47
+
48
+ About 270 MB, in `~/Library/Application Support/PyVistaQuickLook/venv`:
49
+
50
+ - PyVista, from git until 0.49 is released, installed with `--no-deps`
51
+ - [cvista](https://github.com/pyvista/cvista)`[all]`, a VTK fork, in place of stock VTK
52
+ - cyclopts, matplotlib, numpy, pillow, pooch, pyobjc-framework-Cocoa, scooby,
53
+ typing-extensions
54
+
55
+ Installing PyVista with `--no-deps` and swapping stock VTK for cvista is what keeps this
56
+ down; stock VTK alone is 591 MB against cvista's 136 MB.
57
+
58
+ Point `--pyvista` at an environment you already have to use that instead.
59
+
60
+ ## Supported files
61
+
62
+ 36 extensions are claimed by default, including `.vtk`, `.vti`, `.vtp`, `.vtu`, `.vtm`,
63
+ `.vtkhdf`, `.pvd`, `.case`, `.exo`, `.foam`, `.cgns`, `.segy`, and `.xdmf`.
64
+
65
+ ```bash
66
+ pvql types # what is claimed now
67
+ pvql types --all # every format pvql knows about
68
+ ```
69
+
70
+ Formats macOS already previews — `.stl`, `.obj`, `.ply`, `.png` — are deliberately not
71
+ claimed, so the built-in viewer keeps handling them. Claim them by adding them to the
72
+ config and reinstalling:
73
+
74
+ ```json
75
+ { "extensions": { "add": [".stl", ".obj"], "remove": [".pdb"] } }
76
+ ```
77
+
78
+ ## How it works
79
+
80
+ The app bundle contains a Quick Look extension that declares a uniform type identifier
81
+ for each claimed extension. When the Finder previews one of those files, the extension
82
+ hands it to a background render service, which reads it with PyVista, extracts the
83
+ surface, colours the vertices by the active scalars, and writes a PLY. The extension
84
+ shows that PLY in a SceneKit view, which is what makes the preview turnable.
85
+
86
+ Datasets with no surface to show fall back to a still image rendered by
87
+ `pyvista plot --off-screen --screenshot`, which keeps the scalar bar and axes that the
88
+ interactive view leaves out. Setting `"interactive": false` in the config always uses
89
+ that still image.
90
+
91
+ Both are cached under `~/Library/Caches/PyVistaQuickLook`, keyed by the file's path,
92
+ size, and modification time, so editing a file invalidates its preview. Later previews
93
+ of the same file come from the cache.
94
+
95
+ Every preview runs PyVista in a fresh process, and VTK is 600 MB of libraries, so the
96
+ first one after a restart is slow until macOS has those pages cached. The service loads
97
+ them in the background when it starts, which is at login and whenever it is reinstalled,
98
+ so that cost is paid before you press space. `pvql warmup` does the same on demand, and
99
+ `"warm_on_start": false` turns the automatic pass off.
100
+
101
+ Cell data is sampled onto the points before colouring. Surfaces are sent whole:
102
+ decimating them costs more time than the larger file does, so `max_scene_points` is a
103
+ safety valve for very large meshes rather than a routine step. Set it to `0` to never
104
+ decimate.
105
+
106
+ When a preview fails, the Quick Look panel shows the error text instead.
107
+
108
+ ### The render service
109
+
110
+ macOS runs Quick Look extensions in a sandbox that VTK cannot render inside, so
111
+ rendering happens in a launch agent instead. `scripts/install.sh` sets it up.
112
+
113
+ ```bash
114
+ pvql service # report whether it is loaded
115
+ pvql service --install # (re)install and start it
116
+ pvql service --uninstall
117
+ ```
118
+
119
+ Its output goes to `~/Library/Logs/pvqld.log`.
120
+
121
+ ### Files in the Desktop, Documents, and Downloads folders
122
+
123
+ macOS keeps those folders private to each program, and the render service cannot read
124
+ them. The Quick Look extension copies the file it was asked to preview into its own
125
+ container so that the service can render it anyway.
126
+
127
+ A dataset that points at neighbouring files — `.pvd`, `.vtm`, `.case`, `.foam` — needs
128
+ those neighbours, which the copy does not include. Keep such datasets outside those
129
+ three folders, or grant the render service Full Disk Access in System Settings under
130
+ Privacy & Security.
131
+
132
+ ## Configuration
133
+
134
+ `~/Library/Application Support/PyVistaQuickLook/config.json`
135
+
136
+ | Key | Default | Effect |
137
+ | --- | --- | --- |
138
+ | `python` | set at install | Interpreter of the PyVista environment |
139
+ | `pyvista` | unset | Optional `pyvista` executable, for still images |
140
+ | `pvql` | discovered | Absolute path to the `pvql` helper |
141
+ | `interactive` | `true` | Show a turnable surface instead of a still image |
142
+ | `max_scene_points` | `2000000` | Decimate only above this many points; `0` never does |
143
+ | `colormap` | `'viridis'` | Colormap used to colour the surface |
144
+ | `warm_on_start` | `true` | Load PyVista and VTK when the render service starts |
145
+ | `window_size` | `[1024, 1024]` | Rendered preview size in pixels |
146
+ | `timeout` | `60` | Seconds before a render is abandoned |
147
+ | `max_file_size_mb` | `512` | Files above this size show a notice instead of a render |
148
+ | `background` | `null` | Background color passed to `pyvista plot` |
149
+ | `extra_args` | `[]` | Extra arguments appended to `pyvista plot` |
150
+ | `cache` | `true` | Reuse previously rendered previews |
151
+ | `log` | `false` | Append render activity to `pvql.log` beside the config file |
152
+
153
+ Changing `extensions` requires a reinstall, because the claimed types are baked into the
154
+ app bundle. Every other key takes effect on the next preview.
155
+
156
+ ## Commands
157
+
158
+ ```bash
159
+ pvql preview FILE # render and print the path of the cached PNG
160
+ pvql warmup # load PyVista and VTK ahead of the first preview
161
+ pvql warm DIR # render a directory ahead of time
162
+ pvql types # list claimed extensions
163
+ pvql doctor # check every part of the integration
164
+ pvql service # manage the render service
165
+ pvql config --init # write a config file with discovered defaults
166
+ pvql cache --clear # delete cached previews
167
+ ```
168
+
169
+ ## Troubleshooting
170
+
171
+ Run `pvql doctor` first; it checks the helper, the app, the extension registration, the
172
+ service, and a real render.
173
+
174
+ - **The panel says the service is not answering.** Run `pvql service --install`.
175
+ - **Nothing happens on space bar.** Confirm the type is claimed with `pvql types`, then
176
+ check that Finder resolves it: `mdls -name kMDItemContentType yourfile.vtu` should
177
+ report an `io.github.user27182.pyvista-quicklook.*` type.
178
+ - **Previews are stale.** `pvql cache --clear`.
179
+ - **A render fails.** Set `"log": true` in the config; activity is appended to
180
+ `pvql.log` beside it. The extension's own log is in
181
+ `~/Library/Containers/io.github.user27182.PyVistaQuickLook.QuickLook/Data/tmp/`.
182
+
183
+ ## Development
184
+
185
+ ```bash
186
+ uv sync --group dev
187
+ uv run pytest tests/ # helper tests, with coverage
188
+ uv run pre-commit run --all-files
189
+ ./scripts/build.sh # compile and sign the app bundle
190
+ ```
191
+
192
+ `main` is protected by a pre-commit hook, so work on a branch.
193
+
194
+ Releases are published to PyPI by CI through trusted publishing: push a `v*` tag and
195
+ the `publish` job uploads from the `release` environment. That requires a matching
196
+ pending publisher configured on PyPI for this repository and workflow.
197
+
198
+ ## Uninstall
199
+
200
+ ```bash
201
+ ./scripts/uninstall.sh
202
+ uv tool uninstall pyvista-quicklook
203
+ ```
204
+
205
+ That removes the app, the private PyVista environment, the downloaded source, and the
206
+ cache, leaving only the config file.