qplotter 1.6.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.
- qplot/__init__.py +46 -0
- qplot/__main__.py +232 -0
- qplot/_auxiliary_paths.py +67 -0
- qplot/_build_info.json +1 -0
- qplot/_metadata.py +75 -0
- qplot/_repair.py +14 -0
- qplot/_shutdown_supervisor.py +4049 -0
- qplot/_version.py +12 -0
- qplot/_windows_shutdown_job.py +1166 -0
- qplot/configuration/__init__.py +3 -0
- qplot/configuration/config.py +578 -0
- qplot/configuration/config_schema.json +357 -0
- qplot/configuration/fonts.py +3 -0
- qplot/configuration/scripts.py +255 -0
- qplot/configuration/themes/__init__.py +13 -0
- qplot/configuration/themes/_base.py +592 -0
- qplot/configuration/themes/blank.py +17 -0
- qplot/configuration/themes/dark.py +54 -0
- qplot/configuration/themes/light.py +54 -0
- qplot/datahandling/LoadFromDB.py +224 -0
- qplot/datahandling/__init__.py +47 -0
- qplot/datahandling/_trusted_live_helper.py +812 -0
- qplot/datahandling/_trusted_live_protocol.py +1745 -0
- qplot/datahandling/database.py +2340 -0
- qplot/datahandling/dimensions.py +43 -0
- qplot/datahandling/file_identity.py +461 -0
- qplot/datahandling/parameter_data.py +149 -0
- qplot/datahandling/plot_progress.py +17 -0
- qplot/datahandling/qcodes_cache.py +277 -0
- qplot/datahandling/qcodes_compat.py +14 -0
- qplot/datahandling/readSQL.py +2442 -0
- qplot/datahandling/readonly.py +2566 -0
- qplot/datahandling/source_activity.py +21 -0
- qplot/datahandling/trusted_array_preview.py +148 -0
- qplot/datahandling/trusted_derived_cache.py +1605 -0
- qplot/datahandling/trusted_derived_rendering.py +876 -0
- qplot/datahandling/trusted_heatmap.py +246 -0
- qplot/datahandling/trusted_live.py +2808 -0
- qplot/datahandling/trusted_live_queries.py +5907 -0
- qplot/datahandling/trusted_live_service.py +1919 -0
- qplot/datahandling/trusted_live_supervisor.py +2161 -0
- qplot/datahandling/trusted_plot.py +283 -0
- qplot/datahandling/trusted_presentation.py +1065 -0
- qplot/datahandling/trusted_snapshot.py +1132 -0
- qplot/datahandling/trusted_work_coordinator.py +2157 -0
- qplot/datahandling/trusted_work_scheduler.py +1204 -0
- qplot/diagnostics.py +241 -0
- qplot/resources/testdata/qplot_test_db_01_10mb.csv +11 -0
- qplot/resources/testdata/qplot_test_db_02_25mb.csv +14 -0
- qplot/resources/testdata/qplot_test_db_03_50mb.csv +17 -0
- qplot/resources/testdata/qplot_test_db_04_100mb.csv +20 -0
- qplot/resources/testdata/qplot_test_db_05_250mb.csv +23 -0
- qplot/resources/testdata/qplot_test_db_06_500mb.csv +26 -0
- qplot/resources/testdata/qplot_test_db_07_1gb.csv +29 -0
- qplot/resources/testdata/qplot_test_db_08_5gb.csv +32 -0
- qplot/resources/testdata/qplot_test_db_09_10gb.csv +35 -0
- qplot/resources/testdata/qplot_test_db_10_30gb.csv +38 -0
- qplot/testdata.py +1804 -0
- qplot/tools/__init__.py +13 -0
- qplot/tools/finite_means.py +123 -0
- qplot/tools/general.py +139 -0
- qplot/tools/heatmap_geometry.py +391 -0
- qplot/tools/operation_registry.py +179 -0
- qplot/tools/plot_tools.py +650 -0
- qplot/tools/sample_statistics.py +111 -0
- qplot/tools/worker.py +2838 -0
- qplot/windows/__init__.py +9 -0
- qplot/windows/_colorbar.py +422 -0
- qplot/windows/_commands.py +523 -0
- qplot/windows/_config_persistence.py +123 -0
- qplot/windows/_database_actions.py +4933 -0
- qplot/windows/_dataset_handle.py +221 -0
- qplot/windows/_display_precision.py +48 -0
- qplot/windows/_dragdrop.py +70 -0
- qplot/windows/_export_paths.py +1167 -0
- qplot/windows/_help.py +210 -0
- qplot/windows/_mixed_appearance.py +303 -0
- qplot/windows/_mixed_plots.py +461 -0
- qplot/windows/_native_averaging.py +323 -0
- qplot/windows/_native_transforms.py +551 -0
- qplot/windows/_plot1d_snap.py +612 -0
- qplot/windows/_plot1d_traces.py +2236 -0
- qplot/windows/_plot2d_colorbar.py +1127 -0
- qplot/windows/_plot2d_colorbar_dialog.py +786 -0
- qplot/windows/_plot2d_layers.py +2291 -0
- qplot/windows/_plot2d_sweeps.py +712 -0
- qplot/windows/_plotWin.py +2082 -0
- qplot/windows/_plot_actions.py +2703 -0
- qplot/windows/_plot_annotation.py +186 -0
- qplot/windows/_plot_appearance.py +283 -0
- qplot/windows/_plot_axis_scaling.py +1521 -0
- qplot/windows/_plot_data_viewbox.py +94 -0
- qplot/windows/_plot_drop_axes.py +224 -0
- qplot/windows/_plot_export.py +1445 -0
- qplot/windows/_plot_feedback.py +112 -0
- qplot/windows/_plot_marquee.py +659 -0
- qplot/windows/_plot_refresh.py +950 -0
- qplot/windows/_plot_source_promotion.py +178 -0
- qplot/windows/_plot_state.py +237 -0
- qplot/windows/_plot_status.py +115 -0
- qplot/windows/_plot_tiling.py +35 -0
- qplot/windows/_plot_transform_labels.py +60 -0
- qplot/windows/_preferences.py +724 -0
- qplot/windows/_refresh_interval.py +172 -0
- qplot/windows/_run_controls.py +789 -0
- qplot/windows/_shortcuts.py +47 -0
- qplot/windows/_subplots/__init__.py +8 -0
- qplot/windows/_subplots/subplot1d.py +532 -0
- qplot/windows/_subplots/subplot2d.py +919 -0
- qplot/windows/_svg_export.py +209 -0
- qplot/windows/_trusted_derived_qt.py +1759 -0
- qplot/windows/_widgets/__init__.py +23 -0
- qplot/windows/_widgets/_run_formatting.py +387 -0
- qplot/windows/_widgets/details_tables.py +1119 -0
- qplot/windows/_widgets/dropbox.py +290 -0
- qplot/windows/_widgets/operations.py +460 -0
- qplot/windows/_widgets/preview.py +2997 -0
- qplot/windows/_widgets/run_list_items.py +488 -0
- qplot/windows/_widgets/toolbar.py +111 -0
- qplot/windows/_widgets/treeWidgets.py +2572 -0
- qplot/windows/_window_controls.py +414 -0
- qplot/windows/main.py +1632 -0
- qplot/windows/plot1d.py +313 -0
- qplot/windows/plot2d.py +1935 -0
- qplotter-1.6.0.dist-info/METADATA +322 -0
- qplotter-1.6.0.dist-info/RECORD +130 -0
- qplotter-1.6.0.dist-info/WHEEL +5 -0
- qplotter-1.6.0.dist-info/entry_points.txt +4 -0
- qplotter-1.6.0.dist-info/licenses/LICENSE +21 -0
- qplotter-1.6.0.dist-info/top_level.txt +1 -0
qplot/windows/plot1d.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
from PyQt6 import (
|
|
6
|
+
QtCore,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from qplot.datahandling.parameter_data import numeric_isfinite
|
|
10
|
+
|
|
11
|
+
from ._mixed_plots import MixedPlotMixin
|
|
12
|
+
from ._native_transforms import NativePlotDataItem
|
|
13
|
+
from ._plot1d_snap import Plot1DSnapMixin, _line_snap_data
|
|
14
|
+
from ._plot_data_viewbox import coordinate_viewbox
|
|
15
|
+
from ._plotWin import plotWidget
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class plot1d(Plot1DSnapMixin, MixedPlotMixin, plotWidget):
|
|
19
|
+
"""
|
|
20
|
+
Plot window for 1d Line plots.
|
|
21
|
+
Inherits and wraps several functions from qplot.windows._plotWin.plotWidget.
|
|
22
|
+
PlotWidget handles majority of set up, recommend to view first.
|
|
23
|
+
|
|
24
|
+
Key functions to see in plot1d:
|
|
25
|
+
initFrame
|
|
26
|
+
refreshPlot
|
|
27
|
+
|
|
28
|
+
Snap-to-trace behavior lives in qplot.windows._plot1d_snap.
|
|
29
|
+
Trace-control and secondary-axis behavior lives in
|
|
30
|
+
qplot.windows._plot1d_traces.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
operation_kind = "plot1d"
|
|
34
|
+
get_mergables = QtCore.pyqtSignal()
|
|
35
|
+
remove_dataset = QtCore.pyqtSignal([object])
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
*args: Any,
|
|
40
|
+
**kargs: Any,
|
|
41
|
+
) -> None:
|
|
42
|
+
self.mergable = None
|
|
43
|
+
self.line = None
|
|
44
|
+
self.right_vb = None
|
|
45
|
+
self.top_vb = None
|
|
46
|
+
self.top_right_vb = None
|
|
47
|
+
self.snap_to_trace_action = None
|
|
48
|
+
self.trace_label = None
|
|
49
|
+
self.snap_marker = None
|
|
50
|
+
self._snap_marker_view = None
|
|
51
|
+
super().__init__(*args, **kargs)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def initFrame(self) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Sets up the initial plot and starting data.
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
self.line = NativePlotDataItem(connect="all")
|
|
61
|
+
self.plot.addItem(self.line)
|
|
62
|
+
self._register_main_line()
|
|
63
|
+
|
|
64
|
+
# Wait for loader to finish to enure needed data is collected.
|
|
65
|
+
self.load_data()
|
|
66
|
+
self.show_status("Line plot ready; loading data...", 5000)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _marquee_viewbox(self) -> Any:
|
|
70
|
+
"""Use the main trace's coordinate system for selection and hit tests."""
|
|
71
|
+
|
|
72
|
+
line = self.__dict__.get("line")
|
|
73
|
+
get_viewbox = getattr(line, "getViewBox", None)
|
|
74
|
+
viewbox = get_viewbox() if callable(get_viewbox) else None
|
|
75
|
+
return coordinate_viewbox(viewbox) if viewbox is not None else super()._marquee_viewbox()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _marquee_zoom_viewbox(self, axis: Literal["x", "y"]) -> Any:
|
|
79
|
+
# Axis-pair ViewBoxes contain linked dimensions. Change the actual
|
|
80
|
+
# range owner so a later link update cannot undo the selection zoom.
|
|
81
|
+
line = self.__dict__.get("line")
|
|
82
|
+
return self._axis_scale_viewbox(self._axis_scale_axis_for_line(line, axis))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _snap_marquee_rect(self, rect: QtCore.QRectF) -> QtCore.QRectF:
|
|
86
|
+
"""
|
|
87
|
+
Snap marquee X edges to the spaces between plotted data points.
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
boundaries = self._marquee_x_boundaries()
|
|
91
|
+
if boundaries is None:
|
|
92
|
+
return rect
|
|
93
|
+
|
|
94
|
+
left_index = int(np.searchsorted(boundaries, rect.left(), side="right")) - 1
|
|
95
|
+
right_index = int(np.searchsorted(boundaries, rect.right(), side="left"))
|
|
96
|
+
left_index = min(max(left_index, 0), len(boundaries) - 2)
|
|
97
|
+
right_index = min(max(right_index, left_index + 1), len(boundaries) - 1)
|
|
98
|
+
|
|
99
|
+
return QtCore.QRectF(
|
|
100
|
+
boundaries[left_index],
|
|
101
|
+
rect.top(),
|
|
102
|
+
boundaries[right_index] - boundaries[left_index],
|
|
103
|
+
rect.height(),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _marquee_x_boundaries(self) -> npt.NDArray[np.float64] | None:
|
|
108
|
+
"""
|
|
109
|
+
Return X coordinates halfway between full trace sample points.
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
x_data = None
|
|
113
|
+
line = self.__dict__.get("line")
|
|
114
|
+
if line is not None:
|
|
115
|
+
data = _line_snap_data(line)
|
|
116
|
+
if data is not None:
|
|
117
|
+
x_data = data.x_view
|
|
118
|
+
|
|
119
|
+
if x_data is None:
|
|
120
|
+
x_data = self.__dict__.get("axis_data", {}).get("x")
|
|
121
|
+
|
|
122
|
+
if x_data is None:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
values = np.asarray(x_data, dtype=float)
|
|
126
|
+
values = np.unique(values[np.isfinite(values)])
|
|
127
|
+
if values.size == 0:
|
|
128
|
+
return None
|
|
129
|
+
if values.size == 1:
|
|
130
|
+
point = float(values[0])
|
|
131
|
+
return np.array([point - 0.5, point + 0.5])
|
|
132
|
+
|
|
133
|
+
gaps = np.diff(values)
|
|
134
|
+
mids = values[:-1] + gaps / 2
|
|
135
|
+
first = values[0] - gaps[0] / 2
|
|
136
|
+
last = values[-1] + gaps[-1] / 2
|
|
137
|
+
return np.concatenate(([first], mids, [last]))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _marquee_stats_text(self) -> str | None:
|
|
141
|
+
values = self._marquee_line_values()
|
|
142
|
+
if values is None:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
line = self.__dict__.get("line")
|
|
146
|
+
return self._format_marquee_stats_text(
|
|
147
|
+
f"{values.size} points",
|
|
148
|
+
values,
|
|
149
|
+
x_axis=self._axis_scale_axis_for_line(line, "x"),
|
|
150
|
+
y_axis=self._axis_scale_axis_for_line(line, "y"),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _marquee_line_values(self) -> npt.NDArray[np.float64] | None:
|
|
155
|
+
if self.marquee is None:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
line = self.__dict__.get("line")
|
|
159
|
+
if line is not None:
|
|
160
|
+
data = _line_snap_data(line)
|
|
161
|
+
if data is None:
|
|
162
|
+
return None
|
|
163
|
+
x_data, y_data, raw_y_data = data.x_view, data.y_view, data.y_raw
|
|
164
|
+
else:
|
|
165
|
+
axis_data = self.__dict__.get("axis_data", {})
|
|
166
|
+
x_data = axis_data.get("x")
|
|
167
|
+
y_data = axis_data.get("y")
|
|
168
|
+
raw_y_data = y_data
|
|
169
|
+
|
|
170
|
+
if x_data is None or y_data is None or raw_y_data is None:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
x_data = np.asarray(x_data, dtype=float)
|
|
174
|
+
y_data = np.asarray(y_data, dtype=float)
|
|
175
|
+
raw_y_data = np.asarray(raw_y_data)
|
|
176
|
+
count = min(x_data.size, y_data.size, raw_y_data.size)
|
|
177
|
+
if count == 0:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
rect = self.marquee.normalized()
|
|
181
|
+
x_data = x_data[:count]
|
|
182
|
+
y_data = y_data[:count]
|
|
183
|
+
raw_y_data = raw_y_data[:count]
|
|
184
|
+
mask = (
|
|
185
|
+
np.isfinite(x_data)
|
|
186
|
+
& np.isfinite(y_data)
|
|
187
|
+
& numeric_isfinite(raw_y_data)
|
|
188
|
+
& (x_data >= rect.left())
|
|
189
|
+
& (x_data <= rect.right())
|
|
190
|
+
& (y_data >= rect.top())
|
|
191
|
+
& (y_data <= rect.bottom())
|
|
192
|
+
)
|
|
193
|
+
if not np.any(mask):
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
return raw_y_data[mask]
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def refreshPlot(self, finished: bool = True, worker: Any | None = None) -> None:
|
|
200
|
+
"""
|
|
201
|
+
Updates plot based on data produced by the thread worker. Data is
|
|
202
|
+
assigned in plotWidget.refreshPlot, then all plot items are produced
|
|
203
|
+
here.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
finished : bool
|
|
208
|
+
In the event the worker had to abort, finished is False and refresh
|
|
209
|
+
is not ran.
|
|
210
|
+
"""
|
|
211
|
+
plot_worker = worker if worker is not None else self.worker
|
|
212
|
+
if not super().refreshPlot(finished, worker=worker):
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
clear_display = lambda: self.line.setData([], [])
|
|
217
|
+
if not self._refresh_publication_source_is_current(
|
|
218
|
+
plot_worker,
|
|
219
|
+
clear_display=clear_display,
|
|
220
|
+
):
|
|
221
|
+
return
|
|
222
|
+
if not self._has_plottable_line_data():
|
|
223
|
+
self.line.setData([], [])
|
|
224
|
+
if not self._refresh_publication_source_is_current(
|
|
225
|
+
plot_worker,
|
|
226
|
+
clear_display=clear_display,
|
|
227
|
+
):
|
|
228
|
+
return
|
|
229
|
+
self.show_status(
|
|
230
|
+
f"Waiting for plottable data for {self.param.name}...",
|
|
231
|
+
5000,
|
|
232
|
+
)
|
|
233
|
+
self.show_plot_state(
|
|
234
|
+
"Waiting for plottable data",
|
|
235
|
+
f"{self.param.name} has no finite line data yet.",
|
|
236
|
+
kind="empty",
|
|
237
|
+
)
|
|
238
|
+
self.trace_updated.emit()
|
|
239
|
+
if not self._refresh_publication_source_is_current(
|
|
240
|
+
plot_worker,
|
|
241
|
+
clear_display=clear_display,
|
|
242
|
+
):
|
|
243
|
+
return
|
|
244
|
+
display_synchronized = self._mark_display_synchronized(
|
|
245
|
+
plot_worker,
|
|
246
|
+
)
|
|
247
|
+
if (
|
|
248
|
+
getattr(plot_worker, "dataset_completed", None) is True
|
|
249
|
+
and not display_synchronized
|
|
250
|
+
):
|
|
251
|
+
return
|
|
252
|
+
if getattr(plot_worker, "_qplot_source_rejected", False):
|
|
253
|
+
clear_display()
|
|
254
|
+
return
|
|
255
|
+
self._commit_refresh_publication(plot_worker)
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
# Main line
|
|
259
|
+
self.line.setData(
|
|
260
|
+
x=self.axis_data["x"],
|
|
261
|
+
y=self.axis_data["y"],
|
|
262
|
+
)
|
|
263
|
+
if not self._refresh_publication_source_is_current(
|
|
264
|
+
plot_worker,
|
|
265
|
+
clear_display=clear_display,
|
|
266
|
+
):
|
|
267
|
+
return
|
|
268
|
+
if self.marquee is not None:
|
|
269
|
+
self.set_marquee_rect(self.marquee)
|
|
270
|
+
|
|
271
|
+
self.trace_updated.emit()
|
|
272
|
+
if not self._refresh_publication_source_is_current(
|
|
273
|
+
plot_worker,
|
|
274
|
+
clear_display=clear_display,
|
|
275
|
+
):
|
|
276
|
+
return
|
|
277
|
+
self.refresh_secondary_lines()
|
|
278
|
+
self.refresh_secondary_heatmaps()
|
|
279
|
+
if not self._refresh_publication_source_is_current(
|
|
280
|
+
plot_worker,
|
|
281
|
+
clear_display=clear_display,
|
|
282
|
+
):
|
|
283
|
+
return
|
|
284
|
+
self._mark_display_synchronized(plot_worker)
|
|
285
|
+
if getattr(plot_worker, "_qplot_source_rejected", False):
|
|
286
|
+
clear_display()
|
|
287
|
+
return
|
|
288
|
+
self._commit_refresh_publication(plot_worker, preview_ready=True)
|
|
289
|
+
finally:
|
|
290
|
+
overlay = self.__dict__.get("plot_state_overlay")
|
|
291
|
+
if overlay is not None:
|
|
292
|
+
overlay.finish(plot_worker)
|
|
293
|
+
if isinstance(
|
|
294
|
+
getattr(plot_worker, "_qplot_publication_snapshot", None),
|
|
295
|
+
dict,
|
|
296
|
+
):
|
|
297
|
+
if self._refresh_publication_source_is_current(
|
|
298
|
+
plot_worker,
|
|
299
|
+
clear_display=clear_display,
|
|
300
|
+
):
|
|
301
|
+
self._commit_refresh_publication(plot_worker)
|
|
302
|
+
plot_worker.running = False
|
|
303
|
+
self._ensure_refresh_monitor()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _has_plottable_line_data(self) -> bool:
|
|
307
|
+
x_data = np.asarray(self.axis_data.get("x", []), dtype=float)
|
|
308
|
+
y_data = np.asarray(self.axis_data.get("y", []), dtype=float)
|
|
309
|
+
count = min(x_data.size, y_data.size)
|
|
310
|
+
if count == 0:
|
|
311
|
+
return False
|
|
312
|
+
|
|
313
|
+
return bool(np.any(np.isfinite(x_data[:count]) & np.isfinite(y_data[:count])))
|