Skip to content

Commit 85a8ee2

Browse files
committed
FEAT(plot): offer with_XXX() methods on Plotter/Style
+ doc: dogfood in instructions & sample op in changes.
1 parent 609131d commit 85a8ee2

File tree

5 files changed

+47
-26
lines changed

5 files changed

+47
-26
lines changed

.vscode/cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"doctesting",
5454
"doctests",
5555
"dogfood",
56+
"dogfoods",
5657
"dynaimage",
5758
"errgraph",
5859
"exemethod",

CHANGES.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ v6.0.0 (13 Apr 2020, @ankostis): New Plotting Device...
2828
:hide:
2929

3030
>>> from graphtik import compose, operation, varargs
31-
>>> from graphtik import plot
31+
>>> from graphtik.plot import get_installed_plotter
3232
>>> netop = compose('', operation(print, name='print-something', needs=varargs("any"), provides="str")())
3333
>>> netop.net.graph.graph['label'] = None
3434
>>> dot = netop.plot(
35-
... plotter=plot.Plotter(style=plot.Style(kw_legend=None)),
36-
... name=None
35+
... name=None,
36+
... plotter=get_installed_plotter().with_styles(kw_legend=None),
3737
... )
3838

3939
+ ENH: Convey graph, node & edge ("non-private") attributes from the *networkx* graph

docs/source/conf.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,11 @@
105105
graphtik_save_dot_files = True
106106

107107
# Plot graphtik SVGs with links to docs.
108-
#
109-
plotter = plot.get_installed_plotter().copy()
110-
plotter.style.py_item_url_format = "../reference.html#%(dot_path)s"
111-
plot.set_installed_plotter(plotter)
108+
plot.set_installed_plotter(
109+
plot.get_installed_plotter().with_styles(
110+
py_item_url_format="../reference.html#%(dot_path)s"
111+
)
112+
)
112113

113114
github_slug = "pygraphkit/graphtik"
114115
try:

docs/source/plotting.rst

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -329,21 +329,25 @@ Plot customizations
329329

330330
get_installed_plotter().style.kw_op["fillcolor"] = "purple"
331331

332-
This will affect all :meth:`.Plottable.plot()` calls for a python session.
333-
You cannot change the *plotter* instance with this method - only styles
334-
and monkeypatching.
332+
- This will affect all :meth:`.Plottable.plot()` calls for a python session.
333+
- You cannot change the *plotter* instance with this method - only styles
334+
(and monkeypatching plotter's methods).
335335

336-
2. Create a new :class:`.Plotter` (or :meth:`clone <.Plotter.copy>` the default plotter)
337-
to customize its styles, and make it the new *installed plotter*,
338-
affecting all calls in :class:`context <contextvars.ContextVar>`.
336+
2. Create a new :class:`.Plotter` with customized :attr:`.Plotter.style`, or
337+
clone and customize the styles of an existing plotter by the use of
338+
its :meth:`.Plotter.with_styles` method, and make that the new *installed plotter*.
339339

340-
If customizing style constants is not enough, you may subclass :class:`.Plotter`
341-
and install it.
340+
- This will affect all calls in :class:`context <contextvars.ContextVar>`.
341+
- If customizing style constants is not enough, you may subclass :class:`.Plotter`
342+
and install it.
342343

343-
3. Do as above (new *plotter*), but pass the customized plotter when calling
344-
:meth:`.Plottable.plot()`.
344+
3. Take any *plotter*, customize its clone, and then call :meth:`.Plottable.plot()`,
345+
with something like that::
345346

346-
This project does (2) in its own :file:`docs/source/conf.py` sphinx file.
347+
netop.plot(plotter=get_installed_plotter().with_styles(kw_legend=None))
348+
349+
350+
This project dogfoods (2) in its own :file:`docs/source/conf.py` sphinx file.
347351
In particular, it configures the base-url of operation node links
348352
(by default, nodes do not link to any url).
349353

graphtik/plot.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,21 @@ class Style:
472472
"target": "_top",
473473
}
474474

475-
def __init__(self, **kw):
475+
def __init__(self, _prototype: "Style" = None, **kw):
476476
"""
477477
Deep-copy class-attributes (to avoid sideffects) and apply user-overrides,
478478
479479
retargeting any :class:`Ref` on my self (from class's 'values).
480+
481+
:param _prototype:
482+
If given, class-attributes are ignored, deep-copying "public" properties
483+
only from this instance (and apply on top any `kw`).
480484
"""
485+
if _prototype is None:
486+
_prototype = type(self)
487+
else:
488+
assert isinstance(_prototype, Style), _prototype
489+
481490
values = {
482491
k: v
483492
for k, v in vars(type(self)).items()
@@ -515,6 +524,10 @@ def remap_item(path, k, v):
515524
values = vars(self)
516525
vars(self).update(remap(values, remap_item))
517526

527+
def with_set(self, **kw) -> "Style":
528+
"""Returns a deep-clone modified by `kw`."""
529+
return type(self)(_prototype=self, **kw)
530+
518531

519532
class Plotter:
520533
"""
@@ -526,14 +539,16 @@ class Plotter:
526539
controlling theming values & dictionaries for plots.
527540
"""
528541

529-
def __init__(self, style=None):
530-
self.style = style or Style()
542+
def __init__(self, style: Style = None):
543+
self.style: Style = style or Style()
531544

532-
def copy(self) -> "Plotter":
533-
"""deep copy of all styles"""
534-
clone = type(self)(self.style)
535-
clone.__dict__ = remap(vars(self), lambda *a: True)
536-
return clone
545+
def with_styles(self, **kw) -> "Plotter":
546+
"""
547+
Returns a cloned plotter with deep-coped styles modified as given.
548+
549+
See also :meth:`Style.with_set()`.
550+
"""
551+
return type(self)(self.style.with_set(**kw))
537552

538553
def plot(self, plot_args: PlotArgs):
539554
dot = self.build_pydot(plot_args)

0 commit comments

Comments
 (0)