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.
Files changed (73) hide show
  1. pyPLUTO/__init__.py +22 -0
  2. pyPLUTO/amr.py +745 -0
  3. pyPLUTO/baseloadmixin.py +258 -0
  4. pyPLUTO/baseloadstate.py +45 -0
  5. pyPLUTO/codes/echo_load.py +161 -0
  6. pyPLUTO/configure.py +261 -0
  7. pyPLUTO/gui/config.py +174 -0
  8. pyPLUTO/gui/custom_var.py +435 -0
  9. pyPLUTO/gui/globals.py +108 -0
  10. pyPLUTO/gui/main.py +17 -0
  11. pyPLUTO/gui/main_window.py +177 -0
  12. pyPLUTO/gui/panels.py +66 -0
  13. pyPLUTO/gui/utils.py +273 -0
  14. pyPLUTO/h_pypluto.py +84 -0
  15. pyPLUTO/image.py +302 -0
  16. pyPLUTO/imagefuncs/colorbar.py +240 -0
  17. pyPLUTO/imagefuncs/contour.py +254 -0
  18. pyPLUTO/imagefuncs/create_axes.py +464 -0
  19. pyPLUTO/imagefuncs/display.py +306 -0
  20. pyPLUTO/imagefuncs/figure.py +395 -0
  21. pyPLUTO/imagefuncs/imagetools.py +487 -0
  22. pyPLUTO/imagefuncs/interactive.py +403 -0
  23. pyPLUTO/imagefuncs/legend.py +250 -0
  24. pyPLUTO/imagefuncs/plot.py +311 -0
  25. pyPLUTO/imagefuncs/range.py +242 -0
  26. pyPLUTO/imagefuncs/scatter.py +270 -0
  27. pyPLUTO/imagefuncs/set_axis.py +497 -0
  28. pyPLUTO/imagefuncs/streamplot.py +297 -0
  29. pyPLUTO/imagefuncs/zoom.py +428 -0
  30. pyPLUTO/imagemixin.py +259 -0
  31. pyPLUTO/imagestate.py +45 -0
  32. pyPLUTO/load.py +447 -0
  33. pyPLUTO/loadfuncs/baseloadtools.py +71 -0
  34. pyPLUTO/loadfuncs/codeselection.py +48 -0
  35. pyPLUTO/loadfuncs/defpluto.py +123 -0
  36. pyPLUTO/loadfuncs/descriptor.py +102 -0
  37. pyPLUTO/loadfuncs/findfiles.py +182 -0
  38. pyPLUTO/loadfuncs/findformat.py +245 -0
  39. pyPLUTO/loadfuncs/initload.py +203 -0
  40. pyPLUTO/loadfuncs/loadvars.py +227 -0
  41. pyPLUTO/loadfuncs/offsetdata.py +87 -0
  42. pyPLUTO/loadfuncs/offsetfluid.py +408 -0
  43. pyPLUTO/loadfuncs/read_files.py +213 -0
  44. pyPLUTO/loadfuncs/readdata.py +619 -0
  45. pyPLUTO/loadfuncs/readdata_old.py +567 -0
  46. pyPLUTO/loadfuncs/readdefplini.py +101 -0
  47. pyPLUTO/loadfuncs/readfluid.py +479 -0
  48. pyPLUTO/loadfuncs/readformat.py +277 -0
  49. pyPLUTO/loadfuncs/readgridalone.py +224 -0
  50. pyPLUTO/loadfuncs/readgridfile.py +255 -0
  51. pyPLUTO/loadfuncs/readgridout.py +451 -0
  52. pyPLUTO/loadfuncs/readpart.py +419 -0
  53. pyPLUTO/loadfuncs/readtab.py +105 -0
  54. pyPLUTO/loadfuncs/write_files.py +283 -0
  55. pyPLUTO/loadmixin.py +419 -0
  56. pyPLUTO/loadpart.py +233 -0
  57. pyPLUTO/loadstate.py +68 -0
  58. pyPLUTO/newload.py +81 -0
  59. pyPLUTO/pytools.py +145 -0
  60. pyPLUTO/toolfuncs/findlines.py +551 -0
  61. pyPLUTO/toolfuncs/fourier.py +149 -0
  62. pyPLUTO/toolfuncs/nabla.py +676 -0
  63. pyPLUTO/toolfuncs/parttools.py +152 -0
  64. pyPLUTO/toolfuncs/transform.py +638 -0
  65. pyPLUTO/utils/annotator.py +27 -0
  66. pyPLUTO/utils/inspector.py +145 -0
  67. pyPLUTO/utils/make_docstrings.py +3 -0
  68. py_pluto-1.1.4.dist-info/METADATA +218 -0
  69. py_pluto-1.1.4.dist-info/RECORD +73 -0
  70. py_pluto-1.1.4.dist-info/WHEEL +5 -0
  71. py_pluto-1.1.4.dist-info/entry_points.txt +2 -0
  72. py_pluto-1.1.4.dist-info/licenses/LICENSE +27 -0
  73. py_pluto-1.1.4.dist-info/top_level.txt +1 -0
pyPLUTO/image.py ADDED
@@ -0,0 +1,302 @@
1
+ """Image class. It plots the data."""
2
+
3
+ # ruff: noqa: ANN201 # noqa: RUF100
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Unpack
8
+
9
+ from pyPLUTO.amr import oplotbox
10
+ from pyPLUTO.imagefuncs.colorbar import ColorbarManager
11
+ from pyPLUTO.imagefuncs.contour import ContourManager
12
+ from pyPLUTO.imagefuncs.create_axes import CreateAxesManager
13
+ from pyPLUTO.imagefuncs.display import DisplayManager
14
+ from pyPLUTO.imagefuncs.figure import FigureManager
15
+ from pyPLUTO.imagefuncs.imagetools import ImageToolsManager
16
+ from pyPLUTO.imagefuncs.interactive import InteractiveManager
17
+ from pyPLUTO.imagefuncs.legend import LegendManager
18
+ from pyPLUTO.imagefuncs.plot import PlotManager
19
+ from pyPLUTO.imagefuncs.range import RangeManager
20
+ from pyPLUTO.imagefuncs.scatter import ScatterManager
21
+ from pyPLUTO.imagefuncs.set_axis import AxisManager
22
+ from pyPLUTO.imagefuncs.streamplot import StreamplotManager
23
+ from pyPLUTO.imagefuncs.zoom import ZoomManager
24
+ from pyPLUTO.imagemixin import ImageMixin
25
+ from pyPLUTO.imagestate import ImageState
26
+ from pyPLUTO.utils.annotator import AllKwargs
27
+ from pyPLUTO.utils.inspector import track_kwargs
28
+
29
+
30
+ class Image(ImageMixin):
31
+ """Description of the Image class.
32
+
33
+ The Image class is a facade for the different managers that handle the
34
+ various aspects of plotting, such as creating axes, displaying data, adding
35
+ legends, text, fieldlines, colorbars, and more. It provides a unified
36
+ interface for creating and managing plots in a figure. The attributes are
37
+ handled through the `ImageState` class, which is a dataclass that stores the
38
+ state of the image, such as the figure, axes, and other properties. The
39
+ `Image` class uses a mediator pattern to manage the interactions between the
40
+ different managers and the state.
41
+
42
+ """
43
+
44
+ @track_kwargs
45
+ def __init__(
46
+ self,
47
+ text: bool = True,
48
+ check: bool = True,
49
+ **kwargs: Unpack[AllKwargs],
50
+ ) -> None:
51
+ """Initialize the Image class.
52
+
53
+ Ihat creates a new figure and sets the LaTeX conditions, as well as the
54
+ matplotlib style. Every Image is associated to a figure object and only
55
+ one in order to avoid confusion between images and figures. If you want
56
+ to create multiple figures, you have to create multiple Image objects.
57
+
58
+ Returns
59
+ -------
60
+ - None
61
+
62
+ Parameters
63
+ ----------
64
+ - close: bool, default True
65
+ If True, the existing figure with the same window number is closed.
66
+ - fig: Figure | None, default None
67
+ The figure instance. If not None, the figure is used (only
68
+ if we need to associate an Image to an existing figure).
69
+ - figsize: list[float], default [8,5]
70
+ The figure size.
71
+ - fontsize: int, default 17
72
+ The font size.
73
+ - LaTeX: bool | str, default False
74
+ The LaTeX option. Is True is selected, the default LaTeX font
75
+ is used. If 'pgf' is selected, the pgf backend is used to save pdf
76
+ figures with minimal file size. If XeLaTeX is not installed and the
77
+ 'pgf' option is selected, the LaTeX option True is used as backup
78
+ strategy.
79
+ - numcolors: int, default 10
80
+ The number of colors in the colorscheme. The default number is 10,
81
+ but the full list contains 24 colors (+ black or white).
82
+ - nwin: int, default 1
83
+ The window number.
84
+ - oldcolor: bool, default False
85
+ if True, the old colors are used
86
+ - style: str, default 'default'
87
+ The style of the figure. Possible values are: 'seaborn', 'ggplot',
88
+ 'fivethirtyeight', 'bmh', 'grayscale', 'dark_background', 'classic',
89
+ etc.
90
+ - suptitle: str, default None
91
+ The super title of the figure.
92
+ - suptitlesize: str | int, default 'large'
93
+ The figure title size.
94
+ - tight: bool, default True
95
+ If True, the tight layout is used.
96
+ - withblack: bool, default False
97
+ If True, the black color is used as first color.
98
+ - withwhite: bool, default False
99
+ If True, the white color is used as first color.
100
+
101
+ ----
102
+
103
+ Examples
104
+ --------
105
+ - Example #1: create an empty image
106
+
107
+ >>> import pyPLUTO as pp
108
+ >>> I = pp.Image()
109
+
110
+ - Example #2: create an image with the pgf backend
111
+
112
+ >>> import pyPLUTO as pp
113
+ >>> I = pp.Image(LaTeX="pgf")
114
+
115
+ - Example #3: create an image with the LaTeX option True
116
+
117
+ >>> import pyPLUTO as pp
118
+ >>> I = pp.Image(LaTeX=True)
119
+
120
+ - Example #4: create an image with fixed size
121
+
122
+ >>> import pyPLUTO as pp
123
+ >>> I = pp.Image(figsize=[5, 5])
124
+
125
+ - Example #5: create an image with a title
126
+
127
+ >>> import pyPLUTO as pp
128
+ >>> I = pp.Image(suptitle="Title")
129
+
130
+ """
131
+ kwargs.pop("kwargscheck", check)
132
+
133
+ self.state = ImageState()
134
+
135
+ self.FigureManager = FigureManager(self.state, **kwargs)
136
+
137
+ # Initialize managers
138
+ self.AxisManager = AxisManager(self.state)
139
+ self.ColorbarManager = ColorbarManager(self.state)
140
+ self.ContourManager = ContourManager(self.state)
141
+ self.CreateAxesManager = CreateAxesManager(self.state)
142
+ self.DisplayManager = DisplayManager(self.state)
143
+ self.ImageToolsManager = ImageToolsManager(self.state)
144
+ self.InteractiveManager = InteractiveManager(self.state)
145
+ self.LegendManager = LegendManager(self.state)
146
+ self.PlotManager = PlotManager(self.state)
147
+ self.RangeManager = RangeManager(self.state)
148
+ self.ScatterManager = ScatterManager(self.state)
149
+ self.StreamplotManager = StreamplotManager(self.state)
150
+ self.ZoomManager = ZoomManager(self.state)
151
+
152
+ if text:
153
+ print(f"Image class created at nwin {self.nwin}")
154
+
155
+ def __str__(self) -> str:
156
+ """Print the Image class."""
157
+ return r"""
158
+ Image class.
159
+ It plots the data.
160
+
161
+ Image properties:
162
+ - Figure size (figsize)
163
+ - Window number (nwin)
164
+ - Number of subplots (nrow0 x ncol0)
165
+ - Global fontsize (fontsize)
166
+
167
+ Public methods available:
168
+
169
+ - create_axes
170
+ Adds a set of [nrow,ncol] subplots to the figure.
171
+ - colorbar
172
+ Places a colorbar in a subplot or next to a subplot.
173
+ - contour
174
+ Plots a contour plot in a subplot.
175
+ - display
176
+ Plots a 2D quantity in a subplot.
177
+ - interactive
178
+ Creates an interactive plot with a slider to change the data.
179
+ - legend
180
+ Places one legend in a subplot.
181
+ - set_axis
182
+ Changes the parameter of a specific subplot.
183
+ - plot
184
+ Plots one line in a subplot.
185
+ - savefig
186
+ Saves the figure in a file.
187
+ - scatter
188
+ Plots a scatter plot in a subplot.
189
+ - streamplot
190
+ Plots a stream plot in a subplot.
191
+ - text
192
+ Places the text in the figure or in a subplot.
193
+ - zoom
194
+ Creates an inset zoom region of a subplot.
195
+
196
+ Public attributes available:
197
+
198
+ - ax:
199
+ The list of relevant axes in the figure.
200
+ - fig
201
+ The figure associated to the image.
202
+ - fontsize
203
+ The fontsize in the figure.
204
+ - fontweight
205
+ The fontweight in the figure.
206
+ - nwin
207
+ The window number.
208
+ - tg
209
+ The tight layout of the figure.
210
+
211
+ Please do not use 'private' methods and attributes if not absolutely
212
+ necessary.
213
+ """
214
+
215
+ def __getattr__(self, name: str) -> object:
216
+ """Get the attribute of the Image class."""
217
+ return getattr(self.state, name)
218
+
219
+ def __setattr__(self, name: str, value: object) -> None:
220
+ """Set the attribute of the Image class."""
221
+ if name == "state" or not hasattr(self, "state"):
222
+ return super().__setattr__(name, value)
223
+ return setattr(self.state, name, value)
224
+
225
+ @property
226
+ def animate(self):
227
+ """Property for the animate method."""
228
+ return self.InteractiveManager.animate
229
+
230
+ @property
231
+ def colorbar(self):
232
+ """Property for the colorbar method."""
233
+ return self.ColorbarManager.colorbar
234
+
235
+ @property
236
+ def contour(self):
237
+ """Property for the contour method."""
238
+ return self.ContourManager.contour
239
+
240
+ @property
241
+ def create_axes(self):
242
+ """Property for the create_axes method."""
243
+ return self.CreateAxesManager.create_axes
244
+
245
+ @property
246
+ def display(self):
247
+ """Property for the display method."""
248
+ return self.DisplayManager.display
249
+
250
+ @property
251
+ def interactive(self):
252
+ """Property for the interactive method."""
253
+ return self.InteractiveManager.interactive
254
+
255
+ @property
256
+ def legend(self):
257
+ """Property for the legend method."""
258
+ return self.LegendManager.legend
259
+
260
+ @property
261
+ def plot(self):
262
+ """Property for the plot method."""
263
+ return self.PlotManager.plot
264
+
265
+ @property
266
+ def savefig(self):
267
+ """Property for the savefig method."""
268
+ return self.ImageToolsManager.savefig
269
+
270
+ @property
271
+ def scatter(self):
272
+ """Property for the scatter method."""
273
+ return self.ScatterManager.scatter
274
+
275
+ @property
276
+ def set_axis(self):
277
+ """Property for the set_axis method."""
278
+ return self.AxisManager.set_axis
279
+
280
+ @property
281
+ def show(self):
282
+ """Property for the show method."""
283
+ return self.ImageToolsManager.show
284
+
285
+ @property
286
+ def text(self):
287
+ """Property for the text method."""
288
+ return self.ImageToolsManager.text
289
+
290
+ @property
291
+ def streamplot(self):
292
+ """Property for the streamplot method."""
293
+ return self.StreamplotManager.streamplot
294
+
295
+ @property
296
+ def zoom(self):
297
+ """Property for the zoom method."""
298
+ return self.ZoomManager.zoom
299
+
300
+ def oplotbox(self, *args: object, **kwargs: object) -> None:
301
+ """Plot a box in the figure (AMR, WIP)."""
302
+ oplotbox(self, *args, **kwargs)
@@ -0,0 +1,240 @@
1
+ """Module providing colorbar management functionalities for image displays."""
2
+
3
+ import warnings
4
+ from typing import Any
5
+
6
+ from matplotlib.axes import Axes
7
+ from matplotlib.collections import LineCollection, PathCollection, QuadMesh
8
+ from matplotlib.contour import QuadContourSet
9
+ from mpl_toolkits.axes_grid1 import make_axes_locatable
10
+
11
+ from pyPLUTO.imagefuncs.imagetools import ImageToolsManager
12
+ from pyPLUTO.imagemixin import ImageMixin
13
+ from pyPLUTO.imagestate import ImageState
14
+ from pyPLUTO.utils.inspector import track_kwargs
15
+
16
+ # class MyKwargs(TypedDict, total=False):
17
+ # """TypedDict for keyword arguments."""
18
+ #
19
+ # clabel: str
20
+ # cpad: float
21
+ # cpos: str
22
+ # cticks: list[float] | None
23
+ # ctickslabels: list[str]
24
+ # extend: str
25
+ # extendrect: bool
26
+
27
+
28
+ class ColorbarManager(ImageMixin):
29
+ """Class to manage the colorbar in the image.
30
+
31
+ This class provides methods to create and manage colorbars in the image
32
+ class. It allows for customization of the colorbar's position, size,
33
+ ticks, labels, and other properties.
34
+ """
35
+
36
+ exposed_methods = ("colorbar",)
37
+
38
+ def __init__(self, state: ImageState) -> None:
39
+ """Initialize the ColorbarManager with the given state."""
40
+ self.state = state
41
+ self.ImageToolsManager = ImageToolsManager(state)
42
+
43
+ @track_kwargs
44
+ def colorbar(
45
+ self,
46
+ pcm: (
47
+ QuadMesh | PathCollection | LineCollection | QuadContourSet | None
48
+ ) = None,
49
+ axs: Axes | int | None = None,
50
+ cax: Axes | int | None = None,
51
+ check: bool = True,
52
+ **kwargs: Any,
53
+ ) -> None:
54
+ """Display a colorbar in a selected position.
55
+
56
+ The colorbar will be placed next to the axis axs. If the keyword cax is
57
+ enabled the colorbar is located in a specific axis, otherwise an axis
58
+ will be shrunk in order to place the colorbar.
59
+
60
+ Returns
61
+ -------
62
+ - None
63
+
64
+ Parameters
65
+ ----------
66
+ - axs: axis object, default None
67
+ The axes where the display that will be used for the colorbar is
68
+ located. If None, the last considered axis will be used.
69
+ - cax: axis object, default None
70
+ The axes where the colorbar should be placed. If None, the colorbar
71
+ will be placed next to the axis axs.
72
+ - clabel: str, default None
73
+ Sets the label of the colorbar.
74
+ - cpad: float, default 0.07
75
+ Fraction of original axes between colorbar and the axes (in case cax
76
+ is not defined).
77
+ - cpos: {'top','bottom','left','right'}, default 'right'
78
+ Sets the position of the colorbar.
79
+ - cticks: {[float], None}, default None
80
+ If enabled (and different from None), sets manually ticks on the
81
+ colorbar.
82
+ - ctickslabels: str, default None
83
+ If enabled, sets manually ticks labels on the colorbar.
84
+ - extend: {'neither','both','min','max'}, default 'neither'
85
+ Sets the extension of the triangular colorbar extension.
86
+ - extendrect: bool, default False
87
+ If True, the colorbar extension will be rectangular.
88
+ - pcm: QuadMesh | PathCollection | None, default None
89
+ The collection to be used for the colorbar. If None, the axs will be
90
+ used. If both pcm and axs are not None, pcm will be used.
91
+
92
+ ----
93
+
94
+ Examples
95
+ --------
96
+ - Example #1: create a standard colorbar on the right
97
+
98
+ >>> import pyPLUTO as pp
99
+ >>> I = pp.Image()
100
+ >>> I.display(var)
101
+ >>> I.colorbar()
102
+
103
+ - Example #2: create a colorbar in a different axis
104
+
105
+ >>> import pyPLUTO as pp
106
+ >>> I = pp.Image()
107
+ >>> ax = I.create_axes(ncol=2)
108
+ >>> I.display(var, ax=ax[0])
109
+ >>> I.colorbar(axs=ax[0], cax=ax[1])
110
+
111
+ - Example #3: create a set of 3 displays with a colorbar on the bottom.
112
+ Another colorbar is shown on the right of the topmost display
113
+
114
+ >>> import pyPLUTO as pp
115
+ >>> I = pp.Image()
116
+ >>> ax = I.create_axes(nrow=4)
117
+ >>> I.display(var1, ax=ax[0])
118
+ >>> I.colorbar(axs=ax[0])
119
+ >>> I.display(var2, ax=ax[1])
120
+ >>> I.display(var3, ax=ax[2])
121
+ >>> I.colorbar(axs=ax[2], cax=ax[3])
122
+
123
+ """
124
+ # Check parameters
125
+ if not isinstance(check, bool):
126
+ raise TypeError("check must be a boolean value.")
127
+
128
+ # If pcm and a source axes are selected, raise a warning and use pcm
129
+ if pcm is not None and axs is not None:
130
+ warn = "Both pcm and axs are not None, pcm will be used"
131
+ warnings.warn(warn, UserWarning, stacklevel=2)
132
+
133
+ # Standard check on the figure
134
+ if self.state.fig is None:
135
+ raise ValueError(
136
+ "No figure is present. Please create a figure first."
137
+ )
138
+
139
+ # Assign the source axis
140
+ axs = self._find_ax(pcm, axs, **kwargs)
141
+
142
+ # Select the keywords to position the colorbar
143
+ if pcm is None:
144
+ collection = axs.collections[0]
145
+ if not isinstance(collection, QuadMesh):
146
+ raise TypeError("First collection is not a QuadMesh")
147
+ pcm = collection
148
+ cpad = kwargs.get("cpad", 0.07)
149
+ cpos = kwargs.get("cpos", "right")
150
+ ccor = "vertical" if cpos in ["left", "right"] else "horizontal"
151
+
152
+ # Assign the colorbar axis, if cax is None create a new one
153
+ if cax is None:
154
+ divider = make_axes_locatable(axs)
155
+ cax = divider.append_axes(cpos, size="7%", pad=cpad)
156
+ else:
157
+ cax, naxc = self.ImageToolsManager.assign_ax(cax, **kwargs)
158
+ self.ImageToolsManager.hide_text(naxc, cax.texts)
159
+
160
+ # Check if the cax is an Axes instance
161
+ if not isinstance(cax, Axes):
162
+ raise TypeError("cax must be an Axes instance.")
163
+
164
+ # Place the colorbar
165
+ cbar = self.state.fig.colorbar(
166
+ pcm,
167
+ cax=cax,
168
+ label=kwargs.get("clabel", ""),
169
+ ticks=kwargs.get("cticks"),
170
+ orientation=ccor,
171
+ extend=kwargs.get("extend", "neither"),
172
+ extendrect=kwargs.get("extendrect", False),
173
+ )
174
+
175
+ # Set the tickslabels
176
+ ctkc = kwargs.get("ctickslabels", "Default")
177
+ if ctkc != "Default":
178
+ cbar.ax.set_yticklabels(ctkc)
179
+
180
+ # Ensure, if needed, the tight layout
181
+ if self.state.tight:
182
+ self.state.fig.tight_layout()
183
+
184
+ # End of function
185
+
186
+ @track_kwargs
187
+ def _find_ax(
188
+ self,
189
+ pcm: (
190
+ QuadMesh | PathCollection | LineCollection | QuadContourSet | None
191
+ ) = None,
192
+ axs: Axes | int | None = None,
193
+ **kwargs: Any,
194
+ ) -> Axes:
195
+ """Find and return the appropriate axis based on the input.
196
+
197
+ Parameters
198
+ ----------
199
+ - axs: Axes | int | None, default None
200
+ The axis or index of the axis to find. If None, the last used axis
201
+ will be returned.
202
+
203
+ Returns
204
+ -------
205
+ - Axes
206
+ The found axis object.
207
+
208
+ Raises
209
+ ------
210
+ - ValueError
211
+ If no figure is present or if the specified axis index is invalid.
212
+ - TypeError
213
+ If the provided axs parameter is not of type Axes or int.
214
+
215
+ """
216
+ # Standard check on the figure
217
+ if self.state.fig is None:
218
+ raise ValueError(
219
+ "No figure is present. Please create a figure first."
220
+ )
221
+ # Standard check on the figure
222
+ # Select the source axis
223
+ if pcm is not None:
224
+ # If the pcm is not none, use it and find the corresponding axes
225
+ if not isinstance(pcm.axes, Axes):
226
+ raise TypeError("Expected an Axes instance.")
227
+ axs = pcm.axes
228
+ elif axs is None:
229
+ # If axs is None, use the current axes
230
+ gca = self.state.fig.gca()
231
+ if not isinstance(gca, Axes):
232
+ raise TypeError("gca() did not return an Axes instance.")
233
+ axs = gca
234
+ axs, _ = self.ImageToolsManager.assign_ax(axs, **kwargs)
235
+ if self.state.fig is None:
236
+ raise ValueError(
237
+ "No figure is present. Please create a figure first."
238
+ )
239
+
240
+ return axs