Replace bgcolor of node elements with box containing Look

In the elements: Image, AdditionalComponent, Connector, and Cable.
This commit is contained in:
KV 2021-10-03 17:37:38 +02:00
parent 251aab08ff
commit bb39a12256
4 changed files with 76 additions and 43 deletions

View File

@ -43,9 +43,10 @@ class Look:
fontsize: Optional[Points] = None
def _2dict(self) -> dict:
"""Return dict of strings with color values translated to hex."""
"""Return dict of non-None strings with color values translated to hex."""
return {
k:translate_color(v, "hex") if 'color' in k else str(v) for k,v in asdict(self).items()
k:translate_color(v, "hex") if 'color' in k else str(v)
for k,v in asdict(self).items() if v is not None
}
def graph_args(self) -> dict:
@ -111,13 +112,16 @@ class Image:
width: Optional[Points] = None
height: Optional[Points] = None
fixedsize: Optional[bool] = None
bgcolor: Optional[Color] = None
box: Optional[Look] = None
# Contents of the text cell <td> just below the image cell:
caption: Optional[MultilineHypertext] = None
# See also HTML doc at https://graphviz.org/doc/info/shapes.html#html
def __post_init__(self, gv_dir):
if isinstance(self.box, dict):
self.box = Look(**self.box)
if self.fixedsize is None:
# Default True if any dimension specified unless self.scale also is specified.
self.fixedsize = (self.width or self.height) and self.scale is None
@ -150,7 +154,11 @@ class AdditionalComponent:
qty: float = 1
unit: Optional[str] = None
qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None
bgcolor: Optional[Color] = None
box: Optional[Look] = None
def __post_init__(self) -> None:
if isinstance(self.box, dict):
self.box = Look(**self.box)
@property
def description(self) -> str:
@ -160,8 +168,8 @@ class AdditionalComponent:
@dataclass
class Connector:
name: Designator
bgcolor: Optional[Color] = None
bgcolor_title: Optional[Color] = None
box: Optional[Look] = None
title: Optional[Look] = None
manufacturer: Optional[MultilineHypertext] = None
mpn: Optional[MultilineHypertext] = None
supplier: Optional[MultilineHypertext] = None
@ -188,6 +196,10 @@ class Connector:
def __post_init__(self) -> None:
if isinstance(self.box, dict):
self.box = Look(**self.box)
if isinstance(self.title, dict):
self.title = Look(**self.title)
if isinstance(self.image, dict):
self.image = Image(**self.image)
@ -246,8 +258,8 @@ class Connector:
@dataclass
class Cable:
name: Designator
bgcolor: Optional[Color] = None
bgcolor_title: Optional[Color] = None
box: Optional[Look] = None
title: Optional[Look] = None
manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None
mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None
supplier: Union[MultilineHypertext, List[MultilineHypertext], None] = None
@ -276,6 +288,10 @@ class Cable:
def __post_init__(self) -> None:
if isinstance(self.box, dict):
self.box = Look(**self.box)
if isinstance(self.title, dict):
self.title = Look(**self.title)
if isinstance(self.image, dict):
self.image = Image(**self.image)

View File

