helper scripts and images

thumbnail images, example images, two pytho
n helper scipts
This commit is contained in:
Andreas **Felix** Häberle 2024-01-21 19:12:10 +01:00
parent 9deb5e7ffa
commit 05477c4bce
11 changed files with 323 additions and 0 deletions

View file

@ -0,0 +1,227 @@
"""
name: "create_rst.py"
title: "Python Script to Create RST Files for Sphinx"
authors: "felix@42sol.eu"
license: "http://www.apache.org/licenses/LICENSE-2.0"
created: "2023-01-21"
modified: "2024-01-21"
description: |
Python script to generate documentation in RST format.
Used for the `build123d` documentation of examples.
TODO: check if we could add Sphinx-Gallery to the project https://sphinx-gallery.github.io/stable/advanced.html
NOTE: https://yaml-multiline.info/ is a good site to learn about multiline yaml strings.
has_builder_mode: false
has_algebra_mode: false
image_files:
- "none.png"
"""
# ---------------------------------------------------------------------------------------------
# [Imports]
import sys
import ast # see https://docs.python.org/3/library/ast.html
from os import getcwd as pwd # see https://docs.python.org/3/library/os.html#os.getcwd
import dataclasses as dc # see https://docs.python.org/3/library/dataclasses.html
from dataclasses import dataclass, asdict
from typing import List, Dict # see https://docs.python.org/3/library/typing.html
from pyperclip import copy # see https://pyperclip.readthedocs.io/en/latest/
from ruamel.yaml import YAML # see https://yaml.readthedocs.io/en/latest/index.html
# and for yaml tutorial see https://www.cloudbees.com/blog/yaml-tutorial-everything-you-need-get-started
from shellrunner import X # see https://github.com/adamhl8/shellrunner
# ---------------------------------------------------------------------------------------------
# [Definitions]
log = print
stdout = print
stderr = print
Yes = True
No = False
def debug(message):
pass
# ---------------------------------------------------------------------------------------------
# [Parameters]
# - none
# ---------------------------------------------------------------------------------------------
# [Classes]
@dataclass
class Example:
name: str
title: str
authors: str
license: str
created: str
modified: str
description: str
has_builder_mode: bool
has_algebra_mode: bool
image_files: List[str]
# ---------------------------------------------------------------------------------------------
# [Functions]
def wait_for_user():
input("Press Enter to continue...")
def remove_extension( file_name ):
output = file_name
if output.find('.') != -1:
output = file_name.split('.')[:-1][0]
return output
def do_index_rst(file_name, title, has_builder_mode=Yes, has_algebra_mode=No):
file_name = remove_extension( file_name )
modes = ""
if has_builder_mode:
modes += '|Builder| '
if has_algebra_mode:
modes += '|Algebra| '
index_rst = """
.. grid-item-card:: {title} {modes}
:img-top: assets/examples/thumbnail_{file_name}_01.{extension}
:link: examples-{file_name}
:link-type: ref
"""
output = index_rst.format( title=title, modes=modes, file_name=file_name, extension='png' )
return output
def do_code_rst(code_file, has_builder_mode=Yes, has_algebra_mode=No, start_after = "[Code]", end_before = "[End]"):
builder_mode = "Builder"
builder_algebra = "Algebra"
code_file = remove_extension( code_file )
code_template = """
.. dropdown:: |{mode}| Reference Implementation ({mode} Mode)
.. literalinclude:: ../examples/{file}.py
:start-after: {start_after}
:end-before: {end_before}
"""
output = ""
if has_builder_mode:
output += code_template.format( mode=builder_mode, file=code_file, start_after=start_after, end_before=end_before )
if has_algebra_mode:
if code_file.find("algebra") == -1:
code_file = code_file + "_algebra"
output += code_template.format( mode=builder_algebra, file=code_file, start_after=start_after, end_before=end_before )
return output
def do_images_rst(list_of_files):
output = "\n.. dropdown:: More Images\n\n"
for file in list_of_files:
output += f""" .. image:: assets/examples/{file}
:align: center\n\n"""
return output
def do_details_rst(file_name, title, description, image_files=['example_build123d_customizable_logo_01.png'], has_builder_mode=Yes, has_algebra_mode=No, start_after = "[Code]", end_before = "[End]"):
file_name = remove_extension( file_name )
code_file = file_name
output_core = """
.. _examples-{example_name}:
{title}
--------------------------------
.. image:: assets/examples/{image_file_01}
:align: center
\n\n{description}\n\n"""
output = output_core.format( example_name=file_name, title=title, description=description, image_file_01=image_files[0] )
if len(image_files) > 1:
output += do_images_rst(image_files[1:])
output += do_code_rst(code_file, has_builder_mode=has_builder_mode, has_algebra_mode=has_algebra_mode, start_after=start_after, end_before=end_before)
return output
def dict_to_dataclass(the_class, data):
debug(f"{the_class=}, {data=}")
try:
#field_types = {field.name: field.type for field in dc.fields(the_class)}
#return the_class(**{item: dict_to_dataclass(field_types[item], data[item]) for item in data})
return the_class(**data)
except:
stderr(f"Error: Could not convert dictionary to dataclass.")
return data
def get_data_from_docstring_in_file(file_path):
"""
Extracts the docstring from a Python file.
Parameters:
- file_path (str): Path to the Python file.
Returns:
- data: dict or Example
"""
with open(file_path, 'r', encoding='utf-8') as file:
tree = ast.parse(file.read(), filename=file_path)
# Find the first string literal, which is the docstring
docstring_node = next((node for node in ast.walk(tree) if isinstance(node, ast.Str)), None)
if docstring_node:
data = yaml.load(docstring_node.s)
data = dict_to_dataclass(Example, data)
return data
else:
return None
def get_data_from_yaml_file(file_path="examples.yaml"):
"""
Opens a yaml file and returns the content as a dictionary.
Parameters:
- file_path (str): Path to the yaml file.
Returns:
- dict: The content of the yaml file.
"""
log(pwd())
with open(file_path, 'r', encoding='utf-8') as file:
data = yaml.load(file.read())
data = dict_to_dataclass(Example, data)
return data
# ---------------------------------------------------------------------------------------------
# [Code]
if __name__ == "__main__":
yaml = YAML(typ='safe')
# Example usage
file_path = "../../../examples/benchy_v2024.py"
example = get_data_from_docstring_in_file(file_path)
stdout(f"Data from docstring in file '{file_path}':")
if type(example) == Example:
stdout(f"Example: {example.name}")
else:
stderr(f"Error: Could not convert dictionary to dataclass.")
index_rst = do_index_rst(example.name, title=example.title, has_builder_mode=example.has_builder_mode, has_algebra_mode=example.has_algebra_mode)
details_rst = do_details_rst(example.name, title=example.title, description=example.description, image_files=example.image_files, has_builder_mode=example.has_builder_mode, has_algebra_mode=example.has_algebra_mode)
copy(index_rst)
stdout('index copied to clipboard... please add it to the `examples_1.rst` above `NOTE 01`')
wait_for_user()
copy(details_rst)
stdout('details copied to clipboard... please add it to the `examples_1.rst` above `NOTE 02`')
wait_for_user()
stdout('now running sphinx via `make html`')
X([
'cd ../../',
'make html',
'cd assets/examples',
])
# [End]

