qplotter 1.6.1__tar.gz → 1.6.2__tar.gz

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 (417) hide show
  1. {qplotter-1.6.1 → qplotter-1.6.2}/CHANGELOG.md +39 -0
  2. {qplotter-1.6.1 → qplotter-1.6.2}/PKG-INFO +10 -5
  3. {qplotter-1.6.1 → qplotter-1.6.2}/README.md +9 -4
  4. {qplotter-1.6.1 → qplotter-1.6.2}/docs/demo-data.md +7 -0
  5. {qplotter-1.6.1 → qplotter-1.6.2}/docs/distribution.md +20 -19
  6. {qplotter-1.6.1 → qplotter-1.6.2}/pyproject.toml +1 -1
  7. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/benchmark_plot_launch.py +23 -10
  8. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/profile_trusted_reads.py +14 -4
  9. qplotter-1.6.2/src/qplot/_build_info.json +1 -0
  10. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/_metadata.py +14 -5
  11. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/config_schema.json +7 -1
  12. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/LoadFromDB.py +14 -1
  13. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/_trusted_live_helper.py +9 -4
  14. qplotter-1.6.2/src/qplot/datahandling/parameter_data.py +345 -0
  15. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/qcodes_cache.py +17 -1
  16. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/readonly.py +27 -0
  17. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_array_preview.py +44 -9
  18. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_derived_rendering.py +7 -3
  19. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_live.py +12 -1
  20. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_live_queries.py +38 -7
  21. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_plot.py +13 -3
  22. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/diagnostics.py +42 -7
  23. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/testdata.py +38 -2
  24. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/general.py +12 -5
  25. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/plot_tools.py +113 -17
  26. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/sample_statistics.py +12 -3
  27. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/worker.py +15 -1
  28. qplotter-1.6.2/src/qplot/windows/_csv_filenames.py +19 -0
  29. qplotter-1.6.2/src/qplot/windows/_dataset_statistics.py +36 -0
  30. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_display_precision.py +3 -2
  31. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_mixed_plots.py +3 -2
  32. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_native_transforms.py +52 -9
  33. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plotWin.py +3 -3
  34. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_actions.py +100 -6
  35. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_drop_axes.py +12 -2
  36. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_export.py +23 -8
  37. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_marquee.py +31 -23
  38. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_refresh.py +20 -0
  39. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_source_promotion.py +31 -3
  40. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_preferences.py +17 -0
  41. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_run_controls.py +2 -0
  42. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_subplots/subplot2d.py +3 -1
  43. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/preview.py +9 -0
  44. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/run_list_items.py +7 -0
  45. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/treeWidgets.py +6 -0
  46. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplotter.egg-info/SOURCES.txt +40 -0
  47. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_array_preview.py +1 -1
  48. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_derivative_cancellation.py +1 -1
  49. qplotter-1.6.2/tests/test_campaign_array_expansion_budget.py +221 -0
  50. qplotter-1.6.2/tests/test_campaign_boolean_derivative.py +195 -0
  51. qplotter-1.6.2/tests/test_campaign_clipped_trace_reparenting.py +63 -0
  52. qplotter-1.6.2/tests/test_campaign_diagnostics.py +104 -0
  53. qplotter-1.6.2/tests/test_campaign_empty_incremental_records.py +157 -0
  54. qplotter-1.6.2/tests/test_campaign_extended_float_centering.py +109 -0
  55. qplotter-1.6.2/tests/test_campaign_extended_float_derivative.py +109 -0
  56. qplotter-1.6.2/tests/test_campaign_extended_float_display.py +135 -0
  57. qplotter-1.6.2/tests/test_campaign_extended_float_fft.py +159 -0
  58. qplotter-1.6.2/tests/test_campaign_extended_float_heatmap.py +88 -0
  59. qplotter-1.6.2/tests/test_campaign_extended_float_previews.py +199 -0
  60. qplotter-1.6.2/tests/test_campaign_generator_backend.py +178 -0
  61. qplotter-1.6.2/tests/test_campaign_generator_input.py +37 -0
  62. qplotter-1.6.2/tests/test_campaign_heatmap_csv_precision.py +76 -0
  63. qplotter-1.6.2/tests/test_campaign_inferred_array_missing_cells.py +524 -0
  64. qplotter-1.6.2/tests/test_campaign_inferred_parameter_loading.py +259 -0
  65. qplotter-1.6.2/tests/test_campaign_live_schema_refresh.py +121 -0
  66. qplotter-1.6.2/tests/test_campaign_log_symlink_loops.py +81 -0
  67. qplotter-1.6.2/tests/test_campaign_measurement_coordinate_gaps.py +115 -0
  68. qplotter-1.6.2/tests/test_campaign_measurement_longdouble_csv.py +91 -0
  69. qplotter-1.6.2/tests/test_campaign_metadata_omission_recovery.py +154 -0
  70. qplotter-1.6.2/tests/test_campaign_mixed_extended_clipping.py +124 -0
  71. qplotter-1.6.2/tests/test_campaign_mixed_extended_finiteness.py +119 -0
  72. qplotter-1.6.2/tests/test_campaign_narrow_float_clipping.py +115 -0
  73. qplotter-1.6.2/tests/test_campaign_profiling_scripts.py +182 -0
  74. qplotter-1.6.2/tests/test_campaign_safety_cancellation.py +162 -0
  75. qplotter-1.6.2/tests/test_campaign_singleton_array_previews.py +448 -0
  76. qplotter-1.6.2/tests/test_campaign_split_storage_safety.py +148 -0
  77. qplotter-1.6.2/tests/test_campaign_ui_cut_axis_refresh.py +43 -0
  78. qplotter-1.6.2/tests/test_campaign_ui_cut_ownership.py +69 -0
  79. qplotter-1.6.2/tests/test_campaign_ui_manual_refresh.py +280 -0
  80. qplotter-1.6.2/tests/test_campaign_ui_paused_promotion.py +82 -0
  81. qplotter-1.6.2/tests/test_campaign_ui_pending_drop.py +110 -0
  82. qplotter-1.6.2/tests/test_campaign_ui_promoted_cut_mapping.py +64 -0
  83. qplotter-1.6.2/tests/test_campaign_ui_promotion.py +93 -0
  84. qplotter-1.6.2/tests/test_campaign_wal_artifact_observation.py +107 -0
  85. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_config.py +15 -0
  86. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_version_info.py +60 -0
  87. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_run_details.py +3 -3
  88. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_bounded_shutdown.py +111 -18
  89. qplotter-1.6.2/tests/windows/test_csv_filenames.py +42 -0
  90. qplotter-1.6.2/tests/windows/test_dataset_statistics.py +101 -0
  91. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_heatmap_layer_integration.py +18 -8
  92. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot2d_layers.py +48 -3
  93. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_annotation.py +8 -3
  94. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_tiling.py +7 -6
  95. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_preferences.py +4 -0
  96. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_refresh_lifecycle.py +1 -0
  97. qplotter-1.6.1/src/qplot/_build_info.json +0 -1
  98. qplotter-1.6.1/src/qplot/datahandling/parameter_data.py +0 -149
  99. {qplotter-1.6.1 → qplotter-1.6.2}/Agents.md +0 -0
  100. {qplotter-1.6.1 → qplotter-1.6.2}/CONTRIBUTING.md +0 -0
  101. {qplotter-1.6.1 → qplotter-1.6.2}/LICENSE +0 -0
  102. {qplotter-1.6.1 → qplotter-1.6.2}/MANIFEST.in +0 -0
  103. {qplotter-1.6.1 → qplotter-1.6.2}/docs/architecture.md +0 -0
  104. {qplotter-1.6.1 → qplotter-1.6.2}/docs/assets/qplot-color-scale-dialog.png +0 -0
  105. {qplotter-1.6.1 → qplotter-1.6.2}/docs/assets/qplot-heatmap.png +0 -0
  106. {qplotter-1.6.1 → qplotter-1.6.2}/docs/assets/qplot-line-plot.png +0 -0
  107. {qplotter-1.6.1 → qplotter-1.6.2}/docs/assets/qplot-main-window.png +0 -0
  108. {qplotter-1.6.1 → qplotter-1.6.2}/docs/configuration.md +0 -0
  109. {qplotter-1.6.1 → qplotter-1.6.2}/docs/troubleshooting.md +0 -0
  110. {qplotter-1.6.1 → qplotter-1.6.2}/docs/trusted-live-reader.md +0 -0
  111. {qplotter-1.6.1 → qplotter-1.6.2}/docs/user-guide.md +0 -0
  112. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/README.md +0 -0
  113. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/benchmark_heatmap_aggregation.py +0 -0
  114. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/capture_demo_screenshots.py +0 -0
  115. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/liveplot.py +0 -0
  116. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/manual_run.py +0 -0
  117. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/release.py +0 -0
  118. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/revalidate_published_release.py +0 -0
  119. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/time_stress.py +0 -0
  120. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/validate_compiler_free_install.py +0 -0
  121. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/validate_distribution.py +0 -0
  122. {qplotter-1.6.1 → qplotter-1.6.2}/scripts/write_build_info.py +0 -0
  123. {qplotter-1.6.1 → qplotter-1.6.2}/setup.cfg +0 -0
  124. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/__init__.py +0 -0
  125. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/__main__.py +0 -0
  126. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/_auxiliary_paths.py +0 -0
  127. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/_repair.py +0 -0
  128. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/_shutdown_supervisor.py +0 -0
  129. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/_version.py +0 -0
  130. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/_windows_shutdown_job.py +0 -0
  131. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/__init__.py +0 -0
  132. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/config.py +0 -0
  133. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/fonts.py +0 -0
  134. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/scripts.py +0 -0
  135. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/themes/__init__.py +0 -0
  136. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/themes/_base.py +0 -0
  137. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/themes/blank.py +0 -0
  138. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/themes/dark.py +0 -0
  139. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/configuration/themes/light.py +0 -0
  140. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/__init__.py +0 -0
  141. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/_trusted_live_protocol.py +0 -0
  142. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/database.py +0 -0
  143. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/dimensions.py +0 -0
  144. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/file_identity.py +0 -0
  145. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/plot_progress.py +0 -0
  146. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/qcodes_compat.py +0 -0
  147. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/readSQL.py +0 -0
  148. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/source_activity.py +0 -0
  149. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_derived_cache.py +0 -0
  150. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_heatmap.py +0 -0
  151. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_live_service.py +0 -0
  152. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_live_supervisor.py +0 -0
  153. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_presentation.py +0 -0
  154. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_snapshot.py +0 -0
  155. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_work_coordinator.py +0 -0
  156. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/datahandling/trusted_work_scheduler.py +0 -0
  157. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_01_10mb.csv +0 -0
  158. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_02_25mb.csv +0 -0
  159. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_03_50mb.csv +0 -0
  160. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_04_100mb.csv +0 -0
  161. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_05_250mb.csv +0 -0
  162. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_06_500mb.csv +0 -0
  163. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_07_1gb.csv +0 -0
  164. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_08_5gb.csv +0 -0
  165. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_09_10gb.csv +0 -0
  166. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/resources/testdata/qplot_test_db_10_30gb.csv +0 -0
  167. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/__init__.py +0 -0
  168. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/finite_means.py +0 -0
  169. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/heatmap_geometry.py +0 -0
  170. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/tools/operation_registry.py +0 -0
  171. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/__init__.py +0 -0
  172. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_auto_plot.py +0 -0
  173. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_colorbar.py +0 -0
  174. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_commands.py +0 -0
  175. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_config_persistence.py +0 -0
  176. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_database_actions.py +0 -0
  177. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_dataset_handle.py +0 -0
  178. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_dragdrop.py +0 -0
  179. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_export_paths.py +0 -0
  180. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_help.py +0 -0
  181. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_mixed_appearance.py +0 -0
  182. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_native_averaging.py +0 -0
  183. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot1d_snap.py +0 -0
  184. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot1d_traces.py +0 -0
  185. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot2d_colorbar.py +0 -0
  186. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot2d_colorbar_dialog.py +0 -0
  187. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot2d_layers.py +0 -0
  188. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot2d_sweeps.py +0 -0
  189. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_annotation.py +0 -0
  190. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_appearance.py +0 -0
  191. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_axis_scaling.py +0 -0
  192. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_data_viewbox.py +0 -0
  193. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_feedback.py +0 -0
  194. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_state.py +0 -0
  195. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_status.py +0 -0
  196. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_tiling.py +0 -0
  197. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_plot_transform_labels.py +0 -0
  198. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_refresh_interval.py +0 -0
  199. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_shortcuts.py +0 -0
  200. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_subplots/__init__.py +0 -0
  201. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_subplots/subplot1d.py +0 -0
  202. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_svg_export.py +0 -0
  203. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_trusted_derived_qt.py +0 -0
  204. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/__init__.py +0 -0
  205. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/_run_formatting.py +0 -0
  206. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/details_tables.py +0 -0
  207. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/dropbox.py +0 -0
  208. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/operations.py +0 -0
  209. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_widgets/toolbar.py +0 -0
  210. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/_window_controls.py +0 -0
  211. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/main.py +0 -0
  212. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/plot1d.py +0 -0
  213. {qplotter-1.6.1 → qplotter-1.6.2}/src/qplot/windows/plot2d.py +0 -0
  214. {qplotter-1.6.1 → qplotter-1.6.2}/tests/_artifact_audit.py +0 -0
  215. {qplotter-1.6.1 → qplotter-1.6.2}/tests/_ci_scheduling.py +0 -0
  216. {qplotter-1.6.1 → qplotter-1.6.2}/tests/_window_lifecycle.py +0 -0
  217. {qplotter-1.6.1 → qplotter-1.6.2}/tests/conftest.py +0 -0
  218. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/_wal_lineage_scenarios.py +0 -0
  219. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_array_record_metadata.py +0 -0
  220. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_database_worker_instance_binding.py +0 -0
  221. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_dimensions.py +0 -0
  222. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_inferred_planned_counts.py +0 -0
  223. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_integer_record_cache.py +0 -0
  224. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_live_acquired_point_counts.py +0 -0
  225. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_plot_progress.py +0 -0
  226. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_qcodes_cache.py +0 -0
  227. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_read_sql.py +0 -0
  228. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_read_sql_database_identity.py +0 -0
  229. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_readonly.py +0 -0
  230. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_snapshot_planned_setpoints.py +0 -0
  231. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_stage5c_real_qcodes.py +0 -0
  232. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_array_record_metadata.py +0 -0
  233. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_database_information.py +0 -0
  234. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_derived_cache.py +0 -0
  235. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_derived_cache_ownership.py +0 -0
  236. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_derived_live_integration.py +0 -0
  237. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_derived_queries.py +0 -0
  238. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_derived_rendering.py +0 -0
  239. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_heatmap.py +0 -0
  240. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live.py +0 -0
  241. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_partial_frames.py +0 -0
  242. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_protocol.py +0 -0
  243. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_queries.py +0 -0
  244. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_result_limits.py +0 -0
  245. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_service.py +0 -0
  246. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_stage4_integration.py +0 -0
  247. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_supervisor.py +0 -0
  248. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_live_worker_integration.py +0 -0
  249. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_plot.py +0 -0
  250. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_presentation.py +0 -0
  251. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_read_scope.py +0 -0
  252. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_snapshot.py +0 -0
  253. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_source_identifiers.py +0 -0
  254. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_wal_contention.py +0 -0
  255. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_work_coordinator.py +0 -0
  256. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_trusted_work_scheduler.py +0 -0
  257. {qplotter-1.6.1 → qplotter-1.6.2}/tests/datahandling/test_wal_lineage_subprocess.py +0 -0
  258. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_auxiliary_database_protection.py +0 -0
  259. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bounded_heatmap_means.py +0 -0
  260. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bounded_integer_precision.py +0 -0
  261. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_array_bin_ranges.py +0 -0
  262. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_auxiliary_safety.py +0 -0
  263. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_average_view_updates.py +0 -0
  264. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_bounded_mixed_means.py +0 -0
  265. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_config.py +0 -0
  266. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_constant_auto_recovery.py +0 -0
  267. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_database_refresh.py +0 -0
  268. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_duplicate_mean_cancellation.py +0 -0
  269. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_fill_cancellation.py +0 -0
  270. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_float_average_performance.py +0 -0
  271. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_generator_fifo.py +0 -0
  272. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_generator_safety.py +0 -0
  273. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_heatmap_display_ranges.py +0 -0
  274. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_integer_mean_cancellation.py +0 -0
  275. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_line_display_ranges.py +0 -0
  276. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_longdouble_samples.py +0 -0
  277. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_marquee_statistics_performance.py +0 -0
  278. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_measurement_generator.py +0 -0
  279. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_measurement_lines.py +0 -0
  280. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_measurement_previews.py +0 -0
  281. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_mixed_average_performance.py +0 -0
  282. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_native_averaging.py +0 -0
  283. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_object_scan_cancellation.py +0 -0
  284. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_shaped_coordinate_ranges.py +0 -0
  285. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_snapshot_fifo.py +0 -0
  286. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_tiny_heatmap_bins.py +0 -0
  287. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_ui_cuts.py +0 -0
  288. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_ui_display_precision.py +0 -0
  289. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_bug_hunt_ui_refresh.py +0 -0
  290. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_ci_artifacts.py +0 -0
  291. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_ci_scheduling.py +0 -0
  292. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_compiler_free_install.py +0 -0
  293. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_demo_database_safety.py +0 -0
  294. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_derivative_numeric_ranges.py +0 -0
  295. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_diagnostics.py +0 -0
  296. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_distribution_validator.py +0 -0
  297. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_heatmap_cancellation_residuals.py +0 -0
  298. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_heatmap_float_means.py +0 -0
  299. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_heatmap_geometry.py +0 -0
  300. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_independent_heatmap_extremes.py +0 -0
  301. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_liveplot_database_safety.py +0 -0
  302. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_native_fft_mixed_coordinates.py +0 -0
  303. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_nonuniform_cancellation.py +0 -0
  304. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_package_import.py +0 -0
  305. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_packaging_split.py +0 -0
  306. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_release.py +0 -0
  307. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_sample_statistics_accuracy.py +0 -0
  308. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_shutdown_supervisor.py +0 -0
  309. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_testdata.py +0 -0
  310. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_time_stress_database_safety.py +0 -0
  311. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_tools.py +0 -0
  312. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_windows_ci.py +0 -0
  313. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_windows_shutdown_job.py +0 -0
  314. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_array_heatmaps.py +0 -0
  315. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_array_measurements.py +0 -0
  316. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_boolean_heatmap_missing.py +0 -0
  317. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_complex_heatmaps.py +0 -0
  318. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_complex_lines.py +0 -0
  319. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_coordinate_precision.py +0 -0
  320. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_heatmap_limits.py +0 -0
  321. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_nonfinite_sql_heatmaps.py +0 -0
  322. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_shaped_coordinate_validity.py +0 -0
  323. {qplotter-1.6.1 → qplotter-1.6.2}/tests/test_worker_sidecar_identities.py +0 -0
  324. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_operations.py +0 -0
  325. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_plot_preview_reuse.py +0 -0
  326. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_preview_database_instance.py +0 -0
  327. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_preview_dragdrop.py +0 -0
  328. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_preview_extreme_range.py +0 -0
  329. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_preview_placeholders.py +0 -0
  330. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_run_list.py +0 -0
  331. {qplotter-1.6.1 → qplotter-1.6.2}/tests/widgets/test_themes.py +0 -0
  332. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/_trusted_derived_wal_ui.py +0 -0
  333. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_appearance_swap_cuts.py +0 -0
  334. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_auto_plot.py +0 -0
  335. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_axis_auto_padding_overflow.py +0 -0
  336. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_axis_global_auto_validation.py +0 -0
  337. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_axis_limit_overflow.py +0 -0
  338. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_axis_limit_precision.py +0 -0
  339. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_centering_extremes.py +0 -0
  340. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_color_scale_locale.py +0 -0
  341. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_commands.py +0 -0
  342. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_complex_line_data.py +0 -0
  343. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_config_transactions.py +0 -0
  344. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_csv_shared_coordinates.py +0 -0
  345. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_cursor_precision_preference.py +0 -0
  346. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_cut_cursor_integration.py +0 -0
  347. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_database_information.py +0 -0
  348. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_database_worker_errors.py +0 -0
  349. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_derivative_scale.py +0 -0
  350. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_differentiation_integration.py +0 -0
  351. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_export_paths.py +0 -0
  352. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_filtered_axis_autoscale.py +0 -0
  353. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_font_preferences.py +0 -0
  354. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_free_cursor_axis_assignment.py +0 -0
  355. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_heatmap_appearance.py +0 -0
  356. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_heatmap_color_range.py +0 -0
  357. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_heatmap_limit_missing_samples.py +0 -0
  358. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_help_menu.py +0 -0
  359. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_independent_cancellation_mean.py +0 -0
  360. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_independent_native_extremes.py +0 -0
  361. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_limit_integer_integration.py +0 -0
  362. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_live_array_dtypes.py +0 -0
  363. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_main_window.py +0 -0
  364. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_marquee_statistics_accuracy.py +0 -0
  365. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_merged_trace_metadata.py +0 -0
  366. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_mixed_plot_drops.py +0 -0
  367. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_average_cancellation.py +0 -0
  368. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_averaging.py +0 -0
  369. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_boolean_derivatives.py +0 -0
  370. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_fft_coordinates.py +0 -0
  371. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_fft_dynamic_range.py +0 -0
  372. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_fft_float_precision.py +0 -0
  373. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_fft_integer_precision.py +0 -0
  374. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_float_transforms.py +0 -0
  375. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_integer_transforms.py +0 -0
  376. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_transform_labels.py +0 -0
  377. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_native_transform_snapping.py +0 -0
  378. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_nonuniform_gradient_overflow.py +0 -0
  379. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_numeric_sample_preservation.py +0 -0
  380. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_numerical_stability.py +0 -0
  381. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_offset_precision_regressions.py +0 -0
  382. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_pending_preview_drop.py +0 -0
  383. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_planned_integer_array_extent.py +0 -0
  384. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot1d.py +0 -0
  385. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot1d_snap_precision.py +0 -0
  386. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot2d.py +0 -0
  387. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_actions.py +0 -0
  388. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_csv_precision.py +0 -0
  389. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_dialog_lifecycle.py +0 -0
  390. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_integration.py +0 -0
  391. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_overlay_integration.py +0 -0
  392. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_progress_ui.py +0 -0
  393. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_sidecar_binding.py +0 -0
  394. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_plot_window.py +0 -0
  395. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_preferences_lifecycle.py +0 -0
  396. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_preferences_value_preservation.py +0 -0
  397. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_preview_drop_axes.py +0 -0
  398. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_refresh_interval_precision.py +0 -0
  399. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_run_csv_export_race.py +0 -0
  400. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_run_csv_mixed_integer_precision.py +0 -0
  401. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_run_duration_wal.py +0 -0
  402. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_settings_decoder_limits.py +0 -0
  403. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_svg_export_integration.py +0 -0
  404. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_testdata_gui.py +0 -0
  405. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_tiny_tick_labels.py +0 -0
  406. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_derived_live_counts.py +0 -0
  407. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_derived_qt.py +0 -0
  408. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_derived_wal_grid_ui.py +0 -0
  409. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_derived_wal_snapshot_ui.py +0 -0
  410. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_derived_wal_tiers_ui.py +0 -0
  411. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_derived_wal_ui.py +0 -0
  412. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_live_ui.py +0 -0
  413. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_trusted_plot_launch.py +0 -0
  414. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_unloaded_database_export.py +0 -0
  415. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_variable_array_records.py +0 -0
  416. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_visual_smoke.py +0 -0
  417. {qplotter-1.6.1 → qplotter-1.6.2}/tests/windows/test_window_controls.py +0 -0
