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,187 @@
1
+ import copy
2
+
3
+ import dclab
4
+ from dclab.kde import methods as kdem
5
+ import numpy as np
6
+
7
+ from ..util import hashobj
8
+
9
+
10
+ DEFAULT_STATE = {
11
+ "identifier": "no default",
12
+ "layout": {
13
+ "column count": 2,
14
+ "division": "multiscatter+contour",
15
+ "label plots": True,
16
+ "name": "no default", # overridden by __init__
17
+ "size x": 400,
18
+ "size y": 400,
19
+ },
20
+ "general": {
21
+ "auto range": True, # this overrides range x and range y
22
+ "axis x": "area_um",
23
+ "axis y": "deform",
24
+ "isoelastics": True, # display isoelasticity lines
25
+ "kde": "histogram", # see dclab.kde.methods.methods
26
+ "range x": [0, 0], # equal means no preference
27
+ "range y": [0, 0],
28
+ "scale x": "linear",
29
+ "scale y": "linear",
30
+ },
31
+ "scatter": {
32
+ "colormap": "viridis", # only applies when hue is "kde" or "feature"
33
+ "downsample": True,
34
+ "downsampling value": 5000,
35
+ "enabled": True,
36
+ "hue feature": "bright_avg", # which feature to use, if set
37
+ "hue max": 1,
38
+ "hue min": 0,
39
+ "marker alpha": 0.3, # alpha value for feature-hue plots
40
+ "marker hue": "kde", # hue defined by: kde, dataset, feature, none
41
+ "marker size": 3.0, # marker size [pt]
42
+ "show event count": True, # display event count
43
+ },
44
+ "contour": {
45
+ "enabled": True,
46
+ "legend": False, # display plot legend
47
+ "line widths": [3.0, 1.5], # contour line widths [pt]
48
+ "line styles": ["solid", "dashed"],
49
+ "percentiles": [95.0, 50.0],
50
+ "spacing x": 2, # spacing for "axis x" and linear "scale x"
51
+ "spacing y": 0.005, # spacing for "axis y" and linear "scale y"
52
+ }
53
+ }
54
+
55
+ _kde_methods = sorted(kdem.methods.keys())
56
+ _kde_methods.remove("none") # does not make sense here
57
+
58
+ STATE_OPTIONS = {
59
+ "identifier": str,
60
+ "layout": {
61
+ "column count": int,
62
+ "division": ["each", "merge", "multiscatter+contour"],
63
+ "label plots": bool,
64
+ "name": str,
65
+ "size x": float,
66
+ "size y": float,
67
+ },
68
+ "general": {
69
+ "auto range": bool,
70
+ # This is not entirely correct, because `ml_score_???`
71
+ # should also work.
72
+ "axis x": dclab.dfn.scalar_feature_names,
73
+ "axis y": dclab.dfn.scalar_feature_names,
74
+ "isoelastics": bool,
75
+ "kde": _kde_methods,
76
+ "range x": (float,),
77
+ "range y": (float,),
78
+ "scale x": ["linear", "log"],
79
+ "scale y": ["linear", "log"],
80
+ },
81
+ "scatter": {
82
+ "colormap": ["bipolar", "grayblue", "graygreen", "grayorange",
83
+ "grayred", "inferno", "plasma", "viridis"],
84
+ "downsampling": bool,
85
+ "downsampling value": int,
86
+ "enabled": bool,
87
+ # This is not entirely correct, because `ml_score_???`
88
+ # should also work.
89
+ "hue feature": dclab.dfn.scalar_feature_names,
90
+ "hue max": float,
91
+ "hue min": float,
92
+ "marker alpha": float,
93
+ "marker hue": ["dataset", "kde", "feature", "none"],
94
+ "marker size": float,
95
+ "show event count": bool,
96
+ },
97
+ "contour": {
98
+ "enabled": bool,
99
+ "legend": bool,
100
+ "line widths": (float,),
101
+ "line styles": (["solid", "dashed", "dotted"],),
102
+ "percentiles": (float,),
103
+ "spacing x": float,
104
+ "spacing y": float,
105
+ }
106
+ }
107
+
108
+
109
+ class Plot(object):
110
+ """Handles plotting information in a pipeline"""
111
+ _instance_counter = 0
112
+ _instances = {}
113
+
114
+ def __init__(self, identifier=None, name=None):
115
+ Plot._instance_counter += 1
116
+ if identifier is None:
117
+ identifier = "Plot_{}".format(Plot._instance_counter)
118
+ while identifier in Plot._instances:
119
+ Plot._instance_counter += 1
120
+ identifier = "Plot_{}".format(Plot._instance_counter)
121
+
122
+ # initially, set default state
123
+ self._state = copy.deepcopy(DEFAULT_STATE)
124
+
125
+ if name is None:
126
+ name = identifier
127
+ #: unique identifier of the plot
128
+ self.identifier = identifier
129
+ #: user-defined name of the plot
130
+ self.name = name
131
+ if identifier in Plot._instances:
132
+ raise ValueError("Plot with identifier "
133
+ + "'{}' already exists!".format(identifier))
134
+ Plot._instances[identifier] = self
135
+
136
+ def __getstate__(self):
137
+ state = copy.deepcopy(self._state)
138
+ state["identifier"] = self.identifier
139
+ return state
140
+
141
+ def __repr__(self):
142
+ repre = "<Pipeline Plot '{}' at {}>".format(self.identifier,
143
+ hex(id(self)))
144
+ return repre
145
+
146
+ def __setstate__(self, state):
147
+ state = copy.deepcopy(state)
148
+ if self.identifier != state["identifier"]:
149
+ raise ValueError("Identifier mismatch: '{}' vs. '{}'".format(
150
+ self.identifier, state["identifier"]))
151
+ # disallow nan-values
152
+ if np.any(np.isinf(state["general"]["range x"])):
153
+ state["general"]["range x"] = [0, 0]
154
+ if np.any(np.isinf(state["general"]["range y"])):
155
+ state["general"]["range y"] = [0, 0]
156
+ self._state = state
157
+
158
+ @staticmethod
159
+ def get_instances():
160
+ return Plot._instances
161
+
162
+ @staticmethod
163
+ def get_plot(identifier):
164
+ """Get the plot with the given identifier.
165
+
166
+ Notes
167
+ -----
168
+ Creates the plot if it does not exist.
169
+ """
170
+ if identifier in Plot._instances:
171
+ f = Plot._instances[identifier]
172
+ else:
173
+ f = Plot(identifier=identifier)
174
+ return f
175
+
176
+ @property
177
+ def hash(self):
178
+ """Return the hash of the plot"""
179
+ return hashobj(self.__getstate__())
180
+
181
+ @property
182
+ def name(self):
183
+ return self._state["layout"]["name"]
184
+
185
+ @name.setter
186
+ def name(self, value):
187
+ self._state["layout"]["name"] = value
dcscope/plot_cache.py ADDED
@@ -0,0 +1,79 @@
1
+ """Facilitate caching of plot data"""
2
+ from dclab.kde import KernelDensityEstimator
3
+
4
+ from . import util
5
+
6
+
7
+ def get_contour_data(rtdc_ds, xax, yax, xacc, yacc, xscale, yscale,
8
+ kde_type="histogram", kde_kwargs=None, quantiles=None):
9
+ if kde_kwargs is None:
10
+ kde_kwargs = {}
11
+ rtdc_ds.apply_filter()
12
+ cfg = rtdc_ds.config
13
+ tohash = [
14
+ rtdc_ds.identifier, rtdc_ds.filter.all,
15
+ cfg.get("calculation", ""),
16
+ xax, yax, xacc, yacc, xscale, yscale,
17
+ kde_type, kde_kwargs]
18
+ shash = util.hashobj(tohash)
19
+ if shash in cache_data:
20
+ contours = cache_data[shash]
21
+ else:
22
+ # compute contour plot data
23
+ kde_instance = KernelDensityEstimator(rtdc_ds=rtdc_ds)
24
+ contours = kde_instance.get_contour_lines(
25
+ quantiles=quantiles,
26
+ xax=xax,
27
+ yax=yax,
28
+ xacc=xacc,
29
+ yacc=yacc,
30
+ xscale=xscale,
31
+ yscale=yscale,
32
+ kde_type=kde_type,
33
+ kde_kwargs=kde_kwargs)
34
+ # save in cache
35
+ cache_data[shash] = contours
36
+ return contours
37
+
38
+
39
+ def get_scatter_data(rtdc_ds, downsample, xax, yax, xscale, yscale,
40
+ kde_type="histogram", kde_kwargs=None):
41
+ if kde_kwargs is None:
42
+ kde_kwargs = {}
43
+ rtdc_ds.apply_filter()
44
+ cfg = rtdc_ds.config
45
+ tohash = [
46
+ rtdc_ds.identifier, rtdc_ds.filter.all, downsample,
47
+ cfg.get("calculation", ""),
48
+ xax, yax, xscale, yscale, kde_type, kde_kwargs]
49
+ shash = util.hashobj(tohash)
50
+ if shash in cache_data:
51
+ x, y, kde, idx = cache_data[shash]
52
+ else:
53
+ # compute scatter plot data
54
+ x, y, idx = rtdc_ds.get_downsampled_scatter(
55
+ xax=xax,
56
+ yax=yax,
57
+ downsample=downsample,
58
+ xscale=xscale,
59
+ yscale=yscale,
60
+ remove_invalid=True,
61
+ ret_mask=True)
62
+ # kde
63
+ kde = rtdc_ds.get_kde_scatter(
64
+ xax=xax,
65
+ yax=yax,
66
+ positions=(x, y),
67
+ kde_type=kde_type,
68
+ kde_kwargs=kde_kwargs,
69
+ xscale=xscale,
70
+ yscale=yscale)
71
+ if kde.size and kde.min() != kde.max():
72
+ kde -= kde.min()
73
+ kde /= kde.max()
74
+ # save in cache
75
+ cache_data[shash] = x, y, kde, idx
76
+ return x, y, kde, idx
77
+
78
+
79
+ cache_data = {}
dcscope/session.py ADDED
@@ -0,0 +1,379 @@
1
+ import hashlib
2
+ import io
3
+ import json
4
+ import os
5
+ import pathlib
6
+ import shutil
7
+ import tempfile
8
+ import zipfile
9
+
10
+ import dclab
11
+ from dclab.util import file_monitoring_lru_cache
12
+ import numpy as np
13
+
14
+ from .pipeline import Dataslot, Filter, Pipeline, Plot
15
+ from ._version import version
16
+
17
+
18
+ class DataFileNotFoundError(BaseException):
19
+ def __init__(self, missing_paths, *args):
20
+ self.missing_paths = missing_paths
21
+ super(DataFileNotFoundError, self).__init__(*args)
22
+
23
+
24
+ class DCscopeSessionJSONEncoder(json.JSONEncoder):
25
+ def default(self, o):
26
+ if isinstance(o, pathlib.Path):
27
+ return {"__type__": "path",
28
+ "__data__": o.as_posix()
29
+ }
30
+ elif isinstance(o, np.floating): # handle np.float32
31
+ return float(o)
32
+
33
+ # Let the base class default method raise the TypeError
34
+ return super(DCscopeSessionJSONEncoder, self).default(o)
35
+
36
+
37
+ class PathlibJSONDecoder(json.JSONDecoder):
38
+ def __init__(self, *args, **kwargs):
39
+ super(PathlibJSONDecoder, self).__init__(object_hook=self.object_hook,
40
+ *args, **kwargs)
41
+
42
+ @staticmethod
43
+ def compat_2_5_1(obj):
44
+ """New standard for emodulus computation keyword arguments
45
+
46
+ This compatibility hook was introduced in DCscope.5.1
47
+ and is used to convert [calculation]: "emodulus model" which
48
+ was deprecated in dclab 0.32.0 to [calculation]: "emodulus lut".
49
+ """
50
+ if ("emodulus model" in obj
51
+ and obj["emodulus model"] == "elastic sphere"):
52
+ obj.pop("emodulus model")
53
+ obj["emodulus lut"] = "LE-2D-FEM-19"
54
+
55
+ def object_hook(self, obj):
56
+ if "__type__" in obj and obj["__type__"] == "path":
57
+ return pathlib.Path(obj["__data__"])
58
+ self.compat_2_5_1(obj)
59
+ return obj
60
+
61
+
62
+ def export_filters(path, pipeline, filt_ids=None):
63
+ """Export filters of a pipeline to a JSON file"""
64
+ if filt_ids is None:
65
+ # export all filters
66
+ filt_ids = pipeline.filter_ids
67
+ # export all polygon filters as well
68
+ # (also if they are not used by any of the filters)
69
+ poly_ids = [pf.unique_id for pf in dclab.PolygonFilter.instances]
70
+ else:
71
+ # get all relevant polygon filters
72
+ poly_ids = set([])
73
+ for filt in pipeline.filters:
74
+ if filt.identifier in filt_ids:
75
+ poly_ids |= set(filt.polylist)
76
+
77
+ poly_states = []
78
+ for pid in sorted(poly_ids):
79
+ pf = dclab.PolygonFilter.get_instance_from_id(pid)
80
+ poly_states.append(pf.__getstate__())
81
+ # Then, get the remaining filter settings
82
+ filt_states = []
83
+ for filt in pipeline.filters:
84
+ if filt.identifier in filt_ids:
85
+ filt_states.append(filt.__getstate__())
86
+ state = {"polygon filters": poly_states,
87
+ "filters": filt_states}
88
+ dump = json.dumps(state, **JSON_DUMP_KWARGS)
89
+ pathlib.Path(path).write_text(dump)
90
+
91
+
92
+ def import_filters(path, pipeline, strict=False):
93
+ """Load filters from a JSON file into a pipeline
94
+
95
+ Parameters
96
+ ----------
97
+ path: pathlib.Path or str or io.IOBase
98
+ Path to the filter file
99
+ pipeline: dcscope.pipeline.Pipeline
100
+ Analysis pipeline to import filters to
101
+ strict: bool
102
+ If False (default), new filter identifiers are created
103
+ for each filter. If True, the given filter identifiers
104
+ will be used - Raises a ValueError if the filter exists.
105
+ For loading filters into a pipeline, this should be False.
106
+ For loading session filters, this should be True.
107
+ Has no effect when `path` is a .poly file.
108
+ """
109
+ if isinstance(path, io.IOBase):
110
+ import_filter_set(path, pipeline, strict)
111
+ else:
112
+ path = pathlib.Path(path)
113
+ if path.suffix == ".poly":
114
+ # add a new polygon filter
115
+ dclab.PolygonFilter.import_all(path)
116
+ elif path.suffix == ".sof":
117
+ import_filter_set(path, pipeline, strict)
118
+ else:
119
+ raise ValueError("Unrecognized file extension "
120
+ + "'{}' for filters.".format(path.suffix))
121
+
122
+
123
+ def import_filter_set(path, pipeline, strict=False):
124
+ """Import a filter set
125
+
126
+ See :func:`import_filters`
127
+ """
128
+ if isinstance(path, io.IOBase):
129
+ dump = path.read()
130
+ else:
131
+ dump = path.read_text()
132
+ dump_state = json.loads(dump)
133
+ # add polygon filters from file
134
+ if not strict:
135
+ pf_dict = {} # maps old to new identifiers
136
+ for pstate in dump_state["polygon filters"]:
137
+ pf = dclab.PolygonFilter(axes=(pstate["axis x"], pstate["axis y"]),
138
+ points=pstate["points"])
139
+ pid = pstate["identifier"]
140
+ if not strict:
141
+ # keep track of old and new identifiers
142
+ pf_dict[pid] = pf.unique_id
143
+ if pid != pf.unique_id:
144
+ if strict:
145
+ if dclab.PolygonFilter.unique_id_exists(pid):
146
+ raise ValueError("PolygonFilter with unique_id "
147
+ + "{} already exists!".format(pid))
148
+ else:
149
+ # change the unique_id to that of the original filter
150
+ pf._set_unique_id(pid)
151
+ else:
152
+ # use the unique_id of the newly-created filter
153
+ pstate["identifier"] = pf.unique_id
154
+ pf.__setstate__(pstate)
155
+ # add a new filter set
156
+ for state in dump_state["filters"]:
157
+ if strict:
158
+ filt = Filter(identifier=state["identifier"])
159
+ else:
160
+ filt = Filter()
161
+ state["identifier"] = filt.identifier
162
+ # transform original polygon filter ids
163
+ newpids = [pf_dict[pid] for pid in state["polygon filters"]]
164
+ state["polygon filters"] = newpids
165
+ filt.__setstate__(state)
166
+ pipeline.add_filter(filt=filt)
167
+
168
+
169
+ @file_monitoring_lru_cache(maxsize=1000)
170
+ def hash_file_partially(path, size=524288):
171
+ """Hash parts of a file for basic identification
172
+
173
+ By default, the first and final 512kB are hashed.
174
+ """
175
+ fsize = path.stat().st_size
176
+ size = min(size, fsize)
177
+ with path.open("rb") as fd:
178
+ head = fd.read(size)
179
+ fd.seek(fsize-size)
180
+ tail = fd.read(size)
181
+ hexhash = hashlib.md5(head + tail).hexdigest()
182
+ return hexhash
183
+
184
+
185
+ def save_session(path, pipeline):
186
+ """Save an entire pipeline session"""
187
+ path = pathlib.Path(path)
188
+ tempdir = pathlib.Path(tempfile.mkdtemp(prefix="DCscope-session-save_"))
189
+ # filters
190
+ export_filters(tempdir / "filters.sof", pipeline)
191
+ # slots
192
+ for ii, slot in enumerate(pipeline.slots):
193
+ sdump = json.dumps(slot.__getstate__(), **JSON_DUMP_KWARGS)
194
+ (tempdir / "slot_{}.json".format(ii)).write_text(sdump)
195
+ # plots
196
+ for jj, plot in enumerate(pipeline.plots):
197
+ pdump = json.dumps(plot.__getstate__(), **JSON_DUMP_KWARGS)
198
+ (tempdir / "plot_{}.json".format(jj)).write_text(pdump)
199
+ # pipeline block matrix
200
+ mdump = json.dumps(pipeline.element_states, **JSON_DUMP_KWARGS)
201
+ (tempdir / "matrix.json").write_text(mdump)
202
+ # additional information
203
+ search_paths = {}
204
+ dataset_hashes = {}
205
+ dataset_formats = {}
206
+ for slot in pipeline.slots:
207
+ # format (hdf5 or dcor)
208
+ dataset_formats[slot.identifier] = slot.format
209
+ if slot.format == "hdf5":
210
+ # search path
211
+ try:
212
+ rel = os.path.relpath(slot.path.parent, path.parent)
213
+ except (OSError, ValueError):
214
+ rel = "."
215
+ search_paths[slot.identifier] = rel
216
+ # file hash
217
+ hash_size = 48128
218
+ dataset_hashes[slot.identifier] = {
219
+ # these are keyword arguments to find_file
220
+ "partial_hash": hash_file_partially(slot.path, size=hash_size),
221
+ "size_read": hash_size
222
+ }
223
+ remarks = {"search paths": search_paths,
224
+ "file hashes": dataset_hashes,
225
+ "formats": dataset_formats,
226
+ "version": version,
227
+ }
228
+ rdump = json.dumps(remarks, **JSON_DUMP_KWARGS)
229
+ (tempdir / "remarks.json").write_text(rdump)
230
+ # zip everything
231
+ with zipfile.ZipFile(path, mode='w') as arc:
232
+ for pp in tempdir.rglob("*"):
233
+ if pp.is_file():
234
+ arc.write(pp, pp.relative_to(tempdir))
235
+ # cleanup
236
+ shutil.rmtree(str(tempdir), ignore_errors=True)
237
+
238
+
239
+ def clear_session(pipeline=None):
240
+ """Clear the entire analysis pipeline"""
241
+ if pipeline is not None:
242
+ # reset the pipeline
243
+ pipeline.reset()
244
+ # Close all file handles
245
+ for slot_id in list(Dataslot._instances.keys()):
246
+ Dataslot.remove_slot(slot_id)
247
+ # remove any existing filters, plots, or slots and reset their counters
248
+ for cls in [Dataslot, Filter, Plot]:
249
+ cls._instance_counter = 0
250
+ cls._instances = {}
251
+ # remove polygon filters
252
+ dclab.PolygonFilter.clear_all_filters()
253
+
254
+
255
+ def find_file(original_path, search_paths, partial_hash, size_read):
256
+ """Find a file
257
+
258
+ Parameters
259
+ ----------
260
+ original_path: pathlib.Path
261
+ The original path to the file
262
+ search_paths: list
263
+ Directories or possible candidates for the file. In
264
+ directories, only files with the same name are searched.
265
+ partial_hash: str
266
+ Hash of the file, see :func:`hash_file_partially`
267
+
268
+ Returns a pathlib.Path object on success, False otherwise.
269
+ """
270
+ search_paths = [pathlib.Path(sp) for sp in search_paths]
271
+ if original_path.exists():
272
+ # we boldly assume that the hash matches
273
+ path = original_path
274
+ else:
275
+ for pp in search_paths:
276
+ if pp.is_dir():
277
+ # look in the directory
278
+ newp = pp / original_path.name
279
+ if newp.exists():
280
+ newh = hash_file_partially(newp, size=size_read)
281
+ if newh == partial_hash:
282
+ path = newp
283
+ break
284
+ elif pp.exists():
285
+ # maybe this is the file?
286
+ newh = hash_file_partially(pp, size=size_read)
287
+ if newh == partial_hash:
288
+ path = pp
289
+ break
290
+ else:
291
+ path = False
292
+ return path
293
+
294
+
295
+ def open_session(path, pipeline=None, search_paths=None):
296
+ """Load a session (optionally overriding an existing pipeline)
297
+
298
+ Parameters
299
+ ----------
300
+ path: pathlib.Path or str
301
+ Path to the session file
302
+ pipeline: dcscope.pipeline.Pipeline or None
303
+ If a pipeline is given, it is reset before loading anything
304
+ search_paths: list
305
+ Paths to search for missing measurements; entries may be
306
+ directories or .rtdc files
307
+ """
308
+ if search_paths is None:
309
+ search_paths = []
310
+ path = pathlib.Path(path)
311
+ if pipeline is None:
312
+ pipeline = Pipeline()
313
+ clear_session(pipeline)
314
+ # read data directly from the zip file
315
+ with zipfile.ZipFile(path, mode='r') as arc:
316
+ # remarks
317
+ remarks = json.loads(arc.read("remarks.json"))
318
+ # determine dataset paths from slot states
319
+ slotnames = sorted(
320
+ [n for n in arc.namelist() if n.startswith("slot_")],
321
+ key=lambda x: int(x[5:-5])) # by index
322
+ slot_states = []
323
+ missing_paths = []
324
+ for sn in slotnames:
325
+ sstate = json.loads(arc.read(sn), cls=PathlibJSONDecoder)
326
+ slot_id = sstate["identifier"]
327
+ # "formats" was added in 2.1.0 (when dcor format was added)
328
+ ishdf5 = ("formats" not in remarks
329
+ or remarks["formats"][slot_id] == "hdf5")
330
+ if ishdf5:
331
+ # also search relative paths
332
+ search_ap = path.parent / remarks["search paths"][slot_id]
333
+ newpath = find_file(original_path=sstate["path"],
334
+ search_paths=search_paths + [search_ap],
335
+ **remarks["file hashes"][slot_id])
336
+ if newpath:
337
+ sstate["path"] = newpath
338
+ slot_states.append(sstate)
339
+ else:
340
+ missing_paths.append(sstate["path"])
341
+ else:
342
+ slot_states.append(sstate)
343
+ # raise an exception if data files are missing
344
+ if missing_paths:
345
+ # Which files are missing is stored as a property in the exception.
346
+ # By catching this exception, a GUI can request the user to select
347
+ # a search directory and try again.
348
+ raise DataFileNotFoundError(
349
+ missing_paths,
350
+ "Some files are missing! You can access them via the "
351
+ + "`missing_paths` property of this exception.")
352
+ # load filters
353
+ import_filters(arc.open("filters.sof"), pipeline, strict=True)
354
+ # load slots
355
+ for sstate in slot_states:
356
+ slot = Dataslot(identifier=sstate["identifier"],
357
+ path=sstate["path"])
358
+ slot.__setstate__(sstate)
359
+ pipeline.add_slot(slot)
360
+ # load plots
361
+ plotnames = sorted(
362
+ [n for n in arc.namelist() if n.startswith("plot_")],
363
+ key=lambda x: int(x[5:-5])) # by index
364
+ for pn in plotnames:
365
+ pstate = json.loads(arc.read(pn))
366
+ plot = Plot(identifier=pstate["identifier"])
367
+ plot.__setstate__(pstate)
368
+ pipeline.add_plot(plot)
369
+ # load element states
370
+ estates = json.loads(arc.read("matrix.json"))
371
+ pipeline.element_states = estates
372
+ return pipeline
373
+
374
+
375
+ JSON_DUMP_KWARGS = {
376
+ "cls": DCscopeSessionJSONEncoder,
377
+ "sort_keys": True,
378
+ "indent": 2,
379
+ }
dcscope/util.py ADDED
@@ -0,0 +1,34 @@
1
+ # hashobj is imported from several other submodules in DCscope.
2
+ # Would we need to add additional functionalities in the future, which
3
+ # are not within the scope of dclab, then we can patch this method here.
4
+ from dclab.util import hashobj # noqa: F401
5
+
6
+
7
+ def get_valid_filename(value):
8
+ """
9
+ Return the given string converted to a string that can be used
10
+ for a clean filename.
11
+ """
12
+ ret = ""
13
+
14
+ valid = "abcdefghijklmnopqrstuvwxyz" \
15
+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
16
+ + "0123456789" \
17
+ + "._-()"
18
+ replace = {
19
+ " ": "_",
20
+ "[": "(",
21
+ "]": ")",
22
+ "µ": "u",
23
+ }
24
+
25
+ for ch in value:
26
+ if ch in valid:
27
+ ret += ch
28
+ elif ch in replace:
29
+ ret += replace[ch]
30
+ else:
31
+ ret += "-"
32
+
33
+ ret = ret.strip(".")
34
+ return ret