marsilea 0.3.2__py3-none-any.whl → 0.3.3__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.
marsilea/plotter/text.py CHANGED
@@ -22,6 +22,7 @@ class Segment:
22
22
  """
23
23
  The total length is unchanged
24
24
  """
25
+
25
26
  up: float
26
27
  low: float
27
28
 
@@ -111,8 +112,9 @@ def adjust_segments(lim: Segment, segments: List[Segment]):
111
112
  segments_length = np.sum(sl)
112
113
  space = lim.length
113
114
  if segments_length > space:
114
- warnings.warn("No enough space to place all labels, "
115
- "try reducing the fontsize.")
115
+ warnings.warn(
116
+ "No enough space to place all labels, " "try reducing the fontsize."
117
+ )
116
118
 
117
119
  segments_length_r = segments_length
118
120
  space_r = space
@@ -201,15 +203,23 @@ def plot_segments(segments, lim=None):
201
203
 
202
204
 
203
205
  class AdjustableText:
204
-
205
- def __init__(self, x, y, text,
206
- ax=None, renderer=None,
207
- pointer=None,
208
- expand=(1.05, 1.05),
209
- va=None, ha=None, rotation=None,
210
- connectionstyle=None, relpos=None,
211
- linewidth=None,
212
- **kwargs):
206
+ def __init__(
207
+ self,
208
+ x,
209
+ y,
210
+ text,
211
+ ax=None,
212
+ renderer=None,
213
+ pointer=None,
214
+ expand=(1.05, 1.05),
215
+ va=None,
216
+ ha=None,
217
+ rotation=None,
218
+ connectionstyle=None,
219
+ relpos=None,
220
+ linewidth=None,
221
+ **kwargs,
222
+ ):
213
223
  if ax is None:
214
224
  ax = plt.gca()
215
225
  if renderer is None:
@@ -230,14 +240,19 @@ class AdjustableText:
230
240
  self.connectionstyle = connectionstyle
231
241
  self.relpos = relpos
232
242
  self.linewidth = linewidth
233
- self.text_obj = Text(x, y, text, va=va, ha=ha,
234
- rotation=rotation,
235
- transform=self.ax.transAxes,
236
- **kwargs)
243
+ self.text_obj = Text(
244
+ x,
245
+ y,
246
+ text,
247
+ va=va,
248
+ ha=ha,
249
+ rotation=rotation,
250
+ transform=self.ax.transAxes,
251
+ **kwargs,
252
+ )
237
253
  self.text_options = kwargs
238
254
  ax.add_artist(self.text_obj)
239
- self._bbox = self.text_obj.get_window_extent(self.renderer) \
240
- .expanded(*expand)
255
+ self._bbox = self.text_obj.get_window_extent(self.renderer).expanded(*expand)
241
256
  self.annotation = None
242
257
 
243
258
  def get_display_coordinate(self):
@@ -282,8 +297,9 @@ class AdjustableText:
282
297
  arrowstyle="-",
283
298
  connectionstyle=self.connectionstyle,
284
299
  linewidth=self.linewidth,
285
- relpos=self.relpos),
286
- **self.text_options
300
+ relpos=self.relpos,
301
+ ),
302
+ **self.text_options,
287
303
  )
288
304
 
289
305
 
@@ -307,11 +323,7 @@ class TextParams:
307
323
  self._params[k] = v
308
324
 
309
325
  def to_dict(self):
310
- p = dict(
311
- va=self._va,
312
- ha=self._ha,
313
- rotation=self.rotation
314
- )
326
+ p = dict(va=self._va, ha=self._ha, rotation=self.rotation)
315
327
  p.update(self._params)
316
328
  return p
317
329
 
@@ -350,16 +362,15 @@ class _LabelBase(RenderPlan):
350
362
  def get_text_color(bgcolor):
351
363
  """Get text color by background color"""
352
364
  lum = relative_luminance(bgcolor)
353
- return ".15" if lum > .408 else "w"
365
+ return ".15" if lum > 0.408 else "w"
354
366
 
355
367
  def get_expand(self):
356
368
  if self.is_flank:
