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.
Files changed (130) hide show
  1. qplot/__init__.py +46 -0
  2. qplot/__main__.py +232 -0
  3. qplot/_auxiliary_paths.py +67 -0
  4. qplot/_build_info.json +1 -0
  5. qplot/_metadata.py +75 -0
  6. qplot/_repair.py +14 -0
  7. qplot/_shutdown_supervisor.py +4049 -0
  8. qplot/_version.py +12 -0
  9. qplot/_windows_shutdown_job.py +1166 -0
  10. qplot/configuration/__init__.py +3 -0
  11. qplot/configuration/config.py +578 -0
  12. qplot/configuration/config_schema.json +357 -0
  13. qplot/configuration/fonts.py +3 -0
  14. qplot/configuration/scripts.py +255 -0
  15. qplot/configuration/themes/__init__.py +13 -0
  16. qplot/configuration/themes/_base.py +592 -0
  17. qplot/configuration/themes/blank.py +17 -0
  18. qplot/configuration/themes/dark.py +54 -0
  19. qplot/configuration/themes/light.py +54 -0
  20. qplot/datahandling/LoadFromDB.py +224 -0
  21. qplot/datahandling/__init__.py +47 -0
  22. qplot/datahandling/_trusted_live_helper.py +812 -0
  23. qplot/datahandling/_trusted_live_protocol.py +1745 -0
  24. qplot/datahandling/database.py +2340 -0
  25. qplot/datahandling/dimensions.py +43 -0
  26. qplot/datahandling/file_identity.py +461 -0
  27. qplot/datahandling/parameter_data.py +149 -0
  28. qplot/datahandling/plot_progress.py +17 -0
  29. qplot/datahandling/qcodes_cache.py +277 -0
  30. qplot/datahandling/qcodes_compat.py +14 -0
  31. qplot/datahandling/readSQL.py +2442 -0
  32. qplot/datahandling/readonly.py +2566 -0
  33. qplot/datahandling/source_activity.py +21 -0
  34. qplot/datahandling/trusted_array_preview.py +148 -0
  35. qplot/datahandling/trusted_derived_cache.py +1605 -0
  36. qplot/datahandling/trusted_derived_rendering.py +876 -0
  37. qplot/datahandling/trusted_heatmap.py +246 -0
  38. qplot/datahandling/trusted_live.py +2808 -0
  39. qplot/datahandling/trusted_live_queries.py +5907 -0
  40. qplot/datahandling/trusted_live_service.py +1919 -0
  41. qplot/datahandling/trusted_live_supervisor.py +2161 -0
  42. qplot/datahandling/trusted_plot.py +283 -0
  43. qplot/datahandling/trusted_presentation.py +1065 -0
  44. qplot/datahandling/trusted_snapshot.py +1132 -0
  45. qplot/datahandling/trusted_work_coordinator.py +2157 -0
  46. qplot/datahandling/trusted_work_scheduler.py +1204 -0
  47. qplot/diagnostics.py +241 -0
  48. qplot/resources/testdata/qplot_test_db_01_10mb.csv +11 -0
  49. qplot/resources/testdata/qplot_test_db_02_25mb.csv +14 -0
  50. qplot/resources/testdata/qplot_test_db_03_50mb.csv +17 -0
  51. qplot/resources/testdata/qplot_test_db_04_100mb.csv +20 -0
  52. qplot/resources/testdata/qplot_test_db_05_250mb.csv +23 -0
  53. qplot/resources/testdata/qplot_test_db_06_500mb.csv +26 -0
  54. qplot/resources/testdata/qplot_test_db_07_1gb.csv +29 -0
  55. qplot/resources/testdata/qplot_test_db_08_5gb.csv +32 -0
  56. qplot/resources/testdata/qplot_test_db_09_10gb.csv +35 -0
  57. qplot/resources/testdata/qplot_test_db_10_30gb.csv +38 -0
  58. qplot/testdata.py +1804 -0
  59. qplot/tools/__init__.py +13 -0
  60. qplot/tools/finite_means.py +123 -0
  61. qplot/tools/general.py +139 -0
  62. qplot/tools/heatmap_geometry.py +391 -0
  63. qplot/tools/operation_registry.py +179 -0
  64. qplot/tools/plot_tools.py +650 -0
  65. qplot/tools/sample_statistics.py +111 -0
  66. qplot/tools/worker.py +2838 -0
  67. qplot/windows/__init__.py +9 -0
  68. qplot/windows/_colorbar.py +422 -0
  69. qplot/windows/_commands.py +523 -0
  70. qplot/windows/_config_persistence.py +123 -0
  71. qplot/windows/_database_actions.py +4933 -0
  72. qplot/windows/_dataset_handle.py +221 -0
  73. qplot/windows/_display_precision.py +48 -0
  74. qplot/windows/_dragdrop.py +70 -0
  75. qplot/windows/_export_paths.py +1167 -0
  76. qplot/windows/_help.py +210 -0
  77. qplot/windows/_mixed_appearance.py +303 -0
  78. qplot/windows/_mixed_plots.py +461 -0
  79. qplot/windows/_native_averaging.py +323 -0
  80. qplot/windows/_native_transforms.py +551 -0
  81. qplot/windows/_plot1d_snap.py +612 -0
  82. qplot/windows/_plot1d_traces.py +2236 -0
  83. qplot/windows/_plot2d_colorbar.py +1127 -0
  84. qplot/windows/_plot2d_colorbar_dialog.py +786 -0
  85. qplot/windows/_plot2d_layers.py +2291 -0
  86. qplot/windows/_plot2d_sweeps.py +712 -0
  87. qplot/windows/_plotWin.py +2082 -0
  88. qplot/windows/_plot_actions.py +2703 -0
  89. qplot/windows/_plot_annotation.py +186 -0
  90. qplot/windows/_plot_appearance.py +283 -0
  91. qplot/windows/_plot_axis_scaling.py +1521 -0
  92. qplot/windows/_plot_data_viewbox.py +94 -0
  93. qplot/windows/_plot_drop_axes.py +224 -0
  94. qplot/windows/_plot_export.py +1445 -0
  95. qplot/windows/_plot_feedback.py +112 -0
  96. qplot/windows/_plot_marquee.py +659 -0
  97. qplot/windows/_plot_refresh.py +950 -0
  98. qplot/windows/_plot_source_promotion.py +178 -0
  99. qplot/windows/_plot_state.py +237 -0
  100. qplot/windows/_plot_status.py +115 -0
  101. qplot/windows/_plot_tiling.py +35 -0
  102. qplot/windows/_plot_transform_labels.py +60 -0
  103. qplot/windows/_preferences.py +724 -0
  104. qplot/windows/_refresh_interval.py +172 -0
  105. qplot/windows/_run_controls.py +789 -0
  106. qplot/windows/_shortcuts.py +47 -0
  107. qplot/windows/_subplots/__init__.py +8 -0
  108. qplot/windows/_subplots/subplot1d.py +532 -0
  109. qplot/windows/_subplots/subplot2d.py +919 -0
  110. qplot/windows/_svg_export.py +209 -0
  111. qplot/windows/_trusted_derived_qt.py +1759 -0
  112. qplot/windows/_widgets/__init__.py +23 -0
  113. qplot/windows/_widgets/_run_formatting.py +387 -0
  114. qplot/windows/_widgets/details_tables.py +1119 -0
  115. qplot/windows/_widgets/dropbox.py +290 -0
  116. qplot/windows/_widgets/operations.py +460 -0
  117. qplot/windows/_widgets/preview.py +2997 -0
  118. qplot/windows/_widgets/run_list_items.py +488 -0
  119. qplot/windows/_widgets/toolbar.py +111 -0
  120. qplot/windows/_widgets/treeWidgets.py +2572 -0
  121. qplot/windows/_window_controls.py +414 -0
  122. qplot/windows/main.py +1632 -0
  123. qplot/windows/plot1d.py +313 -0
  124. qplot/windows/plot2d.py +1935 -0
  125. qplotter-1.6.0.dist-info/METADATA +322 -0
  126. qplotter-1.6.0.dist-info/RECORD +130 -0
  127. qplotter-1.6.0.dist-info/WHEEL +5 -0
  128. qplotter-1.6.0.dist-info/entry_points.txt +4 -0
  129. qplotter-1.6.0.dist-info/licenses/LICENSE +21 -0
  130. qplotter-1.6.0.dist-info/top_level.txt +1 -0
@@ -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])))