Make setting HTML tag attributes easier through kwargs

This commit is contained in:
Daniel Rojas 2021-10-19 15:44:52 +02:00 committed by KV
parent 587b359fa0
commit 31c5e1aad9
2 changed files with 67 additions and 101 deletions

View File

@ -79,16 +79,15 @@ def gv_node_component(
tbl = nested_table(lines) tbl = nested_table(lines)
if component.bgcolor: tbl.update_attribs(bgcolor=translate_color(component.bgcolor, "HEX"))
tbl.attribs["bgcolor"] = translate_color(component.bgcolor, "HEX")
else:
if isinstance(component, Connector) and harness_options.bgcolor_connector: if isinstance(component, Connector) and harness_options.bgcolor_connector:
tbl.attribs["bgcolor"] = translate_color( tbl.update_attribs(
harness_options.bgcolor_connector, "HEX" bgcolor=translate_color(harness_options.bgcolor_connector, "HEX")
) )
elif isinstance(component, Cable) and harness_options.bgcolor_cable: elif isinstance(component, Cable) and harness_options.bgcolor_cable:
tbl.attribs["bgcolor"] = translate_color( tbl.update_attribs(
harness_options.bgcolor_cable, "HEX" bgcolor=translate_color(harness_options.bgcolor_cable, "HEX")
) )
return tbl return tbl
@ -129,22 +128,13 @@ def nested_table(lines: List[Td]) -> Table:
inner_table = cells[0].contents inner_table = cells[0].contents
else: else:
# nest cell content inside a table # nest cell content inside a table
inner_table_attribs = { inner_table = Table(
"border": 0, Tr(cells), border=0, cellspacing=0, cellpadding=3, cellborder=1
"cellspacing": 0, )
"cellpadding": 3,
"cellborder": 1,
}
inner_table = Table(Tr(cells), attribs=inner_table_attribs)
rows.append(Tr(Td(inner_table))) rows.append(Tr(Td(inner_table)))
if len(rows) == 0: # create dummy row to avoid GraphViz errors due to empty <table> if len(rows) == 0: # create dummy row to avoid GraphViz errors due to empty <table>
rows = Tr(Td("")) rows = Tr(Td(""))
outer_table_attribs = { tbl = Table(rows, border=0, cellspacing=0, cellpadding=0)
"border": 0,
"cellspacing": 0,
"cellpadding": 0,
}
tbl = Table(rows, attribs=outer_table_attribs)
return tbl return tbl
@ -163,20 +153,13 @@ def gv_pin_table(component) -> Table:
gv_pin_row(pinindex, pinname, pinlabel, pincolor, component) gv_pin_row(pinindex, pinname, pinlabel, pincolor, component)
) )
table_attribs = { return Table(pin_rows, border=0, cellspacing=0, cellpadding=3, cellborder=1)
"border": 0,
"cellspacing": 0,
"cellpadding": 3,
"cellborder": 1,
}
return Table(pin_rows, attribs=table_attribs)
def gv_pin_row(pin_index, pin_name, pin_label, pin_color, connector) -> Tr: 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_left = Td(pin_name, port=f"p{pin_index+1}l")
cell_pin_label = Td(pin_label, empty_is_none=True) cell_pin_label = Td(pin_label, delete_if_empty=True)
cell_pin_right = Td(pin_name, attribs={"port": f"p{pin_index+1}r"}) cell_pin_right = Td(pin_name, port=f"p{pin_index+1}r")
cells = [ cells = [
cell_pin_left if connector.ports_left else None, 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("&nbsp;"))) rows.append(Tr(Td("&nbsp;")))
table_attribs = { tbl = Table(rows, border=0, cellspacing=0, cellborder=0)
"border": 0,
"cellspacing": 0,
"cellborder": 0,
}
tbl = Table(rows, attribs=table_attribs)
return tbl return tbl
@ -255,9 +233,8 @@ def gv_wire_cell(index, color, pad) -> Td:
"border": 0, "border": 0,
"bgcolor": bgcolor if bgcolor != "" else "BK", "bgcolor": bgcolor if bgcolor != "" else "BK",
} }
wire_inner_rows.append(Tr(Td("", attribs=wire_inner_cell_attribs))) wire_inner_rows.append(Tr(Td("", **wire_inner_cell_attribs)))
wire_inner_table_attribs = {"cellspacing": 0, "cellborder": 0, "border": 0} wire_inner_table = Table(wire_inner_rows, cellspacing=0, cellborder=0, border=0)
wire_inner_table = Table(wire_inner_rows, wire_inner_table_attribs)
wire_outer_cell_attribs = { wire_outer_cell_attribs = {
"colspan": 3, "colspan": 3,
"border": 0, "border": 0,
@ -265,17 +242,13 @@ def gv_wire_cell(index, color, pad) -> Td:
"port": f"w{index}", "port": f"w{index}",
"height": 2 * len(bgcolors), "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 return wire_outer_cell
def colored_cell(contents, bgcolor) -> Td: def colored_cell(contents, bgcolor) -> Td:
if bgcolor: return Td(contents, bgcolor=translate_color(bgcolor, "HEX"))
attribs = {"bgcolor": translate_color(bgcolor, "HEX")}
else:
attribs = {}
return Td(contents, attribs=attribs)
def part_number_str_list(component: Component) -> List[str]: 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: def colorbar_cell(color) -> Td:
if color: if color:
colorbar_attribs = { return Td("", bgcolor=translate_color(color, "HEX"), width=4)
"bgcolor": translate_color(color, "HEX"),
"width": 4,
}
return Td("", attribs=colorbar_attribs)
else: else:
return None return None
@ -305,35 +274,26 @@ def image_and_caption_cells(component: Component) -> (Td, Td):
if not component.image: if not component.image:
return (None, None) return (None, None)
image_tag = Img( image_tag = Img(scale=component.image.scale, src=component.image.src)
attribs={"scale": component.image.scale, "src": component.image.src}
)
image_cell_inner = Td(image_tag, flat=True) image_cell_inner = Td(image_tag, flat=True)
if component.image.fixedsize: if component.image.fixedsize:
# further nest the image in a table with width/height/fixedsize parameters, and place that table in a cell # 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.update_attribs(**html_size_attr_dict(component.image))
image_cell_inner.attribs = Attribs(inner_cell_attribs)
image_cell = Td( image_cell = Td(
Table( Table(Tr(image_cell_inner), border=0, cellspacing=0, cellborder=0, id="!")
Tr(image_cell_inner),
attribs={"border": 0, "cellspacing": 0, "cellborder": 0, "id": "!"},
)
) )
else: else:
image_cell = image_cell_inner image_cell = image_cell_inner
outer_cell_attribs = {} image_cell.update_attribs(
outer_cell_attribs["balign"] = "left" balign="left",
if component.image.bgcolor: bgcolor=translate_color(component.image.bgcolor, "HEX"),
outer_cell_attribs["bgcolor"] = translate_color(component.image.bgcolor, "HEX") sides="TLR" if component.image.caption else None,
if component.image.caption: )
outer_cell_attribs["sides"] = "TLR"
image_cell.attribs = Attribs(outer_cell_attribs)
if component.image.caption: if component.image.caption:
caption_cell = Td( caption_cell = Td(
f"{html_line_breaks(component.image.caption)}", f"{html_line_breaks(component.image.caption)}", balign="left", sides="BLR"
attribs={"balign": "left", "sides": "BLR", "id": "td_caption"},
) )
else: else:
caption_cell = None caption_cell = None

View File

@ -16,29 +16,27 @@ class Attribs(Dict):
for k, v in self.items(): for k, v in self.items():
if v is not None: if v is not None:
html.append(f' {k}="{v}"') html.append(f' {k}="{v}"')
else: # else:
html.append(f" {k}") # html.append(f" {k}")
return "".join(html) return "".join(html)
@dataclass @dataclass
class Tag: class Tag:
contents: str = None contents = None
attribs: Attribs = field(default_factory=Attribs) attribs: Attribs = field(default_factory=Attribs)
flat: bool = False flat: bool = None
empty_is_none: bool = False delete_if_empty: bool = False
def __post_init__(self): def __init__(self, contents, flat=None, delete_if_empty=False, **kwargs):
if self.attribs is None: self.contents = contents
self.attribs = Attribs({}) self.flat = flat
elif isinstance(self.attribs, Dict): self.delete_if_empty = delete_if_empty
self.attribs = Attribs(self.attribs) self.attribs = Attribs({**kwargs})
elif not isinstance(self.attribs, Attribs):
raise Exception( def update_attribs(self, **kwargs):
"Tag.attribs must be of type None, Dict, or Attribs, " for k, v in kwargs.items():
f"but type {type(self.attribs).__name__} was given instead:\n" self.attribs[k] = v
f"{self.attribs}"
)
@property @property
def tagname(self): def tagname(self):
@ -46,36 +44,41 @@ class Tag:
@property @property
def auto_flat(self): def auto_flat(self):
if self.flat: # force flat if self.flat is not None: # user specified
return True return self.flat
if not _is_iterable_not_str(self.contents): # catch str, int, float, ... if not _is_iterable_not_str(self.contents): # catch str, int, float, ...
if not isinstance(self.contents, Tag): # avoid recursion if not isinstance(self.contents, Tag): # avoid recursion
return not "\n" in str(self.contents) # flatten if single line return not "\n" in str(self.contents) # flatten if single line
def indent_lines(self, lines): @property
if self.auto_flat: 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 return lines
else: else:
indenter = " " * indent_count indenter = " " * indent_count
return "\n".join(f"{indenter}{line}" for line in lines.split("\n")) return "\n".join(f"{indenter}{line}" for line in lines.split("\n"))
def get_contents(self): def get_contents(self, force_flat=False):
separator = "" if self.auto_flat else "\n" separator = "" if self.auto_flat or force_flat else "\n"
if _is_iterable_not_str(self.contents): if _is_iterable_not_str(self.contents):
return separator.join( 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: elif self.contents is None:
return "" return ""
else: # str, int, float, etc. else: # str, int, float, etc.
return self.indent_lines(str(self.contents)) return self.indent_lines(str(self.contents), force_flat)
def __repr__(self): def __repr__(self):
# if self.flat:
# import pudb; pudb.set_trace()
separator = "" if self.auto_flat else "\n" 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 "" return ""
else: else:
html = [ html = [
@ -89,6 +92,9 @@ class Tag:
@dataclass @dataclass
class TagSingleton(Tag): class TagSingleton(Tag):
def __init__(self, **kwargs):
self.attribs = Attribs({**kwargs})
def __repr__(self): def __repr__(self):
return f"<{self.tagname}{str(self.attribs)} />" return f"<{self.tagname}{str(self.attribs)} />"