357
- return 1. + self.text_pad, 1. + self.text_gap
369
+ return 1.0 + self.text_pad, 1.0 + self.text_gap
358
370
  else:
359
- return 1. + self.text_gap, 1. + self.text_pad
360
-
361
- def silent_render(self, figure, expand=(1., 1.)):
371
+ return 1.0 + self.text_gap, 1.0 + self.text_pad
362
372
 
373
+ def silent_render(self, figure, expand=(1.0, 1.0)):
363
374
  renderer = figure.canvas.get_renderer()
364
375
  ax = figure.add_axes([0, 0, 1, 1])
365
376
  params = self.get_text_params()
@@ -398,26 +409,28 @@ class AnnoTextConfig:
398
409
  def get_connectionstyle(self, armA=None, armB=None):
399
410
  armA = self.armA if armA is None else armA
400
411
  armB = self.armB if armB is None else armB
401
- return f"arc,angleA={self.angleA}," \
402
- f"angleB={self.angleB}," \
403
- f"armA={armA}," \
404
- f"armB={armB}," \
405
- f"rad=0"
412
+ return (
413
+ f"arc,angleA={self.angleA},"
414
+ f"angleB={self.angleB},"
415
+ f"armA={armA},"
416
+ f"armB={armB},"
417
+ f"rad=0"
418
+ )
406
419
 
407
420
 
408
421
  anno_default_params = {
409
- "top": AnnoTextConfig(va="bottom", ha="center", rotation=90,
410
- angleA=-90, angleB=90,
411
- relpos=(0.5, 0)),
412
- "bottom": AnnoTextConfig(va="top", ha="center", rotation=-90,
413
- angleA=90, angleB=-90,
414
- relpos=(0.5, 1)),
415
- "right": AnnoTextConfig(va="center", ha="left", rotation=0,
416
- angleA=-180, angleB=0,
417
- relpos=(0, 0.5)),
418
- "left": AnnoTextConfig(va="center", ha="right", rotation=0,
419
- angleA=0, angleB=-180,
420
- relpos=(1, 0.5)),
422
+ "top": AnnoTextConfig(
423
+ va="bottom", ha="center", rotation=90, angleA=-90, angleB=90, relpos=(0.5, 0)
424
+ ),
425
+ "bottom": AnnoTextConfig(
426
+ va="top", ha="center", rotation=-90, angleA=90, angleB=-90, relpos=(0.5, 1)
427
+ ),
428
+ "right": AnnoTextConfig(
429
+ va="center", ha="left", rotation=0, angleA=-180, angleB=0, relpos=(0, 0.5)
430
+ ),
431
+ "left": AnnoTextConfig(
432
+ va="center", ha="right", rotation=0, angleA=0, angleB=-180, relpos=(1, 0.5)
433
+ ),
421
434
  }
422
435
 
423
436
 
@@ -447,6 +460,12 @@ class AnnoLabels(_LabelBase):
447
460
  connectionstyle :
448
461
  relpos : 2-tuple
449
462
  armA, armB : float
463
+ label : str
464
+ The label of the plot
465
+ label_loc : {'top', 'bottom', 'left', 'right'}
466
+ The location of the label
467
+ label_props : dict
468
+ The label properties
450
469
  options :
451
470
  Pass to :class:`matplotlib.text.Text`
452
471
 
@@ -469,22 +488,32 @@ class AnnoLabels(_LabelBase):
469
488
 
