diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py
index d99786f..c72ab17 100644
--- a/src/wireviz/Harness.py
+++ b/src/wireviz/Harness.py
@@ -33,6 +33,9 @@ from wireviz.wv_bom import (
)
from wireviz.wv_colors import get_color_hex, translate_color
from wireviz.wv_gv_html import (
+ gv_connector_loops,
+ gv_node_connector,
+ gv_pin,
html_bgcolor,
html_bgcolor_attr,
html_caption,
@@ -174,102 +177,21 @@ class Harness:
dot.attr("edge", style="bold", fontname=self.options.fontname)
for connector in self.connectors.values():
-
- # 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.
-
- 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)],
- '' 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(
- '
'
- )
-
- 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(" ")
- if connector.ports_left:
- pinhtml.append(f' | {pinname} | ')
- if pinlabel:
- pinhtml.append(f" {pinlabel} | ")
- if connector.pincolors:
- if pincolor in wv_colors._color_hex.keys():
- # fmt: off
- pinhtml.append(f' {translate_color(pincolor, self.options.color_mode)} | ')
- pinhtml.append( ' ')
- pinhtml.append( ' ')
- pinhtml.append(f' | ')
- pinhtml.append( ' ')
- pinhtml.append( ' | ')
- # fmt: on
- else:
- pinhtml.append(' | ')
-
- if connector.ports_right:
- pinhtml.append(f' {pinname} | ')
- pinhtml.append("
")
-
- pinhtml.append("
")
-
- html = [
- row.replace("", "\n".join(pinhtml))
- for row in html
- ]
-
- html = "\n".join(html)
+ gv_html = gv_node_connector(connector, self.options)
+ _default_fillcolor = translate_color(self.options.bgcolor_connector, "HEX")
dot.node(
connector.name,
- label=f"<\n{html}\n>",
+ label=f"<\n{gv_html}\n>",
shape="box",
style="filled",
- fillcolor=translate_color(self.options.bgcolor_connector, "HEX"),
+ fillcolor=_default_fillcolor,
)
if len(connector.loops) > 0:
dot.attr("edge", color="#000000:#ffffff:#000000")
- 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:
- dot.edge(
- f"{connector.name}:p{loop[0]}{loop_side}:{loop_dir}",
- f"{connector.name}:p{loop[1]}{loop_side}:{loop_dir}",
- )
+ loops = gv_connector_loops(connector)
+ for head, tail in loops:
+ dot.edge(head, tail)
# 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
diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py
index 6689d79..d28e3a1 100644
--- a/src/wireviz/wv_bom.py
+++ b/src/wireviz/wv_bom.py
@@ -7,7 +7,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from wireviz.DataClasses import AdditionalComponent, Cable, Color, Connector
from wireviz.wv_colors import translate_color
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_OPTIONAL = ("pn", "manufacturer", "mpn", "supplier", "spn")
@@ -263,17 +263,6 @@ def component_table_entry(
"""
-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:
"""Return the value indexed if it is a list, or simply the value otherwise."""
return value[index] if isinstance(value, list) else value
diff --git a/src/wireviz/wv_colors.py b/src/wireviz/wv_colors.py
index 857f307..34d9241 100644
--- a/src/wireviz/wv_colors.py
+++ b/src/wireviz/wv_colors.py
@@ -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:
- if input == "" or input is None:
+ if input == "":
return ""
+ if input is None:
+ return None
upper = color_mode.isupper()
if not (color_mode.isupper() or color_mode.islower()):
raise Exception("Unknown color mode capitalization")
diff --git a/src/wireviz/wv_gv_html.py b/src/wireviz/wv_gv_html.py
index ec80aa7..92c29a5 100644
--- a/src/wireviz/wv_gv_html.py
+++ b/src/wireviz/wv_gv_html.py
@@ -1,11 +1,155 @@
# -*- coding: utf-8 -*-
import re
+from itertools import zip_longest
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_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 = ""
+ 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('')
+ # 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("", pin_html_joined) for row in html
+ ]
+
+ html = "\n".join(html)
+
+ return html
+
+
+def gv_pin(pinindex, pinname, pinlabel, pincolor, connector):
+ pinhtml = []
+ pinhtml.append(" ")
+ if connector.ports_left:
+ pinhtml.append(f' | {pinname} | ')
+ if pinlabel:
+ pinhtml.append(f" {pinlabel} | ")
+ if connector.pincolors:
+ if pincolor in wv_colors._color_hex.keys():
+ # fmt: off
+ pinhtml.append(f' {translate_color(pincolor, harness_options.color_mode)} | ')
+ pinhtml.append( ' ')
+ pinhtml.append( ' ')
+ pinhtml.append(f' | ')
+ pinhtml.append( ' ')
+ pinhtml.append( ' | ')
+ # fmt: on
+ else:
+ pinhtml.append(' | ')
+
+ if connector.ports_right:
+ pinhtml.append(f' {pinname} | ')
+ pinhtml.append("
")
+
+ 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(
diff --git a/src/wireviz/wv_helper.py b/src/wireviz/wv_helper.py
index a10f6ac..8c46299 100644
--- a/src/wireviz/wv_helper.py
+++ b/src/wireviz/wv_helper.py
@@ -2,7 +2,7 @@
import re
from pathlib import Path
-from typing import Dict, List
+from typing import Dict, List, Optional
awg_equiv_table = {
"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"
+ "\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
diff --git a/src/wireviz/wv_table_util.py b/src/wireviz/wv_table_util.py
new file mode 100644
index 0000000..eb76408
--- /dev/null
+++ b/src/wireviz/wv_table_util.py
@@ -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"",
+ # self.contents,
+ # f" | ",
+ # ]
+ # 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"",
+ # "\n".join([str(c) for c in self.cells]),
+ # f"
",
+ # ]
+ # return "\n".join(html)
+
+
+class Table(Tag):
+ pass
+ # rows: List[Row] = field(default_factory=list)
+ #
+ # def __repr__(self):
+ # html = [
+ # f"",
+ # "\n".join([str(r) for r in self.rows]),
+ # "
",
+ # ]
+ # return "\n".join(html)