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,140 @@
1
+ import dclab
2
+ from PyQt6 import QtCore, QtGui, QtWidgets
3
+
4
+ #: These are features only visible in developer mode
5
+ HIDDEN_FEATURES = []
6
+ for ii in range(10):
7
+ HIDDEN_FEATURES.append(f"basinmap{ii}")
8
+
9
+
10
+ class FeatureComboBox(QtWidgets.QComboBox):
11
+ def __init__(self, *args, **kwargs):
12
+ """A DC data aware combobox for displaying DC features
13
+
14
+ The combobox uses different colors do indicate the
15
+ availability of features. Features can be ancillary,
16
+ innate, basin-based, etc.
17
+
18
+ This class serves as a drop-in replacement for QComboBox
19
+ with a few special properties:
20
+
21
+ - Call `set_dataset` with an instance of RTDCBase and the
22
+ list of options is automatically populated with the values
23
+ for this dataset, including coloring based on feature origin.
24
+ - Features defined in :const:`HIDDEN_FEATURES` are not shown in
25
+ the combobox if "advanced/developer mode" is not set in the
26
+ settings.
27
+ - Make sure to use `findData` and don't rely on any external
28
+ lists when using `addItem`, because some of the items may
29
+ not be added as explained in the previous point.
30
+ """
31
+ super(FeatureComboBox, self).__init__(*args, **kwargs)
32
+ self.rtdc_ds = None
33
+ self.default_choices = []
34
+
35
+ # determine whether we should hide certain features
36
+ settings = QtCore.QSettings()
37
+ devmode = bool(int(settings.value("advanced/developer mode", 0)))
38
+ self.hidden_feats = HIDDEN_FEATURES if not devmode else []
39
+
40
+ # where the data at
41
+ self.data_role = QtCore.Qt.ItemDataRole.UserRole
42
+
43
+ # Set background color to white
44
+ pal = self.palette()
45
+ pal.setColor(self.backgroundRole(), QtGui.QColor(255, 255, 255))
46
+
47
+ def addItem(self, text, userData=None):
48
+ if userData not in self.hidden_feats:
49
+ super(FeatureComboBox, self).addItem(text, userData)
50
+
51
+ def currentData(self, role=None):
52
+ if role is None:
53
+ role = self.data_role
54
+ return super(FeatureComboBox, self).currentData(role)
55
+
56
+ def findData(self, data, role=None, *args, **kwargs):
57
+ if role is None:
58
+ role = self.data_role
59
+ return super(FeatureComboBox, self).findData(data, role=role,
60
+ *args, **kwargs)
61
+
62
+ def set_dataset(self, rtdc_ds):
63
+ self.rtdc_ds = rtdc_ds
64
+ self.update_feature_list()
65
+
66
+ @QtCore.pyqtSlot()
67
+ def update_feature_list(self):
68
+ """Update the colors of all features in the combobox"""
69
+ if self.rtdc_ds is None:
70
+ raise ValueError("Please call `set_dataset` first!")
71
+ # axes combobox choices
72
+ ds_feats = self.rtdc_ds.features_scalar
73
+ # remove hidden features
74
+ [ds_feats.remove(f) for f in self.hidden_feats if f in ds_feats]
75
+ ds_labels = [dclab.dfn.get_feature_label(f) for f in ds_feats]
76
+ ds_colors = []
77
+ ds_tips = []
78
+ feats_loaded = self.rtdc_ds.features_loaded
79
+ feats_ancillary = self.rtdc_ds.features_ancillary
80
+ feats_basin = self.rtdc_ds.features_basin
81
+ for feat in ds_feats:
82
+ if feat in feats_loaded:
83
+ ds_colors.append("#D2FFC8")
84
+ ds_tips.append("feature data loaded")
85
+ elif feat in feats_basin:
86
+ ds_colors.append("#C0E8FF")
87
+ ds_tips.append("feature data located in basin")
88
+ elif feat in feats_ancillary:
89
+ ds_colors.append("#FFD9C1")
90
+ ds_tips.append("feature data needs to be computed")
91
+ else:
92
+ # fall-back
93
+ ds_colors.append("#A9A9A9")
94
+ ds_tips.append("unknown location of feature data")
95
+
96
+ ds_fl = sorted(zip(ds_labels, ds_feats, ds_colors, ds_tips))
97
+
98
+ # Remember current index. If it is -1, we can select a best feature
99
+ # based on self.default_choices in the end.
100
+ idx_cur = self.currentIndex()
101
+
102
+ # Remember user selection. If it exists in ds_feats, the selection
103
+ # will persist even if the user switches the dataset.
104
+ feat_cur = self.currentData(self.data_role)
105
+
106
+ blocked = self.signalsBlocked() # remember block state
107
+ self.blockSignals(True)
108
+
109
+ # set features
110
+ self.clear()
111
+ model = self.model()
112
+ for (label, feat, color, tip) in ds_fl:
113
+ item = QtGui.QStandardItem(label)
114
+ item.setData(feat, self.data_role)
115
+ item.setBackground(QtGui.QColor(color))
116
+ item.setForeground(QtGui.QColor("black"))
117
+ item.setToolTip(tip)
118
+ model.appendRow(item)
119
+
120
+ if feat_cur and feat_cur in ds_feats:
121
+ # If the previous selection exists in the new feature list, set it.
122
+ idx_cur = self.findData(feat_cur)
123
+ else:
124
+ # If the previous selection does not exist in the new feature list,
125
+ # select the first available feature in `default_choices`
126
+ idx_cur = -1
127
+
128
+ if idx_cur < 0:
129
+ # If no selection made by user, select the first available feature
130
+ # in `default_choices`
131
+ for choice in self.default_choices:
132
+ idx_choice = self.findData(choice)
133
+ if idx_choice >= 0:
134
+ idx_cur = idx_choice
135
+ break
136
+
137
+ self.setCurrentIndex(idx_cur)
138
+
139
+ # set previous selection
140
+ self.blockSignals(blocked)
@@ -0,0 +1,42 @@
1
+ import pathlib
2
+
3
+ from PyQt6 import QtCore, QtWidgets
4
+
5
+
6
+ def get_directory(
7
+ parent: QtWidgets.QWidget,
8
+ identifier: str,
9
+ caption: str = "Select directory",
10
+ force_dialog: bool = True,
11
+ ):
12
+ """Retrieve the path for a given identifier
13
+
14
+ When called for the first time, a directory dialog is opened and
15
+ the user can select the target directory. The directory is then stored
16
+ in the settings.
17
+ When called for the second time, a dialog will only pop up when
18
+ `show_dialog` is set to `True`, otherwise the value from the settings
19
+ is returned.
20
+ """
21
+ path = "" # default value
22
+ settings = QtCore.QSettings()
23
+ if not force_dialog:
24
+ # attempt to get the directory from the settings
25
+ dir_settings = settings.value(f"paths/{identifier}", path)
26
+ if dir_settings and pathlib.Path(dir_settings).exists():
27
+ path = dir_settings
28
+ else:
29
+ # we *have* to ask the user for the directory
30
+ force_dialog = True
31
+
32
+ if force_dialog:
33
+ dir_user = QtWidgets.QFileDialog.getExistingDirectory(
34
+ parent=parent,
35
+ caption=caption,
36
+ directory=settings.value(f"paths/{identifier}", path)
37
+ )
38
+ if dir_user:
39
+ path = dir_user
40
+ settings.setValue(f"paths/{identifier}", path)
41
+
42
+ return path
@@ -0,0 +1,12 @@
1
+ /* This did not work (overlap with grid line). I used spaced for padding instead. */
2
+ /*
3
+ QTableView::item
4
+ {
5
+ border: 0px;
6
+ padding: 5px;
7
+ }
8
+ */
9
+
10
+ QTableView {
11
+ gridline-color: white;
12
+ }
@@ -0,0 +1,75 @@
1
+ import pathlib
2
+ import importlib.resources
3
+
4
+ import numpy as np
5
+ from PyQt6 import QtCore, QtWidgets
6
+
7
+
8
+ class KeyValueTableWidget(QtWidgets.QTableWidget):
9
+ def __init__(self, *args, **kwargs):
10
+ """A table widgets with two columns for key-value visualization"""
11
+ super(KeyValueTableWidget, self).__init__(*args, **kwargs)
12
+ ref = importlib.resources.files(
13
+ "dcscope.gui.widgets") / "key_value_table_widget.css"
14
+ with importlib.resources.as_file(ref) as path_css:
15
+ stylesheet = pathlib.Path(path_css).read_text()
16
+ self.setStyleSheet(stylesheet)
17
+ self.horizontalHeader().hide()
18
+ self.verticalHeader().hide()
19
+ self.setAlternatingRowColors(True)
20
+ self.setSelectionMode(
21
+ QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
22
+ # For some reason this does not work here.
23
+ # self.setColumnCount(2)
24
+ # header = self.horizontalHeader()
25
+ # header.setSectionResizeMode(0,
26
+ # QtWidgets.QHeaderView.ResizeToContents)
27
+ # header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
28
+
29
+ def set_key_vals(self, keys, vals, max_key_len=23):
30
+ """Convenience function for setting key-value pairs in this table"""
31
+ # disable updates
32
+ self.setUpdatesEnabled(False)
33
+ # rows'n'cols
34
+ self.setRowCount(len(keys))
35
+ self.setColumnCount(2)
36
+ # populate
37
+ for ii, (hi, vi) in enumerate(zip(keys, vals)):
38
+ # name
39
+ if len(hi) < max_key_len:
40
+ name_vis = hi
41
+ else:
42
+ name_vis = hi[:max_key_len-3] + "..."
43
+ # pad with spaces (b/c css padding caused overlap)
44
+ name_vis = " " + name_vis + " "
45
+ label_name = self.item(ii, 0)
46
+ if label_name is None:
47
+ label_name = QtWidgets.QTableWidgetItem(name_vis)
48
+ label_name.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
49
+ self.setItem(ii, 0, label_name)
50
+ else:
51
+ if label_name.text() != name_vis:
52
+ label_name.setText(name_vis)
53
+ label_name.setToolTip(hi)
54
+ # value
55
+ if np.isnan(vi) or np.isinf(vi):
56
+ fmt = "{}"
57
+ else:
58
+ fmt = "{:.7g}"
59
+ value_vis = fmt.format(vi)
60
+ label_value = self.item(ii, 1)
61
+ if label_value is None:
62
+ label_value = QtWidgets.QTableWidgetItem(value_vis)
63
+ label_value.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
64
+ self.setItem(ii, 1, label_value)
65
+ else:
66
+ label_value.setText(value_vis)
67
+ label_value.setToolTip(hi)
68
+ # spacing (did not work in __init__)
69
+ header = self.horizontalHeader()
70
+ header.setSectionResizeMode(
71
+ 0, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
72
+ header.setSectionResizeMode(1,
73
+ QtWidgets.QHeaderView.ResizeMode.Stretch)
74
+ # enable updates again
75
+ self.setUpdatesEnabled(True)
@@ -0,0 +1,12 @@
1
+ from PyQt6 import QtCore, QtWidgets
2
+
3
+
4
+ class MDISubWindowWOButtons(QtWidgets.QMdiSubWindow):
5
+ def __init__(self, *args, **kwargs):
6
+ super(MDISubWindowWOButtons, self).__init__(*args, **kwargs)
7
+ self.setSystemMenu(None)
8
+ self.setWindowFlags(QtCore.Qt.WindowType.CustomizeWindowHint
9
+ | QtCore.Qt.WindowType.WindowTitleHint)
10
+
11
+ def closeEvent(self, event):
12
+ event.ignore()
@@ -0,0 +1,262 @@
1
+ from PyQt6 import QtCore, QtGui, QtWidgets
2
+
3
+ DEFAULT_CSS = """
4
+ QRangeSlider * {
5
+ border: 0px;
6
+ padding: 0px;
7
+ }
8
+ QRangeSlider #Head {
9
+ background: #cef;
10
+ }
11
+ QRangeSlider #Span {
12
+ background: #16f;
13
+ }
14
+ QRangeSlider #Span:active {
15
+ background: #05f;
16
+ }
17
+ QRangeSlider #Tail {
18
+ background: #cef;
19
+ }
20
+ QRangeSlider > QSplitter::handle {
21
+ background: #08f;
22
+ }
23
+ QRangeSlider > QSplitter::handle:vertical {
24
+ height: 3px;
25
+ }
26
+ QRangeSlider > QSplitter::handle:pressed {
27
+ background: #0cf;
28
+ }
29
+ """
30
+
31
+
32
+ def scale(val, src, dst):
33
+ ret = ((val - src[0]) / float(src[1]-src[0])) * (dst[1]-dst[0]) + dst[0]
34
+ return int(ret)
35
+
36
+
37
+ class Ui_Form(object):
38
+ def setupUi(self, Form):
39
+ Form.setObjectName("QRangeSlider")
40
+ Form.setStyleSheet(DEFAULT_CSS)
41
+ self.gridLayout = QtWidgets.QGridLayout(Form)
42
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
43
+ self.gridLayout.setSpacing(0)
44
+ self.gridLayout.setObjectName("gridLayout")
45
+ self._splitter = QtWidgets.QSplitter(Form)
46
+ self._splitter.setMinimumSize(QtCore.QSize(0, 0))
47
+ self._splitter.setMaximumSize(QtCore.QSize(16777215, 16777215))
48
+ self._splitter.setOrientation(QtCore.Qt.Orientation.Horizontal)
49
+ self._splitter.setObjectName("splitter")
50
+ self._head = QtWidgets.QGroupBox(self._splitter)
51
+ self._head.setTitle("")
52
+ self._head.setObjectName("Head")
53
+ self._handle = QtWidgets.QGroupBox(self._splitter)
54
+ self._handle.setTitle("")
55
+ self._handle.setObjectName("Span")
56
+ self._tail = QtWidgets.QGroupBox(self._splitter)
57
+ self._tail.setTitle("")
58
+ self._tail.setObjectName("Tail")
59
+ self.gridLayout.addWidget(self._splitter, 0, 0, 1, 1)
60
+ self.retranslateUi(Form)
61
+ QtCore.QMetaObject.connectSlotsByName(Form)
62
+
63
+ def retranslateUi(self, Form):
64
+ _translate = QtCore.QCoreApplication.translate
65
+ Form.setWindowTitle(_translate("QRangeSlider", "QRangeSlider"))
66
+
67
+
68
+ class Element(QtWidgets.QGroupBox):
69
+ def __init__(self, parent, main, *args, **kwargs):
70
+ super(Element, self).__init__(parent, *args, **kwargs)
71
+ self.main = main
72
+
73
+ def setStyleSheet(self, style):
74
+ self.parent().setStyleSheet(style)
75
+
76
+ def textColor(self):
77
+ return getattr(self, '__textColor', QtGui.QColor(125, 125, 125))
78
+
79
+ def setTextColor(self, color):
80
+ if type(color) is tuple and len(color) == 3:
81
+ color = QtGui.QColor(color[0], color[1], color[2])
82
+ elif type(color) is int:
83
+ color = QtGui.QColor(color, color, color)
84
+ setattr(self, '__textColor', color)
85
+
86
+ def paintEvent(self, event):
87
+ qp = QtGui.QPainter()
88
+ qp.begin(self)
89
+ if self.main.drawValues():
90
+ self.drawText(event, qp)
91
+ qp.end()
92
+
93
+
94
+ class Head(Element):
95
+ def __init__(self, parent, main, *args, **kwargs):
96
+ super(Head, self).__init__(parent, main, *args, **kwargs)
97
+
98
+ def drawText(self, event, qp):
99
+ qp.setPen(self.textColor())
100
+ qp.setFont(QtGui.QFont('Arial', 10))
101
+ qp.drawText(event.rect(), QtCore.Qt.AlignmentFlag.AlignLeft,
102
+ str(self.main.min()))
103
+
104
+
105
+ class Tail(Element):
106
+ def __init__(self, parent, main, *args, **kwargs):
107
+ super(Tail, self).__init__(parent, main, *args, **kwargs)
108
+
109
+ def drawText(self, event, qp):
110
+ qp.setPen(self.textColor())
111
+ qp.setFont(QtGui.QFont('Arial', 10))
112
+ qp.drawText(event.rect(), QtCore.Qt.AlignmentFlag.AlignRight,
113
+ str(self.main.max()))
114
+
115
+
116
+ class Handle(Element):
117
+ def __init__(self, parent, main, *args, **kwargs):
118
+ super(Handle, self).__init__(parent, main, *args, **kwargs)
119
+
120
+ def drawText(self, event, qp):
121
+ qp.setPen(self.textColor())
122
+ qp.setFont(QtGui.QFont('Arial', 10))
123
+ qp.drawText(event.rect(), QtCore.Qt.AlignmentFlag.AlignLeft,
124
+ str(self.main.start()))
125
+ qp.drawText(event.rect(), QtCore.Qt.AlignmentFlag.AlignRight,
126
+ str(self.main.end()))
127
+
128
+
129
+ class QRangeSlider(QtWidgets.QWidget, Ui_Form):
130
+ minValueChanged = QtCore.pyqtSignal(int)
131
+ maxValueChanged = QtCore.pyqtSignal(int)
132
+ startValueChanged = QtCore.pyqtSignal(int)
133
+ endValueChanged = QtCore.pyqtSignal(int)
134
+ rangeChanged = QtCore.pyqtSignal(int, int)
135
+
136
+ _INT_NUM = 2000
137
+ _SPLIT_START = 1
138
+ _SPLIT_END = 2
139
+
140
+ def __init__(self, parent=None, *args, **kwargs):
141
+ super(QRangeSlider, self).__init__(parent, *args, **kwargs)
142
+ self.setupUi(self)
143
+ self.setFixedHeight(21)
144
+ self.setMouseTracking(False)
145
+ self._splitter.splitterMoved.connect(self._handleMoveSplitter)
146
+ self._head_layout = QtWidgets.QHBoxLayout()
147
+ self._head_layout.setSpacing(0)
148
+ self._head_layout.setContentsMargins(0, 0, 0, 0)
149
+ self._head.setLayout(self._head_layout)
150
+ self.head = Head(self._head, main=self)
151
+ self._head_layout.addWidget(self.head)
152
+ self._handle_layout = QtWidgets.QHBoxLayout()
153
+ self._handle_layout.setSpacing(0)
154
+ self._handle_layout.setContentsMargins(0, 0, 0, 0)
155
+ self._handle.setLayout(self._handle_layout)
156
+ self.handle = Handle(self._handle, main=self)
157
+ self._handle_layout.addWidget(self.handle)
158
+ self._tail_layout = QtWidgets.QHBoxLayout()
159
+ self._tail_layout.setSpacing(0)
160
+ self._tail_layout.setContentsMargins(0, 0, 0, 0)
161
+ self._tail.setLayout(self._tail_layout)
162
+ self.tail = Tail(self._tail, main=self)
163
+ self._tail_layout.addWidget(self.tail)
164
+ # set limits
165
+ self.setMin(0)
166
+ self.setMax(self._INT_NUM)
167
+ self.setStart(0)
168
+ self.setEnd(self._INT_NUM)
169
+ self.setDrawValues(False)
170
+
171
+ @QtCore.pyqtSlot(object)
172
+ def resizeEvent(self, event):
173
+ # The geometry changed and `self.width` is different now. Adjust
174
+ # the entire range slider to reflect the new values.
175
+ self.setStart(self.start())
176
+ self.setEnd(self.end())
177
+ super(QRangeSlider, self).resizeEvent(event)
178
+
179
+ def min(self):
180
+ return getattr(self, '__min', None)
181
+
182
+ def max(self):
183
+ return getattr(self, '__max', None)
184
+
185
+ def setMin(self, value):
186
+ setattr(self, '__min', value)
187
+ self.minValueChanged.emit(value)
188
+
189
+ def setMax(self, value):
190
+ setattr(self, '__max', value)
191
+ self.maxValueChanged.emit(value)
192
+
193
+ def start(self):
194
+ return getattr(self, '__start', None)
195
+
196
+ def end(self):
197
+ return getattr(self, '__end', None)
198
+
199
+ def _setStart(self, value):
200
+ setattr(self, '__start', value)
201
+ self.startValueChanged.emit(value)
202
+ self.rangeChanged.emit(value, self.end())
203
+
204
+ def setStart(self, value):
205
+ v = self._valueToPos(value)
206
+ self._splitter.splitterMoved.disconnect()
207
+ self._splitter.moveSplitter(v, self._SPLIT_START)
208
+ self._splitter.refresh()
209
+ self._splitter.splitterMoved.connect(self._handleMoveSplitter)
210
+ self._setStart(value)
211
+
212
+ def _setEnd(self, value):
213
+ setattr(self, '__end', value)
214
+ self.endValueChanged.emit(value)
215
+ self.rangeChanged.emit(self.start(), value)
216
+
217
+ def setEnd(self, value):
218
+ v = self._valueToPos(value)
219
+ self._splitter.splitterMoved.disconnect()
220
+ self._splitter.moveSplitter(v, self._SPLIT_END)
221
+ self._splitter.refresh()
222
+ self._splitter.splitterMoved.connect(self._handleMoveSplitter)
223
+ self._setEnd(value)
224
+
225
+ def drawValues(self):
226
+ return getattr(self, '__drawValues', None)
227
+
228
+ def setDrawValues(self, draw):
229
+ setattr(self, '__drawValues', draw)
230
+
231
+ def getRange(self):
232
+ return self.start(), self.end()
233
+
234
+ def setRange(self, start, end):
235
+ self.setStart(start)
236
+ self.setEnd(end)
237
+
238
+ def setBackgroundStyle(self, style):
239
+ self._tail.setStyleSheet(style)
240
+ self._head.setStyleSheet(style)
241
+
242
+ def setSpanStyle(self, style):
243
+ self._handle.setStyleSheet(style)
244
+
245
+ def _valueToPos(self, value):
246
+ return scale(value, (self.min(), self.max()), (0, self.width()))
247
+
248
+ def _posToValue(self, xpos):
249
+ return scale(xpos, (0, self.width()), (self.min(), self.max()))
250
+
251
+ def _handleMoveSplitter(self, xpos, index):
252
+ hw = self._splitter.handleWidth()
253
+ if index == self._SPLIT_START:
254
+ v = self._posToValue(xpos)
255
+ if v >= self.end():
256
+ self.setEnd(self._INT_NUM)
257
+ self._setStart(v)
258
+ elif index == self._SPLIT_END:
259
+ v = self._posToValue(xpos + hw)
260
+ if v <= self.start():
261
+ self.setStart(0)
262
+ self._setEnd(v)