View file

@ -0,0 +1,65 @@
# [Imports]
import argparse # see https://docs.python.org/3/library/argparse.html
from PIL import Image # see https://pillow.readthedocs.io/en/stable/
log = print
# [Parameters]
fixed_width = 400.0
file_name = 'example_{name}.png'
# [Code]
# - Setup Parser
parser = argparse.ArgumentParser(
prog='create_thumbnail_images.py',
description='convert a image to a 400x400px image keeping the aspect ratio of the input',
epilog='please see the code in `docs/assets/examples/create_thumbnail_images.py` for more details` for details.')
parser.add_argument('filename') # positional argument
parser.add_argument('-v', '--verbose',
action='store_true') # on/off flag
args = parser.parse_args()
# - create output file name
file_name = args.filename
file_thumbnail_name = f'thumbnail_' + file_name.replace('example_', '')
def make_square(im, min_size=256, fill_color=(0, 0, 0, 0)):
x, y = im.size
size = max(min_size, x, y)
new_im = Image.new('RGBA', (size, size), fill_color)
new_im.paste(im, (int((size - x) / 2), int((size - y) / 2)))
return new_im
# - Load the image
if args.verbose:
log(f'Loading image: {file_name}')
image = Image.open(file_name)
width = image.width
height = image.height
# - Calculate scaling
scale_factor = width / fixed_width
new_height = height / scale_factor
if args.verbose:
log(f'Image size: {width}x{height}')
log(f'Scale factor: {scale_factor}')
log(f'New size: {fixed_width}x{new_height}')
# - Resize the image
resized_image = image.resize((int(fixed_width), int(new_height)))
# - Make the image square
resized_image = make_square(resized_image,min_size=int(fixed_width))
# - Save
if args.verbose:
log(f'Saving image: {file_thumbnail_name}')
resized_image.save(file_thumbnail_name)
# [End]

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,31 @@
---
# Example file helper file
benchy:
name: "benchy.py"
title: "Low Poly Benchy"
authors: "Gumyr"
license: "http://www.apache.org/licenses/LICENSE-2.0"
created: "2023-07-09"
modified: "2024-01-09"
file_name: "benchy"
description: >
STL import and edit example
The Benchy examples shows hot to import a STL model as a `Solid` object and change it.
> *Attribution:*
> The low-poly-benchy used in this example is by `reddaugherty`, see
> https://www.printables.com/model/151134-low-poly-benchy.
.. dropdown:: Info
- uses file `low_poly_benchy.stl`
- uses `class Mesher`
- uses `group_by` and `sort_by`
- uses `make_polygon`
- uses `split`
has_builder_mode: true
has_algebra_mode: false
image_files:
- "example_benchy_01.png"
- "example_benchy_02.png"

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB