WIP
This commit is contained in:
parent
82b5cb710f
commit
f7359ff9b1
@ -33,6 +33,9 @@ from wireviz.wv_bom import (
|
|||||||
)
|
)
|
||||||
from wireviz.wv_colors import get_color_hex, translate_color
|
from wireviz.wv_colors import get_color_hex, translate_color
|
||||||
from wireviz.wv_gv_html import (
|
from wireviz.wv_gv_html import (
|
||||||
|
gv_connector_loops,
|
||||||
|
gv_node_connector,
|
||||||
|
gv_pin,
|
||||||
html_bgcolor,
|
html_bgcolor,
|
||||||
html_bgcolor_attr,
|
html_bgcolor_attr,
|
||||||
html_caption,
|
html_caption,
|
||||||
@ -174,102 +177,21 @@ class Harness:
|
|||||||
dot.attr("edge", style="bold", fontname=self.options.fontname)
|
dot.attr("edge", style="bold", fontname=self.options.fontname)
|
||||||
|
|
||||||
for connector in self.connectors.values():
|
for connector in self.connectors.values():
|
||||||
|
gv_html = gv_node_connector(connector, self.options)
|
||||||
# If no wires connected (except maybe loop wires)?
|
_default_fillcolor = translate_color(self.options.bgcolor_connector, "HEX")
|
||||||
if not (connector.ports_left or connector.ports_right):
|
|
||||||
connector.ports_left = True # Use left side pins.
|
|
||||||
|
|
||||||
html = []
|
|
||||||
# fmt: off
|
|
||||||
rows = [[f'{html_bgcolor(connector.bgcolor_title)}{remove_links(connector.name)}'
|
|
||||||
if connector.show_name else None],
|
|
||||||
[pn_info_string(HEADER_PN, None, remove_links(connector.pn)),
|
|
||||||
html_line_breaks(pn_info_string(HEADER_MPN, connector.manufacturer, connector.mpn)),
|
|
||||||
html_line_breaks(pn_info_string(HEADER_SPN, connector.supplier, connector.spn))],
|
|
||||||
[html_line_breaks(connector.type),
|
|
||||||
html_line_breaks(connector.subtype),
|
|
||||||
f'{connector.pincount}-pin' if connector.show_pincount else None,
|
|
||||||
translate_color(connector.color, self.options.color_mode) if connector.color else None,
|
|
||||||
html_colorbar(connector.color)],
|
|
||||||
'<!-- connector table -->' if connector.style != 'simple' else None,
|
|
||||||
[html_image(connector.image)],
|
|
||||||
[html_caption(connector.image)]]
|
|
||||||
# fmt: on
|
|
||||||
|
|
||||||
rows.extend(get_additional_component_table(self, connector))
|
|
||||||
rows.append([html_line_breaks(connector.notes)])
|
|
||||||
html.extend(nested_html_table(rows, html_bgcolor_attr(connector.bgcolor)))
|
|
||||||
|
|
||||||
if connector.style != "simple":
|
|
||||||
pinhtml = []
|
|
||||||
pinhtml.append(
|
|
||||||
'<table border="0" cellspacing="0" cellpadding="3" cellborder="1">'
|
|
||||||
)
|
|
||||||
|
|
||||||
for pinindex, (pinname, pinlabel, pincolor) in enumerate(
|
|
||||||
zip_longest(
|
|
||||||
connector.pins, connector.pinlabels, connector.pincolors
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if (
|
|
||||||
connector.hide_disconnected_pins
|
|
||||||
and not connector.visible_pins.get(pinname, False)
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
|
|
||||||
pinhtml.append(" <tr>")
|
|
||||||
if connector.ports_left:
|
|
||||||
pinhtml.append(f' <td port="p{pinindex+1}l">{pinname}</td>')
|
|
||||||
if pinlabel:
|
|
||||||
pinhtml.append(f" <td>{pinlabel}</td>")
|
|
||||||
if connector.pincolors:
|
|
||||||
if pincolor in wv_colors._color_hex.keys():
|
|
||||||
# fmt: off
|
|
||||||
pinhtml.append(f' <td sides="tbl">{translate_color(pincolor, self.options.color_mode)}</td>')
|
|
||||||
pinhtml.append( ' <td sides="tbr">')
|
|
||||||
pinhtml.append( ' <table border="0" cellborder="1"><tr>')
|
|
||||||
pinhtml.append(f' <td bgcolor="{wv_colors.translate_color(pincolor, "HEX")}" width="8" height="8" fixedsize="true"></td>')
|
|
||||||
pinhtml.append( ' </tr></table>')
|
|
||||||
pinhtml.append( ' </td>')
|
|
||||||
# fmt: on
|
|
||||||
else:
|
|
||||||
pinhtml.append(' <td colspan="2"></td>')
|
|
||||||
|
|
||||||
if connector.ports_right:
|
|
||||||
pinhtml.append(f' <td port="p{pinindex+1}r">{pinname}</td>')
|
|
||||||
pinhtml.append(" </tr>")
|
|
||||||
|
|
||||||
pinhtml.append(" </table>")
|
|
||||||
|
|
||||||
html = [
|
|
||||||
row.replace("<!-- connector table -->", "\n".join(pinhtml))
|
|
||||||
for row in html
|
|
||||||
]
|
|
||||||
|
|
||||||
html = "\n".join(html)
|
|
||||||
dot.node(
|
dot.node(
|
||||||
connector.name,
|
connector.name,
|
||||||
label=f"<\n{html}\n>",
|
label=f"<\n{gv_html}\n>",
|
||||||
shape="box",
|
shape="box",
|
||||||
style="filled",
|
style="filled",
|
||||||
fillcolor=translate_color(self.options.bgcolor_connector, "HEX"),
|
fillcolor=_default_fillcolor,
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(connector.loops) > 0:
|
if len(connector.loops) > 0:
|
||||||
dot.attr("edge", color="#000000:#ffffff:#000000")
|
dot.attr("edge", color="#000000:#ffffff:#000000")
|
||||||
if connector.ports_left:
|
loops = gv_connector_loops(connector)
|
||||||
loop_side = "l"
|
for head, tail in loops:
|
||||||
loop_dir = "w"
|
dot.edge(head, tail)
|
||||||
elif connector.ports_right:
|
|
||||||
loop_side = "r"
|
|
||||||
loop_dir = "e"
|
|
||||||
else:
|
|
||||||
raise Exception("No side for loops")
|
|
||||||
for loop in connector.loops:
|
|
||||||
dot.edge(
|
|
||||||
f"{connector.name}:p{loop[0]}{loop_side}:{loop_dir}",
|
|
||||||
f"{connector.name}:p{loop[1]}{loop_side}:{loop_dir}",
|
|
||||||
)
|
|
||||||
|
|
||||||
# determine if there are double- or triple-colored wires in the harness;
|
# determine if there are double- or triple-colored wires in the harness;
|
||||||
# if so, pad single-color wires to make all wires of equal thickness
|
# if so, pad single-color wires to make all wires of equal thickness
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
|||||||
from wireviz.DataClasses import AdditionalComponent, Cable, Color, Connector
|
from wireviz.DataClasses import AdditionalComponent, Cable, Color, Connector
|
||||||
from wireviz.wv_colors import translate_color
|
from wireviz.wv_colors import translate_color
|
||||||
from wireviz.wv_gv_html import html_bgcolor_attr, html_line_breaks
|
from wireviz.wv_gv_html import html_bgcolor_attr, html_line_breaks
|
||||||
from wireviz.wv_helper import clean_whitespace
|
from wireviz.wv_helper import clean_whitespace, pn_info_string
|
||||||
|
|
||||||
BOM_COLUMNS_ALWAYS = ("id", "description", "qty", "unit", "designators")
|
BOM_COLUMNS_ALWAYS = ("id", "description", "qty", "unit", "designators")
|
||||||
BOM_COLUMNS_OPTIONAL = ("pn", "manufacturer", "mpn", "supplier", "spn")
|
BOM_COLUMNS_OPTIONAL = ("pn", "manufacturer", "mpn", "supplier", "spn")
|
||||||
@ -263,17 +263,6 @@ def component_table_entry(
|
|||||||
</tr></table>"""
|
</tr></table>"""
|
||||||
|
|
||||||
|
|
||||||
def pn_info_string(
|
|
||||||
header: str, name: Optional[str], number: Optional[str]
|
|
||||||
) -> Optional[str]:
|
|
||||||
"""Return the company name and/or the part number in one single string or None otherwise."""
|
|
||||||
number = str(number).strip() if number is not None else ""
|
|
||||||
if name or number:
|
|
||||||
return f'{name if name else header}{": " + number if number else ""}'
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def index_if_list(value: Any, index: int) -> Any:
|
def index_if_list(value: Any, index: int) -> Any:
|
||||||
"""Return the value indexed if it is a list, or simply the value otherwise."""
|
"""Return the value indexed if it is a list, or simply the value otherwise."""
|
||||||
return value[index] if isinstance(value, list) else value
|
return value[index] if isinstance(value, list) else value
|
||||||
|
|||||||
@ -178,8 +178,10 @@ def get_color_translation(translate: Dict[Color, str], input: Colors) -> List[st
|
|||||||
|
|
||||||
|
|
||||||
def translate_color(input: Colors, color_mode: ColorMode) -> str:
|
def translate_color(input: Colors, color_mode: ColorMode) -> str:
|
||||||
if input == "" or input is None:
|
if input == "":
|
||||||
return ""
|
return ""
|
||||||
|
if input is None:
|
||||||
|
return None
|
||||||
upper = color_mode.isupper()
|
upper = color_mode.isupper()
|
||||||
if not (color_mode.isupper() or color_mode.islower()):
|
if not (color_mode.isupper() or color_mode.islower()):
|
||||||
raise Exception("Unknown color mode capitalization")
|
raise Exception("Unknown color mode capitalization")
|
||||||
|
|||||||
@ -1,11 +1,155 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from itertools import zip_longest
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from wireviz.DataClasses import Color
|
from wireviz.DataClasses import Color, Connector, Options
|
||||||
from wireviz.wv_colors import translate_color
|
from wireviz.wv_colors import translate_color
|
||||||
from wireviz.wv_helper import remove_links
|
from wireviz.wv_helper import pn_info_string, remove_links
|
||||||
|
from wireviz.wv_table_util import * # TODO: explicitly import each needed tag later
|
||||||
|
|
||||||
|
HEADER_PN = "P/N"
|
||||||
|
HEADER_MPN = "MPN"
|
||||||
|
HEADER_SPN = "SPN"
|
||||||
|
|
||||||
|
# TODO: remove harness argument; only used by get_additional_component_table()
|
||||||
|
def gv_node_connector(connector: Connector, harness_options: Options) -> str:
|
||||||
|
# If no wires connected (except maybe loop wires)?
|
||||||
|
if not (connector.ports_left or connector.ports_right):
|
||||||
|
connector.ports_left = True # Use left side pins by default
|
||||||
|
|
||||||
|
html = []
|
||||||
|
if connector.show_name:
|
||||||
|
row_name = [
|
||||||
|
f"{html_bgcolor(connector.bgcolor_title)}" f"{remove_links(connector.name)}"
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
row_name = []
|
||||||
|
|
||||||
|
row_pn = [
|
||||||
|
pn_info_string(HEADER_PN, None, connector.pn),
|
||||||
|
pn_info_string(HEADER_MPN, connector.manufacturer, connector.mpn),
|
||||||
|
pn_info_string(HEADER_SPN, connector.supplier, connector.spn),
|
||||||
|
]
|
||||||
|
row_pn = [html_line_breaks(cell) for cell in row_pn]
|
||||||
|
|
||||||
|
row_info = [
|
||||||
|
html_line_breaks(connector.type),
|
||||||
|
html_line_breaks(connector.subtype),
|
||||||
|
f"{connector.pincount}-pin" if connector.show_pincount else None,
|
||||||
|
translate_color(connector.color, harness_options.color_mode),
|
||||||
|
html_colorbar(connector.color),
|
||||||
|
]
|
||||||
|
|
||||||
|
if connector.style != "simple":
|
||||||
|
row_connector_table = "<!-- connector table -->"
|
||||||
|
else:
|
||||||
|
row_connector_table = None
|
||||||
|
|
||||||
|
row_image = [html_image(connector.image)]
|
||||||
|
row_image_caption = [html_caption(connector.image)]
|
||||||
|
row_notes = [html_line_breaks(connector.notes)]
|
||||||
|
# row_additional_component_table = get_additional_component_table(self, connector)
|
||||||
|
row_additional_component_table = None
|
||||||
|
|
||||||
|
rows = [
|
||||||
|
row_name,
|
||||||
|
row_pn,
|
||||||
|
row_info,
|
||||||
|
row_connector_table,
|
||||||
|
row_image,
|
||||||
|
row_image_caption,
|
||||||
|
row_additional_component_table,
|
||||||
|
row_notes,
|
||||||
|
]
|
||||||
|
|
||||||
|
html.extend(nested_html_table(rows, html_bgcolor_attr(connector.bgcolor)))
|
||||||
|
|
||||||
|
if connector.style != "simple":
|
||||||
|
pinhtml = []
|
||||||
|
# fmt: off
|
||||||
|
# pinhtml.append('<table border="0" cellspacing="0" cellpadding="3" cellborder="1">')
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
pin_tuples = zip_longest(
|
||||||
|
connector.pins,
|
||||||
|
connector.pinlabels,
|
||||||
|
connector.pincolors,
|
||||||
|
)
|
||||||
|
|
||||||
|
contents = []
|
||||||
|
for pinindex, (pinname, pinlabel, pincolor) in enumerate(pin_tuples):
|
||||||
|
if connector.hide_disconnected_pins and not connector.visible_pins.get(
|
||||||
|
pinname, False
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
contents.append(gv_pin(pinindex, pinname, pinlabel, pincolor, connector))
|
||||||
|
|
||||||
|
table_attribs = {
|
||||||
|
"border": 0,
|
||||||
|
"cellspacing": 0,
|
||||||
|
"cellpadding": 3,
|
||||||
|
"cellborder": 1,
|
||||||
|
}
|
||||||
|
pinhtml.append(str(Table(contents, attribs=Attribs(table_attribs))))
|
||||||
|
|
||||||
|
pin_html_joined = "\n".join(pinhtml)
|
||||||
|
|
||||||
|
html = [
|
||||||
|
row.replace("<!-- connector table -->", pin_html_joined) for row in html
|
||||||
|
]
|
||||||
|
|
||||||
|
html = "\n".join(html)
|
||||||
|
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
|
def gv_pin(pinindex, pinname, pinlabel, pincolor, connector):
|
||||||
|
pinhtml = []
|
||||||
|
pinhtml.append(" <tr>")
|
||||||
|
if connector.ports_left:
|
||||||
|
pinhtml.append(f' <td port="p{pinindex+1}l">{pinname}</td>')
|
||||||
|
if pinlabel:
|
||||||
|
pinhtml.append(f" <td>{pinlabel}</td>")
|
||||||
|
if connector.pincolors:
|
||||||
|
if pincolor in wv_colors._color_hex.keys():
|
||||||
|
# fmt: off
|
||||||
|
pinhtml.append(f' <td sides="tbl">{translate_color(pincolor, harness_options.color_mode)}</td>')
|
||||||
|
pinhtml.append( ' <td sides="tbr">')
|
||||||
|
pinhtml.append( ' <table border="0" cellborder="1"><tr>')
|
||||||
|
pinhtml.append(f' <td bgcolor="{wv_colors.translate_color(pincolor, "HEX")}" width="8" height="8" fixedsize="true"></td>')
|
||||||
|
pinhtml.append( ' </tr></table>')
|
||||||
|
pinhtml.append( ' </td>')
|
||||||
|
# fmt: on
|
||||||
|
else:
|
||||||
|
pinhtml.append(' <td colspan="2"></td>')
|
||||||
|
|
||||||
|
if connector.ports_right:
|
||||||
|
pinhtml.append(f' <td port="p{pinindex+1}r">{pinname}</td>')
|
||||||
|
pinhtml.append(" </tr>")
|
||||||
|
|
||||||
|
pinhtml = "\n".join(pinhtml)
|
||||||
|
|
||||||
|
return pinhtml
|
||||||
|
|
||||||
|
|
||||||
|
def gv_connector_loops(connector: Connector) -> List:
|
||||||
|
loop_edges = []
|
||||||
|
if connector.ports_left:
|
||||||
|
loop_side = "l"
|
||||||
|
loop_dir = "w"
|
||||||
|
elif connector.ports_right:
|
||||||
|
loop_side = "r"
|
||||||
|
loop_dir = "e"
|
||||||
|
else:
|
||||||
|
raise Exception("No side for loops")
|
||||||
|
for loop in connector.loops:
|
||||||
|
head = f"{connector.name}:p{loop[0]}{loop_side}:{loop_dir}"
|
||||||
|
tail = f"{connector.name}:p{loop[1]}{loop_side}:{loop_dir}"
|
||||||
|
loop_edges.append((head, tail))
|
||||||
|
return loop_edges
|
||||||
|
|
||||||
|
|
||||||
def nested_html_table(
|
def nested_html_table(
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
awg_equiv_table = {
|
awg_equiv_table = {
|
||||||
"0.09": "28",
|
"0.09": "28",
|
||||||
@ -176,3 +176,14 @@ def smart_file_resolve(filename: str, possible_paths: (str, List[str])) -> Path:
|
|||||||
f"{filename} was not found in any of the following locations: \n"
|
f"{filename} was not found in any of the following locations: \n"
|
||||||
+ "\n".join([str(x) for x in possible_paths])
|
+ "\n".join([str(x) for x in possible_paths])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pn_info_string(
|
||||||
|
header: str, name: Optional[str], number: Optional[str]
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""Return the company name and/or the part number in one single string or None otherwise."""
|
||||||
|
number = str(number).strip() if number is not None else ""
|
||||||
|
if name or number:
|
||||||
|
return f'{name if name else header}{": " + number if number else ""}'
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|||||||
104
src/wireviz/wv_table_util.py
Normal file
104
src/wireviz/wv_table_util.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
|
|
||||||
|
class Attribs(Dict):
|
||||||
|
def __repr__(self):
|
||||||
|
if len(self) == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
html = []
|
||||||
|
for k, v in self.items():
|
||||||
|
if v is not None:
|
||||||
|
html.append(f' {k}="{v}"')
|
||||||
|
else:
|
||||||
|
html.append(f" {k}")
|
||||||
|
return "".join(html)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Tag:
|
||||||
|
contents: str
|
||||||
|
attribs: Attribs = field(default_factory=Attribs)
|
||||||
|
one_line: bool = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tagname(self):
|
||||||
|
return type(self).__name__.lower()
|
||||||
|
|
||||||
|
def get_contents(self):
|
||||||
|
if isinstance(self.contents, Iterable):
|
||||||
|
return "\n".join([str(c) for c in self.contents])
|
||||||
|
else:
|
||||||
|
return str(self.contents)
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
separator = "" if self.one_line else "\n"
|
||||||
|
html = [
|
||||||
|
f"<{self.tagname}{str(self.attribs)}>",
|
||||||
|
self.get_contents(),
|
||||||
|
f"</{self.tagname}>",
|
||||||
|
]
|
||||||
|
return separator.join(html)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TagSingleton(Tag):
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<{self.tagname}{self.attribs} />"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Br(TagSingleton):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Td(Tag):
|
||||||
|
pass
|
||||||
|
# contents: str = ""
|
||||||
|
#
|
||||||
|
# def __init__(self, contents, *args, **kwargs):
|
||||||
|
# self.contents = contents
|
||||||
|
# super().__init__(*args, **kwargs)
|
||||||
|
#
|
||||||
|
# def __repr__(self):
|
||||||
|
# html = [
|
||||||
|
# f"<td{self.attribs}>",
|
||||||
|
# self.contents,
|
||||||
|
# f"</td>",
|
||||||
|
# ]
|
||||||
|
# return "\n".join(html)
|
||||||
|
|
||||||
|
|
||||||
|
class Tr(Tag):
|
||||||
|
pass
|
||||||
|
# cells: List[Cell] = field(default_factory=list)
|
||||||
|
#
|
||||||
|
# def __init__(self, cells, *args, **kwargs):
|
||||||
|
# self.cells = cells
|
||||||
|
# super().__init__(*args, **kwargs)
|
||||||
|
#
|
||||||
|
# def __repr__(self):
|
||||||
|
# html = [
|
||||||
|
# f"<tr{self.attribs}>",
|
||||||
|
# "\n".join([str(c) for c in self.cells]),
|
||||||
|
# f"</tr>",
|
||||||
|
# ]
|
||||||
|
# return "\n".join(html)
|
||||||
|
|
||||||
|
|
||||||
|
class Table(Tag):
|
||||||
|
pass
|
||||||
|
# rows: List[Row] = field(default_factory=list)
|
||||||
|
#
|
||||||
|
# def __repr__(self):
|
||||||
|
# html = [
|
||||||
|
# f"<table{self.attribs}>",
|
||||||
|
# "\n".join([str(r) for r in self.rows]),
|
||||||
|
# "</table>",
|
||||||
|
# ]
|
||||||
|
# return "\n".join(html)
|
||||||
Loading…
x
Reference in New Issue
Block a user