plopp 25.3.0__py3-none-any.whl → 25.4.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.
- plopp/backends/common.py +10 -8
- plopp/backends/matplotlib/fast_image.py +203 -0
- plopp/backends/matplotlib/image.py +20 -235
- plopp/backends/matplotlib/line.py +71 -5
- plopp/backends/matplotlib/mesh_image.py +269 -0
- plopp/backends/matplotlib/scatter.py +36 -3
- plopp/backends/plotly/line.py +64 -2
- plopp/backends/pythreejs/mesh3d.py +18 -6
- plopp/backends/pythreejs/scatter3d.py +14 -6
- plopp/core/helpers.py +1 -7
- plopp/core/limits.py +9 -5
- plopp/data/factory.py +8 -1
- plopp/graphics/colormapper.py +1 -1
- plopp/plotting/common.py +10 -0
- plopp/widgets/checkboxes.py +29 -19
- plopp/widgets/slice.py +49 -61
- plopp/widgets/toolbar.py +2 -0
- {plopp-25.3.0.dist-info → plopp-25.4.0.dist-info}/METADATA +3 -2
- {plopp-25.3.0.dist-info → plopp-25.4.0.dist-info}/RECORD +22 -20
- {plopp-25.3.0.dist-info → plopp-25.4.0.dist-info}/WHEEL +1 -1
- {plopp-25.3.0.dist-info → plopp-25.4.0.dist-info/licenses}/LICENSE +0 -0
- {plopp-25.3.0.dist-info → plopp-25.4.0.dist-info}/top_level.txt +0 -0
plopp/backends/common.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
2
|
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
|
|
3
3
|
|
|
4
|
+
import uuid
|
|
4
5
|
from typing import Literal
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
@@ -98,15 +99,16 @@ def make_line_bbox(
|
|
|
98
99
|
The scale of the y-axis.
|
|
99
100
|
"""
|
|
100
101
|
line_x = data.coords[dim]
|
|
101
|
-
sel = slice(None)
|
|
102
|
-
if data.masks:
|
|
103
|
-
sel = ~merge_masks(data.masks)
|
|
104
|
-
if set(sel.dims) != set(data.data.dims):
|
|
105
|
-
sel = sc.broadcast(sel, sizes=data.data.sizes).copy()
|
|
106
|
-
line_y = data.data[sel]
|
|
107
102
|
if errorbars:
|
|
108
|
-
stddevs = sc.stddevs(data.data
|
|
109
|
-
line_y = sc.
|
|
103
|
+
stddevs = sc.stddevs(data.data)
|
|
104
|
+
line_y = sc.DataArray(
|
|
105
|
+
data=sc.concat(
|
|
106
|
+
[data.data - stddevs, data.data + stddevs], dim=uuid.uuid4().hex
|
|
107
|
+
),
|
|
108
|
+
masks=data.masks,
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
line_y = data
|
|
110
112
|
|
|
111
113
|
return BoundingBox(
|
|
112
114
|
**{**axis_bounds(('xmin', 'xmax'), line_x, xscale, pad=True)},
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
# Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
|
|
3
|
+
|
|
4
|
+
import uuid
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import scipp as sc
|
|
10
|
+
|
|
11
|
+
from ...core.utils import coord_as_bin_edges, scalar_to_string
|
|
12
|
+
from ...graphics.bbox import BoundingBox, axis_bounds
|
|
13
|
+
from ...graphics.colormapper import ColorMapper
|
|
14
|
+
from ..common import check_ndim
|
|
15
|
+
from .canvas import Canvas
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FastImage:
|
|
19
|
+
"""
|
|
20
|
+
Artist to represent two-dimensional data.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
canvas:
|
|
25
|
+
The canvas that will display the image.
|
|
26
|
+
colormapper:
|
|
27
|
+
The colormapper to use for the image.
|
|
28
|
+
data:
|
|
29
|
+
The initial data to create the image from.
|
|
30
|
+
artist_number:
|
|
31
|
+
The canvas keeps track of how many images have been added to it. This is unused
|
|
32
|
+
by the FastImage artist.
|
|
33
|
+
uid:
|
|
34
|
+
The unique identifier of the artist. If None, a random UUID is generated.
|
|
35
|
+
**kwargs:
|
|
36
|
+
Additional arguments are forwarded to Matplotlib's ``imshow``.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
canvas: Canvas,
|
|
42
|
+
colormapper: ColorMapper,
|
|
43
|
+
data: sc.DataArray,
|
|
44
|
+
artist_number: int,
|
|
45
|
+
uid: str | None = None,
|
|
46
|
+
**kwargs,
|
|
47
|
+
):
|
|
48
|
+
check_ndim(data, ndim=2, origin="FastImage")
|
|
49
|
+
self.uid = uid if uid is not None else uuid.uuid4().hex
|
|
50
|
+
self._canvas = canvas
|
|
51
|
+
self._colormapper = colormapper
|
|
52
|
+
self._ax = self._canvas.ax
|
|
53
|
+
self._data = data
|
|
54
|
+
|
|
55
|
+
string_labels = {}
|
|
56
|
+
self._bin_edge_coords = {}
|
|
57
|
+
for i, k in enumerate("yx"):
|
|
58
|
+
self._bin_edge_coords[k] = coord_as_bin_edges(
|
|
59
|
+
self._data, self._data.dims[i]
|
|
60
|
+
)
|
|
61
|
+
if self._data.coords[self._data.dims[i]].dtype == str:
|
|
62
|
+
string_labels[k] = self._data.coords[self._data.dims[i]]
|
|
63
|
+
|
|
64
|
+
self._xmin, self._xmax = self._bin_edge_coords["x"].values[[0, -1]]
|
|
65
|
+
self._ymin, self._ymax = self._bin_edge_coords["y"].values[[0, -1]]
|
|
66
|
+
self._dx = np.diff(self._bin_edge_coords["x"].values[:2])
|
|
67
|
+
self._dy = np.diff(self._bin_edge_coords["y"].values[:2])
|
|
68
|
+
|
|
69
|
+
# Calling imshow sets the aspect ratio to 'equal', which might not be what the
|
|
70
|
+
# user requested. We need to restore the original aspect ratio after making the
|
|
71
|
+
# image.
|
|
72
|
+
original_aspect = self._ax.get_aspect()
|
|
73
|
+
|
|
74
|
+
# Because imshow sets the aspect, it may generate warnings when the axes scales
|
|
75
|
+
# are log.
|
|
76
|
+
with warnings.catch_warnings():
|
|
77
|
+
warnings.filterwarnings(
|
|
78
|
+
"ignore",
|
|
79
|
+
category=UserWarning,
|
|
80
|
+
message="Attempt to set non-positive .* on a log-scaled axis",
|
|
81
|
+
)
|
|
82
|
+
self._image = self._ax.imshow(
|
|
83
|
+
self._data.values,
|
|
84
|
+
origin="lower",
|
|
85
|
+
extent=(self._xmin, self._xmax, self._ymin, self._ymax),
|
|
86
|
+
**({"interpolation": "nearest"} | kwargs),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
self._ax.set_aspect(original_aspect)
|
|
90
|
+
self._colormapper.add_artist(self.uid, self)
|
|
91
|
+
self._update_colors()
|
|
92
|
+
|
|
93
|
+
for xy, var in string_labels.items():
|
|
94
|
+
getattr(self._ax, f"set_{xy}ticks")(np.arange(float(var.shape[0])))
|
|
95
|
+
getattr(self._ax, f"set_{xy}ticklabels")(var.values)
|
|
96
|
+
|
|
97
|
+
self._canvas.register_format_coord(self.format_coord)
|
|
98
|
+
# We also hide the cursor hover values generated by the image, as values are
|
|
99
|
+
# included in our custom format_coord.
|
|
100
|
+
self._image.format_cursor_data = lambda _: ""
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def data(self):
|
|
104
|
+
"""
|
|
105
|
+
Get the image's data in a form that may have been tweaked, compared to the
|
|
106
|
+
original data, in the case of a two-dimensional coordinate.
|
|
107
|
+
"""
|
|
108
|
+
return self._data
|
|
109
|
+
|
|
110
|
+
def notify_artist(self, message: str) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Receive notification from the colormapper that its state has changed.
|
|
113
|
+
We thus need to update the colors of the image.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
message:
|
|
118
|
+
The message from the colormapper.
|
|
119
|
+
"""
|
|
120
|
+
self._update_colors()
|
|
121
|
+
|
|
122
|
+
def _update_colors(self):
|
|
123
|
+
"""
|
|
124
|
+
Update the image colors.
|
|
125
|
+
"""
|
|
126
|
+
rgba = self._colormapper.rgba(self.data)
|
|
127
|
+
self._image.set_data(rgba)
|
|
128
|
+
|
|
129
|
+
def update(self, new_values: sc.DataArray):
|
|
130
|
+
"""
|
|
131
|
+
Update image array with new values.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
new_values:
|
|
136
|
+
New data to update the image values from.
|
|
137
|
+
"""
|
|
138
|
+
check_ndim(new_values, ndim=2, origin="FastImage")
|
|
139
|
+
self._data = new_values
|
|
140
|
+
self._update_colors()
|
|
141
|
+
|
|
142
|
+
def format_coord(
|
|
143
|
+
self, xslice: tuple[str, sc.Variable], yslice: tuple[str, sc.Variable]
|
|
144
|
+
) -> str:
|
|
145
|
+
"""
|
|
146
|
+
Format the coordinates of the mouse pointer to show the value of the
|
|
147
|
+
data at that point.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
xslice:
|
|
152
|
+
Dimension and x coordinate of the mouse pointer, as slice parameters.
|
|
153
|
+
yslice:
|
|
154
|
+
Dimension and y coordinate of the mouse pointer, as slice parameters.
|
|
155
|
+
"""
|
|
156
|
+
ind_x = int((xslice[1].value - self._xmin) / self._dx)
|
|
157
|
+
ind_y = int((yslice[1].value - self._ymin) / self._dy)
|
|
158
|
+
try:
|
|
159
|
+
val = self._data[yslice[0], ind_y][xslice[0], ind_x]
|
|
160
|
+
prefix = self._data.name
|
|
161
|
+
if prefix:
|
|
162
|
+
prefix += ": "
|
|
163
|
+
return prefix + scalar_to_string(val)
|
|
164
|
+
except IndexError:
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def visible(self) -> bool:
|
|
169
|
+
"""
|
|
170
|
+
The visibility of the image.
|
|
171
|
+
"""
|
|
172
|
+
return self._image.get_visible()
|
|
173
|
+
|
|
174
|
+
@visible.setter
|
|
175
|
+
def visible(self, val: bool):
|
|
176
|
+
self._image.set_visible(val)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def opacity(self) -> float:
|
|
180
|
+
"""
|
|
181
|
+
The opacity of the image.
|
|
182
|
+
"""
|
|
183
|
+
return self._image.get_alpha()
|
|
184
|
+
|
|
185
|
+
@opacity.setter
|
|
186
|
+
def opacity(self, val: float):
|
|
187
|
+
self._image.set_alpha(val)
|
|
188
|
+
|
|
189
|
+
def bbox(self, xscale: Literal["linear", "log"], yscale: Literal["linear", "log"]):
|
|
190
|
+
"""
|
|
191
|
+
The bounding box of the image.
|
|
192
|
+
"""
|
|
193
|
+
return BoundingBox(
|
|
194
|
+
**{**axis_bounds(("xmin", "xmax"), self._bin_edge_coords["x"], xscale)},
|
|
195
|
+
**{**axis_bounds(("ymin", "ymax"), self._bin_edge_coords["y"], yscale)},
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
def remove(self):
|
|
199
|
+
"""
|
|
200
|
+
Remove the image artist from the canvas.
|
|
201
|
+
"""
|
|
202
|
+
self._image.remove()
|
|
203
|
+
self._colormapper.remove_artist(self.uid)
|
|
@@ -1,251 +1,36 @@
|
|
|
1
1
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
-
# Copyright (c)
|
|
2
|
+
# Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
|
|
3
3
|
|
|
4
|
-
import uuid
|
|
5
|
-
from typing import Literal
|
|
6
|
-
|
|
7
|
-
import numpy as np
|
|
8
4
|
import scipp as sc
|
|
9
5
|
|
|
10
|
-
from ...core.utils import coord_as_bin_edges, merge_masks, repeat, scalar_to_string
|
|
11
|
-
from ...graphics.bbox import BoundingBox, axis_bounds
|
|
12
|
-
from ...graphics.colormapper import ColorMapper
|
|
13
|
-
from ..common import check_ndim
|
|
14
6
|
from .canvas import Canvas
|
|
7
|
+
from .fast_image import FastImage
|
|
8
|
+
from .mesh_image import MeshImage
|
|
15
9
|
|
|
16
10
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _get_dims_of_1d_and_2d_coords(coords):
|
|
24
|
-
dim_2d = _find_dim_of_2d_coord(coords)
|
|
25
|
-
if dim_2d is None:
|
|
26
|
-
return None, None
|
|
27
|
-
axis_1d = 'xy'.replace(dim_2d[0], '')
|
|
28
|
-
dim_1d = (axis_1d, coords[axis_1d]['dim'])
|
|
29
|
-
return dim_1d, dim_2d
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _maybe_repeat_values(data, dim_1d, dim_2d):
|
|
33
|
-
if dim_2d is None:
|
|
34
|
-
return data
|
|
35
|
-
return repeat(data, dim=dim_1d[1], n=2)[dim_1d[1], :-1]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _from_data_array_to_pcolormesh(data, coords, dim_1d, dim_2d):
|
|
39
|
-
z = _maybe_repeat_values(data=data, dim_1d=dim_1d, dim_2d=dim_2d)
|
|
40
|
-
if dim_2d is None:
|
|
41
|
-
return coords['x'], coords['y'], z
|
|
42
|
-
|
|
43
|
-
# Broadcast 1d coord to 2d and repeat along 1d dim
|
|
44
|
-
# TODO: It may be more efficient here to first repeat and then broadcast, but
|
|
45
|
-
# the current order is simpler in implementation.
|
|
46
|
-
broadcasted_coord = repeat(
|
|
47
|
-
sc.broadcast(
|
|
48
|
-
coords[dim_1d[0]],
|
|
49
|
-
sizes={**coords[dim_2d[0]].sizes, **coords[dim_1d[0]].sizes},
|
|
50
|
-
).transpose(data.dims),
|
|
51
|
-
dim=dim_1d[1],
|
|
52
|
-
n=2,
|
|
53
|
-
)
|
|
54
|
-
# Repeat 2d coord along 1d dim
|
|
55
|
-
repeated_coord = repeat(coords[dim_2d[0]].transpose(data.dims), dim=dim_1d[1], n=2)
|
|
56
|
-
out = {dim_1d[0]: broadcasted_coord[dim_1d[1], 1:-1], dim_2d[0]: repeated_coord}
|
|
57
|
-
return out['x'], out['y'], z
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class Image:
|
|
11
|
+
def Image(
|
|
12
|
+
canvas: Canvas,
|
|
13
|
+
data: sc.DataArray,
|
|
14
|
+
**kwargs,
|
|
15
|
+
):
|
|
61
16
|
"""
|
|
62
|
-
|
|
17
|
+
Factory function to create an image artist.
|
|
18
|
+
If all the coordinates of the data are 1D and linearly spaced,
|
|
19
|
+
a `FastImage` is created.
|
|
20
|
+
Otherwise, a `MeshImage` is created.
|
|
63
21
|
|
|
64
22
|
Parameters
|
|
65
23
|
----------
|
|
66
24
|
canvas:
|
|
67
25
|
The canvas that will display the image.
|
|
68
|
-
colormapper:
|
|
69
|
-
The colormapper to use for the image.
|
|
70
26
|
data:
|
|
71
|
-
The
|
|
72
|
-
uid:
|
|
73
|
-
The unique identifier of the artist. If None, a random UUID is generated.
|
|
74
|
-
shading:
|
|
75
|
-
The shading to use for the ``pcolormesh``.
|
|
76
|
-
rasterized:
|
|
77
|
-
Rasterize the mesh/image if ``True``.
|
|
78
|
-
**kwargs:
|
|
79
|
-
Additional arguments are forwarded to Matplotlib's ``pcolormesh``.
|
|
27
|
+
The data to create the image from.
|
|
80
28
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
colormapper: ColorMapper,
|
|
86
|
-
data: sc.DataArray,
|
|
87
|
-
uid: str | None = None,
|
|
88
|
-
shading: str = 'auto',
|
|
89
|
-
rasterized: bool = True,
|
|
90
|
-
**kwargs,
|
|
29
|
+
if (canvas.ax.name != 'polar') and all(
|
|
30
|
+
(data.coords[dim].ndim < 2)
|
|
31
|
+
and ((data.coords[dim].dtype == str) or (sc.islinspace(data.coords[dim])))
|
|
32
|
+
for dim in data.dims
|
|
91
33
|
):
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
self._colormapper = colormapper
|
|
96
|
-
self._ax = self._canvas.ax
|
|
97
|
-
self._data = data
|
|
98
|
-
# Because all keyword arguments from the figure are forwarded to both the canvas
|
|
99
|
-
# and the line, we need to remove the arguments that belong to the canvas.
|
|
100
|
-
kwargs.pop('ax', None)
|
|
101
|
-
kwargs.pop('cax', None)
|
|
102
|
-
# An artist number is passed to the artist, but is unused for the image.
|
|
103
|
-
kwargs.pop('artist_number', None)
|
|
104
|
-
# If the grid is visible on the axes, we need to set that on again after we
|
|
105
|
-
# call pcolormesh, because that turns the grid off automatically.
|
|
106
|
-
# See https://github.com/matplotlib/matplotlib/issues/15600.
|
|
107
|
-
need_grid = self._ax.xaxis.get_gridlines()[0].get_visible()
|
|
108
|
-
|
|
109
|
-
to_dim_search = {}
|
|
110
|
-
string_labels = {}
|
|
111
|
-
bin_edge_coords = {}
|
|
112
|
-
self._data_with_bin_edges = sc.DataArray(data=self._data.data)
|
|
113
|
-
for i, k in enumerate('yx'):
|
|
114
|
-
to_dim_search[k] = {
|
|
115
|
-
'dim': self._data.dims[i],
|
|
116
|
-
'var': self._data.coords[self._data.dims[i]],
|
|
117
|
-
}
|
|
118
|
-
bin_edge_coords[k] = coord_as_bin_edges(self._data, self._data.dims[i])
|
|
119
|
-
self._data_with_bin_edges.coords[self._data.dims[i]] = bin_edge_coords[k]
|
|
120
|
-
if self._data.coords[self._data.dims[i]].dtype == str:
|
|
121
|
-
string_labels[k] = self._data.coords[self._data.dims[i]]
|
|
122
|
-
|
|
123
|
-
self._dim_1d, self._dim_2d = _get_dims_of_1d_and_2d_coords(to_dim_search)
|
|
124
|
-
self._mesh = None
|
|
125
|
-
|
|
126
|
-
x, y, z = _from_data_array_to_pcolormesh(
|
|
127
|
-
data=self._data.data,
|
|
128
|
-
coords=bin_edge_coords,
|
|
129
|
-
dim_1d=self._dim_1d,
|
|
130
|
-
dim_2d=self._dim_2d,
|
|
131
|
-
)
|
|
132
|
-
self._mesh = self._ax.pcolormesh(
|
|
133
|
-
x.values,
|
|
134
|
-
y.values,
|
|
135
|
-
z.values,
|
|
136
|
-
shading=shading,
|
|
137
|
-
rasterized=rasterized,
|
|
138
|
-
**kwargs,
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
self._colormapper.add_artist(self.uid, self)
|
|
142
|
-
self._mesh.set_array(None)
|
|
143
|
-
self._update_colors()
|
|
144
|
-
|
|
145
|
-
for xy, var in string_labels.items():
|
|
146
|
-
getattr(self._ax, f'set_{xy}ticks')(np.arange(float(var.shape[0])))
|
|
147
|
-
getattr(self._ax, f'set_{xy}ticklabels')(var.values)
|
|
148
|
-
|
|
149
|
-
if need_grid:
|
|
150
|
-
self._ax.grid(True)
|
|
151
|
-
|
|
152
|
-
self._canvas.register_format_coord(self.format_coord)
|
|
153
|
-
|
|
154
|
-
@property
|
|
155
|
-
def data(self):
|
|
156
|
-
"""
|
|
157
|
-
Get the Mesh's data in a form that may have been tweaked, compared to the
|
|
158
|
-
original data, in the case of a two-dimensional coordinate.
|
|
159
|
-
"""
|
|
160
|
-
out = sc.DataArray(
|
|
161
|
-
data=_maybe_repeat_values(
|
|
162
|
-
data=self._data.data, dim_1d=self._dim_1d, dim_2d=self._dim_2d
|
|
163
|
-
)
|
|
164
|
-
)
|
|
165
|
-
if self._data.masks:
|
|
166
|
-
out.masks['one_mask'] = _maybe_repeat_values(
|
|
167
|
-
data=sc.broadcast(
|
|
168
|
-
merge_masks(self._data.masks),
|
|
169
|
-
dims=self._data.dims,
|
|
170
|
-
shape=self._data.shape,
|
|
171
|
-
),
|
|
172
|
-
dim_1d=self._dim_1d,
|
|
173
|
-
dim_2d=self._dim_2d,
|
|
174
|
-
)
|
|
175
|
-
return out
|
|
176
|
-
|
|
177
|
-
def notify_artist(self, message: str) -> None:
|
|
178
|
-
"""
|
|
179
|
-
Receive notification from the colormapper that its state has changed.
|
|
180
|
-
We thus need to update the colors of the mesh.
|
|
181
|
-
|
|
182
|
-
Parameters
|
|
183
|
-
----------
|
|
184
|
-
message:
|
|
185
|
-
The message from the colormapper.
|
|
186
|
-
"""
|
|
187
|
-
self._update_colors()
|
|
188
|
-
|
|
189
|
-
def _update_colors(self):
|
|
190
|
-
"""
|
|
191
|
-
Update the mesh colors.
|
|
192
|
-
"""
|
|
193
|
-
rgba = self._colormapper.rgba(self.data)
|
|
194
|
-
self._mesh.set_facecolors(rgba.reshape(np.prod(rgba.shape[:-1]), 4))
|
|
195
|
-
|
|
196
|
-
def update(self, new_values: sc.DataArray):
|
|
197
|
-
"""
|
|
198
|
-
Update image array with new values.
|
|
199
|
-
|
|
200
|
-
Parameters
|
|
201
|
-
----------
|
|
202
|
-
new_values:
|
|
203
|
-
New data to update the mesh values from.
|
|
204
|
-
"""
|
|
205
|
-
check_ndim(new_values, ndim=2, origin='Image')
|
|
206
|
-
self._data = new_values
|
|
207
|
-
self._data_with_bin_edges.data = new_values.data
|
|
208
|
-
self._update_colors()
|
|
209
|
-
|
|
210
|
-
def format_coord(
|
|
211
|
-
self, xslice: tuple[str, sc.Variable], yslice: tuple[str, sc.Variable]
|
|
212
|
-
) -> str:
|
|
213
|
-
"""
|
|
214
|
-
Format the coordinates of the mouse pointer to show the value of the
|
|
215
|
-
data at that point.
|
|
216
|
-
|
|
217
|
-
Parameters
|
|
218
|
-
----------
|
|
219
|
-
xslice:
|
|
220
|
-
Dimension and x coordinate of the mouse pointer, as slice parameters.
|
|
221
|
-
yslice:
|
|
222
|
-
Dimension and y coordinate of the mouse pointer, as slice parameters.
|
|
223
|
-
"""
|
|
224
|
-
try:
|
|
225
|
-
val = self._data_with_bin_edges[yslice][xslice]
|
|
226
|
-
prefix = self._data.name
|
|
227
|
-
if prefix:
|
|
228
|
-
prefix += ': '
|
|
229
|
-
return prefix + scalar_to_string(val)
|
|
230
|
-
except (IndexError, RuntimeError):
|
|
231
|
-
return None
|
|
232
|
-
|
|
233
|
-
def bbox(self, xscale: Literal['linear', 'log'], yscale: Literal['linear', 'log']):
|
|
234
|
-
"""
|
|
235
|
-
The bounding box of the image.
|
|
236
|
-
"""
|
|
237
|
-
ydim, xdim = self._data.dims
|
|
238
|
-
image_x = self._data_with_bin_edges.coords[xdim]
|
|
239
|
-
image_y = self._data_with_bin_edges.coords[ydim]
|
|
240
|
-
|
|
241
|
-
return BoundingBox(
|
|
242
|
-
**{**axis_bounds(('xmin', 'xmax'), image_x, xscale)},
|
|
243
|
-
**{**axis_bounds(('ymin', 'ymax'), image_y, yscale)},
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
def remove(self):
|
|
247
|
-
"""
|
|
248
|
-
Remove the image artist from the canvas.
|
|
249
|
-
"""
|
|
250
|
-
self._mesh.remove()
|
|
251
|
-
self._colormapper.remove_artist(self.uid)
|
|
34
|
+
return FastImage(canvas=canvas, data=data, **kwargs)
|
|
35
|
+
else:
|
|
36
|
+
return MeshImage(canvas=canvas, data=data, **kwargs)
|
|
@@ -58,9 +58,6 @@ class Line:
|
|
|
58
58
|
self._canvas = canvas
|
|
59
59
|
self._ax = self._canvas.ax
|
|
60
60
|
self._data = data
|
|
61
|
-
# Because all keyword arguments from the figure are forwarded to both the canvas
|
|
62
|
-
# and the line, we need to remove the arguments that belong to the canvas.
|
|
63
|
-
kwargs.pop('ax', None)
|
|
64
61
|
|
|
65
62
|
line_args = parse_dicts_in_kwargs(kwargs, name=data.name)
|
|
66
63
|
|
|
@@ -191,20 +188,89 @@ class Line:
|
|
|
191
188
|
self._canvas.draw()
|
|
192
189
|
|
|
193
190
|
@property
|
|
194
|
-
def color(self):
|
|
191
|
+
def color(self) -> str:
|
|
195
192
|
"""
|
|
196
193
|
The line color.
|
|
197
194
|
"""
|
|
198
195
|
return self._line.get_color()
|
|
199
196
|
|
|
200
197
|
@color.setter
|
|
201
|
-
def color(self, val):
|
|
198
|
+
def color(self, val: str):
|
|
202
199
|
self._line.set_color(val)
|
|
203
200
|
if self._error is not None:
|
|
204
201
|
for artist in self._error.get_children():
|
|
205
202
|
artist.set_color(val)
|
|
206
203
|
self._canvas.draw()
|
|
207
204
|
|
|
205
|
+
@property
|
|
206
|
+
def style(self) -> str:
|
|
207
|
+
"""
|
|
208
|
+
The line style.
|
|
209
|
+
"""
|
|
210
|
+
return self._line.get_linestyle()
|
|
211
|
+
|
|
212
|
+
@style.setter
|
|
213
|
+
def style(self, val: str):
|
|
214
|
+
self._line.set_linestyle(val)
|
|
215
|
+
self._canvas.draw()
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def width(self) -> float:
|
|
219
|
+
"""
|
|
220
|
+
The line width.
|
|
221
|
+
"""
|
|
222
|
+
return self._line.get_linewidth()
|
|
223
|
+
|
|
224
|
+
@width.setter
|
|
225
|
+
def width(self, val: float):
|
|
226
|
+
self._line.set_linewidth(val)
|
|
227
|
+
self._canvas.draw()
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def marker(self) -> str:
|
|
231
|
+
"""
|
|
232
|
+
The line marker.
|
|
233
|
+
"""
|
|
234
|
+
return self._line.get_marker()
|
|
235
|
+
|
|
236
|
+
@marker.setter
|
|
237
|
+
def marker(self, val: str):
|
|
238
|
+
self._line.set_marker(val)
|
|
239
|
+
self._mask.set_marker(val)
|
|
240
|
+
self._canvas.draw()
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def visible(self) -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Whether the line is visible.
|
|
246
|
+
"""
|
|
247
|
+
return self._line.get_visible()
|
|
248
|
+
|
|
249
|
+
@visible.setter
|
|
250
|
+
def visible(self, val: bool):
|
|
251
|
+
self._line.set_visible(val)
|
|
252
|
+
self._mask.set_visible(val)
|
|
253
|
+
if self._error is not None:
|
|
254
|
+
for artist in self._error.get_children():
|
|
255
|
+
artist.set_visible(val)
|
|
256
|
+
self._canvas.draw()
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def opacity(self) -> float:
|
|
260
|
+
"""
|
|
261
|
+
The line opacity.
|
|
262
|
+
"""
|
|
263
|
+
return self._line.get_alpha()
|
|
264
|
+
|
|
265
|
+
@opacity.setter
|
|
266
|
+
def opacity(self, val: float):
|
|
267
|
+
self._line.set_alpha(val)
|
|
268
|
+
self._mask.set_alpha(val)
|
|
269
|
+
if self._error is not None:
|
|
270
|
+
for artist in self._error.get_children():
|
|
271
|
+
artist.set_alpha(val)
|
|
272
|
+
self._canvas.draw()
|
|
273
|
+
|
|
208
274
|
def bbox(
|
|
209
275
|
self, xscale: Literal['linear', 'log'], yscale: Literal['linear', 'log']
|
|
210
276
|
) -> BoundingBox:
|