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.
- qplot/__init__.py +46 -0
- qplot/__main__.py +232 -0
- qplot/_auxiliary_paths.py +63 -0
- qplot/_metadata.py +12 -0
- qplot/_repair.py +14 -0
- qplot/_shutdown_supervisor.py +4049 -0
- qplot/_version.py +12 -0
- qplot/_windows_shutdown_job.py +1166 -0
- qplot/configuration/__init__.py +3 -0
- qplot/configuration/config.py +573 -0
- qplot/configuration/config_schema.json +319 -0
- qplot/configuration/scripts.py +255 -0
- qplot/configuration/themes/__init__.py +13 -0
- qplot/configuration/themes/_base.py +579 -0
- qplot/configuration/themes/blank.py +17 -0
- qplot/configuration/themes/dark.py +54 -0
- qplot/configuration/themes/light.py +54 -0
- qplot/datahandling/LoadFromDB.py +224 -0
- qplot/datahandling/__init__.py +47 -0
- qplot/datahandling/_trusted_live_helper.py +812 -0
- qplot/datahandling/_trusted_live_protocol.py +1745 -0
- qplot/datahandling/database.py +2330 -0
- qplot/datahandling/dimensions.py +43 -0
- qplot/datahandling/file_identity.py +461 -0
- qplot/datahandling/parameter_data.py +149 -0
- qplot/datahandling/plot_progress.py +17 -0
- qplot/datahandling/qcodes_cache.py +277 -0
- qplot/datahandling/qcodes_compat.py +14 -0
- qplot/datahandling/readSQL.py +2442 -0
- qplot/datahandling/readonly.py +2561 -0
- qplot/datahandling/source_activity.py +21 -0
- qplot/datahandling/trusted_array_preview.py +127 -0
- qplot/datahandling/trusted_derived_cache.py +1605 -0
- qplot/datahandling/trusted_derived_rendering.py +868 -0
- qplot/datahandling/trusted_heatmap.py +241 -0
- qplot/datahandling/trusted_live.py +2808 -0
- qplot/datahandling/trusted_live_queries.py +5907 -0
- qplot/datahandling/trusted_live_service.py +1894 -0
- qplot/datahandling/trusted_live_supervisor.py +2161 -0
- qplot/datahandling/trusted_plot.py +283 -0
- qplot/datahandling/trusted_presentation.py +1065 -0
- qplot/datahandling/trusted_snapshot.py +1132 -0
- qplot/datahandling/trusted_work_coordinator.py +2157 -0
- qplot/datahandling/trusted_work_scheduler.py +1204 -0
- qplot/diagnostics.py +241 -0
- qplot/resources/testdata/qplot_test_db_01_10mb.csv +11 -0
- qplot/resources/testdata/qplot_test_db_02_25mb.csv +14 -0
- qplot/resources/testdata/qplot_test_db_03_50mb.csv +17 -0
- qplot/resources/testdata/qplot_test_db_04_100mb.csv +20 -0
- qplot/resources/testdata/qplot_test_db_05_250mb.csv +23 -0
- qplot/resources/testdata/qplot_test_db_06_500mb.csv +26 -0
- qplot/resources/testdata/qplot_test_db_07_1gb.csv +29 -0
- qplot/resources/testdata/qplot_test_db_08_5gb.csv +32 -0
- qplot/resources/testdata/qplot_test_db_09_10gb.csv +35 -0
- qplot/resources/testdata/qplot_test_db_10_30gb.csv +38 -0
- qplot/testdata.py +1733 -0
- qplot/tools/__init__.py +13 -0
- qplot/tools/finite_means.py +114 -0
- qplot/tools/general.py +134 -0
- qplot/tools/heatmap_geometry.py +381 -0
- qplot/tools/operation_registry.py +179 -0
- qplot/tools/plot_tools.py +625 -0
- qplot/tools/sample_statistics.py +56 -0
- qplot/tools/worker.py +2787 -0
- qplot/windows/__init__.py +9 -0
- qplot/windows/_colorbar.py +422 -0
- qplot/windows/_commands.py +523 -0
- qplot/windows/_config_persistence.py +123 -0
- qplot/windows/_database_actions.py +4904 -0
- qplot/windows/_dataset_handle.py +221 -0
- qplot/windows/_dragdrop.py +71 -0
- qplot/windows/_export_paths.py +1167 -0
- qplot/windows/_help.py +169 -0
- qplot/windows/_native_averaging.py +173 -0
- qplot/windows/_native_transforms.py +389 -0
- qplot/windows/_plot1d_snap.py +608 -0
- qplot/windows/_plot1d_traces.py +2183 -0
- qplot/windows/_plot2d_colorbar.py +1126 -0
- qplot/windows/_plot2d_colorbar_dialog.py +786 -0
- qplot/windows/_plot2d_layers.py +2197 -0
- qplot/windows/_plot2d_sweeps.py +712 -0
- qplot/windows/_plotWin.py +1993 -0
- qplot/windows/_plot_actions.py +2695 -0
- qplot/windows/_plot_appearance.py +276 -0
- qplot/windows/_plot_axis_scaling.py +1485 -0
- qplot/windows/_plot_export.py +1445 -0
- qplot/windows/_plot_feedback.py +110 -0
- qplot/windows/_plot_marquee.py +656 -0
- qplot/windows/_plot_refresh.py +942 -0
- qplot/windows/_plot_state.py +239 -0
- qplot/windows/_plot_transform_labels.py +60 -0
- qplot/windows/_preferences.py +635 -0
- qplot/windows/_refresh_interval.py +70 -0
- qplot/windows/_run_controls.py +828 -0
- qplot/windows/_shortcuts.py +47 -0
- qplot/windows/_subplots/__init__.py +8 -0
- qplot/windows/_subplots/subplot1d.py +531 -0
- qplot/windows/_subplots/subplot2d.py +918 -0
- qplot/windows/_svg_export.py +209 -0
- qplot/windows/_trusted_derived_qt.py +1759 -0
- qplot/windows/_widgets/__init__.py +23 -0
- qplot/windows/_widgets/_run_formatting.py +387 -0
- qplot/windows/_widgets/details_tables.py +1119 -0
- qplot/windows/_widgets/dropbox.py +290 -0
- qplot/windows/_widgets/operations.py +460 -0
- qplot/windows/_widgets/preview.py +2997 -0
- qplot/windows/_widgets/run_list_items.py +488 -0
- qplot/windows/_widgets/toolbar.py +111 -0
- qplot/windows/_widgets/treeWidgets.py +2572 -0
- qplot/windows/_window_controls.py +413 -0
- qplot/windows/main.py +1635 -0
- qplot/windows/plot1d.py +311 -0
- qplot/windows/plot2d.py +1901 -0
- qplotter-1.6.0b3.dist-info/METADATA +318 -0
- qplotter-1.6.0b3.dist-info/RECORD +119 -0
- qplotter-1.6.0b3.dist-info/WHEEL +5 -0
- qplotter-1.6.0b3.dist-info/entry_points.txt +4 -0
- qplotter-1.6.0b3.dist-info/licenses/LICENSE +21 -0
- qplotter-1.6.0b3.dist-info/top_level.txt +1 -0
qplot/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from ._metadata import package_version
|
|
2
|
+
|
|
3
|
+
__version__ = package_version()
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"datahandling",
|
|
7
|
+
"windows",
|
|
8
|
+
"tools",
|
|
9
|
+
"run",
|
|
10
|
+
"config",
|
|
11
|
+
"__version__",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run(*args, **kwargs):
|
|
16
|
+
"""
|
|
17
|
+
Start qPlot through its dedicated public-API launcher.
|
|
18
|
+
|
|
19
|
+
Plain package import still avoids importing GUI modules. Passing
|
|
20
|
+
``return_objects=True`` remains the explicit caller-owned exception.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
from .__main__ import run_public as _run
|
|
24
|
+
|
|
25
|
+
return _run(*args, **kwargs)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def __getattr__(name):
|
|
29
|
+
if name == "config":
|
|
30
|
+
from qplot.configuration.config import config
|
|
31
|
+
|
|
32
|
+
globals()[name] = config
|
|
33
|
+
return config
|
|
34
|
+
|
|
35
|
+
if name in {"datahandling", "tools", "windows"}:
|
|
36
|
+
import importlib
|
|
37
|
+
|
|
38
|
+
module = importlib.import_module(f"qplot.{name}")
|
|
39
|
+
globals()[name] = module
|
|
40
|
+
return module
|
|
41
|
+
|
|
42
|
+
raise AttributeError(f"module 'qplot' has no attribute {name!r}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def __dir__():
|
|
46
|
+
return sorted(set(globals()) | set(__all__))
|
qplot/__main__.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from qplot._metadata import package_version
|
|
4
|
+
from qplot.diagnostics import (
|
|
5
|
+
configure_logging,
|
|
6
|
+
install_excepthook,
|
|
7
|
+
log_event,
|
|
8
|
+
log_exception,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
QT_OPTIONS_WITH_VALUES = {
|
|
12
|
+
"-display",
|
|
13
|
+
"-font",
|
|
14
|
+
"-geometry",
|
|
15
|
+
"-name",
|
|
16
|
+
"-platform",
|
|
17
|
+
"-platformpluginpath",
|
|
18
|
+
"-platformtheme",
|
|
19
|
+
"-plugin",
|
|
20
|
+
"-qwindowgeometry",
|
|
21
|
+
"-qwindowicon",
|
|
22
|
+
"-qwindowtitle",
|
|
23
|
+
"-session",
|
|
24
|
+
"-style",
|
|
25
|
+
"-stylesheet",
|
|
26
|
+
"-title",
|
|
27
|
+
"-visual",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _database_path_from_arguments(args):
|
|
32
|
+
"""
|
|
33
|
+
Return the first database path passed on the command line.
|
|
34
|
+
|
|
35
|
+
File managers pass the double-clicked file as a plain positional argument.
|
|
36
|
+
Qt options are ignored here so they can still be handled by QApplication.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
skip_next = False
|
|
40
|
+
positional_only = False
|
|
41
|
+
for arg in args:
|
|
42
|
+
if skip_next:
|
|
43
|
+
skip_next = False
|
|
44
|
+
continue
|
|
45
|
+
if arg == "--":
|
|
46
|
+
positional_only = True
|
|
47
|
+
continue
|
|
48
|
+
if not positional_only and arg in QT_OPTIONS_WITH_VALUES:
|
|
49
|
+
skip_next = True
|
|
50
|
+
continue
|
|
51
|
+
if not positional_only and arg.startswith("-"):
|
|
52
|
+
continue
|
|
53
|
+
return arg
|
|
54
|
+
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _configure_application_identity(app):
|
|
59
|
+
"""
|
|
60
|
+
Sets the application name used by native desktop menus.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
app.setApplicationName("qPlot")
|
|
64
|
+
if hasattr(app, "setApplicationDisplayName"):
|
|
65
|
+
app.setApplicationDisplayName("qPlot")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _finalize_confirmed_process_shutdown(
|
|
69
|
+
app,
|
|
70
|
+
window,
|
|
71
|
+
process_fail_safe,
|
|
72
|
+
*,
|
|
73
|
+
retain_objects,
|
|
74
|
+
):
|
|
75
|
+
"""Destroy quiescent Qt ownership while launcher containment stays armed."""
|
|
76
|
+
|
|
77
|
+
from PyQt6 import sip
|
|
78
|
+
|
|
79
|
+
from qplot.windows import MainWindow
|
|
80
|
+
|
|
81
|
+
if not process_fail_safe.armed:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
deadline_exhausted = bool(getattr(window, "_shutdown_deadline_exhausted", False))
|
|
85
|
+
quiescent = False
|
|
86
|
+
if not deadline_exhausted:
|
|
87
|
+
try:
|
|
88
|
+
quiescent = not MainWindow._shutdown_background_work_active(window)
|
|
89
|
+
except BaseException:
|
|
90
|
+
quiescent = False
|
|
91
|
+
|
|
92
|
+
if retain_objects:
|
|
93
|
+
if quiescent:
|
|
94
|
+
process_fail_safe.disarm()
|
|
95
|
+
return
|
|
96
|
+
process_fail_safe.wait_for_forced_exit()
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
# Never enter a QThreadPool-owning destructor after the deadline or while
|
|
100
|
+
# the final resource scan is non-quiescent. sip.delete() may hold this
|
|
101
|
+
# entry-point thread indefinitely, which would otherwise make its
|
|
102
|
+
# hard-deadline os._exit fallback unreachable if the launcher failed.
|
|
103
|
+
if deadline_exhausted or not quiescent:
|
|
104
|
+
process_fail_safe.wait_for_forced_exit()
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
# The launcher remains armed through both destructors in the quiescent path.
|
|
108
|
+
if not process_fail_safe.watchdog_operational():
|
|
109
|
+
process_fail_safe.wait_for_forced_exit()
|
|
110
|
+
return
|
|
111
|
+
try:
|
|
112
|
+
sip.delete(window)
|
|
113
|
+
if not process_fail_safe.watchdog_operational():
|
|
114
|
+
process_fail_safe.wait_for_forced_exit()
|
|
115
|
+
return
|
|
116
|
+
sip.delete(app)
|
|
117
|
+
except BaseException:
|
|
118
|
+
process_fail_safe.wait_for_forced_exit()
|
|
119
|
+
raise
|
|
120
|
+
process_fail_safe.disarm()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _run_gui(
|
|
124
|
+
return_objects=False,
|
|
125
|
+
database_path=None,
|
|
126
|
+
*,
|
|
127
|
+
shutdown_supervisor_client=None,
|
|
128
|
+
shutdown_supervisor_diagnostic=None,
|
|
129
|
+
):
|
|
130
|
+
"""Run Qt in this process, optionally with an authenticated launcher client."""
|
|
131
|
+
|
|
132
|
+
from PyQt6 import QtWidgets as qtw
|
|
133
|
+
|
|
134
|
+
from qplot.windows import MainWindow
|
|
135
|
+
from qplot.windows.main import _ProcessShutdownFailSafe
|
|
136
|
+
|
|
137
|
+
configure_logging()
|
|
138
|
+
install_excepthook()
|
|
139
|
+
log_event("Starting qPlot %s", package_version())
|
|
140
|
+
print("Initialising GUI, this may take a few seconds.\n")
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
app = qtw.QApplication(sys.argv)
|
|
144
|
+
_configure_application_identity(app)
|
|
145
|
+
if database_path is None:
|
|
146
|
+
database_path = _database_path_from_arguments(sys.argv[1:])
|
|
147
|
+
w = MainWindow(startup_database_path=database_path)
|
|
148
|
+
process_fail_safe = None
|
|
149
|
+
if hasattr(w, "_shutdown_process_fail_safe"):
|
|
150
|
+
process_fail_safe = _ProcessShutdownFailSafe(
|
|
151
|
+
shutdown_supervisor_client,
|
|
152
|
+
startup_diagnostic=shutdown_supervisor_diagnostic,
|
|
153
|
+
)
|
|
154
|
+
w._shutdown_process_fail_safe = process_fail_safe
|
|
155
|
+
exit_code = app.exec()
|
|
156
|
+
except Exception as err:
|
|
157
|
+
log_exception("qPlot startup failed", err)
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
log_event("qPlot event loop exited with code %s", exit_code)
|
|
161
|
+
|
|
162
|
+
if process_fail_safe is not None:
|
|
163
|
+
_finalize_confirmed_process_shutdown(
|
|
164
|
+
app,
|
|
165
|
+
w,
|
|
166
|
+
process_fail_safe,
|
|
167
|
+
retain_objects=bool(return_objects),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if return_objects:
|
|
171
|
+
return app, w
|
|
172
|
+
return exit_code
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def run(return_objects=False, database_path=None):
|
|
176
|
+
"""
|
|
177
|
+
Entry point for opening the qplot app.
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
return_objects : bool, optional
|
|
182
|
+
If true, returns the QApplication and MainWindow after the event loop
|
|
183
|
+
exits. This deliberately runs in the caller's process, which owns the
|
|
184
|
+
returned objects and their cleanup; it does not acquire the default
|
|
185
|
+
launcher's process-tree containment or hard-deadline guarantee. The
|
|
186
|
+
default is false so the command-line entry point uses that launcher and
|
|
187
|
+
exits quietly and successfully.
|
|
188
|
+
database_path : str, optional
|
|
189
|
+
QCoDeS database path to load after the main window opens. When omitted,
|
|
190
|
+
qPlot uses the first positional path passed on the command line, if any.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
tuple[PyQt6.QtWidgets.QApplication, qplot.windows.main.MainWindow] | int
|
|
195
|
+
Application objects when return_objects is true; otherwise the Qt
|
|
196
|
+
event-loop exit status.
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
if return_objects:
|
|
200
|
+
return _run_gui(return_objects=True, database_path=database_path)
|
|
201
|
+
|
|
202
|
+
from qplot._shutdown_supervisor import launch_gui
|
|
203
|
+
|
|
204
|
+
return launch_gui(original_argv=sys.argv, database_path=database_path)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def run_public(return_objects=False, database_path=None):
|
|
208
|
+
"""Run qPlot for library callers without containing the caller process.
|
|
209
|
+
|
|
210
|
+
The default path delegates GUI ownership to a dedicated launcher process.
|
|
211
|
+
It returns the GUI status normally; on POSIX, termination by signal is
|
|
212
|
+
represented non-destructively as ``-signal_number``. Unlike the command
|
|
213
|
+
line entry point, this function never reproduces that signal or hard-exits
|
|
214
|
+
the Python process that called :func:`qplot.run`.
|
|
215
|
+
|
|
216
|
+
Caller control-flow exceptions are preserved while the dedicated launcher
|
|
217
|
+
terminates and reaps its complete tree, then re-raised unchanged after the
|
|
218
|
+
authenticated outcome and launcher EOF have been observed.
|
|
219
|
+
|
|
220
|
+
``return_objects=True`` intentionally retains the historical in-process,
|
|
221
|
+
caller-owned behavior and does not provide the hard-deadline guarantee.
|
|
222
|
+
"""
|
|
223
|
+
if return_objects:
|
|
224
|
+
return _run_gui(return_objects=True, database_path=database_path)
|
|
225
|
+
|
|
226
|
+
from qplot._shutdown_supervisor import launch_gui_for_api
|
|
227
|
+
|
|
228
|
+
return launch_gui_for_api(original_argv=sys.argv, database_path=database_path)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
raise SystemExit(run())
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Keep settings and diagnostics away from existing SQLite artifacts."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import stat
|
|
5
|
+
|
|
6
|
+
_SQLITE_HEADER = b"SQLite format 3\x00"
|
|
7
|
+
_SQLITE_HEADERS = (
|
|
8
|
+
_SQLITE_HEADER,
|
|
9
|
+
b"\x37\x7f\x06\x82", # WAL, little-endian checksums
|
|
10
|
+
b"\x37\x7f\x06\x83", # WAL, big-endian checksums
|
|
11
|
+
b"\xd9\xd5\x05\xf9\x20\xa1\x63\xd7", # rollback journal
|
|
12
|
+
)
|
|
13
|
+
_SIDECAR_SUFFIXES = ("-wal", "-journal", "-shm")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _signature(file_stat):
|
|
17
|
+
return tuple(int(getattr(file_stat, field)) for field in (
|
|
18
|
+
"st_dev", "st_ino", "st_mode", "st_nlink", "st_size",
|
|
19
|
+
"st_mtime_ns", "st_ctime_ns",
|
|
20
|
+
))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _read_header(filename):
|
|
24
|
+
"""Inspect a bounded regular file without SQLite or writable handles."""
|
|
25
|
+
try:
|
|
26
|
+
before = os.lstat(filename)
|
|
27
|
+
except FileNotFoundError:
|
|
28
|
+
return b""
|
|
29
|
+
if not stat.S_ISREG(before.st_mode):
|
|
30
|
+
raise OSError("The auxiliary destination is not a regular file.")
|
|
31
|
+
flags = os.O_RDONLY | getattr(os, "O_BINARY", 0)
|
|
32
|
+
flags |= getattr(os, "O_NONBLOCK", 0) | getattr(os, "O_NOFOLLOW", 0)
|
|
33
|
+
flags |= getattr(os, "O_CLOEXEC", 0)
|
|
34
|
+
descriptor = os.open(filename, flags)
|
|
35
|
+
try:
|
|
36
|
+
opened = os.fstat(descriptor)
|
|
37
|
+
# Windows lstat and fstat expose different ctime definitions.
|
|
38
|
+
common_fields = 6 if os.name == "nt" else 7
|
|
39
|
+
if _signature(before)[:common_fields] != _signature(opened)[:common_fields]:
|
|
40
|
+
raise OSError("The auxiliary destination changed during inspection.")
|
|
41
|
+
header = os.read(descriptor, len(_SQLITE_HEADER))
|
|
42
|
+
if _signature(os.fstat(descriptor)) != _signature(opened):
|
|
43
|
+
raise OSError("The auxiliary destination changed during inspection.")
|
|
44
|
+
finally:
|
|
45
|
+
os.close(descriptor)
|
|
46
|
+
if _signature(os.lstat(filename)) != _signature(before):
|
|
47
|
+
raise OSError("The auxiliary destination changed during inspection.")
|
|
48
|
+
return header
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def ensure_safe_auxiliary_path(filename):
|
|
52
|
+
"""Reject database files and reserved sidecars before any auxiliary write.
|
|
53
|
+
|
|
54
|
+
These fixed application paths can contain measurement data even when the
|
|
55
|
+
database has never been selected in qPlot. Unreadable files fail closed.
|
|
56
|
+
"""
|
|
57
|
+
filename = os.path.abspath(os.fspath(filename))
|
|
58
|
+
if _read_header(filename).startswith(_SQLITE_HEADERS):
|
|
59
|
+
raise OSError("The auxiliary destination contains a SQLite database or journal.")
|
|
60
|
+
for suffix in _SIDECAR_SUFFIXES:
|
|
61
|
+
if filename.casefold().endswith(suffix):
|
|
62
|
+
if _read_header(filename[:-len(suffix)]).startswith(_SQLITE_HEADER):
|
|
63
|
+
raise OSError("The auxiliary destination is reserved for a SQLite sidecar.")
|
qplot/_metadata.py
ADDED
qplot/_repair.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from os.path import isfile
|
|
2
|
+
|
|
3
|
+
from qcodes.dataset.sqlite.database import get_DB_location
|
|
4
|
+
|
|
5
|
+
from qplot.datahandling.readonly import qcodes_read_only_connection
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def repair():
|
|
9
|
+
"""
|
|
10
|
+
Attempts to remove SQL lock that can happens on crashes while in IDE
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
if isfile(get_DB_location()): # close conn is already open by mistake
|
|
14
|
+
qcodes_read_only_connection(get_DB_location()).close()
|