470
489
  """
471
490
 
472
- def __init__(self,
473
- labels: Iterable | np.ma.MaskedArray,
474
- mark=None,
475
- text_pad=.5,
476
- text_gap=.5, pointer_size=0.5,
477
- linewidth=None, connectionstyle=None, relpos=None,
478
- armA=None, armB=None,
479
- **options):
480
-
491
+ def __init__(
492
+ self,
493
+ labels: Iterable | np.ma.MaskedArray,
494
+ mark=None,
495
+ text_pad=0.5,
496
+ text_gap=0.5,
497
+ pointer_size=0.5,
498
+ linewidth=None,
499
+ connectionstyle=None,
500
+ relpos=None,
501
+ armA=None,
502
+ armB=None,
503
+ label=None,
504
+ label_loc=None,
505
+ label_props=None,
506
+ **options,
507
+ ):
481
508
  if not np.ma.isMaskedArray(labels):
482
509
  if mark is not None:
483
510
  labels = np.ma.masked_where(~np.in1d(labels, mark), labels)
484
511
  else:
485
- raise TypeError("Must be numpy masked array or "
486
- "use `marks` to mark "
487
- "the labels you want to draw")
512
+ raise TypeError(
513
+ "Must be numpy masked array or "
514
+ "use `marks` to mark "
515
+ "the labels you want to draw"
516
+ )
488
517
  texts = self.data_validator(labels, target="1d")
489
518
  self.set_data(texts)
490
519
  self.texts = texts
@@ -501,15 +530,17 @@ class AnnoLabels(_LabelBase):
501
530
 
502
531
  super().__init__()
503
532
  self._sort_params(**options)
533
+ self.set_label(label, label_loc, label_props)
504
534
 
505
535
  def get_text_params(self):
506
536
  default_params = anno_default_params[self.side]
507
537
  self.relpos = default_params.relpos
508
- self.connectionstyle = (default_params
509
- .get_connectionstyle(armA=self.armA,
510
- armB=self.armB))
511
- params = dict(va=default_params.va, ha=default_params.ha,
512
- rotation=default_params.rotation)
538
+ self.connectionstyle = default_params.get_connectionstyle(
539
+ armA=self.armA, armB=self.armB
540
+ )
541
+ params = dict(
542
+ va=default_params.va, ha=default_params.ha, rotation=default_params.rotation
543
+ )
513
544
  p = TextParams(**params)
514
545
  p.update_params(self._user_params)
515
546
  return p
@@ -531,12 +562,15 @@ class AnnoLabels(_LabelBase):
531
562
  ax_bbox = ax.get_window_extent(renderer)
532
563
  params = self.get_text_params()
533
564
 
534
- text_options = dict(ax=ax, renderer=renderer,
535
- expand=self.get_expand(),
536
- linewidth=self.linewidth,
537
- relpos=self.relpos,
538
- connectionstyle=self.connectionstyle,
539
- **params.to_dict())
565
+ text_options = dict(
566
+ ax=ax,
567
+ renderer=renderer,
568
+ expand=self.get_expand(),
569
+ linewidth=self.linewidth,
570
+ relpos=self.relpos,
571
+ connectionstyle=self.connectionstyle,
572
+ **params.to_dict(),
573
+ )
540
574
 
541
575
  texts = []
542
576
  segments = []
@@ -544,8 +578,7 @@ class AnnoLabels(_LabelBase):
544
578
  y = self.text_anchor
545
579
  for x, s in zip(locs, labels):
546
580
  if not np.ma.is_masked(s):
547
- t = AdjustableText(x=x, y=y, text=s, pointer=(x, 0),
548
- **text_options)
581
+ t = AdjustableText(x=x, y=y, text=s, pointer=(x, 0), **text_options)
549
582
  texts.append(t)
550
583
  segments.append(t.get_segment_x())
551
584
 
@@ -558,8 +591,7 @@ class AnnoLabels(_LabelBase):
558
591
  x = self.text_anchor
559
592
  for y, s in zip(locs, labels):
560
593
  if not np.ma.is_masked(s):
561
- t = AdjustableText(x=x, y=y, text=s, pointer=(0, y),
562
- **text_options)
594
+ t = AdjustableText(x=x, y=y, text=s, pointer=(0, y), **text_options)
563
595
  texts.append(t)
564
596
  segments.append(t.get_segment_y())
565
597
  lim = Segment(ax_bbox.ymin, ax_bbox.ymax)
@@ -576,10 +608,10 @@ class AnnoLabels(_LabelBase):
576
608
 
577
609
 
578
610
  label_default_params = {
579
- 'right': dict(align='left', rotation=0),
580
- 'left': dict(align='right', rotation=0),
581
- 'top': dict(align='bottom', rotation=90),
582
- 'bottom': dict(align='top', rotation=90)
611
+ "right": dict(align="left", rotation=0),
612
+ "left": dict(align="right", rotation=0),
613
+ "top": dict(align="bottom", rotation=90),
614
+ "bottom": dict(align="top", rotation=90),
583
615
  }
584
616
 
585
617
 
@@ -595,6 +627,12 @@ class Labels(_LabelBase):
595
627
  The buffer space between text and the adjcent plots, in points unit
596
628
  text_props : dict
597
629
  A dict of array that control the text properties for each text.
630
+ label : str
631
+ The label of the plot
632
+ label_loc : {'top', 'bottom', 'left', 'right'}
633
+ The location of the label
634
+ label_props : dict
635
+ The label properties
598
636
  options : dict
599
637
  Pass to :class:`matplotlib.text.Text`
600
638
 
@@ -618,10 +656,17 @@ class Labels(_LabelBase):
618
656
 
619
657
  """
