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.
- qplot/__init__.py +46 -0
- qplot/__main__.py +232 -0
- qplot/_auxiliary_paths.py +67 -0
- qplot/_build_info.json +1 -0
- qplot/_metadata.py +75 -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 +578 -0
- qplot/configuration/config_schema.json +357 -0
- qplot/configuration/fonts.py +3 -0
- qplot/configuration/scripts.py +255 -0
- qplot/configuration/themes/__init__.py +13 -0
- qplot/configuration/themes/_base.py +592 -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 +2340 -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 +2566 -0
- qplot/datahandling/source_activity.py +21 -0
- qplot/datahandling/trusted_array_preview.py +148 -0
- qplot/datahandling/trusted_derived_cache.py +1605 -0
- qplot/datahandling/trusted_derived_rendering.py +876 -0
- qplot/datahandling/trusted_heatmap.py +246 -0
- qplot/datahandling/trusted_live.py +2808 -0
- qplot/datahandling/trusted_live_queries.py +5907 -0
- qplot/datahandling/trusted_live_service.py +1919 -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 +1804 -0
- qplot/tools/__init__.py +13 -0
- qplot/tools/finite_means.py +123 -0
- qplot/tools/general.py +139 -0
- qplot/tools/heatmap_geometry.py +391 -0
- qplot/tools/operation_registry.py +179 -0
- qplot/tools/plot_tools.py +650 -0
- qplot/tools/sample_statistics.py +111 -0
- qplot/tools/worker.py +2838 -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 +4933 -0
- qplot/windows/_dataset_handle.py +221 -0
- qplot/windows/_display_precision.py +48 -0
- qplot/windows/_dragdrop.py +70 -0
- qplot/windows/_export_paths.py +1167 -0
- qplot/windows/_help.py +210 -0
- qplot/windows/_mixed_appearance.py +303 -0
- qplot/windows/_mixed_plots.py +461 -0
- qplot/windows/_native_averaging.py +323 -0
- qplot/windows/_native_transforms.py +551 -0
- qplot/windows/_plot1d_snap.py +612 -0
- qplot/windows/_plot1d_traces.py +2236 -0
- qplot/windows/_plot2d_colorbar.py +1127 -0
- qplot/windows/_plot2d_colorbar_dialog.py +786 -0
- qplot/windows/_plot2d_layers.py +2291 -0
- qplot/windows/_plot2d_sweeps.py +712 -0
- qplot/windows/_plotWin.py +2082 -0
- qplot/windows/_plot_actions.py +2703 -0
- qplot/windows/_plot_annotation.py +186 -0
- qplot/windows/_plot_appearance.py +283 -0
- qplot/windows/_plot_axis_scaling.py +1521 -0
- qplot/windows/_plot_data_viewbox.py +94 -0
- qplot/windows/_plot_drop_axes.py +224 -0
- qplot/windows/_plot_export.py +1445 -0
- qplot/windows/_plot_feedback.py +112 -0
- qplot/windows/_plot_marquee.py +659 -0
- qplot/windows/_plot_refresh.py +950 -0
- qplot/windows/_plot_source_promotion.py +178 -0
- qplot/windows/_plot_state.py +237 -0
- qplot/windows/_plot_status.py +115 -0
- qplot/windows/_plot_tiling.py +35 -0
- qplot/windows/_plot_transform_labels.py +60 -0
- qplot/windows/_preferences.py +724 -0
- qplot/windows/_refresh_interval.py +172 -0
- qplot/windows/_run_controls.py +789 -0
- qplot/windows/_shortcuts.py +47 -0
- qplot/windows/_subplots/__init__.py +8 -0
- qplot/windows/_subplots/subplot1d.py +532 -0
- qplot/windows/_subplots/subplot2d.py +919 -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 +414 -0
- qplot/windows/main.py +1632 -0
- qplot/windows/plot1d.py +313 -0
- qplot/windows/plot2d.py +1935 -0
- qplotter-1.6.0.dist-info/METADATA +322 -0
- qplotter-1.6.0.dist-info/RECORD +130 -0
- qplotter-1.6.0.dist-info/WHEEL +5 -0
- qplotter-1.6.0.dist-info/entry_points.txt +4 -0
- qplotter-1.6.0.dist-info/licenses/LICENSE +21 -0
- qplotter-1.6.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Filesystem evidence of acquisition activity, without opening SQLite files."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import stat
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def source_activity_timestamp(database_path):
|
|
8
|
+
"""Return the latest main/WAL modification time; never use SHM or the clock.
|
|
9
|
+
|
|
10
|
+
This is a database-wide estimate for unfinished runs, not a recorded run
|
|
11
|
+
completion time. Merely viewing an idle database cannot advance it.
|
|
12
|
+
"""
|
|
13
|
+
timestamps = []
|
|
14
|
+
for path in (os.fspath(database_path), f"{os.fspath(database_path)}-wal"):
|
|
15
|
+
try:
|
|
16
|
+
observation = os.stat(path)
|
|
17
|
+
except OSError:
|
|
18
|
+
continue
|
|
19
|
+
if stat.S_ISREG(observation.st_mode) and observation.st_size:
|
|
20
|
+
timestamps.append(observation.st_mtime)
|
|
21
|
+
return max(timestamps, default=None)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Bounded QCoDeS array previews using stored coordinates within each record."""
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import math
|
|
5
|
+
from bisect import bisect_left
|
|
6
|
+
from dataclasses import replace
|
|
7
|
+
from fractions import Fraction
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from qplot.tools.sample_statistics import finite_mean
|
|
12
|
+
|
|
13
|
+
from .trusted_live_queries import TRUSTED_DERIVED_MAX_SAMPLE_ROWS
|
|
14
|
+
|
|
15
|
+
MAX_SAMPLES = 131_072
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _decode(value):
|
|
19
|
+
from .trusted_derived_rendering import _UnsupportedNumericData
|
|
20
|
+
|
|
21
|
+
if not isinstance(value, bytes):
|
|
22
|
+
return np.asarray([value])
|
|
23
|
+
try:
|
|
24
|
+
stream = io.BytesIO(value)
|
|
25
|
+
version = np.lib.format.read_magic(stream)
|
|
26
|
+
if version not in {(1, 0), (2, 0), (3, 0)}:
|
|
27
|
+
raise ValueError("Unsupported NPY version")
|
|
28
|
+
# Numeric headers are ASCII in both v2 and QCoDeS' v3; the latter
|
|
29
|
+
# changes only the encoding of structured dtype field names, which
|
|
30
|
+
# this renderer rejects below.
|
|
31
|
+
reader = (np.lib.format.read_array_header_1_0 if version == (1, 0)
|
|
32
|
+
else np.lib.format.read_array_header_2_0)
|
|
33
|
+
shape, fortran, dtype = reader(stream)
|
|
34
|
+
count = math.prod(shape)
|
|
35
|
+
if dtype.kind not in "biuf" or not 0 <= count <= MAX_SAMPLES:
|
|
36
|
+
raise ValueError("Unsupported array dtype or size")
|
|
37
|
+
if len(value) - stream.tell() != count * dtype.itemsize:
|
|
38
|
+
raise ValueError("Invalid array payload size")
|
|
39
|
+
# Validate the header and exact payload length before allocating. Never
|
|
40
|
+
# load pickled object arrays or allocate from an unchecked NPY shape.
|
|
41
|
+
return np.frombuffer(value, dtype=dtype, offset=stream.tell()).reshape(
|
|
42
|
+
shape, order="F" if fortran else "C",
|
|
43
|
+
).ravel()
|
|
44
|
+
except (ValueError, KeyError, EOFError, OverflowError) as error:
|
|
45
|
+
raise _UnsupportedNumericData("The array sample cannot be decoded safely.") from error
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def render_array_preview(observation, dependencies, dependent, width, height, check):
|
|
49
|
+
from .trusted_derived_rendering import (
|
|
50
|
+
_MISSING_CELL_RGBA,
|
|
51
|
+
_finite_number,
|
|
52
|
+
_normalised_position,
|
|
53
|
+
_pixel,
|
|
54
|
+
_render_1d,
|
|
55
|
+
_UnsupportedNumericData,
|
|
56
|
+
_viridis_rgba,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
indexes = [observation.sample_columns.index(name)
|
|
60
|
+
for name in (*dependencies, dependent)]
|
|
61
|
+
points = []
|
|
62
|
+
expanded_count = 0
|
|
63
|
+
for row in observation.sample_rows:
|
|
64
|
+
check()
|
|
65
|
+
if any(row[index] is None for index in indexes):
|
|
66
|
+
continue
|
|
67
|
+
columns = [_decode(row[index]) for index in indexes]
|
|
68
|
+
size = max(column.size for column in columns)
|
|
69
|
+
if any(column.size != (size if isinstance(row[source], bytes) else 1)
|
|
70
|
+
for source, column in zip(indexes, columns, strict=True)):
|
|
71
|
+
raise _UnsupportedNumericData("Array measurement and setpoint sizes differ.")
|
|
72
|
+
expanded_count += size
|
|
73
|
+
if expanded_count > MAX_SAMPLES:
|
|
74
|
+
raise _UnsupportedNumericData("The array preview exceeds its sample budget.")
|
|
75
|
+
for index in range(size):
|
|
76
|
+
if index % 128 == 0:
|
|
77
|
+
check()
|
|
78
|
+
# NPY Boolean arrays are numeric acquired samples. Preserve
|
|
79
|
+
# integers too: rounding before grouping can merge adjacent
|
|
80
|
+
# recorded coordinates above float64's integer precision.
|
|
81
|
+
samples = (column[0 if column.size == 1 else index].item()
|
|
82
|
+
for column in columns)
|
|
83
|
+
values = tuple(int(value) if isinstance(value, (bool, int))
|
|
84
|
+
else _finite_number(value)
|
|
85
|
+
for value in samples)
|
|
86
|
+
if all(value is not None for value in values[:-1]):
|
|
87
|
+
points.append(values)
|
|
88
|
+
if not points:
|
|
89
|
+
raise _UnsupportedNumericData("No bounded numeric array samples are available.")
|
|
90
|
+
if len(dependencies) == 1:
|
|
91
|
+
# Keep both endpoints while limiting the ordinary line renderer's input.
|
|
92
|
+
selected = np.linspace(0, len(points) - 1,
|
|
93
|
+
min(len(points), TRUSTED_DERIVED_MAX_SAMPLE_ROWS), dtype=int)
|
|
94
|
+
rows = tuple((i + 1, *points[index]) for i, index in enumerate(selected))
|
|
95
|
+
sampled = replace(observation, sample_columns=("id", *dependencies, dependent),
|
|
96
|
+
sample_rows=rows, validated_2d_layouts=())
|
|
97
|
+
return _render_1d(sampled, dependencies[0], dependent, width, height, check)
|
|
98
|
+
|
|
99
|
+
# Build cells from actual paired coordinates, never from planned shapes or
|
|
100
|
+
# physical row IDs (one QCoDeS row can contain an entire oscilloscope trace).
|
|
101
|
+
vertical = sorted({point[0] for point in points})
|
|
102
|
+
horizontal = sorted({point[1] for point in points})
|
|
103
|
+
if len(vertical) * len(horizontal) > MAX_SAMPLES:
|
|
104
|
+
raise _UnsupportedNumericData("The array preview grid exceeds its cell budget.")
|
|
105
|
+
cells = {}
|
|
106
|
+
for y, x, z in points:
|
|
107
|
+
if z is not None:
|
|
108
|
+
cells.setdefault((y, x), []).append(z)
|
|
109
|
+
if not cells:
|
|
110
|
+
raise _UnsupportedNumericData("No finite array measurements are available.")
|
|
111
|
+
values = {}
|
|
112
|
+
for key, samples in cells.items():
|
|
113
|
+
check()
|
|
114
|
+
if len(samples) == 1:
|
|
115
|
+
values[key] = samples[0]
|
|
116
|
+
continue
|
|
117
|
+
has_integers = any(isinstance(value, int) for value in samples)
|
|
118
|
+
if has_integers:
|
|
119
|
+
# A float mean would erase the small differences between nearby
|
|
120
|
+
# integer cells. Keep its exact offset until color normalization.
|
|
121
|
+
total = Fraction()
|
|
122
|
+
for index, value in enumerate(samples):
|
|
123
|
+
if index % 1024 == 0:
|
|
124
|
+
check()
|
|
125
|
+
total += Fraction(value)
|
|
126
|
+
values[key] = total / len(samples)
|
|
127
|
+
continue
|
|
128
|
+
# Scaling every source cell by the largest loses small cancellation
|
|
129
|
+
# residuals. The common finite mean retains the original samples.
|
|
130
|
+
values[key] = finite_mean(np.array(samples), check_cancelled=check)
|
|
131
|
+
low, high = min(values.values()), max(values.values())
|
|
132
|
+
|
|
133
|
+
def pixel_axis(axis, count):
|
|
134
|
+
normalized = [_normalised_position(value, axis[0], axis[-1]) for value in axis]
|
|
135
|
+
edges = [(left + right) / 2
|
|
136
|
+
for left, right in zip(normalized, normalized[1:], strict=False)]
|
|
137
|
+
return [axis[bisect_left(edges, index / max(1, count - 1))]
|
|
138
|
+
for index in range(count)]
|
|
139
|
+
|
|
140
|
+
xs, ys = pixel_axis(horizontal, width), pixel_axis(vertical, height)[::-1]
|
|
141
|
+
rgba = bytearray(width * height * 4)
|
|
142
|
+
for py, y in enumerate(ys):
|
|
143
|
+
check()
|
|
144
|
+
for px, x in enumerate(xs):
|
|
145
|
+
value = values.get((y, x))
|
|
146
|
+
color = _MISSING_CELL_RGBA if value is None else _viridis_rgba(value, low, high)
|
|
147
|
+
_pixel(rgba, width, px, py, color)
|
|
148
|
+
return rgba, sum(len(samples) for samples in cells.values())
|