FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(diagram): make the diagram font configurable by dimitri-yatsenko · Pull Request #1624 · datajoint/datajoint-python · GitHub

Repository navigation

Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (4) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
20 changes: 13 additions & 7 deletions src/datajoint/diagram.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1484,13 +1484,18 @@ def make_dot(self, theme=None):

Notes
-----
Layout direction is controlled via ``dj.config.display.diagram_direction``.
Layout direction is controlled via ``dj.config.display.diagram_direction``,
and the typeface of node and schema labels via
``dj.config.display.diagram_fontname`` (default ``"Helvetica"``).
Graphviz sizes nodes with the face it finds on the rendering machine, so
a face that machine lacks changes the layout.
Tables are grouped by schema, with the Python module name shown as the
group label when available.
"""
if not diagram_active:
raise DataJointError("Install pygraphviz and pydot libraries to enable diagram visualization.")
direction = self._connection._config.display.diagram_direction
fontname = self._connection._config.display.diagram_fontname
graph = self._make_graph()

# Apply collapse logic if needed
Expand Down Expand Up @@ -1630,7 +1635,7 @@ def _master_of(part_stripped):
node.set_fontsize(props["fontsize"])
node.set_fontcolor(props["fontcolor"])
node.set_shape(props["shape"])
node.set_fontname("Helvetica")
node.set_fontname(fontname)
node.set_fixedsize("shape" if props["fixed"] else False)
node.set_width(props["size"])
node.set_height(props["size"])
Expand Down Expand Up @@ -1742,7 +1747,7 @@ def _master_of(part_stripped):
style="rounded,dashed",
color=sc_color,
fontcolor=sc_fontcolor,
fontname="Helvetica", # schema label in a sans font, not Graphviz's Times default
fontname=fontname, # the node font, not Graphviz's Times default
)
node_by_name = {n.get_name().strip('"'): n for n in nodes}
# masters in this schema that have at least one part present
Expand Down Expand Up @@ -1840,10 +1845,11 @@ def make_mermaid(self) -> str:
the tier palette is shared from ``_DIAGRAM_THEMES["light"]``; edge
thickness encodes cardinality (thick = 1:1, thin = one-to-many),
**not** primary-vs-secondary; renamed foreign keys are drawn in the
theme's amber. Three Graphviz features have no Mermaid equivalent and
are omitted: the top-right schema-label placement, the Helvetica font,
and the underline marking a dimension-introducing table. Dark mode is
left to the Mermaid host's theme rather than emitted inline.
theme's amber. Two Graphviz features have no Mermaid equivalent and
are omitted: the top-right schema-label placement and the underline
marking a dimension-introducing table. The typeface and dark mode are
left to the Mermaid host rather than emitted inline, so
``display.diagram_fontname`` and ``display.diagram_theme`` do not apply.

Examples
--------
Expand Down
11 changes: 11 additions & 0 deletions src/datajoint/settings.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"loglevel": "DJ_LOG_LEVEL",
"display.diagram_direction": "DJ_DIAGRAM_DIRECTION",
"display.diagram_theme": "DJ_DIAGRAM_THEME",
"display.diagram_fontname": "DJ_DIAGRAM_FONTNAME",
}

Role = Enum("Role", "manual lookup imported computed job")
Expand Down Expand Up @@ -260,6 +261,16 @@ class DisplaySettings(BaseSettings):
"mode; default), 'light', or 'dark' (dark background with adjusted palette)"
),
)
diagram_fontname: str = Field(
default="Helvetica",
min_length=1,
validation_alias="DJ_DIAGRAM_FONTNAME",
description=(
"Typeface of node and schema labels in Graphviz diagrams (default 'Helvetica'). Graphviz sizes "
"nodes with the face it finds on the rendering machine, so a face that machine lacks changes "
"the layout. Set it only where the face is installed. Mermaid output names no typeface."
),
)


class StoresSettings(BaseSettings):
Expand Down
37 changes: 37 additions & 0 deletions tests/integration/test_diagram_style.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
fails here; a layout tweak does not.
"""

import re
import time

import pytest
Expand Down Expand Up @@ -111,6 +112,42 @@ def test_auto_theme_is_adaptive(schema_by_backend):
assert "#e8f0e9" in svg and "#16281f" in svg, "auto theme must carry both light base and dark override colors"


def _fontnames(dot_source):
"""Every fontname value in a DOT source, in order of appearance."""
return [quoted or bare for quoted, bare in re.findall(r'fontname=(?:"([^"]*)"|([^\s,;\]]+))', dot_source)]


def test_diagram_font_default_is_helvetica(schema_by_backend):
"""Without the setting, every node and schema label stays in Helvetica (#1539)."""
if not dj.diagram.diagram_active:
pytest.skip("networkx/pydot not available")
ctx = _build(schema_by_backend)
assert dj.config.display.diagram_fontname == "Helvetica"
fonts = _fontnames(dj.Diagram(schema_by_backend, context=ctx).make_dot().to_string())
# six table nodes (Subject, Params, Session, Session.Note, Scan, Analysis) and one schema cluster
assert fonts == ["Helvetica"] * len(fonts) and len(fonts) >= 7


def test_diagram_font_setting(schema_by_backend):
"""``display.diagram_fontname`` sets the face of every node and schema label (#1539)."""
if not dj.diagram.diagram_active:
pytest.skip("networkx/pydot not available")
ctx = _build(schema_by_backend)
diagram = dj.Diagram(schema_by_backend, context=ctx)
default_count = len(_fontnames(diagram.make_dot().to_string()))
default_mermaid = diagram.make_mermaid()
with dj.config.override(display__diagram_fontname="Roboto Slab"):
dot_source = diagram.make_dot().to_string()
svg = diagram.svg_string()
mermaid = diagram.make_mermaid()
fonts = _fontnames(dot_source)
assert fonts == ["Roboto Slab"] * default_count
assert "Helvetica" not in dot_source
assert 'font-family="Roboto Slab' in svg
assert mermaid == default_mermaid # Mermaid output leaves the typeface to its host
assert dj.config.display.diagram_fontname == "Helvetica" # restored after the override


def test_mermaid_matches_modernized_notation(schema_by_backend):
"""`make_mermaid` speaks the same notation as the Graphviz renderer:
shared palette, cardinality-weighted edges, amber renamed FKs, and
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/test_settings.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,27 @@ def test_display_limit(self):
finally:
dj.config.display.limit = original

def test_diagram_fontname_default(self, monkeypatch):
"""Diagram labels default to Helvetica."""
from datajoint.settings import DisplaySettings

monkeypatch.delenv("DJ_DIAGRAM_FONTNAME", raising=False)
assert DisplaySettings().diagram_fontname == "Helvetica"

def test_diagram_fontname_from_env(self, monkeypatch):
"""DJ_DIAGRAM_FONTNAME sets display.diagram_fontname, and outranks a config file value."""
from datajoint.settings import ENV_VAR_MAPPING, DisplaySettings

monkeypatch.setenv("DJ_DIAGRAM_FONTNAME", "Roboto Slab")
assert DisplaySettings().diagram_fontname == "Roboto Slab"
assert ENV_VAR_MAPPING["display.diagram_fontname"] == "DJ_DIAGRAM_FONTNAME"

def test_diagram_fontname_rejects_empty(self):
"""An empty name would let Graphviz fall back to Times."""
with pytest.raises(ValidationError):
dj.config.display.diagram_fontname = ""
assert dj.config.display.diagram_fontname == "Helvetica"


class TestCachePaths:
"""Test cache path settings."""
Expand Down
Loading

Back | FazBrowse Home | New Git URL