620
658
 
621
- def __init__(self, labels, align=None,
622
- padding=2,
623
- text_props=None,
624
- **options):
659
+ def __init__(
660
+ self,
661
+ labels,
662
+ align=None,
663
+ padding=2,
664
+ text_props=None,
665
+ label=None,
666
+ label_loc=None,
667
+ label_props=None,
668
+ **options,
669
+ ):
625
670
  labels = self.data_validator(labels, target="1d")
626
671
  self.set_data(labels)
627
672
  self.texts = labels
@@ -634,6 +679,7 @@ class Labels(_LabelBase):
634
679
  self._sort_params(**options)
635
680
  if text_props is not None:
636
681
  self.set_params(text_props)
682
+ self.set_label(label, label_loc, label_props)
637
683
 
638
684
  def _align_compact(self, align):
639
685
  """Make align keyword compatible to any side"""
@@ -646,14 +692,14 @@ class Labels(_LabelBase):
646
692
  def get_text_params(self) -> TextParams:
647
693
  default_params = label_default_params[self.side]
648
694
  if self.align is None:
649
- self.align = default_params['align']
695
+ self.align = default_params["align"]
650
696
 
651
697
  self.align = self._align_compact(self.align)
652
- va, ha = self.align, 'center'
698
+ va, ha = self.align, "center"
653
699
  if self.is_flank:
654
700
  va, ha = ha, va
655
701
 
656
- p = TextParams(va=va, ha=ha, rotation=default_params['rotation'])
702
+ p = TextParams(va=va, ha=ha, rotation=default_params["rotation"])
657
703
  p.update_params(self._user_params)
658
704
 
659
705
  return p
@@ -676,7 +722,7 @@ class Labels(_LabelBase):
676
722
  if self.is_flank:
677
723
  coords = coords[::-1]
678
724
  if self.align == "center":
679
- const = .5
725
+ const = 0.5
680
726
  elif self.align in ["right", "top"]:
681
727
  const = 1 - offset_ratio / 2
682
728
  else:
@@ -685,8 +731,7 @@ class Labels(_LabelBase):
685
731
  for s, c, p in zip(data, coords, text_props):
686
732
  x, y = (const, c) if self.is_flank else (c, const)
687
733
  options = {**params.to_dict(), **p}
688
- ax.text(x, y, s=s, transform=ax.transAxes,
689
- **options)
734
+ ax.text(x, y, s=s, transform=ax.transAxes, **options)
690
735
  ax.set_axis_off()
691
736
  # from matplotlib.patches import Rectangle
692
737
  # ax.add_artist(Rectangle((0, 0), 1, 1, edgecolor="r",
@@ -742,16 +787,21 @@ class Title(_LabelBase):
742
787
 
743
788
 