@@ -2,6 +2,45 @@
2
2
 
3
3
  All notable user-facing changes are recorded here.
4
4
 
5
+ ## 1.6.2 — Unreleased
6
+
7
+ This bugfix release improves measurement correctness, live viewing and plot
8
+ lifecycle reliability. The native dependency remains `qplotter-native==1.0.1`.
9
+
10
+ ### Fixed
11
+
12
+ - Load inferred QCoDeS parameters and missing ancillary array cells, preserving
13
+ recorded samples across previews, plotting, incremental refresh and raw CSV.
14
+ - Handle singleton and empty array acquisitions consistently, including planned
15
+ scan shapes, and check expanded memory requirements before allocating them.
16
+ - Preserve extended floating-point and integer precision in derivatives, FFTs,
17
+ clipping, centering, statistics, heatmaps, previews and CSV exports.
18
+ Honor finite clipping bounds on float16/float32 measurement arrays.
19
+ - Preserve axis meaning and source ownership during mixed-plot promotion,
20
+ cut refresh and drag-and-drop; refresh retained sources while paused and
21
+ cancel pending work when its target window closes.
22
+ - Avoid Qt callback errors when reassigning axes on a clipped trace, and clear
23
+ obsolete omission warnings when live metadata becomes available again.
24
+ - Honor accepted reader cancellation and delayed helper cancellation without
25
+ publishing stale results or unnecessarily retiring healthy readers.
26
+ - Refresh live-reader schema descriptions when QCoDeS adds metadata, so newly
27
+ acquired runs expose their current columns without missing data or query errors.
28
+ - Protect measurement files from diagnostic-log reuse and synthetic generation
29
+ side effects; report malformed generator inputs and profiling failures cleanly.
30
+ Retire benchmark plot windows before Qt application teardown.
31
+ - Allow qPlot to start when the diagnostic log path contains a symlink loop.
32
+
33
+ ### Compatibility and validation
34
+
35
+ - Standard QCoDeS main-database storage remains supported. Runs using the
36
+ optional separate-per-dataset storage backend are refused before opening
37
+ their measurement files, since safe multi-file live viewing is not implemented.
38
+ - Main databases, WAL files and rollback journals remain physically read-only.
39
+ Only the pinned native reader may modify the exact colocated SHM file.
40
+ - Add regression coverage and independently reviewed fixes documented in
41
+ `BUG_HUNT.md`. No legacy settings migrations are introduced. This draft does
42
+ not publish a release.
43
+
5
44
  ## 1.6.1
