plotnine 0.15.0.dev2__py3-none-any.whl → 0.15.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plotnine/__init__.py +2 -0
- plotnine/_mpl/layout_manager/_engine.py +1 -1
- plotnine/_mpl/layout_manager/_layout_items.py +128 -83
- plotnine/_mpl/layout_manager/_layout_tree.py +761 -310
- plotnine/_mpl/layout_manager/_spaces.py +320 -103
- plotnine/_mpl/patches.py +70 -34
- plotnine/_mpl/text.py +144 -63
- plotnine/_mpl/utils.py +1 -1
- plotnine/_utils/__init__.py +50 -107
- plotnine/_utils/context.py +78 -2
- plotnine/_utils/ipython.py +35 -51
- plotnine/_utils/quarto.py +21 -0
- plotnine/_utils/yippie.py +115 -0
- plotnine/composition/__init__.py +11 -0
- plotnine/composition/_beside.py +55 -0
- plotnine/composition/_compose.py +471 -0
- plotnine/composition/_plot_spacer.py +60 -0
- plotnine/composition/_stack.py +55 -0
- plotnine/coords/coord.py +3 -3
- plotnine/data/__init__.py +31 -0
- plotnine/data/anscombe-quartet.csv +45 -0
- plotnine/doctools.py +4 -4
- plotnine/facets/facet.py +4 -4
- plotnine/facets/strips.py +17 -28
- plotnine/geoms/annotate.py +13 -13
- plotnine/geoms/annotation_logticks.py +7 -8
- plotnine/geoms/annotation_stripes.py +6 -6
- plotnine/geoms/geom.py +60 -27
- plotnine/geoms/geom_abline.py +3 -2
- plotnine/geoms/geom_area.py +2 -2
- plotnine/geoms/geom_bar.py +11 -2
- plotnine/geoms/geom_bin_2d.py +6 -2
- plotnine/geoms/geom_blank.py +0 -3
- plotnine/geoms/geom_boxplot.py +8 -4
- plotnine/geoms/geom_col.py +8 -2
- plotnine/geoms/geom_count.py +6 -2
- plotnine/geoms/geom_crossbar.py +3 -3
- plotnine/geoms/geom_density_2d.py +6 -2
- plotnine/geoms/geom_dotplot.py +2 -2
- plotnine/geoms/geom_errorbar.py +2 -2
- plotnine/geoms/geom_errorbarh.py +2 -2
- plotnine/geoms/geom_histogram.py +1 -1
- plotnine/geoms/geom_hline.py +3 -2
- plotnine/geoms/geom_linerange.py +2 -2
- plotnine/geoms/geom_map.py +5 -5
- plotnine/geoms/geom_path.py +11 -12
- plotnine/geoms/geom_point.py +4 -5
- plotnine/geoms/geom_pointdensity.py +4 -0
- plotnine/geoms/geom_pointrange.py +3 -5
- plotnine/geoms/geom_polygon.py +2 -3
- plotnine/geoms/geom_qq.py +4 -0
- plotnine/geoms/geom_qq_line.py +4 -0
- plotnine/geoms/geom_quantile.py +4 -0
- plotnine/geoms/geom_raster.py +4 -5
- plotnine/geoms/geom_rect.py +3 -4
- plotnine/geoms/geom_ribbon.py +7 -7
- plotnine/geoms/geom_rug.py +1 -1
- plotnine/geoms/geom_segment.py +2 -2
- plotnine/geoms/geom_sina.py +3 -3
- plotnine/geoms/geom_smooth.py +7 -3
- plotnine/geoms/geom_step.py +2 -2
- plotnine/geoms/geom_text.py +2 -3
- plotnine/geoms/geom_violin.py +28 -8
- plotnine/geoms/geom_vline.py +3 -2
- plotnine/ggplot.py +64 -85
- plotnine/guides/guide.py +7 -10
- plotnine/guides/guide_colorbar.py +3 -3
- plotnine/guides/guide_legend.py +3 -3
- plotnine/guides/guides.py +6 -6
- plotnine/helpers.py +49 -0
- plotnine/iapi.py +28 -5
- plotnine/labels.py +3 -3
- plotnine/layer.py +36 -19
- plotnine/mapping/_atomic.py +178 -0
- plotnine/mapping/_env.py +13 -2
- plotnine/mapping/_eval_environment.py +85 -0
- plotnine/mapping/aes.py +91 -72
- plotnine/mapping/evaluation.py +7 -65
- plotnine/scales/__init__.py +2 -0
- plotnine/scales/limits.py +7 -7
- plotnine/scales/scale.py +3 -3
- plotnine/scales/scale_color.py +82 -18
- plotnine/scales/scale_continuous.py +6 -4
- plotnine/scales/scale_datetime.py +28 -14
- plotnine/scales/scale_discrete.py +1 -1
- plotnine/scales/scale_identity.py +21 -2
- plotnine/scales/scale_manual.py +8 -2
- plotnine/scales/scale_xy.py +2 -2
- plotnine/stats/binning.py +4 -1
- plotnine/stats/smoothers.py +23 -36
- plotnine/stats/stat.py +20 -32
- plotnine/stats/stat_bin.py +6 -5
- plotnine/stats/stat_bin_2d.py +11 -9
- plotnine/stats/stat_bindot.py +13 -16
- plotnine/stats/stat_boxplot.py +6 -6
- plotnine/stats/stat_count.py +6 -9
- plotnine/stats/stat_density.py +7 -10
- plotnine/stats/stat_density_2d.py +12 -8
- plotnine/stats/stat_ecdf.py +7 -6
- plotnine/stats/stat_ellipse.py +9 -6
- plotnine/stats/stat_function.py +10 -8
- plotnine/stats/stat_hull.py +6 -3
- plotnine/stats/stat_identity.py +5 -2
- plotnine/stats/stat_pointdensity.py +5 -7
- plotnine/stats/stat_qq.py +46 -20
- plotnine/stats/stat_qq_line.py +16 -11
- plotnine/stats/stat_quantile.py +15 -9
- plotnine/stats/stat_sina.py +45 -14
- plotnine/stats/stat_smooth.py +8 -10
- plotnine/stats/stat_sum.py +5 -2
- plotnine/stats/stat_summary.py +7 -10
- plotnine/stats/stat_summary_bin.py +11 -14
- plotnine/stats/stat_unique.py +5 -2
- plotnine/stats/stat_ydensity.py +8 -11
- plotnine/themes/elements/__init__.py +2 -1
- plotnine/themes/elements/element_line.py +17 -9
- plotnine/themes/elements/margin.py +64 -1
- plotnine/themes/theme.py +9 -1
- plotnine/themes/theme_538.py +0 -1
- plotnine/themes/theme_bw.py +0 -1
- plotnine/themes/theme_dark.py +0 -1
- plotnine/themes/theme_gray.py +6 -5
- plotnine/themes/theme_light.py +1 -1
- plotnine/themes/theme_matplotlib.py +5 -5
- plotnine/themes/theme_seaborn.py +7 -4
- plotnine/themes/theme_void.py +9 -8
- plotnine/themes/theme_xkcd.py +0 -1
- plotnine/themes/themeable.py +110 -32
- plotnine/typing.py +17 -6
- plotnine/watermark.py +3 -3
- {plotnine-0.15.0.dev2.dist-info → plotnine-0.15.1.dist-info}/METADATA +13 -6
- plotnine-0.15.1.dist-info/RECORD +221 -0
- {plotnine-0.15.0.dev2.dist-info → plotnine-0.15.1.dist-info}/WHEEL +1 -1
- plotnine/plot_composition/__init__.py +0 -10
- plotnine/plot_composition/_compose.py +0 -436
- plotnine/plot_composition/_spacer.py +0 -32
- plotnine-0.15.0.dev2.dist-info/RECORD +0 -214
- /plotnine/{plot_composition → composition}/_plotspec.py +0 -0
- {plotnine-0.15.0.dev2.dist-info → plotnine-0.15.1.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.0.dev2.dist-info → plotnine-0.15.1.dist-info}/top_level.txt +0 -0
plotnine/__init__.py
CHANGED
|
@@ -198,6 +198,7 @@ from .scales import (
|
|
|
198
198
|
scale_size_radius,
|
|
199
199
|
scale_stroke,
|
|
200
200
|
scale_stroke_continuous,
|
|
201
|
+
scale_stroke_identity,
|
|
201
202
|
scale_x_continuous,
|
|
202
203
|
scale_x_date,
|
|
203
204
|
scale_x_datetime,
|
|
@@ -441,6 +442,7 @@ __all__ = (
|
|
|
441
442
|
"scale_size_radius",
|
|
442
443
|
"scale_stroke",
|
|
443
444
|
"scale_stroke_continuous",
|
|
445
|
+
"scale_stroke_identity",
|
|
444
446
|
"scale_x_continuous",
|
|
445
447
|
"scale_x_date",
|
|
446
448
|
"scale_x_datetime",
|
|
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, cast
|
|
|
6
6
|
|
|
7
7
|
from matplotlib.text import Text
|
|
8
8
|
|
|
9
|
+
from plotnine._mpl.patches import StripTextPatch
|
|
10
|
+
from plotnine._utils import ha_as_float, va_as_float
|
|
9
11
|
from plotnine.exceptions import PlotnineError
|
|
10
12
|
|
|
11
13
|
from ..utils import (
|
|
@@ -35,7 +37,11 @@ if TYPE_CHECKING:
|
|
|
35
37
|
from plotnine._mpl.text import StripText
|
|
36
38
|
from plotnine.iapi import legend_artists
|
|
37
39
|
from plotnine.themes.elements import margin as Margin
|
|
38
|
-
from plotnine.typing import
|
|
40
|
+
from plotnine.typing import (
|
|
41
|
+
HorizontalJustification,
|
|
42
|
+
StripPosition,
|
|
43
|
+
VerticalJustification,
|
|
44
|
+
)
|
|
39
45
|
|
|
40
46
|
from ._spaces import LayoutSpaces
|
|
41
47
|
|
|
@@ -299,43 +305,9 @@ class LayoutItems:
|
|
|
299
305
|
|
|
300
306
|
return chain(major, minor)
|
|
301
307
|
|
|
302
|
-
def
|
|
303
|
-
"""
|
|
304
|
-
Return XTicks paddings
|
|
305
|
-
"""
|
|
306
|
-
# In plotnine tick padding are specified as a margin to the
|
|
307
|
-
# the axis_text.
|
|
308
|
-
major, minor = [], []
|
|
309
|
-
if not self._is_blank("axis_text_x"):
|
|
310
|
-
h = self.plot.figure.bbox.height
|
|
311
|
-
major = [
|
|
312
|
-
(t.get_pad() or 0) / h for t in ax.xaxis.get_major_ticks()
|
|
313
|
-
]
|
|
314
|
-
minor = [
|
|
315
|
-
(t.get_pad() or 0) / h for t in ax.xaxis.get_minor_ticks()
|
|
316
|
-
]
|
|
317
|
-
return chain(major, minor)
|
|
318
|
-
|
|
319
|
-
def axis_text_y_margin(self, ax: Axes) -> Iterator[float]:
|
|
320
|
-
"""
|
|
321
|
-
Return YTicks paddings
|
|
322
|
-
"""
|
|
323
|
-
# In plotnine tick padding are specified as a margin to the
|
|
324
|
-
# the axis_text.
|
|
325
|
-
major, minor = [], []
|
|
326
|
-
if not self._is_blank("axis_text_y"):
|
|
327
|
-
w = self.plot.figure.bbox.width
|
|
328
|
-
major = [
|
|
329
|
-
(t.get_pad() or 0) / w for t in ax.yaxis.get_major_ticks()
|
|
330
|
-
]
|
|
331
|
-
minor = [
|
|
332
|
-
(t.get_pad() or 0) / w for t in ax.yaxis.get_minor_ticks()
|
|
333
|
-
]
|
|
334
|
-
return chain(major, minor)
|
|
335
|
-
|
|
336
|
-
def strip_text_x_height(self, position: StripPosition) -> float:
|
|
308
|
+
def strip_text_x_extra_height(self, position: StripPosition) -> float:
|
|
337
309
|
"""
|
|
338
|
-
Height taken up by the top strips
|
|
310
|
+
Height taken up by the top strips that is outside the panels
|
|
339
311
|
"""
|
|
340
312
|
if not self.strip_text_x:
|
|
341
313
|
return 0
|
|
@@ -345,11 +317,23 @@ class LayoutItems:
|
|
|
345
317
|
for st in self.strip_text_x
|
|
346
318
|
if st.patch.position == position
|
|
347
319
|
]
|
|
348
|
-
return self.calc.max_height(artists)
|
|
349
320
|
|
|
350
|
-
|
|
321
|
+
heights = []
|
|
322
|
+
|
|
323
|
+
for a in artists:
|
|
324
|
+
info = (
|
|
325
|
+
a.text.draw_info
|
|
326
|
+
if isinstance(a, StripTextPatch)
|
|
327
|
+
else a.draw_info
|
|
328
|
+
)
|
|
329
|
+
h = self.calc.height(a)
|
|
330
|
+
heights.append(max(h + h * info.strip_align, 0))
|
|
331
|
+
|
|
332
|
+
return max(heights)
|
|
333
|
+
|
|
334
|
+
def strip_text_y_extra_width(self, position: StripPosition) -> float:
|
|
351
335
|
"""
|
|
352
|
-
Width taken up by the
|
|
336
|
+
Width taken up by the top strips that is outside the panels
|
|
353
337
|
"""
|
|
354
338
|
if not self.strip_text_y:
|
|
355
339
|
return 0
|
|
@@ -359,7 +343,19 @@ class LayoutItems:
|
|
|
359
343
|
for st in self.strip_text_y
|
|
360
344
|
if st.patch.position == position
|
|
361
345
|
]
|
|
362
|
-
|
|
346
|
+
|
|
347
|
+
widths = []
|
|
348
|
+
|
|
349
|
+
for a in artists:
|
|
350
|
+
info = (
|
|
351
|
+
a.text.draw_info
|
|
352
|
+
if isinstance(a, StripTextPatch)
|
|
353
|
+
else a.draw_info
|
|
354
|
+
)
|
|
355
|
+
w = self.calc.width(a)
|
|
356
|
+
widths.append(max(w + w * info.strip_align, 0))
|
|
357
|
+
|
|
358
|
+
return max(widths)
|
|
363
359
|
|
|
364
360
|
def axis_ticks_x_max_height_at(self, location: AxesLocation) -> float:
|
|
365
361
|
"""
|
|
@@ -377,10 +373,7 @@ class LayoutItems:
|
|
|
377
373
|
Return maximum height[figure space] of x tick labels
|
|
378
374
|
"""
|
|
379
375
|
heights = [
|
|
380
|
-
self.calc.tight_height(label)
|
|
381
|
-
for label, pad in zip(
|
|
382
|
-
self.axis_text_x(ax), self.axis_text_x_margin(ax)
|
|
383
|
-
)
|
|
376
|
+
self.calc.tight_height(label) for label in self.axis_text_x(ax)
|
|
384
377
|
]
|
|
385
378
|
return max(heights) if len(heights) else 0
|
|
386
379
|
|
|
@@ -410,10 +403,7 @@ class LayoutItems:
|
|
|
410
403
|
Return maximum width[figure space] of y tick labels
|
|
411
404
|
"""
|
|
412
405
|
widths = [
|
|
413
|
-
self.calc.tight_width(label)
|
|
414
|
-
for label, pad in zip(
|
|
415
|
-
self.axis_text_y(ax), self.axis_text_y_margin(ax)
|
|
416
|
-
)
|
|
406
|
+
self.calc.tight_width(label) for label in self.axis_text_y(ax)
|
|
417
407
|
]
|
|
418
408
|
return max(widths) if len(widths) else 0
|
|
419
409
|
|
|
@@ -529,6 +519,8 @@ class LayoutItems:
|
|
|
529
519
|
|
|
530
520
|
self._adjust_axis_text_x(justify)
|
|
531
521
|
self._adjust_axis_text_y(justify)
|
|
522
|
+
self._strip_text_x_background_equal_heights()
|
|
523
|
+
self._strip_text_y_background_equal_widths()
|
|
532
524
|
|
|
533
525
|
def _adjust_axis_text_x(self, justify: TextJustifier):
|
|
534
526
|
"""
|
|
@@ -614,6 +606,36 @@ class LayoutItems:
|
|
|
614
606
|
text, ha, -axis_text_col_width, 0, width=width
|
|
615
607
|
)
|
|
616
608
|
|
|
609
|
+
def _strip_text_x_background_equal_heights(self):
|
|
610
|
+
"""
|
|
611
|
+
Make the strip_text_x_backgrounds have equal heights
|
|
612
|
+
|
|
613
|
+
The smaller heights are expanded to match the largest height
|
|
614
|
+
"""
|
|
615
|
+
if not self.strip_text_x:
|
|
616
|
+
return
|
|
617
|
+
|
|
618
|
+
heights = [self.calc.bbox(t.patch).height for t in self.strip_text_x]
|
|
619
|
+
max_height = max(heights)
|
|
620
|
+
relative_heights = [max_height / h for h in heights]
|
|
621
|
+
for text, scale in zip(self.strip_text_x, relative_heights):
|
|
622
|
+
text.patch.expand = scale
|
|
623
|
+
|
|
624
|
+
def _strip_text_y_background_equal_widths(self):
|
|
625
|
+
"""
|
|
626
|
+
Make the strip_text_y_backgrounds have equal widths
|
|
627
|
+
|
|
628
|
+
The smaller widths are expanded to match the largest width
|
|
629
|
+
"""
|
|
630
|
+
if not self.strip_text_y:
|
|
631
|
+
return
|
|
632
|
+
|
|
633
|
+
widths = [self.calc.bbox(t.patch).width for t in self.strip_text_y]
|
|
634
|
+
max_width = max(widths)
|
|
635
|
+
relative_widths = [max_width / w for w in widths]
|
|
636
|
+
for text, scale in zip(self.strip_text_y, relative_widths):
|
|
637
|
+
text.patch.expand = scale
|
|
638
|
+
|
|
617
639
|
|
|
618
640
|
def _text_is_visible(text: Text) -> bool:
|
|
619
641
|
"""
|
|
@@ -636,7 +658,7 @@ class TextJustifier:
|
|
|
636
658
|
def horizontally(
|
|
637
659
|
self,
|
|
638
660
|
text: Text,
|
|
639
|
-
ha:
|
|
661
|
+
ha: HorizontalJustification | float,
|
|
640
662
|
left: float,
|
|
641
663
|
right: float,
|
|
642
664
|
width: float | None = None,
|
|
@@ -644,8 +666,7 @@ class TextJustifier:
|
|
|
644
666
|
"""
|
|
645
667
|
Horizontally Justify text between left and right
|
|
646
668
|
"""
|
|
647
|
-
|
|
648
|
-
rel = lookup.get(ha, ha) # pyright: ignore[reportCallIssue, reportArgumentType]
|
|
669
|
+
rel = ha_as_float(ha)
|
|
649
670
|
if width is None:
|
|
650
671
|
width = self.spaces.items.calc.width(text)
|
|
651
672
|
x = rel_position(rel, width, left, right)
|
|
@@ -655,7 +676,7 @@ class TextJustifier:
|
|
|
655
676
|
def vertically(
|
|
656
677
|
self,
|
|
657
678
|
text: Text,
|
|
658
|
-
va:
|
|
679
|
+
va: VerticalJustification | float,
|
|
659
680
|
bottom: float,
|
|
660
681
|
top: float,
|
|
661
682
|
height: float | None = None,
|
|
@@ -663,14 +684,7 @@ class TextJustifier:
|
|
|
663
684
|
"""
|
|
664
685
|
Vertically Justify text between bottom and top
|
|
665
686
|
"""
|
|
666
|
-
|
|
667
|
-
"top": 1.0,
|
|
668
|
-
"center": 0.5,
|
|
669
|
-
"baseline": 0.5,
|
|
670
|
-
"center_baseline": 0.5,
|
|
671
|
-
"bottom": 0.0,
|
|
672
|
-
}
|
|
673
|
-
rel = lookup.get(va, va) # pyright: ignore[reportCallIssue, reportArgumentType]
|
|
687
|
+
rel = va_as_float(va)
|
|
674
688
|
|
|
675
689
|
if height is None:
|
|
676
690
|
height = self.spaces.items.calc.height(text)
|
|
@@ -678,13 +692,19 @@ class TextJustifier:
|
|
|
678
692
|
text.set_y(y)
|
|
679
693
|
text.set_verticalalignment("bottom")
|
|
680
694
|
|
|
681
|
-
def horizontally_across_panel(
|
|
695
|
+
def horizontally_across_panel(
|
|
696
|
+
self, text: Text, ha: HorizontalJustification | float
|
|
697
|
+
):
|
|
682
698
|
"""
|
|
683
699
|
Horizontally Justify text accross the panel(s) width
|
|
684
700
|
"""
|
|
685
|
-
self.horizontally(
|
|
701
|
+
self.horizontally(
|
|
702
|
+
text, ha, self.spaces.l.panel_left, self.spaces.r.panel_right
|
|
703
|
+
)
|
|
686
704
|
|
|
687
|
-
def horizontally_across_plot(
|
|
705
|
+
def horizontally_across_plot(
|
|
706
|
+
self, text: Text, ha: HorizontalJustification | float
|
|
707
|
+
):
|
|
688
708
|
"""
|
|
689
709
|
Horizontally Justify text across the plot's width
|
|
690
710
|
"""
|
|
@@ -692,13 +712,19 @@ class TextJustifier:
|
|
|
692
712
|
text, ha, self.spaces.l.plot_left, self.spaces.r.plot_right
|
|
693
713
|
)
|
|
694
714
|
|
|
695
|
-
def vertically_along_panel(
|
|
715
|
+
def vertically_along_panel(
|
|
716
|
+
self, text: Text, va: VerticalJustification | float
|
|
717
|
+
):
|
|
696
718
|
"""
|
|
697
719
|
Horizontally Justify text along the panel(s) height
|
|
698
720
|
"""
|
|
699
|
-
self.vertically(
|
|
721
|
+
self.vertically(
|
|
722
|
+
text, va, self.spaces.b.panel_bottom, self.spaces.t.panel_top
|
|
723
|
+
)
|
|
700
724
|
|
|
701
|
-
def vertically_along_plot(
|
|
725
|
+
def vertically_along_plot(
|
|
726
|
+
self, text: Text, va: VerticalJustification | float
|
|
727
|
+
):
|
|
702
728
|
"""
|
|
703
729
|
Vertically Justify text along the plot's height
|
|
704
730
|
"""
|
|
@@ -763,7 +789,7 @@ def set_legends_position(legends: legend_artists, spaces: LayoutSpaces):
|
|
|
763
789
|
if legends.right:
|
|
764
790
|
y = rel_position(
|
|
765
791
|
legends.right.justification,
|
|
766
|
-
spaces.r.
|
|
792
|
+
spaces.r.legend_height,
|
|
767
793
|
params.bottom,
|
|
768
794
|
params.top,
|
|
769
795
|
)
|
|
@@ -773,7 +799,7 @@ def set_legends_position(legends: legend_artists, spaces: LayoutSpaces):
|
|
|
773
799
|
if legends.left:
|
|
774
800
|
y = rel_position(
|
|
775
801
|
legends.left.justification,
|
|
776
|
-
spaces.l.
|
|
802
|
+
spaces.l.legend_height,
|
|
777
803
|
params.bottom,
|
|
778
804
|
params.top,
|
|
779
805
|
)
|
|
@@ -783,7 +809,7 @@ def set_legends_position(legends: legend_artists, spaces: LayoutSpaces):
|
|
|
783
809
|
if legends.top:
|
|
784
810
|
x = rel_position(
|
|
785
811
|
legends.top.justification,
|
|
786
|
-
spaces.t.
|
|
812
|
+
spaces.t.legend_width,
|
|
787
813
|
params.left,
|
|
788
814
|
params.right,
|
|
789
815
|
)
|
|
@@ -793,7 +819,7 @@ def set_legends_position(legends: legend_artists, spaces: LayoutSpaces):
|
|
|
793
819
|
if legends.bottom:
|
|
794
820
|
x = rel_position(
|
|
795
821
|
legends.bottom.justification,
|
|
796
|
-
spaces.b.
|
|
822
|
+
spaces.b.legend_width,
|
|
797
823
|
params.left,
|
|
798
824
|
params.right,
|
|
799
825
|
)
|
|
@@ -870,7 +896,10 @@ def set_plot_tag_position(tag: Text, spaces: LayoutSpaces):
|
|
|
870
896
|
|
|
871
897
|
def set_plot_tag_position_in_margin(tag: Text, spaces: LayoutSpaces):
|
|
872
898
|
"""
|
|
873
|
-
Place the tag in
|
|
899
|
+
Place the tag in an inner margin around the plot
|
|
900
|
+
|
|
901
|
+
The panel_margin remains outside the tag. For compositions, the
|
|
902
|
+
tag is placed and within the tag_alignment space.
|
|
874
903
|
"""
|
|
875
904
|
position: TagPosition = spaces.plot.theme.getp("plot_tag_position")
|
|
876
905
|
if not isinstance(position, str):
|
|
@@ -880,18 +909,34 @@ def set_plot_tag_position_in_margin(tag: Text, spaces: LayoutSpaces):
|
|
|
880
909
|
)
|
|
881
910
|
|
|
882
911
|
tag.set_position(spaces.to_figure_space((0.5, 0.5)))
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
tag.
|
|
889
|
-
|
|
890
|
-
|
|
912
|
+
ha = spaces.plot.theme.get_ha("plot_tag")
|
|
913
|
+
va = spaces.plot.theme.get_va("plot_tag")
|
|
914
|
+
if "left" in position: # left, topleft, bottomleft
|
|
915
|
+
space = spaces.l.tag_alignment
|
|
916
|
+
x = spaces.l.x1("plot_tag") - (1 - ha) * space
|
|
917
|
+
tag.set_x(x)
|
|
918
|
+
tag.set_horizontalalignment("left")
|
|
919
|
+
if "right" in position: # right, topright, bottomright
|
|
920
|
+
space = spaces.r.tag_alignment
|
|
921
|
+
x = spaces.r.x1("plot_tag") + ha * space
|
|
922
|
+
tag.set_x(x)
|
|
891
923
|
tag.set_horizontalalignment("left")
|
|
892
|
-
if "
|
|
893
|
-
|
|
894
|
-
|
|
924
|
+
if "bottom" in position: # bottom, bottomleft, bottomright
|
|
925
|
+
space = spaces.b.tag_alignment
|
|
926
|
+
y = spaces.b.y1("plot_tag") + (1 - va) * space
|
|
927
|
+
tag.set_y(y)
|
|
928
|
+
tag.set_verticalalignment("bottom")
|
|
929
|
+
if "top" in position: # top, topleft, topright
|
|
930
|
+
space = spaces.t.tag_alignment
|
|
931
|
+
y = spaces.t.y1("plot_tag") + va * space
|
|
932
|
+
tag.set_y(y)
|
|
933
|
+
tag.set_verticalalignment("bottom")
|
|
934
|
+
|
|
935
|
+
justify = TextJustifier(spaces)
|
|
936
|
+
if position in ("left", "right"):
|
|
937
|
+
justify.vertically_along_plot(tag, va)
|
|
938
|
+
elif position in ("top", "bottom"):
|
|
939
|
+
justify.horizontally_across_plot(tag, ha)
|
|
895
940
|
|
|
896
941
|
|
|
897
942
|
def _plot_tag_margin_adjustment(
|