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