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,239 @@
1
+ from time import perf_counter
2
+ from typing import Any
3
+
4
+ from PyQt6 import QtCore
5
+ from PyQt6 import QtWidgets as qtw
6
+
7
+ from qplot.datahandling.plot_progress import PlotProgress
8
+
9
+
10
+ class PlotStateOverlay(QtCore.QObject):
11
+ """
12
+ Lightweight status overlay shown inside a plot widget.
13
+
14
+ The status bar is easy to miss while a blank plot is loading. This overlay
15
+ keeps transient plot states visible in the plot area without intercepting
16
+ mouse interaction once it is hidden.
17
+ """
18
+
19
+ _styles: dict[str, str] = {
20
+ "info": (
21
+ "background-color: rgba(250, 252, 255, 235);"
22
+ "border: 1px solid rgba(119, 135, 153, 190);"
23
+ "color: #1f2933;"
24
+ ),
25
+ "loading": (
26
+ "background-color: rgba(246, 251, 255, 238);"
27
+ "border: 1px solid rgba(70, 130, 180, 190);"
28
+ "color: #102a43;"
29
+ ),
30
+ "empty": (
31
+ "background-color: rgba(255, 251, 235, 238);"
32
+ "border: 1px solid rgba(180, 132, 35, 190);"
33
+ "color: #3b2f12;"
34
+ ),
35
+ "error": (
36
+ "background-color: rgba(255, 245, 245, 240);"
37
+ "border: 1px solid rgba(190, 72, 72, 200);"
38
+ "color: #3b0d0c;"
39
+ ),
40
+ }
41
+
42
+ def __init__(self, target: qtw.QWidget) -> None:
43
+ super().__init__(target)
44
+ self.owner: qtw.QWidget = target
45
+ target_obj: Any = target
46
+ viewport = target_obj.viewport() if hasattr(target_obj, "viewport") else None
47
+ self.target: qtw.QWidget = viewport or target
48
+
49
+ self.frame = qtw.QFrame(self.target)
50
+ self.frame.setObjectName("plotStateOverlay")
51
+ self.frame.setAttribute(QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
52
+ self.frame.setAutoFillBackground(False)
53
+
54
+ layout = qtw.QVBoxLayout(self.frame)
55
+ layout.setContentsMargins(14, 10, 14, 10)
56
+ layout.setSpacing(4)
57
+
58
+ self.title_label = qtw.QLabel(self.frame)
59
+ self.title_label.setObjectName("plotStateOverlayTitle")
60
+ title_font = self.title_label.font()
61
+ title_font.setBold(True)
62
+ self.title_label.setFont(title_font)
63
+ self.title_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
64
+ layout.addWidget(self.title_label)
65
+
66
+ self.detail_label = qtw.QLabel(self.frame)
67
+ self.detail_label.setObjectName("plotStateOverlayDetail")
68
+ self.detail_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
69
+ self.detail_label.setWordWrap(True)
70
+ layout.addWidget(self.detail_label)
71
+
72
+ self.progress_bar = qtw.QProgressBar(self.frame)
73
+ self.progress_bar.setAccessibleName("Plot loading progress for the current stage")
74
+ self.progress_bar.setMinimumWidth(260)
75
+ self.progress_bar.setToolTip(
76
+ "Progress within the named stage, not total loading time. Scan progress "
77
+ "covers the captured source range, including partial array records."
78
+ )
79
+ layout.addWidget(self.progress_bar)
80
+ self.elapsed_label = qtw.QLabel(self.frame)
81
+ self.elapsed_label.setObjectName("plotStateOverlayElapsed")
82
+ self.elapsed_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
83
+ self.elapsed_label.setAccessibleName("Elapsed plot loading time")
84
+ layout.addWidget(self.elapsed_label)
85
+ self._worker: Any = None
86
+ self._last_progress: PlotProgress | None = None
87
+ self._started_at: float | None = None
88
+ self._rendering = False
89
+ self.progress_timer = QtCore.QTimer(self)
90
+ self.progress_timer.setInterval(100)
91
+ self.progress_timer.timeout.connect(self._poll_progress)
92
+
93
+ self.target.installEventFilter(self)
94
+ if self.owner is not self.target:
95
+ self.owner.installEventFilter(self)
96
+ self.hide()
97
+
98
+ def show(
99
+ self,
100
+ title: object,
101
+ detail: object | None = None,
102
+ kind: str = "info",
103
+ ) -> None:
104
+ if kind != "loading":
105
+ self._stop_progress()
106
+ self.title_label.setText(str(title or ""))
107
+ self.detail_label.setText(str(detail or ""))
108
+ self.detail_label.setVisible(bool(detail))
109
+ self.frame.setStyleSheet(self._stylesheet(kind))
110
+ self._sync_geometry()
111
+ self.frame.show()
112
+ self.frame.raise_()
113
+
114
+ def hide(self) -> None:
115
+ self._stop_progress()
116
+ self.frame.hide()
117
+
118
+ def track(self, worker: Any) -> None:
119
+ """Poll one latest value, never queue an event per reader chunk."""
120
+ self._worker = worker
121
+ self._last_progress = None
122
+ self._started_at = getattr(worker, "started_at", perf_counter())
123
+ self._rendering = False
124
+ self._poll_progress()
125
+ if self._worker is worker:
126
+ self.progress_timer.start()
127
+
128
+ def _stop_progress(self) -> None:
129
+ self.progress_timer.stop()
130
+ self._worker = None
131
+ self._last_progress = None
132
+ self._started_at = None
133
+ self._rendering = False
134
+ self.progress_bar.hide()
135
+ self.elapsed_label.hide()
136
+
137
+ def _update_elapsed(self) -> None:
138
+ if self._started_at is None:
139
+ return
140
+ seconds = max(0, int(perf_counter() - self._started_at))
141
+ minutes, seconds = divmod(seconds, 60)
142
+ hours, minutes = divmod(minutes, 60)
143
+ duration = (f"{hours}h {minutes:02d}m {seconds:02d}s" if hours else
144
+ f"{minutes}m {seconds:02d}s" if minutes else f"{seconds}s")
145
+ text = f"Elapsed: {duration}"
146
+ if self.elapsed_label.text() != text:
147
+ self.elapsed_label.setText(text)
148
+ self.elapsed_label.show()
149
+
150
+ def _poll_progress(self) -> None:
151
+ worker = self._worker
152
+ if worker is None:
153
+ return
154
+ if worker.is_cancelled():
155
+ self.show("Plot load cancelled")
156
+ return
157
+ self._update_elapsed()
158
+ if self._rendering:
159
+ return
160
+ progress = worker.progress
161
+ if progress == self._last_progress:
162
+ return
163
+ self._last_progress = progress
164
+ self.detail_label.setText(progress.title)
165
+ self.detail_label.show()
166
+ if progress.total is None or progress.total <= 0:
167
+ self.progress_bar.setRange(0, 0)
168
+ self.progress_bar.setFormat("")
169
+ else:
170
+ self.progress_bar.setRange(0, 1000)
171
+ self.progress_bar.setValue(min(1000, max(0, progress.completed * 1000 // progress.total)))
172
+ self.progress_bar.setFormat(f"%p% of {progress.unit}")
173
+ self.progress_bar.show()
174
+ self._sync_geometry()
175
+
176
+ def rendering(self, worker: Any) -> None:
177
+ if worker is not self._worker:
178
+ return
179
+ self._rendering = True
180
+ self._update_elapsed()
181
+ self.detail_label.setText(PlotProgress("Rendering plot", stage=2).title)
182
+ self.progress_bar.setRange(0, 0)
183
+ self.progress_bar.setFormat("")
184
+
185
+ def finish(self, worker: Any) -> None:
186
+ if worker is self._worker:
187
+ # Only a loading state is removed. Empty/error explanations stay.
188
+ self.hide()
189
+
190
+ def eventFilter(
191
+ self,
192
+ source: QtCore.QObject | None,
193
+ event: QtCore.QEvent | None,
194
+ ) -> bool:
195
+ if (
196
+ event is not None
197
+ and (source is self.owner or source is self.target)
198
+ and event.type() in (QtCore.QEvent.Type.Resize, QtCore.QEvent.Type.Show)
199
+ ):
200
+ self._sync_geometry()
201
+ return super().eventFilter(source, event)
202
+
203
+ def _stylesheet(self, kind: str) -> str:
204
+ panel_style = self._styles.get(kind, self._styles["info"])
205
+ return (
206
+ "QFrame#plotStateOverlay {"
207
+ f"{panel_style}"
208
+ "border-radius: 6px;"
209
+ "}"
210
+ "QLabel#plotStateOverlayTitle {"
211
+ "background: transparent;"
212
+ "font-size: 10pt;"
213
+ "}"
214
+ "QLabel#plotStateOverlayDetail {"
215
+ "background: transparent;"
216
+ "font-size: 8pt;"
217
+ "}"
218
+ "QLabel#plotStateOverlayElapsed {"
219
+ "background: transparent;"
220
+ "font-size: 8pt;"
221
+ "}"
222
+ )
223
+
224
+ def _sync_geometry(self) -> None:
225
+ if not self.frame.isVisible() and not self.title_label.text():
226
+ return
227
+
228
+ target_rect = self.target.rect()
229
+ margin = 24
230
+ max_width = max(160, min(420, target_rect.width() - (2 * margin)))
231
+ self.frame.setMaximumWidth(max_width)
232
+ self.frame.adjustSize()
233
+
234
+ size = self.frame.sizeHint()
235
+ width = min(max_width, max(160, size.width()))
236
+ height = size.height()
237
+ x = target_rect.left() + max(margin, (target_rect.width() - width) // 2)
238
+ y = target_rect.top() + max(margin, (target_rect.height() - height) // 2)
239
+ self.frame.setGeometry(x, y, width, height)
@@ -0,0 +1,60 @@
1
+ """Display quantities for PyQtGraph's native PlotDataItem mappings.
2
+
3
+ Input parameters describe samples after qPlot operations. These immutable
4
+ descriptions never replace measurement or operation metadata.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class AxisQuantity:
13
+ label: str
14
+ unit: str
15
+
16
+ @classmethod
17
+ def from_parameter(cls, parameter: Any) -> "AxisQuantity":
18
+ return cls(
19
+ str(getattr(parameter, "label", None) or getattr(parameter, "name", "")),
20
+ str(getattr(parameter, "unit", "") or ""),
21
+ )
22
+
23
+
24
+ def _unit_operand(unit: str) -> str:
25
+ return f"({unit})" if any(char in unit for char in "/ *") else unit
26
+
27
+
28
+ def _quotient_unit(numerator: str, denominator: str) -> str:
29
+ if not denominator:
30
+ return numerator
31
+ return f"{_unit_operand(numerator) if numerator else '1'}/{_unit_operand(denominator)}"
32
+
33
+
34
+ def native_axis_quantities(
35
+ x_parameter: Any, y_parameter: Any, options: dict[str, Any],
36
+ ) -> dict[str, AxisQuantity]:
37
+ """Mirror native mapping order, including mappings that reset to input.
38
+
39
+ PyQtGraph 0.14 subtracts the mean, then computes abs(rfft(y)/N).
40
+ Derivative mode replaces Y with diff(input Y)/diff(input X), retaining
41
+ the current X. Phase-map mode replaces both coordinates with input Y
42
+ and its derivative. Log ticking still describes these physical quantities.
43
+ """
44
+ input_x = AxisQuantity.from_parameter(x_parameter)
45
+ input_y = AxisQuantity.from_parameter(y_parameter)
46
+ x, y = input_x, input_y
47
+ if options.get("subtractMeanMode"):
48
+ y = AxisQuantity(f"{y.label} - mean({y.label})", y.unit)
49
+ if options.get("fftMode"):
50
+ x = AxisQuantity(f"Frequency of {input_x.label}", _quotient_unit("", input_x.unit))
51
+ fft_input = f"({y.label})" if options.get("subtractMeanMode") else y.label
52
+ y = AxisQuantity(f"FFT magnitude of {fft_input}", y.unit)
53
+ if options.get("derivativeMode") or options.get("phasemapMode"):
54
+ y = AxisQuantity(
55
+ f"d({input_y.label})/d({input_x.label})",
56
+ _quotient_unit(input_y.unit, input_x.unit),
57
+ )
58
+ if options.get("phasemapMode"):
59
+ x = input_y
60
+ return {"x": x, "y": y}