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,394 @@
1
+ import importlib.resources
2
+ import logging
3
+ import pathlib
4
+ import traceback
5
+
6
+ from PyQt6 import uic, QtCore, QtTest, QtWidgets
7
+
8
+ import dclab
9
+
10
+ from ..widgets import get_directory, show_wait_cursor
11
+ from ..widgets.feature_combobox import HIDDEN_FEATURES
12
+
13
+ from ...util import get_valid_filename
14
+ from ..._version import version
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ExportData(QtWidgets.QDialog):
21
+ def __init__(self, parent, pipeline, *args, **kwargs):
22
+ super(ExportData, self).__init__(parent=parent, *args, **kwargs)
23
+ ref = importlib.resources.files("dcscope.gui.export") / "e2data.ui"
24
+ with importlib.resources.as_file(ref) as path_ui:
25
+ uic.loadUi(path_ui, self)
26
+
27
+ self.features = []
28
+
29
+ # output path
30
+ self._path = None
31
+ # Get output path
32
+ self.on_browse(force_dialog=False)
33
+ # set pipeline
34
+ self.pipeline = pipeline
35
+ # update list widget
36
+ self.bulklist_features.set_title("Features")
37
+ self.on_radio()
38
+ self.on_select_features_innate()
39
+ # Set storage strategy options
40
+ self.comboBox_storage.clear()
41
+ self.comboBox_storage.addItem(
42
+ "No basins: Store only selected features (legacy behavior)",
43
+ "no-basins"
44
+ )
45
+ self.comboBox_storage.addItem(
46
+ "With basins: Store features, link to original data (recommended)",
47
+ "with-basins"
48
+ )
49
+ self.comboBox_storage.addItem(
50
+ "Only basins: Do not store features, link to original data (fast)",
51
+ "only-basins"
52
+ )
53
+ self.comboBox_storage.setCurrentIndex(
54
+ self.comboBox_storage.findData("with-basins"))
55
+ # Signals
56
+ self.pushButton_path.clicked.connect(self.on_browse)
57
+ # file type selection
58
+ self.radioButton_fcs.clicked.connect(self.on_radio)
59
+ self.radioButton_rtdc.clicked.connect(self.on_radio)
60
+ self.radioButton_tsv.clicked.connect(self.on_radio)
61
+ self.radioButton_avi.clicked.connect(self.on_radio)
62
+ # storage strategy selection
63
+ self.comboBox_storage.currentIndexChanged.connect(
64
+ self.on_storage_strategy)
65
+
66
+ self.comboBox_format.clear()
67
+ self.comboBox_format.addItem("MKV", "mkv")
68
+ self.comboBox_format.addItem("AVI", "avi")
69
+ self.comboBox_format.addItem("MOV", "mov")
70
+
71
+ self.comboBox_codec.clear()
72
+ self.comboBox_codec.addItem("H264 (high quality, fast export)",
73
+ {"pixel_format": "yuv420p",
74
+ "codec": "libx264",
75
+ "codec_options": {'preset': 'ultrafast',
76
+ 'crf': '0'}})
77
+ self.comboBox_codec.addItem("H264 (high quality, small file size)",
78
+ {"pixel_format": "yuv420p",
79
+ "codec": "libx264",
80
+ "codec_options": {'preset': 'slow',
81
+ 'crf': '0'}})
82
+ self.comboBox_codec.addItem("H264 (lossy compression)",
83
+ {"pixel_format": "yuv420p",
84
+ "codec": "libx264",
85
+ "codec_options": {'preset': 'slow',
86
+ 'crf': '7'}})
87
+ self.comboBox_codec.addItem("RAW (huge files)",
88
+ {"pixel_format": "yuv420p",
89
+ "codec": "rawvideo"})
90
+
91
+ @property
92
+ def file_format(self):
93
+ if self.radioButton_fcs.isChecked():
94
+ return "fcs"
95
+ elif self.radioButton_rtdc.isChecked():
96
+ return "rtdc"
97
+ elif self.radioButton_avi.isChecked():
98
+ return self.comboBox_format.currentData()
99
+ else:
100
+ return "tsv"
101
+
102
+ @property
103
+ def path(self):
104
+ return self._path
105
+
106
+ @path.setter
107
+ def path(self, value):
108
+ if value and pathlib.Path(value).exists():
109
+ self._path = value
110
+ self.lineEdit_path.setText(value)
111
+
112
+ @property
113
+ def storage_strategy(self):
114
+ if self.file_format == "rtdc":
115
+ storage_strategy = self.comboBox_storage.currentData()
116
+ else:
117
+ storage_strategy = "no-basins"
118
+ return storage_strategy
119
+
120
+ def done(self, r):
121
+ if r:
122
+ self.export_data()
123
+ super(ExportData, self).done(r)
124
+
125
+ @show_wait_cursor
126
+ @QtCore.pyqtSlot()
127
+ def export_data(self):
128
+ """Export data to the desired file format"""
129
+ # get features
130
+ if self.storage_strategy == "only-basins":
131
+ # This case will also only happen for the .rtdc format
132
+ features = []
133
+ elif self.radioButton_avi.isChecked():
134
+ # We are only exporting images
135
+ features = []
136
+ else:
137
+ features = self.bulklist_features.get_selection()
138
+
139
+ # create dummy progress dialog
140
+ prog = QtWidgets.QProgressDialog("Exporting...", "Abort", 1,
141
+ 10, self)
142
+ prog.setWindowTitle("Data Export")
143
+ prog.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
144
+ prog.setMinimumDuration(0)
145
+ prog.setAutoClose(True)
146
+ QtWidgets.QApplication.processEvents(
147
+ QtCore.QEventLoop.ProcessEventsFlag.AllEvents, 300)
148
+
149
+ # correct dialog maximum
150
+ prog.setValue(0)
151
+ slots_n_paths = self.get_export_filenames()
152
+ pend = len(slots_n_paths) * 100
153
+ prog.setMaximum(pend)
154
+ QtWidgets.QApplication.processEvents(
155
+ QtCore.QEventLoop.ProcessEventsFlag.AllEvents, 300)
156
+
157
+ tasks = []
158
+
159
+ for slot_index, path in slots_n_paths:
160
+ ds = self.pipeline.get_dataset(slot_index)
161
+ # check features
162
+ fmiss = [ff for ff in features if ff not in ds.features]
163
+ if fmiss:
164
+ lmiss = [dclab.dfn.get_feature_label(ff) for ff in fmiss]
165
+ QtWidgets.QMessageBox.warning(
166
+ self,
167
+ "Features missing!",
168
+ (f"Dataslot {slot_index} does not have these features:"
169
+ + "\n"
170
+ + "".join([f"\n- {fl}" for fl in lmiss])
171
+ + "\n\n"
172
+ + f"They are not exported to .{self.file_format}!")
173
+ )
174
+ if self.file_format == "rtdc":
175
+ tasks.append((
176
+ ds.export.hdf5,
177
+ dict(path=path,
178
+ features=[ff for ff in features if ff in ds.features],
179
+ logs=True,
180
+ tables=True,
181
+ basins=self.storage_strategy != "no-basins",
182
+ meta_prefix="",
183
+ override=False)
184
+ ))
185
+ elif self.file_format == "fcs":
186
+ tasks.append((
187
+ ds.export.fcs,
188
+ dict(path=path,
189
+ features=[ff for ff in features if ff in ds.features],
190
+ meta_data={"DCscope version": version},
191
+ override=False)
192
+ ))
193
+ elif self.file_format == "tsv":
194
+ tasks.append((
195
+ ds.export.tsv,
196
+ dict(path=path,
197
+ features=[ff for ff in features if ff in ds.features],
198
+ meta_data={"DCscope version": version},
199
+ override=False)
200
+ ))
201
+ elif self.radioButton_avi.isChecked():
202
+ tasks.append((
203
+ ds.export.avi,
204
+ dict(path=path,
205
+ **self.comboBox_codec.currentData())
206
+ ))
207
+
208
+ logger.info(f"Exporting {len(tasks)} objects")
209
+
210
+ exporter = ExportThread(self, tasks)
211
+ exporter.communicate_progress.connect(prog.setValue)
212
+ exporter.communicate_message.connect(prog.setLabelText)
213
+ prog.canceled.connect(exporter.request_abort)
214
+ exporter.start()
215
+
216
+ while True:
217
+ QtWidgets.QApplication.processEvents(
218
+ QtCore.QEventLoop.ProcessEventsFlag.AllEvents, 300)
219
+ QtTest.QTest.qWait(500)
220
+ if prog.wasCanceled() or exporter.isFinished():
221
+ # This will break the loop but possibly keep the exporter
222
+ # alive. We hope that the exporter will eventually be
223
+ # terminated.
224
+ break
225
+
226
+ prog.setValue(pend)
227
+
228
+ if prog.wasCanceled():
229
+ exporter.terminate()
230
+ else:
231
+ exporter.wait()
232
+
233
+ if exporter.failed_tasks:
234
+ info_string = "\n".join(
235
+ [f"- {kw['path']}" for _, kw in exporter.failed_tasks])
236
+ QtWidgets.QMessageBox.critical(
237
+ self, f"Error exporting {len(exporter.failed_tasks)} objects",
238
+ f"Could not export to the following paths:\n{info_string}")
239
+
240
+ def get_export_filenames(self):
241
+ """Compute names for exporting data, avoiding overriding anything
242
+
243
+ Return a list of tuples `(slot_index, filename)`.
244
+ """
245
+ # for every slot there is a path
246
+ slots_n_paths = []
247
+ out = pathlib.Path(self.path)
248
+ # assemble the slots
249
+ slots = []
250
+ for s_index in range(len(self.pipeline.slots)):
251
+ slot = self.pipeline.slots[s_index]
252
+ if slot.slot_used:
253
+ slots.append((s_index, slot))
254
+ # find non-existent file names
255
+ ap = "" # this gets appended to the file stem if the file exists
256
+ counter = 0 # counts up an index for appending to the file
257
+ while True:
258
+ slots_n_paths.clear()
259
+ for s_index, slot in slots:
260
+ fn = f"SO2-export_{s_index}_{slot.name}{ap}.{self.file_format}"
261
+ # remove bad characters from file name
262
+ fn = get_valid_filename(fn)
263
+ path = out / fn
264
+ if path.exists():
265
+ # The file already exists. Break here and the counter
266
+ # is incremented for a next iteration.
267
+ break
268
+ else:
269
+ # Everything good so far.
270
+ slots_n_paths.append((s_index, path))
271
+ else:
272
+ # If nothing in the for loop caused it to break, then we
273
+ # have a fully populated list of slots_n_paths, and we can
274
+ # exit this while-loop.
275
+ break
276
+
277
+ counter += 1
278
+ ap = f"_{counter}"
279
+ # Return the list of slots and corresponding paths
280
+ return slots_n_paths
281
+
282
+ @QtCore.pyqtSlot()
283
+ def on_browse(self, force_dialog=True):
284
+ self.path = get_directory(
285
+ parent=self,
286
+ identifier="export data",
287
+ caption="Export directory",
288
+ force_dialog=force_dialog
289
+ )
290
+
291
+ @QtCore.pyqtSlot()
292
+ def on_radio(self):
293
+ self.widget_storage_strategy.setEnabled(self.file_format == "rtdc")
294
+ # set storage strategy based on file format
295
+ strategy = "with-basins" if self.file_format == "rtdc" else "no-basins"
296
+ self.comboBox_storage.setCurrentIndex(
297
+ self.comboBox_storage.findData(strategy))
298
+
299
+ if self.radioButton_avi.isChecked():
300
+ self.stackedWidget.setCurrentWidget(self.page_video)
301
+ else:
302
+ self.update_feature_list()
303
+ self.stackedWidget.setCurrentWidget(self.page_features)
304
+
305
+ @QtCore.pyqtSlot()
306
+ def on_select_features_innate(self):
307
+ """Only select all innate features of the first dataset"""
308
+ if self.pipeline.num_slots:
309
+ ds = self.pipeline.get_dataset(0)
310
+ features_loaded = ds.features_loaded
311
+ lw = self.bulklist_features.listWidget
312
+ for ii in range(lw.count()):
313
+ wid = lw.item(ii)
314
+ for feat in features_loaded:
315
+ if wid.data(101) == feat:
316
+ wid.setCheckState(QtCore.Qt.CheckState.Checked)
317
+ break
318
+ else:
319
+ wid.setCheckState(QtCore.Qt.CheckState.Unchecked)
320
+
321
+ @QtCore.pyqtSlot()
322
+ def on_storage_strategy(self):
323
+ self.bulklist_features.setEnabled(
324
+ self.storage_strategy != "only-basins")
325
+
326
+ def update_feature_list(self, scalar=False):
327
+ if self.file_format == "rtdc":
328
+ self.features = self.pipeline.get_features(union=True,
329
+ label_sort=True)
330
+ # do not allow exporting event index, since it will be
331
+ # re-enumerated in any case.
332
+ self.features.remove("index")
333
+ else:
334
+ self.features = self.pipeline.get_features(scalar=True,
335
+ union=True,
336
+ label_sort=True)
337
+ # do not export basinmap features
338
+ for feat in HIDDEN_FEATURES + ["index"]:
339
+ if feat in self.features:
340
+ self.features.remove(feat)
341
+
342
+ labels = [dclab.dfn.get_feature_label(feat) for feat in self.features]
343
+ self.bulklist_features.set_items(self.features, labels)
344
+ self.on_select_features_innate()
345
+
346
+
347
+ class ExportAbortError(BaseException):
348
+ """Used for aborting data export via progress_callback"""
349
+ pass
350
+
351
+
352
+ class ExportThread(QtCore.QThread):
353
+ communicate_progress = QtCore.pyqtSignal(int)
354
+ communicate_message = QtCore.pyqtSignal(str)
355
+
356
+ def __init__(self, parent, tasks):
357
+ super(ExportThread, self).__init__(parent)
358
+ self.abort = False
359
+ self.tasks = tasks
360
+ self.current_path = None
361
+ self.tasks_done = []
362
+ self.failed_tasks = []
363
+
364
+ def progress_callback(self, progress, message):
365
+ cur_pos = int((len(self.tasks_done) + progress) * 100)
366
+ self.communicate_progress.emit(cur_pos)
367
+ self.communicate_message.emit(f"{self.current_path.name} ({message})")
368
+ if self.abort:
369
+ raise ExportAbortError("User aborted")
370
+
371
+ def run(self):
372
+ for ii in range(len(self.tasks)):
373
+ if self.abort:
374
+ break
375
+ func, kwargs = self.tasks.pop(0)
376
+ self.current_path = pathlib.Path(kwargs["path"])
377
+ try:
378
+ func(progress_callback=self.progress_callback, **kwargs)
379
+ except ExportAbortError:
380
+ # remove current path
381
+ self.current_path.unlink(missing_ok=True)
382
+ break
383
+ except BaseException:
384
+ # remove current path
385
+ self.current_path.unlink(missing_ok=True)
386
+ logger.error(traceback.format_exc())
387
+ self.failed_tasks.append((func, kwargs))
388
+ continue
389
+ finally:
390
+ self.tasks_done.append((func, kwargs))
391
+
392
+ @QtCore.pyqtSlot()
393
+ def request_abort(self):
394
+ self.abort = True