How it works#
The app bundle contains a Quick Look extension that declares a uniform type identifier for each claimed extension. When the Finder previews one of those files, the extension hands it to a background render service, which reads it with PyVista, extracts the surface, colours the vertices by the active scalars, and writes a PLY. The extension shows that PLY in a SceneKit view, which is what makes the preview turnable. The scene is lit by VTK’s light kit, which turns with the camera, so its colours match PyVista’s own renders; faces are drawn flat unless the theme shades smoothly.
Drawing#
Point data is interpolated across each face. Cell data is drawn flat: the cells are
split apart first, so each keeps its own colour. Lines and point clouds are drawn as
PyVista draws them: unlit, lines at the theme’s line_width and points as round
points of its point_size, both in pixels, so a saved theme sets them. Lines are never
drawn thinner than two pixels, since one pixel is a hairline on a Retina display.
A volume that carries scalars is cut into three slices through its centre; one without is shown as its outer surface. Both sides of every face are drawn and lit, as PyVista draws them, so an open surface or a slice shows from wherever the camera looks.
Surfaces are sent whole up to max_scene_points. Above that, images and volumes are
thinned on their lattice and other surfaces are decimated, or binned onto a grid above
4 million points. The grid uses at most about 1 GB of memory, so a compact surface
binned that way can end below a large max_scene_points. The preview then has fewer
points and cells than the file, and its shape can differ in fine detail.
pvql config set max_scene_points 0 sends every surface whole, at the cost of slower
previews of large files.
Cache#
Previews are cached under ~/Library/Caches/PyVistaQuickLook, keyed by the file’s
path, size, and modification time, so editing a file invalidates its preview, and by the
version of the converter, so an update rebuilds them rather than serving old ones.
The render service deletes previews of files that have changed or gone when it starts
and then once a day (cache_check_minutes); pvql cache --prune does it now,
dropping previews built by an older version too, and pvql cache --clear empties the
cache. Changing a setting that previews depend on, such as the theme, leaves the
previews built under the old one until they are pruned or cleared.
A preview is kept whenever the service cannot tell: its file is on a disk that is not plugged in, a share that is not mounted, or in a folder macOS keeps private and no preview has read since the service started, so a check never raises a permission prompt of its own.
The service loads PyVista and VTK in the background when it starts, which is at login
and whenever it is reinstalled, so the first preview does not wait for them.
pvql preload does the same on demand.
Large files#
A .pv file is read with only the array it is coloured by. Its other arrays are read
one at a time for their ranges, so a file never needs its arrays and its mesh in memory
at once. A VTK XML file above 256 MB is read the same way, its other ranges taken from
the header. A vector’s range is that of its magnitude, shown as ||v||, in every
format. Files saved with a thumbnail open faster still: see Fast previews with pyvista-zstd.
For a file of 32 MB or more, the dataset panel appears as soon as the header is read, with a spinner where the render goes until the preview is ready.
Previews are built side by side while their estimated memory, about six times each
file’s size, fits within half the Mac’s memory (max_conversion_memory_mb); the rest
wait their turn, and a file too big for that budget is built on its own. Moving on from
a slow file does not cancel it, so it is ready when you come back, unless its conversion
runs past timeout.
A file above max_file_size_mb is not converted or copied when it is selected. The
panel shows what its header says, and a Preview Anyway button previews it once.
A claimed file that turns out not to be a mesh is shown the way Quick Look would have
shown it: as plain text when it reads as text, such as a .dat holding a table of
numbers, and otherwise as its icon, name, size, and date, with a line saying why it
holds no mesh. Any other failure, such as a render service that is not running or a
conversion that timed out, is reported in full instead.
Compressed and multi-file datasets#
Two kinds of dataset are not a single file with a claimable extension, and both are reached by claiming the type macOS gives them instead.
A compressed dataset is named .nii.gz or .vol.gz, but Launch Services sees only
.gz and keeps its own type for it, so declaring those extensions does nothing. The
extension claims the gzip type: press space on a gzipped NIfTI, PERMAS, Netgen, or Medit
file and it is drawn like any other. Every other gzip, a tarball or a compressed log,
shows its details.
A DICOM series is a folder of slices, so the folder is what gets previewed. Press space on one in the Finder and the slices are read as a single volume, cut through its centre and coloured, the same as any other volume. An ordinary folder shows its own details immediately, as the Finder would.
The render service#
macOS runs Quick Look extensions in a sandbox that VTK cannot run inside, so reading and
conversion happen in a launch agent instead, which the installer writes to
~/Library/LaunchAgents and starts.
pvql service # report whether it is loaded
pvql service --install # (re)install and start it
pvql service --uninstall
It appears as pyvista-quicklook-daemon under Login Items & Extensions in System
Settings, next to the app’s own entry, where it can be switched off; its output goes to
~/Library/Logs/pvqld.log.
Files in the Desktop, Documents, and Downloads folders#
macOS keeps those folders private to each program, and the render service cannot read them. The Quick Look extension copies the file it was asked to preview into its own container so that the service can convert it anyway.
A dataset that points at neighbouring files (.pvd, .vtm, .case, .foam,
.xdmf, .xmf) needs those neighbours, which the copy does not include. Keep such
datasets outside those three folders, or grant the render service Full Disk Access in
System Settings under Privacy & Security. The service runs from the app nested at
~/Applications/PyVista Quick Look.app/Contents/Library/PyVista Quick Look.app; in the
file picker that opens from the + button, press Cmd-Shift-G and paste that path.