├── pinout
├── __init__.py
├── components
│ ├── __init__.py
│ ├── text.py
│ ├── legend.py
│ ├── annotation.py
│ ├── layout.py
│ └── pinlabel.py
├── resources
│ ├── pinout_kicad_example.zip
│ ├── quick_start
│ │ ├── hardware.png
│ │ ├── data.py
│ │ ├── styles.css
│ │ └── pinout_diagram.py
│ └── config.py
├── templates
│ ├── path.svg
│ ├── text.svg
│ ├── circle.svg
│ ├── group.svg
│ ├── use.svg
│ ├── style.svg
│ ├── rect.svg
│ ├── component_common.svg
│ ├── clippath.svg
│ ├── image.svg
│ ├── textblock.svg
│ ├── svg.svg
│ ├── kicad_footprint_lib
│ │ ├── Origin.j2
│ │ ├── Annotation.j2
│ │ └── PinLabel.j2
│ ├── label.svg
│ └── stylesheet.j2
├── templates.py
├── style_tools.py
└── config.py
├── docs
├── requirements.txt
├── _static
│ ├── kicad_export.png
│ ├── huzzah32_pinout.png
│ ├── kicad_screenshot.png
│ ├── label_dimensions.png
│ ├── annotations_custom.png
│ ├── annotations_enhanced.png
│ ├── annotations_simple.png
│ ├── demo_pinout_diagram.png
│ ├── quick_start_pinout_diagram.png
│ ├── quick_start_measurements_pins.png
│ ├── quick_start_measurements_scale.png
│ └── quick_start_measurements_labels.png
├── pages
│ ├── modules.rst
│ ├── text.rst
│ ├── resources.rst
│ ├── legend.rst
│ ├── config.rst
│ ├── leaderline.rst
│ ├── annotation.rst
│ ├── layout.rst
│ ├── manager.rst
│ ├── install.rst
│ ├── customise.rst
│ ├── pinlabel.rst
│ └── integrated_circuits.rst
├── index.rst
├── Makefile
├── make.bat
└── conf.py
├── tests
├── resources
│ ├── __init__.py
│ ├── 200x200.png
│ ├── styles.css
│ ├── diagram_export.py
│ ├── diagram_export.svg
│ └── diagram_image.py
└── test_samples.py
├── samples
├── full_sample
│ ├── __init__.py
│ ├── hardware.png
│ ├── components.py
│ ├── styles.css
│ ├── full_sample_data.py
│ ├── styles_auto.css
│ └── pinout_diagram.py
├── panel_layout
│ ├── __init__.py
│ ├── output
│ │ └── populated_layout.png
│ ├── styles.css
│ ├── auto_styles.css
│ ├── readme.md
│ ├── populated_layout.py
│ └── panel_layout.py
├── arduino
│ ├── arduino
│ │ ├── __init__.py
│ │ ├── common
│ │ │ ├── __init__.py
│ │ │ ├── patterns.xml
│ │ │ ├── preprocessor.py
│ │ │ ├── styles.css
│ │ │ └── arduino_components.py
│ │ ├── rp2040
│ │ │ ├── __init__.py
│ │ │ ├── hardware.png
│ │ │ ├── arduino_nano_rp2040_connect.py
│ │ │ └── rp2040_data.py
│ │ └── uno
│ │ │ ├── __init__.py
│ │ │ ├── hardware.png
│ │ │ ├── delete
│ │ │ ├── patterns.xml
│ │ │ ├── arduino_components.py
│ │ │ └── styles.css
│ │ │ ├── arduino_uno.py
│ │ │ └── uno_data.py
│ └── arduino_notes.md
├── clip_path
│ ├── hardware_18pin.png
│ ├── styles.css
│ └── pinout_diagram.py
├── pci-express
│ ├── pci-express_data.xlsx
│ ├── pinout_x1.py
│ ├── pci_data.py
│ ├── pci-express_x1.svg
│ └── autostyles.css
├── section_pullout
│ ├── hardware_18pin.png
│ ├── section_pullout_data.py
│ ├── styles.css
│ └── pinout_diagram.py
├── teensy_4.0
│ ├── styles.scss
│ ├── styles.css
│ ├── teensy_4_data.py
│ └── pinout_diagram.py
└── attiny85
│ ├── attiny_styles.css
│ └── attiny85.py
├── MANIFEST.in
├── pinout.toml
├── .readthedocs.yaml
├── setup.py
├── LICENSE
├── README.md
└── .gitignore
/pinout/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Jinja2
--------------------------------------------------------------------------------
/tests/resources/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pinout/components/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/full_sample/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/panel_layout/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/common/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/rp2040/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include pinout/templates *
2 | recursive-include pinout/resources *
--------------------------------------------------------------------------------
/tests/resources/200x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/tests/resources/200x200.png
--------------------------------------------------------------------------------
/docs/_static/kicad_export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/kicad_export.png
--------------------------------------------------------------------------------
/docs/_static/huzzah32_pinout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/huzzah32_pinout.png
--------------------------------------------------------------------------------
/samples/full_sample/hardware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/full_sample/hardware.png
--------------------------------------------------------------------------------
/docs/_static/kicad_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/kicad_screenshot.png
--------------------------------------------------------------------------------
/docs/_static/label_dimensions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/label_dimensions.png
--------------------------------------------------------------------------------
/docs/_static/annotations_custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/annotations_custom.png
--------------------------------------------------------------------------------
/docs/_static/annotations_enhanced.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/annotations_enhanced.png
--------------------------------------------------------------------------------
/docs/_static/annotations_simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/annotations_simple.png
--------------------------------------------------------------------------------
/docs/_static/demo_pinout_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/demo_pinout_diagram.png
--------------------------------------------------------------------------------
/samples/clip_path/hardware_18pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/clip_path/hardware_18pin.png
--------------------------------------------------------------------------------
/pinout/resources/pinout_kicad_example.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/pinout/resources/pinout_kicad_example.zip
--------------------------------------------------------------------------------
/pinout/resources/quick_start/hardware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/pinout/resources/quick_start/hardware.png
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/hardware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/arduino/arduino/uno/hardware.png
--------------------------------------------------------------------------------
/samples/pci-express/pci-express_data.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/pci-express/pci-express_data.xlsx
--------------------------------------------------------------------------------
/samples/section_pullout/hardware_18pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/section_pullout/hardware_18pin.png
--------------------------------------------------------------------------------
/docs/_static/quick_start_pinout_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/quick_start_pinout_diagram.png
--------------------------------------------------------------------------------
/pinout.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools>=42",
4 | "wheel"
5 | ]
6 | build-backend = "setuptools.build_meta"
--------------------------------------------------------------------------------
/samples/arduino/arduino/rp2040/hardware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/arduino/arduino/rp2040/hardware.png
--------------------------------------------------------------------------------
/docs/_static/quick_start_measurements_pins.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/quick_start_measurements_pins.png
--------------------------------------------------------------------------------
/docs/_static/quick_start_measurements_scale.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/quick_start_measurements_scale.png
--------------------------------------------------------------------------------
/docs/_static/quick_start_measurements_labels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/docs/_static/quick_start_measurements_labels.png
--------------------------------------------------------------------------------
/samples/panel_layout/output/populated_layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j0ono0/pinout/HEAD/samples/panel_layout/output/populated_layout.png
--------------------------------------------------------------------------------
/tests/resources/styles.css:
--------------------------------------------------------------------------------
1 |
2 | text {
3 | font-family: Verdana, Georgia, sans-serif;
4 | font-size: 14px;
5 | font-weight: normal;
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/pinout/templates/path.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/pinout/templates/text.svg:
--------------------------------------------------------------------------------
1 |
7 | {{ text.content }}
8 |
9 |
--------------------------------------------------------------------------------
/samples/panel_layout/styles.css:
--------------------------------------------------------------------------------
1 | .panel--banner .panel__inner,
2 | .panel--footer .panel__inner{
3 | fill: #333;
4 | }
5 |
6 | .panel--detail .panel__inner{
7 | fill: #ededed;
8 | }
--------------------------------------------------------------------------------
/pinout/templates/circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pinout/templates/group.svg:
--------------------------------------------------------------------------------
1 |
6 | {% block content %}
7 | {{ group.render_children()|safe }}
8 | {% endblock %}
9 |
--------------------------------------------------------------------------------
/pinout/templates/use.svg:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/panel_layout/auto_styles.css:
--------------------------------------------------------------------------------
1 |
2 | text {
3 | font-family: Verdana, Georgia, sans-serif;
4 | font-size: 14px;
5 | font-weight: normal;
6 | }
7 |
8 | .panel__inner {
9 | fill: #fff;
10 | }
11 | .panel__outer {
12 | fill: #333;
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/tests/resources/diagram_export.py:
--------------------------------------------------------------------------------
1 | from typing import Text
2 | from pinout.components.layout import Diagram
3 | from pinout.components.text import TextBlock
4 |
5 | diagram = Diagram(800, 400, tag="pinout")
6 | diagram.add_stylesheet("styles.css")
7 | diagram.add(TextBlock("Pinout export test.", x=100, y=100))
8 |
--------------------------------------------------------------------------------
/pinout/templates/style.svg:
--------------------------------------------------------------------------------
1 | {% if stylesheet %}
2 |
3 | {% else %}
4 |
9 | {% endif %}
--------------------------------------------------------------------------------
/docs/pages/modules.rst:
--------------------------------------------------------------------------------
1 | .. _modules:
2 |
3 | Modules
4 | =======
5 |
6 |
7 | .. toctree::
8 | :maxdepth: 2
9 | :caption: Contents:
10 |
11 | manager
12 | config
13 | core
14 | layout
15 | pinlabel
16 | leaderline
17 | annotation
18 | legend
19 | text
20 | integrated_circuits
21 |
--------------------------------------------------------------------------------
/pinout/templates/rect.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pinout/templates.py:
--------------------------------------------------------------------------------
1 | from jinja2 import Environment, PackageLoader, select_autoescape
2 |
3 |
4 | env = Environment(
5 | loader=PackageLoader("pinout", "templates"),
6 | autoescape=select_autoescape(["html", "xml"]),
7 | trim_blocks=True,
8 | lstrip_blocks=True,
9 | )
10 |
11 |
12 | def get(template_name):
13 | return env.get_template(template_name)
14 |
--------------------------------------------------------------------------------
/pinout/templates/component_common.svg:
--------------------------------------------------------------------------------
1 | {% if params.tag %}
2 | class="{{ params.tag }}"
3 | {% endif %}
4 | {% if params.clip %}
5 | clip-path="url(#{{ params.clip.id }})"
6 | {% endif %}
7 | transform="
8 | translate({{ params.x }} {{ params.y }})
9 | scale({{ params.scale.x }} {{ params.scale.y }})
10 | rotate({{ params.rotate }})
11 | "
12 | id="{{ params.id }}"
13 | clipPathUnits="userSpaceOnUse"
14 | shape-rendering="geometricPrecision"
15 |
--------------------------------------------------------------------------------
/pinout/templates/clippath.svg:
--------------------------------------------------------------------------------
1 |
16 | {{ path.render_children() }}
17 |
--------------------------------------------------------------------------------
/pinout/templates/image.svg:
--------------------------------------------------------------------------------
1 | {% if image.svg_data is not none %}
2 |
7 | {{ image.svg_data }}
8 |
9 | {% else %}
10 |
20 | {% endif %}
--------------------------------------------------------------------------------
/samples/clip_path/styles.css:
--------------------------------------------------------------------------------
1 |
2 | text {
3 | font-family: Verdana, Georgia, sans-serif;
4 | font-size: 14px;
5 | font-weight: normal;
6 | }
7 |
8 | .panel__inner {
9 | fill: #fff;
10 | }
11 | .panel__outer {
12 | fill: #333;
13 | }
14 |
15 | .layout .h1{
16 | font-size: 26px;
17 | font-weight: bold;
18 | }
19 | .layout .strong{
20 | font-weight: bold;
21 | }
22 | .panel--info .panel__inner{
23 | fill: #ededed;
24 | }
25 |
26 | .box01{
27 | fill:rgb(198, 220, 233);
28 | }
29 |
30 | .opacity_40 {
31 | opacity: 0.40;
32 | }
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yaml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 1
7 |
8 | # Build documentation in the docs/ directory with Sphinx
9 | sphinx:
10 | builder: html
11 | configuration: docs/conf.py
12 |
13 | # Optionally build your docs in additional formats such as PDF
14 | formats:
15 | - epub
16 | - pdf
17 |
18 | # Optionally set the version of Python and requirements required to build your docs
19 | python:
20 | version: 3.6
21 | install:
22 | - requirements: docs/requirements.txt
--------------------------------------------------------------------------------
/pinout/templates/textblock.svg:
--------------------------------------------------------------------------------
1 |
6 |
7 | {% for content in text_content %}
8 |
18 | {{ content }}
19 |
20 | {% endfor %}
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/samples/full_sample/components.py:
--------------------------------------------------------------------------------
1 | # User defined components
2 |
3 | from pinout.components.pinlabel import PinLabelBody
4 | from pinout import core
5 |
6 |
7 | class LabelIn(PinLabelBody):
8 | def render(self):
9 | skew = 3
10 | body = core.Path(
11 | path_definition=" ".join(
12 | [
13 | f"M {self.x + skew} {self.y - self.height/2}",
14 | f"l {self.width} 0",
15 | f"l {-skew*2} {self.height}",
16 | f"{-self.width} 0",
17 | "z",
18 | ]
19 | )
20 | )
21 | body.add_tag("label__body")
22 | return body.render()
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. pinout documentation master file, created by
2 | sphinx-quickstart on Tue Mar 16 00:16:18 2021.
3 |
4 | **pinout**
5 | ==========
6 |
7 | SVG diagram creation from Python code - **pinout** provides an easy method to create pin-out diagrams for electronic hardware.
8 |
9 | .. figure:: _static/demo_pinout_diagram.*
10 |
11 | .. toctree::
12 | :maxdepth: 2
13 | :caption: Contents:
14 |
15 | pages/install
16 | pages/tutorial
17 | pages/KiCad_integration
18 | pages/modules
19 | pages/customise
20 | pages/resources
21 |
22 |
23 |
24 | Indices and tables
25 | ==================
26 |
27 | * :ref:`genindex`
28 | * :ref:`modindex`
29 | * :ref:`search`
30 |
--------------------------------------------------------------------------------
/pinout/templates/svg.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/common/patterns.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/delete/patterns.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/panel_layout/readme.md:
--------------------------------------------------------------------------------
1 | # Example: Panel layout
2 |
3 | The Diagram and Panel classes can be directly used to create custom layouts or build upon diagram sub-classes.
4 |
5 | Dimensions and position of each panel can be manually calculated and entered. Alternatively, as done in this example, many of these details reference other panels. This setup allows for easier layout adjustments. As critical layout dimensions (*panel_banner.height*, *panel_main.width* and *panel_main.height*) are altered other panels automatically resize to fit.
6 |
7 | ## Output
8 |
9 | Now the layout has been completed further content can in added to the Panel instances creating a finished diagram featuring multiple sections (output from 'populated_layout.py').
10 |
11 | 
12 |
--------------------------------------------------------------------------------
/docs/pages/text.rst:
--------------------------------------------------------------------------------
1 | Text
2 | ====
3 |
4 | .. currentmodule:: pinout.components.text
5 |
6 | TextBlock
7 | ---------
8 |
9 | .. autoclass:: TextBlock
10 | :show-inheritance:
11 |
12 | The TextBlock accepts either a string or list for content. Each list entry is presented as a line of text. Where a string is provided, it is converted to a list by splitting on new-line characters ('\\n') and stripping whitespace from start and end of each line created.
13 |
14 | .. note::
15 |
16 | *pinout* cannot detect text character size! Consequently care should be taken to ensure text does not render outside expected boundaries.
17 |
18 | :param content: Text to be displayed
19 | :type content: String or List
20 | :param line_height: Distance between lines, defaults to None
21 | :type line_height: int, optional
--------------------------------------------------------------------------------
/docs/pages/resources.rst:
--------------------------------------------------------------------------------
1 | Resources
2 | =========
3 |
4 | Every *pinout* diagram has the fundamental requirement of an image to enhance with graphical additions. This prerequisite can be sizable barrier to creating the clearest diagram possible.
5 |
6 | During *pinout* development several image create methods have been investigated. Community members have also reached out and generously provided feedback and further options to tryout.
7 |
8 | Documented here is a list of what was tried during *pinout* development and some community input (some tried, some on my list to try out). The intent is to revisit all option and write-up some reviews and process notes.
9 |
10 | + Export from KiCad
11 | + Create from scratch
12 | + InkScape
13 | + Illustrator
14 | + With exported elements from KiCad
15 | + Photograph
16 | + https://github.com/yaqwsx/PcbDraw
17 | + fritzing
18 |
--------------------------------------------------------------------------------
/pinout/templates/kicad_footprint_lib/Origin.j2:
--------------------------------------------------------------------------------
1 | ({{ component_type }} "Origin"
2 | {% if version >= 6 %}
3 | (version {{ timestamp }}) (generator pinout)
4 | {% endif %}
5 | (layer "{{ layer }}")
6 | {% if version >= 6 %}
7 | (tedit 0)
8 | {%endif%}
9 | (descr "Set origin coordinates for pinout")
10 | (attr {% if version >= 6 %}exclude_from_pos_files exclude_from_bom{%else%}virtual{%endif%})
11 | (fp_text reference "pinout_origin" (at 1.5 1.5) (layer "{{ layer }}") hide
12 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
13 | )
14 | (fp_text value "Pinout Origin: Place at image top left." (at 1.5 3.5) (layer "{{ layer }}")
15 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
16 | )
17 | (fp_line (start 0.1 0.1) (end 4 0.1) (layer "{{ layer }}") (width 0.2))
18 | (fp_line (start 0.1 0.1) (end 0.1 4) (layer "{{ layer }}") (width 0.2))
19 | )
20 |
--------------------------------------------------------------------------------
/samples/full_sample/styles.css:
--------------------------------------------------------------------------------
1 | .comments__{
2 | /* NOTE: comments in a css rule to work around an InkScape bug! */
3 | /*
4 |
5 | Just for fun css for this diagram has been auto-generated, then enhanced with this second file.
6 | A new auto-generated CSS file can be created from the command-line:
7 | >>> py -m pinout.manager -o --css pinout_diagram styles_auto.css
8 |
9 | */
10 |
11 | }
12 |
13 |
14 | text {
15 | font-family: Verdana, Georgia, sans-serif;
16 | font-size: 14px;
17 | font-weight: 300;
18 | dominant-baseline: auto;
19 | }
20 | .h1 {
21 | font-size: 30px;
22 | font-weight: bold;
23 | font-style: italic;
24 | }
25 | .italic{
26 | font-style: italic;
27 | }
28 | .strong{
29 | font-weight: bold;
30 | }
31 | .panel__title .panel__inner{
32 | fill: rgb(253, 203, 36);
33 | font-weight: bold;
34 | font-style: italic;
35 | }
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/samples/panel_layout/populated_layout.py:
--------------------------------------------------------------------------------
1 | #
2 | # Example: Inserting content into 'panel_layout'
3 | #
4 | # export this sample via the command line:
5 | # >>> py -m pinout.manager --export populated_layout.py output/populated_layout.svg
6 | #
7 |
8 | from pinout.components.text import TextBlock
9 | import panel_layout as layout
10 |
11 | # pinout expects to see 'diagram' here when exporting
12 | from panel_layout import diagram
13 |
14 | # All of the content panels are now available for content to be added
15 | layout.panel_banner.add(TextBlock(content="Banner", x=20, y=40))
16 | layout.panel_main.add(TextBlock(content="Main", x=20, y=40))
17 | layout.panel_detail_01.add(TextBlock(content="Detail 01", x=20, y=40))
18 | layout.panel_detail_02.add(TextBlock(content="Detail 02", x=20, y=40))
19 | layout.panel_detail_03.add(TextBlock(content="Detail 03", x=20, y=40))
20 | layout.panel_footer.add(TextBlock(content="Footer", x=20, y=40))
21 |
--------------------------------------------------------------------------------
/pinout/templates/label.svg:
--------------------------------------------------------------------------------
1 |
8 |
18 |
31 | {{ text_content }}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/resources/diagram_export.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/arduino/arduino_notes.md:
--------------------------------------------------------------------------------
1 | # Arduino pinout
2 |
3 | The Arduino samples endeavour to maximise common components and styles across different diagrams.
4 | This has been done by converting files into a Python package with repeated resources grouped into a 'common' folder.
5 | Consequently some aspects of the code deviate from the official docs:
6 |
7 | + Paths are relative to the package
8 | + Use of a preprocessing function to better seperate data and configuration
9 |
10 | There are likely to be alternative ways to organise pinout to cater for mulitple related diagrams - use this example as a launching point for ideas rather than a best-practice example.
11 |
12 | Build Uno diagram, open command-line at the location of this file and enter:
13 | ```py -m pinout.manager --export arduino/uno/arduino_uno.py pinout_arduino_uno_rev3.svg -o```
14 |
15 |
16 | Build RP2024 diagram, open command-line at the location of this file and enter:
17 | ```py -m pinout.manager --export arduino/rp2040/arduino_nano_rp2040_connect.py pinout_arduino_nano_rp2040_connect.svg -o```
18 |
19 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r", encoding="utf-8") as fh:
4 | long_description = fh.read()
5 |
6 |
7 | setuptools.setup(
8 | name="pinout",
9 | version="0.0.20",
10 | author="John Newall",
11 | author_email="john@johnnewall.com",
12 | description="Generate graphical pinout references for electronic hardware.",
13 | license="MIT",
14 | long_description=long_description,
15 | long_description_content_type="text/markdown",
16 | url="https://github.com/j0ono0/pinout",
17 | project_urls={
18 | "Bug Tracker": "https://github.com/j0ono0/pinout/issues",
19 | },
20 | classifiers=[
21 | "Development Status :: 3 - Alpha",
22 | "Programming Language :: Python :: 3",
23 | "License :: OSI Approved :: MIT License",
24 | "Operating System :: OS Independent",
25 | ],
26 | packages=setuptools.find_packages(include=["pinout", "pinout.*"]),
27 | python_requires=">=3.6",
28 | install_requires=["Jinja2", "Pillow"],
29 | include_package_data=True,
30 | )
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 John Newall (aka j0ono0)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/pinout/templates/kicad_footprint_lib/Annotation.j2:
--------------------------------------------------------------------------------
1 | ({{ component_type }} "Annotation"
2 | {% if version >= 6 %}
3 | (version {{ timestamp }}) (generator pinout)
4 | {% endif %}
5 | (layer "{{ layer }}")
6 | {% if version >= 6 %}
7 | (tedit 0)
8 | {% endif %}
9 | (descr "Add an annotation")
10 | (attr {% if version >= 6 %}exclude_from_pos_files exclude_from_bom{%else%}virtual{%endif%})
11 | (fp_text reference "pinout_annotation" (at 9.525 6.985) (layer "{{ layer }}") {% if annotation.hide_fp_text_reference %}hide{% endif %}
12 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
13 | )
14 | (fp_text value "Annotation_content_here" (at 23.495 6.985 unlocked) (layer "{{ layer }}")
15 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
16 | )
17 | (fp_text user "vh" (at 9.525 8.89) (layer "{{ layer }}") {% if annotation.hide_fp_text_user %}hide{% endif %}
18 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
19 | )
20 | (fp_line (start 1 0) (end -1 0) (layer "{{ layer }}") (width 0.12))
21 | (fp_line (start 0 -1) (end 0 1) (layer "{{ layer }}") (width 0.12))
22 | (fp_circle (center 0 0) (end 0 -0.635) (layer "{{ layer }}") (width 0.12){% if version >= 6 %} (fill none){% endif %} )
23 | )
24 |
--------------------------------------------------------------------------------
/pinout/templates/kicad_footprint_lib/PinLabel.j2:
--------------------------------------------------------------------------------
1 | ({{ component_type }} "PinLabel"
2 | {% if version >= 6 %}
3 | (version {{ timestamp }}) (generator pinout)
4 | {% endif %}
5 | (layer "{{ layer }}")
6 | {% if version >= 6 %}
7 | (tedit 0)
8 | {% endif %}
9 | (descr "Add one or more labels in a row to a single location.")
10 | (attr {% if version >= 6 %}exclude_from_pos_files exclude_from_bom{%else%}virtual{%endif%})
11 | (fp_text reference "pinout_pinlabel" (at 9.525 7.62) (layer "{{ layer }}") {% if pinlabel.hide_fp_text_reference %}hide{% endif %}
12 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
13 |
14 | )
15 | (fp_text value "LBL {{tag}}" (at 21.59 7.62) (layer "{{ layer }}")
16 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
17 |
18 | )
19 | (fp_text user "hh" (at 9.525 9.525) (layer "{{ layer }}") {% if pinlabel.hide_fp_text_user %}hide{% endif %}
20 | (effects (font (size 1 1) (thickness 0.15)) (justify left))
21 |
22 | )
23 | (fp_line (start 1 0) (end -1 0) (layer "{{ layer }}") (width 0.12))
24 | (fp_line (start 0 -1) (end 0 1) (layer "{{ layer }}") (width 0.12))
25 | (fp_circle (center 0 0) (end 0 -0.5) (layer "{{ layer }}") (width 0.12) {% if version >= 6 %} (fill none){%endif%})
26 | )
27 |
28 |
--------------------------------------------------------------------------------
/pinout/components/text.py:
--------------------------------------------------------------------------------
1 | from pinout import core, config
2 | from pinout.components.layout import Group
3 |
4 |
5 | class TextBlock(Group):
6 | """Multiline text component."""
7 |
8 | def __init__(self, content, line_height=None, **kwargs):
9 | # initialise module attrs
10 | self._scale = core.Coords(*kwargs.pop("scale", (1, 1)))
11 | self._content = None
12 | super().__init__(**kwargs)
13 | self.update_config(config.textblock)
14 | self.line_height = line_height or self.config["line_height"]
15 | self.add_tag(self.config["tag"])
16 | self.content = content
17 |
18 | @property
19 | def content(self):
20 | return self._content
21 |
22 | @content.setter
23 | def content(self, content):
24 | # Convert string to list
25 | if isinstance(content, str):
26 | content = [line.strip() for line in content.split("\n")]
27 | self._content = content
28 |
29 | def render(self):
30 | self.add_tag(self.config["tag"])
31 | y = 0
32 | for text in self.content:
33 | self.add(
34 | core.Text(content=text, x=0, y=y, scale=self._scale, **self.config)
35 | )
36 | y += self.line_height * self._scale.y
37 |
38 | return super().render()
39 |
--------------------------------------------------------------------------------
/docs/pages/legend.rst:
--------------------------------------------------------------------------------
1 | Legend
2 | ======
3 |
4 | .. currentmodule:: pinout.components.legend
5 |
6 | Legend
7 | ------
8 |
9 | .. autoclass:: Legend
10 |
11 | *Note*: *pinout* does not calculate text widths. A manually provided width may be required to ensure text remains enclosed within the legend.
12 |
13 | :param data: [description]
14 | :type data: [type]
15 | :param max_height: [description], defaults to None
16 | :type max_height: [type], optional
17 |
18 |
19 | Swatch
20 | ------
21 |
22 | .. autoclass:: Swatch
23 |
24 | :param width: Width of swatch, defaults to None
25 | :type width: int, optional
26 | :param height: Height of swatch, defaults to None
27 | :type height: int, optional
28 |
29 |
30 | Entry
31 | -----
32 |
33 | .. autoclass:: LegendEntry
34 |
35 | The swatch attribute accepts either a dictionary of Swatch attributes or a Swatch instance. Swatch styling (ie filling with color) is done via CSS and should reference the LegendEntry class(es).
36 |
37 | :param content: Text displayed in entry
38 | :type content: [type]
39 | :param width: Width of entry, defaults to None
40 | :type width: int, optional
41 | :param height: height of entry, defaults to None
42 | :type height: int, optional
43 | :param swatch: Graphical icon included in entry, defaults to None
44 | :type swatch: dict or Swatch, optional
45 |
46 |
--------------------------------------------------------------------------------
/pinout/resources/quick_start/data.py:
--------------------------------------------------------------------------------
1 | legend = [
2 | ("Analog", "analog"),
3 | ("Communication", "comms"),
4 | ("Ground", "gnd"),
5 | ("GPIO", "gpio"),
6 | ("Touch", "touch"),
7 | ("Power", "pwr"),
8 | ("PWM", "pwm"),
9 | ]
10 |
11 | # Pinlabels
12 |
13 | left_header = [
14 | [
15 | ("0", "gpio"),
16 | ("A0", "analog"),
17 | ("MISO", "comms"),
18 | ],
19 | [
20 | ("1", "gpio"),
21 | ("MOSI", "comms", {"body": {"x": 92}}),
22 | ],
23 | [
24 | ("2", "gpio"),
25 | ("A1", "analog"),
26 | ("SCLK", "comms"),
27 | ],
28 | ]
29 |
30 | lower_header = [
31 | [
32 | ("3", "gpio"),
33 | ("PWM", "pwm"),
34 | ],
35 | [
36 | ("4", "gpio"),
37 | ("A2", "analog"),
38 | ("TOUCH", "touch"),
39 | ],
40 | [
41 | ("5", "gpio"),
42 | ("A3", "analog"),
43 | ],
44 | ]
45 |
46 | right_header = [
47 | [
48 | ("Vcc", "pwr"),
49 | ],
50 | [
51 | ("GND", "gnd"),
52 | ],
53 | [
54 | ("6", "gpio"),
55 | ("A4", "analog"),
56 | ("TOUCH", "touch"),
57 | ],
58 | ]
59 |
60 |
61 | # Text
62 |
63 | title = "Pinout Quick start"
64 |
65 | description = """Python tool kit to assist with
66 | documentation of electronic hardware.
67 | More info at pinout.readthedocs.io"""
68 |
--------------------------------------------------------------------------------
/tests/resources/diagram_image.py:
--------------------------------------------------------------------------------
1 | # Build with:
2 | # py -m pinout.manager -e diagram_image.py diagram_image.svg -o
3 |
4 | from pinout.core import Image
5 | from pinout.components.layout import Diagram
6 |
7 |
8 | diagram = Diagram(800, 800, tag="pinout")
9 |
10 | # PNG image
11 | # ---------
12 |
13 | # PNG def, linked
14 | png_def_linked = diagram.add_def(Image("200x200.png"))
15 |
16 | # PNG def, embedded
17 | png_def_embedded = diagram.add_def(Image("200x200.png", embed=True))
18 |
19 | # PNG linked
20 | diagram.add(Image("200x200.png", x=0, y=0))
21 |
22 | # PNG embedded
23 | diagram.add(Image("200x200.png", x=200, y=0, embed=True))
24 |
25 | # Referenced linked
26 | diagram.add(Image(png_def_linked, x=400, y=0))
27 |
28 | # Referenced embedded
29 | diagram.add(Image(png_def_embedded, x=600, y=0))
30 |
31 | # PNG linked, resized
32 | diagram.add(Image("200x200.png", x=400, y=400, width=400, height=400))
33 |
34 | # SVG image
35 | # ---------
36 |
37 |
38 | # SVG linked
39 | diagram.add(Image("200x200.svg", x=0, y=200))
40 |
41 | # SVG embedded
42 | diagram.add(Image("200x200.svg", x=200, y=200, width=200, height=200, embed=True))
43 |
44 | # Referenced linked
45 | svg_def_linked = diagram.add_def(Image("200x200.svg"))
46 | diagram.add(Image(svg_def_linked, x=400, y=200))
47 |
48 | # Referenced embedded
49 | svg_def_embedded = diagram.add_def(Image("200x200.svg", embed=True))
50 | diagram.add(Image(svg_def_embedded, x=600, y=200))
51 |
52 | # SVG linked rezised
53 | diagram.add(Image("200x200.svg", x=0, y=400, width=400, height=400))
54 |
--------------------------------------------------------------------------------
/docs/pages/config.rst:
--------------------------------------------------------------------------------
1 | .. _Config:
2 |
3 | Config
4 | ======
5 |
6 | Components with a graphical representation have a variety of configuration attributes that affect their appearance. These attributes can be modified at several location whilst scripting.
7 |
8 |
9 | Default values
10 | --------------
11 |
12 | These attributes are stored as Python dictionaries in the **config** module.
13 |
14 | A complete set of all default configurations can be duplicated for reference from the command line::
15 |
16 | py -m pinout.manager --duplicate config
17 |
18 | # expected response:
19 | # >>> config.py duplicated.
20 |
21 | Amending the default configurations can be done by replacing or updating any of the dictionaries with plain Python::
22 |
23 | from pinout import config
24 | config.pinlabel["body"].update({"width": 120})
25 |
26 | # All pin-label bodies will now default to 120 wide
27 |
28 |
29 | Instance attributes
30 | -------------------
31 |
32 | PinLabels and Annotations accept a dictionary of configurations for some attributes. These values are used to update the default settings for that single instance. This is ideal when small alterations are required for a low number items::
33 |
34 | from pinout.core import Diagram
35 | from pinout.components.pinlabel import PinLabel
36 |
37 | diagram = Diagram(1200, 675, "pinout")
38 | diagram.add(
39 | PinLabel(
40 | x=30,
41 | y=30,
42 | tag="sm-label",
43 | body={"width": 40},
44 | )
45 | )
46 |
47 |
--------------------------------------------------------------------------------
/docs/pages/leaderline.rst:
--------------------------------------------------------------------------------
1 | Leaderlines
2 | ===========
3 |
4 | .. currentmodule:: pinout.components.leaderline
5 |
6 |
7 | Leaderline
8 | ----------
9 | .. autoclass:: Leaderline
10 | :show-inheritance:
11 |
12 | :param direction: 2 letter code, defaults to "hh"
13 | :type direction: str, optional
14 |
15 | The leaderline connects an origin and destination point. Route taken is controlled with a *direction* argument where the first character dictates the start direction and the second character the end direction:
16 |
17 | - **vh**: vertical , horizontal
18 | - **hv**: horizontal , vertical
19 | - **hh**: horizontal , horizontal
20 | - **vv**: vertical , vertical
21 |
22 |
23 | .. automethod:: Leaderline.end_points
24 |
25 | The end_point method takes two components as arguments and returns coordinates that are aligned with the centre coordinates of the relevant side.
26 |
27 | :param origin: origin component
28 | :type origin: component with width and height attributes and bounding_coords method
29 | :param destination: destination component
30 | :type destination: component with width and height attributes and bounding_coords method
31 | :return: coordinates of start and end points
32 | :rtype: Tuple ((ox, oy), (dx, dy))
33 |
34 | Curved
35 | ------
36 | .. autoclass:: Curved
37 | :show-inheritance:
38 |
39 |
40 | Angled
41 | ------
42 | .. autoclass:: Angled
43 | :show-inheritance:
44 |
45 |
46 | Straight
47 | --------
48 | .. autoclass:: Straight
49 | :show-inheritance:
50 |
--------------------------------------------------------------------------------
/samples/pci-express/pinout_x1.py:
--------------------------------------------------------------------------------
1 | #####################################################
2 | #
3 | #
4 | # This sample is a WORK IN PROGRESS!
5 | #
6 | # ...It should work but might be a bit
7 | # rough around the edges.
8 | #
9 | #####################################################
10 |
11 | from pinout.components.layout import Diagram, Panel
12 | from pinout.core import Group, Image
13 | from pinout.components.pinlabel import PinLabelGroup
14 | from pinout import config
15 |
16 | # Python has multiple options for reading popular spreadsheet formats.
17 | # This example uses Pandas in conjunction with openpyxl.
18 | # Installation of these packages can be done via the command line.
19 | # (**If you are using a virtual environment, ensure it is activated):
20 | # >>> pip install pandas
21 | # >>> pip install openpyxl
22 |
23 | # Export:
24 | # >>> py -m pinout.manager -e pinout_x1.py pinout_x1.svg
25 |
26 | import pci_data as data
27 |
28 | # configuration customsations
29 | config.pinlabel["body"]["corner_radius"] = 0
30 | config.pinlabel["body"]["height"] = 28
31 |
32 | diagram = Diagram(1200, 675, "diagram")
33 | diagram.add_stylesheet("autostyles.css")
34 | content = diagram.add(Panel(width=1200, height=675, tag="panel__content"))
35 |
36 | graphic = content.add(Group(30, 10))
37 | graphic.add(Image("pci-express_x1.svg", width=94, height=264, embed=True, scale=(2, 2)))
38 | graphic.add(
39 | PinLabelGroup(
40 | x=80 * 2,
41 | y=64.5 * 2,
42 | pin_pitch=(0, 9 * 2),
43 | label_start=(100, -40),
44 | label_pitch=(0, 30),
45 | labels=data.get_from_xlsx("pci-express_data.xlsx")[0:11],
46 | )
47 | )
48 | graphic.add(
49 | PinLabelGroup(
50 | x=80 * 2,
51 | y=181 * 2,
52 | pin_pitch=(0, 9 * 2),
53 | label_start=(100, -181 * 2 + 64.5 * 2 + 30 * 10),
54 | label_pitch=(0, 30),
55 | labels=data.get_from_xlsx("pci-express_data.xlsx")[11:18],
56 | )
57 | )
58 |
--------------------------------------------------------------------------------
/samples/section_pullout/section_pullout_data.py:
--------------------------------------------------------------------------------
1 | legend = [
2 | ("Analog", "analog"),
3 | ("I2C", "i2c"),
4 | ("GPIO", "gpio"),
5 | ("Touch", "touch"),
6 | ("PWM", "pwm"),
7 | ("Power", "pwr"),
8 | ("Ground", "gnd"),
9 | ]
10 |
11 | # Pinlabels
12 |
13 | left_header = [
14 | [
15 | ("0", "gpio"),
16 | ("A0", "analog"),
17 | ("MISO", "comms"),
18 | ],
19 | [
20 | ("1", "gpio"),
21 | ("MOSI", "comms", {"body": {"x": 92}}),
22 | ],
23 | [
24 | ("2", "gpio"),
25 | ("A1", "analog"),
26 | ("SCLK", "comms"),
27 | ],
28 | [
29 | ("3", "gpio"),
30 | ("PWM", "pwm"),
31 | ],
32 | ]
33 |
34 | lower_header_out = [
35 | [
36 | ("VBAT", "pwr"),
37 | ],
38 | [
39 | ("3.3V", "pwr"),
40 | ],
41 | [
42 | ("GND", "gnd"),
43 | ],
44 | [
45 | ("AREF", "pwr"),
46 | ],
47 | ]
48 | lower_header_in = [
49 | [
50 | ("A2", "analog"),
51 | ("TOUCH", "touch"),
52 | ],
53 | [
54 | ("PWM", "pwm"),
55 | ],
56 | [
57 | ("6", "gpio"),
58 | ("SDA1", "i2c"),
59 | ],
60 | [
61 | ("A5", "analog"),
62 | ("SCL1", "i2c"),
63 | ],
64 | ]
65 |
66 | right_header = [
67 | [
68 | ("Vcc", "pwr"),
69 | ],
70 | [
71 | ("GND", "gnd"),
72 | ],
73 | [
74 | ("RESET", "reset"),
75 | ],
76 | [
77 | ("8", "gpio"),
78 | ("A6", "analog"),
79 | ],
80 | ]
81 |
82 |
83 | # Text
84 |
85 | title = "Pinout sample: Section pull-out"
86 |
87 | description = """Pinout is a Python tool kit to assist with documentation of electronic hardware.
88 | This sample demonstrates documenting a section of hardware.
89 | More info at pinout.readthedocs.io"""
90 |
91 | section_a_text = """Section A
92 | Detail of double row header."""
--------------------------------------------------------------------------------
/docs/pages/annotation.rst:
--------------------------------------------------------------------------------
1 | Annotations
2 | ===========
3 |
4 | .. currentmodule:: pinout.components.annotation
5 |
6 | Annotation
7 | ----------
8 |
9 | .. autoclass:: AnnotationLabel
10 | :show-inheritance:
11 |
12 | An alternative method to 'label' a diagram, suitable for highlighting hardware details.
13 |
14 | It is likely the body, leaderline, and target will all require customisation to best suit specific usages. Several methods of customisation are possible:
15 |
16 | **diagram-wide customisations**:
17 |
18 | - Over-ride dictionary settings in pinout.config.annotation
19 | - Over-ride default annotation body, leaderline, and target classes
20 |
21 | **instance specific customisations**:
22 |
23 | - Supply a dictionary of arguments to body, content, leaderline, and target attributes. These override config.annotation settings
24 | - provide an alternative component instance to body, content, leaderline, and target attributes.
25 |
26 | :param content: [description]
27 | :type content: [type]
28 | :param body: [description], defaults to None
29 | :type body: [type], optional
30 | :param leaderline: [description], defaults to None
31 | :type leaderline: [type], optional
32 | :param target: [description], defaults to None
33 | :type target: [type], optional
34 |
35 |
36 | Body
37 | ----
38 | .. autoclass:: Body
39 | :show-inheritance:
40 |
41 |
42 | Content
43 | -------
44 | .. autoclass:: Content
45 | :show-inheritance:
46 |
47 | Content can be provided as a string, list, dictionary, or component instance. Strings are presented as a single line. Entries of a list present as lines of text. If a dictionary is provided it updates the default config settings and expects the 'content' attribute to be a list.
48 |
49 |
50 | Leaderline
51 | ----------
52 |
53 | .. autoclass:: Leaderline
54 | :show-inheritance:
55 |
56 | Target
57 | ------
58 |
59 | .. autoclass:: Target
60 | :show-inheritance:
--------------------------------------------------------------------------------
/docs/pages/layout.rst:
--------------------------------------------------------------------------------
1 | Layout
2 | ======
3 |
4 | .. currentmodule:: pinout.components.layout
5 |
6 | Diagram
7 | --------
8 |
9 | .. autoclass:: Diagram
10 | :show-inheritance:
11 |
12 | :param width: width of diagram
13 | :type width: int
14 | :param height: height of diagram
15 | :type height: int
16 | :param tag: CSS class applied to diagram, defaults to None
17 | :type tag: string (must comply to CSS naming rules), optional
18 |
19 | .. automethod:: Diagram.add_stylesheet
20 |
21 | Pinout relies on cascading-style-sheet (CSS) rules to control presentation attributes of components.
22 |
23 | The path attribute is dependent on whether the styles are linked or embedded. When linked, the path is relative to the exported file. When embedded the path is relative to the diagram script file.
24 |
25 | :param path: Path to stylesheet file
26 | :type path: string
27 | :param embed: embed stylesheet in exported file, defaults to True
28 | :type embed: bool, optional
29 |
30 |
31 | .. automethod:: Diagram.render
32 |
33 | :return: SVG markup
34 | :rtype: string
35 |
36 | Panel
37 | -----
38 | .. autoclass:: Panel
39 | :show-inheritance:
40 |
41 | The basic building block to control layout (grouping and location) of components that make up a complete diagram document. The Panel component renders two rectangles - and outer and inner rectangle - behind all child components to assist with graphical styling.
42 |
43 | The inset attribute controls dimensions of the 'inner rectangle'. All children are aligned relative to the inset coordinate (x1, y1).
44 |
45 | The inner dimensions can be accessed via the properties ``Panel.inset_width`` and ``Panel.inset_height``.
46 |
47 | :param width: Width of component
48 | :type width: int
49 | :param height: Height of component
50 | :type height: int
51 | :param inset: Inset of inner dimensions, defaults to None
52 | :type inset: Tuple (x1, y1, x2, y2), optional
53 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/common/preprocessor.py:
--------------------------------------------------------------------------------
1 | # An attempt to keep style/config separate from content
2 | from copy import deepcopy
3 | from .arduino_components import PlbStart, PlbEnd, Plb
4 |
5 |
6 | # Generator function that applies pinlabel configurations to label data
7 | def pinlabel_preprocessor(pinlabel_set):
8 | for row in pinlabel_set:
9 |
10 | if len(row) == 1:
11 | # Single label row config
12 | row[0] = (
13 | *row[0],
14 | {"body": Plb(width=80, height=20, x=0, y=0, corner_radius=10)},
15 | )
16 | else:
17 | # Default label config
18 | row = [
19 | (name, tag, {"body": Plb(width=80, height=20, x=2, y=0)})
20 | for (name, tag) in row
21 | ]
22 |
23 | # Row start and end labels
24 | row[0] = (
25 | row[0][0],
26 | row[0][1],
27 | {"body": PlbStart(x=0, y=0, width=80, height=20)},
28 | )
29 | row[-1] = (
30 | row[-1][0],
31 | row[-1][1],
32 | {"body": PlbEnd(x=2, y=0, width=80, height=20)},
33 | )
34 |
35 | # Order dependent labels
36 | if row[0][1] == "digital" and row[1][1] == "analog":
37 | row[0] = (
38 | row[0][0],
39 | row[0][1],
40 | {"body": PlbStart(x=0, y=0, width=50, height=20)},
41 | )
42 | row[1] = (
43 | row[1][0],
44 | row[1][1],
45 | {"body": Plb(width=30, height=20, x=0, y=0)},
46 | )
47 |
48 | # Tag specific labels
49 | for i, label in enumerate(row):
50 | name, tag = label[:2]
51 | if "show-leader" in tag:
52 | row[i] = (
53 | name,
54 | tag,
55 | {"body": PlbEnd(x=84, y=0, width=80, height=20)},
56 | )
57 |
58 | yield row
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pinout
2 |
3 |   
4 |
5 | SVG diagram creation from Python code - **pinout** provides an easy method of creating pinout diagrams for electronic hardware.
6 |
7 |
8 |
9 |
10 |
11 | Please visit [pinout.readthedocs.io](https://pinout.readthedocs.io) for the full *quick start* tutorial and detailed documentation on all options provided by the *pinout* package.
12 |
13 | ## Quick start
14 |
15 | *pinout* can be easily installed with pip and provides some sample files that demonstrate key features.
16 |
17 | ### Install
18 |
19 | Using a virtual environment is recommended; Start by installing the *pinout* package. Either clone this repo and pip install it or install from PyPi:
20 | ```
21 | pip install pinout
22 |
23 | # Or upgrade to the latest version
24 | pip install --upgrade pinout
25 | ```
26 |
27 | ### Duplicate sample files
28 |
29 | A normal pinout diagram will include a hardware image, stylesheet, data file, and a Python script. Sample files are included with the package and can be duplicated for your use. Open a command line (with enabled virtual environment if you are using one) in the location you plan to work and enter the following:
30 | ```python
31 | py -m pinout.manager --duplicate quick_start
32 |
33 | # expected output:
34 | # >>> data.py duplicated.
35 | # >>> hardware.png duplicated.
36 | # >>> pinout_diagram.py duplicated.
37 | # >>> styles.css duplicated.
38 | ```
39 |
40 | Once you have these file a finished diagram can be generated from a command line `py -m pinout.manager --export pinout_diagram diagram.svg`. An SVG file is created and can be conveniently view in a browser.
41 |
42 | 
43 |
44 | For a detailed walk through *pinout_diagram.py* and more information on *pinout* please visit [pinout.readthedocs.io](https://pinout.readthedocs.io).
45 |
--------------------------------------------------------------------------------
/samples/pci-express/pci_data.py:
--------------------------------------------------------------------------------
1 | # Example of importing data from a spreadsheet
2 | #
3 | # Python has multiple options for reading popular spreadsheet formats.
4 | # This example uses Pandas in conjunction with openpyxl.
5 | # Installation of these packages can be done via the command line.
6 | # (**If you are using a virtual environment, ensure it is activated):
7 | # >>> pip install pandas
8 | # >>> pip install openpyxl
9 | import pandas as pd
10 |
11 |
12 | def assign_css(text):
13 | # Rather than alter then source data css classes can be
14 | # figured out and assigned here
15 | if text == "Ground":
16 | return "gnd"
17 | elif text in ["SMCLK", "SMDAT", "WAKE#", "PERST#", "CLKREQ#", "PWRBRK#"]:
18 | return "open-drain"
19 | elif text.startswith("+"):
20 | return "pwr"
21 | elif text.startswith("HSO") or text.startswith("REFCLK"):
22 | return "host-to-card"
23 | elif text.startswith("HSI") or text == "TDO":
24 | return "card-to-host"
25 | elif text.startswith("PRSNT"):
26 | return "sense-pin"
27 | return "reserved"
28 |
29 |
30 | def get_from_xlsx(filepath):
31 | # read the excel spreadsheet into a 'dataframe'
32 | df = pd.read_excel(filepath, engine="openpyxl")
33 |
34 | # Remove the Description column as we are not using it
35 | df.drop("Description", axis=1, inplace=True)
36 |
37 | # Remove the rows that have notes (rather than data).
38 | # Luckily, those rows all have 'None' in at least one column
39 | df.dropna(inplace=True)
40 |
41 | # Problem characters are easy to replace with Pandas.
42 | # In this instance a minus sign is causing grief.
43 | df["Side A"] = df["Side A"].str.replace("−", "−")
44 |
45 | # Parse the data for use with pinout
46 | data = df.values.tolist()
47 | data = [
48 | [
49 | (f"{i}", "pinid", {"body": {"width": 30}}),
50 | (f"{b}", assign_css(b)),
51 | (f"{a}", assign_css(a)),
52 | ]
53 | for (i, b, a) in data
54 | ]
55 |
56 | return data
57 |
58 |
59 | if __name__ == "__main__":
60 | get_from_xlsx("pci-express_data.xlsx")
61 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 |
16 | sys.path.insert(0, os.path.abspath("../../pinout/"))
17 |
18 |
19 | # -- Project information -----------------------------------------------------
20 |
21 | project = "pinout"
22 | copyright = "2021, John Newall"
23 | author = "John Newall"
24 |
25 | # The full version, including alpha/beta/rc tags
26 | release = "0.0.20"
27 |
28 |
29 | # -- General configuration ---------------------------------------------------
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = [
35 | "sphinx.ext.autodoc",
36 | ]
37 |
38 | # Add any paths that contain templates here, relative to this directory.
39 | templates_path = ["_templates"]
40 |
41 | # List of patterns, relative to source directory, that match files and
42 | # directories to ignore when looking for source files.
43 | # This pattern also affects html_static_path and html_extra_path.
44 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
45 |
46 |
47 | # -- Options for HTML output -------------------------------------------------
48 |
49 | # The theme to use for HTML and HTML Help pages. See the documentation for
50 | # a list of builtin themes.
51 | #
52 | html_theme = "sphinx_rtd_theme"
53 |
54 | # Add any paths that contain custom static files (such as style sheets) here,
55 | # relative to this directory. They are copied after the builtin static files,
56 | # so a file named "default.css" will overwrite the builtin "default.css".
57 | html_static_path = ["_static"]
58 |
--------------------------------------------------------------------------------
/pinout/resources/quick_start/styles.css:
--------------------------------------------------------------------------------
1 | text {
2 | font-family: Verdana, Georgia, sans-serif;
3 | font-size: 14px;
4 | font-weight: normal;
5 | }
6 |
7 | .pinlabel__leader{
8 | stroke-width: 2;
9 | fill: none;
10 | }
11 |
12 | .pinlabel__text{
13 | dominant-baseline: central;
14 | fill: #fff;
15 | font-weight: bold;
16 | stroke-width: 0;
17 | text-anchor: middle;
18 | }
19 |
20 | .pwr .pinlabel__body{
21 | fill: rgb(173, 0, 0);
22 | }
23 | .pwr .pinlabel__leader{
24 | stroke: rgb(173, 0, 0);
25 | }
26 | .pwr .swatch__body {
27 | fill: rgb(173, 0, 0);
28 | }
29 | .comms .pinlabel__body{
30 | fill: rgb(182, 69, 176);
31 | }
32 | .comms .pinlabel__leader{
33 | stroke: rgb(182, 69, 176);
34 | }
35 | .comms .swatch__body {
36 | fill: rgb(182, 69, 176);
37 | }
38 | .gpio .pinlabel__body{
39 | fill: rgb(160, 133, 26);
40 | }
41 | .gpio .pinlabel__leader{
42 | stroke: rgb(160, 133, 26);
43 | }
44 | .gpio .swatch__body {
45 | fill: rgb(160, 133, 26);
46 | }
47 | .analog .pinlabel__body{
48 | fill: rgb(32, 150, 165);
49 | }
50 | .analog .pinlabel__leader{
51 | stroke: rgb(32, 150, 165);
52 | }
53 | .analog .swatch__body {
54 | fill: rgb(32, 150, 165);
55 | }
56 | .gnd .pinlabel__body{
57 | fill: rgb(0, 0, 0);
58 | }
59 | .gnd .pinlabel__leader{
60 | stroke: rgb(0, 0, 0);
61 | }
62 | .gnd .swatch__body {
63 | fill: rgb(0, 0, 0);
64 | }
65 | .pwm .pinlabel__body{
66 | fill: rgb(151, 76, 23);
67 | }
68 | .pwm .pinlabel__leader{
69 | stroke: rgb(151, 76, 23);
70 | }
71 | .pwm .swatch__body {
72 | fill: rgb(151, 76, 23);
73 | }
74 | .touch .pinlabel__body{
75 | fill: rgb(230, 87, 10);
76 | }
77 | .touch .pinlabel__leader{
78 | stroke: rgb(230, 87, 10);
79 | }
80 | .touch .swatch__body {
81 | fill: rgb(230, 87, 10);
82 | }
83 |
84 | .panel__inner {
85 | fill: #fff;
86 | }
87 | .panel__outer {
88 | fill: #333;
89 | }
90 |
91 | .legendentry text {
92 | dominant-baseline: central;
93 | }
94 |
95 | .h1 {
96 | font-size: 26px;
97 | font-weight: bold;
98 | font-style: italic;
99 | }
100 | .italic{
101 | font-style: italic;
102 | }
103 | .strong{
104 | font-weight: bold;
105 | }
106 |
107 | .panel--info .panel__inner{
108 | fill: #f4f4f4;
109 | }
--------------------------------------------------------------------------------
/samples/full_sample/full_sample_data.py:
--------------------------------------------------------------------------------
1 | legend = [
2 | ("Analog", "analog"),
3 | ("Communication", "comms"),
4 | ("Ground", "gnd"),
5 | ("GPIO", "gpio"),
6 | ("Touch", "touch"),
7 | ("Power", "pwr"),
8 | ("PWM", "pwm"),
9 | ]
10 |
11 | # Pinlabels
12 |
13 | lhs = [
14 | [
15 | ("0", "gpio"),
16 | ("A0", "analog"),
17 | ("MISO", "comms"),
18 | ],
19 | [
20 | ("1", "gpio"),
21 | ("MOSI", "comms", {"body": {"x": 92}}),
22 | ],
23 | [
24 | ("2", "gpio"),
25 | ("A1", "analog"),
26 | ("SCLK", "comms"),
27 | ],
28 | [
29 | ("RESET", "pwr"),
30 | ],
31 | ]
32 |
33 | btm_lhs = [
34 | [
35 | ("3", "gpio"),
36 | ("A2", "analog"),
37 | ("PWM", "pwm"),
38 | ],
39 | [
40 | ("4", "gpio"),
41 | ("A3", "analog"),
42 | ],
43 | ]
44 |
45 | btm_rhs = [
46 | [
47 | ("6", "gpio"),
48 | ("PWM", "pwm"),
49 | ],
50 | [
51 | ("5", "gpio"),
52 | ("A4", "analog"),
53 | ("PWM", "pwm"),
54 | ],
55 | ]
56 | rhs = [
57 | [
58 | ("Vcc", "pwr"),
59 | ],
60 | [
61 | ("GND", "gnd"),
62 | ],
63 | [
64 | ("3", "gpio"),
65 | ("A6", "analog"),
66 | ("TOUCH", "touch"),
67 | ],
68 | [
69 | ("4", "gpio"),
70 | ("A5", "analog"),
71 | ],
72 | ]
73 |
74 |
75 | aux = [
76 | [
77 | ("TOUCH", "touch"),
78 | ("A7", "analog"),
79 | ],
80 | [
81 | ("TOUCH", "touch"),
82 | ],
83 | ]
84 |
85 |
86 | # Text
87 |
88 | annotation_usb = ["USB-C", "port"]
89 | annotation_led = ["Status", "LED"]
90 |
91 | title = "pinout"
92 |
93 | desc = """Description
94 | Demonstration diagram displaying pin-out
95 | information of non-existent hardware.
96 | Created with version 0.0.10
97 | """
98 |
99 | notes = """Notes
100 | pinout is a Python application to assist
101 | with documentation of electronic hardware.
102 | Development is active with a goal to convert
103 | a promising idea into a useful tool.
104 |
105 | Current release:
106 | pinout.readthedocs.io"""
107 |
--------------------------------------------------------------------------------
/samples/pci-express/pci-express_x1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 | tests/temp/
54 | temp_pytest_*.svg
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 | local_settings.py
63 | db.sqlite3
64 | db.sqlite3-journal
65 |
66 | # Flask stuff:
67 | instance/
68 | .webassets-cache
69 |
70 | # Scrapy stuff:
71 | .scrapy
72 |
73 | # Sphinx documentation
74 | docs/_build/
75 |
76 | # PyBuilder
77 | target/
78 |
79 | # Jupyter Notebook
80 | .ipynb_checkpoints
81 |
82 | # IPython
83 | profile_default/
84 | ipython_config.py
85 |
86 | # pyenv
87 | .python-version
88 |
89 | # pipenv
90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
93 | # install all needed dependencies.
94 | #Pipfile.lock
95 |
96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
97 | __pypackages__/
98 |
99 | # Celery stuff
100 | celerybeat-schedule
101 | celerybeat.pid
102 |
103 | # SageMath parsed files
104 | *.sage.py
105 |
106 | # Environments
107 | .env
108 | .venv
109 | env/
110 | venv/
111 | ENV/
112 | env.bak/
113 | venv.bak/
114 |
115 | # Spyder project settings
116 | .spyderproject
117 | .spyproject
118 |
119 | # Rope project settings
120 | .ropeproject
121 |
122 | # Visual studio code project settings
123 | .vscode/
124 | *.code-workspace
125 |
126 | # mkdocs documentation
127 | /site
128 |
129 | # mypy
130 | .mypy_cache/
131 | .dmypy.json
132 | dmypy.json
133 |
134 | # Pyre type checker
135 | .pyre/
136 |
--------------------------------------------------------------------------------
/samples/teensy_4.0/styles.scss:
--------------------------------------------------------------------------------
1 |
2 | $panel_bg: rgb(255,255,255);
3 | $diagram_bg: rgb(0,0,0);
4 |
5 | @mixin paragraph_text {
6 | fill: #000;
7 | font-family: Arial, Georgia, sans-serif;
8 | font-size: 16px;
9 | font-weight: 300;
10 | dominant-baseline: auto;
11 | }
12 |
13 | // pinlabels /////////////////////////////////////////
14 |
15 | $pinlabels: (
16 | "gnd": #000,
17 | "pwr": #ad0000,
18 | "digital": #cfd5d5,
19 | "analog": #ffd7ad,
20 | "pwm": #ffb3aa,
21 | "audio": #f7f09d,
22 | "serial": #c9daed,
23 | "i2c": #b1add7,
24 | "spi": #c2e6b9,
25 | "canbus": #fedae2,
26 | "note": #ddd
27 | );
28 | @each $name, $color in $pinlabels {
29 | .#{$name} .pinlabel__body,
30 | .#{$name} .textblock__bg{
31 | fill: #{$color}
32 | }
33 | .#{$name} .pinlabel__leader{
34 | stroke: #{$color}
35 | }
36 | .#{$name}.legend__entry .panel__inner{
37 | fill: #{$color}
38 | }
39 | }
40 |
41 | .pinlabel__leader{
42 | fill: none;
43 | stroke-width:0;
44 | }
45 | .pingroup g .pinlabel:first-child .pinlabel__leader,
46 | .lline--visible .pinlabel__leader{
47 | stroke-width: 2;
48 | }
49 |
50 | .pinlabel__text{
51 | dominant-baseline: central;
52 | fill: #000;
53 | font-size: 20px;
54 | font-weight: normal;
55 | letter-spacing: -1px;
56 | stroke-width: 0;
57 | text-anchor: middle;
58 | }
59 | // Power and ground labels have dark background and need light text
60 | .gnd .pinlabel__text,
61 | .pwr .pinlabel__text{
62 | fill:#fff;
63 | }
64 | .tight text{
65 | letter-spacing: -2px;
66 | }
67 |
68 |
69 | // Title block /////////////////////////////////////////
70 |
71 | .titlebar__bg{
72 | fill:$panel_bg;
73 | }
74 | .h1 text {
75 | font-size: 36px;
76 | font-weight: bold;
77 | }
78 | .h2 text {
79 | font-size: 18px;
80 | font-weight: bold;
81 | font-style: italic;
82 | }
83 | .italic{
84 | font-style: italic;
85 | }
86 | .strong{
87 | font-weight: bold;
88 | }
89 | .url{
90 | fill: #0084ff;
91 | font-weight: bold;
92 | }
93 | text {
94 | @include paragraph_text;
95 | }
96 |
97 |
98 | // legend /////////////////////////////////////////
99 |
100 | .legend__bg{
101 | fill: $diagram_bg;
102 | }
103 | .legend__entry .panel__outer{
104 | fill: $diagram_bg;
105 | }
106 | .legend__title{
107 | font-size: 18px;
108 | font-weight: bold;
109 | dominant-baseline: auto;
110 | }
111 | .legend text{
112 | @include paragraph_text;
113 | }
114 |
115 | // diagram layout /////////////////////////////////
116 |
117 | .diagram__bg{
118 | fill: #000;
119 | }
120 | .panel__inner{
121 | fill:$panel_bg;
122 | }
--------------------------------------------------------------------------------
/samples/panel_layout/panel_layout.py:
--------------------------------------------------------------------------------
1 | # Example diagram layout using Diagram and Panel classes
2 | #
3 | # export this sample via the command line:
4 | # >>> py -m pinout.manager --export panel_layout.py output/panel_layout.svg
5 | #
6 |
7 | from pinout.components.layout import Diagram, Panel
8 |
9 | # Create a blank diagram
10 | diagram = Diagram(1200, 675, tag="panel_layout")
11 |
12 | # 'auto_styles.css' was auto-generated and inserted here
13 | # *AFTER* all components were added to this script.
14 | # >>> py -m pinout.manager --css panel_layout styles.css
15 | diagram.add_stylesheet("auto_styles.css")
16 |
17 | # User defined styles can be added to the auto-generated
18 | # file or included as an additional asset.
19 | diagram.add_stylesheet("styles.css")
20 |
21 | # Panel fills entire diagram.
22 | # All other panels will be added to this one.
23 | panel_00 = diagram.add(Panel(1200, 675, (2, 2, 2, 2)))
24 |
25 | # Banner panel
26 | panel_banner = panel_00.add(
27 | Panel(
28 | width=panel_00.inset_width,
29 | height=50,
30 | tag="panel--banner",
31 | )
32 | )
33 |
34 | # Main panel
35 | panel_main = panel_00.add(
36 | Panel(
37 | width=panel_00.inset_width * 2 / 3,
38 | height=500,
39 | x=0,
40 | y=panel_banner.height,
41 | tag="panel--main",
42 | )
43 | )
44 |
45 | # Detail panel
46 | # This component is a wrapper for easier alignment
47 | panel_details = panel_00.add(
48 | Panel(
49 | x=panel_main.width,
50 | y=panel_banner.height,
51 | width=panel_00.inset_width - panel_main.width,
52 | height=panel_main.height,
53 | inset=(0, 0, 0, 0),
54 | tag="panel--detail",
55 | )
56 | )
57 |
58 | # x3 'sub' panels
59 | panel_detail_01 = panel_details.add(
60 | Panel(
61 | x=0,
62 | y=0,
63 | width=panel_details.width,
64 | height=panel_details.height / 3,
65 | tag="panel--detail",
66 | )
67 | )
68 | panel_detail_02 = panel_details.add(
69 | Panel(
70 | x=0,
71 | y=panel_detail_01.bounding_coords().y2,
72 | width=panel_details.width,
73 | height=panel_details.height / 3,
74 | tag="panel--detail",
75 | )
76 | )
77 |
78 | panel_detail_03 = panel_details.add(
79 | Panel(
80 | x=0,
81 | y=panel_detail_02.bounding_coords().y2,
82 | width=panel_details.width,
83 | height=panel_details.height / 3,
84 | tag="panel--detail",
85 | )
86 | )
87 |
88 | # Footer panel
89 | panel_footer = panel_00.add(
90 | Panel(
91 | x=0,
92 | y=panel_main.bounding_coords().y2,
93 | width=panel_00.inset_width,
94 | height=panel_00.inset_height - panel_main.bounding_coords().y2,
95 | tag="panel--footer",
96 | )
97 | )
98 |
--------------------------------------------------------------------------------
/samples/pci-express/autostyles.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | text {
4 | font-family: Verdana, Georgia, sans-serif;
5 | font-size: 14px;
6 | font-weight: normal;
7 | }
8 |
9 |
10 |
11 | .pinlabel__leader{
12 | stroke-width: 1;
13 | fill: none;
14 | }
15 |
16 | .pinlabel__text{
17 | dominant-baseline: central;
18 | fill: #000;
19 | font-size: 12px;
20 |
21 | font-weight: normal;
22 | stroke-width: 0;
23 | text-anchor: middle;
24 | }
25 |
26 |
27 |
28 | .pinid .pinlabel__body,
29 | .pinid .swatch__body{
30 | fill: rgb(0, 0, 0);
31 | }
32 | .pinid .pinlabel__leader{
33 | stroke: rgb(0, 0, 0);
34 | }
35 | .pinid .pinlabel__text{
36 | fill: #fff;
37 | }
38 |
39 | .host-to-card .pinlabel__body,
40 | .host-to-card .swatch__body{
41 | fill: rgb(83, 102, 165);
42 | }
43 | .host-to-card .pinlabel__leader{
44 | stroke: rgb(83, 102, 165);
45 | }
46 |
47 |
48 | .card-to-host .pinlabel__body,
49 | .card-to-host .swatch__body{
50 | fill: rgb(145, 79, 158);
51 | }
52 | .card-to-host .pinlabel__leader{
53 | stroke: rgb(145, 79, 158);
54 | }
55 |
56 | .open-drain .pinlabel__body,
57 | .open-drain .swatch__body{
58 | fill: rgb(158, 121, 79);
59 | }
60 | .open-drain .pinlabel__leader{
61 | stroke: rgb(158, 121, 79);
62 | }
63 |
64 | .sense-pin .pinlabel__body,
65 | .sense-pin .swatch__body{
66 | fill: rgb(75, 185, 31);
67 | }
68 | .sense-pin .pinlabel__leader{
69 | stroke: rgb(75, 185, 31);
70 | }
71 |
72 |
73 | .gnd .pinlabel__body,
74 | .gnd .swatch__body{
75 | fill: rgb(100, 100, 100);
76 | }
77 | .gnd .pinlabel__leader{
78 | stroke: rgb(100, 100, 100);
79 | }
80 |
81 |
82 | .pwr .pinlabel__body{
83 | fill: rgb(163, 34, 34);
84 | }
85 | .pwr .pinlabel__leader{
86 | stroke: rgb(163, 34, 34);
87 | }
88 | .pwr .swatch__body {
89 | fill: rgb(163, 34, 34);
90 | }
91 | .pinb .pinlabel__body{
92 | fill: rgb(99, 88, 162);
93 | }
94 | .pinb .pinlabel__leader{
95 | stroke: rgb(99, 88, 162);
96 | }
97 | .pinb .swatch__body {
98 | fill: rgb(99, 88, 162);
99 | }
100 |
101 |
102 | .panel__inner {
103 | fill: #fff;
104 | }
105 | .panel__outer {
106 | fill: #333;
107 | }
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/samples/section_pullout/styles.css:
--------------------------------------------------------------------------------
1 | text {
2 | font-family: Verdana, Georgia, sans-serif;
3 | font-size: 14px;
4 | font-weight: normal;
5 | }
6 |
7 | .pinlabel__leader{
8 | stroke-width: 2;
9 | fill: none;
10 | }
11 |
12 | .pinlabel__text{
13 | dominant-baseline: central;
14 | fill: #fff;
15 | font-weight: bold;
16 | stroke-width: 0;
17 | text-anchor: middle;
18 | }
19 |
20 | .pwr .pinlabel__body{
21 | fill: rgb(173, 0, 0);
22 | }
23 | .pwr .pinlabel__leader{
24 | stroke: rgb(173, 0, 0);
25 | }
26 | .pwr .swatch__body {
27 | fill: rgb(173, 0, 0);
28 | }
29 | .i2c .pinlabel__body{
30 | fill: rgb(182, 69, 176);
31 | }
32 | .i2c .pinlabel__leader{
33 | stroke: rgb(182, 69, 176);
34 | }
35 | .i2c .swatch__body {
36 | fill: rgb(182, 69, 176);
37 | }
38 | .gpio .pinlabel__body{
39 | fill: rgb(160, 133, 26);
40 | }
41 | .gpio .pinlabel__leader{
42 | stroke: rgb(160, 133, 26);
43 | }
44 | .gpio .swatch__body {
45 | fill: rgb(160, 133, 26);
46 | }
47 | .analog .pinlabel__body{
48 | fill: rgb(32, 150, 165);
49 | }
50 | .analog .pinlabel__leader{
51 | stroke: rgb(32, 150, 165);
52 | }
53 | .analog .swatch__body {
54 | fill: rgb(32, 150, 165);
55 | }
56 | .gnd .pinlabel__body{
57 | fill: rgb(0, 0, 0);
58 | }
59 | .gnd .pinlabel__leader{
60 | stroke: rgb(0, 0, 0);
61 | }
62 | .gnd .swatch__body {
63 | fill: rgb(0, 0, 0);
64 | }
65 | .pwm .pinlabel__body{
66 | fill: rgb(151, 76, 23);
67 | }
68 | .pwm .pinlabel__leader{
69 | stroke: rgb(151, 76, 23);
70 | }
71 | .pwm .swatch__body {
72 | fill: rgb(151, 76, 23);
73 | }
74 | .touch .pinlabel__body{
75 | fill: rgb(230, 87, 10);
76 | }
77 | .touch .pinlabel__leader{
78 | stroke: rgb(230, 87, 10);
79 | }
80 | .touch .swatch__body {
81 | fill: rgb(230, 87, 10);
82 | }
83 |
84 | .panel__inner {
85 | fill: #fff;
86 | }
87 | .panel__outer {
88 | fill: #333;
89 | }
90 |
91 | .legendentry text {
92 | dominant-baseline: central;
93 | }
94 |
95 | .h1 {
96 | font-size: 26px;
97 | font-weight: bold;
98 | font-style: italic;
99 | }
100 | .italic{
101 | font-style: italic;
102 | }
103 | .strong{
104 | font-weight: bold;
105 | }
106 |
107 | .panel--info .panel__inner{
108 |
109 | fill: #f4f4f4;
110 | }
111 |
112 |
113 | .annotation__body {
114 | fill: rgb(253, 203, 36);
115 | }
116 | .annotation__text text {
117 | dominant-baseline: central;
118 | font-size: 18px;
119 | font-weight: bold;
120 | text-anchor: middle;
121 | }
122 | .annotation__target {
123 | fill: none;
124 | stroke: rgb(253, 203, 36);
125 | stroke-width: 4px;
126 | }
127 | .annotation__leaderline {
128 | stroke: rgb(253, 203, 36);
129 | stroke-width: 4px;
130 | fill: none;
131 | }
132 | .section_title{
133 | font-size: 20px;
134 | font-weight: bold;
135 | }
--------------------------------------------------------------------------------
/pinout/components/legend.py:
--------------------------------------------------------------------------------
1 | from pinout import core, config
2 | from pinout.components.layout import Group
3 |
4 |
5 | class Swatch(Group):
6 | """Graphical icon for display in LegendEntry"""
7 |
8 | def __init__(self, width=None, height=None, **kwargs):
9 | super().__init__(**kwargs)
10 | self.update_config(config.legend["entry"]["swatch"])
11 | width = width or self.config["width"]
12 | height = height or self.config["height"]
13 |
14 | # Rect aligned left hand edge, vertically centered around origin.
15 | shape = self.add(core.Rect(y=-height / 2, width=width, height=height))
16 | self.add_tag("swatch")
17 | shape.add_tag("swatch__body")
18 |
19 |
20 | class LegendEntry(Group):
21 | """Legend entry comprised of a swatch and single line of text."""
22 |
23 | def __init__(
24 | self,
25 | content,
26 | width=None,
27 | height=None,
28 | swatch=None,
29 | **kwargs,
30 | ):
31 | super().__init__(**kwargs)
32 | self.update_config(config.legend["entry"])
33 | self.add_tag(self.config["tag"])
34 |
35 | width = width or self.config["width"]
36 | height = height or self.config["height"]
37 | swatch = swatch or {}
38 |
39 | if isinstance(swatch, dict):
40 | swatch = Swatch(**swatch)
41 |
42 | self.add(
43 | core.SvgShape(
44 | width=width,
45 | height=height,
46 | ),
47 | )
48 |
49 | swatch.y = height / 2
50 | swatch.x = (height - swatch.height) / 2
51 | self.add(swatch)
52 |
53 | self.add(
54 | core.Text(
55 | content,
56 | x=swatch.bounding_coords().x2 + swatch.x,
57 | y=self.height / 2,
58 | )
59 | )
60 |
61 |
62 | class Legend(Group):
63 | """Auto generate a legend component"""
64 |
65 | def __init__(
66 | self,
67 | data,
68 | max_height=None,
69 | **kwargs,
70 | ):
71 | super().__init__(**kwargs)
72 | self.update_config(config.legend)
73 | self.add_tag(self.config["tag"])
74 |
75 | max_height = max_height or self.config["max_height"]
76 |
77 | entry_x = 0
78 | entry_y = 0
79 | for entry in data:
80 |
81 | if type(entry) is tuple:
82 | content, tag, *args = entry
83 | attrs = args[0] if len(args) > 0 else {}
84 | entry = LegendEntry(content, tag=tag, **attrs, scale=self.scale)
85 |
86 | self.add(entry)
87 |
88 | # Position entry in legend
89 | if max_height and entry_y + entry.height > max_height:
90 | entry_x = self.width
91 | entry_y = 0
92 | entry.x = entry_x
93 | entry.y = entry_y
94 | entry_y += entry.height
95 |
--------------------------------------------------------------------------------
/docs/pages/manager.rst:
--------------------------------------------------------------------------------
1 | Manager
2 | =======
3 |
4 | The manager module provides various functions to assist *pinout* create diagrams. For users, Manager is primarily accessed via the command-line for the following.
5 |
6 | Duplicate quick_start files
7 | ---------------------------
8 |
9 | A fast way to get started exploring *pinout* is by trying out the quick_start diagram that is featured in the tutorial. Required files can be duplicated from the *pinout* package via command line::
10 |
11 | py -m pinout.manager --duplicate quick_start
12 |
13 | # expected output:
14 | # >>> data.py duplicated.
15 | # >>> hardware.png duplicated.
16 | # >>> pinout_diagram.py duplicated.
17 | # >>> styles.css duplicated.
18 |
19 | *-d* works as a short-hand version of *--duplicate*
20 |
21 | Export an SVG diagram
22 | ---------------------
23 |
24 | Once a diagram has been documented it can be exported to SVG format via the command-line. Two arguments must be supplied. A path to the diagram python script and destination path including filename::
25 |
26 | >>> py pinout.manager --export pinout_diagram.py my_diagram.svg
27 |
28 | # expected response:
29 | # 'my_diagram.svg' exported successfully.
30 |
31 | # Example where pinout.Diagram instance is named 'board_x_diagram'
32 | >>> py pinout.manager --export pinout_diagram.py my_diagram.svg board_x_diagram
33 |
34 | Details to note:
35 |
36 | - *--export* can be expressed as a single letter *-e*
37 | - An *--overwrite* (*-o*) can also be included to overwrite an existing file
38 | - if the instance name is not 'diagram' the alternative name can be added as as third argument
39 |
40 | Export in other formats
41 | -----------------------
42 |
43 | With the addition of CairoSVG *pinout* is able to export to PNG, PDF, and PS formats. Installation is done via pip::
44 |
45 | pip install cairosvg
46 |
47 | .. note::
48 |
49 | CairoSVG has it's own (non-Python) dependencies. See :ref:`Install` for more details.
50 |
51 | Once these dependencies have been installed replace the filename suffix to export in the desired format::
52 |
53 | # Export as png
54 | >>> py pinout.manager --export pinout_diagram.py my_diagram.png
55 |
56 | # Export as pdf
57 | >>> py pinout.manager --export pinout_diagram.py my_diagram.pdf
58 |
59 | # Export as ps
60 | >>> py pinout.manager --export pinout_diagram.py my_diagram.ps
61 |
62 |
63 | Generate a cascading stylesheet
64 | -------------------------------
65 |
66 | Provided with a diagram file, the manager can extract components and tags, then export a stylesheet based on this data to assist with styling. The resulting stylesheet can then be further edited or a second stylesheet created to supplement the default styles::
67 |
68 | >>> py pinout.manager --css pinout_diagram.py diagram_styles.css
69 |
70 | # expected response:
71 | # Stylesheet created: 'diagram_styles.css'
72 |
73 | As with exporting an SVG, the *-o* flag can be used to overwrite and existing file. Note, there is no short-hand for the *-css* flag.
--------------------------------------------------------------------------------
/docs/pages/install.rst:
--------------------------------------------------------------------------------
1 | .. _install:
2 |
3 | Install and Quickstart
4 | ======================
5 |
6 |
7 | Install
8 | -------
9 |
10 | Using a virtual environment is recommended; Start by installing the *pinout* package from PyPi::
11 |
12 | pip install pinout
13 |
14 | # Or upgrade to the latest version
15 | pip install --upgrade pinout
16 |
17 | *pinout* exports diagrams in SVG format and can be used with with no further package installations. With the additional installation of CairoSVG, diagrams can also be exported in PNG, PDF, and PS formats::
18 |
19 | pip install cairosvg
20 |
21 | .. warning::
22 | CairoSVG has non-Python dependencies that will require installing if not present. Installation varies depending on platform and may feel like quite a journey for non-technical users. Information regarding installation requirements can be found in the `CairoSVG `_ and `Cairo Graphics Library `_ websites.
23 |
24 | For Windows users `installing GTK3 via MSYS2 `_ may be the most reliable method to install all requirements (Don't forget to add the correct GTK bin folder to the system PATH environmental variable!)
25 |
26 |
27 | .. _quickstart:
28 |
29 | Quickstart
30 | ----------
31 |
32 | .. image:: /_static/quick_start_pinout_diagram.*
33 |
34 | This guide makes use of a hardware image, stylesheet, data file, and a Python script. Sample files are included with the package and can be duplicated for your use. Open a command line (with enabled virtual environment if you are using one) in the location you plan to work and enter the following
35 |
36 | .. note::
37 | Depending on your operating system the command to invoke Python may differ. This guide uses Windows default method. Exchanging 'py' for 'python' or similar may be required for examples to work on other systems.
38 |
39 | ::
40 |
41 | py -m pinout.manager --duplicate quick_start
42 |
43 | # expected output:
44 | # >>> data.py duplicated.
45 | # >>> hardware.png duplicated.
46 | # >>> pinout_diagram.py duplicated.
47 | # >>> styles.css duplicated.
48 |
49 |
50 | Generating the final SVG graphic is done from the command line::
51 |
52 | py -m pinout.manager --export pinout_diagram.py diagram.svg
53 |
54 | If everything is correctly configured the newly created file 'diagram.svg' can be viewed in a browser and should look identical to the diagram pictured here.
55 |
56 | .. warning::
57 | **Not all SVG viewers are build equal!**
58 | *pinout* uses SVG format 'under-the-hood' and can also output diagrams in this format. SVG is well supported by modern browsers and applications that *specialize* in rendering SVG such as InkScape. If a *pinout* diagram displays unexpected results (eg. mis-aligned text) cross-check by viewing the diagram in an up-to-date browser (eg. Firefox or Chrome) as an initial trouble-shooting step.
59 |
60 | Once you have installed the *pinout* package explore its main features in the :ref:`tutorial`.
--------------------------------------------------------------------------------
/docs/pages/customise.rst:
--------------------------------------------------------------------------------
1 | Customisation
2 | =============
3 |
4 | Documentation conveys not just information about its subject but also the personality of the owner. In the context of product/electronics documentation this 'personality' may be of the hardware itself, the creator of the hardware, or company that creates/distributes/sells the hardware.
5 |
6 | Many *pinout* components have facility for customisation and easy integration into a diagram.
7 |
8 |
9 | Stylesheet
10 | ----------
11 |
12 | The first stop for altering a diagram's appearance is to edit its stylesheet. Presentation styles are all controlled here. If you are coming with some knowlege of CSS for web, be aware SVG has some different names for rules!
13 |
14 |
15 | Component config
16 | ----------------
17 |
18 | Altering the geometry of default components can be done by changing, or providing new, config values. See the :ref:`Config` section for more details
19 |
20 | Building components
21 | -------------------
22 |
23 | It is possible to build new components and integrate them into *pinout*.
24 |
25 | Existing components are split into parts to allow easier overriding. Where a universal change is desired this maybe the best approach - until a guide is written for this, reviewing the package code (hosted on `github `_) is recommended.
26 |
27 | Insertion of customised elements into some component instances is also possible and suitable where only small changes, or multiple variants, of a component are required in a single diagram.
28 |
29 | **PinLabel** has 'leaderline' and 'body' attributes. These accept either a dictionary of values (see :ref:`Config`) or an instance that will be used in preference to the equivalent default component.
30 |
31 | **Annotation** has 'leaderline', 'body', and 'target' atttributes that accept new component instances.
32 |
33 | **An example**: The following code can be added to the quick_start script ('pinout_diagram.py') for quick and easy testing::
34 |
35 | # Import required modules and class at top of the script
36 | from pinout.components import pinlabel
37 | from pinout.core import Path
38 |
39 | # Create a new pin-label body class
40 | # and override the render function
41 | class SkewLabelBody(pinlabel.Body):
42 | def render(self):
43 | skew = 3
44 | path_def = " ".join(
45 | [
46 | f"M {self.x + skew} {self.y -self.height/2}",
47 | f"l {self.width} 0",
48 | f"l {-skew*2} {self.height}",
49 | f"l {-self.width} 0" "Z",
50 | ]
51 | )
52 | body = Path(path_definition=path_def, tag="label__body")
53 | return body.render()
54 |
55 |
56 | # Insert the following before the export statement
57 | # Add an instance of the custom pin-label body to the diagram
58 | diagram.add(
59 | pinlabel.PinLabel(
60 | content="SKEWED",
61 | x=50,
62 | y=50,
63 | body=SkewLabelBody(70, 0, 100, 30),
64 | )
65 | )
66 |
--------------------------------------------------------------------------------
/pinout/style_tools.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 |
4 | def luminace(color_component):
5 | """Luminance of an individual Red, Green, or Blue, color component.
6 |
7 | :param color_component: Value between 0 and 255 (inclusive)
8 | :type color_component: int
9 | :return: Luminance value of the color component
10 | :rtype: float
11 | """
12 | i = color_component / 255
13 |
14 | if i <= 0.03928:
15 | return i / 12.92
16 | else:
17 | return ((i + 0.055) / 1.055) ** 2.4
18 |
19 |
20 | def relative_luminance(rgb):
21 | """Normalised luminance value of an RGB color.
22 |
23 | :param rgb: Tuple (or List) representing RGB value.
24 | :type rgb: tuple
25 | :return: Value between 0 and 1.
26 | :rtype: float
27 | """
28 | return (
29 | 0.2126 * luminace(rgb[0])
30 | + 0.7152 * luminace(rgb[1])
31 | + 0.0722 * luminace(rgb[2])
32 | )
33 |
34 |
35 | def unique_contrasting_rgb(ref_color):
36 | """Generate a psudo-random color that, compared to 'ref_color', has a contrast ratio greater than 3. This contrast value is the minimum value to pass WCAG AA contrast recommendation for UI components.
37 |
38 | :param ref_color: Tuple (or List) representing RGB value.
39 | :type ref_color: tuple
40 | :return: Tuple representing an RGB color.
41 | :rtype: tuple
42 | """
43 | contrast = 0
44 | unique = False
45 | while contrast < 3 or not unique:
46 | rgb = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
47 |
48 | color_one_luminance = relative_luminance(ref_color)
49 | color_two_luminance = relative_luminance(rgb)
50 |
51 | if color_one_luminance > color_two_luminance:
52 | light = color_one_luminance
53 | dark = color_two_luminance
54 | else:
55 | dark = color_one_luminance
56 | light = color_two_luminance
57 |
58 | contrast = (light + 0.05) / (dark + 0.05)
59 | unique = is_distinct_rbg(rgb)
60 | palette.append(rgb)
61 | return rgb
62 |
63 |
64 | def is_distinct_rbg(rgb_color, threshold=60):
65 | r, g, b = rgb_color
66 | for (pr, pg, pb) in palette:
67 | diff = abs(r - pr) + abs(g - pg) + abs(b - pb)
68 | if diff < threshold:
69 | return False
70 | return True
71 |
72 |
73 | def assign_color(tag_list, ref_color=(255, 255, 255)):
74 | """Generate a stylesheet using metrics from a diagram. Various styles are tailored by making a *best-guess* based on diagram component dimensions or a *lucky-guess* filtered by preset criteria. The output should be considered a boot-strapping step to styling a diagram ...unless you feel lucky!
75 |
76 | :param diagram: The Diagram object requiring styling
77 | :type diagram: Diagram
78 | :return: content of a css stylesheet with all required styles to display a diagram.
79 | :rtype: str
80 | """
81 |
82 | # Assign random-ish color to each tag
83 | return [(tag, unique_contrasting_rgb(ref_color)) for tag in tag_list]
84 |
85 |
86 | # Store created color for reference
87 | palette = []
--------------------------------------------------------------------------------
/docs/pages/pinlabel.rst:
--------------------------------------------------------------------------------
1 | Pin Labels
2 | ==========
3 |
4 | .. currentmodule:: pinout.components.pinlabel
5 |
6 | Base
7 | ----
8 | .. autoclass:: Base
9 | :show-inheritance:
10 |
11 | :param content: Text displayed in label, defaults to ""
12 | :type content: str, optional
13 | :param x: position of label on x-axis , defaults to 0
14 | :type x: int, optional
15 | :param y: position of label on y-axis, defaults to 0
16 | :type y: int, optional
17 | :param tag: categorise the label - applied as a CSS class, defaults to None
18 | :type tag: str (CSS name compliant), optional
19 | :param body: replace or configure the default body component, defaults to None
20 | :type body: dict or pinlabel.Body instance, optional
21 | :param leaderline: replace or configure the default leaderline component, defaults to None
22 | :type leaderline: dict or pinlabel.Leaderline, optional
23 |
24 |
25 |
26 | PinLabel
27 | --------
28 | .. autoclass:: PinLabel
29 | :show-inheritance:
30 |
31 | See Base for details of this component.
32 |
33 | Body
34 | ----
35 | .. autoclass:: Body
36 | :show-inheritance:
37 |
38 | :param x: position of label on x-axis
39 | :type x: int
40 | :param y: position of label on y-axis
41 | :type y: int
42 | :param width: Width of label body
43 | :type width: int
44 | :param height: Height of label body
45 | :type height: int
46 | :param corner_radius: Corner radius of label body, defaults to 0
47 | :type corner_radius: int, optional
48 |
49 |
50 | Leaderline
51 | ----------
52 | .. autoclass:: Leaderline
53 | :show-inheritance:
54 |
55 | :param lline: Override configuration or replace the pinlabel's leaderline.
56 | :type lline: dict of leaderline attributes or replacement Leaderline instance
57 |
58 | PinLabelGroup
59 | -------------
60 | .. autoclass:: PinLabelGroup
61 | :show-inheritance:
62 |
63 | This is the recommended method of adding pin labels to a diagram. Locate the PinLabelSet by setting *x* and *y* attributes.
64 |
65 | Pitch is the distance, in pixels, between each pin of the header. (0, 30) steps 0px right and 30px down for each pin. (30, 0) creates a horizontal header. (-30, 0) creates a horizontal header in the reverse direction. This can be useful for 'stacking' rows in reversed order to avoid leader-lines overlapping.
66 |
67 | :param x: x-coordinate of the first pin in the header
68 | :type x: int
69 | :param y: y-coordinate of the first pin in the header
70 | :type y: int
71 | :param pin_pitch: Distance between pins in the header
72 | :type pin_pitch: tuple: (x,y)
73 | :param label_start: Offset of the first label from the first pin
74 | :type label_start: tuple: (x,y)
75 | :param label_pitch: Distance between each row of labels
76 | :type label_pitch: tuple: (x,y)
77 | :param labels: Label data
78 | :type labels: List
79 | :param leaderline: Leaderline customisations, defaults to None
80 | :type leaderline: dict or Leaderline object, optional
81 | :param body: Label body customisations, defaults to None
82 | :type body: dict or LabelBody object, optional
83 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/rp2040/arduino_nano_rp2040_connect.py:
--------------------------------------------------------------------------------
1 | from pinout.core import Group, Image, Raw, Rect
2 | from pinout.components.layout import Diagram, Panel
3 | from pinout.components import pinlabel, legend, leaderline
4 | from pinout.components.text import TextBlock
5 | import sys
6 |
7 |
8 | # Import data and custom function
9 | # Slightly inelegant method to amend module search path
10 | sys.path.append("..")
11 | import rp2040_data as data
12 | from common.preprocessor import pinlabel_preprocessor as prep
13 |
14 |
15 | # Create a new diagram
16 | diagram = Diagram(1200, 675, tag="arduino-rp2040-connect")
17 |
18 | # Add a stylesheet and some custom patterns
19 | diagram.add_stylesheet("../common/styles.css", True)
20 |
21 | # Load some svg markup to be used as patterns
22 | # The Raw class allows arbitary code to be inserted
23 | # into the diagram.
24 | with open("../common/patterns.xml") as f:
25 | patterns = f.read()
26 | diagram.add_def(Raw(patterns))
27 |
28 | # Construct a layout and add some backgrounds
29 | diagram.add(Rect(x=0, y=0, width=1200, height=675, tag="diagram__bg"))
30 | group_main = diagram.add(Group(2, 2, tag="panel panel--main"))
31 | group_main.add(Rect(x=0, y=0, width=1196, height=548, tag="panel__bg"))
32 |
33 | # Keeping elements in a group allows for easier measuring and moving
34 | # Create a group for the main pinout graphic
35 | pinout_graphic = group_main.add(Group(600, 80, tag="pinout-graphic"))
36 |
37 | group_notes = diagram.add(Group(2, 552, tag="panel panel--notes"))
38 | group_notes.add(Rect(x=0, y=0, width=1196, height=121, tag="panel__bg"))
39 | group_notes.add(
40 | legend.Legend(data.legend, max_height=112, x=10, y=5, inset=(0, 0, 0, 0))
41 | )
42 | group_notes.add(TextBlock(data.title_1, 22, x=580, y=30))
43 | group_notes.add(TextBlock(data.para_1, 17, x=580, y=74))
44 | group_notes.add(TextBlock(data.para_2, 17, x=900, y=74))
45 |
46 | # Add a hardware image
47 | # Note its coordinates are relative to the group it is within
48 | pinout_graphic.add(
49 | Image(
50 | "hardware.png",
51 | x=-176 / 2,
52 | y=0,
53 | width=176,
54 | height=449,
55 | embed=True,
56 | )
57 | )
58 | # Right hand side pin header
59 | pinout_graphic.add(
60 | pinlabel.PinLabelGroup(
61 | x=86,
62 | y=58,
63 | pin_pitch=(0, 24.6),
64 | label_start=(80, 0),
65 | label_pitch=(0, 24.6),
66 | labels=prep(data.header_rhs),
67 | tag="pinheader",
68 | )
69 | )
70 |
71 | # Left hand side pin header
72 | pinout_graphic.add(
73 | pinlabel.PinLabelGroup(
74 | x=-86,
75 | y=58,
76 | pin_pitch=(0, 24.6),
77 | label_start=(80, 0),
78 | label_pitch=(0, 24.6),
79 | scale=(-1, 1),
80 | labels=prep(data.header_lhs),
81 | tag="pinheader",
82 | )
83 | )
84 |
85 | # LED labels
86 | pinout_graphic.add(
87 | pinlabel.PinLabelGroup(
88 | x=-56,
89 | y=28,
90 | pin_pitch=(112, 0),
91 | label_start=(104, 60),
92 | label_pitch=(0, 22),
93 | scale=(-1, -1),
94 | labels=prep(data.leds),
95 | leaderline=leaderline.Curved(direction="vh"),
96 | tag="pinheader",
97 | )
98 | )
99 |
--------------------------------------------------------------------------------
/pinout/templates/stylesheet.j2:
--------------------------------------------------------------------------------
1 |
2 | {# Diagram settings #}
3 | text {
4 | font-family: Verdana, Georgia, sans-serif;
5 | font-size: 14px;
6 | font-weight: normal;
7 | }
8 |
9 | {# Pinlabel component #}
10 | {% if css.pinlabel %}
11 | .{{ css.pinlabel.leaderline.tag }}{
12 | stroke-width: 2;
13 | fill: none;
14 | }
15 |
16 | .{{ css.pinlabel.text.tag }}{
17 | dominant-baseline: central;
18 | fill: #fff;
19 |
20 | font-weight: bold;
21 | stroke-width: 0;
22 | text-anchor: middle;
23 | }
24 |
25 | {% endif -%}
26 |
27 | {# Color styling derived from pinlabel classes #}
28 | {% if css.tags %}
29 | {% for (name, color) in css.tags %}
30 | .{{name}} .{{ css.pinlabel.body.tag }}{
31 | fill: rgb{{color}};
32 | }
33 | .{{name}} .{{ css.pinlabel.leaderline.tag }}{
34 | stroke: rgb{{color}};
35 | }
36 | .{{name}} .swatch__body {
37 | fill: rgb{{color}};
38 | }
39 | {% endfor %}
40 |
41 | {% endif -%}
42 |
43 | {# Panel component #}
44 | {% if css.panel %}
45 | .{{ css.panel.inner.tag }} {
46 | fill: #fff;
47 | }
48 | .{{ css.panel.outer.tag }} {
49 | fill: #333;
50 | }
51 |
52 | {% endif -%}
53 |
54 | {# Legend component #}
55 | {% if css.legend %}
56 | .{{ css.legend.entry.tag }} text {
57 | dominant-baseline: central;
58 | }
59 | {% endif -%}
60 |
61 | {# Annotation component #}
62 | {% if css.annotation %}
63 | .{{ css.annotation.body.tag }} {
64 | fill: rgb(253, 203, 36);
65 | stroke-width: 0;
66 | }
67 | .{{ css.annotation.content.tag }} {
68 | dominant-baseline: central;
69 | font-size: 14px;
70 | font-weight: bold;
71 | fill: rgb(63, 40, 6);
72 | text-anchor: middle;
73 | }
74 | .{{ css.annotation.target.tag }} {
75 | fill: none;
76 | stroke: rgb(253, 203, 36);
77 | stroke-width: 4px;
78 | }
79 | .{{ css.annotation.leaderline.tag }} {
80 | stroke: rgb(253, 203, 36);
81 | stroke-width: 4px;
82 | fill: none;
83 | }
84 |
85 | {% endif -%}
86 |
87 | {# Integrated circuits #}
88 | {% if css.ic_dip %}
89 | .{{ css.ic_dip.body.tag }}{
90 | fill: rgb(85, 85, 85);
91 | stroke-width: 1px;
92 | stroke: #000;
93 | }
94 | .{{ css.ic_dip.leg.tag }} .{{ css.ic_dip.polarity_mark.tag }}{
95 | fill: rgb(85, 85, 85);
96 | stroke-width: 2px;
97 | stroke: rgb(138, 138, 138);
98 |
99 | }
100 | .{{ css.ic_dip.leg.tag }}{
101 | fill: rgb(255, 255, 255);
102 | stroke-width: 1px;
103 | stroke: #000;
104 | }
105 |
106 | {% endif -%}
107 |
108 | {% if css.ic_qfp %}
109 | .{{ css.ic_qfp.body.tag }}{
110 | fill: rgb(85, 85, 85);
111 | stroke-width: 1px;
112 | stroke: #000;
113 | }
114 | .{{ css.ic_qfp.leg.tag }} .{{ css.ic_qfp.polarity_mark.tag }}{
115 | fill: rgb(85, 85, 85);
116 | stroke-width: 2px;
117 | stroke: rgb(138, 138, 138);
118 |
119 | }
120 | .{{ css.ic_qfp.leg.tag }}{
121 | fill: rgb(255, 255, 255);
122 | stroke-width: 1px;
123 | stroke: #000;
124 | }
125 |
126 | {% endif -%}
127 |
128 | {# Diagram preset templates #}
129 | {% if css.diagram_presets %}
130 | .{{ css.diagram_presets.tag }} .h1{
131 | font-size: 26px;
132 | font-weight: bold;
133 | }
134 | .{{ css.diagram_presets.panel_02.tag }} .panel__inner{
135 | fill: #ededed;
136 | }
137 |
138 | {% endif -%}
--------------------------------------------------------------------------------
/samples/full_sample/styles_auto.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | text {
4 | font-family: Verdana, Georgia, sans-serif;
5 | font-size: 14px;
6 | font-weight: normal;
7 | }
8 |
9 |
10 |
11 | .pinlabel__leader{
12 | stroke-width: 2;
13 | fill: none;
14 | }
15 |
16 | .pinlabel__text{
17 | dominant-baseline: central;
18 | fill: #fff;
19 |
20 | font-weight: bold;
21 | stroke-width: 0;
22 | text-anchor: middle;
23 | }
24 |
25 |
26 |
27 | .gpio .pinlabel__body{
28 | fill: rgb(183, 76, 231);
29 | }
30 | .gpio .pinlabel__leader{
31 | stroke: rgb(183, 76, 231);
32 | }
33 | .gpio .swatch__body {
34 | fill: rgb(183, 76, 231);
35 | }
36 | .pwr .pinlabel__body{
37 | fill: rgb(109, 99, 25);
38 | }
39 | .pwr .pinlabel__leader{
40 | stroke: rgb(109, 99, 25);
41 | }
42 | .pwr .swatch__body {
43 | fill: rgb(109, 99, 25);
44 | }
45 | .pwm .pinlabel__body{
46 | fill: rgb(218, 96, 195);
47 | }
48 | .pwm .pinlabel__leader{
49 | stroke: rgb(218, 96, 195);
50 | }
51 | .pwm .swatch__body {
52 | fill: rgb(218, 96, 195);
53 | }
54 | .gnd .pinlabel__body{
55 | fill: rgb(25, 90, 5);
56 | }
57 | .gnd .pinlabel__leader{
58 | stroke: rgb(25, 90, 5);
59 | }
60 | .gnd .swatch__body {
61 | fill: rgb(25, 90, 5);
62 | }
63 | .touch .pinlabel__body{
64 | fill: rgb(200, 9, 45);
65 | }
66 | .touch .pinlabel__leader{
67 | stroke: rgb(200, 9, 45);
68 | }
69 | .touch .swatch__body {
70 | fill: rgb(200, 9, 45);
71 | }
72 | .comms .pinlabel__body{
73 | fill: rgb(57, 138, 188);
74 | }
75 | .comms .pinlabel__leader{
76 | stroke: rgb(57, 138, 188);
77 | }
78 | .comms .swatch__body {
79 | fill: rgb(57, 138, 188);
80 | }
81 | .analog .pinlabel__body{
82 | fill: rgb(96, 42, 122);
83 | }
84 | .analog .pinlabel__leader{
85 | stroke: rgb(96, 42, 122);
86 | }
87 | .analog .swatch__body {
88 | fill: rgb(96, 42, 122);
89 | }
90 |
91 |
92 | .panel__inner {
93 | fill: #fff;
94 | }
95 | .panel__outer {
96 | fill: #333;
97 | }
98 |
99 |
100 | .legendentry text {
101 | dominant-baseline: central;
102 | }
103 |
104 |
105 | .annotation__body {
106 | fill: rgb(253, 203, 36);
107 | stroke-width: 0;
108 | }
109 | .annotation__text {
110 | dominant-baseline: central;
111 | font-size: 14px;
112 | font-weight: bold;
113 | fill: rgb(63, 40, 6);
114 | text-anchor: middle;
115 | }
116 | .annotation__target {
117 | fill: none;
118 | stroke: rgb(253, 203, 36);
119 | stroke-width: 4px;
120 | }
121 | .annotation__leaderline {
122 | stroke: rgb(253, 203, 36);
123 | stroke-width: 4px;
124 | fill: none;
125 | }
126 |
127 |
--------------------------------------------------------------------------------
/docs/pages/integrated_circuits.rst:
--------------------------------------------------------------------------------
1 | Integrated circuits
2 | ===================
3 |
4 | .. currentmodule:: pinout.components.integrated_circuits
5 |
6 | *pinout* can generate simple integrated circuit (IC) graphics - Ideal for documenting stand-alone IC components.
7 |
8 | DIP and QFP components can be utilised in a diagram in the same way as an image. However helper functions also exists for easy application of labels to these component.
9 |
10 |
11 | Labelled QFP graphic
12 | --------------------
13 |
14 | .. autofunction:: labelled_qfn
15 |
16 | :param labels: List of label data
17 | :type labels: list
18 | :param length: length of the IC sides (including legs), defaults to 160
19 | :type length: int, optional
20 | :param label_start: Offset of the first label from the first pin, defaults to (100, 20)
21 | :type label_start: tuple, optional
22 | :param label_pitch: Offest between each label row, defaults to (0, 30)
23 | :type label_pitch: tuple, optional
24 | :return: IC graphic with pinlabels applied
25 | :rtype: SVG markup
26 |
27 |
28 | Labelled DIP graphic
29 | --------------------
30 |
31 | .. autofunction:: labelled_dip
32 |
33 | :param labels: List of label data
34 | :type labels: list
35 | :param width: Width of IC (includes legs), defaults to 100
36 | :type width: int, optional
37 | :param height: Height of IC (includes inset), defaults to 160
38 | :type height: int, optional
39 | :param label_start_x: Offset in x-axis of first label from first pin, defaults to 100
40 | :type label_start_x: int, optional
41 | :param label_pitch: Offest between each label row, defaults to (0, 30)
42 | :type label_pitch: tuple, optional
43 | :return: IC graphic with pinlabels applied
44 | :rtype: SVG markup
45 |
46 |
47 | Dual in-line package (DIP)
48 | --------------------------
49 |
50 | .. autoclass:: DIP
51 | :show-inheritance:
52 |
53 | :param pin_count: Total number of pins on the integrated circuit
54 | :type pin_count: int
55 | :param width: width of the graphic, including body and legs
56 | :type width: int
57 | :param height: height of the graphic, including body and legs
58 | :type height: int
59 |
60 | Dimensions can be modified to depict a variety of IC types, eg SOIC and TSOP.
61 |
62 | .. autoproperty:: pin_coords
63 |
64 | :param index: Pin number (starts at 1)
65 | :type index: int
66 | :param rotate: If true, includes component rotation in the calculation, defaults to True
67 | :type rotate: bool, optional
68 | :return: coordinates of the pin relative to the IC's origin
69 | :rtype: namedtuple (x,y)
70 |
71 |
72 | Quad flat package (QFP)
73 | -----------------------
74 |
75 | .. autoclass:: QFP
76 | :show-inheritance:
77 |
78 | :param pin_count: Total number of pins on the integrated circuit
79 | :type pin_count: int
80 | :param length: length of the QFP sides
81 | :type length: int
82 |
83 | Dimensions can be modified to depict a variety of 'quad' IC types.
84 |
85 | .. autoproperty:: pin_coords
86 |
87 | :param index: Pin number (starts at 1)
88 | :type index: int
89 | :param rotate: If true, includes component rotation in the calculation, defaults to True
90 | :type rotate: bool, optional
91 | :return: coordinates of the pin relative to the IC's origin
92 | :rtype: namedtuple (x,y)
--------------------------------------------------------------------------------
/samples/arduino/arduino/common/styles.css:
--------------------------------------------------------------------------------
1 |
2 | svg {
3 | fill-opacity: 1;
4 | font-family: 'Roboto', Helvetica, Verdana, monospace;
5 | font-size: 13px;
6 | font-weight: 300;
7 | opacity: 1;
8 |
9 | }
10 |
11 | path{
12 | fill: none;
13 | }
14 | rect {
15 | fill: rgba(0, 0, 0, 0);
16 | }
17 |
18 | text {
19 | dominant-baseline: auto;
20 | }
21 |
22 | .pinlabel__body{
23 | fill: rgb(36, 36, 36);
24 | }
25 | .pinlabel__bodyinner{
26 | fill: none;
27 | stroke-linejoin: "miter";
28 | }
29 | .pinlabel .pinlabel__leader{
30 | stroke-width: 0;
31 | }
32 | .pinlabel__text{
33 | dominant-baseline: central;
34 | fill: #fff;
35 | font-size: 12px;
36 | font-weight: 600;
37 | stroke-width: 0;
38 | text-anchor: middle;
39 | }
40 |
41 | .legend rect{
42 | fill: none;
43 | }
44 | .legend .swatch__body{
45 | fill: rgba(0,0,0,0);
46 | }
47 | .legendentry text{
48 | dominant-baseline: central;
49 | fill: #000;
50 | }
51 |
52 | .diagram__bg{
53 | fill: #333;
54 | }
55 | .panel--main .panel__bg{
56 | fill: #fff;
57 | }
58 | .panel--notes .panel__bg{
59 | fill: #e4e4e2;
60 | }
61 |
62 | .pinheader g .pinlabel:first-child .pinlabel__leader,
63 | .pinlabel.show-leader .pinlabel__leader{
64 | stroke-width: 2px;
65 | stroke: #bcc6c6;
66 | }
67 |
68 | .led .pinlabel__body,
69 | .led .swatch__body{
70 | fill: #1da086;
71 | }
72 | .mu-port .pinlabel__body,
73 | .mu-port .swatch__body{
74 | fill: #f39c12;
75 | }
76 | .default .pinlabel__body,
77 | .default .swatch__body{
78 | fill: #f1c40f;
79 | }
80 | .default .pinlabel__text{
81 | fill: #000;
82 | }
83 | .digital .pinlabel__body,
84 | .digital .swatch__body{
85 | fill: url("#stripe-a");
86 | }
87 |
88 | #stripe-a rect{
89 | fill: #d35400;
90 | }
91 |
92 | #stripe-a path{
93 | fill: #de7f40;
94 | }
95 |
96 | .analog .pinlabel__body{
97 | fill: url("#stripe-b");
98 | }
99 |
100 | #stripe-b rect{
101 | fill: #ffffff;
102 | }
103 | #stripe-b path{
104 | fill: #f8e6d9;
105 | }
106 |
107 | .analog .pinlabel__bodyinner{
108 | fill: none;
109 | stroke: #d35400;
110 | stroke-width: 2;
111 | }
112 | .analog .pinlabel__text{
113 | fill: #d35400;
114 | }
115 | .analog .swatch__body{
116 | fill: url("#stripe-b");
117 | stroke: #d35400;
118 | stroke-width: 2;
119 | }
120 | .pwr .pinlabel__body,
121 | .pwr .swatch__body{
122 | fill: #c11f09;
123 | }
124 |
125 | .other .pinlabel__body,
126 | .other .swatch__body{
127 | fill: #fff;
128 | }
129 | .other .pinlabel__bodyinner,
130 | .other .swatch__body{
131 | stroke: #d86e29;
132 | stroke-width: 2;
133 | }
134 | .other .pinlabel__text{
135 | fill: #d86e29;
136 | }
137 | .gnd .pinlabel__body,
138 | .gnd .swatch__body{
139 | fill: #000;
140 | }
141 | .internal .pinlabel__body,
142 | .internal .swatch__body{
143 | fill: #94a3a6;
144 | }
145 | .swd .pinlabel__body,
146 | .swd .swatch__body{
147 | fill: #9e856e;
148 | }
149 | .nc .pinlabel__body,
150 | .nc .swatch__body{
151 | fill: #fff;
152 | }
153 | .nc .pinlabel__text{
154 | fill: #ccc;
155 | }
156 | .nc .pinlabel__bodyinner,
157 | .nc .swatch__body{
158 | stroke: #ccc;
159 | stroke-width: 2;
160 | }
161 |
162 | .h1{
163 | font-size: 28px;
164 | font-weight: 900;
165 | }
166 | .p{
167 | fill: #333;
168 | }
169 | .strong{ font-weight: bold; }
170 | .italic{ font-style: italic; }
--------------------------------------------------------------------------------
/samples/attiny85/attiny_styles.css:
--------------------------------------------------------------------------------
1 |
2 | text {
3 | font-family: Verdana, Georgia, sans-serif;
4 | font-size: 14px;
5 | font-weight: normal;
6 | }
7 |
8 | .panel__title{
9 | font-size: 16px;
10 | }
11 | .diagram__title{
12 | font-size: 22px;
13 | font-weight: bold;
14 | }
15 | .italic{
16 | font-style:italic;
17 | font-weight: bold;
18 | }
19 |
20 | .panel__inner {
21 | fill: #fff;
22 | }
23 | .panel__outer {
24 | fill: #99a5b2;
25 | }
26 | .panel__title .panel__inner{
27 | fill: #ededed;
28 | }
29 |
30 | .legendentry text {
31 | dominant-baseline: central;
32 | }
33 |
34 | .pinlabel__leader{
35 | stroke-width: 1;
36 | fill: none;
37 | }
38 |
39 | .pinlabel__text{
40 | dominant-baseline: central;
41 | fill: #fff;
42 | font-size: 12px;
43 | stroke-width: 0;
44 | text-anchor: middle;
45 | }
46 |
47 | .dnc .pinlabel__body{
48 | fill: rgb(220,220,220);
49 | }
50 | .dnc .pinlabel__leader{
51 | stroke: rgb(220,220,220);
52 | }
53 | .dnc text{
54 | fill: #999;
55 | }
56 | .pin_id .pinlabel__body{
57 | fill: rgb(85, 85, 85);
58 | }
59 | .pin_id .pinlabel__leader{
60 | stroke: rgb(85, 85, 85);
61 | }
62 | .pin_id text{
63 | font-size: 9px;
64 | }
65 | .comparator .pinlabel__body{
66 | fill: rgb(243, 85, 191);
67 | }
68 | .comparator .pinlabel__leader{
69 | stroke: rgb(243, 85, 191);
70 | }
71 | .comparator .swatch__body {
72 | fill: rgb(243, 85, 191);
73 | }
74 | .adc .pinlabel__body{
75 | fill: rgb(83, 93, 222);
76 | }
77 | .adc .pinlabel__leader{
78 | stroke: rgb(83, 93, 222);
79 | }
80 | .adc .swatch__body {
81 | fill: rgb(83, 93, 222);
82 | }
83 | .interrupt .pinlabel__body{
84 | fill: rgb(3, 130, 92);
85 | }
86 | .interrupt .pinlabel__leader{
87 | stroke: rgb(3, 130, 92);
88 | }
89 | .interrupt .swatch__body {
90 | fill: rgb(3, 130, 92);
91 | }
92 | .pwr .pinlabel__body{
93 | fill: rgb(255, 53, 53);
94 | }
95 | .pwr .pinlabel__leader{
96 | stroke: rgb(255, 53, 53);
97 | }
98 | .pwr .swatch__body {
99 | fill: rgb(255, 53, 53);
100 | }
101 | .port .pinlabel__body{
102 | fill: rgb(177, 30, 236);
103 | }
104 | .port .pinlabel__leader{
105 | stroke: rgb(177, 30, 236);
106 | }
107 | .port .swatch__body {
108 | fill: rgb(177, 30, 236);
109 | }
110 | .timer-counter .pinlabel__body{
111 | fill: rgb(160, 79, 27);
112 | }
113 | .timer-counter .pinlabel__leader{
114 | stroke: rgb(160, 79, 27);
115 | }
116 | .timer-counter .swatch__body {
117 | fill: rgb(160, 79, 27);
118 | }
119 | .comms .pinlabel__body{
120 | fill: rgb(59, 145, 130);
121 | }
122 | .comms .pinlabel__leader{
123 | stroke: rgb(59, 145, 130);
124 | }
125 | .comms .swatch__body {
126 | fill: rgb(59, 145, 130);
127 | }
128 | .gnd .pinlabel__body{
129 | fill: rgb(0, 0, 0);
130 | }
131 | .gnd .pinlabel__leader{
132 | stroke: rgb(0, 0, 0);
133 | }
134 | .gnd .swatch__body {
135 | fill: rgb(0, 0, 0);
136 | }
137 | .oscillator .pinlabel__body{
138 | fill: rgb(124, 21, 205);
139 | }
140 | .oscillator .pinlabel__leader{
141 | stroke: rgb(124, 21, 205);
142 | }
143 | .oscillator .swatch__body {
144 | fill: rgb(124, 21, 205);
145 | }
146 |
147 | .ic__body{
148 | fill: rgb(85, 85, 85);
149 | stroke-width: 1px;
150 | stroke: #000;
151 | }
152 | .ic__leg .polarity{
153 | fill: rgb(85, 85, 85);
154 | stroke-width: 2px;
155 | stroke: rgb(138, 138, 138);
156 |
157 | }
158 | .ic__leg{
159 | fill: rgb(255, 255, 255);
160 | stroke-width: 1px;
161 | stroke: #000;
162 | }
163 |
164 |
--------------------------------------------------------------------------------
/pinout/resources/quick_start/pinout_diagram.py:
--------------------------------------------------------------------------------
1 | ###########################################
2 | #
3 | # Example script to build a
4 | # pinout diagram. Includes basic
5 | # features and convenience classes.
6 | #
7 | ###########################################
8 |
9 | from pinout.core import Group, Image
10 | from pinout.components.layout import Diagram_2Rows
11 | from pinout.components.pinlabel import PinLabelGroup, PinLabel
12 | from pinout.components.text import TextBlock
13 | from pinout.components import leaderline as lline
14 | from pinout.components.legend import Legend
15 |
16 |
17 | # Import data for the diagram
18 | import data
19 |
20 | # Create a new diagram
21 | # The Diagram_2Rows class provides 2 panels,
22 | # 'panel_01' and 'panel_02', to insert components into.
23 | diagram = Diagram_2Rows(1024, 576, 440, "diagram")
24 |
25 | # Add a stylesheet
26 | diagram.add_stylesheet("styles.css", embed=True)
27 |
28 | # Create a group to hold the pinout-diagram components.
29 | graphic = diagram.panel_01.add(Group(400, 42))
30 |
31 | # Add and embed an image
32 | hardware = graphic.add(Image("hardware.png", embed=True))
33 |
34 | # Measure and record key locations with the hardware Image instance
35 | hardware.add_coord("gpio0", 16, 100)
36 | hardware.add_coord("gpio3", 65, 244)
37 | hardware.add_coord("reset", 155, 244)
38 | hardware.add_coord("vcc", 206, 100)
39 | # Other (x,y) pairs can also be stored here
40 | hardware.add_coord("pin_pitch_v", 0, 30)
41 | hardware.add_coord("pin_pitch_h", 30, 0)
42 |
43 | # Create a single pin label
44 | graphic.add(
45 | PinLabel(
46 | content="RESET",
47 | x=hardware.coord("reset").x,
48 | y=hardware.coord("reset").y,
49 | tag="pwr",
50 | body={"x": 117, "y": 30},
51 | leaderline={"direction": "vh"},
52 | )
53 | )
54 |
55 | # Create pinlabels on the right header
56 | graphic.add(
57 | PinLabelGroup(
58 | x=hardware.coord("vcc").x,
59 | y=hardware.coord("vcc").y,
60 | pin_pitch=hardware.coord("pin_pitch_v", raw=True),
61 | label_start=(60, 0),
62 | label_pitch=(0, 30),
63 | labels=data.right_header,
64 | )
65 | )
66 |
67 | # Create pinlabels on the left header
68 | graphic.add(
69 | PinLabelGroup(
70 | x=hardware.coord("gpio0").x,
71 | y=hardware.coord("gpio0").y,
72 | pin_pitch=hardware.coord("pin_pitch_v", raw=True),
73 | label_start=(60, 0),
74 | label_pitch=(0, 30),
75 | scale=(-1, 1),
76 | labels=data.left_header,
77 | )
78 | )
79 |
80 | # Create pinlabels on the lower header
81 | graphic.add(
82 | PinLabelGroup(
83 | x=hardware.coord("gpio3").x,
84 | y=hardware.coord("gpio3").y,
85 | scale=(-1, 1),
86 | pin_pitch=hardware.coord("pin_pitch_h", raw=True),
87 | label_start=(110, 30),
88 | label_pitch=(0, 30),
89 | labels=data.lower_header,
90 | leaderline=lline.Curved(direction="vh"),
91 | )
92 | )
93 |
94 | # Create a title and description text-blocks
95 | title_block = diagram.panel_02.add(
96 | TextBlock(
97 | data.title,
98 | x=20,
99 | y=30,
100 | line_height=18,
101 | tag="panel title_block",
102 | )
103 | )
104 | diagram.panel_02.add(
105 | TextBlock(
106 | data.description,
107 | x=20,
108 | y=60,
109 | width=title_block.width,
110 | height=diagram.panel_02.height - title_block.height,
111 | line_height=18,
112 | tag="panel text_block",
113 | )
114 | )
115 |
116 | # Create a legend
117 | legend = diagram.panel_02.add(
118 | Legend(
119 | data.legend,
120 | x=340,
121 | y=8,
122 | max_height=132,
123 | )
124 | )
125 |
126 | # Export the diagram via commandline:
127 | # >>> py -m pinout.manager --export pinout_diagram.py diagram.svg
128 |
--------------------------------------------------------------------------------
/pinout/components/annotation.py:
--------------------------------------------------------------------------------
1 | import copy
2 | from pinout import core
3 | from pinout.components.text import TextBlock
4 | from pinout.components import leaderline as lline
5 | from pinout.components.layout import Group
6 |
7 | from pinout import config
8 |
9 |
10 | class Leaderline(lline.Curved):
11 | pass
12 |
13 |
14 | class Target(core.Rect):
15 | pass
16 |
17 |
18 | class Body(core.Rect):
19 | pass
20 |
21 |
22 | class Content(TextBlock):
23 | pass
24 |
25 |
26 | class AnnotationLabel(Group):
27 | """Annotation style label."""
28 |
29 | def __init__(
30 | self,
31 | content=None,
32 | body=None,
33 | leaderline=None,
34 | target=None,
35 | **kwargs,
36 | ):
37 | self._content = None
38 | self._body = None
39 | self._leaderline = None
40 | self._target = None
41 |
42 | super().__init__(**kwargs)
43 | self.update_config(config.annotation)
44 | self.add_tag(self.config["tag"])
45 |
46 | self.leaderline = leaderline
47 | self.body = body
48 | self.target = target
49 | # content relied on body - must come after
50 | self.content = content
51 |
52 | # Route leaderline once other elements exist
53 | self._leaderline.route(self.target, self.body)
54 |
55 | self.add(self.leaderline)
56 | self.add(self.target)
57 | self.add(self.body)
58 | self.add(self.content)
59 |
60 | @property
61 | def content(self):
62 | return self._content
63 |
64 | @content.setter
65 | def content(self, content):
66 | content = copy.deepcopy(content or {})
67 | config = self.config["content"]
68 | # Parse content: str > list > dict > TextBlock
69 | if type(content) is str:
70 | content = [content]
71 | if type(content) is list:
72 | content = {
73 | "content": content,
74 | "x": self.body.x + self.body.width / 2,
75 | "y": (self.body.y + self.body.height / 2)
76 | - (config["line_height"] * (len(content) - 1) / 2 * self.scale.y),
77 | }
78 | if isinstance(content, dict):
79 | config.update(content)
80 | content = Content(**config)
81 | content.add_tag(self.config["content"]["tag"])
82 | content.scale = self.scale
83 | self._content = content
84 |
85 | @property
86 | def leaderline(self):
87 | return self._leaderline
88 |
89 | @leaderline.setter
90 | def leaderline(self, leaderline):
91 | leaderline = copy.deepcopy(leaderline or {})
92 | if isinstance(leaderline, dict):
93 | leaderline_config = self.config["leaderline"]
94 | leaderline_config.update(leaderline)
95 | leaderline = Leaderline(**leaderline_config)
96 | leaderline.add_tag(self.config["leaderline"]["tag"])
97 | self._leaderline = leaderline
98 |
99 | @property
100 | def target(self):
101 | return self._target
102 |
103 | @target.setter
104 | def target(self, target):
105 | target = copy.deepcopy(target or {})
106 | if isinstance(target, dict):
107 | target_config = self.config["target"]
108 | target_config.update(target)
109 | target = Target(**target_config)
110 | target.add_tag(self.config["target"]["tag"])
111 | self._target = target
112 |
113 | @property
114 | def body(self):
115 | return self._body
116 |
117 | @body.setter
118 | def body(self, body):
119 | body = copy.deepcopy(body or {})
120 | if isinstance(body, dict):
121 | body_config = self.config["body"]
122 | body_config.update(body)
123 | body = Body(**body_config)
124 | body.add_tag(self.config["body"]["tag"])
125 | self._body = body
126 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/common/arduino_components.py:
--------------------------------------------------------------------------------
1 | # Customised components for Arduino pinout diagram
2 | from pinout.core import Group, Path, Rect
3 | from pinout.components import pinlabel
4 |
5 |
6 | # PinBodyStart and PinBody include an inset shape
7 | INSET = 2
8 |
9 |
10 | class PlbStart(pinlabel.Body):
11 | def render(self):
12 |
13 | output = Group()
14 |
15 | # Label body
16 | radius = self.height / 2
17 | path_def = " ".join(
18 | [
19 | f"M {radius} 0",
20 | f"l {self.width - radius} 0",
21 | f"l 0 {self.height}",
22 | f"l {-(self.width - radius)} 0",
23 | f"a {-radius} {-radius} 0 0 1 0 {-self.height}",
24 | "z",
25 | ]
26 | )
27 | output.add(
28 | Path(
29 | path_definition=path_def,
30 | x=self.x,
31 | y=self.y - (self.height / 2),
32 | width=self.width,
33 | height=self.height,
34 | tag="pinlabel__body",
35 | )
36 | )
37 |
38 | # SVG does not support stroke alignment.
39 | # To achive an 'inner stroke' effect another
40 | # component has been added with the desired inset.
41 |
42 | h = self.height - INSET
43 | w = self.width - INSET
44 | radius = h / 2
45 | path_def = " ".join(
46 | [
47 | f"M {radius + INSET/2} {INSET/2}",
48 | f"l {w - radius} 0",
49 | f"l 0 {h}",
50 | f"l {-(w - radius)} 0",
51 | f"a {-radius} {-radius} 0 0 1 0 {-h}",
52 | "z",
53 | ]
54 | )
55 | output.add(
56 | Path(
57 | path_definition=path_def,
58 | x=self.x,
59 | y=self.y - (self.height / 2),
60 | width=self.width,
61 | height=self.height,
62 | tag="pinlabel__bodyinner",
63 | )
64 | )
65 | return output.render()
66 |
67 |
68 | class PlbEnd(pinlabel.Body):
69 | def render(self):
70 |
71 | output = Group()
72 |
73 | radius = self.height / 2
74 | path_def = " ".join(
75 | [
76 | f"M 0 0",
77 | f"L {self.width - radius} 0",
78 | f"a {radius} {radius} 0 0 1 0 {self.height}",
79 | f"L 0 {self.height}",
80 | "Z",
81 | ]
82 | )
83 | output.add(
84 | Path(
85 | path_definition=path_def,
86 | x=self.x,
87 | y=self.y - (self.height / 2),
88 | width=self.width,
89 | height=self.height,
90 | tag="pinlabel__body",
91 | )
92 | )
93 |
94 | return output.render()
95 |
96 |
97 | class Plb(pinlabel.Body):
98 | # this class differs from the default version as it include an
99 | # # 'inner rect' for custom styling
100 | def render(self):
101 |
102 | output = Group()
103 |
104 | output.add(
105 | Rect(
106 | x=self.x,
107 | y=self.y - (self.height / 2),
108 | width=self.width,
109 | height=self.height,
110 | corner_radius=self.corner_radius,
111 | tag="block pinlabel__body",
112 | )
113 | )
114 |
115 | # Add an inner body for 'inner-stroke' styling
116 | output.add(
117 | Rect(
118 | x=self.x + INSET / 2,
119 | y=self.y - (self.height / 2) + INSET / 2,
120 | width=self.width - INSET,
121 | height=self.height - INSET,
122 | corner_radius=self.corner_radius,
123 | tag="block pinlabel__bodyinner",
124 | )
125 | )
126 | return output.render()
127 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/delete/arduino_components.py:
--------------------------------------------------------------------------------
1 | # Customised components for Arduino pinout diagram
2 | from pinout import core
3 | from pinout.components import pinlabel
4 |
5 |
6 | # PinBodyStart and PinBody include an inset shape
7 | INSET = 2
8 |
9 |
10 | class PlbStart(pinlabel.Body):
11 | def render(self):
12 |
13 | output = Group()
14 |
15 | # Label body
16 | radius = self.height / 2
17 | path_def = " ".join(
18 | [
19 | f"M {radius} 0",
20 | f"l {self.width - radius} 0",
21 | f"l 0 {self.height}",
22 | f"l {-(self.width - radius)} 0",
23 | f"a {-radius} {-radius} 0 0 1 0 {-self.height}",
24 | "z",
25 | ]
26 | )
27 | output.add(
28 | core.Path(
29 | path_definition=path_def,
30 | x=self.x,
31 | y=self.y - (self.height / 2),
32 | width=self.width,
33 | height=self.height,
34 | tag="label__body",
35 | )
36 | )
37 |
38 | # SVG does not support stroke alignment.
39 | # To achive an 'inner stroke' effect another
40 | # component has been added with the desired inset.
41 |
42 | h = self.height - INSET
43 | w = self.width - INSET
44 | radius = h / 2
45 | path_def = " ".join(
46 | [
47 | f"M {radius + INSET/2} {INSET/2}",
48 | f"l {w - radius} 0",
49 | f"l 0 {h}",
50 | f"l {-(w - radius)} 0",
51 | f"a {-radius} {-radius} 0 0 1 0 {-h}",
52 | "z",
53 | ]
54 | )
55 | output.add(
56 | core.Path(
57 | path_definition=path_def,
58 | x=self.x,
59 | y=self.y - (self.height / 2),
60 | width=self.width,
61 | height=self.height,
62 | tag="label__bodyinner",
63 | )
64 | )
65 | return output.render()
66 |
67 |
68 | class PlbEnd(pinlabel.Body):
69 | def render(self):
70 |
71 | output = Group()
72 |
73 | radius = self.height / 2
74 | path_def = " ".join(
75 | [
76 | f"M 0 0",
77 | f"L {self.width - radius} 0",
78 | f"a {radius} {radius} 0 0 1 0 {self.height}",
79 | f"L 0 {self.height}",
80 | "Z",
81 | ]
82 | )
83 | output.add(
84 | core.Path(
85 | path_definition=path_def,
86 | x=self.x,
87 | y=self.y - (self.height / 2),
88 | width=self.width,
89 | height=self.height,
90 | tag="label__body",
91 | )
92 | )
93 |
94 | return output.render()
95 |
96 |
97 | class Plb(pinlabel.Body):
98 | # this class differs from the default version as it include an
99 | # # 'inner rect' for custom styling
100 | def render(self):
101 |
102 | output = core.Group()
103 |
104 | output.add(
105 | core.Rect(
106 | x=self.x,
107 | y=self.y - (self.height / 2),
108 | width=self.width,
109 | height=self.height,
110 | corner_radius=self.corner_radius,
111 | tag="block label__body",
112 | )
113 | )
114 |
115 | # Add an inner body for 'inner-stroke' styling
116 | output.add(
117 | core.Rect(
118 | x=self.x + INSET / 2,
119 | y=self.y - (self.height / 2) + INSET / 2,
120 | width=self.width - INSET,
121 | height=self.height - INSET,
122 | corner_radius=self.corner_radius,
123 | tag="block label__bodyinner",
124 | )
125 | )
126 | return output.render()
127 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/delete/styles.css:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | svg {
6 | fill-opacity: 1;
7 | font-family: 'Roboto', Helvetica, Verdana, monospace;
8 | font-size: 13px;
9 | font-weight: 300;
10 | opacity: 1;
11 |
12 | }
13 | path, rect {
14 | stroke: rgb(0, 0, 0);
15 | stroke-dasharray: 0;
16 | stroke-dashoffset: 0;
17 | stroke-linecap: round;
18 | stroke-linejoin: miter;
19 | stroke-miterlimit: inherit;
20 | stroke-opacity: 1;
21 | stroke-width: 0;
22 | }
23 | path{
24 | fill: none;
25 | }
26 | rect {
27 | fill: rgba(0, 0, 0, 0);
28 | }
29 |
30 | text {
31 | dominant-baseline: auto;
32 | }
33 |
34 |
35 |
36 |
37 |
38 | .label__body{
39 | fill: rgb(36, 36, 36);
40 | }
41 | .label__bodyinner{
42 | fill: none;
43 | stroke-linejoin: "miter";
44 | }
45 | .label .lline{
46 | stroke-width: 0;
47 | }
48 | .label__text{
49 | dominant-baseline: central;
50 | fill: #fff;
51 | font-size: 12px;
52 | font-weight: 600;
53 | stroke-width: 0;
54 | text-anchor: middle;
55 | }
56 |
57 | .legend rect{
58 | fill: none;
59 | }
60 | .legend .swatch__body{
61 | fill: rgba(0,0,0,0);
62 | }
63 | .legend-entry__text{
64 | dominant-baseline: central;
65 | fill: #000;
66 | }
67 |
68 |
69 |
70 |
71 | .diagram__bg{
72 | fill: #333;
73 | }
74 |
75 | .panel--main .panel__bg{
76 | fill: #fff;
77 | }
78 |
79 | .panel--notes .panel__bg{
80 | fill: #e4e4e2;
81 | }
82 |
83 | .led .label__body,
84 | .gnd .label__body,
85 | .pwr .label__body,
86 | .nc .label__body,
87 | .nc .label__bodyinner,
88 | .other .label__body,
89 | .other .label__bodyinner{
90 | rx: 10px;
91 | }
92 |
93 | .label__row .label:first-child .lline,
94 | .label.show-leader .lline{
95 | stroke-width: 2px;
96 | stroke: #bcc6c6;
97 | }
98 |
99 | .led .label__body,
100 | .led .swatch__body{
101 | fill: #1da086;
102 | }
103 |
104 | .mu-port .label__body,
105 | .mu-port .swatch__body{
106 | fill: #f39c12;
107 | }
108 | .default .label__body,
109 | .default .swatch__body{
110 | fill: #f1c40f;
111 | }
112 | .default .label__text{
113 | fill: #000;
114 | }
115 | .digital .label__body,
116 | .digital .swatch__body{
117 | fill: url("#stripe-a");
118 | }
119 |
120 | #stripe-a rect{
121 | fill: #d35400;
122 | }
123 |
124 | #stripe-a path{
125 | fill: #de7f40;
126 | }
127 |
128 | .analog .label__body{
129 | fill: url("#stripe-b");
130 | }
131 |
132 | #stripe-b rect{
133 | fill: #ffffff;
134 | }
135 | #stripe-b path{
136 | fill: #f8e6d9;
137 | }
138 |
139 | .analog .label__bodyinner{
140 | fill: none;
141 | stroke: #d35400;
142 | stroke-width: 2;
143 | }
144 |
145 | .analog .label__text{
146 | fill: #d35400;
147 | }
148 |
149 | .analog .swatch__body{
150 | fill: url("#stripe-b");
151 | stroke: #d35400;
152 | stroke-width: 2;
153 | }
154 | .pwr .label__body,
155 | .pwr .swatch__body{
156 | fill: #c11f09;
157 | }
158 |
159 | .other .label__body,
160 | .other .swatch__body{
161 | fill: #fff;
162 | }
163 |
164 | .other .label__bodyinner,
165 | .other .swatch__body{
166 | stroke: #d86e29;
167 | stroke-width: 2;
168 | }
169 |
170 | .other .label__text{
171 | fill: #d86e29;
172 | }
173 |
174 | .gnd .label__body,
175 | .gnd .swatch__body{
176 | fill: #000;
177 | }
178 |
179 | .internal .label__body,
180 | .internal .swatch__body{
181 | fill: #94a3a6;
182 | }
183 |
184 | .swd .label__body,
185 | .swd .swatch__body{
186 | fill: #9e856e;
187 | }
188 |
189 | .nc .label__body,
190 | .nc .swatch__body{
191 | fill: #fff;
192 | }
193 | .nc .label__text{
194 | fill: #ccc;
195 | }
196 |
197 | .nc .label__bodyinner,
198 | .nc .swatch__body{
199 | stroke: #ccc;
200 | stroke-width: 2;
201 | }
202 |
203 |
204 |
205 | .h1{
206 | font-size: 28px;
207 | font-weight: 900;
208 | }
209 | .p{
210 | fill: #333;
211 | }
212 | .strong{ font-weight: bold; }
213 | .italic{ font-style: italic; }
--------------------------------------------------------------------------------
/samples/teensy_4.0/styles.css:
--------------------------------------------------------------------------------
1 | .gnd .pinlabel__body,
2 | .gnd .textblock__bg {
3 | fill: #000; }
4 |
5 | .gnd .pinlabel__leader {
6 | stroke: #000; }
7 |
8 | .gnd.legend__entry .panel__inner {
9 | fill: #000; }
10 |
11 | .pwr .pinlabel__body,
12 | .pwr .textblock__bg {
13 | fill: #ad0000; }
14 |
15 | .pwr .pinlabel__leader {
16 | stroke: #ad0000; }
17 |
18 | .pwr.legend__entry .panel__inner {
19 | fill: #ad0000; }
20 |
21 | .digital .pinlabel__body,
22 | .digital .textblock__bg {
23 | fill: #cfd5d5; }
24 |
25 | .digital .pinlabel__leader {
26 | stroke: #cfd5d5; }
27 |
28 | .digital.legend__entry .panel__inner {
29 | fill: #cfd5d5; }
30 |
31 | .analog .pinlabel__body,
32 | .analog .textblock__bg {
33 | fill: #ffd7ad; }
34 |
35 | .analog .pinlabel__leader {
36 | stroke: #ffd7ad; }
37 |
38 | .analog.legend__entry .panel__inner {
39 | fill: #ffd7ad; }
40 |
41 | .pwm .pinlabel__body,
42 | .pwm .textblock__bg {
43 | fill: #ffb3aa; }
44 |
45 | .pwm .pinlabel__leader {
46 | stroke: #ffb3aa; }
47 |
48 | .pwm.legend__entry .panel__inner {
49 | fill: #ffb3aa; }
50 |
51 | .audio .pinlabel__body,
52 | .audio .textblock__bg {
53 | fill: #f7f09d; }
54 |
55 | .audio .pinlabel__leader {
56 | stroke: #f7f09d; }
57 |
58 | .audio.legend__entry .panel__inner {
59 | fill: #f7f09d; }
60 |
61 | .serial .pinlabel__body,
62 | .serial .textblock__bg {
63 | fill: #c9daed; }
64 |
65 | .serial .pinlabel__leader {
66 | stroke: #c9daed; }
67 |
68 | .serial.legend__entry .panel__inner {
69 | fill: #c9daed; }
70 |
71 | .i2c .pinlabel__body,
72 | .i2c .textblock__bg {
73 | fill: #b1add7; }
74 |
75 | .i2c .pinlabel__leader {
76 | stroke: #b1add7; }
77 |
78 | .i2c.legend__entry .panel__inner {
79 | fill: #b1add7; }
80 |
81 | .spi .pinlabel__body,
82 | .spi .textblock__bg {
83 | fill: #c2e6b9; }
84 |
85 | .spi .pinlabel__leader {
86 | stroke: #c2e6b9; }
87 |
88 | .spi.legend__entry .panel__inner {
89 | fill: #c2e6b9; }
90 |
91 | .canbus .pinlabel__body,
92 | .canbus .textblock__bg {
93 | fill: #fedae2; }
94 |
95 | .canbus .pinlabel__leader {
96 | stroke: #fedae2; }
97 |
98 | .canbus.legend__entry .panel__inner {
99 | fill: #fedae2; }
100 |
101 | .note .pinlabel__body,
102 | .note .textblock__bg {
103 | fill: #ddd; }
104 |
105 | .note .pinlabel__leader {
106 | stroke: #ddd; }
107 |
108 | .note.legend__entry .panel__inner {
109 | fill: #ddd; }
110 |
111 | .pinlabel__leader {
112 | fill: none;
113 | stroke-width: 0; }
114 |
115 | .pingroup g .pinlabel:first-child .pinlabel__leader,
116 | .lline--visible .pinlabel__leader {
117 | stroke-width: 2; }
118 |
119 | .pinlabel__text {
120 | dominant-baseline: central;
121 | fill: #000;
122 | font-size: 20px;
123 | font-weight: normal;
124 | letter-spacing: -1px;
125 | stroke-width: 0;
126 | text-anchor: middle; }
127 |
128 | .gnd .pinlabel__text,
129 | .pwr .pinlabel__text {
130 | fill: #fff; }
131 |
132 | .tight text {
133 | letter-spacing: -2px; }
134 |
135 | .titlebar__bg {
136 | fill: white; }
137 |
138 | .h1 text {
139 | font-size: 36px;
140 | font-weight: bold; }
141 |
142 | .h2 text {
143 | font-size: 18px;
144 | font-weight: bold;
145 | font-style: italic; }
146 |
147 | .italic {
148 | font-style: italic; }
149 |
150 | .strong {
151 | font-weight: bold; }
152 |
153 | .url {
154 | fill: #0084ff;
155 | font-weight: bold; }
156 |
157 | text {
158 | fill: #000;
159 | font-family: Arial, Georgia, sans-serif;
160 | font-size: 16px;
161 | font-weight: 300;
162 | dominant-baseline: auto; }
163 |
164 | .legend__bg {
165 | fill: black; }
166 |
167 | .legend__entry .panel__outer {
168 | fill: black; }
169 |
170 | .legend__title {
171 | font-size: 18px;
172 | font-weight: bold;
173 | dominant-baseline: auto; }
174 |
175 | .legend text {
176 | fill: #000;
177 | font-family: Arial, Georgia, sans-serif;
178 | font-size: 16px;
179 | font-weight: 300;
180 | dominant-baseline: auto; }
181 |
182 | .diagram__bg {
183 | fill: #000; }
184 |
185 | .panel__inner {
186 | fill: white; }
187 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/arduino_uno.py:
--------------------------------------------------------------------------------
1 | from pinout.core import Group, Image, Raw, Rect
2 | from pinout.components.layout import Diagram, Panel
3 | from pinout.components import pinlabel, legend, leaderline
4 | from pinout.components.text import TextBlock
5 | import sys
6 |
7 | # Import data and custom function
8 | # Slightly inelegant method to amend module search path
9 | sys.path.append("..")
10 | import uno_data as data
11 | from common.preprocessor import pinlabel_preprocessor as prep
12 |
13 | # Create a new digram
14 | diagram = Diagram(1200, 675, tag="arduino-rp2040-connect")
15 |
16 | # Add a stylesheet and some custom patterns
17 | diagram.add_stylesheet("../common/styles.css", True)
18 |
19 | # Load some svg markup to be used as patterns
20 | # The Raw class allows arbitary code to be inserted
21 | # into the diagram.
22 | with open("../common/patterns.xml") as f:
23 | patterns = f.read()
24 | diagram.add_def(Raw(patterns))
25 |
26 | # Construct a layout and add some backgrounds
27 | diagram.add(Rect(x=0, y=0, width=1200, height=675, tag="diagram__bg"))
28 | group_main = diagram.add(Group(2, 2, tag="panel panel--main"))
29 | group_main.add(Rect(x=0, y=0, width=1196, height=548, tag="panel__bg"))
30 |
31 | # Keeping elements in a group allows for easier measuring and moving
32 | # Create a group for the main pinout graphic
33 | pinout_graphic = group_main.add(Group(600, 10, tag="pinout-graphic"))
34 |
35 | group_notes = diagram.add(Group(2, 552, tag="panel panel--notes"))
36 | group_notes.add(Rect(x=0, y=0, width=1196, height=121, tag="panel__bg"))
37 | group_notes.add(
38 | legend.Legend(data.legend, max_height=112, x=10, y=5, inset=(0, 0, 0, 0))
39 | )
40 | group_notes.add(TextBlock(data.title_1, 22, x=580, y=30))
41 | group_notes.add(TextBlock(data.para_1, 17, x=580, y=74))
42 | group_notes.add(TextBlock(data.para_2, 17, x=900, y=74))
43 |
44 | # Add a hardware image
45 | # Note its coordinates are relative to the group it is within
46 | pinout_graphic.add(
47 | Image(
48 | "hardware.png",
49 | x=-326 / 2,
50 | y=0,
51 | width=326,
52 | height=455,
53 | embed=True,
54 | )
55 | )
56 | # Right hand side pin headers
57 | pinout_graphic.add(
58 | pinlabel.PinLabelGroup(
59 | x=147,
60 | y=153,
61 | pin_pitch=(0, 15.35),
62 | label_start=(90, -84),
63 | label_pitch=(0, 23.35),
64 | labels=prep(data.header_rhs_a),
65 | tag="pinheader",
66 | )
67 | )
68 |
69 | pinout_graphic.add(
70 | pinlabel.PinLabelGroup(
71 | x=147,
72 | y=316,
73 | pin_pitch=(0, 15.35),
74 | label_start=(90, 8),
75 | label_pitch=(0, 23.35),
76 | labels=prep(data.header_rhs_b),
77 | tag="pinheader",
78 | )
79 | )
80 |
81 | # Left hand side pin header
82 | pinout_graphic.add(
83 | pinlabel.PinLabelGroup(
84 | x=-147,
85 | y=208,
86 | pin_pitch=(0, 15.35),
87 | label_start=(90, -64),
88 | label_pitch=(0, 23.35),
89 | scale=(-1, 1),
90 | labels=prep(data.header_lhs_a),
91 | tag="pinheader",
92 | )
93 | )
94 | pinout_graphic.add(
95 | pinlabel.PinLabelGroup(
96 | x=-147,
97 | y=347,
98 | pin_pitch=(0, 15.35),
99 | label_start=(90, 12),
100 | label_pitch=(0, 23.35),
101 | scale=(-1, 1),
102 | labels=prep(data.header_lhs_b),
103 | tag="pinheader",
104 | )
105 | )
106 |
107 |
108 | # LEDs RX & TX
109 | pinout_graphic.add(
110 | pinlabel.PinLabelGroup(
111 | x=46,
112 | y=206,
113 | pin_pitch=(17, 0),
114 | label_start=(284, 120),
115 | label_pitch=(0, 23),
116 | scale=(-1, -1),
117 | labels=prep(data.leds_a),
118 | leaderline=leaderline.Curved(direction="vh"),
119 | tag="pinheader",
120 | )
121 | )
122 |
123 | # LEDs pwr
124 | pinout_graphic.add(
125 | pinlabel.PinLabelGroup(
126 | x=62,
127 | y=392,
128 | pin_pitch=(39, -185),
129 | label_start=(38, 90),
130 | label_pitch=(0, 23.35),
131 | scale=(-1, 1),
132 | labels=prep(data.leds_b),
133 | leaderline=leaderline.Curved(direction="vh"),
134 | tag="pinheader",
135 | )
136 | )
137 |
--------------------------------------------------------------------------------
/tests/test_samples.py:
--------------------------------------------------------------------------------
1 | ##########################################################
2 | #
3 | # pinout tests (with coverage reporting)
4 | #
5 | # Use a user-defined temporary directory if
6 | # you have problems with multiple harddrives (like I do):
7 | #
8 | # >>> coverage run -m pytest --basetemp=temp
9 | #
10 | # Build coverage html report
11 | #
12 | # >>> coverage html
13 | #
14 | ##########################################################
15 | import filecmp
16 | import pytest
17 | import re
18 | import shutil
19 | import uuid
20 | from pathlib import Path
21 | from importlib import reload
22 | from pinout import manager
23 | from pinout import config
24 |
25 |
26 | def re_sub_ids(re_m):
27 | id = re_m.group(0).split("_")
28 | id = "unique_id_replaced-for-testing_" + id[-1]
29 | return id
30 |
31 |
32 | def mk_test_file(src, dest):
33 |
34 | shutil.copyfile(src, dest)
35 | with src.open() as f:
36 | data = f.read()
37 | # sub ids
38 | id = re.compile(r"(?<=id=\").+(?=\")")
39 | data = re.sub(id, re_sub_ids, data)
40 | # sub hrefs
41 | id = re.compile(r"(?<=href=\"#).+(?=\")")
42 | data = re.sub(id, re_sub_ids, data)
43 | # sub clip-path urls
44 | id = re.compile(r"(?<=clip-path=\"url\(#).+(?=\")")
45 | data = re.sub(id, re_sub_ids, data)
46 | # write modified file data to testfile
47 | dest.write_text(data)
48 | return dest
49 |
50 |
51 | @pytest.mark.parametrize(
52 | "module_path, ref_path",
53 | [
54 | # Components
55 | (
56 | "./resources/diagram_export.py",
57 | "./resources/diagram_export.svg",
58 | ),
59 | (
60 | "./resources/diagram_image.py",
61 | "./resources/diagram_image.svg",
62 | ),
63 | # Samples
64 | (
65 | "../samples/arduino/arduino/uno/arduino_uno.py",
66 | "../samples/arduino/pinout_arduino_uno_rev3.svg",
67 | ),
68 | (
69 | "../samples/arduino/arduino/rp2040/arduino_nano_rp2040_connect.py",
70 | "../samples/arduino/pinout_arduino_nano_rp2040_connect.svg",
71 | ),
72 | (
73 | "../samples/attiny85/attiny85.py",
74 | "../samples/attiny85/pinout_attiny85.svg",
75 | ),
76 | (
77 | "../samples/clip_path/pinout_diagram.py",
78 | "../samples/clip_path/diagram.svg",
79 | ),
80 | (
81 | "../samples/full_sample/pinout_diagram.py",
82 | "../samples/full_sample/pinout_diagram.svg",
83 | ),
84 | (
85 | "../samples/panel_layout/panel_layout.py",
86 | "../samples/panel_layout/output/panel_layout.svg",
87 | ),
88 | (
89 | "../samples/panel_layout/populated_layout.py",
90 | "../samples/panel_layout/output/populated_layout.svg",
91 | ),
92 | (
93 | "../samples/pci-express/pinout_x1.py",
94 | "../samples/pci-express/pinout_x1.svg",
95 | ),
96 | (
97 | "../samples/section_pullout/pinout_diagram.py",
98 | "../samples/section_pullout/diagram.svg",
99 | ),
100 | (
101 | "../samples/teensy_4.0/pinout_diagram.py",
102 | "../samples/teensy_4.0/teensy_4.0_front_pinout_diagram.svg",
103 | ),
104 | ],
105 | )
106 | def test_output_against_reference(tmp_path, module_path, ref_path):
107 | # Config requires reloading between tests to to ensure
108 | # is in default state.
109 | reload(config)
110 |
111 | module_path = Path(module_path)
112 | ref_path = Path(ref_path)
113 |
114 | # Export a temp file in same location as reference:
115 | # Required for relative links to be identical.
116 | tempsvg = ref_path.parent / f"temp_pytest_{str(uuid.uuid4())}.svg"
117 | manager.export_diagram(
118 | module_path,
119 | tempsvg,
120 | overwrite=True,
121 | )
122 |
123 | # Create files for comparison. Unique ids are converted to match
124 | file1 = mk_test_file(tempsvg, tmp_path / f"test_file.svg")
125 | file2 = mk_test_file(ref_path, tmp_path / f"ref_file.svg")
126 |
127 | # Remove temp file
128 | tempsvg.unlink()
129 |
130 | # Test files are identical
131 | assert filecmp.cmp(file1, file2, shallow=False)
132 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/uno/uno_data.py:
--------------------------------------------------------------------------------
1 | #########################################################
2 | #
3 | # Legend
4 | #
5 | #########################################################
6 |
7 | legend = [
8 | ("Ground", "gnd"),
9 | ("Power", "pwr"),
10 | ("LED", "led"),
11 | ("Internal Pin", "internal"),
12 | ("SWD Pin", "swd"),
13 | ("Digital Pin", "digital"),
14 | ("Analog Pin", "analog"),
15 | ("Other Pin", "other"),
16 | ("Microcontroller's Port", "mu-port"),
17 | ("Default", "default"),
18 | ]
19 |
20 | #########################################################
21 | #
22 | # Header: Right-hand-side
23 | #
24 | #########################################################
25 |
26 |
27 | header_rhs_a = [
28 | [
29 | ("D19/SCL", "digital"),
30 | ("PC5", "mu-port"),
31 | ("SCL", "default"),
32 | ],
33 | [
34 | ("D18/SDA", "digital"),
35 | ("PC4", "mu-port"),
36 | ("SDA", "default"),
37 | ],
38 | [
39 | ("AREF", "other"),
40 | ],
41 | [
42 | ("GND", "gnd"),
43 | ],
44 | [
45 | ("D13", "digital"),
46 | ("PB5", "mu-port"),
47 | ("SCK", "default"),
48 | ],
49 | [
50 | ("D12", "digital"),
51 | ("PB4", "mu-port"),
52 | ("MISO", "default"),
53 | ],
54 | [
55 | ("~D11", "digital"),
56 | ("PB3", "mu-port"),
57 | ("MOSI", "default"),
58 | ],
59 | [
60 | ("~D10", "digital"),
61 | ("PB2", "mu-port"),
62 | ("SS", "default"),
63 | ],
64 | [
65 | ("~D9", "digital"),
66 | ("PB1", "mu-port"),
67 | ],
68 | [
69 | ("D8", "digital"),
70 | ("PB0", "mu-port"),
71 | ],
72 | ]
73 |
74 | header_rhs_b = [
75 | [
76 | ("D7", "digital"),
77 | ("PD7", "default"),
78 | ],
79 | [
80 | ("~D6", "digital"),
81 | ("PD6", "default"),
82 | ],
83 | [
84 | ("~D5", "digital"),
85 | ("PD5", "mu-port"),
86 | ],
87 | [
88 | ("D4", "digital"),
89 | ("PD4", "mu-port"),
90 | ],
91 | [
92 | ("~D3", "digital"),
93 | ("PD3", "mu-port"),
94 | ],
95 | [
96 | ("D2", "digital"),
97 | ("PD2", "mu-port"),
98 | ],
99 | [
100 | ("D1/TX", "digital"),
101 | ("PD1", "mu-port"),
102 | ],
103 | [
104 | ("D0/RX", "digital"),
105 | ("PD0", "mu-port"),
106 | ],
107 | ]
108 |
109 | #########################################################
110 | #
111 | # Header: Left-hand-side
112 | #
113 | #########################################################
114 |
115 | header_lhs_a = [
116 | [("NC", "nc")],
117 | [("IOREF", "other")],
118 | [
119 | ("RESET", "other"),
120 | ("PC6", "default"),
121 | ],
122 | [("+3V3", "pwr")],
123 | [("+5", "pwr")],
124 | [("GND", "gnd")],
125 | [("GND", "gnd")],
126 | [("VIN", "pwr")],
127 | ]
128 |
129 | # Example of using a list comprehension to generate pin data
130 |
131 |
132 | header_lhs_b = [
133 | [
134 | (f"D{14 + i}", "digital"),
135 | (f"A{i}", "analog"),
136 | (f"PC{i}", "mu-port"),
137 | (f"ADC[{i}]", "default"),
138 | ]
139 | for i in range(6)
140 | ]
141 |
142 | #########################################################
143 | #
144 | # LED labels
145 | #
146 | #########################################################
147 |
148 | leds_a = [
149 | [
150 | ("RX LED", "led"),
151 | ("PD4", "default"),
152 | ],
153 | [
154 | ("TX LED", "led"),
155 | ("PD5", "default"),
156 | ],
157 | ]
158 |
159 |
160 | leds_b = [
161 | [
162 | ("POWER", "led"),
163 | ],
164 | [
165 | ("LED_BUILTIN", "led"),
166 | ("PB5", "default"),
167 | ],
168 | ]
169 |
170 | #########################################################
171 | #
172 | # Text blocks
173 | #
174 | #########################################################
175 |
176 | title_1 = [
177 | 'ARDUINO',
178 | 'UNO REV3',
179 | ]
180 | para_1 = [
181 | 'Pinout diagram created with pinout (v0.0.10)',
182 | 'A Python package for creating pinout diagrams.',
183 | 'pinout.readthedocs.io',
184 | ]
185 | para_2 = [
186 | 'NOTE: This is not official documentation.',
187 | 'Diagram aesthetics from Arduino docs.',
188 | 'https://www.arduino.cc/',
189 | ]
--------------------------------------------------------------------------------
/samples/attiny85/attiny85.py:
--------------------------------------------------------------------------------
1 | from pinout import config
2 | from pinout.components import integrated_circuits as ic
3 | from pinout.components.legend import Legend
4 | from pinout.components.layout import Diagram, Group, Panel
5 | from pinout.components.text import TextBlock
6 |
7 | legend_data = [
8 | ("Port", "port"),
9 | ("Analog", "adc"),
10 | ("Analog Comparator", "comparator"),
11 | ("Ground", "gnd"),
12 | ("Interrupt", "interrupt"),
13 | ("Timer-counter", "timer-counter"),
14 | ("Communications", "comms"),
15 | ("Oscillator", "oscillator"),
16 | ("Power", "pwr"),
17 | ]
18 |
19 | attiny85 = [
20 | [
21 | ("PB5", "port"),
22 | ("dW", "port"),
23 | ("ADC0", "adc"),
24 | ("RESET", "gnd"),
25 | ("PCINT5", "interrupt"),
26 | ],
27 | [
28 | ("PB3", "port"),
29 | ("ADC3", "adc"),
30 | ("OC1B", "timer-counter"),
31 | ("CLKI", "comms"),
32 | ("XTAL1", "oscillator"),
33 | ("PCINT3", "interrupt"),
34 | ],
35 | [
36 | ("PB4", "port"),
37 | ("ADC2", "adc"),
38 | ("OC1B", "timer-counter"),
39 | ("CLKO", "port"),
40 | ("XTAL2", "oscillator"),
41 | ("PCINT4", "interrupt"),
42 | ],
43 | [
44 | ("GND", "gnd"),
45 | ],
46 | [
47 | ("PB0", "port"),
48 | ("MOSI", "comms"),
49 | ("DI", "comms"),
50 | ("SDA", "comms"),
51 | ("AIN0", "comparator"),
52 | ("OC0A", "timer-counter"),
53 | ("OC1A", "timer-counter"),
54 | ("AREF", "pwr"),
55 | ("PCINT0", "interrupt"),
56 | ],
57 | [
58 | ("PB1", "port"),
59 | ("MISO", "comms"),
60 | ("DO", "comms"),
61 | ("AIN1", "comparator"),
62 | ("OC0B", "timer-counter"),
63 | ("OC1A", "timer-counter"),
64 | ("PCINT1", "interrupt"),
65 | ],
66 | [
67 | ("PB2", "port"),
68 | ("SCK", "comms"),
69 | ("USCK", "comms"),
70 | ("SCL", "comms"),
71 | ("ADC1", "adc"),
72 | ("T0", "timer-counter"),
73 | ("INT0", "interrupt"),
74 | ("PCINT2", "interrupt"),
75 | ],
76 | [
77 | ("VCC", "pwr"),
78 | ],
79 | ]
80 |
81 | attiny85_QFN = (
82 | attiny85[0:2]
83 | + [[("DNC", "dnc")]] * 2
84 | + attiny85[2:3]
85 | + [[("DNC", "dnc")]] * 2
86 | + attiny85[3:4]
87 | + [[("DNC", "dnc")]] * 2
88 | + attiny85[4:6]
89 | + [[("DNC", "dnc")]]
90 | + attiny85[6:]
91 | + [[("DNC", "dnc")]] * 5
92 | )
93 |
94 | # Modify default config
95 | config.pinlabel["body"]["width"] = 50
96 | config.ic_qfp["inset"] = (3, 3, 3, 3)
97 | config.panel["inset"] = (1.5, 1.5, 1.5, 1.5)
98 | config.legend["entry"]["width"] = 200
99 | config.legend["entry"]["inset"] = 0
100 |
101 | # Add pin numbers in a list comprehension
102 | attiny85_numbered = [
103 | [(f"{i+1}", "pin_id", {"body": {"width": 20, "height": 15}})] + row
104 | for i, row in enumerate(attiny85)
105 | ]
106 |
107 |
108 | diagram = Diagram(1200, 675)
109 | diagram.add_stylesheet("attiny_styles.css")
110 | content = diagram.add(Panel(width=1200, height=675))
111 | dip_panel = content.add(Panel(x=0, y=0, width=content.inset_width, height=200))
112 | qfn_panel = content.add(Panel(x=0, y=200, width=content.inset_width, height=360))
113 | title_panel = content.add(
114 | Panel(
115 | x=0,
116 | y=qfn_panel.bounding_coords().y2,
117 | width=content.inset_width,
118 | height=content.inset_height - qfn_panel.bounding_coords().y2,
119 | tag="panel__title",
120 | )
121 | )
122 |
123 | dip_panel.add(
124 | TextBlock("PDIP / SOIC / TSSOP", x=10, y=30)
125 | )
126 | dip_graphic = dip_panel.add(Group(450, 50))
127 | dip_graphic.add(
128 | ic.labelled_dip(labels=attiny85_numbered, width=110, height=120, label_start_x=50)
129 | )
130 |
131 | qfn_numbered = [
132 | [(f"{i+1}", "pin_id", {"body": {"width": 20, "height": 15}})] + row
133 | for i, row in enumerate(attiny85_QFN)
134 | ]
135 | qfn_panel.add(TextBlock("QFN / MLF", x=10, y=30))
136 | qfn_graphic = qfn_panel.add(Group(500, 110))
137 | qfn_graphic.add(ic.labelled_qfn(labels=qfn_numbered, length=100, label_start=(50, 10)))
138 |
139 |
140 | title_panel.add(
141 | TextBlock(
142 | [
143 | "Pinout ATtiny25/45/85",
144 | "Created with pinout Python package.",
145 | "pinout.readthedocs.io",
146 | ],
147 | x=10,
148 | y=30,
149 | )
150 | )
151 | title_panel.add(
152 | Legend(
153 | legend_data,
154 | max_height=96,
155 | x=title_panel.width - 614,
156 | y=14,
157 | )
158 | )
159 |
--------------------------------------------------------------------------------
/samples/teensy_4.0/teensy_4_data.py:
--------------------------------------------------------------------------------
1 | lline_84 = {"body": {"x": 80}, "tag": "lline--visible"}
2 | lline_53 = {"body": {"x": 60}, "tag": "lline--visible"}
3 | lline_40 = {"body": {"x": 40}, "tag": "lline--visible"}
4 | lg_body = {"body": {"width": 170}}
5 |
6 |
7 | # Pinlabels
8 |
9 | header_rhs = [
10 | [("Vin", "pwr"), ("(3.6 to 5.5 volts)", "note", lg_body)],
11 | [("GND", "gnd")],
12 | [("3.3V", "pwr"), ("(250 mA max)", "note", lg_body)],
13 | [
14 | ("23", "digital"),
15 | ("A9", "analog"),
16 | ("PWM", "pwm"),
17 | ("CRX1", "canbus"),
18 | ("MCLK1", "audio"),
19 | ],
20 | [
21 | ("22", "digital"),
22 | ("A8", "analog"),
23 | ("PWM", "pwm"),
24 | ("CTX1", "canbus"),
25 | ("MCLK1", "audio"),
26 | ],
27 | [
28 | ("21", "digital"),
29 | ("A7", "analog"),
30 | ("RX5", "serial", lline_53),
31 | ("BCLK1", "audio"),
32 | ],
33 | [
34 | ("20", "digital"),
35 | ("A6", "analog"),
36 | ("TX5", "serial", lline_53),
37 | ("LRCLK1", "audio"),
38 | ],
39 | [("19", "digital"), ("A5", "analog"), ("PWM", "pwm"), ("SCL0", "i2c", lline_84)],
40 | [("18", "digital"), ("A4", "analog"), ("PWM", "pwm"), ("SDA0", "i2c", lline_84)],
41 | [("17", "digital"), ("A3", "analog"), ("TX4", "serial", lline_53), ("SDA1", "i2c")],
42 | [("16", "digital"), ("A2", "analog"), ("RX4", "serial", lline_53), ("SCL1", "i2c")],
43 | [
44 | ("15", "digital"),
45 | ("A1", "analog"),
46 | ("PWM", "pwm"),
47 | ("RX3", "serial"),
48 | ("DIF IN", "audio tight"),
49 | ],
50 | [
51 | ("14", "digital"),
52 | ("A0", "analog"),
53 | ("PWM", "pwm"),
54 | ("TX3", "serial"),
55 | ("DIF OUT", "audio tight"),
56 | ],
57 | [
58 | ("13", "digital"),
59 | ("PWM", "pwm", lline_40),
60 | ("CRX1", "canbus"),
61 | ("SCK", "spi"),
62 | ],
63 | ]
64 |
65 | header_lhs = [
66 | [("GND", "gnd")],
67 | [("0", "digital"), ("PWM", "pwm"), ("RX1", "serial"), ("CRX2", "canbus")],
68 | [("1", "digital"), ("PWM", "pwm"), ("TX1", "serial"), ("CTX2", "canbus")],
69 | [("2", "digital"), ("PWM", "pwm"), ("OUT2", "audio", lline_84)],
70 | [("3", "digital"), ("PWM", "pwm"), ("LRCLK2", "audio", lline_84)],
71 | [("4", "digital"), ("PWM", "pwm"), ("BCLK2", "audio", lline_84)],
72 | [("5", "digital"), ("PWM", "pwm"), ("IN2", "audio", lline_84)],
73 | [("6", "digital"), ("PWM", "pwm"), ("OUT1D", "audio", lline_84)],
74 | [("7", "digital"), ("PWM", "pwm"), ("RX2", "serial"), ("OUT1A", "audio")],
75 | [("8", "digital"), ("PWM", "pwm"), ("TX2", "serial"), ("IN1", "audio")],
76 | [("9", "digital"), ("PWM", "pwm"), ("OUT1C", "audio", lline_84)],
77 | [("10", "digital"), ("PWM", "pwm"), ("CS", "spi"), ("MQSR", "audio")],
78 | [("11", "digital"), ("PWM", "pwm"), ("MOSI", "spi"), ("CTX1", "canbus")],
79 | [("12", "digital"), ("PWM", "pwm"), ("MISO", "spi"), ("MQSL", "audio")],
80 | ]
81 |
82 |
83 | header_end_lhs = [
84 | [("VBat", "pwr")],
85 | [("3.3v", "pwr")],
86 | [("GND", "gnd")],
87 | ]
88 | header_end_rhs = [
89 | [("Prog", "pwr")],
90 | [("On/Off", "gnd")],
91 | ]
92 |
93 |
94 | # Legend
95 | legend_digital = """Digital Pins
96 | digitalRead
97 | digitalWrite
98 | pinMode"""
99 |
100 | legend_analog = """Analog Pins
101 | analogRead"""
102 |
103 | legend_pwm = """PWM Pins
104 | analogWrite"""
105 |
106 | legend_audio = """Digital Audio
107 | Audio library"""
108 |
109 | legend_serial = """Serial Ports
110 | Serial1 - Serial7"""
111 |
112 | legend_i2c = """I2C Port
113 | Wire library"""
114 |
115 | legend_spi = """SPI Port
116 | SPI library"""
117 |
118 | legend_canbus = """CAN Bus
119 | FlexCAN_t4
120 | library"""
121 |
122 | legend_content = [
123 | (legend_digital, "digital"),
124 | (legend_analog, "analog"),
125 | (legend_pwm, "pwm"),
126 | (legend_audio, "audio"),
127 | (legend_serial, "serial"),
128 | (legend_i2c, "i2c"),
129 | (legend_spi, "spi"),
130 | (legend_canbus, "canbus"),
131 | ]
132 |
133 | # Text
134 |
135 | title = "Teensy 4 pinout"
136 |
137 | title_2 = """32 Bit Arduino Compatible
138 | Microcontroller"""
139 |
140 | instructions = """To begin please visit 'Getting Started'
141 | at www.pjrc.com/teensy"""
142 |
143 | notes = """• All digital pins have interrupt capability.
144 | • Loading status (Red LED): dim: Ready | bright: Writing | blink: No USB
145 | • LED on pin-13"""
--------------------------------------------------------------------------------
/pinout/config.py:
--------------------------------------------------------------------------------
1 | ################################
2 | #
3 | # Default component settings
4 | #
5 | ################################
6 |
7 | # Pinlabel
8 | pinlabel = {
9 | "tag": "pinlabel",
10 | "body": {
11 | "x": 6,
12 | "y": 0,
13 | "width": 80,
14 | "height": 26,
15 | "corner_radius": 3,
16 | "tag": "pinlabel__body",
17 | },
18 | "leaderline": {
19 | "direction": "hh",
20 | "tag": "pinlabel__leader",
21 | },
22 | "text": {
23 | "tag": "pinlabel__text",
24 | },
25 | }
26 |
27 | # Legend
28 | legend = {
29 | "max_height": None,
30 | "inset": (10, 10, 10, 10),
31 | "tag": "legend",
32 | "entry": {
33 | "width": 159,
34 | "height": 28,
35 | "swatch": {
36 | "width": 20,
37 | "height": 20,
38 | "tag": "swatch",
39 | },
40 | "tag": "legendentry",
41 | },
42 | }
43 |
44 | # TextBlock
45 | textblock = {
46 | "line_height": 22,
47 | "width": None,
48 | "height": None,
49 | "offset": (0, 0),
50 | "tag": "textblock",
51 | }
52 |
53 | # Annotation
54 | annotation = {
55 | "tag": "annotation",
56 | "content": {
57 | "tag": "annotation__text",
58 | "x": 28,
59 | "y": 17,
60 | "line_height": 16,
61 | },
62 | "body": {
63 | "x": 40,
64 | "y": 29,
65 | "width": 250,
66 | "height": 50,
67 | "corner_radius": 25,
68 | "tag": "annotation__body",
69 | },
70 | "target": {
71 | "x": -10,
72 | "y": -10,
73 | "width": 20,
74 | "height": 20,
75 | "corner_radius": 10,
76 | "tag": "annotation__target",
77 | },
78 | "leaderline": {
79 | "direction": "vh",
80 | "tag": "annotation__leaderline",
81 | },
82 | }
83 |
84 | # Panel
85 | panel = {
86 | "inset": (2, 2, 2, 2),
87 | "tag": "panel",
88 | "inner": {"tag": "panel__inner"},
89 | "outer": {"tag": "panel__outer"},
90 | }
91 |
92 |
93 | # Integrated circuit
94 | ic_dip = {
95 | "inset": (15, 0, 15, 0),
96 | "tag": "ic ic--dip",
97 | "body": {
98 | "x": 15,
99 | "y": 0,
100 | "corner_radius": 3,
101 | "tag": "ic__body",
102 | },
103 | "leg": {
104 | "tag": "ic__leg",
105 | },
106 | "polarity_mark": {
107 | "radius": 5,
108 | "tag": "polarity",
109 | },
110 | }
111 | ic_qfp = {
112 | "inset": (15, 15, 15, 15),
113 | "pin_pitch": 30,
114 | "tag": "ic ic--qfp",
115 | "body": {
116 | "x": 15,
117 | "y": 15,
118 | "corner_radius": 3,
119 | "tag": "ic__body",
120 | },
121 | "leg": {
122 | "tag": "ic__leg",
123 | },
124 | "polarity_mark": {
125 | "radius": 5,
126 | "tag": "polarity",
127 | },
128 | }
129 |
130 | # Diagram layout template presets
131 | diagram_presets = {
132 | "tag": "layout",
133 | "panel_00": {
134 | "inset": (2, 2, 2, 2),
135 | "tag": "panel",
136 | "inner": {"tag": "panel__inner"},
137 | "outer": {"tag": "panel__outer"},
138 | },
139 | "panel_01": {
140 | "inset": (0.5, 0.5, 0.5, 0.5),
141 | "tag": "panel--main",
142 | "inner": {"tag": "panel__inner"},
143 | "outer": {"tag": "panel__outer"},
144 | },
145 | "panel_02": {
146 | "inset": (0.5, 0.5, 0.5, 0.5),
147 | "tag": "panel--info",
148 | "inner": {"tag": "panel__inner"},
149 | "outer": {"tag": "panel__outer"},
150 | },
151 | }
152 |
153 |
154 | ################################
155 | #
156 | # KiCad footprint settings
157 | #
158 | ################################
159 | kicad_6_footprints = {
160 | "version": 6,
161 | "layer": "User.1",
162 | "pinlabel": {
163 | "hide_fp_text_reference": True,
164 | "hide_fp_text_user": True,
165 | "value_offset": (0, 25), # (mm dimensions)
166 | },
167 | "annotation": {
168 | "hide_fp_text_reference": True,
169 | "hide_fp_text_user": True,
170 | "value_offset": (25, 25), # (mm dimensions)
171 | },
172 | "textblock": {
173 | "hide_fp_text_reference": True,
174 | "hide_fp_text_user": True,
175 | },
176 | }
177 | kicad_5_footprints = {
178 | "version": 5,
179 | "layer": "Eco1.User",
180 | "pinlabel": {
181 | "hide_fp_text_reference": True,
182 | "hide_fp_text_user": True,
183 | "value_offset": (0, 25), # (mm dimensions)
184 | },
185 | "annotation": {
186 | "hide_fp_text_reference": True,
187 | "hide_fp_text_user": True,
188 | "value_offset": (25, 25), # (mm dimensions)
189 | },
190 | "textblock": {
191 | "hide_fp_text_reference": True,
192 | "hide_fp_text_user": True,
193 | },
194 | }
195 |
--------------------------------------------------------------------------------
/pinout/resources/config.py:
--------------------------------------------------------------------------------
1 | ################################
2 | #
3 | # Default component settings
4 | #
5 | ################################
6 |
7 | # Pinlabel
8 | pinlabel = {
9 | "tag": "pinlabel",
10 | "body": {
11 | "x": 6,
12 | "y": 0,
13 | "width": 80,
14 | "height": 26,
15 | "corner_radius": 3,
16 | "tag": "pinlabel__body",
17 | },
18 | "leaderline": {
19 | "direction": "hh",
20 | "tag": "pinlabel__leader",
21 | },
22 | "text": {
23 | "tag": "pinlabel__text",
24 | },
25 | }
26 |
27 | # Legend
28 | legend = {
29 | "max_height": None,
30 | "inset": (10, 10, 10, 10),
31 | "tag": "legend",
32 | "entry": {
33 | "width": 159,
34 | "height": 28,
35 | "swatch": {
36 | "width": 20,
37 | "height": 20,
38 | "tag": "swatch",
39 | },
40 | "tag": "legendentry",
41 | },
42 | }
43 |
44 | # TextBlock
45 | textblock = {
46 | "line_height": 22,
47 | "width": None,
48 | "height": None,
49 | "offset": (0, 0),
50 | "tag": "textblock",
51 | }
52 |
53 | # Annotation
54 | annotation = {
55 | "tag": "annotation",
56 | "content": {
57 | "tag": "annotation__text",
58 | "x": 28,
59 | "y": 17,
60 | "line_height": 16,
61 | },
62 | "body": {
63 | "x": 40,
64 | "y": 29,
65 | "width": 250,
66 | "height": 50,
67 | "corner_radius": 25,
68 | "tag": "annotation__body",
69 | },
70 | "target": {
71 | "x": -10,
72 | "y": -10,
73 | "width": 20,
74 | "height": 20,
75 | "corner_radius": 10,
76 | "tag": "annotation__target",
77 | },
78 | "leaderline": {
79 | "direction": "vh",
80 | "tag": "annotation__leaderline",
81 | },
82 | }
83 |
84 | # Panel
85 | panel = {
86 | "inset": (2, 2, 2, 2),
87 | "tag": "panel",
88 | "inner": {"tag": "panel__inner"},
89 | "outer": {"tag": "panel__outer"},
90 | }
91 |
92 |
93 | # Integrated circuit
94 | ic_dip = {
95 | "inset": (15, 0, 15, 0),
96 | "tag": "ic ic--dip",
97 | "body": {
98 | "x": 15,
99 | "y": 0,
100 | "corner_radius": 3,
101 | "tag": "ic__body",
102 | },
103 | "leg": {
104 | "tag": "ic__leg",
105 | },
106 | "polarity_mark": {
107 | "radius": 5,
108 | "tag": "polarity",
109 | },
110 | }
111 | ic_qfp = {
112 | "inset": (15, 15, 15, 15),
113 | "pin_pitch": 30,
114 | "tag": "ic ic--qfp",
115 | "body": {
116 | "x": 15,
117 | "y": 15,
118 | "corner_radius": 3,
119 | "tag": "ic__body",
120 | },
121 | "leg": {
122 | "tag": "ic__leg",
123 | },
124 | "polarity_mark": {
125 | "radius": 5,
126 | "tag": "polarity",
127 | },
128 | }
129 |
130 | # Diagram layout template presets
131 | diagram_presets = {
132 | "tag": "layout",
133 | "panel_00": {
134 | "inset": (2, 2, 2, 2),
135 | "tag": "panel",
136 | "inner": {"tag": "panel__inner"},
137 | "outer": {"tag": "panel__outer"},
138 | },
139 | "panel_01": {
140 | "inset": (0.5, 0.5, 0.5, 0.5),
141 | "tag": "panel--main",
142 | "inner": {"tag": "panel__inner"},
143 | "outer": {"tag": "panel__outer"},
144 | },
145 | "panel_02": {
146 | "inset": (0.5, 0.5, 0.5, 0.5),
147 | "tag": "panel--info",
148 | "inner": {"tag": "panel__inner"},
149 | "outer": {"tag": "panel__outer"},
150 | },
151 | }
152 |
153 |
154 | ################################
155 | #
156 | # KiCad footprint settings
157 | #
158 | ################################
159 | kicad_6_footprints = {
160 | "version": 6,
161 | "layer": "User.1",
162 | "pinlabel": {
163 | "hide_fp_text_reference": True,
164 | "hide_fp_text_user": True,
165 | "value_offset": (0, 25), # (mm dimensions)
166 | },
167 | "annotation": {
168 | "hide_fp_text_reference": True,
169 | "hide_fp_text_user": True,
170 | "value_offset": (25, 25), # (mm dimensions)
171 | },
172 | "textblock": {
173 | "hide_fp_text_reference": True,
174 | "hide_fp_text_user": True,
175 | },
176 | }
177 | kicad_5_footprints = {
178 | "version": 5,
179 | "layer": "Eco1.User",
180 | "pinlabel": {
181 | "hide_fp_text_reference": True,
182 | "hide_fp_text_user": True,
183 | "value_offset": (0, 25), # (mm dimensions)
184 | },
185 | "annotation": {
186 | "hide_fp_text_reference": True,
187 | "hide_fp_text_user": True,
188 | "value_offset": (25, 25), # (mm dimensions)
189 | },
190 | "textblock": {
191 | "hide_fp_text_reference": True,
192 | "hide_fp_text_user": True,
193 | },
194 | }
195 |
--------------------------------------------------------------------------------
/samples/arduino/arduino/rp2040/rp2040_data.py:
--------------------------------------------------------------------------------
1 | #########################################################
2 | #
3 | # Legend
4 | #
5 | #########################################################
6 |
7 | legend = [
8 | ("Ground", "gnd"),
9 | ("Power", "pwr"),
10 | ("LED", "led"),
11 | ("Internal Pin", "internal"),
12 | ("SWD Pin", "swd"),
13 | ("Digital Pin", "digital"),
14 | ("Analog Pin", "analog"),
15 | ("Other Pin", "other"),
16 | ("Microcontroller's Port", "mu-port"),
17 | ("Default", "default"),
18 | ]
19 |
20 |
21 | #########################################################
22 | #
23 | # Header: Right-hand-side
24 | #
25 | #########################################################
26 |
27 | header_rhs = [
28 | [
29 | ("D12", "digital"),
30 | ("GPIO4", "mu-port"),
31 | ("CIPO", "default"),
32 | ],
33 | [
34 | ("D11", "digital"),
35 | ("GPIO7", "mu-port"),
36 | ("CIPI", "default"),
37 | ],
38 | [
39 | ("D10", "digital"),
40 | ("GPIO5", "mu-port"),
41 | ],
42 | [
43 | ("D9", "digital"),
44 | ("GPIO21", "mu-port"),
45 | ],
46 | [
47 | ("D8", "digital"),
48 | ("GPIO20", "mu-port"),
49 | ],
50 | [
51 | ("D7", "digital"),
52 | ("GPIO19", "mu-port"),
53 | ],
54 | [
55 | ("D6", "digital"),
56 | ("GPIO18", "mu-port"),
57 | ],
58 | [
59 | ("D5", "digital"),
60 | ("GPIO17", "mu-port"),
61 | ],
62 | [
63 | ("D4", "digital"),
64 | ("GPIO16", "mu-port"),
65 | ],
66 | [
67 | ("D3", "digital"),
68 | ("GPIO15", "mu-port"),
69 | ],
70 | [
71 | ("D2", "digital"),
72 | ("GPIO25", "mu-port"),
73 | ],
74 | [
75 | ("GND", "gnd"),
76 | ],
77 | [
78 | ("RESET", "other"),
79 | ("RESET", "mu-port"),
80 | ],
81 | [
82 | ("RX", "digital"),
83 | ("GPIO1", "mu-port"),
84 | ],
85 | [
86 | ("TX", "digital"),
87 | ("GPIO0", "mu-port"),
88 | ],
89 | ]
90 |
91 | #########################################################
92 | #
93 | # Header: Left-hand-side
94 | #
95 | #########################################################
96 |
97 | header_lhs = [
98 | [
99 | ("D13", "digital"),
100 | ("GPIO6", "mu-port"),
101 | ("SCK", "default"),
102 | ],
103 | [
104 | ("+3V3", "pwr"),
105 | ],
106 | [
107 | ("AREF", "other"),
108 | ("PA03", "mu-port"),
109 | ],
110 | [
111 | ("D14", "digital"),
112 | ("A0", "analog"),
113 | ("GPIO26", "mu-port"),
114 | ("A0/DAC0", "default"),
115 | ],
116 | [
117 | ("D15", "digital"),
118 | ("A1", "analog"),
119 | ("GPIO27", "mu-port"),
120 | ("A1", "default"),
121 | ],
122 | [
123 | ("D16", "digital"),
124 | ("A2", "analog"),
125 | ("GPIO28", "mu-port"),
126 | ("A2", "default"),
127 | ],
128 | [
129 | ("D17", "digital"),
130 | ("A3", "analog"),
131 | ("GPIO29", "mu-port"),
132 | ("A3", "default"),
133 | ],
134 | [
135 | ("D18", "digital"),
136 | ("A4", "analog"),
137 | ("GPIO12", "mu-port"),
138 | ("A4", "default"),
139 | ],
140 | [
141 | ("D19", "digital"),
142 | ("A5", "analog"),
143 | ("GPIO13", "mu-port"),
144 | ("A5", "default"),
145 | ],
146 | [
147 | ("D20", "digital"),
148 | ("A6", "analog"),
149 | ("A6", "default show-leader"),
150 | ],
151 | [
152 | ("D21", "digital"),
153 | ("A7", "analog"),
154 | ("A7", "default show-leader"),
155 | ],
156 | [
157 | ("+5V", "pwr"),
158 | ],
159 | [
160 | ("RESET", "other"),
161 | ("QSPI_CSn", "default"),
162 | ],
163 | [
164 | ("GND", "gnd"),
165 | ],
166 | [
167 | ("VIN", "pwr"),
168 | ],
169 | ]
170 |
171 |
172 | #########################################################
173 | #
174 | # LED labels
175 | #
176 | #########################################################
177 |
178 | leds = [
179 | [("Power", "led")],
180 | [("LED_BUILTIN", "led")],
181 | ]
182 |
183 |
184 | #########################################################
185 | #
186 | # Text blocks
187 | #
188 | #########################################################
189 |
190 | title_1 = [
191 | 'Arduino',
192 | 'Nano RP2024 Connect',
193 | ]
194 | para_1 = [
195 | 'Pinout diagram created with pinout (v0.0.10)',
196 | 'A Python package for creating pinout diagrams.',
197 | 'pinout.readthedocs.io',
198 | ]
199 | para_2 = [
200 | 'NOTE: This is not official documentation.',
201 | 'Diagram aesthetics from Arduino docs.',
202 | 'https://www.arduino.cc/',
203 | ]
204 |
--------------------------------------------------------------------------------
/samples/clip_path/pinout_diagram.py:
--------------------------------------------------------------------------------
1 | ##############################################################
2 | #
3 | # Example script demonstrating clipping paths
4 | #
5 | # Export the diagram via commandline:
6 | # >>> py -m pinout.manager -e pinout_diagram.py diagram.svg
7 | #
8 | ##############################################################
9 |
10 |
11 | from pinout.core import Circle, Group, Image, Rect, ClipPath
12 | from pinout.components.layout import Diagram_2Rows
13 | from pinout.components.text import TextBlock
14 |
15 |
16 | ##############################################################
17 | #
18 | # Diagram setup
19 | # Create a layout and add an image component
20 | #
21 | # Diagram_2Rows provides 2 panels to add components,
22 | # 'panel_01' and 'panel_02'.
23 | #
24 | ##############################################################
25 |
26 | diagram = Diagram_2Rows(1200, 675, 500, tag="clip-demo")
27 | diagram.add_stylesheet("styles.css")
28 |
29 | # Components added to do not render but can be referenced
30 | # by other components.
31 | hardware_def = diagram.add_def(Image("hardware_18pin.png"))
32 |
33 | # Coordinates and sizes of intended ClipPaths can be measured
34 | # against the reference image and stored here. These coordinates
35 | # are automatically adjusted if the image is transformed.
36 | hardware_def.add_coord("led_loc", 32, 63)
37 | hardware_def.add_coord("led_size", 74, 45)
38 | hardware_def.add_coord("ic_center", 110, 176)
39 |
40 |
41 | ##############################################################
42 | #
43 | # All components have a 'clip' attribute.
44 | # Clip arguments can be an instance of a ClipPath, SvgShape,
45 | # or a list of SvgShapes.
46 | #
47 | ##############################################################
48 |
49 | # Example of applying a simple clip-path
50 | box01 = diagram.panel_01.add(
51 | Rect(
52 | x=0,
53 | y=0,
54 | width=1195,
55 | height=499,
56 | tag="box01",
57 | clip=Rect(x=10, y=10, width=589, height=489),
58 | )
59 | )
60 |
61 |
62 | ##############################################################
63 | #
64 | # Apply clipping to an Image
65 | #
66 | ##############################################################
67 |
68 | # Grouping components and clipping paths makes positioning easier
69 | group_overlay = diagram.panel_01.add(Group())
70 |
71 | # Add a semi-transparent image as a base
72 | group_overlay.add(Image(hardware_def, tag="opacity_40"))
73 |
74 | # Create a clip-path
75 | image_clip_path = ClipPath(Rect(x=0, y=230, width=220, height=70))
76 |
77 | # Create an image, applying the clip-path to it
78 | overlay_image = group_overlay.add(Image(hardware_def, clip=image_clip_path))
79 |
80 | # Now 'group_overlay' is populated its dimensions can be calculated and
81 | # centered over 'box01'.
82 | group_overlay.x = (box01.width - group_overlay.width) / 2
83 | group_overlay.y = (box01.height - group_overlay.height) / 2
84 |
85 |
86 | ##############################################################
87 | #
88 | # Apply clipping to a Group
89 | #
90 | # Aligning the visible section of a clipped component
91 | # at at (0,0) origin can make for more intuative
92 | # layout calculations.
93 | #
94 | ##############################################################
95 |
96 | # Create a group
97 | led_detail = diagram.panel_01.add(Group())
98 |
99 | # Create an image, adding it to 'led_detail'
100 | led_image = led_detail.add(Image(hardware_def))
101 |
102 | # Access relevant coordinates form 'led_image'.
103 | led_x, led_y = led_image.coord("led_loc")
104 | led_w, led_h = led_image.coord("led_size", True)
105 |
106 | # Realign 'led_image' so the clipped section's top-left
107 | # aligns with its parent's origin.
108 | led_image.x = -led_x
109 | led_image.y = -led_y
110 |
111 | # Add a clip-path to 'led_detail'.
112 | led_detail.clip = Rect(
113 | width=led_w,
114 | height=led_h,
115 | )
116 |
117 | # Locating 'led_detail' is now more intuative as
118 | # its (x,y) location matches with the visible portion
119 | # of its children
120 |
121 | led_detail.x = 837
122 | led_detail.y = 150
123 |
124 | ##############################################################
125 | #
126 | # Succinct version of previous example
127 | #
128 | ##############################################################
129 |
130 | circle_group = diagram.panel_01.add(
131 | Group(
132 | x=874,
133 | y=300,
134 | clip=Circle(cx=0, cy=0, r=68),
135 | children=[
136 | Image(
137 | hardware_def,
138 | x=-hardware_def.coord("ic_center").x,
139 | y=-hardware_def.coord("ic_center").y,
140 | )
141 | ],
142 | )
143 | )
144 |
145 | ##############################################################
146 | #
147 | # Additional non ClipPath diagram components
148 | #
149 | ##############################################################
150 | diagram.panel_02.add(
151 | TextBlock(
152 | """Pinout: ClipPath examples
153 | pinout provides an easy method to create pinout diagrams
154 | for electronic hardware.
155 |
156 | pinout.readthedocs.io""",
157 | x=20,
158 | y=30,
159 | )
160 | )
161 |
--------------------------------------------------------------------------------
/samples/full_sample/pinout_diagram.py:
--------------------------------------------------------------------------------
1 | ###########################################
2 | #
3 | # Example script to build a pinout diagram
4 | # Includes examples of all basic features
5 | #
6 | # Export SVG diagram via command-line:
7 | # >>> py -m pinout.manager --export pinout_diagram.py pinout_diagram.svg -o
8 | #
9 | ###########################################
10 | from pinout import config
11 | from pinout.core import Group, Image
12 | from pinout.components.layout import Diagram, Panel
13 | from pinout.components.pinlabel import PinLabelGroup
14 | from pinout.components.annotation import AnnotationLabel
15 | from pinout.components.text import TextBlock
16 | from pinout.components import leaderline as lline
17 | from pinout.components.legend import Legend
18 |
19 | import full_sample_data as data
20 |
21 | # Edit some component default config
22 | config.panel["inset"] = (2, 2, 2, 2)
23 |
24 | # Create a new diagram, add styles and a base panel
25 | diagram = Diagram(1200, 675, "diagram")
26 | diagram.add_stylesheet("styles_auto.css", True)
27 | diagram.add_stylesheet("styles.css", True)
28 | content = diagram.add(Panel(width=1200, height=675, tag="panel__content"))
29 |
30 |
31 | # Create panels to from a graphical layout
32 |
33 | panel_graphic = content.add(
34 | Panel(
35 | width=860,
36 | height=content.inset_height,
37 | tag="panel__graphic",
38 | )
39 | )
40 | panel_title = content.add(
41 | Panel(
42 | x=panel_graphic.width,
43 | y=0,
44 | width=content.inset_width - panel_graphic.width,
45 | height=60,
46 | tag="panel__title",
47 | )
48 | )
49 | panel_legend = content.add(
50 | Panel(
51 | x=panel_graphic.width,
52 | y=panel_title.bounding_coords().y2,
53 | width=panel_title.width,
54 | height=135,
55 | tag="panel__legend",
56 | )
57 | )
58 | panel_description = content.add(
59 | Panel(
60 | x=panel_graphic.width,
61 | y=panel_legend.bounding_coords().y2,
62 | width=panel_title.width,
63 | height=120,
64 | tag="panel__description",
65 | )
66 | )
67 | panel_notes = content.add(
68 | Panel(
69 | x=panel_graphic.width,
70 | y=panel_description.bounding_coords().y2,
71 | width=panel_title.width,
72 | height=content.inset_height - panel_description.bounding_coords().y2,
73 | tag="panel__notes",
74 | )
75 | )
76 |
77 | # Create a group to hold the actual diagram components.
78 | graphic = panel_graphic.add(Group(318, 200))
79 |
80 | # Add and embed an image
81 | graphic.add(Image("hardware.png", width=220, height=300, embed=True))
82 |
83 |
84 | # Add a pinlabels to the right header
85 | graphic.add(
86 | PinLabelGroup(
87 | x=204,
88 | y=106,
89 | pin_pitch=(0, 30),
90 | label_start=(60, 0),
91 | label_pitch=(0, 30),
92 | labels=data.rhs,
93 | )
94 | )
95 |
96 | # Add a pinlabels to the left header
97 | graphic.add(
98 | PinLabelGroup(
99 | x=16,
100 | y=106,
101 | pin_pitch=(0, 30),
102 | label_start=(60, 0),
103 | label_pitch=(0, 30),
104 | scale=(-1, 1),
105 | labels=data.lhs,
106 | )
107 | )
108 |
109 | # Add a pinlabels to the left header
110 | graphic.add(
111 | PinLabelGroup(
112 | x=65,
113 | y=284,
114 | pin_pitch=(30, 0),
115 | label_start=(109, 60),
116 | label_pitch=(0, 30),
117 | scale=(-1, 1),
118 | labels=data.btm_lhs,
119 | leaderline=lline.Curved(direction="vh"),
120 | )
121 | )
122 |
123 | # Add a pinlabels to the left header
124 | graphic.add(
125 | PinLabelGroup(
126 | x=155,
127 | y=284,
128 | pin_pitch=(-30, 0),
129 | label_start=(109, 60),
130 | label_pitch=(0, 30),
131 | labels=data.btm_rhs,
132 | leaderline=lline.Curved(direction="vh"),
133 | )
134 | )
135 |
136 | graphic.add(
137 | PinLabelGroup(
138 | x=47,
139 | y=80,
140 | scale=(-1, -1),
141 | pin_pitch=(15, 15),
142 | label_start=(91, 110),
143 | label_pitch=(0, 30),
144 | labels=data.aux,
145 | leaderline=lline.Curved(direction="vh"),
146 | )
147 | )
148 |
149 |
150 | graphic.add(
151 | AnnotationLabel(
152 | content={"x": 102, "y": 55, "content": data.annotation_usb},
153 | x=110,
154 | y=0,
155 | scale=(1, -1),
156 | body={"width": 125},
157 | )
158 | )
159 |
160 | graphic.add(
161 | AnnotationLabel(
162 | x=87,
163 | y=85,
164 | scale=(1, -1),
165 | content={"x": 102, "y": 196, "content": data.annotation_led},
166 | body={"y": 168, "width": 125},
167 | target={"x": -20, "y": -20, "width": 40, "height": 40, "corner_radius": 20},
168 | )
169 | )
170 |
171 | title_block = panel_title.add(
172 | TextBlock(
173 | [data.title],
174 | x=10,
175 | y=40,
176 | line_height=18,
177 | tag="panel title_block",
178 | )
179 | )
180 |
181 | legend = panel_legend.add(
182 | Legend(
183 | data.legend,
184 | x=10,
185 | y=5,
186 | max_height=132,
187 | )
188 | )
189 |
190 | description = panel_description.add(
191 | TextBlock(
192 | data.desc,
193 | x=10,
194 | y=20,
195 | line_height=18,
196 | tag="panel text_block",
197 | )
198 | )
199 |
200 | panel_notes.add(
201 | TextBlock(
202 | data.notes,
203 | x=10,
204 | y=20,
205 | line_height=18,
206 | tag="panel text_block",
207 | )
208 | )
209 |
--------------------------------------------------------------------------------
/pinout/components/layout.py:
--------------------------------------------------------------------------------
1 | import uuid
2 | from pinout import templates, config
3 | from pinout.core import (
4 | Layout,
5 | StyleSheet,
6 | Group,
7 | SvgShape,
8 | Rect,
9 | BoundingCoords,
10 | )
11 |
12 |
13 | class Diagram(Layout):
14 | """Basis of a pinout diagram"""
15 |
16 | def __init__(self, width, height, tag=None, **kwargs):
17 | self.width = width
18 | self.height = height
19 | super().__init__(tag=tag, **kwargs)
20 | self.add(SvgShape(width=width, height=height))
21 |
22 | def add_stylesheet(self, path, embed=False):
23 | """Add a stylesheet to the diagram"""
24 | self.children.insert(0, StyleSheet(path, embed))
25 |
26 | def render(self):
27 | """Render children into an