Skip to content

Launch dcmview from Python

Launch the same v0.4.1 Rust viewer after Python code has selected the files or cases to inspect.

Terminal window
python -m pip install --user dcmview-py==0.4.1

Supported wheels bundle the dcmview binary. The wrapper requires Python 3.9 or later.

The default call blocks until the viewer stops and returns None after a successful exit:

inspect_case.py
from dcmview_py import view
view("./scan.dcm")

Use a list to inspect multiple files or directories, and disable the automatic browser when working in a terminal or notebook:

inspect_study.py
from dcmview_py import view
view(
["./study", "./derived/segmentation.dcm"],
browser=False,
timeout=300,
filters=["Modality=CT"],
)

timeout is an idle timeout that starts when the scan finishes; API or browser requests reset it. Filter fields accept snake_case names such as patient_id or DICOM keywords such as PatientID.

Interrupting a blocking call, for example with a notebook’s interrupt button, stops the viewer before the interrupt is raised.

Set block=False to receive a handle:

nonblocking.py
from dcmview_py import view
handle = view("./study", browser=False, block=False)
if handle is None:
raise RuntimeError("expected a non-blocking handle")
print(handle.url)
return_code = handle.stop()

handle.url is the launch URL and ends in #token=…, the session’s access token. Open the complete link; the address without the fragment shows a page that asks for the access link. Printing it shows a credential, so keep it out of output you share.

A non-blocking call waits for the viewer’s URL, then up to five more seconds for its scan to finish. If the scan finds no DICOM files, the call raises subprocess.CalledProcessError instead of returning a handle. If no URL arrives within 30 seconds, the call warns and handle.url stays None until the server reports it.

Non-blocking viewers are stopped when the Python interpreter exits, so a finished script or a restarted notebook kernel does not leave a server running. Use a blocking call or the dcmview command when the viewer should outlive the script.

For deterministic cleanup, use the handle as a context manager:

context_managed.py
from dcmview_py import view
with view("./study", browser=False, block=False) as handle:
print(handle.url)

The context manager calls stop() on exit. Local handles request graceful shutdown, then terminate and finally kill the subprocess only if it does not stop within the timeout.

Every request to the viewer’s HTTP routes needs Authorization: Bearer <token>. A non-blocking handle for a local viewer exposes the two values a script needs: handle.base_url, the origin without the fragment, and handle.token.

authenticated_request.py
import json
from urllib.request import Request, urlopen
from dcmview_py import view
with view("./study", browser=False, block=False, vscode_bridge=False) as handle:
request = Request(
handle.base_url + "/api/files",
headers={"Authorization": f"Bearer {handle.token}"},
)
with urlopen(request) as response:
catalog = json.load(response)

Both properties are None with a binary older than v0.4.0 and for a viewer routed into VS Code, which is why the example passes vscode_bridge=False. To choose the token yourself, set DCMVIEW_TOKEN in the environment before calling view(); the binary inherits it. The routes are an unsupported internal protocol and can change with any release.

annotations.py
from dcmview_py import view
view(
"./study",
annotations="./embed_annotations.csv",
browser=False,
)

See the annotation guide for CSV shape and in-memory editing behavior.

Pass mask=True to start a masked session, the same as dcmview --mask:

masked.py
from dcmview_py import view
view("./study", mask=True)

The viewer replaces patient identifiers in everything it displays. This is a display aid, not de-identification: files are not modified, and burned-in pixel text, free text, and folder and file names are not masked. See Share your screen before you rely on it.

With the dcmview VS Code extension active, view() opens the viewer in a VS Code webview when Python runs in a VS Code integrated terminal, or when its working directory is inside an open workspace folder. The second case covers notebooks started from the workspace. Anywhere else, the local viewer starts. The returned non-blocking handle is a ShutdownHandle in both cases and controls a VS Code-managed viewer through the same .url and .stop() interface. For a VS Code-managed viewer, handle.token and handle.base_url are None, and a DCMVIEW_TOKEN set only in the launching terminal or notebook is not forwarded: the viewer inherits the extension host’s environment and otherwise generates its own token.

Force a local subprocess when a browser window is preferable:

local_only.py
from dcmview_py import view
view("./study", vscode_bridge=False)

You can also set DCMVIEW_VSCODE_BYPASS=1 for the process environment.

The package also provides a module command. It and the dcmview and dcmview-py console scripts pass their arguments unchanged to the binary, so options, --help, and --version are the Rust CLI’s own:

Terminal window
python -m dcmview_py --no-browser --timeout 120 ./study

The command follows the same VS Code routing rule as view(), waits for the viewer to exit, and returns its exit code.

  • ValueError: no paths were supplied.
  • TypeError: a file, annotation, or filter value has the wrong type, or a removed keyword such as tunnel was passed.
  • RuntimeError: no usable binary was found.
  • subprocess.CalledProcessError: the viewer exited with a non-zero status or found no files. The exception carries the viewer’s last output.

To inspect remote data, run view() with browser=False on the remote machine, forward the port from your local machine, and open the printed http://localhost:PORT/#token=… link with its token fragment, as described in Inspect a study over SSH. view() does not offer the private --unix-socket mode; use the dcmview command for that.

Read the exact Python API reference for every parameter, binary resolution, and bridge environment variable.