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
|
@@ -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
|