dcscope 2.22.1__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 (267) hide show
  1. dcscope/__init__.py +17 -0
  2. dcscope/__main__.py +44 -0
  3. dcscope/_version.py +21 -0
  4. dcscope/extensions.py +250 -0
  5. dcscope/gui/__init__.py +2 -0
  6. dcscope/gui/analysis/__init__.py +10 -0
  7. dcscope/gui/analysis/ana_basins.py +110 -0
  8. dcscope/gui/analysis/ana_basins.ui +143 -0
  9. dcscope/gui/analysis/ana_filter.py +341 -0
  10. dcscope/gui/analysis/ana_filter.ui +364 -0
  11. dcscope/gui/analysis/ana_log.py +122 -0
  12. dcscope/gui/analysis/ana_log.ui +100 -0
  13. dcscope/gui/analysis/ana_meta.py +207 -0
  14. dcscope/gui/analysis/ana_meta.ui +141 -0
  15. dcscope/gui/analysis/ana_plot.py +638 -0
  16. dcscope/gui/analysis/ana_plot.ui +1060 -0
  17. dcscope/gui/analysis/ana_slot.py +504 -0
  18. dcscope/gui/analysis/ana_slot.ui +684 -0
  19. dcscope/gui/analysis/ana_tables.py +227 -0
  20. dcscope/gui/analysis/ana_tables.ui +195 -0
  21. dcscope/gui/analysis/ana_view.py +69 -0
  22. dcscope/gui/analysis/ana_view.ui +269 -0
  23. dcscope/gui/analysis/dlg_slot_reorder.py +55 -0
  24. dcscope/gui/analysis/dlg_slot_reorder.ui +138 -0
  25. dcscope/gui/bulk/__init__.py +2 -0
  26. dcscope/gui/bulk/bulk_emodulus.py +200 -0
  27. dcscope/gui/bulk/bulk_emodulus.ui +245 -0
  28. dcscope/gui/compute/__init__.py +3 -0
  29. dcscope/gui/compute/comp_lme4.py +115 -0
  30. dcscope/gui/compute/comp_lme4.ui +194 -0
  31. dcscope/gui/compute/comp_lme4_dataset.py +51 -0
  32. dcscope/gui/compute/comp_lme4_dataset.ui +92 -0
  33. dcscope/gui/compute/comp_lme4_results.py +120 -0
  34. dcscope/gui/compute/comp_lme4_results.ui +268 -0
  35. dcscope/gui/compute/comp_stats.py +223 -0
  36. dcscope/gui/compute/comp_stats.ui +187 -0
  37. dcscope/gui/dcor/__init__.py +304 -0
  38. dcscope/gui/dcor/dcor.ui +118 -0
  39. dcscope/gui/export/__init__.py +3 -0
  40. dcscope/gui/export/e2data.py +394 -0
  41. dcscope/gui/export/e2data.ui +367 -0
  42. dcscope/gui/export/e2filter.py +76 -0
  43. dcscope/gui/export/e2filter.ui +111 -0
  44. dcscope/gui/export/e2plot.py +94 -0
  45. dcscope/gui/export/e2plot.ui +175 -0
  46. dcscope/gui/main.py +1089 -0
  47. dcscope/gui/main.ui +579 -0
  48. dcscope/gui/matrix/__init__.py +4 -0
  49. dcscope/gui/matrix/block_matrix.py +141 -0
  50. dcscope/gui/matrix/block_matrix.ui +207 -0
  51. dcscope/gui/matrix/data_matrix.py +574 -0
  52. dcscope/gui/matrix/dm_dataset.py +116 -0
  53. dcscope/gui/matrix/dm_dataset.ui +348 -0
  54. dcscope/gui/matrix/dm_element.py +111 -0
  55. dcscope/gui/matrix/dm_element.ui +169 -0
  56. dcscope/gui/matrix/dm_filter.py +115 -0
  57. dcscope/gui/matrix/dm_filter.ui +287 -0
  58. dcscope/gui/matrix/plot_matrix.py +316 -0
  59. dcscope/gui/matrix/pm_element.py +39 -0
  60. dcscope/gui/matrix/pm_plot.py +86 -0
  61. dcscope/gui/matrix/pm_plot.ui +249 -0
  62. dcscope/gui/pipeline_plot.py +797 -0
  63. dcscope/gui/pipeline_plot.ui +66 -0
  64. dcscope/gui/preferences.py +380 -0
  65. dcscope/gui/preferences.ui +554 -0
  66. dcscope/gui/quick_view/__init__.py +1 -0
  67. dcscope/gui/quick_view/qv_main.py +1072 -0
  68. dcscope/gui/quick_view/qv_main.ui +1297 -0
  69. dcscope/gui/quick_view/qv_scatter.py +284 -0
  70. dcscope/gui/quick_view/qv_style.css +10 -0
  71. dcscope/gui/update.py +90 -0
  72. dcscope/gui/widgets/__init__.py +15 -0
  73. dcscope/gui/widgets/bg_thread.py +29 -0
  74. dcscope/gui/widgets/bulk_list.py +79 -0
  75. dcscope/gui/widgets/bulk_list.ui +95 -0
  76. dcscope/gui/widgets/double_spin_box_nan.py +75 -0
  77. dcscope/gui/widgets/feature_combobox.py +140 -0
  78. dcscope/gui/widgets/get_path.py +42 -0
  79. dcscope/gui/widgets/key_value_table_widget.css +12 -0
  80. dcscope/gui/widgets/key_value_table_widget.py +75 -0
  81. dcscope/gui/widgets/mdi_subwindow_wo_close.py +12 -0
  82. dcscope/gui/widgets/qrangeslider.py +262 -0
  83. dcscope/gui/widgets/rangecontrol.py +308 -0
  84. dcscope/gui/widgets/rangecontrol.ui +136 -0
  85. dcscope/gui/widgets/simple_image_view.py +55 -0
  86. dcscope/gui/widgets/simple_plot_widget.py +113 -0
  87. dcscope/gui/widgets/so_colorbaritem.py +29 -0
  88. dcscope/gui/widgets/wait_cursor.py +33 -0
  89. dcscope/idiom.py +26 -0
  90. dcscope/img/__init__.py +2 -0
  91. dcscope/img/icon-theme/breeze/COPYING-ICONS +209 -0
  92. dcscope/img/icon-theme/breeze/COPYING.LIB +510 -0
  93. dcscope/img/icon-theme/breeze/README +5 -0
  94. dcscope/img/icon-theme/breeze/actions/16/application-exit.svg +17 -0
  95. dcscope/img/icon-theme/breeze/actions/16/code-context.svg +13 -0
  96. dcscope/img/icon-theme/breeze/actions/16/dialog-cancel.svg +13 -0
  97. dcscope/img/icon-theme/breeze/actions/16/dialog-close.svg +17 -0
  98. dcscope/img/icon-theme/breeze/actions/16/dialog-messages.svg +13 -0
  99. dcscope/img/icon-theme/breeze/actions/16/dialog-ok-apply.svg +13 -0
  100. dcscope/img/icon-theme/breeze/actions/16/dialog-ok.svg +13 -0
  101. dcscope/img/icon-theme/breeze/actions/16/document-open-folder.svg +13 -0
  102. dcscope/img/icon-theme/breeze/actions/16/document-open.svg +13 -0
  103. dcscope/img/icon-theme/breeze/actions/16/document-save.svg +13 -0
  104. dcscope/img/icon-theme/breeze/actions/16/documentinfo.svg +12 -0
  105. dcscope/img/icon-theme/breeze/actions/16/draw-watercolor.svg +13 -0
  106. dcscope/img/icon-theme/breeze/actions/16/edit-clear-all.svg +13 -0
  107. dcscope/img/icon-theme/breeze/actions/16/edit-clear.svg +13 -0
  108. dcscope/img/icon-theme/breeze/actions/16/edit-paste.svg +13 -0
  109. dcscope/img/icon-theme/breeze/actions/16/globe.svg +16 -0
  110. dcscope/img/icon-theme/breeze/actions/16/gtk-preferences.svg +13 -0
  111. dcscope/img/icon-theme/breeze/actions/16/list-add.svg +13 -0
  112. dcscope/img/icon-theme/breeze/actions/16/messagebox_warning.svg +20 -0
  113. dcscope/img/icon-theme/breeze/actions/16/object-columns.svg +13 -0
  114. dcscope/img/icon-theme/breeze/actions/16/object-order-lower.svg +13 -0
  115. dcscope/img/icon-theme/breeze/actions/16/object-rows.svg +13 -0
  116. dcscope/img/icon-theme/breeze/actions/16/office-chart-line-stacked.svg +13 -0
  117. dcscope/img/icon-theme/breeze/actions/16/office-chart-ring.svg +13 -0
  118. dcscope/img/icon-theme/breeze/actions/16/office-chart-scatter.svg +13 -0
  119. dcscope/img/icon-theme/breeze/actions/16/path-mode-polyline.svg +13 -0
  120. dcscope/img/icon-theme/breeze/actions/16/preferences-activities.svg +13 -0
  121. dcscope/img/icon-theme/breeze/actions/16/remove.svg +10 -0
  122. dcscope/img/icon-theme/breeze/actions/16/search.svg +13 -0
  123. dcscope/img/icon-theme/breeze/actions/16/show-grid.svg +13 -0
  124. dcscope/img/icon-theme/breeze/actions/16/special_paste.svg +13 -0
  125. dcscope/img/icon-theme/breeze/actions/16/tools-wizard.svg +13 -0
  126. dcscope/img/icon-theme/breeze/actions/16/view-calendar-list.svg +13 -0
  127. dcscope/img/icon-theme/breeze/actions/16/view-filter.svg +13 -0
  128. dcscope/img/icon-theme/breeze/actions/16/view-list-tree.svg +13 -0
  129. dcscope/img/icon-theme/breeze/actions/16/view-statistics.svg +13 -0
  130. dcscope/img/icon-theme/breeze/actions/16/visibility.svg +21 -0
  131. dcscope/img/icon-theme/breeze/actions/22/application-exit.svg +13 -0
  132. dcscope/img/icon-theme/breeze/actions/22/code-context.svg +14 -0
  133. dcscope/img/icon-theme/breeze/actions/22/dialog-cancel.svg +14 -0
  134. dcscope/img/icon-theme/breeze/actions/22/dialog-close.svg +10 -0
  135. dcscope/img/icon-theme/breeze/actions/22/dialog-messages.svg +14 -0
  136. dcscope/img/icon-theme/breeze/actions/22/dialog-ok-apply.svg +14 -0
  137. dcscope/img/icon-theme/breeze/actions/22/dialog-ok.svg +14 -0
  138. dcscope/img/icon-theme/breeze/actions/22/document-open-folder.svg +14 -0
  139. dcscope/img/icon-theme/breeze/actions/22/document-open.svg +14 -0
  140. dcscope/img/icon-theme/breeze/actions/22/document-save.svg +14 -0
  141. dcscope/img/icon-theme/breeze/actions/22/documentinfo.svg +14 -0
  142. dcscope/img/icon-theme/breeze/actions/22/draw-watercolor.svg +14 -0
  143. dcscope/img/icon-theme/breeze/actions/22/edit-clear-all.svg +14 -0
  144. dcscope/img/icon-theme/breeze/actions/22/edit-clear.svg +14 -0
  145. dcscope/img/icon-theme/breeze/actions/22/edit-paste.svg +14 -0
  146. dcscope/img/icon-theme/breeze/actions/22/globe.svg +12 -0
  147. dcscope/img/icon-theme/breeze/actions/22/gtk-preferences.svg +13 -0
  148. dcscope/img/icon-theme/breeze/actions/22/list-add.svg +14 -0
  149. dcscope/img/icon-theme/breeze/actions/22/messagebox_warning.svg +17 -0
  150. dcscope/img/icon-theme/breeze/actions/22/object-columns.svg +14 -0
  151. dcscope/img/icon-theme/breeze/actions/22/object-order-lower.svg +17 -0
  152. dcscope/img/icon-theme/breeze/actions/22/object-rows.svg +17 -0
  153. dcscope/img/icon-theme/breeze/actions/22/office-chart-line-stacked.svg +17 -0
  154. dcscope/img/icon-theme/breeze/actions/22/office-chart-ring.svg +17 -0
  155. dcscope/img/icon-theme/breeze/actions/22/office-chart-scatter.svg +17 -0
  156. dcscope/img/icon-theme/breeze/actions/22/path-mode-polyline.svg +17 -0
  157. dcscope/img/icon-theme/breeze/actions/22/preferences-activities.svg +14 -0
  158. dcscope/img/icon-theme/breeze/actions/22/remove.svg +10 -0
  159. dcscope/img/icon-theme/breeze/actions/22/special_paste.svg +14 -0
  160. dcscope/img/icon-theme/breeze/actions/22/tools-wizard.svg +14 -0
  161. dcscope/img/icon-theme/breeze/actions/22/view-calendar-list.svg +14 -0
  162. dcscope/img/icon-theme/breeze/actions/22/view-filter.svg +14 -0
  163. dcscope/img/icon-theme/breeze/actions/22/view-list-tree.svg +14 -0
  164. dcscope/img/icon-theme/breeze/actions/22/view-statistics.svg +14 -0
  165. dcscope/img/icon-theme/breeze/actions/22/visibility.svg +14 -0
  166. dcscope/img/icon-theme/breeze/actions/24/application-exit.svg +12 -0
  167. dcscope/img/icon-theme/breeze/actions/24/code-context.svg +12 -0
  168. dcscope/img/icon-theme/breeze/actions/24/dialog-cancel.svg +12 -0
  169. dcscope/img/icon-theme/breeze/actions/24/dialog-close.svg +10 -0
  170. dcscope/img/icon-theme/breeze/actions/24/dialog-messages.svg +12 -0
  171. dcscope/img/icon-theme/breeze/actions/24/dialog-ok-apply.svg +12 -0
  172. dcscope/img/icon-theme/breeze/actions/24/dialog-ok.svg +14 -0
  173. dcscope/img/icon-theme/breeze/actions/24/document-open-folder.svg +12 -0
  174. dcscope/img/icon-theme/breeze/actions/24/document-open.svg +12 -0
  175. dcscope/img/icon-theme/breeze/actions/24/document-save.svg +12 -0
  176. dcscope/img/icon-theme/breeze/actions/24/documentinfo.svg +14 -0
  177. dcscope/img/icon-theme/breeze/actions/24/draw-watercolor.svg +12 -0
  178. dcscope/img/icon-theme/breeze/actions/24/edit-clear-all.svg +14 -0
  179. dcscope/img/icon-theme/breeze/actions/24/edit-clear.svg +12 -0
  180. dcscope/img/icon-theme/breeze/actions/24/edit-paste.svg +12 -0
  181. dcscope/img/icon-theme/breeze/actions/24/globe.svg +14 -0
  182. dcscope/img/icon-theme/breeze/actions/24/gtk-preferences.svg +13 -0
  183. dcscope/img/icon-theme/breeze/actions/24/list-add.svg +12 -0
  184. dcscope/img/icon-theme/breeze/actions/24/messagebox_warning.svg +13 -0
  185. dcscope/img/icon-theme/breeze/actions/24/object-columns.svg +12 -0
  186. dcscope/img/icon-theme/breeze/actions/24/object-order-lower.svg +15 -0
  187. dcscope/img/icon-theme/breeze/actions/24/object-rows.svg +15 -0
  188. dcscope/img/icon-theme/breeze/actions/24/office-chart-line-stacked.svg +15 -0
  189. dcscope/img/icon-theme/breeze/actions/24/office-chart-ring.svg +15 -0
  190. dcscope/img/icon-theme/breeze/actions/24/office-chart-scatter.svg +15 -0
  191. dcscope/img/icon-theme/breeze/actions/24/path-mode-polyline.svg +15 -0
  192. dcscope/img/icon-theme/breeze/actions/24/preferences-activities.svg +13 -0
  193. dcscope/img/icon-theme/breeze/actions/24/remove.svg +10 -0
  194. dcscope/img/icon-theme/breeze/actions/24/special_paste.svg +12 -0
  195. dcscope/img/icon-theme/breeze/actions/24/tools-wizard.svg +12 -0
  196. dcscope/img/icon-theme/breeze/actions/24/view-calendar-list.svg +12 -0
  197. dcscope/img/icon-theme/breeze/actions/24/view-filter.svg +12 -0
  198. dcscope/img/icon-theme/breeze/actions/24/view-list-tree.svg +12 -0
  199. dcscope/img/icon-theme/breeze/actions/24/view-statistics.svg +12 -0
  200. dcscope/img/icon-theme/breeze/actions/24/visibility.svg +14 -0
  201. dcscope/img/icon-theme/breeze/actions/32/application-exit.svg +8 -0
  202. dcscope/img/icon-theme/breeze/actions/32/dialog-cancel.svg +13 -0
  203. dcscope/img/icon-theme/breeze/actions/32/dialog-messages.svg +13 -0
  204. dcscope/img/icon-theme/breeze/actions/32/dialog-ok-apply.svg +1 -0
  205. dcscope/img/icon-theme/breeze/actions/32/dialog-ok.svg +1 -0
  206. dcscope/img/icon-theme/breeze/actions/32/document-open-folder.svg +13 -0
  207. dcscope/img/icon-theme/breeze/actions/32/document-open.svg +13 -0
  208. dcscope/img/icon-theme/breeze/actions/32/document-save.svg +18 -0
  209. dcscope/img/icon-theme/breeze/actions/32/gtk-preferences.svg +17 -0
  210. dcscope/img/icon-theme/breeze/actions/32/object-order-lower.svg +18 -0
  211. dcscope/img/icon-theme/breeze/actions/32/view-filter.svg +18 -0
  212. dcscope/img/icon-theme/breeze/actions/32/view-list-tree.svg +18 -0
  213. dcscope/img/icon-theme/breeze/places/16/folder-cloud.svg +13 -0
  214. dcscope/img/icon-theme/breeze/places/16/folder.svg +13 -0
  215. dcscope/img/icon-theme/breeze/places/22/folder-cloud.svg +13 -0
  216. dcscope/img/icon-theme/breeze/places/22/folder.svg +13 -0
  217. dcscope/img/icon-theme/breeze/places/24/folder-cloud.svg +12 -0
  218. dcscope/img/icon-theme/breeze/places/24/folder.svg +12 -0
  219. dcscope/img/icon-theme/breeze/places/32/folder-cloud.svg +35 -0
  220. dcscope/img/icon-theme/breeze/places/32/folder.svg +30 -0
  221. dcscope/img/icon-theme/breeze/places/64/folder-cloud.svg +35 -0
  222. dcscope/img/icon-theme/breeze/places/64/folder.svg +30 -0
  223. dcscope/img/icon-theme/breeze/status/16/dialog-error.svg +9 -0
  224. dcscope/img/icon-theme/breeze/status/16/dialog-information.svg +9 -0
  225. dcscope/img/icon-theme/breeze/status/16/dialog-question.svg +7 -0
  226. dcscope/img/icon-theme/breeze/status/16/dialog-warning.svg +9 -0
  227. dcscope/img/icon-theme/breeze/status/22/dialog-error.svg +9 -0
  228. dcscope/img/icon-theme/breeze/status/22/dialog-information.svg +9 -0
  229. dcscope/img/icon-theme/breeze/status/22/dialog-question.svg +7 -0
  230. dcscope/img/icon-theme/breeze/status/22/dialog-warning.svg +9 -0
  231. dcscope/img/icon-theme/breeze/status/24/dialog-error.svg +13 -0
  232. dcscope/img/icon-theme/breeze/status/24/dialog-information.svg +13 -0
  233. dcscope/img/icon-theme/breeze/status/24/dialog-question.svg +9 -0
  234. dcscope/img/icon-theme/breeze/status/24/dialog-warning.svg +1 -0
  235. dcscope/img/icon-theme/breeze/status/64/dialog-error.svg +25 -0
  236. dcscope/img/icon-theme/breeze/status/64/dialog-information.svg +25 -0
  237. dcscope/img/icon-theme/breeze/status/64/dialog-question.svg +25 -0
  238. dcscope/img/icon-theme/breeze/status/64/dialog-warning.svg +25 -0
  239. dcscope/img/icon-theme/collect_icons.py +124 -0
  240. dcscope/img/icon-theme/dcscope/dcor.png +0 -0
  241. dcscope/img/icon-theme/dcscope/edit-copy-anew.svg +13 -0
  242. dcscope/img/icon-theme/dcscope/emodulus.svg +69 -0
  243. dcscope/img/icon-theme/dcscope/filter_ray.svg +54 -0
  244. dcscope/img/icon-theme/dcscope/region_channel.svg +86 -0
  245. dcscope/img/icon-theme/dcscope/region_reservoir.svg +89 -0
  246. dcscope/img/icon-theme/dcscope/rlang.svg +108 -0
  247. dcscope/img/icon-theme/dcscope/statistical_significance.svg +106 -0
  248. dcscope/img/icon-theme/index.theme +81 -0
  249. dcscope/img/icon.png +0 -0
  250. dcscope/img/icon.svg +73 -0
  251. dcscope/img/splash.png +0 -0
  252. dcscope/meta_tool.py +143 -0
  253. dcscope/pipeline/__init__.py +5 -0
  254. dcscope/pipeline/core.py +632 -0
  255. dcscope/pipeline/dataslot.py +270 -0
  256. dcscope/pipeline/filter.py +161 -0
  257. dcscope/pipeline/filter_ray.py +170 -0
  258. dcscope/pipeline/plot.py +187 -0
  259. dcscope/plot_cache.py +79 -0
  260. dcscope/session.py +379 -0
  261. dcscope/util.py +34 -0
  262. dcscope-2.22.1.dist-info/METADATA +95 -0
  263. dcscope-2.22.1.dist-info/RECORD +267 -0
  264. dcscope-2.22.1.dist-info/WHEEL +5 -0
  265. dcscope-2.22.1.dist-info/entry_points.txt +2 -0
  266. dcscope-2.22.1.dist-info/licenses/LICENSE +621 -0
  267. dcscope-2.22.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,308 @@
