maidr 0.13.0__py3-none-any.whl → 0.15.0__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.
maidr/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.13.0"
1
+ __version__ = "0.15.0"
2
2
 
3
3
  from .api import close, render, save_html, set_engine, show, stacked
4
4
  from .core import Maidr
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
- from typing import Any
2
+
3
3
  import threading
4
+ from typing import Any
4
5
 
5
6
  from matplotlib.artist import Artist
6
7
  from matplotlib.axes import Axes
@@ -57,17 +58,16 @@ class FigureManager:
57
58
  raise ValueError(f"No figure found for axis: {ax}.")
58
59
 
59
60
  # Add plot to the Maidr object associated with the plot's figure.
60
- maidr = cls._get_maidr(ax.get_figure())
61
+ maidr = cls._get_maidr(ax.get_figure(), plot_type)
61
62
  plot = MaidrPlotFactory.create(ax, plot_type, **kwargs)
62
63
  maidr.plots.append(plot)
63
-
64
64
  return maidr
65
65
 
66
66
  @classmethod
67
- def _get_maidr(cls, fig: Figure) -> Maidr:
67
+ def _get_maidr(cls, fig: Figure, plot_type: PlotType) -> Maidr:
68
68
  """Retrieve or create a Maidr instance for the given Figure."""
69
69
  if fig not in cls.figs.keys():
70
- cls.figs[fig] = Maidr(fig)
70
+ cls.figs[fig] = Maidr(fig, plot_type)
71
71
  return cls.figs[fig]
72
72
 
73
73
  @classmethod
maidr/core/maidr.py CHANGED
@@ -14,6 +14,7 @@ from matplotlib.figure import Figure
14
14
 
15
15
  from maidr.core.context_manager import HighlightContextManager
16
16
  from maidr.core.enum.maidr_key import MaidrKey
17
+ from maidr.core.enum.plot_type import PlotType
17
18
  from maidr.core.plot import MaidrPlot
18
19
  from maidr.util.environment import Environment
19
20
 
@@ -39,11 +40,13 @@ class Maidr:
39
40
  Displays the rendered HTML content in the specified rendering context.
