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,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())