Skip to content

Commit

Permalink
fixup! Add documentation for biotite.interface.pymol
Browse files Browse the repository at this point in the history
  • Loading branch information
padix-key committed Feb 8, 2025
1 parent 8dc25f2 commit 140e54a
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 111 deletions.
4 changes: 4 additions & 0 deletions doc/apidoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
"Combined shapes": [
"draw_arrows",
"draw_box"
],
"Display": [
"show",
"play"
]
},

Expand Down
3 changes: 2 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,16 @@
"default_thumb_file": join(
DOC_PATH, "static/assets/general/biotite_icon_thumb.png"
),
"capture_repr": (),
"image_scrapers": (
"matplotlib",
scraper.static_image_scraper,
scraper.pymol_scraper,
),
"matplotlib_animations": True,
"image_srcset": ["2x"],
"backreferences_dir": "examples/backreferences",
"doc_module": ("biotite",),
# Set the NCBI API key
"reset_modules": (preamble.setup_script),
"remove_config_comments": True,
}
Expand Down
41 changes: 2 additions & 39 deletions doc/contribution/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,8 @@ To build the documentation without the gallery and the tutorial, run
You may also ask the *Biotite* maintainers to run the example script and check
the generated page, if building the gallery on your device is not possible.

Static images and molecular visualizations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to *Matplotlib* plots, the *Biotite* example gallery can also
show molecular visualizations, via the *PyMOL* software, and static images.

Static images
^^^^^^^^^^^^^
Static images can be included by adding the following comment in the
corresponding code block:

Expand All @@ -96,40 +93,6 @@ corresponding code block:
The image file must be stored in the same directory as the example script.

|
To visualize images using *PyMOL*, the
`Ammolite <https://ammolite.biotite-python.org/>`_ package is required.
Please make sure to use open-source *PyMOL* to avoid licensing issues.

Let's assume you have an example script `<example_name>.py`.
The visualization is initiated by adding the comment line

.. code-block:: python
# sphinx_gallery_ammolite_script = <name_of_the_script>.py
in the code block where you want show the visualization.
Then the visualization script ``<name_of_the_script>.py`` is executed, which
can use the global variables from the example script and the special
``__image_destination__`` variable.
``__image_destination__`` is a string representing the path to the output image
file.
The PyMOL visualization can be saved to this file with e.g.

.. code-block:: python
ammolite.cmd.png(__image_destination__)
The rendered image is saved in the directory of the example script as
``<example_name>.png`` and is added to version control.
The visualization script is only executed, if the rendered image does not
exist, yet.
The traceback of errors in the visualization script are printed, if
``sphinx-build`` is run in verbose (``-v``) mode.
An example of this can be seen in the
``doc/examples/structure/contact_sites.py`` example.

Tutorial
--------
When adding new content for a broad audience, it is appreciated to update the
Expand Down
86 changes: 15 additions & 71 deletions doc/scraper.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import copy
import os
import shutil
import sys
from os.path import dirname, isfile, join, splitext
from sphinx.errors import ExtensionError
from os.path import dirname, join, splitext
from IPython.display import Image
from sphinx_gallery.py_source_parser import extract_file_config
from sphinx_gallery.scrapers import figure_rst

STATIC_IMAGE_COMMAND = "static_image"
PYMOL_IMAGE_COMMAND = "ammolite_script"


def static_image_scraper(block, block_vars, gallery_conf):
Expand All @@ -18,7 +14,7 @@ def static_image_scraper(block, block_vars, gallery_conf):
# Search for `sphinx_gallery_static_image` commands
block_conf = extract_file_config(code)
if STATIC_IMAGE_COMMAND not in block_conf:
return figure_rst([], gallery_conf["src_dir"])
return ""

image_sources = [
join(script_dir, image_name.strip())
Expand All @@ -43,68 +39,16 @@ def static_image_scraper(block, block_vars, gallery_conf):


def pymol_scraper(block, block_vars, gallery_conf):
_, code, _ = block
block_conf = extract_file_config(code)
# Search for a `sphinx_gallery_ammolite_script` command
if PYMOL_IMAGE_COMMAND not in block_conf:
return figure_rst([], gallery_conf["src_dir"])

script_dir = dirname(block_vars["src_file"])
pymol_script_path = join(script_dir, block_conf[PYMOL_IMAGE_COMMAND])
# The rendered image will be created in the same directory as
# the example script
# -> the image will be included in version control
# -> Rendering with PyMOL is not necessary for building the docs
pymol_image_path = splitext(block_vars["src_file"])[0] + ".png"
if not isfile(pymol_script_path):
raise ExtensionError(
f"'{block_vars['src_file']}' has no corresponding "
f"'{pymol_script_path}' file"
)

try:
import ammolite # noqa: F401
import pymol # noqa: F401
except ImportError:
# If Ammolite is not installed, fall back to the image file,
# if already existing
if not isfile(pymol_image_path):
raise ExtensionError("PyMOL or Ammolite is not installed")
else:
# Create a shallow copy,
# to avoid adding new variables to example script
script_globals = copy.copy(block_vars["example_globals"])
script_globals["__image_destination__"] = pymol_image_path

with open(pymol_script_path, "r") as script:
# Prevent PyMOL from writing stuff (splash screen, etc.)
# to STDOUT or STDERR
# -> Save original STDOUT/STDERR and point them
# temporarily to DEVNULL
dev_null = open(os.devnull, "w")
orig_stdout = sys.stdout
orig_stderr = sys.stderr
sys.stdout = dev_null
sys.stderr = dev_null
try:
exec(script.read(), script_globals)
except Exception as e:
raise ExtensionError(
f"PyMOL script raised a {type(e).__name__}: {str(e)}"
)
finally:
# Restore STDOUT/STDERR
sys.stdout = orig_stdout
sys.stderr = orig_stderr
dev_null.close()
if not isfile(pymol_image_path):
raise ExtensionError(
"PyMOL script did not create an image " "(at expected location)"
)

# Copy the images into the 'gallery' directory under a canonical
# sphinx-gallery name
image_path_iterator = block_vars["image_path_iterator"]
image_destination = image_path_iterator.next()
shutil.copy(pymol_image_path, image_destination)
return figure_rst([image_destination], gallery_conf["src_dir"])
image_paths = []

for object in block_vars["example_globals"].values():
if isinstance(object, Image) and object.metadata["source"] == "PyMOL":
image_path = next(image_path_iterator)
with open(image_path, "wb") as image_file:
image_file.write(object.data)
image_paths.append(image_path)
return figure_rst(
image_paths,
gallery_conf["src_dir"],
)

0 comments on commit 140e54a

Please sign in to comment.