40
41
  """
41
42
 
42
- def __init__(self, fig: Figure) -> None:
43
+ def __init__(self, fig: Figure, plot_type: PlotType = PlotType.LINE) -> None:
43
44
  """Create a new Maidr for the given ``matplotlib.figure.Figure``."""
44
45
  self._fig = fig
45
46
  self._plots = []
46
47
  self.maidr_id = None
48
+ self.selector_id = Maidr._unique_id()
49
+ self.plot_type = plot_type
47
50
 
48
51
  @property
49
52
  def fig(self) -> Figure:
@@ -125,7 +128,7 @@ class Maidr:
125
128
  maidr = f"\nlet maidr = {json.dumps(self._flatten_maidr(), indent=2)}\n"
126
129
 
127
130
  # In SVG we will replace maidr=id with the unique id.
128
- svg = svg.replace('maidr="true"', f'maidr="{self.maidr_id}"')
131
+ svg = svg.replace('maidr="true"', f'maidr="{self.selector_id}"')
129
132
 
130
133
  # Inject plot's svg and MAIDR structure into html tag.
131
134
  return Maidr._inject_plot(svg, maidr, self.maidr_id)
@@ -136,13 +139,15 @@ class Maidr:
136
139
 
137
140
  def _flatten_maidr(self) -> dict | list[dict]:
138
141
  """Return a single plot schema or a list of schemas from the Maidr instance."""
142
+ if self.plot_type == PlotType.LINE:
143
+ self._plots = [self._plots[0]]
139
144
  maidr = [plot.schema for plot in self._plots]
140
145
 
141
146
  # Replace the selector having maidr='true' with maidr={self.maidr_id}
142
147
  for plot in maidr:
143
148
  if MaidrKey.SELECTOR in plot:
144
149
  plot[MaidrKey.SELECTOR] = plot[MaidrKey.SELECTOR].replace(
145
- "maidr='true'", f"maidr='{self.maidr_id}'"
150
+ "maidr='true'", f"maidr='{self.selector_id}'"
146
151
  )
147
152
 
148
153
  return maidr if len(maidr) != 1 else maidr[0]
@@ -41,7 +41,7 @@ class BarPlot(MaidrPlot, ContainerExtractorMixin, LevelExtractorMixin, DictMerge
41
41
  combined_data = (
42
42
  zip(levels, data) if plot[0].orientation == "vertical" else zip(levels, data) # type: ignore
43
43
  )
44
- if len(data) != len(plot): # type: ignore
44
+ if len(data) == len(plot): # type: ignore
45
45
  for x, y in combined_data: # type: ignore
46
46
  formatted_data.append({"x": x, "y": y})
47
47
  return formatted_data
@@ -16,7 +16,7 @@ from maidr.util.mixin import (
16
16
  class GroupedBarPlot(
17
17
  MaidrPlot, ContainerExtractorMixin, LevelExtractorMixin, DictMergerMixin
18
18
  ):
19
- def __init__(self, ax: Axes, plot_type: PlotType) -> None:
19
+ def __init__(self, ax: Axes, plot_type: PlotType, **kwargs) -> None:
20
20
  super().__init__(ax, plot_type)
21
21
  self._support_highlighting = False
22
22
 
@@ -1,41 +1,102 @@
1
- from __future__ import annotations
2
-
3
1
  from matplotlib.axes import Axes
4
2
  from matplotlib.lines import Line2D
5
3
 
6
- from maidr.core.enum import MaidrKey, PlotType
7
- from maidr.core.plot import MaidrPlot
8
- from maidr.exception import ExtractionError
9
- from maidr.util.mixin import LineExtractorMixin
4
+ from maidr.core.enum.maidr_key import MaidrKey
5
+ from maidr.core.enum.plot_type import PlotType
6
+ from maidr.core.plot.maidr_plot import MaidrPlot
7
+ from maidr.exception.extraction_error import ExtractionError
8
+ from maidr.util.environment import Environment
9
+ from maidr.util.mixin.extractor_mixin import LineExtractorMixin
10
+
11
+
12
+ class MultiLinePlot(MaidrPlot, LineExtractorMixin):
13
+ """
14
+ A class for extracting and processing data from line plots.
15
+
16
+ This class can handle both single-line and multi-line plots, extracting
17
+ coordinate data and line identifiers. It processes matplotlib Line2D objects
18
+ and converts them into structured data for further processing or visualization.
19
+
20
+ Parameters
21
+ ----------
22
+ ax : Axes
23
+ The matplotlib axes object containing the line plot(s).
24
+ **kwargs : dict
25
+ Additional keyword arguments to pass to the parent class.
26
+
27
+ Attributes
28
+ ----------
29
+ type : PlotType
30
+ Set to PlotType.LINE to identify this as a line plot.
10
31
 
32
+ Notes
33
+ -----
34
+ - When using the JavaScript engine, only single-line plots are supported.
35
+ - For multi-line plots, use the TypeScript engine.
36
+ - The extracted data structure includes x, y coordinates and line identifiers
37
+ (fill values) for each point.
38
+ """
11
39
 
12
- class LinePlot(MaidrPlot, LineExtractorMixin):
13
- def __init__(self, ax: Axes) -> None:
40
+ def __init__(self, ax: Axes, **kwargs):
14
41
  super().__init__(ax, PlotType.LINE)
15
42
 
16
43
  def _get_selector(self) -> str:
17
44
  return "g[maidr='true'] > path"
18
45
 
19
46
  def _extract_plot_data(self) -> list[dict]:
20
- plot = self.extract_line(self.ax)
47
+ plot = self.extract_lines(self.ax)
21
48
  data = self._extract_line_data(plot)
49
+ engine = Environment.get_engine()
50
+ if engine == "js":
51
+ if len(data) > 1:
52
+ raise Exception(
53
+ "MultiLine Plot not supported in JS. Use TypeScript Engine for this plot!"
54
+ )
55
+ data = data[0]
22
56
 
23
57
  if data is None:
24
58
  raise ExtractionError(self.type, plot)
25
59
 
26
60
  return data
27
61
 
28
- def _extract_line_data(self, plot: Line2D | None) -> list[dict] | None:
29
- if plot is None or plot.get_xydata() is None:
62
+ def _extract_line_data(self, plot: list[Line2D] | None) -> list[dict] | None:
63
+ """
64
+ Extract data from multiple line objects.
65
+
66
+ Parameters
67
+ ----------
68
+ plot : list[Line2D] | None
69
+ List of Line2D objects to extract data from.
70
+
71
+ Returns
72
+ -------
73
+ list[dict] | None
74
+ List of dictionaries containing x,y coordinates and line identifiers,
75
+ or None if the plot data is invalid.
76
+ """
77
+ if plot is None or len(plot) == 0:
30
78
  return None
31
79
 
32
- # Tag the elements for highlighting.
33
- self._elements.append(plot)
80
+ all_line_data = []
81
+
82
+ # Process each line in the plot
83
+ for i, line in enumerate(plot):
84
+ if line.get_xydata() is None:
85
+ continue
86
+
87
+ # Tag the element for highlighting
88
+ self._elements.append(line)
89
+
90
+ # Extract data from this line
91
+ line_data = [
92
+ {
93
+ MaidrKey.X: float(x),
94
+ MaidrKey.Y: float(y),
95
+ MaidrKey.FILL: line.get_label(),
96
+ }
97
+ for x, y in line.get_xydata() # type: ignore
98
+ ]
99
+
100
+ all_line_data.append(line_data)
34
101
 
35
- return [
36
- {
37
- MaidrKey.X: float(x),
38
- MaidrKey.Y: float(y),
39
- }
40
- for x, y in plot.get_xydata()
41
- ]
102
+ return all_line_data if all_line_data else None
@@ -8,7 +8,7 @@ from maidr.core.plot.boxplot import BoxPlot
8
8
  from maidr.core.plot.grouped_barplot import GroupedBarPlot
9
9
  from maidr.core.plot.heatmap import HeatPlot
10
10
  from maidr.core.plot.histogram import HistPlot
11
- from maidr.core.plot.lineplot import LinePlot
11
+ from maidr.core.plot.lineplot import MultiLinePlot
12
12
  from maidr.core.plot.maidr_plot import MaidrPlot
13
13
  from maidr.core.plot.scatterplot import ScatterPlot
14
14
 
@@ -39,7 +39,7 @@ class MaidrPlotFactory:
39
39
  elif PlotType.HIST == plot_type:
40
40
  return HistPlot(ax)
41
41
  elif PlotType.LINE == plot_type:
42
- return LinePlot(ax)
42
+ return MultiLinePlot(ax)
43
43
  elif PlotType.SCATTER == plot_type:
44
44
  return ScatterPlot(ax)
45
45
  elif PlotType.DODGED == plot_type or PlotType.STACKED == plot_type:
maidr/patch/lineplot.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import wrapt
4
-
5
4
  from matplotlib.axes import Axes
6
5
  from matplotlib.lines import Line2D
7
6
 
@@ -10,6 +9,25 @@ from maidr.patch.common import common
10
9
 
11
10
 
12
11
  def line(wrapped, instance, args, kwargs) -> Axes | list[Line2D]:
12
+ """
13
+ Wrapper function for line plotting functions in matplotlib and seaborn.
14
+
15
+ Parameters
16
+ ----------
17
+ wrapped : callable
18
+ The wrapped function (plot or lineplot)
19
+ instance : object
20
+ The object to which the wrapped function belongs
21
+ args : tuple
22
+ Positional arguments passed to the wrapped function
23
+ kwargs : dict
24
+ Keyword arguments passed to the wrapped function
25
+
26
+ Returns
27
+ -------
28
+ Axes | list[Line2D]
29
+ The result of the wrapped function after processing
30
+ """
13
31
  return common(PlotType.LINE, wrapped, instance, args, kwargs)
14
32
 
15
33
 
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import Any
3
4
 
4
5
  from matplotlib.axes import Axes
@@ -64,6 +65,16 @@ class LineExtractorMixin:
64
65
  # `maidr` package supports the same.
65
66
  return ax.get_lines()[-1]
66
67
 
68
+ @staticmethod
69
+ def extract_lines(ax: Axes) -> list[Line2D] | None:
70
+ """Retrieve all line objects from Axes, if available."""
71
+ if ax is None or ax.get_lines() is None:
72
+ return None
73
+
74
+ # Since the upstream MaidrJS library currently supports only the last plot line,
75
+ # `maidr` package supports the same.
76
+ return ax.get_lines()
77
+
67
78
 
68
79
  class CollectionExtractorMixin:
69
80
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: maidr
3
- Version: 0.13.0
3
+ Version: 0.15.0
4
4
  Summary: Multimodal Access and Interactive Data Representations
5
5
  License: GPL-3.0-or-later
6
6
  Keywords: accessibility,visualization,sonification,braille,tactile,multimodal,data representation,blind,low vision,visual impairments
@@ -1,4 +1,4 @@
1
- maidr/__init__.py,sha256=l8GxTHYGbPGuc6jgS4bbzP2Caqb90CQYYYwW7LWmbbo,369
1
+ maidr/__init__.py,sha256=5JZ0w4wAfRluMtHwkTW2AMeiJnSuV2DycEcNdpxxHQI,369
2
2
  maidr/api.py,sha256=u2WOQ8RWmXpzseEgbZJCB7FFZs7cEy-ya-Q18tGAYB4,1350
3
3
  maidr/core/__init__.py,sha256=WgxLpSEYMc4k3OyEOf1shOxfEq0ASzppEIZYmE91ThQ,25
4
4
  maidr/core/context_manager.py,sha256=HSzD4wpiALdI4Vm0QTdWqi1MWR5Uqy8BX_lOlCM3JYY,3463
@@ -6,17 +6,17 @@ maidr/core/enum/__init__.py,sha256=9ee78L0dlxEx4ulUGVlD-J23UcUZmrGu0rXms54up3c,9
6
6
  maidr/core/enum/library.py,sha256=e8ujT_L-McJWfoVJd1ty9K_2bwITnf1j0GPLsnAcHes,104
7
7
  maidr/core/enum/maidr_key.py,sha256=LNtt74d2gurefDNPD3SLLsTxKqO9qTyo3PBAUAyofnc,736
8
8
  maidr/core/enum/plot_type.py,sha256=pyfyIwlq3taqe2Z1sVUSbMsTp5QTvYBlZXsMMMclEVM,293
9
- maidr/core/figure_manager.py,sha256=HyYLIX3QzE-PmatSsTez9MmncTEu0wzPdFktZzE3NQM,3891
10
- maidr/core/maidr.py,sha256=2tOBxsyG9a_xIq7cg6P1U665WSC3_NkzKxuz1sS3KxU,11459
9
+ maidr/core/figure_manager.py,sha256=rd8XEwqNmfKu1I9zGD1p9g3wVlkMYNCrdLLCg4IzlfQ,3934
10
+ maidr/core/maidr.py,sha256=G7ACqroX80F9z4-tJj6-YFcjvv9--A-qrmH1Syp4jgQ,11717
11
11
  maidr/core/plot/__init__.py,sha256=xDIpRGM-4DfaSSL3nKcXrjdMecCHJ6en4K4nA_fPefQ,83
12
- maidr/core/plot/barplot.py,sha256=iKob48QXYjezxw5Pqw8C_W8KZvQB2fioEGWCtOeQ3JM,2605
12
+ maidr/core/plot/barplot.py,sha256=DGiMbakzER-Czpzekw-yBHXZuzWpBg7ODtJPQ8voI04,2605
13
13
  maidr/core/plot/boxplot.py,sha256=roADG8xJYRQlZPYbfGBQSQRw8974lhVnngGKEKXJttk,6090
14
- maidr/core/plot/grouped_barplot.py,sha256=pRmSAr6obcdjK4PPOt7-U77tMMKLM4lkKOiOklS8TbA,2061
14
+ maidr/core/plot/grouped_barplot.py,sha256=tAiOWwtfsWDwyGdUuCdQ6-f8UQxnsDDwpN30Skk8p-U,2071
15
15
  maidr/core/plot/heatmap.py,sha256=_Hn_wXsu-BQBYs4YO440-WRhmXAJ1WeBndBGVEUAP5M,2447
16
16
  maidr/core/plot/histogram.py,sha256=QV5W-6ZJQQcZsrM91JJBX-ONktJzH7yg_et5_bBPfQQ,1525
17
- maidr/core/plot/lineplot.py,sha256=iHPJgQ7CByM7UItBMDUy7YnkVV6tBRyW_vY4QoWib34,1140
17
+ maidr/core/plot/lineplot.py,sha256=5FKgT4s6l9Y7Lb7ELJ4SqMo0ItqimEhbKMgoVG_Rmu8,3242
18
18
  maidr/core/plot/maidr_plot.py,sha256=_9Ugn3cUwi-URxeTRbDAKDZwASrB65p9TKOXhFX1zbU,3176
19
- maidr/core/plot/maidr_plot_factory.py,sha256=Ai-vFpn9YhZPQeVQxdjq0prVJNcGZ8NJvS3eKAxxZ_w,1681
19
+ maidr/core/plot/maidr_plot_factory.py,sha256=QGcHK_oquY_M2_KJVEtc5-rBAD-gm7KI1kSD7cduHzg,1691
20
20
  maidr/core/plot/scatterplot.py,sha256=sQhT80w0Sp9AsUAZi4v6lfWsHJAvuFcer2aAE2NpAYE,1240
21
21
  maidr/exception/__init__.py,sha256=PzaXoYBhyZxMDcJkuxJugDx7jZeseI0El6LpxIwXyG4,46
22
22
  maidr/exception/extraction_error.py,sha256=rd37Oxa9gn2OWFWt9AOH5fv0hNd3sAWGvpDMFBuJY2I,607
@@ -28,16 +28,16 @@ maidr/patch/common.py,sha256=bRe4jruUvCIwp1t1E8Q3OuQ0eEyHgpMjXVpsPBZj4C8,843
28
28
  maidr/patch/heatmap.py,sha256=uxLLg530Ql9KVC5rxk8vnwPHXBWWHwYgJRkyHY-tJzs,1048
29
29
  maidr/patch/highlight.py,sha256=2DOO1tLDpXDtGd8DAPpnDOtq2_rWViPS7IDn9TbBXyA,925
30
30
  maidr/patch/histogram.py,sha256=tnyKkTMuzDXdyQBywhpHZgH3i2mXzOCSyfUSRM0tfUg,1315
31
- maidr/patch/lineplot.py,sha256=AIVsEPXMlkyUfYJ2xcU0GtZLpykEhHDLVFZjYnWCq9U,492
31
+ maidr/patch/lineplot.py,sha256=_EyqOp8BcO-kGq9sn8PyNQ8-Bz0FsnHAufXRkTGQzBw,1025
32
32
  maidr/patch/scatterplot.py,sha256=JZJXy3vWbJgzZh2zysNrqSA7MRcfSCms6lr_MY5CMD0,526
33
33
  maidr/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  maidr/util/environment.py,sha256=xtfibTH4RXW2GC7R_9OQo119BTe5M3bx0u13eFZaiZs,4554
35
35
  maidr/util/mixin/__init__.py,sha256=aGJZNhtWh77yIVPc7ipIZm1OajigjMtCWYKPuDWTC-c,217
36
- maidr/util/mixin/extractor_mixin.py,sha256=Y-34424UR8ECHECAuQZ7zKhyk5QC6GOiUeCMRhI06QQ,3132
36
+ maidr/util/mixin/extractor_mixin.py,sha256=lOVDaNsHnOS2aYxiGXAigGjt8WXzcer9MocaipcOh60,3511
37
37
  maidr/util/mixin/merger_mixin.py,sha256=V0qLw_6DUB7X6CQ3BCMpsCQX_ZuwAhoSTm_E4xAJFKM,712
38
38
  maidr/widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  maidr/widget/shiny.py,sha256=wrrw2KAIpE_A6CNQGBtNHauR1DjenA_n47qlFXX9_rk,745
40
- maidr-0.13.0.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
41
- maidr-0.13.0.dist-info/METADATA,sha256=WNQKQgPsz3s7J8J5-MK9AsZWJcu6zTfybnoDXRoKJWQ,2688
42
- maidr-0.13.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
43
- maidr-0.13.0.dist-info/RECORD,,
40
+ maidr-0.15.0.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
41
+ maidr-0.15.0.dist-info/METADATA,sha256=_jG9FuP7lS5XtMdPzKjQbJUmtaMFdNb0r-GmDrP9Fps,2688
42
+ maidr-0.15.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
43
+ maidr-0.15.0.dist-info/RECORD,,
File without changes