6
45
 
7
46
  This patch release improves live acquisition viewing and release validation.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qplotter
3
- Version: 1.6.1
3
+ Version: 1.6.2
4
4
  Summary: PyQt-based viewer for completed and running QCoDeS database experiments
5
5
  Author-email: Ben Wordsworth <b.wordsworth@lancaster.ac.uk>, Edward Laird <e.a.laird@lancaster.ac.uk>
6
6
  License-Expression: MIT
@@ -84,6 +84,11 @@ selected run's plain detail view without copying the database. Main-database,
84
84
  WAL, and rollback-journal handles remain read-only; SQLite may update only the
85
85
  exact colocated `-shm` file as transient WAL coordination state.
86
86
 
87
+ Measurement viewing supports QCoDeS's standard `sqlite_main_db` storage backend.
88
+ Runs using the optional `sqlite_per_dataset_db` backend can appear in the run
89
+ list, but their measurement actions are refused before opening the separate
90
+ raw-data files. Safe multi-file live viewing is not yet implemented.
91
+
87
92
  Normal `qplot`, `python -m qplot`, and `qplot.run()` launches put the complete
88
93
  qPlot process tree behind a shutdown boundary before Qt or reader helpers can
89
94
  start. The command-line process is the launcher. A public `qplot.run()` call