744
789
  """
790
+
745
791
  allow_split = False
746
792
 
747
- def __init__(self, title, align="center",
748
- padding=10,
749
- fontsize=None,
750
- fill_color=None,
751
- bordercolor=None,
752
- borderwidth=None,
753
- borderstyle=None,
754
- **options):
793
+ def __init__(
794
+ self,
795
+ title,
796
+ align="center",
797
+ padding=10,
798
+ fontsize=None,
799
+ fill_color=None,
800
+ bordercolor=None,
801
+ borderwidth=None,
802
+ borderstyle=None,
803
+ **options,
804
+ ):
755
805
  self.title = title
756
806
  self.texts = [title]
757
807
  self.align = align
@@ -766,25 +816,18 @@ class Title(_LabelBase):
766
816
  self.bordercolor = bordercolor
767
817
  self.borderwidth = borderwidth
768
818
  self.borderstyle = borderstyle
769
- self._draw_bg = (self.fill_color is not None) \
770
- or (self.bordercolor is not None)
819
+ self._draw_bg = (self.fill_color is not None) or (self.bordercolor is not None)
771
820
 
772
821
  super().__init__()
773
822
  self._sort_params(**options)
774
823
 
775
- align_pos = {
776
- 'right': 1,
777
- 'left': 0,
778
- 'top': 1,
779
- 'bottom': 0,
780
- 'center': 0.5
781
- }
824
+ align_pos = {"right": 1, "left": 0, "top": 1, "bottom": 0, "center": 0.5}
782
825
 
783
826
  default_rotation = {
784
- 'right': -90,
785
- 'left': 90,
786
- 'top': 0,
787
- 'bottom': 0,
827
+ "right": -90,
828
+ "left": 90,
829
+ "top": 0,
830
+ "bottom": 0,
788
831
  }
789
832
 
790
833
  def _align_compact(self, align):
@@ -797,12 +840,11 @@ class Title(_LabelBase):
797
840
 
798
841
  def get_text_params(self) -> TextParams:
799
842
  self.align = self._align_compact(self.align)
800
- va, ha = 'center', self.align
843
+ va, ha = "center", self.align
801
844
  if self.is_flank:
802
845
  va, ha = ha, va
803
846
 
804
- p = TextParams(rotation=self.default_rotation[self.side],
805
- va=va, ha=ha)
847
+ p = TextParams(rotation=self.default_rotation[self.side], va=va, ha=ha)
806
848
  p.update_params(self._user_params)
807
849
  return p
808
850
 
@@ -812,34 +854,44 @@ class Title(_LabelBase):
812
854
 
813
855
  if self._draw_bg:
814
856
  bgcolor = "white" if self.fill_color is None else self.fill_color
815
- ax.add_artist(Rectangle((0, 0), 1, 1,
816
- facecolor=self.fill_color,
817
- edgecolor=self.bordercolor,
818
- linewidth=self.borderwidth,
819
- linestyle=self.borderstyle,
820
- transform=ax.transAxes
821
- ))
822
-
823
- fontdict.setdefault('color', self.get_text_color(bgcolor))
857
+ ax.add_artist(
858
+ Rectangle(
859
+ (0, 0),
860
+ 1,
861
+ 1,
862
+ facecolor=self.fill_color,
863
+ edgecolor=self.bordercolor,
864
+ linewidth=self.borderwidth,
865
+ linestyle=self.borderstyle,
866
+ transform=ax.transAxes,
867
+ )
868
+ )
869
+
870
+ fontdict.setdefault("color", self.get_text_color(bgcolor))
824
871
 
825
872
  const = self.align_pos[self.align]
826
873
 
827
- pos = .5
874
+ pos = 0.5
828
875
  x, y = (const, pos) if self.is_body else (pos, const)
829
- ax.text(x, y, self.title, fontsize=self.fontsize,
830
- transform=ax.transAxes, **fontdict)
876
+ ax.text(
877
+ x, y, self.title, fontsize=self.fontsize, transform=ax.transAxes, **fontdict
878
+ )
831
879
  ax.set_axis_off()
832
880
 
833
881
 
834
882
  class _ChunkBase(_LabelBase):
835
-
836
- def __init__(self, texts,
837
- fill_colors=None,
838
- align=None,
839
- props=None, padding=2, bordercolor=None,
840
- borderwidth=None, borderstyle=None,
841
- **options):
842
-
883
+ def __init__(
884
+ self,
885
+ texts,
886
+ fill_colors=None,
887
+ align=None,
888
+ props=None,
889
+ padding=2,
890
+ bordercolor=None,
891
+ borderwidth=None,
892
+ borderstyle=None,
893
+ **options,
894
+ ):
843
895
  n = len(texts)
844
896
  self.n = n
845
897
  self.texts = texts
@@ -872,27 +924,15 @@ class _ChunkBase(_LabelBase):
872
924
  borderstyle = [borderstyle for _ in range(n)]
873
925
  self.borderstyle = borderstyle
874
926
 
875
- self._draw_bg = (self.fill_colors is not None) \
876
- or (self.bordercolor is not None)
927
+ self._draw_bg = (self.fill_colors is not None) or (self.bordercolor is not None)
877
928
  self.text_pad = 0
878
929
 
879
930
  super().__init__()
880
931
  self._sort_params(**options)
881
932
 
882
- align_pos = {
883
- 'right': 1,
884
- 'left': 0,
885
- 'top': 1,
886
- 'bottom': 0,
887
- 'center': 0.5
888
- }
933
+ align_pos = {"right": 1, "left": 0, "top": 1, "bottom": 0, "center": 0.5}
889
934
 
890
- default_align = {
891
- "right": "left",
892
- "left": "right",
893
- "top": "bottom",
894
- "bottom": "top"
895
- }
935
+ default_align = {"right": "left", "left": "right", "top": "bottom", "bottom": "top"}
896
936
 
897
937
  default_rotation = {
898
938
  "right": -90,
@@ -920,7 +960,7 @@ class _ChunkBase(_LabelBase):
920
960
  self.align = self.default_align[self.side]
921
961
 
922
962
  self.align = self._align_compact(self.align)
923
- va, ha = self.align, 'center'
963
+ va, ha = self.align, "center"
924
964
  if self.is_flank:
925
965
  va, ha = ha, va
926
966
 
@@ -929,9 +969,9 @@ class _ChunkBase(_LabelBase):
929
969
  p.update_params(self._user_params)
930
970
  return p
931
971
 
932
- def _render(self, axes, texts, fill_colors, border_colors,
933
- borderwidth, borderstyle, props):
934
-
972
+ def _render(
973
+ self, axes, texts, fill_colors, border_colors, borderwidth, borderstyle, props
974
+ ):
935
975
  params = self.get_text_params()
936
976
  if self.texts_size is not None:
937
977
  padding_px = self.padding / 72
@@ -940,14 +980,14 @@ class _ChunkBase(_LabelBase):
940
980
  offset_ratio = 0
941
981
 
942
982
  if self.align == "center":
943
- const = .5
983
+ const = 0.5
944
984
  elif self.align in ["right", "top"]:
945
985
  const = 1 - offset_ratio / 2
946
986
  else:
947
987
  const = offset_ratio / 2 # self.text_pad / (1 + self.text_pad) / 2
948
988
 
949
989
  # adjust the text alignment based on the alignment position and rotation
950
- c = .5
990
+ c = 0.5
951
991
  x, y = (const, c) if self.is_flank else (c, const)
952
992
 
953
993
  fill_colors = [] if fill_colors is None else fill_colors
@@ -956,19 +996,27 @@ class _ChunkBase(_LabelBase):
956
996
  borderstyle = [] if borderstyle is None else borderstyle
957
997
  props = [] if props is None else props
958
998
 
959
- specs = zip_longest(axes, texts, fill_colors, border_colors,
960
- borderwidth, borderstyle, props)
999
+ specs = zip_longest(
1000
+ axes, texts, fill_colors, border_colors, borderwidth, borderstyle, props
1001
+ )
961
1002
  for ax, t, bgcolor, bc, lw, ls, prop in specs:
962
1003
  ax.set_axis_off()
963
1004
  fontdict = params.to_dict()
964
1005
  if self._draw_bg:
965
1006
  if bgcolor is None:
966
1007
  bgcolor = "white"
967
- rect = Rectangle((0, 0), 1, 1, facecolor=bgcolor,
968
- edgecolor=bc, linewidth=lw, linestyle=ls,
969
- transform=ax.transAxes)
1008
+ rect = Rectangle(
1009
+ (0, 0),
1010
+ 1,
1011
+ 1,
1012
+ facecolor=bgcolor,
1013
+ edgecolor=bc,
1014
+ linewidth=lw,
1015
+ linestyle=ls,
1016
+ transform=ax.transAxes,
1017
+ )
970
1018
  ax.add_artist(rect)
971
- fontdict.setdefault('color', self.get_text_color(bgcolor))
1019
+ fontdict.setdefault("color", self.get_text_color(bgcolor))
972
1020
 
973
1021
  if prop is not None:
974
1022
  fontdict.update(prop)
@@ -989,7 +1037,7 @@ class Chunk(_ChunkBase):
989
1037
  The label for each chunk
990
1038
  fill_colors : color, array of color
991
1039
  The color used as background color for each chunk
992
- borderwidth, bordercolor, borderstyle :
1040
+ borderwidth, bordercolor, borderstyle :
993
1041
  Control the style of border, you can pass an array to style each group.
994
1042
  For borderstyle, see :meth:`linestyles <matplotlib.lines.Line2D.set_linestyle>`
995
1043
  props : dict or array of dict
@@ -998,6 +1046,12 @@ class Chunk(_ChunkBase):
998
1046
  How many angle to rotate the text coutner-clockwise, in degree unit
999
1047
  padding : float
1000
1048
  The buffer space between text and the adjcent plots, in points unit
1049
+ label : str
1050
+ The label of the plot
1051
+ label_loc : {'right', 'left', 'top', 'bottom'}
1052
+ The location of the label
1053
+ label_props : dict
1054
+ The label properties
1001
1055
 
1002
1056
  See Also
1003
1057
  --------
@@ -1021,33 +1075,46 @@ class Chunk(_ChunkBase):
1021
1075
  >>> h.add_right(Chunk(chunk, bordercolor="gray"), pad=.1)
1022
1076
  >>> h.add_dendrogram("left")
1023
1077
  >>> h.render()
1024
-
1078
+
1025
1079
  """
