diff --git a/src/wireviz/wv_gv_html.py b/src/wireviz/wv_gv_html.py index af11743..812b9a0 100644 --- a/src/wireviz/wv_gv_html.py +++ b/src/wireviz/wv_gv_html.py @@ -79,17 +79,16 @@ def gv_node_component( tbl = nested_table(lines) - if component.bgcolor: - tbl.attribs["bgcolor"] = translate_color(component.bgcolor, "HEX") - else: - if isinstance(component, Connector) and harness_options.bgcolor_connector: - tbl.attribs["bgcolor"] = translate_color( - harness_options.bgcolor_connector, "HEX" - ) - elif isinstance(component, Cable) and harness_options.bgcolor_cable: - tbl.attribs["bgcolor"] = translate_color( - harness_options.bgcolor_cable, "HEX" - ) + tbl.update_attribs(bgcolor=translate_color(component.bgcolor, "HEX")) + + if isinstance(component, Connector) and harness_options.bgcolor_connector: + tbl.update_attribs( + bgcolor=translate_color(harness_options.bgcolor_connector, "HEX") + ) + elif isinstance(component, Cable) and harness_options.bgcolor_cable: + tbl.update_attribs( + bgcolor=translate_color(harness_options.bgcolor_cable, "HEX") + ) return tbl @@ -129,22 +128,13 @@ def nested_table(lines: List[Td]) -> Table: inner_table = cells[0].contents else: # nest cell content inside a table - inner_table_attribs = { - "border": 0, - "cellspacing": 0, - "cellpadding": 3, - "cellborder": 1, - } - inner_table = Table(Tr(cells), attribs=inner_table_attribs) + inner_table = Table( + Tr(cells), border=0, cellspacing=0, cellpadding=3, cellborder=1 + ) rows.append(Tr(Td(inner_table))) if len(rows) == 0: # create dummy row to avoid GraphViz errors due to empty rows = Tr(Td("")) - outer_table_attribs = { - "border": 0, - "cellspacing": 0, - "cellpadding": 0, - } - tbl = Table(rows, attribs=outer_table_attribs) + tbl = Table(rows, border=0, cellspacing=0, cellpadding=0) return tbl @@ -163,20 +153,13 @@ def gv_pin_table(component) -> Table: gv_pin_row(pinindex, pinname, pinlabel, pincolor, component) ) - table_attribs = { - "border": 0, - "cellspacing": 0, - "cellpadding": 3, - "cellborder": 1, - } - - return Table(pin_rows, attribs=table_attribs) + return Table(pin_rows, border=0, cellspacing=0, cellpadding=3, cellborder=1) def gv_pin_row(pin_index, pin_name, pin_label, pin_color, connector) -> Tr: - cell_pin_left = Td(pin_name, attribs={"port": f"p{pin_index+1}l"}) - cell_pin_label = Td(pin_label, empty_is_none=True) - cell_pin_right = Td(pin_name, attribs={"port": f"p{pin_index+1}r"}) + cell_pin_left = Td(pin_name, port=f"p{pin_index+1}l") + cell_pin_label = Td(pin_label, delete_if_empty=True) + cell_pin_right = Td(pin_name, port=f"p{pin_index+1}r") cells = [ cell_pin_left if connector.ports_left else None, @@ -234,12 +217,7 @@ def gv_conductor_table(cable, harness_options) -> Table: rows.append(Tr(Td(" "))) - table_attribs = { - "border": 0, - "cellspacing": 0, - "cellborder": 0, - } - tbl = Table(rows, attribs=table_attribs) + tbl = Table(rows, border=0, cellspacing=0, cellborder=0) return tbl @@ -255,9 +233,8 @@ def gv_wire_cell(index, color, pad) -> Td: "border": 0, "bgcolor": bgcolor if bgcolor != "" else "BK", } - wire_inner_rows.append(Tr(Td("", attribs=wire_inner_cell_attribs))) - wire_inner_table_attribs = {"cellspacing": 0, "cellborder": 0, "border": 0} - wire_inner_table = Table(wire_inner_rows, wire_inner_table_attribs) + wire_inner_rows.append(Tr(Td("", **wire_inner_cell_attribs))) + wire_inner_table = Table(wire_inner_rows, cellspacing=0, cellborder=0, border=0) wire_outer_cell_attribs = { "colspan": 3, "border": 0, @@ -265,17 +242,13 @@ def gv_wire_cell(index, color, pad) -> Td: "port": f"w{index}", "height": 2 * len(bgcolors), } - wire_outer_cell = Td(wire_inner_table, attribs=wire_outer_cell_attribs) + wire_outer_cell = Td(wire_inner_table, **wire_outer_cell_attribs) return wire_outer_cell def colored_cell(contents, bgcolor) -> Td: - if bgcolor: - attribs = {"bgcolor": translate_color(bgcolor, "HEX")} - else: - attribs = {} - return Td(contents, attribs=attribs) + return Td(contents, bgcolor=translate_color(bgcolor, "HEX")) def part_number_str_list(component: Component) -> List[str]: @@ -292,11 +265,7 @@ def part_number_str_list(component: Component) -> List[str]: def colorbar_cell(color) -> Td: if color: - colorbar_attribs = { - "bgcolor": translate_color(color, "HEX"), - "width": 4, - } - return Td("", attribs=colorbar_attribs) + return Td("", bgcolor=translate_color(color, "HEX"), width=4) else: return None @@ -305,35 +274,26 @@ def image_and_caption_cells(component: Component) -> (Td, Td): if not component.image: return (None, None) - image_tag = Img( - attribs={"scale": component.image.scale, "src": component.image.src} - ) + image_tag = Img(scale=component.image.scale, src=component.image.src) image_cell_inner = Td(image_tag, flat=True) if component.image.fixedsize: # further nest the image in a table with width/height/fixedsize parameters, and place that table in a cell - inner_cell_attribs = html_size_attr_dict(component.image) - image_cell_inner.attribs = Attribs(inner_cell_attribs) + image_cell_inner.update_attribs(**html_size_attr_dict(component.image)) image_cell = Td( - Table( - Tr(image_cell_inner), - attribs={"border": 0, "cellspacing": 0, "cellborder": 0, "id": "!"}, - ) + Table(Tr(image_cell_inner), border=0, cellspacing=0, cellborder=0, id="!") ) else: image_cell = image_cell_inner - outer_cell_attribs = {} - outer_cell_attribs["balign"] = "left" - if component.image.bgcolor: - outer_cell_attribs["bgcolor"] = translate_color(component.image.bgcolor, "HEX") - if component.image.caption: - outer_cell_attribs["sides"] = "TLR" - image_cell.attribs = Attribs(outer_cell_attribs) + image_cell.update_attribs( + balign="left", + bgcolor=translate_color(component.image.bgcolor, "HEX"), + sides="TLR" if component.image.caption else None, + ) if component.image.caption: caption_cell = Td( - f"{html_line_breaks(component.image.caption)}", - attribs={"balign": "left", "sides": "BLR", "id": "td_caption"}, + f"{html_line_breaks(component.image.caption)}", balign="left", sides="BLR" ) else: caption_cell = None diff --git a/src/wireviz/wv_table_util.py b/src/wireviz/wv_table_util.py index aa23663..6de208c 100644 --- a/src/wireviz/wv_table_util.py +++ b/src/wireviz/wv_table_util.py @@ -16,29 +16,27 @@ class Attribs(Dict): for k, v in self.items(): if v is not None: html.append(f' {k}="{v}"') - else: - html.append(f" {k}") + # else: + # html.append(f" {k}") return "".join(html) @dataclass class Tag: - contents: str = None + contents = None attribs: Attribs = field(default_factory=Attribs) - flat: bool = False - empty_is_none: bool = False + flat: bool = None + delete_if_empty: 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}" - ) + def __init__(self, contents, flat=None, delete_if_empty=False, **kwargs): + self.contents = contents + self.flat = flat + self.delete_if_empty = delete_if_empty + self.attribs = Attribs({**kwargs}) + + def update_attribs(self, **kwargs): + for k, v in kwargs.items(): + self.attribs[k] = v @property def tagname(self): @@ -46,36 +44,41 @@ class Tag: @property def auto_flat(self): - if self.flat: # force flat - return True + if self.flat is not None: # user specified + return self.flat if not _is_iterable_not_str(self.contents): # catch str, int, float, ... if not isinstance(self.contents, Tag): # avoid recursion return not "\n" in str(self.contents) # flatten if single line - def indent_lines(self, lines): - if self.auto_flat: + @property + def is_empty(self): + return self.get_contents(force_flat=True) == "" + + def indent_lines(self, lines, force_flat=False): + if self.auto_flat or force_flat: return lines else: indenter = " " * indent_count return "\n".join(f"{indenter}{line}" for line in lines.split("\n")) - def get_contents(self): - separator = "" if self.auto_flat else "\n" + def get_contents(self, force_flat=False): + separator = "" if self.auto_flat or force_flat else "\n" if _is_iterable_not_str(self.contents): return separator.join( - [self.indent_lines(str(c)) for c in self.contents if c is not None] + [ + self.indent_lines(str(c), force_flat) + for c in self.contents + if c is not None + ] ) elif self.contents is None: return "" else: # str, int, float, etc. - return self.indent_lines(str(self.contents)) + return self.indent_lines(str(self.contents), force_flat) def __repr__(self): - # if self.flat: - # import pudb; pudb.set_trace() - separator = "" if self.auto_flat else "\n" - if self.contents is None and self.empty_is_none: + if self.delete_if_empty and self.is_empty: return "" else: html = [ @@ -89,6 +92,9 @@ class Tag: @dataclass class TagSingleton(Tag): + def __init__(self, **kwargs): + self.attribs = Attribs({**kwargs}) + def __repr__(self): return f"<{self.tagname}{str(self.attribs)} />"