py-pluto 1.1.4__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.
- pyPLUTO/__init__.py +22 -0
- pyPLUTO/amr.py +745 -0
- pyPLUTO/baseloadmixin.py +258 -0
- pyPLUTO/baseloadstate.py +45 -0
- pyPLUTO/codes/echo_load.py +161 -0
- pyPLUTO/configure.py +261 -0
- pyPLUTO/gui/config.py +174 -0
- pyPLUTO/gui/custom_var.py +435 -0
- pyPLUTO/gui/globals.py +108 -0
- pyPLUTO/gui/main.py +17 -0
- pyPLUTO/gui/main_window.py +177 -0
- pyPLUTO/gui/panels.py +66 -0
- pyPLUTO/gui/utils.py +273 -0
- pyPLUTO/h_pypluto.py +84 -0
- pyPLUTO/image.py +302 -0
- pyPLUTO/imagefuncs/colorbar.py +240 -0
- pyPLUTO/imagefuncs/contour.py +254 -0
- pyPLUTO/imagefuncs/create_axes.py +464 -0
- pyPLUTO/imagefuncs/display.py +306 -0
- pyPLUTO/imagefuncs/figure.py +395 -0
- pyPLUTO/imagefuncs/imagetools.py +487 -0
- pyPLUTO/imagefuncs/interactive.py +403 -0
- pyPLUTO/imagefuncs/legend.py +250 -0
- pyPLUTO/imagefuncs/plot.py +311 -0
- pyPLUTO/imagefuncs/range.py +242 -0
- pyPLUTO/imagefuncs/scatter.py +270 -0
- pyPLUTO/imagefuncs/set_axis.py +497 -0
- pyPLUTO/imagefuncs/streamplot.py +297 -0
- pyPLUTO/imagefuncs/zoom.py +428 -0
- pyPLUTO/imagemixin.py +259 -0
- pyPLUTO/imagestate.py +45 -0
- pyPLUTO/load.py +447 -0
- pyPLUTO/loadfuncs/baseloadtools.py +71 -0
- pyPLUTO/loadfuncs/codeselection.py +48 -0
- pyPLUTO/loadfuncs/defpluto.py +123 -0
- pyPLUTO/loadfuncs/descriptor.py +102 -0
- pyPLUTO/loadfuncs/findfiles.py +182 -0
- pyPLUTO/loadfuncs/findformat.py +245 -0
- pyPLUTO/loadfuncs/initload.py +203 -0
- pyPLUTO/loadfuncs/loadvars.py +227 -0
- pyPLUTO/loadfuncs/offsetdata.py +87 -0
- pyPLUTO/loadfuncs/offsetfluid.py +408 -0
- pyPLUTO/loadfuncs/read_files.py +213 -0
- pyPLUTO/loadfuncs/readdata.py +619 -0
- pyPLUTO/loadfuncs/readdata_old.py +567 -0
- pyPLUTO/loadfuncs/readdefplini.py +101 -0
- pyPLUTO/loadfuncs/readfluid.py +479 -0
- pyPLUTO/loadfuncs/readformat.py +277 -0
- pyPLUTO/loadfuncs/readgridalone.py +224 -0
- pyPLUTO/loadfuncs/readgridfile.py +255 -0
- pyPLUTO/loadfuncs/readgridout.py +451 -0
- pyPLUTO/loadfuncs/readpart.py +419 -0
- pyPLUTO/loadfuncs/readtab.py +105 -0
- pyPLUTO/loadfuncs/write_files.py +283 -0
- pyPLUTO/loadmixin.py +419 -0
- pyPLUTO/loadpart.py +233 -0
- pyPLUTO/loadstate.py +68 -0
- pyPLUTO/newload.py +81 -0
- pyPLUTO/pytools.py +145 -0
- pyPLUTO/toolfuncs/findlines.py +551 -0
- pyPLUTO/toolfuncs/fourier.py +149 -0
- pyPLUTO/toolfuncs/nabla.py +676 -0
- pyPLUTO/toolfuncs/parttools.py +152 -0
- pyPLUTO/toolfuncs/transform.py +638 -0
- pyPLUTO/utils/annotator.py +27 -0
- pyPLUTO/utils/inspector.py +145 -0
- pyPLUTO/utils/make_docstrings.py +3 -0
- py_pluto-1.1.4.dist-info/METADATA +218 -0
- py_pluto-1.1.4.dist-info/RECORD +73 -0
- py_pluto-1.1.4.dist-info/WHEEL +5 -0
- py_pluto-1.1.4.dist-info/entry_points.txt +2 -0
- py_pluto-1.1.4.dist-info/licenses/LICENSE +27 -0
- py_pluto-1.1.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Module to manage the display of 2D plots in the image."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from matplotlib.collections import QuadMesh
|
|
7
|
+
from numpy.typing import ArrayLike
|
|
8
|
+
|
|
9
|
+
from pyPLUTO.imagefuncs.colorbar import ColorbarManager
|
|
10
|
+
from pyPLUTO.imagefuncs.imagetools import ImageToolsManager
|
|
11
|
+
from pyPLUTO.imagefuncs.range import RangeManager
|
|
12
|
+
from pyPLUTO.imagefuncs.set_axis import AxisManager
|
|
13
|
+
from pyPLUTO.imagemixin import ImageMixin
|
|
14
|
+
from pyPLUTO.imagestate import ImageState
|
|
15
|
+
from pyPLUTO.utils.inspector import track_kwargs
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DisplayManager(ImageMixin):
|
|
19
|
+
"""Class to manage the display of 2D plots in the image.
|
|
20
|
+
|
|
21
|
+
This class provides methods to create and manage 2D plots using matplotlib's
|
|
22
|
+
pcolormesh function. It allows for customization of the plot's appearance,
|
|
23
|
+
colorbar, axes, and other properties.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
exposed_methods = ("display",)
|
|
27
|
+
|
|
28
|
+
def __init__(self, state: ImageState) -> None:
|
|
29
|
+
"""Initialize the DisplayManager with the given state."""
|
|
30
|
+
self.state = state
|
|
31
|
+
self.ColorbarManager = ColorbarManager(state)
|
|
32
|
+
self.ImageToolsManager = ImageToolsManager(state)
|
|
33
|
+
self.RangeManager = RangeManager(state)
|
|
34
|
+
self.AxisManager = AxisManager(state)
|
|
35
|
+
|
|
36
|
+
@track_kwargs
|
|
37
|
+
def display(
|
|
38
|
+
self,
|
|
39
|
+
var: ArrayLike,
|
|
40
|
+
check: bool = True,
|
|
41
|
+
**kwargs: Any,
|
|
42
|
+
) -> QuadMesh:
|
|
43
|
+
"""Plot for a 2D function using the matplotlib's pcolormesh function.
|
|
44
|
+
|
|
45
|
+
A simple figure and a single axis can also be
|
|
46
|
+
created.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
- The 2D plot
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
- alpha: float, default 1.0
|
|
55
|
+
Sets the transparency of the plot.
|
|
56
|
+
- aspect: {'auto', 'equal', float}, default 'auto'
|
|
57
|
+
Sets the aspect ratio of the plot.
|
|
58
|
+
The 'auto' keyword is the default option (most likely the plot will
|
|
59
|
+
be squared). The 'equal' keyword will set the same scaling for
|
|
60
|
+
x and y. A float will fix the ratio between the y-scale and the
|
|
61
|
+
x-scale (1.0 is the same as 'equal').
|
|
62
|
+
- ax: ax | int | None, default None
|
|
63
|
+
The axis where to plot the lines. If None, a new axis is created.
|
|
64
|
+
If 'old', the last considered axis will be used.
|
|
65
|
+
- bottom: float, default 0.1
|
|
66
|
+
The space from the bottom border to the plot.
|
|
67
|
+
- clabel: str, default None
|
|
68
|
+
Sets the label of the colorbar.
|
|
69
|
+
- cmap: str, default 'plasma'
|
|
70
|
+
Selects the colormap. If not defined, the colormap 'plasma' will be
|
|
71
|
+
adopted. Some useful colormaps are: plasma, magma, seismic. Please
|
|
72
|
+
avoid using colorbars like jet or rainbow, which are not
|
|
73
|
+
perceptively uniform and not suited for people with vision
|
|
74
|
+
deficiencies.
|
|
75
|
+
- cpad: float, default 0.07
|
|
76
|
+
Fraction of original axes between colorbar and the axes (in case cax
|
|
77
|
+
is not defined).
|
|
78
|
+
- cpos: {'top','bottom','left','right'}, default None
|
|
79
|
+
Enables the colorbar (if defined), default position on the right.
|
|
80
|
+
- cscale: {'linear','log','symlog','twoslope'}, default 'linear'
|
|
81
|
+
Sets the colorbar scale. Default is the linear ('norm') scale.
|
|
82
|
+
- cticks: {[float], None}, default None
|
|
83
|
+
If enabled (and different from None), sets manually ticks on the
|
|
84
|
+
colorbar.
|
|
85
|
+
- ctickslabels: str, default None
|
|
86
|
+
If enabled, sets manually ticks labels on the colorbar.
|
|
87
|
+
- extend: {'neither','both','min','max'}, default 'neither'
|
|
88
|
+
Sets the extension of the triangular colorbar extension.
|
|
89
|
+
- extendrect: bool, default False
|
|
90
|
+
If True, the colorbar extension will be rectangular.
|
|
91
|
+
- figsize: [float, float], default [6*sqrt(ncol),5*sqrt(nrow)]
|
|
92
|
+
Sets the figure size. The default value is computed from the number
|
|
93
|
+
of rows and columns.
|
|
94
|
+
- fontsize: float, default 17.0
|
|
95
|
+
Sets the fontsize for all the axes.
|
|
96
|
+
- grid: Bool, default False
|
|
97
|
+
Enables the grid on the plot.
|
|
98
|
+
- labelsize: float, default fontsize
|
|
99
|
+
Sets the labels fontsize (which is the same for both labels).
|
|
100
|
+
The default value corresponds to the value of the keyword
|
|
101
|
+
'fontsize'.
|
|
102
|
+
- left: float, default 0.125
|
|
103
|
+
The space from the left border to the plot.
|
|
104
|
+
- minorticks: str, default None
|
|
105
|
+
If not None enables the minor ticks on the plot (for both grid
|
|
106
|
+
axes).
|
|
107
|
+
- proj: str, default None
|
|
108
|
+
Custom projection for the plot (e.g. 3D). Recommended only if
|
|
109
|
+
needed.
|
|
110
|
+
This keyword should be used only if the axis is created.
|
|
111
|
+
WARNING: pyPLUTO does not support 3D plotting for now, only 3D axes.
|
|
112
|
+
The 3D plot feature will be available in future releases.
|
|
113
|
+
- right: float, default 0.9
|
|
114
|
+
The space from the right border to the plot.
|
|
115
|
+
- shading: {'flat,'nearest','auto','gouraud'}, default 'auto'
|
|
116
|
+
The shading between the grid points. If not defined, the shading
|
|
117
|
+
will one between 'flat' and 'nearest' depending on the size of the
|
|
118
|
+
x,y and z arrays. The 'flat' shading works only if, given a NxM
|
|
119
|
+
z-array, the x- and y-arrays have sizes of, respectively, N+1 and
|
|
120
|
+
M+1. All the other shadings require a N x-array and a M y-array.
|
|
121
|
+
- ticksdir: {'in', 'out'}, default 'in'
|
|
122
|
+
Sets the ticks direction. The default option is 'in'.
|
|
123
|
+
- tickssize: float, default fontsize
|
|
124
|
+
Sets the ticks fontsize (which is the same for both grid axes).
|
|
125
|
+
The default value corresponds to the value of the keyword
|
|
126
|
+
'fontsize'.
|
|
127
|
+
- title: str, default None
|
|
128
|
+
Places the title of the plot on top of it.
|
|
129
|
+
- titlesize: float, default fontsize
|
|
130
|
+
Sets the title fontsize. The default value corresponds to the value
|
|
131
|
+
of the keyword 'fontsize'.
|
|
132
|
+
- top: float, default 0.9
|
|
133
|
+
The space from the top border to the plot.
|
|
134
|
+
- transpose: True/False, default False
|
|
135
|
+
Transposes the variable matrix. Use is not recommended if not really
|
|
136
|
+
necessary (e.g. in case of highly customized variables and plots)
|
|
137
|
+
- tresh: float, default max(abs(vmin),vmax)*0.01
|
|
138
|
+
Sets the threshold for the colormap. If not defined, the threshold
|
|
139
|
+
will be set to 1% of the maximum absolute value of the variable.
|
|
140
|
+
The default cases are the following:
|
|
141
|
+
- twoslope colorscale: sets the limit between the two linear
|
|
142
|
+
regimes.
|
|
143
|
+
- symlog: sets the limit between the logaitrhmic and the linear
|
|
144
|
+
regime.
|
|
145
|
+
- var (not optional): 2D array
|
|
146
|
+
The array to be plotted.
|
|
147
|
+
- vmax: float, default max(var)
|
|
148
|
+
The maximum value of the colormap. If not defined, the maximum value
|
|
149
|
+
of z will be taken.
|
|
150
|
+
- vmin: float, default min(var)
|
|
151
|
+
The minimum value of the colormap. If not defined, the minimum value
|
|
152
|
+
of z will be taken.
|
|
153
|
+
- x1: np.ndarray, default 'Default'
|
|
154
|
+
the 'x' array. If not defined, a default array will be generated
|
|
155
|
+
depending on the size of z.
|
|
156
|
+
- x2: np.ndarray, default 'Default'
|
|
157
|
+
the 'y' array. If not defined, a default array will be generated
|
|
158
|
+
depending on the size of z.
|
|
159
|
+
- xrange: [float, float], default 'Default'
|
|
160
|
+
Sets the range in the x-direction. If not defined or set to
|
|
161
|
+
'Default' the code will compute the range while plotting the data by
|
|
162
|
+
taking the minimum and the maximum values of the x1-array.
|
|
163
|
+
- xscale: {'linear','log'}, default 'linear'
|
|
164
|
+
If enabled (and different from 'Default'), sets automatically the
|
|
165
|
+
scale on the x-axis. Data in log scale should be used with the
|
|
166
|
+
keyword 'log', while data in linear scale should be used with the
|
|
167
|
+
keyword 'linear'.
|
|
168
|
+
- xticks: [float] | None | bool, default True
|
|
169
|
+
If enabled (and different from 'Default'), sets manually ticks on
|
|
170
|
+
x-axis. In order to completely remove the ticks the keyword should
|
|
171
|
+
be used with None.
|
|
172
|
+
- xtickslabels: [str] | None | bool, default True
|
|
173
|
+
If enabled (and different from 'Default'), sets manually the ticks
|
|
174
|
+
labels on the x-axis. In order to completely remove the ticks the
|
|
175
|
+
keyword should be used with None. Note that fixed tickslabels should
|
|
176
|
+
always correspond to fixed ticks.
|
|
177
|
+
- xtitle: str, default None
|
|
178
|
+
Sets and places the label of the x-axis.
|
|
179
|
+
- yrange: [float, float], default 'Default'
|
|
180
|
+
Sets the range in the y-direction. If not defined or set to
|
|
181
|
+
'Default' the code will compute the range while plotting the data by
|
|
182
|
+
taking the minimum and the maximum values of the x2-array.
|
|
183
|
+
- yscale: {'linear','log'}, default 'linear'
|
|
184
|
+
If enabled (and different from 'Default'), sets automatically the
|
|
185
|
+
scale on the y-axis. Data in log scale should be used with the
|
|
186
|
+
keyword 'log', while data in linear scale should be used with the
|
|
187
|
+
keyword 'linear'.
|
|
188
|
+
- yticks: [float] | None | bool, default True
|
|
189
|
+
If enabled (and different from 'Default'), sets manually ticks on
|
|
190
|
+
y-axis. In order to completely remove the ticks the keyword should
|
|
191
|
+
be used with None.
|
|
192
|
+
- ytickslabels: [float] | None | bool, default True
|
|
193
|
+
If enabled (and different from 'Default'), sets manually the ticks
|
|
194
|
+
labels on the y-axis. In order to completely remove the ticks the
|
|
195
|
+
keyword should be used with None. Note that fixed tickslabels should
|
|
196
|
+
always correspond to fixed ticks.
|
|
197
|
+
- ytitle: str, default None
|
|
198
|
+
Sets and places the label of the y-axis.
|
|
199
|
+
|
|
200
|
+
----
|
|
201
|
+
|
|
202
|
+
Examples
|
|
203
|
+
--------
|
|
204
|
+
- Example #1: create a simple 2d plot with title and colorbar on the
|
|
205
|
+
right
|
|
206
|
+
|
|
207
|
+
>>> import pyPLUTO as pp
|
|
208
|
+
>>> I = pp.Image()
|
|
209
|
+
>>> I.display(var, title="title", cpos="right")
|
|
210
|
+
|
|
211
|
+
- Example #2: create a 2d plot with title on the axes, bottom colorbar
|
|
212
|
+
and custom shading
|
|
213
|
+
|
|
214
|
+
>>> import pyPLUTO as pp
|
|
215
|
+
>>> I = pp.Image()
|
|
216
|
+
>>> I.display(x1, x2, var, xtitle = 'x', ytitle = 'y',
|
|
217
|
+
cpos = 'bottom', shading = 'gouraud', cpad = 0.3)
|
|
218
|
+
|
|
219
|
+
- Example #3: create a 2d plot con custom range on axes and logarithmic
|
|
220
|
+
scale colorbar
|
|
221
|
+
|
|
222
|
+
>>> import pyPLUTO as pp
|
|
223
|
+
>>> I = pp.Image()
|
|
224
|
+
>>> I.display(var, xrange = [2,3], yrange = [2,4], cbar = 'right',
|
|
225
|
+
cscale = 'log')
|
|
226
|
+
|
|
227
|
+
- Example #4: create a 2d plot with a custom symmetric logarithmic
|
|
228
|
+
colorbar with custom ticks.
|
|
229
|
+
|
|
230
|
+
>>> import pyPLUTO as pp
|
|
231
|
+
>>> I = pp.Image()
|
|
232
|
+
>>> I.display(var, cpos = 'right', cmap = 'RdBu_r',
|
|
233
|
+
cscale = 'symlog', tresh = 0.001, vmin = -1, vmax = 1)
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
kwargs.pop("check", check)
|
|
237
|
+
|
|
238
|
+
# Set or create figure and axes
|
|
239
|
+
ax, nax = self.ImageToolsManager.assign_ax(
|
|
240
|
+
kwargs.pop("ax", None), **kwargs
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
if self.fig is None:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
"No figure is present. Please create a figure first."
|
|
246
|
+
)
|
|
247
|
+
# Keyword x1 and x2
|
|
248
|
+
var = np.asarray(var)
|
|
249
|
+
if kwargs.get("transpose", False) is True:
|
|
250
|
+
var = var.T
|
|
251
|
+
x = np.asarray(kwargs.get("x1", np.arange(len(var[:, 0]) + 1)))
|
|
252
|
+
y = np.asarray(kwargs.get("x2", np.arange(len(var[0, :]) + 1)))
|
|
253
|
+
|
|
254
|
+
# Keywords xrange and yrange
|
|
255
|
+
if not kwargs.get("xrange") and self.setax[nax] != 1:
|
|
256
|
+
kwargs["xrange"] = [x.min(), x.max()]
|
|
257
|
+
if not kwargs.get("yrange") and self.setay[nax] != 1:
|
|
258
|
+
kwargs["yrange"] = [y.min(), y.max()]
|
|
259
|
+
# Set ax parameters
|
|
260
|
+
self.AxisManager.set_axis(ax=ax, check=False, **kwargs)
|
|
261
|
+
self.ImageToolsManager.hide_text(nax, ax.texts)
|
|
262
|
+
|
|
263
|
+
# Keywords vmin and vmax
|
|
264
|
+
vmin = kwargs.get("vmin", np.nanmin(var))
|
|
265
|
+
vmax = kwargs.get("vmax", np.nanmax(var))
|
|
266
|
+
|
|
267
|
+
# Keyword for colorbar and colorscale
|
|
268
|
+
cpos = kwargs.get("cpos")
|
|
269
|
+
cscale = kwargs.get("cscale", "norm")
|
|
270
|
+
tresh = kwargs.get("tresh", max(np.abs(vmin), vmax) * 0.01)
|
|
271
|
+
lint = kwargs.get("lint")
|
|
272
|
+
self.vlims[nax] = [vmin, vmax, tresh]
|
|
273
|
+
|
|
274
|
+
# Set the colorbar scale (put in function)
|
|
275
|
+
norm = self.ImageToolsManager.set_cscale(
|
|
276
|
+
cscale, vmin, vmax, tresh, lint
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Select shading
|
|
280
|
+
shade = kwargs.get("shading", "auto")
|
|
281
|
+
alpha = kwargs.get("alpha", 1.0)
|
|
282
|
+
|
|
283
|
+
cmap = self.ImageToolsManager.find_cmap(kwargs.get("cmap", "plasma"))
|
|
284
|
+
|
|
285
|
+
# Display the image
|
|
286
|
+
pcm = ax.pcolormesh(
|
|
287
|
+
x,
|
|
288
|
+
y,
|
|
289
|
+
var.T,
|
|
290
|
+
shading=shade,
|
|
291
|
+
cmap=cmap,
|
|
292
|
+
norm=norm,
|
|
293
|
+
linewidth=0,
|
|
294
|
+
rasterized=True,
|
|
295
|
+
alpha=alpha,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Place the colorbar (use colorbar function)
|
|
299
|
+
if cpos is not None:
|
|
300
|
+
self.ColorbarManager.colorbar(pcm, check=False, **kwargs)
|
|
301
|
+
|
|
302
|
+
# If tight_layout is enabled, is re-inforced
|
|
303
|
+
if self.tight:
|
|
304
|
+
self.fig.tight_layout()
|
|
305
|
+
|
|
306
|
+
return pcm
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""Figure Manager Module."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import Unpack
|
|
6
|
+
|
|
7
|
+
import matplotlib as mpl
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from matplotlib.figure import Figure
|
|
10
|
+
|
|
11
|
+
from pyPLUTO.imagemixin import ImageMixin
|
|
12
|
+
from pyPLUTO.imagestate import ImageState
|
|
13
|
+
from pyPLUTO.utils.annotator import AllKwargs
|
|
14
|
+
from pyPLUTO.utils.inspector import track_kwargs
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FigureManager(ImageMixin):
|
|
18
|
+
"""Manages the figure and sets the style, size, and LaTeX settings."""
|
|
19
|
+
|
|
20
|
+
@track_kwargs
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
state: ImageState,
|
|
24
|
+
**kwargs: Unpack[AllKwargs],
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Initialize the FigureManager class.
|
|
27
|
+
|
|
28
|
+
It creates a new figure and sets the LaTeX conditions, as well as the
|
|
29
|
+
matplotlib style. Every Image is associated to a figure object and only
|
|
30
|
+
one in order to avoid confusion between images and figures. If you
|
|
31
|
+
want to create multiple figures, you have to create multiple
|
|
32
|
+
Image objects.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
- None
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
- state: ImageState
|
|
41
|
+
The state of the image, which contains the figure and other
|
|
42
|
+
properties.
|
|
43
|
+
- kwargs: dict[str, Any]
|
|
44
|
+
Additional keyword arguments to customize the figure, such as
|
|
45
|
+
`figsize`, `fontsize`, `nwin`, `suptitle`, etc.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
# needed because the __init__ is longer than simply self.state = state
|
|
49
|
+
self.state = state
|
|
50
|
+
|
|
51
|
+
# Extract specific kwargs for colorlines, with defaults if not provided
|
|
52
|
+
close = kwargs.pop("close", True)
|
|
53
|
+
fontweight = kwargs.pop("fontweight", "normal")
|
|
54
|
+
numcolors = kwargs.pop("numcolors", 10)
|
|
55
|
+
replace = kwargs.pop("replace", False)
|
|
56
|
+
suptitle = kwargs.pop("suptitle", None)
|
|
57
|
+
suptitlesize = kwargs.pop("suptitlesize", "large")
|
|
58
|
+
withblack = kwargs.pop("withblack", False)
|
|
59
|
+
withwhite = kwargs.pop("withwhite", False)
|
|
60
|
+
|
|
61
|
+
self.fig = kwargs.get("fig", self.fig)
|
|
62
|
+
self.figsize = kwargs.get("figsize", self.figsize)
|
|
63
|
+
self.fontsize = kwargs.get("fontsize", self.fontsize)
|
|
64
|
+
self.LaTeX = kwargs.get("LaTeX", self.LaTeX)
|
|
65
|
+
self.nwin = kwargs.get("nwin", self.nwin)
|
|
66
|
+
self.style = kwargs.get("style", self.style)
|
|
67
|
+
self.tight = kwargs.get("tight", self.tight)
|
|
68
|
+
|
|
69
|
+
self.check_previous_fig(close)
|
|
70
|
+
if "figsize" in kwargs:
|
|
71
|
+
self.set_size = True
|
|
72
|
+
|
|
73
|
+
self.setup_style()
|
|
74
|
+
self.color = self.choose_colorlines(numcolors, withblack, withwhite)
|
|
75
|
+
self.assign_LaTeX(fontweight)
|
|
76
|
+
self.create_figure(replace, suptitle, suptitlesize)
|
|
77
|
+
|
|
78
|
+
def setup_style(self) -> None:
|
|
79
|
+
"""Set the matplotlib style."""
|
|
80
|
+
try:
|
|
81
|
+
plt.style.use(self.style)
|
|
82
|
+
except OSError:
|
|
83
|
+
warn = f"Warning: Style '{self.style}' not found. \
|
|
84
|
+
Switching to 'default'"
|
|
85
|
+
warnings.warn(warn, UserWarning, stacklevel=2)
|
|
86
|
+
self.style = "default"
|
|
87
|
+
|
|
88
|
+
def choose_colorlines(
|
|
89
|
+
self, numcolors: int, withblack: bool, withwhite: bool
|
|
90
|
+
) -> list[str]:
|
|
91
|
+
"""Choose the colors for the lines.
|
|
92
|
+
|
|
93
|
+
The colors are taken from a list of colors that are suitable for all
|
|
94
|
+
types of color vision deficiencies.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
- colors: list[str]
|
|
99
|
+
The list of colors for the lines.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
- numcolors: int, default 10
|
|
104
|
+
The number of colors.
|
|
105
|
+
- withblack: bool, default False
|
|
106
|
+
If True, the black color is used as first color.
|
|
107
|
+
- withwhite: bool default False
|
|
108
|
+
If True, the white color is used as first color.
|
|
109
|
+
|
|
110
|
+
----
|
|
111
|
+
|
|
112
|
+
Examples
|
|
113
|
+
--------
|
|
114
|
+
- Example #1: withblack = True
|
|
115
|
+
|
|
116
|
+
>>> _choose_colorlines(6, True)
|
|
117
|
+
|
|
118
|
+
- Example #2: 12 colors, withwhite = True
|
|
119
|
+
|
|
120
|
+
>>> _choose_colorlines(12, False, True)
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
# New colors dictionary (black and white included)
|
|
124
|
+
self.dictcol = {
|
|
125
|
+
0: "#ffffff",
|
|
126
|
+
1: "#e8ecfb",
|
|
127
|
+
2: "#d9cce3",
|
|
128
|
+
3: "#d1bbd7",
|
|
129
|
+
4: "#caaccb",
|
|
130
|
+
5: "#ae76a3",
|
|
131
|
+
6: "#aa6f9e",
|
|
132
|
+
7: "#994f88",
|
|
133
|
+
8: "#882e72",
|
|
134
|
+
9: "#0104fe",
|
|
135
|
+
10: "#1e3888",
|
|
136
|
+
11: "#437dbf",
|
|
137
|
+
12: "#5289c7",
|
|
138
|
+
13: "#6195cf",
|
|
139
|
+
14: "#7bafde",
|
|
140
|
+
15: "#4eb265",
|
|
141
|
+
16: "#90c987",
|
|
142
|
+
17: "#cae0ab",
|
|
143
|
+
18: "#f7f056",
|
|
144
|
+
19: "#f7cb45",
|
|
145
|
+
20: "#f6c141",
|
|
146
|
+
21: "#f4a736",
|
|
147
|
+
22: "#f1932d",
|
|
148
|
+
23: "#ee8026",
|
|
149
|
+
24: "#e8601c",
|
|
150
|
+
25: "#e65518",
|
|
151
|
+
26: "#dc050c",
|
|
152
|
+
27: "#a5170e",
|
|
153
|
+
28: "#72190e",
|
|
154
|
+
29: "#42150a",
|
|
155
|
+
30: "#777777",
|
|
156
|
+
31: "#000000",
|
|
157
|
+
32: "#0104fe",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Colors are ordered to avoid color vision deficiencies
|
|
161
|
+
lstc = [
|
|
162
|
+
9,
|
|
163
|
+
26,
|
|
164
|
+
15,
|
|
165
|
+
23,
|
|
166
|
+
14,
|
|
167
|
+
17,
|
|
168
|
+
6,
|
|
169
|
+
25,
|
|
170
|
+
28,
|
|
171
|
+
18,
|
|
172
|
+
11,
|
|
173
|
+
2,
|
|
174
|
+
8,
|
|
175
|
+
16,
|
|
176
|
+
10,
|
|
177
|
+
21,
|
|
178
|
+
7,
|
|
179
|
+
27,
|
|
180
|
+
4,
|
|
181
|
+
13,
|
|
182
|
+
19,
|
|
183
|
+
29,
|
|
184
|
+
1,
|
|
185
|
+
30,
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
# Black and white addition
|
|
189
|
+
lstc = [0, *lstc] if withwhite else [31, *lstc] if withblack else lstc
|
|
190
|
+
|
|
191
|
+
# End of function, return the colors
|
|
192
|
+
return [self.dictcol[lstc[i]] for i in range(numcolors)]
|
|
193
|
+
|
|
194
|
+
def assign_LaTeX(self, fontweight: str) -> None:
|
|
195
|
+
"""Set the LaTeX conditions.
|
|
196
|
+
|
|
197
|
+
The option 'pgf' requires XeLaTeX and should be used only to get
|
|
198
|
+
vectorial figures with minimal file size.
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
- None
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
- LaTeX (not optional): bool | str
|
|
207
|
+
The LaTeX option. Is True is selected, the default LaTeX font is
|
|
208
|
+
used. If 'pgf' is selected, the pgf backend is used to save pdf
|
|
209
|
+
figures with minimal file size. If XeLaTeX is not installed and the
|
|
210
|
+
'pgf' option is selected, the LaTeX option True is used as backup
|
|
211
|
+
strategy.
|
|
212
|
+
|
|
213
|
+
----
|
|
214
|
+
|
|
215
|
+
Examples
|
|
216
|
+
--------
|
|
217
|
+
- Example #1: LaTeX option True
|
|
218
|
+
|
|
219
|
+
>>> _assign_LaTeX(True)
|
|
220
|
+
|
|
221
|
+
- Example #2: LaTeX option 'pgf'
|
|
222
|
+
|
|
223
|
+
>>> _assign_LaTeX("pgf")
|
|
224
|
+
|
|
225
|
+
"""
|
|
226
|
+
# LaTeX option 'pgf' (requires XeLaTeX)
|
|
227
|
+
if self.LaTeX == "pgf" and not shutil.which("latex"):
|
|
228
|
+
warn = "LaTeX not installed, switching to LaTeX = True"
|
|
229
|
+
warnings.warn(warn, UserWarning, stacklevel=2)
|
|
230
|
+
self.LaTeX = True
|
|
231
|
+
|
|
232
|
+
if self.LaTeX == "pgf":
|
|
233
|
+
# Set the pgf backend
|
|
234
|
+
try:
|
|
235
|
+
plt.switch_backend("pgf")
|
|
236
|
+
|
|
237
|
+
# Preamble (LaTeX commands and packages)
|
|
238
|
+
pgf_preamble = r"""
|
|
239
|
+
\usepackage{amsmath}
|
|
240
|
+
\usepackage{amssymb}
|
|
241
|
+
\usepackage{mathptmx}
|
|
242
|
+
\usepackage{siunitx}
|
|
243
|
+
\usepackage[T1]{fontenc}
|
|
244
|
+
\newcommand{\DS}{\displaystyle}
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
# Update the rcParams
|
|
248
|
+
mpl.rcParams.update(
|
|
249
|
+
{
|
|
250
|
+
"pgf.preamble": pgf_preamble,
|
|
251
|
+
"font.family": "serif",
|
|
252
|
+
"font.weight": fontweight,
|
|
253
|
+
"text.usetex": True,
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# If errors occur, the LaTeX option True is used and a warning
|
|
258
|
+
# message is displayed
|
|
259
|
+
except ImportError:
|
|
260
|
+
warn = "The pgf backend is not available, reverting to True\n"
|
|
261
|
+
warnings.warn(warn, UserWarning, stacklevel=2)
|
|
262
|
+
self.LaTeX = True
|
|
263
|
+
|
|
264
|
+
# LaTeX option True: default LaTeX font
|
|
265
|
+
if self.LaTeX is True:
|
|
266
|
+
try:
|
|
267
|
+
mpl.rcParams["mathtext.fontset"] = "stix"
|
|
268
|
+
mpl.rcParams["font.family"] = "STIXGeneral"
|
|
269
|
+
except ImportError:
|
|
270
|
+
warn = "The LaTeX = True option is not available."
|
|
271
|
+
warnings.warn(warn, UserWarning, stacklevel=2)
|
|
272
|
+
|
|
273
|
+
# End of the function
|
|
274
|
+
|
|
275
|
+
def check_previous_fig(self, close: bool) -> None:
|
|
276
|
+
"""Check if there is an existing figure.
|
|
277
|
+
|
|
278
|
+
If it exists, the code will check if it is closed or not.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
- None
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
- close: bool, default True
|
|
287
|
+
If True, the existing figure with the same window number is closed.
|
|
288
|
+
|
|
289
|
+
----
|
|
290
|
+
|
|
291
|
+
Examples
|
|
292
|
+
--------
|
|
293
|
+
- Example #1: Check if there is an existing figure
|
|
294
|
+
|
|
295
|
+
>>> _check_previous_fig(True)
|
|
296
|
+
|
|
297
|
+
"""
|
|
298
|
+
if isinstance(self.fig, Figure):
|
|
299
|
+
self.figsize = [
|
|
300
|
+
self.fig.get_figwidth(),
|
|
301
|
+
self.fig.get_figheight(),
|
|
302
|
+
]
|
|
303
|
+
self.fontsize = plt.rcParams["font.size"]
|
|
304
|
+
try:
|
|
305
|
+
fignum = self.fig.number
|
|
306
|
+
if isinstance(fignum, int):
|
|
307
|
+
self.state.nwin = fignum
|
|
308
|
+
except AttributeError:
|
|
309
|
+
warnings.warn(
|
|
310
|
+
"The figure is not associated to a window number",
|
|
311
|
+
UserWarning,
|
|
312
|
+
stacklevel=2,
|
|
313
|
+
)
|
|
314
|
+
self.nwin = 1
|
|
315
|
+
self.tight = self.fig.get_tight_layout()
|
|
316
|
+
|
|
317
|
+
# Close the existing figure if it exists (and 'close' is enabled).
|
|
318
|
+
# `clf()` releases artists/axes payloads immediately, which prevents
|
|
319
|
+
# stale Image instances (still holding axes/text refs) from retaining
|
|
320
|
+
# heavy plot data in memory.
|
|
321
|
+
if plt.fignum_exists(self.nwin) and close is True:
|
|
322
|
+
existing_fig = plt.figure(self.nwin)
|
|
323
|
+
existing_fig.clf()
|
|
324
|
+
plt.close(existing_fig)
|
|
325
|
+
|
|
326
|
+
def create_figure(
|
|
327
|
+
self, replace: bool, suptitle: str | None, suptitlesize: int | str
|
|
328
|
+
) -> None:
|
|
329
|
+
"""Create the figure associated to an Image instance.
|
|
330
|
+
|
|
331
|
+
It is called by default when the Image class is instantiated.
|
|
332
|
+
|
|
333
|
+
Returns
|
|
334
|
+
-------
|
|
335
|
+
- None
|
|
336
|
+
|
|
337
|
+
Parameters
|
|
338
|
+
----------
|
|
339
|
+
- close: bool, default True
|
|
340
|
+
If True, the existing figure with the same window number is closed.
|
|
341
|
+
- fig (not optional): Figure | None, default None
|
|
342
|
+
The figure instance. If not None, the figure is used (only if we
|
|
343
|
+
need to associate an Image to an existing figure).
|
|
344
|
+
- figsize: list[float], default [8,5]
|
|
345
|
+
The figure size.
|
|
346
|
+
- fontsize: int, default 17
|
|
347
|
+
The font size.
|
|
348
|
+
- nwin: int, default 1
|
|
349
|
+
The window number.
|
|
350
|
+
- suptitle: str | None, default None
|
|
351
|
+
The super title of the figure.
|
|
352
|
+
- suptitlesize: int | str, default 'large'
|
|
353
|
+
The figure title size.
|
|
354
|
+
- tight: bool, default True
|
|
355
|
+
If True, the tight layout is used.
|
|
356
|
+
|
|
357
|
+
----
|
|
358
|
+
|
|
359
|
+
Examples
|
|
360
|
+
--------
|
|
361
|
+
- Example #1: Create a new figure
|
|
362
|
+
|
|
363
|
+
>>> _create_figure()
|
|
364
|
+
|
|
365
|
+
- Example #2: Associate an Image to an existing figure
|
|
366
|
+
|
|
367
|
+
>>> _create_figure(fig=fig)
|
|
368
|
+
|
|
369
|
+
- Example #3: Create a new figure with different size and a figure title
|
|
370
|
+
|
|
371
|
+
>>> _create_figure(suptitle="Super Title", figsize=[10, 5])
|
|
372
|
+
|
|
373
|
+
- Example #4: Create a new figure with a specific window number
|
|
374
|
+
|
|
375
|
+
>>> _create_figure(nwin=2)
|
|
376
|
+
|
|
377
|
+
"""
|
|
378
|
+
# Create a new figure instance with the provided window number
|
|
379
|
+
if self.fig is None or replace is True:
|
|
380
|
+
self.fig = plt.figure(
|
|
381
|
+
self.nwin,
|
|
382
|
+
figsize=(self.figsize[0], self.figsize[1]),
|
|
383
|
+
)
|
|
384
|
+
plt.rcParams.update({"font.size": self.fontsize})
|
|
385
|
+
|
|
386
|
+
if self.fig is None:
|
|
387
|
+
raise ValueError("The figure could not be created.")
|
|
388
|
+
|
|
389
|
+
# Suptitle
|
|
390
|
+
if suptitle is not None:
|
|
391
|
+
self.fig.suptitle(suptitle, fontsize=suptitlesize)
|
|
392
|
+
|
|
393
|
+
# Tight layout
|
|
394
|
+
if self.tight is True:
|
|
395
|
+
self.fig.tight_layout()
|