1026
1080
 
1027
- def __init__(self, texts,
1028
- fill_colors=None,
1029
- *,
1030
- align=None,
1031
- props=None, padding=8, bordercolor=None,
1032
- borderwidth=None, borderstyle=None,
1033
- **options):
1034
-
1035
- super().__init__(texts, fill_colors=fill_colors,
1036
- align=align,
1037
- props=props, padding=padding,
1038
- bordercolor=bordercolor,
1039
- borderwidth=borderwidth,
1040
- borderstyle=borderstyle,
1041
- **options)
1081
+ def __init__(
1082
+ self,
1083
+ texts,
1084
+ fill_colors=None,
1085
+ *,
1086
+ align=None,
1087
+ props=None,
1088
+ padding=8,
1089
+ bordercolor=None,
1090
+ borderwidth=None,
1091
+ borderstyle=None,
1092
+ label=None,
1093
+ label_loc=None,
1094
+ label_props=None,
1095
+ **options,
1096
+ ):
1097
+ super().__init__(
1098
+ texts,
1099
+ fill_colors=fill_colors,
1100
+ align=align,
1101
+ props=props,
1102
+ padding=padding,
1103
+ bordercolor=bordercolor,
1104
+ borderwidth=borderwidth,
1105
+ borderstyle=borderstyle,
1106
+ **options,
1107
+ )
1108
+ self.set_label(label, label_loc, label_props)
1042
1109
 