@@ -152,7 +157,7 @@ source builds, and release validation.
152
157
  ## Install
153
158
 
154
159
  Install qPlot inside a supported Python virtual environment. This checkout
155
- prepares the stable application release `1.6.1`, with native `1.0.1`.
160
+ prepares the stable application release `1.6.2`, with native `1.0.1`.
156
161
  The installation examples below work after publication; until then, use
157
162
  validated local artifacts as described in [Distribution](docs/distribution.md).
158
163
 
@@ -183,9 +188,9 @@ For ordinary installation after a stable PyPI release is available:
183
188
  python -m pip install --only-binary=:all: qplotter
184
189
  ```
185
190
 
186
- To select version 1.6.1 explicitly after publication:
191
+ To select version 1.6.2 explicitly after publication:
187
192
  ```console
188
- python -m pip install --only-binary=:all: qplotter==1.6.1
193
+ python -m pip install --only-binary=:all: qplotter==1.6.2
189
194
  ```
190
195
 
191
196
  Both commands install the full runtime dependency set as wheels, with no C
@@ -207,7 +212,7 @@ installing the new packages:
207
212
 
208
213
  ```console
209
214
  python -m pip uninstall -y qplot qcodes-plotter qcodes-plotter-native qplotter qplotter-native
210
- python -m pip install --only-binary=:all: qplotter==1.6.1
215
+ python -m pip install --only-binary=:all: qplotter==1.6.2
211
216
  python -m pip check
