From 140e54a62abf446a0761c5ed6cc8ec1ecdbd4652 Mon Sep 17 00:00:00 2001 From: Patrick Kunzmann Date: Sat, 8 Feb 2025 15:33:22 +0100 Subject: [PATCH] fixup! Add documentation for `biotite.interface.pymol` --- doc/apidoc.json | 4 ++ doc/conf.py | 3 +- doc/contribution/documentation.rst | 41 +------------- doc/scraper.py | 86 ++++++------------------------ 4 files changed, 23 insertions(+), 111 deletions(-) diff --git a/doc/apidoc.json b/doc/apidoc.json index 1a4b1c610..4d78b54da 100644 --- a/doc/apidoc.json +++ b/doc/apidoc.json @@ -46,6 +46,10 @@ "Combined shapes": [ "draw_arrows", "draw_box" + ], + "Display": [ + "show", + "play" ] }, diff --git a/doc/conf.py b/doc/conf.py index 86d5d438b..cbfb38f77 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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, } diff --git a/doc/contribution/documentation.rst b/doc/contribution/documentation.rst index ca0c8591d..46b4fd432 100644 --- a/doc/contribution/documentation.rst +++ b/doc/contribution/documentation.rst @@ -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: @@ -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 `_ package is required. -Please make sure to use open-source *PyMOL* to avoid licensing issues. - -Let's assume you have an example script `.py`. -The visualization is initiated by adding the comment line - -.. code-block:: python - - # sphinx_gallery_ammolite_script = .py - -in the code block where you want show the visualization. -Then the visualization 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 -``.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 diff --git a/doc/scraper.py b/doc/scraper.py index c9fd629ce..92105b699 100644 --- a/doc/scraper.py +++ b/doc/scraper.py @@ -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): @@ -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()) @@ -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"], + )