1043
1110
  def render(self, axes):
1044
-
1045
1111
  if isinstance(axes, Axes):
1046
1112
  axes = [axes]
1047
1113
 
1048
1114
  if len(axes) != self.n:
1049
- raise ValueError(f"You have {len(axes)} axes "
1050
- f"but you only provide {self.n} texts.")
1115
+ raise ValueError(
1116
+ f"You have {len(axes)} axes " f"but you only provide {self.n} texts."
1117
+ )
1051
1118
 
1052
1119
  texts = self.reindex_by_chunk(self.texts)
1053
1120
  fill_colors = self.reindex_by_chunk(self.fill_colors)
@@ -1056,8 +1123,12 @@ class Chunk(_ChunkBase):
1056
1123
  borderstyle = self.reindex_by_chunk(self.borderstyle)
1057
1124
  props = self.reindex_by_chunk(self.props)
1058
1125
 
1059
- self._render(axes, texts, fill_colors, border_colors,
1060
- borderwidth, borderstyle, props)
1126
+ self._render(
1127
+ axes, texts, fill_colors, border_colors, borderwidth, borderstyle, props
1128
+ )
1129
+
1130
+ if self._plan_label is not None:
1131
+ self._plan_label.add(axes, self.side)
1061
1132
 
1062
1133
 