@ -12,7 +12,7 @@ from wireviz import wv_colors, __version__, APP_NAME, APP_URL
from wireviz.DataClasses import Metadata, Options, Tweak, Connector, Cable
from wireviz.wv_colors import get_color_hex, translate_color
from wireviz.wv_gv_html import nested_html_table, \
html_bgcolor_attr, html_bgcolor, html_colorbar, \
html_cell, html_colorbar, \
html_image, html_caption, remove_links, html_line_breaks
from wireviz.wv_bom import pn_info_string, component_table_entry, \
get_additional_component_table, bom_list, generate_bom, \
@ -124,7 +124,7 @@ class Harness:
html = []
rows = [[f'{html_bgcolor(connector.bgcolor_title)}{remove_links(connector.name)}'
rows = [[html_cell(connector.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)),
@ -139,7 +139,7 @@ class Harness:
[html_caption(connector.image)]]
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)))
html.extend(nested_html_table(rows, connector.box))
if connector.style != 'simple':
pinhtml = []
@ -210,7 +210,7 @@ class Harness:
elif cable.gauge_unit.upper() == 'AWG':
awg_fmt = f' ({mm2_equiv(cable.gauge)} mm\u00B2)'
rows = [[f'{html_bgcolor(cable.bgcolor_title)}{remove_links(cable.name)}'
rows = [[html_cell(cable.title, remove_links(cable.name))
if cable.show_name else None],
[pn_info_string(HEADER_PN, None,
remove_links(cable.pn)) if not isinstance(cable.pn, list) else None,
@ -233,7 +233,7 @@ class Harness:
rows.extend(get_additional_component_table(self, cable))
rows.append([html_line_breaks(cable.notes)])
html.extend(nested_html_table(rows, html_bgcolor_attr(cable.bgcolor)))
html.extend(nested_html_table(rows, cable.box))
wirehtml = []
wirehtml.append('<table border="0" cellspacing="0" cellborder="0">') # conductor table

View File

@ -4,9 +4,9 @@ from dataclasses import asdict
from itertools import groupby
from typing import Any, Dict, List, Optional, Tuple, Union
from wireviz.DataClasses import AdditionalComponent, Cable, Connector
from wireviz.DataClasses import AdditionalComponent, Cable, Connector, Look
from wireviz.wv_colors import Color, translate_color
from wireviz.wv_gv_html import html_bgcolor_attr, html_line_breaks
from wireviz.wv_gv_html import font_tag, html_line_breaks, table_attr
from wireviz.wv_helper import clean_whitespace
BOM_COLUMNS_ALWAYS = ('id', 'description', 'qty', 'unit', 'designators')
@ -35,7 +35,7 @@ def get_additional_component_table(harness: "Harness", component: Union[Connecto
common_args = {
'qty': part.qty * component.get_qty_multiplier(part.qty_multiplier),
'unit': part.unit,
'bgcolor': part.bgcolor,
'box': part.box,
}
if harness.options.mini_bom_mode:
id = get_bom_index(harness.bom(), bom_entry_key({**asdict(part), 'description': part.description}))
@ -158,7 +158,7 @@ def component_table_entry(
type: str,
qty: Union[int, float],
unit: Optional[str] = None,
bgcolor: Optional[Color] = None,
box: Optional[Look] = None,
pn: Optional[str] = None,
manufacturer: Optional[str] = None,
mpn: Optional[str] = None,
@ -178,8 +178,8 @@ def component_table_entry(
+ (', '.join([pn for pn in part_number_list if pn])))
# format the above output as left aligned text in a single visible cell
# indent is set to two to match the indent in the generated html table
return f'''<table border="0" cellspacing="0" cellpadding="3" cellborder="1"{html_bgcolor_attr(bgcolor)}><tr>
<td align="left" balign="left">{html_line_breaks(output)}</td>
return f'''<table border="0" cellspacing="0" cellpadding="3" cellborder="1"{table_attr(box)}><tr>
<td align="left" balign="left">{font_tag(box, html_line_breaks(output))}</td>
</tr></table>'''
def pn_info_string(header: str, name: Optional[str], number: Optional[str]) -> Optional[str]:

View File

@ -1,18 +1,24 @@
# -*- coding: utf-8 -*-
from typing import List, Optional, Union
import re
from wireviz.DataClasses import Image, Look
from wireviz.wv_colors import Color, translate_color
from wireviz.wv_helper import remove_links
def nested_html_table(rows: List[Union[str, List[Optional[str]], None]], table_attrs: str = '') -> str:
# input: list, each item may be scalar or list
GvHtml = str # Graphviz HTML-like label string
GvHtmlX = str # Graphviz HTML-like label string possibly including a leading <tdX> tag
GvHtmlAttr = str # Attributes part of Graphviz HTML-like tag (including a leading space)
def nested_html_table(rows: List[Union[GvHtml, List[Optional[GvHtmlX]], None]], look: Optional[Look]) -> GvHtml:
# input: list, each item may be scalar or list, and look with optional table look attributes
# output: a parent table with one child table per parent item that is list, and one cell per parent item that is scalar
# purpose: create the appearance of one table, where cell widths are independent between rows
# attributes in any leading <tdX> inside a list are injected into to the preceeding <td> tag
html = []
html.append(f'<table border="0" cellspacing="0" cellpadding="0"{table_attrs or ""}>')
attr = font_attr(look)
font = f'<font{attr}>' if attr else ''
html.append(f'{font}<table border="0" cellspacing="0" cellpadding="0"{table_attr(look)}>')
for row in rows:
if isinstance(row, List):
if len(row) > 0 and any(row):
@ -28,23 +34,36 @@ def nested_html_table(rows: List[Union[str, List[Optional[str]], None]], table_a
html.append(' <tr><td>')
html.append(f' {row}')
html.append(' </td></tr>')
html.append('</table>')
html.append(f'</table>{"</font>" if font else ""}')
return html
def html_bgcolor_attr(color: Color) -> str:
"""Return attributes for bgcolor or '' if no color."""
return f' bgcolor="{translate_color(color, "HEX")}"' if color else ''
def table_attr(look: Optional[Look]) -> GvHtmlAttr:
"""Return table tag attributes containing all non-empty table option values."""
return '' if not look else ''.join({
f' {k}="{v}"' for k,v in look._2dict().items() if v and 'font' not in k})
def html_bgcolor(color: Color, _extra_attr: str = '') -> str:
"""Return <td> attributes prefix for bgcolor or '' if no color."""
return f'<tdX{html_bgcolor_attr(color)}{_extra_attr}>' if color else ''
def font_attr(look: Optional[Look]) -> GvHtmlAttr:
"""Return font tag attributes containing all non-empty font option values."""
attr = {k:v for k,v in look._2dict().items() if v and 'font' in k} if look else {}
return ((f' color="{attr["fontcolor"]}"' if attr.get('fontcolor') else '')
+ (f' face="{attr["fontname"]}"' if attr.get('fontname') else '')
+ (f' point-size="{attr["fontsize"]}"' if attr.get('fontsize') else ''))
def html_colorbar(color: Color) -> str:
"""Return <tdX> attributes prefix for bgcolor and minimum width or None if no color."""
return html_bgcolor(color, ' width="4"') if color else None
def font_tag(look: Optional[Look], text: GvHtml) -> GvHtml:
"""Return text in Graphviz HTML font tag with all non-empty font option values."""
attr = font_attr(look)
return f'<font{attr}>{text}</font>' if attr and text > '' else text
def html_image(image):
from wireviz.DataClasses import Image
def html_cell(look: Optional[Look], text: GvHtml = '', attr: GvHtmlAttr = '') -> GvHtmlX:
"""Return cell to be included in the rows list for nested_html_table()."""
return f'<tdX{attr}{table_attr(look)}>{font_tag(look, text)}'
def html_colorbar(color: Optional[Color]) -> Optional[GvHtmlX]:
"""Return colored cell to be included in the rows list for nested_html_table() or None if no color."""
return html_cell(Look(bgcolor=color), attr=' width="4"') if color else None
def html_image(image: Optional[Image]) -> Optional[GvHtmlX]:
"""Return image cell to be included in the rows list for nested_html_table() or None if no image."""
if not image:
return None
# The leading attributes belong to the preceeding tag. See where used below.
@ -57,16 +76,14 @@ def html_image(image):
<td{html}</td>
</tr></table>
'''
return f'''<tdX{' sides="TLR"' if image.caption else ''}{html_bgcolor_attr(image.bgcolor)}{html}'''
return f'''<tdX{' sides="TLR"' if image.caption else ''}{table_attr(image.box)}{html}'''
def html_caption(image):
from wireviz.DataClasses import Image
return (f'<tdX sides="BLR"{html_bgcolor_attr(image.bgcolor)}>{html_line_breaks(image.caption)}'
if image and image.caption else None)
def html_caption(image: Optional[Image]) -> Optional[GvHtmlX]:
"""Return image caption cell to be included just after the image cell or None if no caption."""
return html_cell(image.box, html_line_breaks(image.caption), ' sides="BLR"') if image and image.caption else None
def html_size_attr(image):
from wireviz.DataClasses import Image
# Return Graphviz HTML attributes to specify minimum or fixed size of a TABLE or TD object
def html_size_attr(image: Optional[Image]) -> GvHtmlAttr:
"""Return Graphviz HTML attributes to specify minimum or fixed size of a TABLE or TD object."""
return ((f' width="{image.width}"' if image.width else '')
+ (f' height="{image.height}"' if image.height else '')
+ ( ' fixedsize="true"' if image.fixedsize else '')) if image else ''