gsplot 0.0.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.
gsplot/figure/axes.py ADDED
@@ -0,0 +1,361 @@
1
+ from collections.abc import Hashable
2
+ from enum import Enum
3
+ from typing import Any, Generic, Literal, TypeVar
4
+
5
+ import matplotlib.pyplot as plt
6
+ from matplotlib.axes import Axes
7
+ from matplotlib.typing import HashableList
8
+
9
+ from ..base.base import CreateClassParams, ParamsGetter, bind_passed_params
10
+ from ..plot.line_base import NumLines
11
+ from .axes_base import AxesRangeSingleton
12
+ from .store import StoreSingleton
13
+
14
+ _T = TypeVar("_T")
15
+
16
+ __all__: list[str] = ["axes"]
17
+
18
+
19
+ class Unit(Enum):
20
+ """
21
+ Enumeration of measurement units for various contexts such as dimensions and typography.
22
+
23
+ This class defines common measurement units, including millimeters, centimeters,
24
+ inches, and points. It also includes an "INVALID" option for invalid or unspecified units.
25
+
26
+ Attributes
27
+ --------------------
28
+ MM : str
29
+ Represents millimeters ("mm").
30
+ CM : str
31
+ Represents centimeters ("cm").
32
+ IN : str
33
+ Represents inches ("in").
34
+ PT : str
35
+ Represents points ("pt").
36
+ INVALID : str
37
+ Represents an invalid or unspecified unit ("invalid").
38
+
39
+ Examples
40
+ --------------------
41
+ >>> unit = Unit.MM
42
+ >>> print(unit)
43
+ Unit.MM
44
+ >>> print(unit.value)
45
+ 'mm'
46
+
47
+ >>> invalid_unit = Unit.INVALID
48
+ >>> print(invalid_unit)
49
+ Unit.INVALID
50
+ >>> print(invalid_unit.value)
51
+ 'invalid'
52
+ """
53
+
54
+ MM = "mm"
55
+ CM = "cm"
56
+ IN = "in"
57
+ PT = "pt"
58
+ INVALID = "invalid"
59
+
60
+
61
+ class UnitConv:
62
+ """
63
+ A utility class for converting values between different measurement units.
64
+
65
+ This class supports conversion from millimeters (mm), centimeters (cm),
66
+ inches (in), and points (pt) to a base unit of inches. Conversion factors
67
+ are defined for each supported unit.
68
+
69
+ Attributes
70
+ --------------------
71
+ conversion_factors : dict of Unit, float
72
+ A dictionary mapping each `Unit` to its corresponding conversion factor to inches.
73
+
74
+ Methods
75
+ --------------------
76
+ convert(value, unit)
77
+ Converts a value from the specified unit to inches.
78
+
79
+ Examples
80
+ --------------------
81
+ >>> converter = UnitConv()
82
+ >>> inches = converter.convert(10, Unit.CM)
83
+ >>> print(inches)
84
+ 3.937007874015748
85
+
86
+ >>> points = converter.convert(1, Unit.PT)
87
+ >>> print(points)
88
+ 0.013888888888888888
89
+
90
+ >>> try:
91
+ ... converter.convert(10, Unit.INVALID)
92
+ ... except ValueError as e:
93
+ ... print(e)
94
+ Invalid unit
95
+ """
96
+
97
+ def __init__(self) -> None:
98
+
99
+ self.conversion_factors: dict[Unit, float] = {
100
+ Unit.MM: 1 / 25.4,
101
+ Unit.CM: 1 / 2.54,
102
+ Unit.IN: 1,
103
+ Unit.PT: 1 / 72,
104
+ }
105
+
106
+ def convert(self, value: float, unit: Unit) -> float:
107
+ """
108
+ Converts a value from the specified unit to inches.
109
+
110
+ Parameters
111
+ --------------------
112
+ value : float
113
+ The numerical value to convert.
114
+ unit : Unit
115
+ The unit of the value to be converted. Must be a member of the `Unit` enum.
116
+
117
+ Returns
118
+ --------------------
119
+ float
120
+ The converted value in inches.
121
+
122
+ Raises
123
+ --------------------
124
+ ValueError
125
+ If the specified unit is not supported.
126
+
127
+ Examples
128
+ --------------------
129
+ >>> converter = UnitConv()
130
+ >>> inches = converter.convert(10, Unit.CM)
131
+ >>> print(inches)
132
+ 3.937007874015748
133
+ """
134
+
135
+ if unit not in self.conversion_factors:
136
+ raise ValueError("Invalid unit")
137
+ return value * self.conversion_factors[unit]
138
+
139
+
140
+ class AxesHandler(Generic[_T]):
141
+ """
142
+ A handler for managing Matplotlib figures and axes with custom configurations.
143
+
144
+ This class provides a high-level interface for creating and managing Matplotlib
145
+ figures with support for size adjustments, unit conversions, subplot mosaics,
146
+ and interactive plotting. It also integrates singleton patterns for managing
147
+ state across multiple instances.
148
+
149
+ Parameters
150
+ --------------------
151
+ store : bool, optional
152
+ Whether to use a shared storage for axes or figure states (default is False).
153
+ size : list of int or float, optional
154
+ The size of the figure in the specified unit (default is [5, 5]).
155
+ unit : str, optional
156
+ The unit for the figure size. Supported units are "mm", "cm", "in", and "pt" (default is "in").
157
+ mosaic : str or list of HashableList[_T] or list of HashableList[Hashable], optional
158
+ The mosaic layout for subplots. Can be a string or a list of hashable items (default is "A").
159
+ clear : bool, optional
160
+ Whether to clear the current figure before creating a new one (default is True).
161
+ ion : bool, optional
162
+ Whether to enable interactive mode for the figure (default is False).
163
+ *args : Any
164
+ Additional positional arguments to pass to Matplotlib's figure creation methods.
165
+ **kwargs : Any
166
+ Additional keyword arguments to pass to Matplotlib's figure creation methods.
167
+
168
+ Attributes
169
+ --------------------
170
+ store : bool
171
+ Indicates whether shared storage is used.
172
+ size : list of int or float
173
+ The size of the figure in the specified unit.
174
+ unit : Literal["mm", "cm", "in", "pt"]
175
+ The unit for the figure size.
176
+ mosaic : str or list of HashableList[_T] or list of HashableList[Hashable]
177
+ The mosaic layout for subplots.
178
+ clear : bool
179
+ Indicates whether the current figure is cleared.
180
+ ion : bool
181
+ Indicates whether interactive mode is enabled.
182
+ unit_enum : Unit
183
+ The unit as an enumerated value for validation and conversion.
184
+ unit_conv : UnitConv
185
+ An instance of `UnitConv` for size conversion.
186
+ get_axes : list of matplotlib.axes.Axes
187
+ A property that retrieves the current figure's axes.
188
+
189
+ Methods
190
+ --------------------
191
+ create_figure()
192
+ Creates and configures a Matplotlib figure based on the specified parameters.
193
+
194
+ Examples
195
+ --------------------
196
+ >>> handler = AxesHandler(
197
+ ... size=[10, 8],
198
+ ... unit="cm",
199
+ ... mosaic="AB;CD",
200
+ ... ion=True
201
+ ... )
202
+ >>> handler.create_figure()
203
+ >>> axes = handler.get_axes
204
+ >>> print(axes)
205
+ [<Axes: label='A'>, <Axes: label='B'>, <Axes: label='C'>, <Axes: label='D'>]
206
+ """
207
+
208
+ def __init__(
209
+ self,
210
+ store: bool = False,
211
+ size: list[int | float] = [5, 5],
212
+ unit: Literal["mm", "cm", "in", "pt"] = "in",
213
+ mosaic: str | list[HashableList[_T]] | list[HashableList[Hashable]] = "A",
214
+ clear: bool = True,
215
+ ion: bool = False,
216
+ *args: Any,
217
+ **kwargs: Any,
218
+ ) -> None:
219
+ self.store = store
220
+ self.size: list[int | float] = size
221
+ self.unit: str = unit
222
+ self.mosaic: str | list[HashableList[_T]] | list[HashableList[Hashable]] = (
223
+ mosaic
224
+ )
225
+ self.clear: bool = clear
226
+ self.ion: bool = ion
227
+ self.args: Any = args
228
+ self.kwargs: Any = kwargs
229
+
230
+ self._store_singleton = StoreSingleton()
231
+ self._store_singleton.store = self.store
232
+
233
+ self.unit_enum: Unit = Unit[self.unit.upper()]
234
+ self.unit_conv: UnitConv = UnitConv()
235
+
236
+ @property
237
+ def get_axes(self) -> list[Axes]:
238
+ """
239
+ Retrieves the current figure's axes.
240
+
241
+ Returns
242
+ --------------------
243
+ list of matplotlib.axes.Axes
244
+ A list of axes in the current figure.
245
+ """
246
+ return plt.gcf().axes
247
+
248
+ def create_figure(self) -> None:
249
+ """
250
+ Creates and configures a Matplotlib figure based on the specified parameters.
251
+
252
+ Raises
253
+ --------------------
254
+ ValueError
255
+ If `size` does not contain exactly two elements or if `mosaic` is empty.
256
+
257
+ Examples
258
+ --------------------
259
+ >>> handler = AxesHandler(size=[10, 8], unit="cm", mosaic="AB;CD", ion=True)
260
+ >>> handler.create_figure()
261
+ """
262
+ NumLines().reset()
263
+
264
+ if self.ion:
265
+ plt.ion()
266
+
267
+ if self.clear:
268
+ plt.gcf().clear()
269
+
270
+ if len(self.size) != 2:
271
+ raise ValueError("Size must contain exactly two elements.")
272
+
273
+ conv_size: tuple[float, float] = (
274
+ self.unit_conv.convert(self.size[0], self.unit_enum),
275
+ self.unit_conv.convert(self.size[1], self.unit_enum),
276
+ )
277
+ plt.gcf().set_size_inches(*conv_size, *self.args, **self.kwargs)
278
+
279
+ if self.mosaic != "":
280
+ plt.gcf().subplot_mosaic(self.mosaic)
281
+
282
+ # To ensure that the axes are tightly packed, otherwise axes sizes will be different after tight_layout is called
283
+ plt.tight_layout()
284
+ else:
285
+ raise ValueError("Mosaic must be specified.")
286
+
287
+ # Initialize the axes range list by the number of axes in the current figure
288
+ AxesRangeSingleton().reset(plt.gcf().axes)
289
+
290
+
291
+ @bind_passed_params()
292
+ def axes(
293
+ store: bool = False,
294
+ size: list[int | float] = [5, 5],
295
+ unit: Literal["mm", "cm", "in", "pt"] = "in",
296
+ mosaic: str | list[HashableList[_T]] | list[HashableList[Hashable]] = "A",
297
+ clear: bool = True,
298
+ ion: bool = False,
299
+ *args: Any,
300
+ **kwargs: Any,
301
+ ):
302
+ """
303
+ Creates and configures a Matplotlib figure with specified parameters.
304
+
305
+ This function wraps the `AxesHandler` class to provide an easy interface
306
+ for managing Matplotlib figures and their axes. Parameters such as figure size,
307
+ units, mosaic layouts, and additional configuration options can be specified.
308
+
309
+ Parameters
310
+ --------------------
311
+ store : bool, optional
312
+ Whether to use a shared storage for axes or figure states (default is False).
313
+ size : list of int or float, optional
314
+ The size of the figure in the specified unit (default is [5, 5]).
315
+ unit : Literal["mm", "cm", "in", "pt"], optional
316
+ The unit for the figure size. Supported units are "mm", "cm", "in", and "pt" (default is "in").
317
+ mosaic : str or list of HashableList[_T] or list of HashableList[Hashable], optional
318
+ The mosaic layout for subplots. Can be a string or a list of hashable items (default is "A").
319
+ clear : bool, optional
320
+ Whether to clear the current figure before creating a new one (default is True).
321
+ ion : bool, optional
322
+ Whether to enable interactive mode for the figure (default is False).
323
+ *args : Any
324
+ Additional positional arguments to pass to Matplotlib's figure creation methods.
325
+ **kwargs : Any
326
+ Additional keyword arguments to pass to Matplotlib's figure creation methods.
327
+
328
+ Notes
329
+ --------------------
330
+ This function utilizes the `ParamsGetter` to retrieve bound parameters and
331
+ the `CreateClassParams` class to handle the merging of default, configuration,
332
+ and passed parameters.
333
+
334
+ Returns
335
+ --------------------
336
+ list of matplotlib.axes.Axes
337
+ A list of axes in the created figure.
338
+
339
+ Examples
340
+ --------------------
341
+ >>> import gsplot as
342
+ >>> axs = gs.axes(size=[10, 8], unit="cm", mosaic="AB;CD", ion=True)
343
+ >>> print(axs)
344
+ [<Axes: label='A'>, <Axes: label='B'>, <Axes: label='C'>, <Axes: label='D'>]
345
+ """
346
+
347
+ passed_params: dict[str, Any] = ParamsGetter("passed_params").get_bound_params()
348
+ class_params = CreateClassParams(passed_params).get_class_params()
349
+
350
+ _axes_handler: AxesHandler = AxesHandler(
351
+ class_params["store"],
352
+ class_params["size"],
353
+ class_params["unit"],
354
+ class_params["mosaic"],
355
+ class_params["clear"],
356
+ class_params["ion"],
357
+ *class_params["args"],
358
+ **class_params["kwargs"],
359
+ )
360
+ _axes_handler.create_figure()
361
+ return _axes_handler.get_axes