Further refactor connector node generation

This commit is contained in:
Daniel Rojas 2021-10-17 18:13:59 +02:00 committed by KV
parent 046a1c2ea6
commit 60b2f6caa9
3 changed files with 155 additions and 30 deletions

View File

@ -178,13 +178,11 @@ class Harness:
for connector in self.connectors.values():
# generate connector node
gv_html = gv_node_connector(connector, self.options)
_default_fillcolor = translate_color(self.options.bgcolor_connector, "HEX")
dot.node(
connector.name,
label=f"<\n{gv_html}\n>",
shape="box",
style="filled",
fillcolor=_default_fillcolor,
)
# generate edges for connector loops
if len(connector.loops) > 0:

View File

@ -13,17 +13,22 @@ 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:
def gv_node_connector(connector: Connector, harness_options: Options) -> Table:
# 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
# generate all rows to be shown in the node
if connector.show_name:
row_name = [
f"{html_bgcolor(connector.bgcolor_title)}{remove_links(connector.name)}"
]
str_name = [f"{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:
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]
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 = [
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),
colorbar_cell,
]
row_image = [html_image(connector.image)]
row_image_caption = [html_caption(connector.image)]
# <tdX{' sides="TLR"' if image.caption else ''}
# <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_additional_component_table = get_additional_component_table(self, connector)
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)
)
table_attribs = Attribs(
{"border": 0, "cellspacing": 0, "cellpadding": 3, "cellborder": 1}
)
table_attribs = {
"border": 0,
"cellspacing": 0,
"cellpadding": 3,
"cellborder": 1,
}
row_connector_table = str(Table(pin_rows, attribs=table_attribs))
else:
row_connector_table = None
@ -80,19 +119,20 @@ def gv_node_connector(connector: Connector, harness_options: Options) -> str:
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):
cell_pin_left = Td(
pin_name, attribs=Attribs({"port": f"p{pin_index+1}l"}), 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
)
cell_pin_left = Td(pin_name, 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)
cells = [
cell_pin_left if connector.ports_left else None,
@ -119,6 +159,39 @@ def gv_connector_loops(connector: Connector) -> List:
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(
rows: List[Union[str, List[Optional[str]], None]], table_attrs: 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}"""
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):
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):
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):
return remove_links(inp).replace("\n", "<br />") if isinstance(inp, str) else inp

View File

@ -24,13 +24,25 @@ class Tag:
contents: str
attribs: Attribs = field(default_factory=Attribs)
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
def tagname(self):
return type(self).__name__.lower()
def get_contents(self):
# import pudb; pudb.set_trace()
separator = "" if self.flat else "\n"
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])
@ -41,18 +53,21 @@ class Tag:
def __repr__(self):
separator = "" if self.flat else "\n"
html = [
f"<{self.tagname}{str(self.attribs)}>",
self.get_contents(),
f"</{self.tagname}>",
]
return separator.join(html)
if self.contents is None and self.empty_is_none:
return ""
else:
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} />"
return f"<{self.tagname}{str(self.attribs)} />"
@dataclass