212
217
  ```
213
218
 
@@ -39,6 +39,11 @@ selected run's plain detail view without copying the database. Main-database,
39
39
  WAL, and rollback-journal handles remain read-only; SQLite may update only the
40
40
  exact colocated `-shm` file as transient WAL coordination state.
41
41
 
42
+ Measurement viewing supports QCoDeS's standard `sqlite_main_db` storage backend.
43
+ Runs using the optional `sqlite_per_dataset_db` backend can appear in the run
44
+ list, but their measurement actions are refused before opening the separate
45
+ raw-data files. Safe multi-file live viewing is not yet implemented.
46
+
42
47
  Normal `qplot`, `python -m qplot`, and `qplot.run()` launches put the complete
43
48
  qPlot process tree behind a shutdown boundary before Qt or reader helpers can
44
49
  start. The command-line process is the launcher. A public `qplot.run()` call
@@ -107,7 +112,7 @@ source builds, and release validation.
107
112
  ## Install
108
113
 
109
114
  Install qPlot inside a supported Python virtual environment. This checkout
110
- prepares the stable application release `1.6.1`, with native `1.0.1`.
115
+ prepares the stable application release `1.6.2`, with native `1.0.1`.
111
116
  The installation examples below work after publication; until then, use
112
117
  validated local artifacts as described in [Distribution](docs/distribution.md).
113
118
 
@@ -138,9 +143,9 @@ For ordinary installation after a stable PyPI release is available:
138
143
  python -m pip install --only-binary=:all: qplotter
139
144
  ```
140
145
 
141
- To select version 1.6.1 explicitly after publication:
146
+ To select version 1.6.2 explicitly after publication:
142
147
  ```console
143
- python -m pip install --only-binary=:all: qplotter==1.6.1
148
+ python -m pip install --only-binary=:all: qplotter==1.6.2
144
149
  ```
145
150
 
146
151
  Both commands install the full runtime dependency set as wheels, with no C
@@ -162,7 +167,7 @@ installing the new packages:
162
167
 
163
168
  ```console
164
169
  python -m pip uninstall -y qplot qcodes-plotter qcodes-plotter-native qplotter qplotter-native
165
- python -m pip install --only-binary=:all: qplotter==1.6.1
170
+ python -m pip install --only-binary=:all: qplotter==1.6.2
166
171
  python -m pip check
167
172
  ```
168
173
 
@@ -65,6 +65,13 @@ database and each run, including elapsed times and whether generation completed,
65
65
  was cancelled, or failed. These appear alongside QCoDeS's existing
66
66
  `Starting experimental run with id:` messages.
67
67
 
68
+ Generation requires QCoDeS's `dataset.raw_data_backend` to be `sqlite_main_db`,
69
+ `dataset.export_automatic` to be false, and
70
+ `subscription.default_subscribers` to be empty. Other settings are rejected
71
+ before creating files because they can write outside the selected output.
72
+ qPlot leaves these global settings unchanged and uses an empty private station
73
+ so synthetic measurements do not read parameters from an acquisition station.
74
+
68
75
  Every nonblank CSV row creates one run, named `run_1`, `run_2`, and so on. A 1D
69
76
  row sweeps `V_SD`; a 2D row sweeps both `V_SD` and `V_G`. Each measured signal
70
77
  is the sum of two sinusoids with independently randomised amplitudes,
@@ -29,15 +29,15 @@ virtual environment with:
29
29
  python -m pip install --only-binary=:all: qplotter
30
30
  ```
31
31
 
32
- The current development target is the stable application release `1.6.1`,
32
+ The current development target is the stable application release `1.6.2`,
33
33
  with exact native dependency `1.0.1`. Existing candidate tags do not establish
34
34
  that publication succeeded. Preparing a PR does not publish either package.
35
35
 
36
- The prepared stable application release is `1.6.1`, with exact native
36
+ The prepared stable application release is `1.6.2`, with exact native
37
37
  dependency `1.0.1`. Once published, install that release explicitly:
38
38
 
39
39
  ```console
