Skip to content

Commit

Permalink
Python API docs (#697)
Browse files Browse the repository at this point in the history
* initial inclusion of python API docs

* change workflow

* rename to base.py for each module

* update filter for generated docs for strings

* quartoc module

* move (un)register to addon.py

* more API introduction stuff

* canvas for scene and render management

* add jupyter to docs requirements

* source venv before quarto render

* add oxdna docs

* cleanup

* rendering

* add [all] to pyporoject

* fix docs

* move update_with_scene to entity

* fixx render

* Update docs.yml

* Update docs.yml

* maybe fix render

* maybe fix render

* maybe fix render

* uv lock

* proerly add jupyter and lock

* pin python version

* change to CPU rendering for GHA

* tests

* add back quartodoc

* add IPython for tests

* fix build docs

* remove install of Blender for docs

* add back uv setup

* revamp docs layout for merge
  • Loading branch information
BradyAJohnston authored Feb 14, 2025
1 parent 503ac42 commit ed432b6
Show file tree
Hide file tree
Showing 39 changed files with 3,731 additions and 111 deletions.
23 changes: 11 additions & 12 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,19 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: quarto-dev/quarto-actions/setup@v2
- name: Setup Blender
uses: BradyAJohnston/setup-[email protected]
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: 4.2.5
- name: Install Packages
run: |
blender -b -P tests/python.py -- -m pip install -e ".[docs]"
- name: Generate Docs
run: |
blender -b -P docs/generate.py
version: "latest"

- name: Render Docs
run: quarto render docs

run: |
uv sync --all-extras
cd docs
uv run generate.py
uv run quartodoc build
cd ..
uv run quarto render docs
- name: Configure pull release name
if: ${{github.event_name == 'pull_request'}}
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ logs/
*.log
.luarc.json
dist
poetry.lock
*.lock
*xtc_offsets.lock
*.DS_Store
.mda_session
*.vdb
Expand All @@ -33,3 +32,4 @@ docs/nodes/*.qmd
/.luarc.json
tests/data/starfile/montage.tiff
tests/data/cellpack/petworld/*.cif
/docs/api
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11
71 changes: 55 additions & 16 deletions docs/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ website:
page-navigation: true
navbar:
left:
- file: installation.qmd
text: Installation
# - file: installation.qmd
# text: Installation
- file: tutorials/
text: Tutorials
- text: Nodes
href: nodes/
- file: attributes.qmd
text: Documentation
- text: API
href: api/
- file: examples/
text: Examples
- file: citations/
Expand All @@ -31,25 +35,31 @@ website:
href: https://youtube.com/c/bradyjohnston

sidebar:
- id: documentation
title: Documentation
style: floating
align: left
# pinned: true

- id: nodes
title: Nodes
contents: nodes/

- id: api
title: Python API
contents: api/

- id: attributes
contents:
- installation.qmd
- attributes.qmd
- data_table.qmd
- text: Nodes
href: nodes/index.qmd
contents: nodes/*


- api_introduction.qmd

- id: tutorials
Title: Tutorials
align: left
style: floating
contents: tutorials/
contents:
- tutorials/index.qmd
- tutorials/installation.qmd
- tutorials/00_interface.md
- tutorials/01_importing.qmd
- tutorials/02_selections.md
- tutorials/03_molecular_dynamics.md
- tutorials/04_cryoem.qmd



Expand All @@ -69,3 +79,32 @@ format:
preview-colour:
code: true
text: true

quartodoc:
# the name used to import the package you want to create reference docs for
package: molecularnodes
dir: api

# write sidebar and style data
sidebar: api/_sidebar.yml
# css: api/_styles-quartodoc.css

sections:
- title: Importing
desc: Downloading and importing molecular data through the API.
contents:
- fetch
- parse
- download.download

- title: Entity
desc: Importing and manipulating different molecular entities
contents:
- entities.MolecularEntity
- entities.Molecule
- entities.Trajectory
- entities.Ensemble
- entities.OXDNA

- title: Scene
- scene.Canvas
84 changes: 84 additions & 0 deletions docs/api_introduction.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: Python API
description: Introduction to the APi and the quirks of programming in Blender
jupyter: python3
---

::: {.callout-warning}
# The API is Unstable

Molecular Nodes is designed and created first and foremost as an add-on for Blender, so the API can at times seem a bit quirky and for the time being is not to be considered stable.

Molecular Nodes is versioned to match Blender versions, so while we are currently up to "4.2.*", the API should be not be consired to be that mature.
:::

This is how we can use the API.

```{python}
#| echo: false
#| output: false
import bpy
bpy.ops.wm.read_homefile(app_template="")
```

```{python}
import molecularnodes as mn
import numpy as np
# we currently have to manually register a lot of the internals with Blender
mn.register()
mn.template.install()
# create a 'Molecule' object, by fetching a structure and parsing it into the scene
mol = mn.fetch("6N2Y", style = 'ribbon')
```

## The Molecule Object

The `Molecule` object has the original data, as well as the Blender object associated with.

The different methods that are associated mostly interact with the Blender object, which is accessible via the `mol.object`, and the data is accessible via `mol.array`, which is the `biotite.AtomArrayStack` object.
```{python}
print(f"{len(mol)=}")
print(f"{mol.object=}")
print(f"{mol.array[0][:10]=}")
```

```{python}
mol.named_attribute('chain_id')
```

```{python}
mol.position
```

### Updating the Atom Positions

```{python}
mol.position -= mol.centroid()
mol.position
```

```{python}
canvas = mn.scene.Canvas()
canvas.resolution = (720, 480)
codes = ["4ozs", "8H1B", "8U8W"]
styles = ['cartoon', 'ribbon', 'spheres']
materials = ['MN Ambient Occlusion', 'MN Default', 'MN Ambient Occlusion']
for code, style, material in zip(codes, styles, materials):
canvas.scene_reset()
canvas.render_engine = "CYCLES"
canvas.samples_cycles = 32
# canvas.cycles_device = "GPU"
canvas.cycles_device = "CPU" # can't do GPU rendering on GitHub actions
mol = mn.fetch(code)
mol.material = bpy.data.materials[material]
mol.style = 'ribbon'
canvas.frame_object(mol)
mol.style = style
canvas.snapshot()
```
2 changes: 1 addition & 1 deletion docs/example_animations.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ To morph between 3 conformations of ATP synthase use ChimeraX commands "open 6n2
::: callout-caution
# Requires MDAnalysis Installed

To follow this particular tutorial, ensure you have first [installed Molecular Nodes properly](installation.qmd), including the optional MDAnalysis python package.
To follow this particular tutorial, ensure you have first [installed Molecular Nodes properly](tutorials/installation.qmd), including the optional MDAnalysis python package.
:::

Download the trajectory files from the the [CHARMM-GUI website](https://charmm-gui.org/?doc=archive&lib=covid19):
Expand Down
6 changes: 6 additions & 0 deletions docs/examples/examples.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
- title: Your Operating System |Eukaryotic Transcription
author: Clockwork
description: 🧬How does your DNA actually become who you are? Let's go on the incredible journey it takes to transcribe your DNA into mRNA.
path: https://www.youtube.com/watch?v=HZAmbbTcQ3M
image: https://img.youtube.com/vi/HZAmbbTcQ3M/0.jpg

- title: "What if all the world's biggest problems have the same solution?"
path: https://www.youtube.com/watch?v=P_fHJIYENdI
image: https://img.youtube.com/vi/P_fHJIYENdI/0.jpg
Expand Down
13 changes: 12 additions & 1 deletion docs/filters.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ for _, v in ipairs(attributes) do
table.insert(combined, v)
end

local special_mappings = {
["True"] = "True::Bool",
["False"] = "False::Bool",
["None"] = "None::Object",
}

function Code(el)
if special_mappings[el.text] then
el.text = special_mappings[el.text]
end
if el.text:match("^'.*'$") then
el.text = el.text .. "::String"
end
for _, keyword in ipairs(keywords) do
local pattern = "(.+)::" .. keyword
local name = el.text:match(pattern)
Expand All @@ -27,4 +39,3 @@ function Code(el)
end
end
end

File renamed without changes.
9 changes: 7 additions & 2 deletions molecularnodes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from .addon import register, unregister, _test_register
from .entities import fetch, load_local
from .addon import register, unregister
from .entities import fetch, parse
from . import color, blender

try:
from .scene import Canvas
except ModuleNotFoundError:
pass
2 changes: 1 addition & 1 deletion molecularnodes/blender/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .utils import path_resolve
from . import coll, nodes, mesh
from . import mesh, coll, nodes, mesh
43 changes: 43 additions & 0 deletions molecularnodes/blender/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,46 @@ def add_all_materials() -> None:
def default() -> Material:
"Return the default material."
return append("MN Default")


# class to interact with a bpy.types.Material node tree and change some of the default
# values of the nodes inside of it
class MaterialTreeInterface:
def __init__(self, tree: Material):
self.tree = tree
self.nodes = tree.node_tree.nodes
self.links = tree.node_tree.links


class MaterialAmbientOcclusion(MaterialTreeInterface):
def __init__(self, tree: Material):
super().__init__(tree)

@property
def ao_power(self) -> float:
return self.nodes["Math"].inputs[1].default_value

@ao_power.setter
def ao_power(self, value: float):
self.nodes["Math"].inputs[1].default_value = value

@property
def ao_distance(self) -> float:
return self.nodes["Ambient Occlusion"].inputs["Distance"].default_value

@ao_distance.setter
def ao_distance(self, value: float) -> None:
self.nodes["Ambient Occlusion"].inputs["Distance"].default_value = value


class MaterialSquishy(MaterialTreeInterface):
def __init__(self, tree: Material):
super().__init__(tree)

@property
def subsurf_scale(self) -> float:
return self.nodes["Principled BSDF"].inputs["Subsurface Scale"].default_value

@subsurf_scale.setter
def subsurf_scale(self, value: float):
self.nodes["Principled BSDF"].inputs["Subsurface Scale"].default_value = value
9 changes: 5 additions & 4 deletions molecularnodes/entities/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from . import molecule, trajectory
from .base import EntityType, MolecularEntity
from .density import MN_OT_Import_Map
from .trajectory.dna import MN_OT_Import_OxDNA_Trajectory
from .ensemble import CellPack
from .ensemble import StarFile
from .ensemble import CellPack, Ensemble, StarFile
from .ensemble.ui import MN_OT_Import_Cell_Pack, MN_OT_Import_Star_File
from .molecule import BCIF, CIF, PDB, SDF, Molecule
from .molecule.pdb import PDB
from .molecule.pdbx import BCIF, CIF
from .molecule.sdf import SDF
from .molecule.ui import fetch, load_local, parse
from .trajectory.trajectory import Trajectory
from .trajectory import OXDNA, Trajectory
from .trajectory.dna import MN_OT_Import_OxDNA_Trajectory

CLASSES = (
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ def bob(self) -> BlenderObject:
def node_group(self) -> bpy.types.NodeGroup:
return self.object.modifiers["MolecularNodes"].node_group

@property
def update_with_scene(self) -> bool:
return self.object.mn.update_with_scene

@update_with_scene.setter
def update_with_scene(self, value: bool) -> None:
self.object.mn.update_with_scene = value

def _register_with_session(self) -> None:
bpy.context.scene.MNSession.register_entity(self)

Expand Down
2 changes: 2 additions & 0 deletions molecularnodes/entities/density/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from .ui import MN_OT_Import_Map, load
from .mrc import MRC
from .base import Density
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import bpy
from typing import Union
from pathlib import Path
from ..entity import MolecularEntity, EntityType
from ..base import MolecularEntity, EntityType
from ... import blender as bl
import databpy

Expand Down
2 changes: 1 addition & 1 deletion molecularnodes/entities/density/mrc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .density import Density
from .base import Density

import mrcfile
from ...blender import coll, nodes
Expand Down
Loading

0 comments on commit ed432b6

Please sign in to comment.