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