40
- python -m pip install --only-binary=:all: qplotter==1.6.1
40
+ python -m pip install --only-binary=:all: qplotter==1.6.2
41
41
  ```
42
42
 
43
43
  The earlier `native-and-app/v1.6.0b2` candidate did not pass its complete hosted
@@ -59,7 +59,7 @@ wheel paths from this revision's validated artifacts together. For example,
59
59
  on Windows x64 (replace the application version when appropriate):
60
60
 
61
61
  ```console
62
- python -m pip install --only-binary=:all: dist/qplotter_native-1.0.1-cp311-abi3-win_amd64.whl dist/qplotter-1.6.1-py3-none-any.whl
62
+ python -m pip install --only-binary=:all: dist/qplotter_native-1.0.1-cp311-abi3-win_amd64.whl dist/qplotter-1.6.2-py3-none-any.whl
63
63
  ```
64
64
 
65
65
  Use your platform's native filename on macOS/Linux. Never mix artifacts from
@@ -91,7 +91,7 @@ application names before installing the replacement:
91
91
 
92
92
  ```console
93
93
  python -m pip uninstall -y qplot qcodes-plotter qcodes-plotter-native qplotter qplotter-native
94
- python -m pip install --only-binary=:all: qplotter==1.6.1
94
+ python -m pip install --only-binary=:all: qplotter==1.6.2
95
95
  python -m pip check
96
96
  python -c "from importlib.metadata import packages_distributions; p = packages_distributions(); assert set(p['qplot']) == {'qplotter'}; assert set(p['qplot_native']) == {'qplotter-native'}"
97
97
  ```
@@ -399,8 +399,8 @@ application version from `pyproject.toml`:
399
399
 
400
400
  | Tag | Native handling | PyPI uploads |
401
401
  | --- | --- | --- |
402
- | `native-and-app/v1.6.1` | Build/validate native `1.0.1` at this revision, if unpublished | Native wheels first, then application wheel/sdist |
403
- | `v1.6.1` | Fetch/validate the application's exact published native pin | Application wheel/sdist only |
402
+ | `native-and-app/v1.6.2` | Build/validate native `1.0.1` at this revision, if unpublished | Native wheels first, then application wheel/sdist |
403
+ | `v1.6.2` | Fetch/validate the application's exact published native pin | Application wheel/sdist only |
404
404
 
405
405
  The workflow calls the entire CI workflow at the tagged revision. Failure of
406
406
  any build, static check, source-distribution suite, database-protection suite,
@@ -432,7 +432,7 @@ After application publication and byte verification, `public-installations`
432
432
  runs on all four platforms and every advertised CPython version (3.11–3.14).
433
433
  Each entry creates two fresh environments outside the original checkout.
434
434
  The ordinary path runs `python -m pip install --only-binary=:all:
435
- qplotter==1.6.1` against the public PyPI index with all runtime dependencies.
435
+ qplotter==1.6.2` against the public PyPI index with all runtime dependencies.
436
436
  The editable path fetches the exact tested commit into a fresh checkout and
437
437
  installs `.[dev]` with `qplotter-native==1.0.1` resolved from public PyPI.
438
438
  Neither path installs a local release wheel; the validated artifacts are only