1063
1134
  class FixedChunk(_ChunkBase):
@@ -1080,6 +1151,12 @@ class FixedChunk(_ChunkBase):
1080
1151
  How many to rotate the text
1081
1152
  padding : float
1082
1153
  The buffer space between text and the adjcent plots, in points unit
1154
+ label : str
1155
+ The label of the plot
1156
+ label_loc : {'right', 'left', 'top', 'bottom'}
1157
+ The location of the label
1158
+ label_props : dict
1159
+ The label properties
1083
1160
 
1084
1161
 
1085
1162
  See Also
@@ -1115,31 +1192,63 @@ class FixedChunk(_ChunkBase):
1115
1192
  >>> labels = np.random.choice(chunk, size=20)
1116
1193
  >>> h.hsplit(labels=labels, order=chunk)
1117
1194
  >>> h.add_right(FixedChunk(chunk, bordercolor="gray"), pad=.1)
1118
- >>> h.add_right(FixedChunk(['C1', 'C2', 'C3'],
1119
- ... ratio=[1, 2, 1], fill_colors="red"), pad=.1)
1195
+ >>> h.add_right(FixedChunk(['C1', 'C2', 'C3'], fill_colors="red",
1196
+ ... ratio=[1, 2, 1], ), pad=.1)
1120
1197
  >>> h.render()
1121
1198
 
1122
1199
 
1123
1200
  """
1124
1201
 
1125
- def __init__(self, texts, fill_colors=None, *,
1126
- align=None, ratio=None,
1127
- props=None, padding=8, bordercolor=None,
1128
- borderwidth=None, borderstyle=None,
1129
- **options):
1130
- super().__init__(texts, align, fill_colors, props, padding, bordercolor,
1131
- borderwidth, borderstyle, **options)
1202
+ def __init__(
1203
+ self,
1204
+ texts,
1205
+ fill_colors=None,
1206
+ *,
1207
+ align=None,
1208
+ ratio=None,
1209
+ props=None,
1210
+ padding=8,
1211
+ bordercolor=None,
1212
+ borderwidth=None,
1213
+ borderstyle=None,
1214
+ label=None,
1215
+ label_loc=None,
1216
+ label_props=None,
1217
+ **options,
1218
+ ):
1219
+ super().__init__(
1220
+ texts,
1221
+ fill_colors,
1222
+ align,
1223
+ props,
1224
+ padding,
1225
+ bordercolor,
1226
+ borderwidth,
1227
+ borderstyle,
1228
+ **options,
1229
+ )
1132
1230
  if ratio is not None:
1133
1231
  self.set_split_regroup(ratio)
1232
+ self.set_label(label, label_loc, label_props)
1134
1233
 
1135
1234
  def render(self, axes):
1136
-
1137
1235
  if isinstance(axes, Axes):
1138
1236
  axes = [axes]
1139
1237
 
1140
1238
  if len(axes) != self.n:
1141
- raise ValueError(f"You have {len(axes)} axes "
1142
- f"but you only provide {self.n} texts.")
1239
+ raise ValueError(
1240
+ f"You have {len(axes)} axes " f"but you only provide {self.n} texts."
1241
+ )
1242
+
1243
+ self._render(
1244
+ axes,
1245
+ self.texts,
1246
+ self.fill_colors,
1247
+ self.bordercolor,
1248
+ self.borderwidth,
1249
+ self.borderstyle,
1250
+ self.props,
1251
+ )
1143
1252
 
1144
- self._render(axes, self.texts, self.fill_colors, self.bordercolor,
1145
- self.borderwidth, self.borderstyle, self.props)
1253
+ if self._plan_label is not None:
1254
+ self._plan_label.add(axes, self.side)