A PyQt widget to display crystal structures
Project description
Fastmolwidget
A PyQt/PySide6 widget to display crystal structures
Fastmolwidget is a lightweight, embeddable Qt widget that renders molecular and crystal structures in 2D projection with interactive mouse controls. It supports anisotropic displacement parameter (ADP) ellipsoids, ball-and-stick diagrams, and plain sphere representations — all drawn with a pure-Python QPainter backend (no OpenGL required).
Screenshot
View of a crystal structure from a CIF file with ADP ellipsoids. The control bar allows toggling display options interactively.
Features
- ADP ellipsoids at the 50 % probability level, rendered from anisotropic displacement parameters
- Ball-and-stick and isotropic sphere representations as fallbacks or when speed is more important than detail
- Interactive mouse controls: rotate (left-drag), zoom (right-drag), pan (middle-drag), scroll wheel to resize labels
- Atom and bond selection: single click or Ctrl+click for multi-selection; emits
atomClicked/bondClickedQt signals - Hydrogen visibility toggle
- Atom label display toggle with adjustable font size
- Bond width adjustment via spin box
- Multiple file formats: CIF, SHELX
.res/.ins, and plain XYZ. More to come... - Embeddable —
MoleculeWidgetis a plainQWidgetsubclass; drop it into any layout - Ready-to-use
MoleculeViewerWidgetbundles the renderer with a full control bar
Supported File Formats
| Extension | Format | Notes |
|---|---|---|
.cif |
Crystallographic Information File | Reads atoms, unit cell, and ADPs |
.res / .ins |
SHELXL instruction file | Reads atoms and unit cell via shelxfile |
.xyz |
Standard XYZ coordinate file | Cartesian coordinates, no cell or ADPs |
Installation
pip install fastmolwidget
By default, fastmolwidget installs without a concrete Qt binding.
Install one binding explicitly via extras:
pip install "fastmolwidget[pyside6]"
pip install "fastmolwidget[pyqt6]"
Requirements: Python >= 3.12, NumPy, gemmi, shelxfile, qtpy, and either PySide6 or PyQt6.
Quick Start
Standalone viewer
from qtpy.QtWidgets import QApplication
from fastmolwidget import MoleculeViewerWidget
app = QApplication([])
viewer = MoleculeViewerWidget()
viewer.load_file("structure.cif")
viewer.show()
app.exec()
Embedding in your own layout
from fastmolwidget import MoleculeWidget, MoleculeLoader
mol = MoleculeWidget(parent=self)
loader = MoleculeLoader(mol)
# The loader recognizes the file format from the extension and populates `mol` accordingly
loader.load_file("structure.cif")
# drop `mol` into any QLayout
layout.addWidget(mol)
Loading a different file at runtime
viewer.load_file("new_structure.res")
Reacting to atom / bond clicks
mol.atomClicked.connect(lambda label: print(f"Clicked atom: {label}"))
mol.bondClicked.connect(lambda a, b: print(f"Clicked bond: {a}–{b}"))
Mouse Controls
| Action | Effect |
|---|---|
| Left-drag | Rotate the molecule |
| Right-drag | Zoom in / out |
| Middle-drag | Pan the view |
| Scroll wheel | Increase / decrease label font size |
| Left-click | Select a single atom or bond |
| Ctrl + Left-click | Toggle multi-selection |
Control Bar Options (MoleculeViewerWidget)
| Control | Default | Description |
|---|---|---|
| Show ADP | ✓ | Toggle ORTEP ellipsoid / isotropic sphere rendering |
| Show Labels | ✗ | Toggle non-hydrogen atom labels |
| Round Bonds | ✓ | Switch between 3D-shaded and flat bond drawing |
| Show Hydrogens | ✓ | Show or hide hydrogen atoms and their bonds |
| Bond Width | 3 | Stroke width for bonds (1–15 px) |
API Overview
MoleculeViewerWidget(parent=None)
A self-contained widget combining MoleculeWidget with the control bar described above.
load_file(path)— load a structure file (format auto-detected from extension)render_widget— read-only property exposing the underlyingMoleculeWidget
MoleculeWidget(parent=None)
The low-level renderer widget. It is a plain QWidget subclass that you can drop into any layout.
Provide atom data directly via open_molecule() instead of loading a file through MoleculeLoader.
Qt Signals
| Signal | Signature | Emitted when |
|---|---|---|
atomClicked |
(label: str) |
The user clicks on an atom; label is the atom name (e.g. "C1") |
bondClicked |
(label1: str, label2: str) |
The user clicks on a bond; both atom labels are passed |
Data Methods
-
open_molecule(atoms, cell=None, adps=None, keep_view=False)
Load a new set of atoms and reset (or optionally preserve) the view.atoms— list ofAtomtuple(label, type, x, y, z, part)in Cartesian coordinates (Å)cell— optional(a, b, c, α, β, γ)tuple of unit-cell parameters (Å / °); required for ADP renderingadps— optionaldictmapping atom labels to(U11, U22, U33, U23, U13, U12)ADP tensorskeep_view— whenTrue, the current zoom, pan, and rotation are preserved (useful for live updates)
-
grow_molecule(atoms, cell=None, adps=None)
Replace the atom set while always preserving the current view.
Equivalent to callingopen_molecule(..., keep_view=True). -
clear()
Remove all atoms and bonds from the display.
Display Methods
-
show_adps(value: bool)
Toggle ORTEP-style ADP ellipsoid rendering. WhenFalse, atoms are drawn as isotropic spheres. -
show_labels(value: bool)
Show or hide non-hydrogen atom labels. -
show_hydrogens(value: bool)
Show or hide hydrogen / deuterium atoms and their bonds. -
show_round_bonds(value: bool)
Switch between 3D-shaded cylinder-style bonds (True, default) and flat single-colour bonds (False). -
set_bond_width(width: int)
Set the stroke width for bonds in pixels (valid range: 1–15). -
setLabelFont(font_size: int)
Set the pixel size used for atom labels. -
set_background_color(color: QColor)
Change the widget background colour. -
reset_view()
Reset zoom, pan, and rotation to their defaults.
Example — feeding atom data directly
from fastmolwidget import MoleculeWidget
from fastmolwidget.sdm import Atomtuple
mol = MoleculeWidget(parent=self)
atoms = [
Atomtuple(label="C1", type="C", x=0.0, y=0.0, z=0.0, part=0),
Atomtuple(label="O1", type="O", x=1.22, y=0.0, z=0.0, part=0),
Atomtuple(label="H1", type="H", x=-0.5, y=0.94, z=0.0, part=0),
]
# ADP tensors: {atom_label: (U11, U22, U33, U23, U13, U12)}
adps = {
"C1": (0.02, 0.02, 0.02, 0.0, 0.0, 0.0),
"O1": (0.03, 0.03, 0.03, 0.0, 0.0, 0.0),
}
cell = (5.0, 5.0, 5.0, 90.0, 90.0, 90.0) # optional
mol.open_molecule(atoms=atoms, cell=cell, adps=adps)
mol.atomClicked.connect(lambda label: print(f"Selected: {label}"))
layout.addWidget(mol)
MoleculeLoader(widget)
Format-aware loader that populates a MoleculeWidget.
load_file(path, keep_view=False)— parse file and callopen_molecule/grow_molecule
License
BSD 2-Clause License — see LICENSE for details.
© 2026 Daniel Kratzert
Maintainer Release Workflow
The release workflow is tag-driven and currently publishes to TestPyPI only.
- Ensure
project.versioninpyproject.tomlis the version to publish. - Create and push a matching tag in the format
version-X.Y.Z. - GitHub Actions builds sdist/wheel and uploads to TestPyPI.
Example:
git tag version-0.1.0
git push origin version-0.1.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastmolwidget-0.5.1.tar.gz.
File metadata
- Download URL: fastmolwidget-0.5.1.tar.gz
- Upload date:
- Size: 50.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa9e3bc6715c1ab4f120d0acf5abbd1f1e967e95b29cc7ed244b2bed8096d348
|
|
| MD5 |
c828f4642262d578c0173f581dff35bc
|
|
| BLAKE2b-256 |
ea892efcf2288d737ba1442ac9557aca597246012514f2322c069bd976a97220
|
Provenance
The following attestation bundles were made for fastmolwidget-0.5.1.tar.gz:
Publisher:
python-publish.yml on dkratzert/Fastmolwidget
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastmolwidget-0.5.1.tar.gz -
Subject digest:
aa9e3bc6715c1ab4f120d0acf5abbd1f1e967e95b29cc7ed244b2bed8096d348 - Sigstore transparency entry: 1283548054
- Sigstore integration time:
-
Permalink:
dkratzert/Fastmolwidget@846c5b56b518638c6c84d8dead66d35a8f1ea2ea -
Branch / Tag:
refs/tags/version-0.5.1 - Owner: https://github.com/dkratzert
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@846c5b56b518638c6c84d8dead66d35a8f1ea2ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastmolwidget-0.5.1-py3-none-any.whl.
File metadata
- Download URL: fastmolwidget-0.5.1-py3-none-any.whl
- Upload date:
- Size: 53.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d61d141266962bd279485a50ec4395224a7637da816bfb43ad29a1ecbd758a47
|
|
| MD5 |
8d03c1a2047e19846eba2d5993282d71
|
|
| BLAKE2b-256 |
0d5f0ab2d0b96087ad810e8ee0aefee2b0231d09fbc97b3a3968fb8ed324f0f2
|
Provenance
The following attestation bundles were made for fastmolwidget-0.5.1-py3-none-any.whl:
Publisher:
python-publish.yml on dkratzert/Fastmolwidget
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastmolwidget-0.5.1-py3-none-any.whl -
Subject digest:
d61d141266962bd279485a50ec4395224a7637da816bfb43ad29a1ecbd758a47 - Sigstore transparency entry: 1283548154
- Sigstore integration time:
-
Permalink:
dkratzert/Fastmolwidget@846c5b56b518638c6c84d8dead66d35a8f1ea2ea -
Branch / Tag:
refs/tags/version-0.5.1 - Owner: https://github.com/dkratzert
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@846c5b56b518638c6c84d8dead66d35a8f1ea2ea -
Trigger Event:
push
-
Statement type: