maidr 0.16.1__py3-none-any.whl → 0.18.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.16.1"
1
+ __version__ = "0.18.0"
2
2
 
3
3
  from .api import close, render, save_html, set_engine, show, stacked
4
4
  from .core import Maidr
maidr/api.py CHANGED
@@ -20,16 +20,31 @@ def render(plot: Any) -> Tag:
20
20
 
21
21
  def show(plot: Any, renderer: Literal["auto", "ipython", "browser"] = "auto") -> object:
22
22
  ax = FigureManager.get_axes(plot)
23
- maidr = FigureManager.get_maidr(ax.get_figure())
24
- return maidr.show(renderer)
23
+ htmls = []
24
+ if isinstance(ax, list):
25
+ for axes in ax:
26
+ maidr = FigureManager.get_maidr(axes.get_figure())
27
+ htmls.append(maidr.render())
28
+ return htmls[-1].show(renderer)
29
+ else:
30
+ maidr = FigureManager.get_maidr(ax.get_figure())
31
+ return maidr.show(renderer)
25
32
 
26
33
 
27
34
  def save_html(
28
35
  plot: Any, file: str, *, lib_dir: str | None = "lib", include_version: bool = True
29
36
  ) -> str:
30
37
  ax = FigureManager.get_axes(plot)
31
- maidr = FigureManager.get_maidr(ax.get_figure())
32
- return maidr.save_html(file, lib_dir=lib_dir, include_version=include_version)
38
+ htmls = []
39
+ if isinstance(ax, list):
40
+ for axes in ax:
41
+ maidr = FigureManager.get_maidr(axes.get_figure())
42
+ htmls.append(maidr.render())
43
+ htmls[-1].save_html(file, libdir=lib_dir, include_version=include_version)
44
+ return htmls[-1]
45
+ else:
46
+ maidr = FigureManager.get_maidr(ax.get_figure())
47
+ return maidr.save_html(file, lib_dir=lib_dir, include_version=include_version)
33
48
 
34
49
 
35
50
  def stacked(plot: Axes | BarContainer) -> Maidr:
maidr/core/maidr.py CHANGED
@@ -6,7 +6,7 @@ import os
6
6
  import tempfile
7
7
  import uuid
8
8
  import webbrowser
9
- from typing import Literal
9
+ from typing import Any, Literal
10
10
 
11
11
  from htmltools import HTML, HTMLDocument, Tag, tags
12
12
  from lxml import etree
@@ -139,18 +139,66 @@ class Maidr:
139
139
 
140
140
  def _flatten_maidr(self) -> dict | list[dict]:
141
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]]
144
- maidr = [plot.schema for plot in self._plots]
145
-
146
- # Replace the selector having maidr='true' with maidr={self.maidr_id}
147
- for plot in maidr:
148
- if MaidrKey.SELECTOR in plot:
149
- plot[MaidrKey.SELECTOR] = plot[MaidrKey.SELECTOR].replace(
142
+ # To support legacy JS Engine we will just return the format in this way
143
+ # but soon enough this should be deprecated and when we will completely
144
+ # transition to TypeScript :)
145
+ engine = Environment.get_engine()
146
+ if engine == "js":
147
+ if self.plot_type in (PlotType.LINE, PlotType.DODGED, PlotType.STACKED):
148
+ self._plots = [self._plots[0]]
149
+ maidr = [plot.schema for plot in self._plots]
150
+ for plot in maidr:
151
+ if MaidrKey.SELECTOR in plot:
152
+ plot[MaidrKey.SELECTOR] = plot[MaidrKey.SELECTOR].replace(
153
+ "maidr='true'", f"maidr='{self.selector_id}'"
154
+ )
155
+ return maidr if len(maidr) != 1 else maidr[0]
156
+
157
+ # Now let's start building the maidr object for the newer TypeScript engine
158
+
159
+ plot_schemas = []
160
+
161
+ for plot in self._plots:
162
+ schema = plot.schema
163
+ if MaidrKey.SELECTOR in schema:
164
+ schema[MaidrKey.SELECTOR] = schema[MaidrKey.SELECTOR].replace(
150
165
  "maidr='true'", f"maidr='{self.selector_id}'"
151
166
  )
167
+ plot_schemas.append(
168
+ {
169
+ "schema": schema,
170
+ "row": getattr(plot, "row_index", 0),
171
+ "col": getattr(plot, "col_index", 0),
172
+ }
173
+ )
174
+
175
+ max_row = max([plot.get("row", 0) for plot in plot_schemas], default=0)
176
+ max_col = max([plot.get("col", 0) for plot in plot_schemas], default=0)
177
+
178
+ subplot_grid: list[list[dict[str, str | list[Any]]]] = [
179
+ [{} for _ in range(max_col + 1)] for _ in range(max_row + 1)
180
+ ]
181
+
182
+ position_groups = {}
183
+ for plot in plot_schemas:
184
+ pos = (plot.get("row", 0), plot.get("col", 0))
185
+ if pos not in position_groups:
186
+ position_groups[pos] = []
187
+ position_groups[pos].append(plot["schema"])
188
+
189
+ for (row, col), layers in position_groups.items():
190
+ if subplot_grid[row][col]:
191
+ subplot_grid[row][col]["layers"].append(layers)
192
+ else:
193
+ subplot_grid[row][col] = {"id": Maidr._unique_id(), "layers": layers}
194
+
195
+ for i in range(len(subplot_grid)):
196
+ subplot_grid[i] = [
197
+ cell if cell is not None else {"id": Maidr._unique_id(), "layers": []}
198
+ for cell in subplot_grid[i]
199
+ ]
152
200
 
153
- return maidr if len(maidr) != 1 else maidr[0]
201
+ return {"id": Maidr._unique_id(), "subplots": subplot_grid}
154
202
 
155
203
  def _get_svg(self) -> HTML:
156
204
  """Extract the chart SVG from ``matplotlib.figure.Figure``."""
@@ -200,6 +248,7 @@ class Maidr:
200
248
 
201
249
  engine = Environment.get_engine()
202
250
 
251
+ # MAIDR_TS_CDN_URL = "http://localhost:8080/maidr.js" # DEMO URL
203
252
  MAIDR_TS_CDN_URL = "https://cdn.jsdelivr.net/npm/maidr-ts/dist/maidr.js"
204
253
 
205
254
  maidr_js_script = f"""
@@ -38,10 +38,10 @@ class BarPlot(MaidrPlot, ContainerExtractorMixin, LevelExtractorMixin, DictMerge
38
38
  levels = self.extract_level(self.ax)
39
39
  if engine == "ts":
40
40
  formatted_data = []
41
- combined_data = (
42
- zip(levels, data) if plot[0].orientation == "vertical" else zip(levels, data) # type: ignore
41
+ combined_data = list(
42
+ zip(levels, data) if plot[0].orientation == "vertical" else zip(data, levels) # type: ignore
43
43
  )
44
- if len(data) == len(plot): # type: ignore
44
+ if combined_data: # 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
@@ -41,6 +41,9 @@ class MaidrPlot(ABC):
41
41
  self.ax = ax
42
42
  self._support_highlighting = True
43
43
  self._elements = []
44
+ ss = self.ax.get_subplotspec()
45
+ self.row_index = ss.rowspan.start
46
+ self.col_index = ss.colspan.start
44
47
 
45
48
  # MAIDR data
46
49
  self.type = plot_type
maidr/patch/barplot.py CHANGED
@@ -52,16 +52,18 @@ def bar(
52
52
  bottom = kwargs.get("bottom")
53
53
  if bottom is not None:
54
54
  plot_type = PlotType.STACKED
55
- elif args:
56
- x = args[0]
57
- is_numeric = False
58
- if isinstance(x, np.ndarray) and np.issubdtype(x.dtype, np.number):
59
- is_numeric = True
60
- elif isinstance(x, (list, tuple)) and x and isinstance(x[0], Number):
61
- is_numeric = True
62
- if is_numeric:
63
- plot_type = PlotType.DODGED
55
+ else:
56
+ if len(args) >= 3:
57
+ real_width = args[2]
58
+ else:
59
+ real_width = kwargs.get("width", 0.8)
60
+
61
+ align = kwargs.get("align", "center")
64
62
 
63
+ if (isinstance(real_width, (int, float)) and float(real_width) < 0.8) or (
64
+ align == "edge"
65
+ ):
66
+ plot_type = PlotType.DODGED
65
67
  return common(plot_type, wrapped, instance, args, kwargs)
66
68
 
67
69
 
@@ -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.collections import PathCollection
7
6
 
@@ -45,9 +45,36 @@ class LevelExtractorMixin:
45
45
 
46
46
  level = None
47
47
  if MaidrKey.X == key:
48
- level = [label.get_text() for label in ax.get_xticklabels()]
48
+ ticks = ax.get_xticks()
49
+ labels = [label.get_text() for label in ax.get_xticklabels()]
50
+
51
+ if hasattr(ax, "dataLim") and ax.dataLim.width != 0:
52
+ # Use the actual data limits rather than padded view limits
53
+ data_x_min, data_x_max = ax.dataLim.x0, ax.dataLim.x0 + ax.dataLim.width
54
+ # Filter tick labels to only those within the actual data range
55
+ valid_indices = [
56
+ i for i, pos in enumerate(ticks) if data_x_min <= pos <= data_x_max
57
+ ]
58
+ labels = [labels[i] for i in valid_indices if i < len(labels)]
59
+
60
+ level = labels
49
61
  elif MaidrKey.Y == key:
50
- level = [label.get_text() for label in ax.get_yticklabels()]
62
+ ticks = ax.get_yticks()
63
+ labels = [label.get_text() for label in ax.get_yticklabels()]
64
+
65
+ if hasattr(ax, "dataLim") and ax.dataLim.height != 0:
66
+ # Use the actual data limits rather than padded view limits
67
+ data_y_min, data_y_max = (
68
+ ax.dataLim.y0,
69
+ ax.dataLim.y0 + ax.dataLim.height,
70
+ )
71
+ # Filter tick labels to only those within the actual data range
72
+ valid_indices = [
73
+ i for i, pos in enumerate(ticks) if data_y_min <= pos <= data_y_max
74
+ ]
75
+ labels = [labels[i] for i in valid_indices if i < len(labels)]
76
+
77
+ level = labels
51
78
  elif MaidrKey.FILL == key:
52
79
  level = [container.get_label() for container in ax.containers]
53
80
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: maidr
3
- Version: 0.16.1
3
+ Version: 0.18.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,5 +1,5 @@
1
- maidr/__init__.py,sha256=YLVnqJKygx0YZzQQ51K7_BCx-FX9rfpLo-HhoF0ufZI,369
2
- maidr/api.py,sha256=u2WOQ8RWmXpzseEgbZJCB7FFZs7cEy-ya-Q18tGAYB4,1350
1
+ maidr/__init__.py,sha256=z80HU9gNyrbgzcxnHy9oGr3zFVcNjgigc2Xo8Rw5huY,369
2
+ maidr/api.py,sha256=m4fkbW2gV9yhHq0uMi5_DW35VWh5v0wFGqT00ceR5Wc,1878
3
3
  maidr/core/__init__.py,sha256=WgxLpSEYMc4k3OyEOf1shOxfEq0ASzppEIZYmE91ThQ,25
4
4
  maidr/core/context_manager.py,sha256=HSzD4wpiALdI4Vm0QTdWqi1MWR5Uqy8BX_lOlCM3JYY,3463
5
5
  maidr/core/enum/__init__.py,sha256=9ee78L0dlxEx4ulUGVlD-J23UcUZmrGu0rXms54up3c,93
@@ -7,21 +7,21 @@ maidr/core/enum/library.py,sha256=e8ujT_L-McJWfoVJd1ty9K_2bwITnf1j0GPLsnAcHes,10
7
7
  maidr/core/enum/maidr_key.py,sha256=LNtt74d2gurefDNPD3SLLsTxKqO9qTyo3PBAUAyofnc,736
8
8
  maidr/core/enum/plot_type.py,sha256=pyfyIwlq3taqe2Z1sVUSbMsTp5QTvYBlZXsMMMclEVM,293
9
9
  maidr/core/figure_manager.py,sha256=rd8XEwqNmfKu1I9zGD1p9g3wVlkMYNCrdLLCg4IzlfQ,3934
10
- maidr/core/maidr.py,sha256=G7ACqroX80F9z4-tJj6-YFcjvv9--A-qrmH1Syp4jgQ,11717
10
+ maidr/core/maidr.py,sha256=iEi59R4oLuSIOocEhWzdQCRE3FvvPmzE2LiR5mmZjWk,13811
11
11
  maidr/core/plot/__init__.py,sha256=xDIpRGM-4DfaSSL3nKcXrjdMecCHJ6en4K4nA_fPefQ,83
12
- maidr/core/plot/barplot.py,sha256=DGiMbakzER-Czpzekw-yBHXZuzWpBg7ODtJPQ8voI04,2605
12
+ maidr/core/plot/barplot.py,sha256=b7r0cz3yMZzWwiTJVUy1yWBXhv_oN9rwEb6hfKVg3Zs,2600
13
13
  maidr/core/plot/boxplot.py,sha256=roADG8xJYRQlZPYbfGBQSQRw8974lhVnngGKEKXJttk,6090
14
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
17
  maidr/core/plot/lineplot.py,sha256=OQQtJb7lmO-lZTQfhqMV4sfztyuEicZfe95y9nIhfTo,3280
18
- maidr/core/plot/maidr_plot.py,sha256=_9Ugn3cUwi-URxeTRbDAKDZwASrB65p9TKOXhFX1zbU,3176
18
+ maidr/core/plot/maidr_plot.py,sha256=9z8I_W4o_NWvz-S5QFp1kB0L23AHvt0PDxRhxgeJcmY,3299
19
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
23
23
  maidr/patch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- maidr/patch/barplot.py,sha256=X2vsC_OMGk5ubMc29YZ1il5D5loYtJ5scjCy5gk098g,2412
24
+ maidr/patch/barplot.py,sha256=iCoYaqlHou6_rSnbjfZwpRU8ENnYcfQ9JCdUxhrSgCA,2416
25
25
  maidr/patch/boxplot.py,sha256=Mcz94pSf7PT3SyRsI8TDrIKVdEmiiUQouXvd05mtnfw,2846
26
26
  maidr/patch/clear.py,sha256=2Sc4CIt5jRGkew3TxFsBZm-uowC9yDSxtraEcXZjmGw,396
27
27
  maidr/patch/common.py,sha256=bRe4jruUvCIwp1t1E8Q3OuQ0eEyHgpMjXVpsPBZj4C8,843
@@ -29,15 +29,15 @@ 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
31
  maidr/patch/lineplot.py,sha256=_EyqOp8BcO-kGq9sn8PyNQ8-Bz0FsnHAufXRkTGQzBw,1025
32
- maidr/patch/scatterplot.py,sha256=JZJXy3vWbJgzZh2zysNrqSA7MRcfSCms6lr_MY5CMD0,526
32
+ maidr/patch/scatterplot.py,sha256=kln6zZwjVsdQzICalo-RnBOJrx1BnIB2xYUwItHvSNY,525
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=lOVDaNsHnOS2aYxiGXAigGjt8WXzcer9MocaipcOh60,3511
36
+ maidr/util/mixin/extractor_mixin.py,sha256=AjpgCo_dgASdTwFumfgDOoVCVioxDDoqFWphaBj5gz4,4764
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.16.1.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
41
- maidr-0.16.1.dist-info/METADATA,sha256=n7ZkOMo86zBmTCugcRgI1-7SSgJ_0OXnvHt2dRfC3yQ,2688
42
- maidr-0.16.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
43
- maidr-0.16.1.dist-info/RECORD,,
40
+ maidr-0.18.0.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
41
+ maidr-0.18.0.dist-info/METADATA,sha256=Zd5wYCpVOblHGmOdro8lboqoL6ckGzhhY6JarANFU-E,2688
42
+ maidr-0.18.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
43
+ maidr-0.18.0.dist-info/RECORD,,
File without changes