我需要显示一堆存储在 json 文件(我们的赞助商列表)中的图像,如下所示:
{
"toto": "path/to/image",
"tutu": "path/to/image",
...
}
我有一些同事一开始不太擅长,要求他们编写额外的图像指令太过分了。为了帮助他们,我想创建一个自定义指令来解析 json 文件,然后“调用”图像指令。这可能吗?
我会写这样的东西:
.. logo:: path/to/json
仅此而已。
我已经尝试的是一个完全自定义的指令,但它仅适用于 html,并且我不想创建额外的访问方法,而是想为列表中的每个图像添加图像节点。
"""Logo extention to create a list of logos"""
import json
from pathlib import Path
from typing import Dict, List
from docutils import nodes
from sphinx.application import Sphinx
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, SphinxTranslator
logger = logging.getLogger(__name__)
SIZES = {"xs": 6, "sm": 8, "md": 10, "lg": 12, "xl": 15, "xxl": 20}
"Accommodate different logo shapes (width values in rem)"
class logo_node(nodes.General, nodes.Element):
"""Logo Node"""
pass
class Logos(SphinxDirective):
"""Logo directive to create a grid of logos from a json file
Example:
.. logo:: funder
"""
has_content = True
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self) -> List[logo_node]:
# get the env
self.env
# get the data
data_dir = Path(__file__).parents[1] / "_data" / "logo"
logo_file = data_dir / f"{self.arguments[0]}.json"
logos = json.loads(logo_file.read_text())
return [logo_node(logos=logos)]
def visit_logo_node_html(translator: SphinxTranslator, node: logo_node) -> None:
"""Entry point of the html logo node"""
# Write the html content line by line for readability
# init the html content
html = '<div class="container my-4">\n'
html += '\t<div id="funder-logos" class="d-flex flex-wrap flex-row justify-content-center align-items-center">\n'
for v in node["logos"].values():
# get informations from the parameters
size = SIZES[v["size"]]
link = v["html"]
light_logo = f"_static/logos/{v['light']}"
dark_logo = f"_static/logos/{v['dark']}"
# write the html files
html += f'\t\t<div class="my-1 mx-2" style="width:{size}rem;">'
html += f'<a href="{link}">'
html += f'<img class="card-img only-light" src="{light_logo}">'
html += f'<img class="card-img only-dark" src="{dark_logo}">'
html += "</a>"
html += "</div>\n"
translator.body.append(html)
def depart_logo_node_html(translator: SphinxTranslator, node: logo_node) -> None:
"""exit from the html node and close the container"""
translator.body.append("\t</div>\n</div>\n")
def visit_logo_node_unsuported(translator: SphinxTranslator, node: logo_node) -> None:
"""Entry point of the ignored logo node."""
logger.warning("Logo: unsupported output format (node skipped)")
raise nodes.SkipNode
def setup(app: Sphinx) -> Dict[str, bool]:
app.add_node(
logo_node,
html=(visit_logo_node_html, depart_logo_node_html),
epub=(visit_logo_node_unsuported, None),
latex=(visit_logo_node_unsuported, None),
man=(visit_logo_node_unsuported, None),
texinfo=(visit_logo_node_unsuported, None),
text=(visit_logo_node_unsuported, None),
)
app.add_directive("logos", Logos)
return {
"parallel_read_safe": True,
"parallel_write_safe": True,
}
我偶然发现了这个问题,试图自己找到解决方案。我能够让它适用于我的用例,其中
.rst
正在导入/加载动态转换为图像的对象。我深入研究了 docutils 源代码并想出了这个(我的实际代码的一个非常精简的版本):
from docutils import nodes
class MyDirective(SphinxDirective):
required_arguments: int = 1
def run(self) -> list[nodes.Node]:
obj_import_path = self.arguments[0]
obj_name = "test"
img_path = f"_static/colormaps/{obj_name}.png"
my_obj = _import_my_obj(obj_import_path)
my_obj.save()
paragraph = nodes.paragraph(text=obj_name)
image = nodes.image("", **{"uri": img_path, "alt": obj_name})
return [paragraph, image]
因此,这会创建一个带有对象名称的“标题”,然后以与
.. image::
相同的方式在其下方放置图像。请注意,nodes.image
的第一个参数应该是重组文本(例如.. image:: path/to/image.png
),但我发现没有它也能正常工作。您可以通过以下方式使用它:
.. logos:: path/to/some.json
请注意,在我的实际代码中,我允许加载这些对象的列表,并且由于您只是返回节点列表,因此单个指令调用没有理由无法处理它。