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,23 @@
1
+ from .details_tables import CopyableTableWidget
2
+ from .dropbox import (
3
+ expandingComboBox,
4
+ picker_1d,
5
+ )
6
+ from .operations import operations_widget
7
+ from .preview import PreviewTab
8
+ from .toolbar import QDock_context
9
+ from .treeWidgets import (
10
+ RunList,
11
+ moreInfo,
12
+ )
13
+
14
+ __all__ = [
15
+ "expandingComboBox",
16
+ "CopyableTableWidget",
17
+ "moreInfo",
18
+ "picker_1d",
19
+ "PreviewTab",
20
+ "RunList",
21
+ "QDock_context",
22
+ "operations_widget",
23
+ ]
@@ -0,0 +1,387 @@
1
+ import html
2
+ from datetime import datetime
3
+
4
+
5
+ def run_tooltip_text(metadata):
6
+ """
7
+ Builds the summary shown when hovering over a run table row.
8
+
9
+ """
10
+ sweep = format_parameter_list_html(metadata.get("sweep_parameters"))
11
+ measure = format_parameter_list_html(metadata.get("measure_parameters"))
12
+ status = html.escape(format_run_status(metadata))
13
+ exception_row = ""
14
+ if run_failed(metadata):
15
+ summary = html.escape(measurement_exception_summary(metadata))
16
+ exception_row = (
17
+ "<tr>"
18
+ "<td style='padding:0 0.5em 0 0'>Exception</td>"
19
+ f"<td style='padding:0'>{summary}</td>"
20
+ "</tr>"
21
+ )
22
+
23
+ return (
24
+ "<table style='margin:0; border-spacing:0; border-collapse:collapse'>"
25
+ "<tr>"
26
+ "<td style='padding:0 0.5em 0 0'>Sweep</td>"
27
+ f"<td nowrap='nowrap' style='padding:0; white-space:nowrap'>({sweep})</td>"
28
+ "</tr>"
29
+ "<tr>"
30
+ "<td style='padding:0 0.5em 0 0'>Measure</td>"
31
+ f"<td nowrap='nowrap' style='padding:0; white-space:nowrap'>({measure})</td>"
32
+ "</tr>"
33
+ "<tr>"
34
+ "<td style='padding:0 0.5em 0 0'>Status</td>"
35
+ f"<td style='padding:0'>{status}</td>"
36
+ "</tr>"
37
+ f"{exception_row}"
38
+ "</table>"
39
+ )
40
+
41
+
42
+ def run_tooltip_plain_text(metadata):
43
+ sweep = format_parameter_list(metadata.get("sweep_parameters"))
44
+ measure = format_parameter_list(metadata.get("measure_parameters"))
45
+ lines = [
46
+ f"{'Sweep':<7}({sweep})",
47
+ f"Measure ({measure})",
48
+ f"Status {format_run_status(metadata)}",
49
+ ]
50
+ if run_failed(metadata):
51
+ lines.append(f"Exception {measurement_exception_summary(metadata)}")
52
+
53
+ return "\n".join(lines)
54
+
55
+
56
+ def format_parameter_list(parameters):
57
+ if not parameters:
58
+ return "unknown"
59
+ return ", ".join(str(parameter) for parameter in parameters)
60
+
61
+
62
+ def format_parameter_list_html(parameters):
63
+ if not parameters:
64
+ return "unknown"
65
+ return ",&nbsp;".join(html.escape(str(parameter)) for parameter in parameters)
66
+
67
+
68
+ def run_is_complete(metadata):
69
+ return bool(metadata.get("completed_timestamp") or metadata.get("is_completed"))
70
+
71
+
72
+ def run_was_interrupted(metadata):
73
+ exception = metadata.get("measurement_exception")
74
+ return exception is not None and "KeyboardInterrupt" in str(exception).strip()
75
+
76
+
77
+ def run_failed(metadata):
78
+ exception = metadata.get("measurement_exception")
79
+ return bool(
80
+ exception is not None
81
+ and str(exception).strip()
82
+ and not run_was_interrupted(metadata)
83
+ )
84
+
85
+
86
+ def measurement_exception_summary(metadata, maximum_length=200):
87
+ exception = str(metadata.get("measurement_exception") or "")
88
+ summary = next(
89
+ (line.strip() for line in reversed(exception.splitlines()) if line.strip()),
90
+ "",
91
+ )
92
+ if len(summary) > maximum_length:
93
+ return f"{summary[: maximum_length - 1]}…"
94
+ return summary
95
+
96
+
97
+ def format_run_status(metadata):
98
+ """Return the detailed status used in a run's tooltip."""
99
+ state = format_run_state(metadata)
100
+ if run_was_interrupted(metadata):
101
+ return f"Interrupted ({format_setpoint_progress_percent(metadata)})"
102
+ if state == "Failed":
103
+ return f"{state} ({format_interrupted_progress_percent(metadata)})"
104
+ return state
105
+
106
+
107
+ def format_run_state(metadata):
108
+ """Return the canonical, concise label for a run's current state."""
109
+ if run_was_interrupted(metadata):
110
+ return _format_state_with_setpoint_progress("Interrupted", metadata)
111
+ if run_failed(metadata):
112
+ return "Failed"
113
+ if run_is_complete(metadata):
114
+ return "Completed"
115
+ if metadata.get("is_completed") is not None and not bool(metadata["is_completed"]):
116
+ return _format_state_with_setpoint_progress("Running", metadata)
117
+ return "unknown"
118
+
119
+
120
+ def _format_state_with_setpoint_progress(state, metadata):
121
+ progress = format_setpoint_progress_percent(metadata)
122
+ return f"{state} ({progress})"
123
+
124
+
125
+ def format_progress(metadata):
126
+ progress = format_progress_percent(metadata)
127
+ if progress == "unknown":
128
+ return "unknown% complete"
129
+ return f"{progress} complete"
130
+
131
+
132
+ def format_progress_percent(metadata):
133
+ if run_is_complete(metadata):
134
+ return "100%"
135
+
136
+ percent = progress_percent_value(metadata)
137
+ if percent is None:
138
+ return "unknown"
139
+
140
+ return f"{percent:.1f}%"
141
+
142
+
143
+ def progress_percent_value(metadata):
144
+ expected = metadata.get("expected_results")
145
+ count = metadata.get("result_count")
146
+ return _progress_percent_value(metadata, count, expected)
147
+
148
+
149
+ def setpoint_progress_percent_value(metadata):
150
+ """Return the percentage of known setpoints that have been measured."""
151
+ percent = _progress_percent_value(
152
+ metadata,
153
+ metadata.get("read_setpoint_count"),
154
+ metadata.get("setpoint_count"),
155
+ maximum=100,
156
+ )
157
+ if percent is not None:
158
+ return percent
159
+
160
+ # The cheap detail pass exposes result-row progress before the distinct
161
+ # setpoint query completes. Both counts include the same measurement
162
+ # multiplicity, so their ratio is a valid early setpoint-progress value.
163
+ return _progress_percent_value(
164
+ metadata,
165
+ metadata.get("result_count"),
166
+ metadata.get("expected_results"),
167
+ maximum=100,
168
+ )
169
+
170
+
171
+ def format_setpoint_progress_percent(metadata):
172
+ percent = setpoint_progress_percent_value(metadata)
173
+ if percent is None:
174
+ return "unknown"
175
+ return f"{percent:.1f}%"
176
+
177
+
178
+ def interrupted_progress_percent_value(metadata):
179
+ expected = None
180
+ if metadata.get("setpoint_count_source") != "observed":
181
+ expected = metadata.get("setpoint_count")
182
+ if not expected and metadata.get("expected_results_source") != "observed":
183
+ expected = metadata.get("expected_results")
184
+ count = metadata.get("read_setpoint_count")
185
+ if count is None:
186
+ count = metadata.get("result_count")
187
+ return _progress_percent_value(metadata, count, expected, maximum=100)
188
+
189
+
190
+ def _progress_percent_value(metadata, count, expected, maximum=None):
191
+ if not expected or count is None:
192
+ return None
193
+
194
+ try:
195
+ if maximum is None:
196
+ maximum = 100 if run_is_complete(metadata) else 99.9
197
+ return max(0, min(maximum, (float(count) / float(expected)) * 100))
198
+ except (TypeError, ValueError, ZeroDivisionError):
199
+ return None
200
+
201
+
202
+ def format_interrupted_progress_percent(metadata):
203
+ percent = interrupted_progress_percent_value(metadata)
204
+ if percent is None:
205
+ return "unknown"
206
+ return f"{percent:.1f}%"
207
+
208
+
209
+ def complete_cell_sort_value(metadata):
210
+ if run_was_interrupted(metadata):
211
+ return setpoint_progress_percent_value(metadata)
212
+ if run_failed(metadata):
213
+ return interrupted_progress_percent_value(metadata)
214
+ if run_is_complete(metadata):
215
+ return 100
216
+ return setpoint_progress_percent_value(metadata)
217
+
218
+
219
+ def format_complete_cell(metadata):
220
+ """Return the run-table status label.
221
+
222
+ Kept under its historic name for callers, but deliberately shares the
223
+ Overview's canonical status wording.
224
+ """
225
+ return format_run_state(metadata)
226
+
227
+
228
+ def format_timestamp(timestamp):
229
+ if not timestamp:
230
+ return "unknown"
231
+ try:
232
+ return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
233
+ except (TypeError, ValueError, OSError, OverflowError):
234
+ return "unknown"
235
+
236
+
237
+ def time_taken_seconds(metadata):
238
+ started = metadata.get("run_timestamp")
239
+ if not started:
240
+ return None
241
+
242
+ completed = metadata.get("completed_timestamp")
243
+ if completed:
244
+ end = completed
245
+ elif run_is_complete(metadata):
246
+ return None
247
+ elif metadata.get("database_modified_timestamp"):
248
+ end = metadata.get("database_modified_timestamp")
249
+ else:
250
+ return None
251
+ try:
252
+ return max(0, float(end) - float(started))
253
+ except (TypeError, ValueError):
254
+ return None
255
+
256
+
257
+ def format_time_taken_seconds(metadata):
258
+ seconds = time_taken_seconds(metadata)
259
+ if seconds is None:
260
+ return "unknown"
261
+ return f"{seconds:,.1f} s"
262
+
263
+
264
+ def format_run_duration(metadata):
265
+ seconds = time_taken_seconds(metadata)
266
+ if seconds is None:
267
+ return "unknown"
268
+
269
+ if seconds < 10:
270
+ return f"{seconds:.2f} s"
271
+ if seconds < 100:
272
+ return f"{seconds:.1f} s"
273
+ return f"{seconds:.0f} s"
274
+
275
+
276
+ def format_duration_dhms(seconds):
277
+ total_seconds = int(round(seconds))
278
+ days, remainder = divmod(total_seconds, 24 * 60 * 60)
279
+ hours, remainder = divmod(remainder, 60 * 60)
280
+ minutes, seconds = divmod(remainder, 60)
281
+ return f"{days}d {hours}h {minutes}m {seconds}s"
282
+
283
+
284
+ def format_storage_size(bytes_value):
285
+ if bytes_value is None:
286
+ return "unknown"
287
+
288
+ try:
289
+ size = float(bytes_value)
290
+ except (TypeError, ValueError):
291
+ return "unknown"
292
+
293
+ if size < 0:
294
+ return "unknown"
295
+
296
+ units = ["B", "KB", "MB", "GB", "TB"]
297
+ unit_index = 0
298
+ while size >= 1024 and unit_index < len(units) - 1:
299
+ size /= 1024
300
+ unit_index += 1
301
+
302
+ if unit_index == 0:
303
+ return f"{int(size)} B"
304
+ if size < 10:
305
+ return f"{size:.1f} {units[unit_index]}"
306
+ return f"{size:.0f} {units[unit_index]}"
307
+
308
+
309
+ def format_point_count(metadata):
310
+ expected = metadata.get("setpoint_count", metadata.get("expected_results"))
311
+ acquired = metadata.get("read_setpoint_count")
312
+ shape = metadata.get("setpoint_shape") or metadata.get("point_shape")
313
+ if shape:
314
+ try:
315
+ shape_values = [int(size) for size in shape]
316
+ shape_parts = " × ".join(f"{size:,}" for size in shape_values)
317
+ except (TypeError, ValueError):
318
+ shape_values = []
319
+ shape_parts = ""
320
+
321
+ duplicate_count = one_dimensional_duplicate_point_count(metadata, shape_values)
322
+ if duplicate_count is not None:
323
+ return duplicate_count
324
+
325
+ if (
326
+ acquired is not None
327
+ and expected
328
+ and metadata.get("setpoint_shape_source") == "planned"
329
+ and acquired != expected
330
+ ):
331
+ try:
332
+ return f"{int(acquired):,} / {int(expected):,} = {shape_parts}"
333
+ except (TypeError, ValueError):
334
+ pass
335
+ if expected:
336
+ return f"{int(expected):,} = {shape_parts}"
337
+ if shape_parts:
338
+ return shape_parts
339
+
340
+ if expected:
341
+ return f"{int(expected):,}"
342
+
343
+ # With no physical result rows, logical multiplicity is immaterial: the
344
+ # acquired logical count is exactly zero. Positive MAX(id) values remain
345
+ # internal until a structural layout proves their logical interpretation.
346
+ if type(metadata.get("result_count")) is int and metadata["result_count"] == 0:
347
+ return "0"
348
+
349
+ return "unknown"
350
+
351
+
352
+ def point_count_sort_value(metadata):
353
+ for name in ("setpoint_count", "expected_results"):
354
+ value = metadata.get(name)
355
+ if type(value) is int and value >= 0:
356
+ return value
357
+ if type(metadata.get("result_count")) is int and metadata["result_count"] == 0:
358
+ return 0
359
+ return None
360
+
361
+
362
+ def one_dimensional_duplicate_point_count(metadata, shape_values=None):
363
+ expected = metadata.get("setpoint_count", metadata.get("expected_results"))
364
+ shape = metadata.get("setpoint_shape") or metadata.get("point_shape")
365
+ if shape_values is None:
366
+ if not shape:
367
+ return None
368
+ try:
369
+ shape_values = [int(size) for size in shape]
370
+ except (TypeError, ValueError):
371
+ return None
372
+
373
+ if len(shape_values) != 1:
374
+ return None
375
+
376
+ try:
377
+ expected_count = int(expected)
378
+ except (TypeError, ValueError):
379
+ return None
380
+
381
+ if expected_count != shape_values[0]:
382
+ return None
383
+ return f"{expected_count:,}"
384
+
385
+
386
+ def measured_parameter_count(metadata):
387
+ return len(metadata.get("measure_parameters") or [])