Further refactor connector node generation
This commit is contained in:
parent
046a1c2ea6
commit
60b2f6caa9
@ -178,13 +178,11 @@ class Harness:
|
|||||||
for connector in self.connectors.values():
|
for connector in self.connectors.values():
|
||||||
# generate connector node
|
# generate connector node
|
||||||
gv_html = gv_node_connector(connector, self.options)
|
gv_html = gv_node_connector(connector, self.options)
|
||||||
_default_fillcolor = translate_color(self.options.bgcolor_connector, "HEX")
|
|
||||||
dot.node(
|
dot.node(
|
||||||
connector.name,
|
connector.name,
|
||||||
label=f"<\n{gv_html}\n>",
|
label=f"<\n{gv_html}\n>",
|
||||||
shape="box",
|
shape="box",
|
||||||
style="filled",
|
style="filled",
|
||||||
fillcolor=_default_fillcolor,
|
|
||||||
)
|
)
|
||||||
# generate edges for connector loops
|
# generate edges for connector loops
|
||||||
if len(connector.loops) > 0:
|
if len(connector.loops) > 0:
|
||||||
|
|||||||
@ -13,17 +13,22 @@ HEADER_PN = "P/N"
|
|||||||
HEADER_MPN = "MPN"
|
HEADER_MPN = "MPN"
|
||||||
HEADER_SPN = "SPN"
|
HEADER_SPN = "SPN"
|
||||||
|
|
||||||
# TODO: remove harness argument; only used by get_additional_component_table()
|
|
||||||
def gv_node_connector(connector: Connector, harness_options: Options) -> str:
|
def gv_node_connector(connector: Connector, harness_options: Options) -> Table:
|
||||||
# If no wires connected (except maybe loop wires)?
|
# If no wires connected (except maybe loop wires)?
|
||||||
if not (connector.ports_left or connector.ports_right):
|
if not (connector.ports_left or connector.ports_right):
|
||||||
connector.ports_left = True # Use left side pins by default
|
connector.ports_left = True # Use left side pins by default
|
||||||
|
|
||||||
# generate all rows to be shown in the node
|
# generate all rows to be shown in the node
|
||||||
if connector.show_name:
|
if connector.show_name:
|
||||||
row_name = [
|
str_name = [f"{remove_links(connector.name)}"]
|
||||||
f"{html_bgcolor(connector.bgcolor_title)}{remove_links(connector.name)}"
|
if connector.bgcolor_title:
|
||||||
]
|
row_name_attribs = {
|
||||||
|
"bgcolor": translate_color(connector.bgcolor_title, "HEX")
|
||||||
|
}
|
||||||
|
row_name = [Td(str_name, attribs=row_name_attribs)]
|
||||||
|
else:
|
||||||
|
row_name = [str_name]
|
||||||
else:
|
else:
|
||||||
row_name = []
|
row_name = []
|
||||||
|
|
||||||
@ -34,16 +39,47 @@ def gv_node_connector(connector: Connector, harness_options: Options) -> str:
|
|||||||
]
|
]
|
||||||
row_pn = [html_line_breaks(cell) for cell in row_pn]
|
row_pn = [html_line_breaks(cell) for cell in row_pn]
|
||||||
|
|
||||||
|
if connector.color:
|
||||||
|
colorbar_attribs = {
|
||||||
|
"bgcolor": translate_color(connector.color, "HEX"),
|
||||||
|
"width": 4,
|
||||||
|
}
|
||||||
|
colorbar_cell = Td("", attribs=colorbar_attribs)
|
||||||
|
else:
|
||||||
|
colorbar_cell = None
|
||||||
|
|
||||||
row_info = [
|
row_info = [
|
||||||
html_line_breaks(connector.type),
|
html_line_breaks(connector.type),
|
||||||
html_line_breaks(connector.subtype),
|
html_line_breaks(connector.subtype),
|
||||||
f"{connector.pincount}-pin" if connector.show_pincount else None,
|
f"{connector.pincount}-pin" if connector.show_pincount else None,
|
||||||
translate_color(connector.color, harness_options.color_mode),
|
translate_color(connector.color, harness_options.color_mode),
|
||||||
html_colorbar(connector.color),
|
colorbar_cell,
|
||||||
]
|
]
|
||||||
|
|
||||||
row_image = [html_image(connector.image)]
|
# <tdX{' sides="TLR"' if image.caption else ''}
|
||||||
row_image_caption = [html_caption(connector.image)]
|
# <tdX sides="BLR"{html_bgcolor_attr(image.bgcolor)}>
|
||||||
|
# {html_size_attr(image)}
|
||||||
|
if connector.image and connector.image.caption:
|
||||||
|
# import pudb; pudb.set_trace()
|
||||||
|
row_image_attribs = html_size_attr_dict(connector.image)
|
||||||
|
row_image_attribs["balign"] = "left"
|
||||||
|
row_image_attribs["sides"] = "TLR"
|
||||||
|
if connector.image.bgcolor:
|
||||||
|
row_image_attribs["bgcolor"] = translate_color(
|
||||||
|
connector.image.bgcolor, "HEX"
|
||||||
|
)
|
||||||
|
row_caption_attribs = {"balign": "left", "sides": "BLR"}
|
||||||
|
row_image = [Td(html_image_new(connector.image), attribs=row_image_attribs)]
|
||||||
|
row_image_caption = [
|
||||||
|
Td(
|
||||||
|
html_caption_new(connector.image),
|
||||||
|
attribs=row_caption_attribs,
|
||||||
|
flat=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
row_image = []
|
||||||
|
row_image_caption = []
|
||||||
row_notes = [html_line_breaks(connector.notes)]
|
row_notes = [html_line_breaks(connector.notes)]
|
||||||
# row_additional_component_table = get_additional_component_table(self, connector)
|
# row_additional_component_table = get_additional_component_table(self, connector)
|
||||||
row_additional_component_table = None
|
row_additional_component_table = None
|
||||||
@ -62,9 +98,12 @@ def gv_node_connector(connector: Connector, harness_options: Options) -> str:
|
|||||||
gv_pin_row(pinindex, pinname, pinlabel, pincolor, connector)
|
gv_pin_row(pinindex, pinname, pinlabel, pincolor, connector)
|
||||||
)
|
)
|
||||||
|
|
||||||
table_attribs = Attribs(
|
table_attribs = {
|
||||||
{"border": 0, "cellspacing": 0, "cellpadding": 3, "cellborder": 1}
|
"border": 0,
|
||||||
)
|
"cellspacing": 0,
|
||||||
|
"cellpadding": 3,
|
||||||
|
"cellborder": 1,
|
||||||
|
}
|
||||||
row_connector_table = str(Table(pin_rows, attribs=table_attribs))
|
row_connector_table = str(Table(pin_rows, attribs=table_attribs))
|
||||||
else:
|
else:
|
||||||
row_connector_table = None
|
row_connector_table = None
|
||||||
@ -80,19 +119,20 @@ def gv_node_connector(connector: Connector, harness_options: Options) -> str:
|
|||||||
row_notes,
|
row_notes,
|
||||||
]
|
]
|
||||||
|
|
||||||
html = "\n".join(nested_html_table(rows, html_bgcolor_attr(connector.bgcolor)))
|
tbl = nested_table(rows)
|
||||||
|
|
||||||
return html
|
if connector.bgcolor:
|
||||||
|
tbl.attribs["bgcolor"] = translate_color(connector.bgcolor, "HEX")
|
||||||
|
elif harness_options.bgcolor_connector:
|
||||||
|
tbl.attribs["bgcolor"] = translate_color(harness_options.bgcolor_connector, "HEX")
|
||||||
|
|
||||||
|
return tbl
|
||||||
|
|
||||||
|
|
||||||
def gv_pin_row(pin_index, pin_name, pin_label, pin_color, connector):
|
def gv_pin_row(pin_index, pin_name, pin_label, pin_color, connector):
|
||||||
cell_pin_left = Td(
|
cell_pin_left = Td(pin_name, attribs={"port": f"p{pin_index+1}l"}, flat=True)
|
||||||
pin_name, attribs=Attribs({"port": f"p{pin_index+1}l"}), flat=True
|
cell_pin_label = Td(pin_label, flat=True, empty_is_none=True)
|
||||||
)
|
cell_pin_right = Td(pin_name, attribs={"port": f"p{pin_index+1}r"}, flat=True)
|
||||||
cell_pin_label = Td(pin_label, flat=True)
|
|
||||||
cell_pin_right = Td(
|
|
||||||
pin_name, attribs=Attribs({"port": f"p{pin_index+1}r"}), flat=True
|
|
||||||
)
|
|
||||||
|
|
||||||
cells = [
|
cells = [
|
||||||
cell_pin_left if connector.ports_left else None,
|
cell_pin_left if connector.ports_left else None,
|
||||||
@ -119,6 +159,39 @@ def gv_connector_loops(connector: Connector) -> List:
|
|||||||
return loop_edges
|
return loop_edges
|
||||||
|
|
||||||
|
|
||||||
|
def nested_table(rows_in: List[Tr]):
|
||||||
|
outer_rows = []
|
||||||
|
for row in rows_in:
|
||||||
|
if isinstance(row, List) and len(row) > 0 and any(row):
|
||||||
|
# remove rows which are none
|
||||||
|
row_no_empty = [cell for cell in row if cell is not None]
|
||||||
|
inner_cells = []
|
||||||
|
for cell in row_no_empty:
|
||||||
|
if isinstance(cell, Td):
|
||||||
|
inner_cells.append(cell)
|
||||||
|
else:
|
||||||
|
inner_cell_attribs = {"balign": "left"}
|
||||||
|
inner_cells.append(Td(cell, attribs=inner_cell_attribs, flat=True))
|
||||||
|
|
||||||
|
inner_table_attribs = {
|
||||||
|
"border": 0,
|
||||||
|
"cellspacing": 0,
|
||||||
|
"cellpadding": 3,
|
||||||
|
"cellborder": 1,
|
||||||
|
}
|
||||||
|
if len(inner_cells) > 0:
|
||||||
|
inner_table = Table(Tr(inner_cells), attribs=inner_table_attribs)
|
||||||
|
outer_rows.append(Tr(Td(inner_table)))
|
||||||
|
elif row is not None and any(row):
|
||||||
|
outer_rows.append(Tr(Td(row)))
|
||||||
|
if len(outer_rows) == 0:
|
||||||
|
outer_rows = Tr(Td("")) # Generate empty cell to avoid GraphViz errors
|
||||||
|
outer_table_attribs = {"border": 0, "cellspacing": 0, "cellpadding": 0}
|
||||||
|
outer_table = Table(outer_rows, attribs=outer_table_attribs)
|
||||||
|
|
||||||
|
return outer_table
|
||||||
|
|
||||||
|
|
||||||
def nested_html_table(
|
def nested_html_table(
|
||||||
rows: List[Union[str, List[Optional[str]], None]], table_attrs: str = ""
|
rows: List[Union[str, List[Optional[str]], None]], table_attrs: str = ""
|
||||||
) -> str:
|
) -> str:
|
||||||
@ -193,6 +266,24 @@ def html_image(image):
|
|||||||
return f"""<tdX{' sides="TLR"' if image.caption else ''}{html_bgcolor_attr(image.bgcolor)}{html}"""
|
return f"""<tdX{' sides="TLR"' if image.caption else ''}{html_bgcolor_attr(image.bgcolor)}{html}"""
|
||||||
|
|
||||||
|
|
||||||
|
def html_image_new(image):
|
||||||
|
from wireviz.DataClasses import Image
|
||||||
|
|
||||||
|
if not image:
|
||||||
|
return None
|
||||||
|
# The leading attributes belong to the preceeding tag. See where used below.
|
||||||
|
html = f'<img scale="{image.scale}" src="{image.src}"/>'
|
||||||
|
if image.fixedsize:
|
||||||
|
# Close the preceeding tag and enclose the image cell in a table without
|
||||||
|
# borders to avoid narrow borders when the fixed width < the node width.
|
||||||
|
html = f"""
|
||||||
|
<table border="0" cellspacing="0" cellborder="0"><tr>
|
||||||
|
<td>{html}</td>
|
||||||
|
</tr></table>
|
||||||
|
"""
|
||||||
|
return f"{html_bgcolor_attr(image.bgcolor)}{html}"
|
||||||
|
|
||||||
|
|
||||||
def html_caption(image):
|
def html_caption(image):
|
||||||
from wireviz.DataClasses import Image
|
from wireviz.DataClasses import Image
|
||||||
|
|
||||||
@ -203,6 +294,12 @@ def html_caption(image):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def html_caption_new(image):
|
||||||
|
from wireviz.DataClasses import Image
|
||||||
|
|
||||||
|
return f"{html_line_breaks(image.caption)}" if image and image.caption else None
|
||||||
|
|
||||||
|
|
||||||
def html_size_attr(image):
|
def html_size_attr(image):
|
||||||
from wireviz.DataClasses import Image
|
from wireviz.DataClasses import Image
|
||||||
|
|
||||||
@ -218,5 +315,20 @@ def html_size_attr(image):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def html_size_attr_dict(image):
|
||||||
|
# Return Graphviz HTML attributes to specify minimum or fixed size of a TABLE or TD object
|
||||||
|
from wireviz.DataClasses import Image
|
||||||
|
|
||||||
|
attr_dict = {}
|
||||||
|
if image:
|
||||||
|
if image.width:
|
||||||
|
attr_dict["width"] = image.width
|
||||||
|
if image.height:
|
||||||
|
attr_dict["height"] = image.height
|
||||||
|
if image.fixedsize:
|
||||||
|
attr_dict["fixedsize"] = "true"
|
||||||
|
return attr_dict
|
||||||
|
|
||||||
|
|
||||||
def html_line_breaks(inp):
|
def html_line_breaks(inp):
|
||||||
return remove_links(inp).replace("\n", "<br />") if isinstance(inp, str) else inp
|
return remove_links(inp).replace("\n", "<br />") if isinstance(inp, str) else inp
|
||||||
|
|||||||
@ -24,13 +24,25 @@ class Tag:
|
|||||||
contents: str
|
contents: str
|
||||||
attribs: Attribs = field(default_factory=Attribs)
|
attribs: Attribs = field(default_factory=Attribs)
|
||||||
flat: bool = False
|
flat: bool = False
|
||||||
|
empty_is_none: bool = False
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.attribs is None:
|
||||||
|
self.attribs = Attribs({})
|
||||||
|
elif isinstance(self.attribs, Dict):
|
||||||
|
self.attribs = Attribs(self.attribs)
|
||||||
|
elif not isinstance(self.attribs, Attribs):
|
||||||
|
raise Exception(
|
||||||
|
"Tag.attribs must be of type None, Dict, or Attribs, "
|
||||||
|
f"but type {type(self.attribs).__name__} was given instead:\n"
|
||||||
|
f"{self.attribs}"
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tagname(self):
|
def tagname(self):
|
||||||
return type(self).__name__.lower()
|
return type(self).__name__.lower()
|
||||||
|
|
||||||
def get_contents(self):
|
def get_contents(self):
|
||||||
# import pudb; pudb.set_trace()
|
|
||||||
separator = "" if self.flat else "\n"
|
separator = "" if self.flat else "\n"
|
||||||
if isinstance(self.contents, Iterable) and not isinstance(self.contents, str):
|
if isinstance(self.contents, Iterable) and not isinstance(self.contents, str):
|
||||||
return separator.join([str(c) for c in self.contents if c is not None])
|
return separator.join([str(c) for c in self.contents if c is not None])
|
||||||
@ -41,6 +53,9 @@ class Tag:
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
separator = "" if self.flat else "\n"
|
separator = "" if self.flat else "\n"
|
||||||
|
if self.contents is None and self.empty_is_none:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
html = [
|
html = [
|
||||||
f"<{self.tagname}{str(self.attribs)}>",
|
f"<{self.tagname}{str(self.attribs)}>",
|
||||||
self.get_contents(),
|
self.get_contents(),
|
||||||
@ -52,7 +67,7 @@ class Tag:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class TagSingleton(Tag):
|
class TagSingleton(Tag):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.tagname}{self.attribs} />"
|
return f"<{self.tagname}{str(self.attribs)} />"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user