1
+ import importlib.resources
2
+
3
+ import numpy as np
4
+ from PyQt6 import uic, QtCore, QtWidgets
5
+
6
+
7
+ #: Precision for these features (``data``) should not go below this value.
8
+ #: (Precision is set automatically based on data range)
9
+ SPIN_CONTROL_PRECISION = {
10
+ "area_ratio": 3,
11
+ "aspect": 3,
12
+ "bright_avg": 1,
13
+ "bright_sd": 3,
14
+ }
15
+
16
+
17
+ class RangeControl(QtWidgets.QWidget):
18
+ #: Emitted when the range changed
19
+ range_changed = QtCore.pyqtSignal(float, float)
20
+
21
+ def __init__(self, parent, label="feature", checkbox=True, integer=False,
22
+ data=None, *args, **kwargs):
23
+ super(RangeControl, self).__init__(parent=parent, *args, **kwargs)
24
+ ref = importlib.resources.files(
25
+ "dcscope.gui.widgets") / "rangecontrol.ui"
26
+ with importlib.resources.as_file(ref) as path_ui:
27
+ uic.loadUi(path_ui, self)
28
+
29
+ for spinbox in (self.doubleSpinBox_min, self.doubleSpinBox_max):
30
+ spinbox.setOpts(
31
+ format="{scaledValue:.{decimals}f}{suffixGap}{suffix}",
32
+ compactHeight=False,
33
+ )
34
+
35
+ # arbitrary data
36
+ self.data = data
37
+
38
+ # default values for limits
39
+ self.minimum = -100
40
+ self.maximum = +100
41
+
42
+ # label on top of control
43
+ self.setLabel(label)
44
+
45
+ # enable/disable checkbox
46
+ if not checkbox:
47
+ self.checkBox.hide()
48
+
49
+ # integer-valued control
50
+ if integer:
51
+ self.doubleSpinBox_min.setDecimals(1)
52
+ self.doubleSpinBox_max.setDecimals(1)
53
+ self.is_integer = integer
54
+
55
+ # reduce font size of name
56
+ font = self.label.font()
57
+ font.setPointSize(font.pointSize()-1)
58
+ self.label.setFont(font)
59
+
60
+ # signals
61
+ self.range_slider.rangeChanged.connect(self.on_range)
62
+ self.doubleSpinBox_min.valueChanged.connect(self.on_spinbox)
63
+ self.doubleSpinBox_max.valueChanged.connect(self.on_spinbox)
64
+
65
+ # call show to make sure slider is updated
66
+ self.show()
67
+
68
+ def read_pipeline_state(self):
69
+ state = {
70
+ "active": self.checkBox.isChecked(),
71
+ "start": self.doubleSpinBox_min.value(),
72
+ "end": self.doubleSpinBox_max.value(),
73
+ }
74
+ return state
75
+
76
+ def write_pipeline_state(self, state):
77
+ self.checkBox.setChecked(state["active"])
78
+ self.setSpinRange(state["start"], state["end"])
79
+
80
+ def check_boundary(self, old_value):
81
+ """Make sure boundaries are properly set in the UI
82
+
83
+ The main purpose of this function is to fix #123. For integer
84
+ features we would like to have 0.5 step sizes to properly
85
+ filter out e.g. ML classes.
86
+ """
87
+ if self.is_integer:
88
+ # force an increment of 0.5
89
+ new_value = round(old_value * 2) / 2
90
+ else:
91
+ new_value = old_value
92
+ return new_value
93
+
94
+ @QtCore.pyqtSlot(float, float)
95
+ def map_spin_values_to_range_slider(self):
96
+ """Read values from spin controls and update the slider UI"""
97
+ # spin values
98
+ smin = self.doubleSpinBox_min.value()
99
+ smax = self.doubleSpinBox_max.value()
100
+ # limits
101
+ lmin = self.minimum
102
+ lmax = self.maximum
103
+
104
+ # current range slider limits [a.u.]
105
+ rmin = self.range_slider.min()
106
+ rmax = self.range_slider.max()
107
+ # ranges for translating to handle widths
108
+ dr = rmax - rmin # handle range
109
+ dl = lmax - lmin # value range
110
+ # range slider handles (not the limits)
111
+ if dl == 0:
112
+ hmin = hmax = 0
113
+ else:
114
+ hmin = rmin + (smin - lmin) * dr / dl
115
+ hmax = rmax - (lmax - smax) * dr / dl
116
+ if hmin < rmin:
117
+ hmin = 0
118
+ if hmax > rmax:
119
+ hmax = self.range_slider._INT_NUM
120
+
121
+ # make range selection stick tight to edges
122
+ if hmin < 10:
123
+ hmin = 0
124
+ if hmin > self.range_slider._INT_NUM - 10:
125
+ hmax = self.range_slider._INT_NUM
126
+
127
+ self.range_slider.update()
128
+ self.range_slider.blockSignals(True)
129
+ self.range_slider.setRange(hmin, hmax)
130
+ self.range_slider.blockSignals(False)
131
+ return hmin, hmax
132
+
133
+ @QtCore.pyqtSlot(int, int)
134
+ def map_range_slider_to_spin_values(self):
135
+ """Return the respective value of the current range
136
+
137
+ Range limits are defined by
138
+ - self.doubleSpinBox_min.minimum()
139
+ - self.doubleSpinBox_min.maximum()
140
+ """
141
+ # limits
142
+ lmin = self.minimum
143
+ lmax = self.maximum
144
+ # range slider limits
145
+ rmin = self.range_slider.min()
146
+ rmax = self.range_slider.max()
147
+ # range slider handles
148
+ hmin = self.range_slider.start()
149
+ hmax = self.range_slider.end()
150
+ # compute values
151
+ dr = rmax - rmin
152
+ dl = lmax - lmin
153
+ vmin = lmin + (hmin - rmin) * dl / dr
154
+ vmax = lmax - (rmax - hmax) * dl / dr
155
+
156
+ vmin = self.check_boundary(vmin)
157
+ vmax = self.check_boundary(vmax)
158
+
159
+ self.setSpinRange(vmin, vmax)
160
+ return vmin, vmax
161
+
162
+ def is_active(self):
163
+ return self.checkBox.isChecked()
164
+
165
+ @QtCore.pyqtSlot()
166
+ def on_range(self):
167
+ self.map_range_slider_to_spin_values()
168
+ self.range_changed.emit(self.doubleSpinBox_min.value(),
169
+ self.doubleSpinBox_max.value())
170
+
171
+ @QtCore.pyqtSlot()
172
+ def on_spinbox(self):
173
+ self.doubleSpinBox_min.setValue(
174
+ self.check_boundary(self.doubleSpinBox_min.value()))
175
+ self.doubleSpinBox_max.setValue(
176
+ self.check_boundary(self.doubleSpinBox_max.value()))
177
+
178
+ self.map_spin_values_to_range_slider()
179
+ self.range_changed.emit(self.doubleSpinBox_min.value(),
180
+ self.doubleSpinBox_max.value())
181
+
182
+ def reset_range(self):
183
+ self.doubleSpinBox_min.setValue(self.minimum)
184
+ self.doubleSpinBox_max.setValue(self.maximum)
185
+
186
+ def setActive(self, b=True):
187
+ self.checkBox.setChecked(b)
188
+
189
+ def setCheckable(self, b=True):
190
+ self.checkBox.setVisible(b)
191
+
192
+ def setInteger(self, b=True):
193
+ self.is_integer = b
194
+
195
+ def setLabel(self, label):
196
+ if label:
197
+ self.label.setText(label)
198
+ self.label.show()
199
+ else:
200
+ self.label.hide()
201
+
202
+ def setLimits(self, vmin, vmax, hard_limit=False):
203
+ """Set the limits of the range control
204
+
205
+ Parameters
206
+ ----------
207
+ vmin, vmax: float
208
+ Minimum and maximum values
209
+ hard_limit: bool
210
+ If set to True, the spin controls will have a hard limit
211
+ that matches vmin and vmax. If False (default), the
212
+ limit of the spin controls is larger, giving the user
213
+ a broader range.
214
+ """
215
+ if vmin == vmax:
216
+ return
217
+
218
+ self.minimum = vmin
219
+ self.maximum = vmax
220
+
221
+ if hard_limit:
222
+ vminh = vmin
223
+ vmaxh = vmax
224
+ else:
225
+ # estimate based on number of digits
226
+ if vmin >= 0:
227
+ vminh = 0
228
+ else:
229
+ lmin = int(np.ceil(np.log10(np.abs(vmin)))) + 1
230
+ vminh = -10**lmin
231
+ if vmax > 0:
232
+ lmax = int(np.ceil(np.log10(vmax))) + 1
233
+ vmaxh = 10**lmax
234
+ else:
235
+ vmaxh = 0
236
+
237
+ self.setSpinLimits(vmin=vminh, vmax=vmaxh)
238
+ # slider values
239
+ self.map_spin_values_to_range_slider()
240
+
241
+ def setSpinLimits(self, vmin, vmax):
242
+ """Sets spin control limits and precision
243
+
244
+ Notes
245
+ -----
246
+ The precision is set automatically from the min/max
247
+ peak-to-peak size. If this precision is not high
248
+ enough for a specific application, you can set the
249
+ ``data`` attribute upon initialization and add the
250
+ precision (in decimals) to :const:`SPIN_CONTROL_PRECISION`
251
+ via ``SPIN_CONTROL_PRECISION[data] = precision``.
252
+ """
253
+ # min/max
254
+ self.doubleSpinBox_min.setMinimum(vmin)
255
+ self.doubleSpinBox_max.setMinimum(vmin)
256
+ self.doubleSpinBox_min.setMaximum(vmax)
257
+ self.doubleSpinBox_max.setMaximum(vmax)
258
+
259
+ # decimals
260
+ if not self.is_integer:
261
+ if vmax == vmin:
262
+ dec = 1
263
+ else:
264
+ # two significant digits
265
+ dec = int(np.ceil(np.log10(1/np.abs(vmax-vmin)))) + 5
266
+ if dec <= 0:
267
+ dec = 1
268
+ if self.data in SPIN_CONTROL_PRECISION:
269
+ dec = max(dec, SPIN_CONTROL_PRECISION[self.data])
270
+ self.doubleSpinBox_min.setDecimals(dec)
271
+ self.doubleSpinBox_max.setDecimals(dec)
272
+ self.doubleSpinBox_min.setSingleStep(10**-dec)
273
+ self.doubleSpinBox_max.setSingleStep(10**-dec)
274
+
275
+ def setSpinRange(self, vmin, vmax):
276
+ """Set values of left and right spin controls (not the limits)
277
+
278
+ Extends the range if necessary
279
+ """
280
+ limits_changed = False
281
+
282
+ if vmin < self.minimum:
283
+ limit_min = np.floor(vmin)
284
+ limits_changed = True
285
+ else:
286
+ limit_min = self.minimum
287
+
288
+ if vmax > self.maximum:
289
+ limit_max = np.ceil(vmax)
290
+ limits_changed = True
291
+ else:
292
+ limit_max = self.maximum
293
+
294
+ if limits_changed:
295
+ self.setLimits(limit_min, limit_max)
296
+
297
+ self.doubleSpinBox_min.blockSignals(True)
298
+ self.doubleSpinBox_max.blockSignals(True)
299
+ self.doubleSpinBox_min.setValue(vmin)
300
+ self.doubleSpinBox_max.setValue(vmax)
301
+ self.doubleSpinBox_min.blockSignals(False)
302
+ self.doubleSpinBox_max.blockSignals(False)
303
+
304
+ self.range_slider.blockSignals(True)
305
+ self.map_spin_values_to_range_slider()
306
+ self.range_slider.blockSignals(False)
307
+
308
+ self.range_changed.emit(vmin, vmax)
@@ -0,0 +1,136 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ui version="4.0">
3
+ <class>Form</class>
4
+ <widget class="QWidget" name="Form">
5
+ <property name="geometry">
6
+ <rect>
7
+ <x>0</x>
8
+ <y>0</y>
9
+ <width>481</width>
10
+ <height>45</height>
11
+ </rect>
12
+ </property>
13
+ <property name="sizePolicy">
14
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
15
+ <horstretch>0</horstretch>
16
+ <verstretch>0</verstretch>
17
+ </sizepolicy>
18
+ </property>
19
+ <property name="windowTitle">
20
+ <string>Form</string>
21
+ </property>
22
+ <layout class="QVBoxLayout" name="verticalLayout">
23
+ <property name="spacing">
24
+ <number>0</number>
25
+ </property>
26
+ <property name="leftMargin">
27
+ <number>0</number>
28
+ </property>
29
+ <property name="topMargin">
30
+ <number>0</number>
31
+ </property>
32
+ <property name="rightMargin">
33
+ <number>0</number>
34
+ </property>
35
+ <property name="bottomMargin">
36
+ <number>0</number>
37
+ </property>
38
+ <item>
39
+ <widget class="QLabel" name="label">
40
+ <property name="text">
41
+ <string>Feature description</string>
42
+ </property>
43
+ </widget>
44
+ </item>
45
+ <item>
46
+ <layout class="QHBoxLayout" name="horizontalLayout">
47
+ <property name="spacing">
48
+ <number>6</number>
49
+ </property>
50
+ <item>
51
+ <widget class="QCheckBox" name="checkBox">
52
+ <property name="text">
53
+ <string/>
54
+ </property>
55
+ <property name="checked">
56
+ <bool>true</bool>
57
+ </property>
58
+ </widget>
59
+ </item>
60
+ <item>
61
+ <widget class="SpinBox" name="doubleSpinBox_min">
62
+ <property name="sizePolicy">
63
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
64
+ <horstretch>0</horstretch>
65
+ <verstretch>0</verstretch>
66
+ </sizepolicy>
67
+ </property>
68
+ <property name="minimumSize">
69
+ <size>
70
+ <width>0</width>
71
+ <height>0</height>
72
+ </size>
73
+ </property>
74
+ <property name="maximumSize">
75
+ <size>
76
+ <width>90</width>
77
+ <height>16777215</height>
78
+ </size>
79
+ </property>
80
+ <property name="decimals">
81
+ <number>2</number>
82
+ </property>
83
+ <property name="maximum">
84
+ <double>100.000000000000000</double>
85
+ </property>
86
+ </widget>
87
+ </item>
88
+ <item>
89
+ <widget class="QRangeSlider" name="range_slider" native="true">
90
+ <property name="sizePolicy">
91
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
92
+ <horstretch>0</horstretch>
93
+ <verstretch>0</verstretch>
94
+ </sizepolicy>
95
+ </property>
96
+ </widget>
97
+ </item>
98
+ <item>
99
+ <widget class="SpinBox" name="doubleSpinBox_max">
100
+ <property name="sizePolicy">
101
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
102
+ <horstretch>0</horstretch>
103
+ <verstretch>0</verstretch>
104
+ </sizepolicy>
105
+ </property>
106
+ <property name="maximumSize">
107
+ <size>
108
+ <width>90</width>
109
+ <height>16777215</height>
110
+ </size>
111
+ </property>
112
+ <property name="maximum">
113
+ <double>100.000000000000000</double>
114
+ </property>
115
+ </widget>
116
+ </item>
117
+ </layout>
118
+ </item>
119
+ </layout>
120
+ </widget>
121
+ <customwidgets>
122
+ <customwidget>
123
+ <class>QRangeSlider</class>
124
+ <extends>QWidget</extends>
125
+ <header>dcscope.gui.widgets</header>
126
+ <container>1</container>
127
+ </customwidget>
128
+ <customwidget>
129
+ <class>SpinBox</class>
130
+ <extends>QDoubleSpinBox</extends>
131
+ <header>pyqtgraph</header>
132
+ </customwidget>
133
+ </customwidgets>
134
+ <resources/>
135
+ <connections/>
136
+ </ui>
@@ -0,0 +1,55 @@
1
+ import numpy as np
2
+ from PyQt6 import QtCore, QtGui, QtWidgets
3
+ import pyqtgraph as pg
4
+
5
+ from .simple_plot_widget import SimpleViewBox
6
+
7
+
8
+ class SimpleImageView(pg.ImageView):
9
+ """Custom class for data visualization in DCscope"""
10
+
11
+ def __init__(self, *args, **kwargs):
12
+ super(SimpleImageView, self).__init__(view=SimpleImageViewBox(),
13
+ *args, **kwargs)
14
+ self.view.export.connect(self.on_export)
15
+
16
+ # disable pyqtgraph controls we don't need
17
+ self.ui.histogram.hide()
18
+ self.ui.roiBtn.hide()
19
+ self.ui.menuBtn.hide()
20
+ # disable keyboard shortcuts
21
+ self.keyPressEvent = lambda _: None
22
+ self.keyReleaseEvent = lambda _: None
23
+
24
+ def on_export(self, suffix):
25
+ assert suffix == "png"
26
+ path, _ = QtWidgets.QFileDialog.getSaveFileName(
27
+ self, '', '', 'PNG image (*.png)', '')
28
+ if not path.endswith(".png"):
29
+ path += ".png"
30
+
31
+ img = np.require(self.image, np.uint8, 'C')
32
+ height, width, _ = self.image.shape
33
+
34
+ qImg = QtGui.QImage(img, width, height, width *
35
+ 3, QtGui.QImage.Format.Format_RGB888)
36
+ qImg.save(path)
37
+
38
+
39
+ class SimpleImageViewBox(SimpleViewBox):
40
+ export = QtCore.pyqtSignal(str)
41
+
42
+ def autoRange(self, padding=0, *args, **kwargs):
43
+ super(SimpleImageViewBox, self).autoRange(padding=padding,
44
+ *args,
45
+ **kwargs)
46
+
47
+ def raiseContextMenu(self, ev):
48
+ menu = self.menu
49
+ menu.clear()
50
+ menu.addAction("Save event image as PNG",
51
+ lambda: self.export.emit("png"))
52
+
53
+ pos = ev.screenPos()
54
+ menu.popup(QtCore.QPoint(int(pos.x()), int(pos.y())))
55
+ return True
@@ -0,0 +1,113 @@
1
+ from PyQt6 import QtCore, QtWidgets
2
+ import pyqtgraph as pg
3
+ from pyqtgraph import exporters
4
+
5
+
6
+ class SimplePlotItem(pg.PlotItem):
7
+ """Custom class for data visualization in DCscope
8
+
9
+ Modifications include:
10
+ - right click menu only with "Export..."
11
+ - top and right axes
12
+ """
13
+
14
+ def __init__(self, parent=None, *args, **kwargs):
15
+ if "viewBox" not in kwargs:
16
+ kwargs["viewBox"] = SimpleViewBox()
17
+ super(SimplePlotItem, self).__init__(parent, *args, **kwargs)
18
+ self.vb.export.connect(self.on_export)
19
+ # show top and right axes, but not ticklabels
20
+ for kax in ["top", "right"]:
21
+ self.showAxis(kax)
22
+ ax = self.axes[kax]["item"]
23
+ ax.setTicks([])
24
+ ax.setLabel(None)
25
+ ax.setStyle(tickTextOffset=0,
26
+ tickTextWidth=0,
27
+ tickTextHeight=0,
28
+ autoExpandTextSpace=False,
29
+ showValues=False,
30
+ )
31
+ # show grid
32
+ # https://github.com/DC-analysis/DCscope/issues/75
33
+ # self.showGrid(x=True, y=True, alpha=.1)
34
+ # visualization
35
+ self.hideButtons()
36
+
37
+ def axes_to_front(self):
38
+ """Give the axes a high zValue"""
39
+ # bring axes to front
40
+ # (This screws up event selection in QuickView)
41
+ for kax in self.axes:
42
+ self.axes[kax]["item"].setZValue(900)
43
+
44
+ def on_export(self, suffix):
45
+ """Export subplots as original figures (with axes labels, etc)"""
46
+ file, _ = QtWidgets.QFileDialog.getSaveFileName(
47
+ None,
48
+ 'Save {} file'.format(suffix.upper()),
49
+ '',
50
+ '{} file (*.{})'.format(suffix.upper(), suffix))
51
+ if not file.endswith("." + suffix):
52
+ file += "." + suffix
53
+ self.perform_export(file)
54
+
55
+ def perform_export(self, file):
56
+ suffix = file[-3:]
57
+ if suffix == "png":
58
+ exp = exporters.ImageExporter(self)
59
+ # translate from screen resolution (80dpi) to 300dpi
60
+ exp.params["width"] = int(exp.params["width"] / 72 * 300)
61
+ elif suffix == "svg":
62
+ exp = exporters.SVGExporter(self)
63
+ exp.export(file)
64
+
65
+
66
+ class SimplePlotWidget(pg.PlotWidget):
67
+ """Custom class for data visualization in DCscope
68
+
69
+ Modifications include:
70
+ - white background
71
+ - those of SimplePlotItem
72
+ """
73
+
74
+ def __init__(self, parent=None, background='w', **item_kwargs):
75
+ plot_item = SimplePlotItem(**item_kwargs)
76
+ super(SimplePlotWidget, self).__init__(parent,
77
+ background=background,
78
+ plotItem=plot_item)
79
+
80
+
81
+ class SimpleViewBox(pg.ViewBox):
82
+ export = QtCore.pyqtSignal(str)
83
+
84
+ def __init__(self, *args, **kwargs):
85
+ super(SimpleViewBox, self).__init__(*args, **kwargs)
86
+ #: allowed right-click menu options with new name
87
+ self.right_click_actions = {}
88
+ settings = QtCore.QSettings()
89
+ if int(settings.value("advanced/developer mode", 0)):
90
+ # Enable advanced export in developer mode
91
+ self.right_click_actions["Export..."] = "Advanced Export"
92
+
93
+ def raiseContextMenu(self, ev):
94
+ # Let the scene add on to the end of our context menu
95
+ menu = self.scene().addParentContextMenus(self, self.menu, ev)
96
+
97
+ # Only keep list of actions defined in `self.right_click_actions`
98
+ for action in self.menu.actions():
99
+ if action.text() in self.right_click_actions.values():
100
+ pass
101
+ elif action.text() not in self.right_click_actions:
102
+ self.menu.removeAction(action)
103
+ else:
104
+ action.setText(self.right_click_actions[action.text()])
105
+
106
+ menu.addAction("Export subplot as PNG",
107
+ lambda: self.export.emit("png"))
108
+ menu.addAction("Export subplot as SVG",
109
+ lambda: self.export.emit("svg"))
110
+
111
+ pos = ev.screenPos()
112
+ menu.popup(QtCore.QPoint(int(pos.x()), int(pos.y())))
113
+ return True
@@ -0,0 +1,29 @@
1
+ import pyqtgraph as pg
2
+
3
+
4
+ class DCscopeColorBarItem(pg.ColorBarItem):
5
+ def __init__(self, yoffset, height, label, colorMap, *args, **kwargs):
6
+ """pg.ColorBarItem modified for DCscope
7
+
8
+ - Added option to define height
9
+ - translate the colorbar so that it is aligned with the plot
10
+ - show the label on the right-hand axis
11
+ - increase the contents margins
12
+ """
13
+ super(DCscopeColorBarItem, self).__init__(
14
+ colorMap=colorMap,
15
+ # TODO: Removing `cmap=colorMap` results in grayscale colormaps.
16
+ *args, **kwargs)
17
+
18
+ # show label on right side
19
+ self.axis.setLabel(label)
20
+
21
+ # increase contents margins
22
+ self.layout.setContentsMargins(7, 0, 7, 0)
23
+
24
+ # set correct size and position
25
+ self.setFixedHeight(height)
26
+
27
+ tr = self.transform()
28
+ tr.translate(0, yoffset)
29
+ self.setTransform(tr)
@@ -0,0 +1,33 @@
1
+ import functools
2
+
3
+ from PyQt6.QtWidgets import QApplication
4
+ from PyQt6.QtGui import QCursor
5
+ from PyQt6.QtCore import Qt, QEventLoop
6
+
7
+
8
+ class ShowWaitCursor(object):
9
+ def __enter__(self):
10
+ QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
11
+ # This overloaded function call makes sure that all events,
12
+ # even those triggered during the function call, are processed.
13
+ QApplication.processEvents(QEventLoop.ProcessEventsFlag.AllEvents, 50)
14
+ return self
15
+
16
+ def __exit__(self, type, value, traceback):
17
+ QApplication.restoreOverrideCursor()
18
+
19
+
20
+ def show_wait_cursor(func):
21
+ """A decorator that starts and stops a wait cursor for a function call
22
+
23
+ https://doc.qt.io/qt-5/qguiapplication.html#setOverrideCursor
24
+ Every setOverrideCursor() must eventually be followed by a
25
+ corresponding restoreOverrideCursor(), otherwise the stack
26
+ will never be emptied.
27
+ """
28
+ @functools.wraps(func)
29
+ def wrapper(*args, **kwargs):
30
+ with ShowWaitCursor():
31
+ ret = func(*args, **kwargs)
32
+ return ret
33
+ return wrapper
dcscope/idiom.py ADDED
@@ -0,0 +1,26 @@
1
+ from dclab.rtdc_dataset import feat_anc_core
2
+
3
+ #: integer-valued features
4
+ INTEGER_FEATURES = [
5
+ "fl1_max",
6
+ "fl1_npeaks",
7
+ "fl2_max",
8
+ "fl2_npeaks",
9
+ "fl3_max",
10
+ "fl3_npeaks",
11
+ "frame",
12
+ "index",
13
+ "ml_class",
14
+ "nevents",
15
+ ]
16
+
17
+ #: features that by definition trend over the course of a measurement
18
+ SLOPING_FEATURES = [
19
+ "frame",
20
+ "index",
21
+ "index_online",
22
+ "time",
23
+ ]
24
+
25
+ #: ancillary features that are easily computed
26
+ QUICK_FEATURES = feat_anc_core.FEATURES_RAPID