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,413 @@
1
+ from collections import Counter
2
+
3
+ from PyQt6 import QtCore, QtGui
4
+ from PyQt6 import QtWidgets as qtw
5
+
6
+ from ._commands import create_action
7
+ from ._config_persistence import (
8
+ persist_config_value,
9
+ set_widget_value_without_signals,
10
+ )
11
+
12
+ CONFIRM_CLOSE_ALL_KEY = "user_preference.confirm_close_all"
13
+ CONFIRM_QUIT_KEY = "user_preference.confirm_close"
14
+ DO_NOT_ASK_AGAIN_LABEL = "Don't ask again"
15
+
16
+ _WINDOW_LIST_ACTIONS_ATTRIBUTE = "_qplot_window_list_actions"
17
+ _WINDOW_LIST_ENTRY_PROPERTY = "qplotWindowListEntry"
18
+ _WINDOW_KIND_LABELS = {
19
+ "plot1d": "1D",
20
+ "plot2d": "2D",
21
+ "sweeper": "Cut",
22
+ }
23
+
24
+
25
+ class _ApplicationQuitShortcutFilter(QtCore.QObject):
26
+ """Deliver Quit while a modal dialog blocks its owner window."""
27
+
28
+ def __init__(self, quit_action, parent):
29
+ super().__init__(parent)
30
+ self.quit_action = quit_action
31
+
32
+ def eventFilter(self, watched, event):
33
+ if (
34
+ event.type() != QtCore.QEvent.Type.KeyPress
35
+ or not isinstance(event, QtGui.QKeyEvent)
36
+ or event.isAutoRepeat()
37
+ ):
38
+ return False
39
+
40
+ key_sequence = QtGui.QKeySequence(event.keyCombination())
41
+ if any(
42
+ key_sequence.matches(shortcut)
43
+ == QtGui.QKeySequence.SequenceMatch.ExactMatch
44
+ for shortcut in self.quit_action.shortcuts()
45
+ ):
46
+ self.quit_action.trigger()
47
+ return True
48
+
49
+ return False
50
+
51
+
52
+ def _install_application_quit_shortcut_filter(action_owner, quit_action):
53
+ """Install the modal-dialog fallback for qPlot's Quit action."""
54
+ if getattr(action_owner, "_qplot_quit_shortcut_filter", None) is not None:
55
+ return
56
+
57
+ app = qtw.QApplication.instance()
58
+ if app is None:
59
+ return
60
+
61
+ shortcut_filter = _ApplicationQuitShortcutFilter(quit_action, action_owner)
62
+ app.installEventFilter(shortcut_filter)
63
+ action_owner._qplot_quit_shortcut_filter = shortcut_filter
64
+
65
+
66
+ def add_standard_window_controls(window):
67
+ """
68
+ Adds standard window control actions to a QMainWindow menu bar.
69
+
70
+ """
71
+ window_menu = window.menuBar().addMenu("&Window")
72
+
73
+ main_front_back_action = create_action("window.main_front_back", window)
74
+ main_front_back_action.triggered.connect(lambda: toggle_main_window_front_back(window))
75
+ window_menu.addAction(main_front_back_action)
76
+
77
+ window_menu.addSeparator()
78
+
79
+ minimize_action = create_action("window.minimize", window)
80
+ minimize_action.triggered.connect(window.showMinimized)
81
+ window_menu.addAction(minimize_action)
82
+
83
+ maximize_action = create_action("window.maximize_restore", window)
84
+ maximize_action.triggered.connect(lambda: toggle_maximized(window))
85
+ window_menu.addAction(maximize_action)
86
+
87
+ fullscreen_action = create_action("window.full_screen", window)
88
+ fullscreen_action.triggered.connect(lambda: toggle_fullscreen(window))
89
+ window_menu.addAction(fullscreen_action)
90
+
91
+ # Plot windows are registered by the main window only after they have been
92
+ # constructed. Rebuild this section immediately before display so every
93
+ # Window menu reflects the currently open qPlot windows.
94
+ window_menu.aboutToShow.connect(
95
+ lambda: populate_available_window_actions(window, window_menu)
96
+ )
97
+
98
+ return window_menu
99
+
100
+
101
+ def add_application_quit_action(window, menu, fallback_handler):
102
+ """
103
+ Add qPlot's single application-wide Quit action to a File menu.
104
+
105
+ A shared action prevents Qt from treating identical application shortcuts
106
+ in the main and plot windows as ambiguous, while still exposing Quit in
107
+ every window's File menu.
108
+ """
109
+ main_window = main_window_for(window)
110
+ action_owner = main_window if main_window is not None else window
111
+ quit_action = getattr(action_owner, "_qplot_quit_action", None)
112
+
113
+ if not isinstance(quit_action, QtGui.QAction):
114
+ quit_action = create_action("app.quit", action_owner)
115
+ quit_handler = getattr(action_owner, "quit_application", fallback_handler)
116
+ quit_action.triggered.connect(quit_handler)
117
+ action_owner._qplot_quit_action = quit_action
118
+
119
+ _install_application_quit_shortcut_filter(action_owner, quit_action)
120
+ menu.addAction(quit_action)
121
+ return quit_action
122
+
123
+
124
+ def populate_available_window_actions(window, window_menu):
125
+ """Add the current qPlot windows to the bottom of a Window menu."""
126
+
127
+ _remove_available_window_actions(window_menu)
128
+
129
+ main_window = main_window_for(window)
130
+ if main_window is None:
131
+ return
132
+
133
+ plot_windows = tuple(getattr(main_window, "windows", ()) or ())
134
+ labels = [_plot_window_menu_label(plot_window) for plot_window in plot_windows]
135
+ label_counts = Counter(labels)
136
+ label_indexes = Counter()
137
+
138
+ actions = [window_menu.addSeparator()]
139
+ actions.append(_add_window_menu_action(window_menu, main_window, "qPlot"))
140
+ for plot_window, label in zip(plot_windows, labels, strict=True):
141
+ label_indexes[label] += 1
142
+ if label_counts[label] > 1:
143
+ label = f"{label} ({label_indexes[label]})"
144
+ actions.append(_add_window_menu_action(window_menu, plot_window, label))
145
+
146
+ setattr(window_menu, _WINDOW_LIST_ACTIONS_ATTRIBUTE, actions)
147
+
148
+
149
+ def _remove_available_window_actions(window_menu):
150
+ """Remove actions generated during the previous Window-menu opening."""
151
+
152
+ for action in getattr(window_menu, _WINDOW_LIST_ACTIONS_ATTRIBUTE, ()):
153
+ window_menu.removeAction(action)
154
+ action.deleteLater()
155
+ setattr(window_menu, _WINDOW_LIST_ACTIONS_ATTRIBUTE, ())
156
+
157
+
158
+ def _plot_window_menu_label(plot_window):
159
+ """Return a concise, recognisable label for a qPlot graph window."""
160
+
161
+ operation_kind = getattr(plot_window, "operation_kind", None)
162
+ kind_label = _WINDOW_KIND_LABELS.get(operation_kind)
163
+ label = str(getattr(plot_window, "label", "")).strip()
164
+ if not label:
165
+ window_title = getattr(plot_window, "windowTitle", None)
166
+ label = str(window_title()).strip() if callable(window_title) else "Plot"
167
+
168
+ return f"{kind_label} — {label}" if kind_label is not None else label
169
+
170
+
171
+ def _add_window_menu_action(menu, target_window, text):
172
+ """Create a checked-as-active menu action that focuses ``target_window``."""
173
+
174
+ action = QtGui.QAction(text, menu)
175
+ action.setCheckable(True)
176
+ action.setChecked(target_window.isActiveWindow())
177
+ action.setProperty(_WINDOW_LIST_ENTRY_PROPERTY, True)
178
+ action.setStatusTip(f"Show {text}")
179
+ action.triggered.connect(
180
+ lambda _checked=False: focus_qplot_window(target_window)
181
+ )
182
+ menu.addAction(action)
183
+ return action
184
+
185
+
186
+ def focus_qplot_window(target_window):
187
+ """Restore, raise, and activate a qPlot window selected from the menu."""
188
+
189
+ if target_window.isMinimized():
190
+ target_window.showNormal()
191
+ elif not target_window.isVisible():
192
+ target_window.show()
193
+
194
+ target_window.raise_()
195
+ target_window.activateWindow()
196
+
197
+
198
+ def add_confirmation_options(window, menu):
199
+ """
200
+ Adds shared confirmation preferences to a menu.
201
+
202
+ """
203
+ close_all_action = add_config_checkbox_action(
204
+ window,
205
+ menu,
206
+ "Confirm Before Closing All Plot Windows",
207
+ CONFIRM_CLOSE_ALL_KEY,
208
+ "Ask before closing every open plot window",
209
+ )
210
+ quit_action = add_config_checkbox_action(
211
+ window,
212
+ menu,
213
+ "Confirm Before Quit",
214
+ CONFIRM_QUIT_KEY,
215
+ "Ask before quitting qPlot",
216
+ )
217
+ return close_all_action, quit_action
218
+
219
+
220
+ def add_restore_defaults_option(window, menu):
221
+ """
222
+ Adds a reset-all-settings action to a menu.
223
+
224
+ """
225
+ action = QtGui.QAction("Reset All Settings...", window)
226
+ action.setStatusTip("Reset all qPlot settings to their defaults")
227
+ action.triggered.connect(lambda: request_restore_defaults(window))
228
+ menu.addAction(action)
229
+ return action
230
+
231
+
232
+ def request_restore_defaults(window):
233
+ """
234
+ Requests a settings reset through the main window.
235
+
236
+ """
237
+ if hasattr(window, "restore_default_settings"):
238
+ window.restore_default_settings()
239
+ return
240
+
241
+ main_window = main_window_for(window)
242
+ if main_window is not None and hasattr(main_window, "restore_default_settings"):
243
+ main_window.restore_default_settings()
244
+
245
+
246
+ def add_config_checkbox_action(window, menu, text, key, status_tip):
247
+ """
248
+ Adds a checkable config-backed action to a menu.
249
+
250
+ """
251
+ action = QtGui.QAction(text, window)
252
+ action.setCheckable(True)
253
+ action.setStatusTip(status_tip)
254
+
255
+ def sync_checked():
256
+ action.blockSignals(True)
257
+ action.setChecked(window.config.get(key))
258
+ action.blockSignals(False)
259
+
260
+ sync_checked()
261
+
262
+ def persist_checked(checked):
263
+ previous_checked = window.config.get(key)
264
+ persist_config_value(
265
+ window,
266
+ window.config,
267
+ key,
268
+ checked,
269
+ "the confirmation preference",
270
+ lambda: set_widget_value_without_signals(
271
+ action,
272
+ action.setChecked,
273
+ previous_checked,
274
+ ),
275
+ )
276
+
277
+ action.toggled.connect(persist_checked)
278
+ menu.aboutToShow.connect(sync_checked)
279
+ menu.addAction(action)
280
+ return action
281
+
282
+
283
+ def close_all_warning_enabled(config):
284
+ """
285
+ Returns whether closing all plot windows should ask first.
286
+
287
+ """
288
+ return config.get(CONFIRM_CLOSE_ALL_KEY)
289
+
290
+
291
+ def ask_confirmation_with_dont_ask_again(
292
+ window,
293
+ title,
294
+ message,
295
+ config_key,
296
+ default_button=qtw.QMessageBox.StandardButton.No,
297
+ ):
298
+ """
299
+ Asks for confirmation and lets the user disable future prompts.
300
+
301
+ """
302
+ parent = window if isinstance(window, qtw.QWidget) else None
303
+ box = qtw.QMessageBox(
304
+ qtw.QMessageBox.Icon.Question,
305
+ title,
306
+ message,
307
+ qtw.QMessageBox.StandardButton.Yes | qtw.QMessageBox.StandardButton.No,
308
+ parent,
309
+ )
310
+ box.setDefaultButton(default_button)
311
+
312
+ dont_ask_again = qtw.QCheckBox(DO_NOT_ASK_AGAIN_LABEL)
313
+ box.setCheckBox(dont_ask_again)
314
+
315
+ reply = box.exec()
316
+ if reply == qtw.QMessageBox.StandardButton.Yes and dont_ask_again.isChecked():
317
+ persist_config_value(
318
+ window,
319
+ window.config,
320
+ config_key,
321
+ False,
322
+ "the close-confirmation preference",
323
+ )
324
+ return reply
325
+
326
+
327
+ def main_window_for(window):
328
+ """
329
+ Finds the main qPlot window for shared window actions.
330
+
331
+ """
332
+ app = qtw.QApplication.instance()
333
+ if app is None:
334
+ return window if window.__class__.__name__ == "MainWindow" else None
335
+
336
+ for top_level in qtw.QApplication.topLevelWidgets():
337
+ if top_level.__class__.__name__ == "MainWindow":
338
+ return top_level
339
+
340
+ return window if window.__class__.__name__ == "MainWindow" else None
341
+
342
+
343
+ def toggle_main_window_front_back(window):
344
+ """
345
+ Brings the main qPlot window forward, or behind qPlot graph windows.
346
+
347
+ """
348
+ main_window = main_window_for(window)
349
+ if main_window is None:
350
+ return
351
+
352
+ if main_window.isActiveWindow():
353
+ send_main_window_behind_graphs(main_window, window)
354
+ return
355
+
356
+ if main_window.isMinimized():
357
+ main_window.showNormal()
358
+ else:
359
+ main_window.show()
360
+
361
+ main_window.raise_()
362
+ main_window.activateWindow()
363
+
364
+
365
+ def send_main_window_behind_graphs(main_window, active_window):
366
+ """
367
+ Places graph windows above the main qPlot window without lowering qPlot
368
+ behind unrelated applications.
369
+
370
+ """
371
+ app = qtw.QApplication.instance()
372
+ if app is None:
373
+ return
374
+
375
+ graph_windows = [
376
+ window for window in qtw.QApplication.topLevelWidgets()
377
+ if window is not main_window
378
+ and window.isVisible()
379
+ and hasattr(window, "_guid")
380
+ and hasattr(window, "param")
381
+ ]
382
+ if not graph_windows:
383
+ return
384
+
385
+ for graph_window in graph_windows:
386
+ graph_window.raise_()
387
+
388
+ if active_window in graph_windows:
389
+ active_window.activateWindow()
390
+ else:
391
+ graph_windows[-1].activateWindow()
392
+
393
+
394
+ def toggle_maximized(window):
395
+ """
396
+ Toggles a window between maximized and normal size.
397
+
398
+ """
399
+ if window.isMaximized():
400
+ window.showNormal()
401
+ else:
402
+ window.showMaximized()
403
+
404
+
405
+ def toggle_fullscreen(window):
406
+ """
407
+ Toggles a window between full-screen and normal size.
408
+
409
+ """
410
+ if window.isFullScreen():
411
+ window.showNormal()
412
+ else:
413
+ window.showFullScreen()