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.
- dcscope/__init__.py +17 -0
- dcscope/__main__.py +44 -0
- dcscope/_version.py +21 -0
- dcscope/extensions.py +250 -0
- dcscope/gui/__init__.py +2 -0
- dcscope/gui/analysis/__init__.py +10 -0
- dcscope/gui/analysis/ana_basins.py +110 -0
- dcscope/gui/analysis/ana_basins.ui +143 -0
- dcscope/gui/analysis/ana_filter.py +341 -0
- dcscope/gui/analysis/ana_filter.ui +364 -0
- dcscope/gui/analysis/ana_log.py +122 -0
- dcscope/gui/analysis/ana_log.ui +100 -0
- dcscope/gui/analysis/ana_meta.py +207 -0
- dcscope/gui/analysis/ana_meta.ui +141 -0
- dcscope/gui/analysis/ana_plot.py +638 -0
- dcscope/gui/analysis/ana_plot.ui +1060 -0
- dcscope/gui/analysis/ana_slot.py +504 -0
- dcscope/gui/analysis/ana_slot.ui +684 -0
- dcscope/gui/analysis/ana_tables.py +227 -0
- dcscope/gui/analysis/ana_tables.ui +195 -0
- dcscope/gui/analysis/ana_view.py +69 -0
- dcscope/gui/analysis/ana_view.ui +269 -0
- dcscope/gui/analysis/dlg_slot_reorder.py +55 -0
- dcscope/gui/analysis/dlg_slot_reorder.ui +138 -0
- dcscope/gui/bulk/__init__.py +2 -0
- dcscope/gui/bulk/bulk_emodulus.py +200 -0
- dcscope/gui/bulk/bulk_emodulus.ui +245 -0
- dcscope/gui/compute/__init__.py +3 -0
- dcscope/gui/compute/comp_lme4.py +115 -0
- dcscope/gui/compute/comp_lme4.ui +194 -0
- dcscope/gui/compute/comp_lme4_dataset.py +51 -0
- dcscope/gui/compute/comp_lme4_dataset.ui +92 -0
- dcscope/gui/compute/comp_lme4_results.py +120 -0
- dcscope/gui/compute/comp_lme4_results.ui +268 -0
- dcscope/gui/compute/comp_stats.py +223 -0
- dcscope/gui/compute/comp_stats.ui +187 -0
- dcscope/gui/dcor/__init__.py +304 -0
- dcscope/gui/dcor/dcor.ui +118 -0
- dcscope/gui/export/__init__.py +3 -0
- dcscope/gui/export/e2data.py +394 -0
- dcscope/gui/export/e2data.ui +367 -0
- dcscope/gui/export/e2filter.py +76 -0
- dcscope/gui/export/e2filter.ui +111 -0
- dcscope/gui/export/e2plot.py +94 -0
- dcscope/gui/export/e2plot.ui +175 -0
- dcscope/gui/main.py +1089 -0
- dcscope/gui/main.ui +579 -0
- dcscope/gui/matrix/__init__.py +4 -0
- dcscope/gui/matrix/block_matrix.py +141 -0
- dcscope/gui/matrix/block_matrix.ui +207 -0
- dcscope/gui/matrix/data_matrix.py +574 -0
- dcscope/gui/matrix/dm_dataset.py +116 -0
- dcscope/gui/matrix/dm_dataset.ui +348 -0
- dcscope/gui/matrix/dm_element.py +111 -0
- dcscope/gui/matrix/dm_element.ui +169 -0
- dcscope/gui/matrix/dm_filter.py +115 -0
- dcscope/gui/matrix/dm_filter.ui +287 -0
- dcscope/gui/matrix/plot_matrix.py +316 -0
- dcscope/gui/matrix/pm_element.py +39 -0
- dcscope/gui/matrix/pm_plot.py +86 -0
- dcscope/gui/matrix/pm_plot.ui +249 -0
- dcscope/gui/pipeline_plot.py +797 -0
- dcscope/gui/pipeline_plot.ui +66 -0
- dcscope/gui/preferences.py +380 -0
- dcscope/gui/preferences.ui +554 -0
- dcscope/gui/quick_view/__init__.py +1 -0
- dcscope/gui/quick_view/qv_main.py +1072 -0
- dcscope/gui/quick_view/qv_main.ui +1297 -0
- dcscope/gui/quick_view/qv_scatter.py +284 -0
- dcscope/gui/quick_view/qv_style.css +10 -0
- dcscope/gui/update.py +90 -0
- dcscope/gui/widgets/__init__.py +15 -0
- dcscope/gui/widgets/bg_thread.py +29 -0
- dcscope/gui/widgets/bulk_list.py +79 -0
- dcscope/gui/widgets/bulk_list.ui +95 -0
- dcscope/gui/widgets/double_spin_box_nan.py +75 -0
- dcscope/gui/widgets/feature_combobox.py +140 -0
- dcscope/gui/widgets/get_path.py +42 -0
- dcscope/gui/widgets/key_value_table_widget.css +12 -0
- dcscope/gui/widgets/key_value_table_widget.py +75 -0
- dcscope/gui/widgets/mdi_subwindow_wo_close.py +12 -0
- dcscope/gui/widgets/qrangeslider.py +262 -0
- dcscope/gui/widgets/rangecontrol.py +308 -0
- dcscope/gui/widgets/rangecontrol.ui +136 -0
- dcscope/gui/widgets/simple_image_view.py +55 -0
- dcscope/gui/widgets/simple_plot_widget.py +113 -0
- dcscope/gui/widgets/so_colorbaritem.py +29 -0
- dcscope/gui/widgets/wait_cursor.py +33 -0
- dcscope/idiom.py +26 -0
- dcscope/img/__init__.py +2 -0
- dcscope/img/icon-theme/breeze/COPYING-ICONS +209 -0
- dcscope/img/icon-theme/breeze/COPYING.LIB +510 -0
- dcscope/img/icon-theme/breeze/README +5 -0
- dcscope/img/icon-theme/breeze/actions/16/application-exit.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/16/code-context.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/dialog-cancel.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/dialog-close.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/16/dialog-messages.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/dialog-ok-apply.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/dialog-ok.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/document-open-folder.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/document-open.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/document-save.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/documentinfo.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/16/draw-watercolor.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/edit-clear-all.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/edit-clear.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/edit-paste.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/globe.svg +16 -0
- dcscope/img/icon-theme/breeze/actions/16/gtk-preferences.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/list-add.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/messagebox_warning.svg +20 -0
- dcscope/img/icon-theme/breeze/actions/16/object-columns.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/object-order-lower.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/object-rows.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/office-chart-line-stacked.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/office-chart-ring.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/office-chart-scatter.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/path-mode-polyline.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/preferences-activities.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/remove.svg +10 -0
- dcscope/img/icon-theme/breeze/actions/16/search.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/show-grid.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/special_paste.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/tools-wizard.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/view-calendar-list.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/view-filter.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/view-list-tree.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/view-statistics.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/16/visibility.svg +21 -0
- dcscope/img/icon-theme/breeze/actions/22/application-exit.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/22/code-context.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/dialog-cancel.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/dialog-close.svg +10 -0
- dcscope/img/icon-theme/breeze/actions/22/dialog-messages.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/dialog-ok-apply.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/dialog-ok.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/document-open-folder.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/document-open.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/document-save.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/documentinfo.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/draw-watercolor.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/edit-clear-all.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/edit-clear.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/edit-paste.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/globe.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/22/gtk-preferences.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/22/list-add.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/messagebox_warning.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/object-columns.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/object-order-lower.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/object-rows.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/office-chart-line-stacked.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/office-chart-ring.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/office-chart-scatter.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/path-mode-polyline.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/22/preferences-activities.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/remove.svg +10 -0
- dcscope/img/icon-theme/breeze/actions/22/special_paste.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/tools-wizard.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/view-calendar-list.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/view-filter.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/view-list-tree.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/view-statistics.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/22/visibility.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/24/application-exit.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/code-context.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/dialog-cancel.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/dialog-close.svg +10 -0
- dcscope/img/icon-theme/breeze/actions/24/dialog-messages.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/dialog-ok-apply.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/dialog-ok.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/24/document-open-folder.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/document-open.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/document-save.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/documentinfo.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/24/draw-watercolor.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/edit-clear-all.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/24/edit-clear.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/edit-paste.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/globe.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/24/gtk-preferences.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/24/list-add.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/messagebox_warning.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/24/object-columns.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/object-order-lower.svg +15 -0
- dcscope/img/icon-theme/breeze/actions/24/object-rows.svg +15 -0
- dcscope/img/icon-theme/breeze/actions/24/office-chart-line-stacked.svg +15 -0
- dcscope/img/icon-theme/breeze/actions/24/office-chart-ring.svg +15 -0
- dcscope/img/icon-theme/breeze/actions/24/office-chart-scatter.svg +15 -0
- dcscope/img/icon-theme/breeze/actions/24/path-mode-polyline.svg +15 -0
- dcscope/img/icon-theme/breeze/actions/24/preferences-activities.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/24/remove.svg +10 -0
- dcscope/img/icon-theme/breeze/actions/24/special_paste.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/tools-wizard.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/view-calendar-list.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/view-filter.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/view-list-tree.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/view-statistics.svg +12 -0
- dcscope/img/icon-theme/breeze/actions/24/visibility.svg +14 -0
- dcscope/img/icon-theme/breeze/actions/32/application-exit.svg +8 -0
- dcscope/img/icon-theme/breeze/actions/32/dialog-cancel.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/32/dialog-messages.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/32/dialog-ok-apply.svg +1 -0
- dcscope/img/icon-theme/breeze/actions/32/dialog-ok.svg +1 -0
- dcscope/img/icon-theme/breeze/actions/32/document-open-folder.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/32/document-open.svg +13 -0
- dcscope/img/icon-theme/breeze/actions/32/document-save.svg +18 -0
- dcscope/img/icon-theme/breeze/actions/32/gtk-preferences.svg +17 -0
- dcscope/img/icon-theme/breeze/actions/32/object-order-lower.svg +18 -0
- dcscope/img/icon-theme/breeze/actions/32/view-filter.svg +18 -0
- dcscope/img/icon-theme/breeze/actions/32/view-list-tree.svg +18 -0
- dcscope/img/icon-theme/breeze/places/16/folder-cloud.svg +13 -0
- dcscope/img/icon-theme/breeze/places/16/folder.svg +13 -0
- dcscope/img/icon-theme/breeze/places/22/folder-cloud.svg +13 -0
- dcscope/img/icon-theme/breeze/places/22/folder.svg +13 -0
- dcscope/img/icon-theme/breeze/places/24/folder-cloud.svg +12 -0
- dcscope/img/icon-theme/breeze/places/24/folder.svg +12 -0
- dcscope/img/icon-theme/breeze/places/32/folder-cloud.svg +35 -0
- dcscope/img/icon-theme/breeze/places/32/folder.svg +30 -0
- dcscope/img/icon-theme/breeze/places/64/folder-cloud.svg +35 -0
- dcscope/img/icon-theme/breeze/places/64/folder.svg +30 -0
- dcscope/img/icon-theme/breeze/status/16/dialog-error.svg +9 -0
- dcscope/img/icon-theme/breeze/status/16/dialog-information.svg +9 -0
- dcscope/img/icon-theme/breeze/status/16/dialog-question.svg +7 -0
- dcscope/img/icon-theme/breeze/status/16/dialog-warning.svg +9 -0
- dcscope/img/icon-theme/breeze/status/22/dialog-error.svg +9 -0
- dcscope/img/icon-theme/breeze/status/22/dialog-information.svg +9 -0
- dcscope/img/icon-theme/breeze/status/22/dialog-question.svg +7 -0
- dcscope/img/icon-theme/breeze/status/22/dialog-warning.svg +9 -0
- dcscope/img/icon-theme/breeze/status/24/dialog-error.svg +13 -0
- dcscope/img/icon-theme/breeze/status/24/dialog-information.svg +13 -0
- dcscope/img/icon-theme/breeze/status/24/dialog-question.svg +9 -0
- dcscope/img/icon-theme/breeze/status/24/dialog-warning.svg +1 -0
- dcscope/img/icon-theme/breeze/status/64/dialog-error.svg +25 -0
- dcscope/img/icon-theme/breeze/status/64/dialog-information.svg +25 -0
- dcscope/img/icon-theme/breeze/status/64/dialog-question.svg +25 -0
- dcscope/img/icon-theme/breeze/status/64/dialog-warning.svg +25 -0
- dcscope/img/icon-theme/collect_icons.py +124 -0
- dcscope/img/icon-theme/dcscope/dcor.png +0 -0
- dcscope/img/icon-theme/dcscope/edit-copy-anew.svg +13 -0
- dcscope/img/icon-theme/dcscope/emodulus.svg +69 -0
- dcscope/img/icon-theme/dcscope/filter_ray.svg +54 -0
- dcscope/img/icon-theme/dcscope/region_channel.svg +86 -0
- dcscope/img/icon-theme/dcscope/region_reservoir.svg +89 -0
- dcscope/img/icon-theme/dcscope/rlang.svg +108 -0
- dcscope/img/icon-theme/dcscope/statistical_significance.svg +106 -0
- dcscope/img/icon-theme/index.theme +81 -0
- dcscope/img/icon.png +0 -0
- dcscope/img/icon.svg +73 -0
- dcscope/img/splash.png +0 -0
- dcscope/meta_tool.py +143 -0
- dcscope/pipeline/__init__.py +5 -0
- dcscope/pipeline/core.py +632 -0
- dcscope/pipeline/dataslot.py +270 -0
- dcscope/pipeline/filter.py +161 -0
- dcscope/pipeline/filter_ray.py +170 -0
- dcscope/pipeline/plot.py +187 -0
- dcscope/plot_cache.py +79 -0
- dcscope/session.py +379 -0
- dcscope/util.py +34 -0
- dcscope-2.22.1.dist-info/METADATA +95 -0
- dcscope-2.22.1.dist-info/RECORD +267 -0
- dcscope-2.22.1.dist-info/WHEEL +5 -0
- dcscope-2.22.1.dist-info/entry_points.txt +2 -0
- dcscope-2.22.1.dist-info/licenses/LICENSE +621 -0
- dcscope-2.22.1.dist-info/top_level.txt +1 -0
dcscope/pipeline/plot.py
ADDED
|
@@ -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
|