@@ -491,13 +491,14 @@ and [pending-publisher instructions](https://docs.pypi.org/trusted-publishers/cr
491
491
  release version before tagging; never move a published tag. If migrating
492
492
  from token publishing, revoke/remove the old upload tokens/secrets.
493
493
 
494
- ### Paired release commands
494
+ ### Application 1.6.2 release commands
495
495
 
496
- Version 1.6.1 includes native-reader changes, so release native 1.0.1 with
497
- the paired tag below. Keep the already published native 1.0.0 unchanged.
496
+ Version 1.6.2 contains Python-only changes and retains the published native
497
+ 1.0.1 dependency. Use the application-only tag below; published native files
498
+ remain unchanged.
498
499
 
499
500
  Complete the account setup and merge the reviewed packaging/release changes
500
- to `main`. With application `1.6.1` and native `1.0.1`, use the configured
501
+ to `main`. With application `1.6.2` and native `1.0.1`, use the configured
501
502
  Git remote/credentials:
502
503
 
503
504
  ```console
@@ -505,22 +506,22 @@ git fetch origin
505
506
  git switch main
506
507
  git pull --ff-only origin main
507
508
  python scripts/validate_distribution.py --check-clean
508
- git tag -a native-and-app/v1.6.1 -m "qPlot 1.6.1 with native reader 1.0.1"
509
- git push origin refs/tags/native-and-app/v1.6.1
509
+ git tag -a v1.6.2 -m "qPlot 1.6.2"
510
+ git push origin refs/tags/v1.6.2
510
511
  ```
511
512
 
512
513
  Use the activated project venv for Python; on macOS set
513
514
  `MPLCONFIGDIR=/private/tmp/qplot-matplotlib-cache` for Python commands that may
514
515
  import Matplotlib. The tag push is the publication trigger. Review its hosted
515
- CI results, then approve the `pypi-native` environment. Confirm native
516
- verification succeeds before approving `pypi-application`. The final PyPI
516
+ CI results and confirm verification of the published native dependency
517
+ succeeds before approving `pypi-application`. No native publication is needed. The final PyPI
517
518
  verification and all `public-installations` jobs must succeed before announcing the release. No GitHub Release
518
519
  object is needed to trigger publication; use the connected GitHub app for any
519
520
  subsequent release/PR operations if supported, never `gh`.
520
521
 
521
522
  ### Later Python-only releases
522
523
 
523
- Update the application version (for example to `1.6.0b4`) and changelog, keep
524
+ Update the application version (for example to `1.6.3`) and changelog, keep
524
525
  the exact native/APSW pins when still compatible, and merge the changes. Do
525
526
  not change native sources/ABI under an existing native version. Then:
526
527
 
@@ -529,8 +530,8 @@ git fetch origin
529
530
  git switch main
530
531
  git pull --ff-only origin main
531
532
  python scripts/validate_distribution.py --check-clean
532
- git tag -a v1.6.0b4 -m "qPlot 1.6.0b4"
533
- git push origin refs/tags/v1.6.0b4
533
+ git tag -a v1.6.3 -m "qPlot 1.6.3"
534
+ git push origin refs/tags/v1.6.3
534
535
  ```
535
536
 
536
537
  The native release must already contain all four non-yanked compatible wheels.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qplotter"
7
- version = "1.6.1"
7
+ version = "1.6.2"
8
8
  description = "PyQt-based viewer for completed and running QCoDeS database experiments"
9
9
  readme = "README.md"
10
10
  authors = [{name="Ben Wordsworth", email="b.wordsworth@lancaster.ac.uk"},{name="Edward Laird", email="e.a.laird@lancaster.ac.uk"}]
@@ -27,7 +27,7 @@ def main():
27
27
  parser.add_argument("--preceding-stage", choices=("any", "aggregate", "large-summary"), default="any")
28
28
  args = parser.parse_args()
29
29
 
30
- from PyQt6 import QtCore, QtWidgets
30
+ from PyQt6 import QtCore, QtWidgets, sip
31
31
 
32
32
  from qplot.configuration.config import config
33
33
  from qplot.datahandling import readonly, trusted_heatmap, trusted_plot
@@ -36,6 +36,7 @@ def main():
36
36
  _BrokerQueryExecutor,
37
37
  )
38
38
  from qplot.windows._plot_feedback import PlotWindowFeedbackMixin
39
+ from qplot.windows._plotWin import plotWidget
39
40
  from qplot.windows.main import MainWindow
40
41
 
41
42
  def source_state():
@@ -143,7 +144,7 @@ def main():
143
144
  config.default_file = str(Path(directory) / config.config_file_name)
144
145
  window = MainWindow()
145
146
  window.show_error = lambda *items: errors.append(items)
146
- observer = PaintTimes()
147
+ observer = PaintTimes(window)
147
148
  try:
148
149
  window.startupDatabaseTimer.stop()
149
150
  window.config.config["user_preference"]["confirm_close"] = False
@@ -208,7 +209,9 @@ def main():
208
209
  "iteration": iteration + 1,
209
210
  "display_ms": (time.perf_counter() - started) * 1000,
210
211
  "paint_ms": [(value - started) * 1000 for value in painted.values()],
211
- "grids": [list(plot.dataGrid.shape) for plot in plots],
212
+ "grids": [list(plot.dataGrid.shape) for plot in plots if hasattr(plot, "dataGrid")],
213
+ "line_samples": [int(plot.axis_data["y"].size) for plot in plots
214
+ if not hasattr(plot, "dataGrid")],
212
215
  "capture": list(timings),
213
216
  "broker_queries": [{"run_id": run, "phase": phase, **record}
214
217
  for (run, phase), record in list(phase_times.items())],
@@ -220,13 +223,23 @@ def main():
220
223
  window.close_plot_windows(confirm=False, status=False)
221
224
  wait_for(lambda: not window._plot_workers)
222
225
  finally:
223
- window.close_plot_windows(confirm=False, status=False)
224
- window._cancel_plot_work()
225
- window.threadPool.waitForDone(10_000)
226
- window.close_database(status=False)
227
- window.hide()
228
- wait_for(lambda: not window._retired_trusted_read_services,
229
- timeout=20, check_errors=False)
226
+ # Use the application's bounded shutdown for every worker pool,
227
+ # preview and broker, including a failed measurement iteration.
228
+ window.close()
229
+ wait_for(lambda: window._shutdown_ready, timeout=20, check_errors=False)
230
+ if window._shutdown_background_work_active():
231
+ raise RuntimeError("Plot benchmark background work did not stop")
232
+ # Closed plots are still top-level widgets held by iteration
233
+ # locals and the paint records. Retire their native ownership
234
+ # before QApplication's unordered sweep of Python Qt wrappers.
235
+ # Leave child menus and embedded widgets to their Qt owners.
236
+ for widget in app.topLevelWidgets():
237
+ if isinstance(widget, plotWidget) and not sip.isdeleted(widget):
238
+ sip.delete(widget)
239
+ window.deleteLater()
240
+ app.sendPostedEvents(None, QtCore.QEvent.Type.DeferredDelete)
241
+ app.processEvents()
242
+ sip.delete(app)
230
243
  after = source_state()
231
244
  print(json.dumps({"database_bytes": args.database.stat().st_size,
232
245
  "database_open_ms": open_ms, "run_id": args.run_id,
@@ -6,6 +6,8 @@ import json
6
6
  import time
7
7
  from pathlib import Path
8
8
 
9
+ from qcodes.dataset.descriptions.versioning.serialization import from_json_to_current
10
+
9
11
  from qplot.datahandling.trusted_live import TrustedLiveReader
10
12
  from qplot.datahandling.trusted_plot import identifier
11
13
 
@@ -38,12 +40,19 @@ def main():
38
40
  results = []
39
41
  reader_type = FullPathReader if args.full_path_checks else TrustedLiveReader
40
42
  with reader_type.open(args.database) as reader:
41
- table, = reader.query("SELECT result_table_name FROM runs WHERE run_id=?",
42
- (args.run_id,)).rows[0]
43
+ table, description = reader.query(
44
+ "SELECT result_table_name, run_description FROM runs WHERE run_id=?",
45
+ (args.run_id,),
46
+ ).rows[0]
43
47
  table = identifier(table)
44
48
  columns = reader.query(f"SELECT * FROM {table} WHERE 0").columns
45
- numeric = [identifier(name) for name in columns if name != "id"]
46
- # Read all numeric fields, with only a small aggregate crossing the
49
+ types = {spec.name: spec.type for spec in from_json_to_current(description).interdeps.paramspecs}
50
+ numeric_parameters = [name for name in columns if types.get(name) == "numeric"]
51
+ if not numeric_parameters:
52
+ parser.error(f"Run {args.run_id} has no scalar numeric parameters to profile")
53
+ numeric = [identifier(name) for name in numeric_parameters]
54
+ # Array/complex/text values are outside this numeric profiler's scope.
55
+ # Read declared numeric fields, with only a small aggregate crossing the
47
56
  # helper boundary. Match the plot's finite primary-key intervals.
48
57
  expressions = ", ".join(f"MIN({name}), MAX({name})" for name in numeric)
49
58
  for repeat in range(args.repeats):
@@ -69,6 +78,7 @@ def main():
69
78
  if before != after:
70
79
  raise AssertionError("Protected source file sizes/timestamps changed")
71
80
  print(json.dumps({"run_id": args.run_id, "pages": args.pages, "results": results,
81
+ "numeric_parameters": numeric_parameters,
72
82
  "full_path_checks": args.full_path_checks,
73
83
  "protected_file_stats_unchanged": True}, indent=2))
74
84
 
@@ -0,0 +1 @@
1
+ {"version": "1.6.2", "commit": "4a0baf68be2b005e25ba802d2fc166a4da38d418", "dirty": false, "released": true}
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import re
3
3
  import subprocess
4
+ import time
4
5
  import tomllib
5
6
  from importlib.metadata import PackageNotFoundError, distribution, version
6
7
  from pathlib import Path
@@ -17,11 +18,14 @@ def package_version():
17
18
  return "0+unknown"
18
19
 
19
20
 
20
- def _git(root: Path, *arguments: str) -> str:
21
+ def _git(root: Path, *arguments: str, deadline: float) -> str:
21
22
  """Read bounded local Git metadata without refreshing the index."""
23
+ timeout = deadline - time.monotonic()
24
+ if timeout <= 0:
25
+ raise subprocess.TimeoutExpired("git", timeout=0)
22
26
  return subprocess.run(
23
27
  ["git", "--no-optional-locks", "-C", str(root), *arguments],
24
- capture_output=True, text=True, check=True, timeout=1,
28
+ capture_output=True, text=True, check=True, timeout=timeout,
25
29
  ).stdout.strip()
26
30
 
27
31
 
@@ -35,9 +39,14 @@ def checkout_info(root: Path) -> dict:
35
39
  if project["name"] != "qplotter":
36
40
  return {}
37
41
  info = {"version": project["version"], "checkout": True}
38
- commit = _git(root, "rev-parse", "HEAD")
39
- dirty = bool(_git(root, "status", "--porcelain", "--untracked-files=normal"))
40
- tags = _git(root, "tag", "--points-at", "HEAD").splitlines()
42
+ # Cold Git startup can exceed one second on Windows. Share one budget
43
+ # across all three reads so startup and About remain bounded.
44
+ deadline = time.monotonic() + 5
45
+ commit = _git(root, "rev-parse", "HEAD", deadline=deadline)
46
+ dirty = bool(_git(
47
+ root, "status", "--porcelain", "--untracked-files=normal", deadline=deadline,
48
+ ))
49
+ tags = _git(root, "tag", "--points-at", "HEAD", deadline=deadline).splitlines()
41
50
  release_tags = {f"v{project['version']}", f"native-and-app/v{project['version']}"}
42
51
  info.update(commit=commit, dirty=dirty, released=bool(release_tags.intersection(tags)) and not dirty)
43
52
  return info
@@ -106,6 +106,11 @@
106
106
  "user_preference": {
107
107
  "type": "object",
108
108
  "properties": {
109
+ "csv_filename_format": {
110
+ "type": "string",
111
+ "enum": ["current", "qcodes"],
112
+ "default": "current"
113
+ },
109
114
  "show_source_annotation": {
110
115
  "type": "boolean",
111
116
  "default": true
@@ -132,7 +137,7 @@
132
137
  "type": "integer",
133
138
  "minimum": 1,
134
139
  "maximum": 10,
135
- "default": 2
140
+ "default": 3
136
141
  },
137
142
  "theme":{
138
143
  "type": "string",
@@ -302,6 +307,7 @@
302
307
  "confirm_incompatible_axes",
303
308
  "mouse_mode",
304
309
  "copy_plot_image_resolution",
310
+ "csv_filename_format",
305
311
  "auto_plot",
306
312
  "default_refresh_rate"
307
313
  ],
@@ -68,6 +68,14 @@ def append_shaped_parameter_data_to_existing_arrays(
68
68
 
69
69
  new_data_1_tree = new_data.get(meas_parameter, {})
70
70
 
71
+ if new_data_1_tree and not new_data_1_tree[meas_parameter].size:
72
+ # QCoDeS skips an empty column independently when merging unshaped
73
+ # caches. Empty array outputs can retain singleton setpoints, which
74
+ # would then be appended without their measurement and misalign the
75
+ # next refresh. Validate those records and retain zero samples in
76
+ # every field; the separate read status still counts acquired rows.
77
+ new_data_1_tree = flatten_record_columns(new_data_1_tree, check_cancelled)
78
+
71
79
  shapes = rundescriber.shapes
72
80
  if shapes is not None:
73
81
  shape = shapes.get(meas_parameter, None)
@@ -82,7 +90,8 @@ def append_shaped_parameter_data_to_existing_arrays(
82
90
  # Object cells count records; shaped cache offsets count samples.
83
91
  # Flatten only the new records so QCoDeS' usual padding and offsets
84
92
  # still apply, including the same missing-value handling.
85
- if any(values.dtype.kind == "O" for values in new_data_1_tree.values()):
93
+ if (any(values.dtype.kind == "O" for values in new_data_1_tree.values())
94
+ or len({values.shape for values in new_data_1_tree.values()}) > 1):
86
95
  new_data_1_tree = flatten_record_columns(new_data_1_tree, check_cancelled)
87
96
  private_data = {}
88
97
  for name, values in existing_data_1_tree.items():
@@ -191,6 +200,7 @@ def load_param_data_from_db(
191
200
  end: int | None = None,
192
201
  *,
193
202
  check_cancelled=_check_noop,
203
+ max_decode_bytes=None,
194
204
  ):
195
205
  # Data fetch
196
206
  updated_read_status: dict[str, int] = dict(read_status)
@@ -205,6 +215,9 @@ def load_param_data_from_db(
205
215
  start=start,
206
216
  end=end,
207
217
  check_cancelled=check_cancelled,
218
+ max_decode_bytes=max_decode_bytes,
219
+ existing_data=existing_data.get(meas_parameter, {}),
220
+ written_count=write_status.get(meas_parameter),
208
221
  )
209
222
  new_data_dict[meas_parameter] = new_data
210
223
  updated_read_status[meas_parameter] = start + n_rows_read - 1
@@ -193,15 +193,19 @@ def _control_loop(control_receive: Connection, state: _ChildControlState) -> Non
193
193
  _interrupt_active_locked(state)
194
194
  continue
195
195
  if (
196
- state.last_completed_generation == envelope.generation
196
+ state.last_completed_generation is not None
197
+ and envelope.generation <= state.last_completed_generation
197
198
  and cancellation_is_new
198
199
  ):
199
200
  # The operation may finish after the parent decides to
200
201
  # cancel but before this control thread receives that
201
202
  # exact-generation frame. It may even arrive after the
202
- # following operation starts because commands and control
203
- # use separate pipes. Accept it once as a no-op; never
204
- # interrupt the newer generation.
203
+ # following operations finish because commands and control
204
+ # use separate pipes. Commands are contiguous, and the
205
+ # cancellation high watermark starts at this incarnation's
206
+ # first generation minus one. Thus this is a completed job
207
+ # owned by this helper. Accept it once as a no-op; never
208
+ # interrupt a newer generation.
205
209
  state.last_cancelled_generation = envelope.generation
206
210
  continue
207
211
  if (
@@ -434,6 +438,7 @@ def trusted_live_helper_main(
434
438
  session,
435
439
  orphan_grace_ms / 1_000.0,
436
440
  first_generation,
441
+ last_cancelled_generation=first_generation - 1,
437
442
  )
438
443
  reader: TrustedLiveReader | None = None
439
444
  control_thread = threading.Thread(