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,66 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<ui version="4.0">
|
|
3
|
+
<class>Form</class>
|
|
4
|
+
<widget class="QWidget" name="Form">
|
|
5
|
+
<property name="geometry">
|
|
6
|
+
<rect>
|
|
7
|
+
<x>0</x>
|
|
8
|
+
<y>0</y>
|
|
9
|
+
<width>739</width>
|
|
10
|
+
<height>641</height>
|
|
11
|
+
</rect>
|
|
12
|
+
</property>
|
|
13
|
+
<property name="sizePolicy">
|
|
14
|
+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
15
|
+
<horstretch>0</horstretch>
|
|
16
|
+
<verstretch>0</verstretch>
|
|
17
|
+
</sizepolicy>
|
|
18
|
+
</property>
|
|
19
|
+
<property name="windowTitle">
|
|
20
|
+
<string>Form</string>
|
|
21
|
+
</property>
|
|
22
|
+
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
23
|
+
<property name="leftMargin">
|
|
24
|
+
<number>0</number>
|
|
25
|
+
</property>
|
|
26
|
+
<property name="topMargin">
|
|
27
|
+
<number>0</number>
|
|
28
|
+
</property>
|
|
29
|
+
<property name="rightMargin">
|
|
30
|
+
<number>0</number>
|
|
31
|
+
</property>
|
|
32
|
+
<property name="bottomMargin">
|
|
33
|
+
<number>0</number>
|
|
34
|
+
</property>
|
|
35
|
+
<item>
|
|
36
|
+
<widget class="GraphicsLayoutWidget" name="plot_layout" native="true">
|
|
37
|
+
<property name="sizePolicy">
|
|
38
|
+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
39
|
+
<horstretch>0</horstretch>
|
|
40
|
+
<verstretch>0</verstretch>
|
|
41
|
+
</sizepolicy>
|
|
42
|
+
</property>
|
|
43
|
+
<property name="minimumSize">
|
|
44
|
+
<size>
|
|
45
|
+
<width>200</width>
|
|
46
|
+
<height>100</height>
|
|
47
|
+
</size>
|
|
48
|
+
</property>
|
|
49
|
+
<property name="styleSheet">
|
|
50
|
+
<string notr="true">background-color:#FFF</string>
|
|
51
|
+
</property>
|
|
52
|
+
</widget>
|
|
53
|
+
</item>
|
|
54
|
+
</layout>
|
|
55
|
+
</widget>
|
|
56
|
+
<customwidgets>
|
|
57
|
+
<customwidget>
|
|
58
|
+
<class>GraphicsLayoutWidget</class>
|
|
59
|
+
<extends>QWidget</extends>
|
|
60
|
+
<header>pyqtgraph</header>
|
|
61
|
+
<container>1</container>
|
|
62
|
+
</customwidget>
|
|
63
|
+
</customwidgets>
|
|
64
|
+
<resources/>
|
|
65
|
+
<connections/>
|
|
66
|
+
</ui>
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import os.path as os_path
|
|
2
|
+
import pathlib
|
|
3
|
+
import traceback
|
|
4
|
+
|
|
5
|
+
import importlib.resources
|
|
6
|
+
import platform
|
|
7
|
+
|
|
8
|
+
from dclab.rtdc_dataset.fmt_dcor import access_token
|
|
9
|
+
from dclab.lme4 import rsetup
|
|
10
|
+
from PyQt6 import uic, QtCore, QtWidgets
|
|
11
|
+
from PyQt6.QtCore import QStandardPaths
|
|
12
|
+
|
|
13
|
+
from .widgets import show_wait_cursor
|
|
14
|
+
from ..extensions import ExtensionManager, SUPPORTED_FORMATS
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExtensionErrorWrapper:
|
|
18
|
+
def __init__(self, ehash):
|
|
19
|
+
self.ehash = ehash
|
|
20
|
+
|
|
21
|
+
def __enter__(self):
|
|
22
|
+
return self
|
|
23
|
+
|
|
24
|
+
def __exit__(self, exc_type, exc_val, trc):
|
|
25
|
+
if exc_type is not None:
|
|
26
|
+
QtWidgets.QMessageBox.warning(
|
|
27
|
+
None,
|
|
28
|
+
f"Loading extension {self.ehash} failed!",
|
|
29
|
+
f"It was not possible to load the extension {self.ehash}! "
|
|
30
|
+
+ "You might have to install additional software:\n\n"
|
|
31
|
+
+ traceback.format_exc(),
|
|
32
|
+
)
|
|
33
|
+
return True # do not raise the exception
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Preferences(QtWidgets.QDialog):
|
|
37
|
+
"""Preferences dialog to interact with QSettings"""
|
|
38
|
+
instances = {}
|
|
39
|
+
feature_changed = QtCore.pyqtSignal()
|
|
40
|
+
|
|
41
|
+
def __init__(self, parent, *args, **kwargs):
|
|
42
|
+
super(Preferences, self).__init__(parent=parent, *args, **kwargs)
|
|
43
|
+
ref = importlib.resources.files("dcscope.gui") / "preferences.ui"
|
|
44
|
+
with importlib.resources.as_file(ref) as path_ui:
|
|
45
|
+
uic.loadUi(path_ui, self)
|
|
46
|
+
self.settings = QtCore.QSettings()
|
|
47
|
+
self.parent = parent
|
|
48
|
+
|
|
49
|
+
# Get default R path
|
|
50
|
+
if rsetup.has_r():
|
|
51
|
+
rdefault = str(rsetup.get_r_path())
|
|
52
|
+
else:
|
|
53
|
+
rdefault = ""
|
|
54
|
+
|
|
55
|
+
#: configuration keys, corresponding widgets, and defaults
|
|
56
|
+
self.config_pairs = [
|
|
57
|
+
["advanced/developer mode", self.advanced_developer_mode, "0"],
|
|
58
|
+
["check for updates", self.general_check_for_updates, "1"],
|
|
59
|
+
["dcor/api key", self.dcor_api_key, ""],
|
|
60
|
+
["dcor/servers", self.dcor_servers, ["dcor.mpl.mpg.de"]],
|
|
61
|
+
["dcor/use ssl", self.dcor_use_ssl, "1"],
|
|
62
|
+
["lme4/r path", self.lme4_rpath, rdefault],
|
|
63
|
+
["s3/endpoint url", self.lineEdit_s3_endpoint_url, ""],
|
|
64
|
+
["s3/access key id", self.lineEdit_s3_access_key_id, ""],
|
|
65
|
+
["s3/secret access key", self.lineEdit_s3_secret_access_key, ""],
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# extensions
|
|
69
|
+
store_path = os_path.join(
|
|
70
|
+
QStandardPaths.writableLocation(
|
|
71
|
+
QStandardPaths.StandardLocation.AppDataLocation), "extensions")
|
|
72
|
+
self.extensions = ExtensionManager(store_path)
|
|
73
|
+
|
|
74
|
+
self.tabWidget.setCurrentIndex(0)
|
|
75
|
+
self.reload()
|
|
76
|
+
|
|
77
|
+
# signals
|
|
78
|
+
self.btn_apply = self.buttonBox.button(
|
|
79
|
+
QtWidgets.QDialogButtonBox.StandardButton.Apply)
|
|
80
|
+
self.btn_apply.clicked.connect(self.on_settings_apply)
|
|
81
|
+
self.btn_cancel = self.buttonBox.button(
|
|
82
|
+
QtWidgets.QDialogButtonBox.StandardButton.Cancel)
|
|
83
|
+
self.btn_ok = self.buttonBox.button(
|
|
84
|
+
QtWidgets.QDialogButtonBox.StandardButton.Ok)
|
|
85
|
+
self.btn_ok.clicked.connect(self.on_settings_apply)
|
|
86
|
+
self.btn_restore = self.buttonBox.button(
|
|
87
|
+
QtWidgets.QDialogButtonBox.StandardButton.RestoreDefaults)
|
|
88
|
+
self.btn_restore.clicked.connect(self.on_settings_restore)
|
|
89
|
+
# DCOR
|
|
90
|
+
self.pushButton_enc_token.clicked.connect(self.on_dcor_enc_token)
|
|
91
|
+
# extension buttons
|
|
92
|
+
self.checkBox_ext_enabled.clicked.connect(self.on_ext_enabled)
|
|
93
|
+
self.pushButton_ext_load.clicked.connect(self.on_ext_load)
|
|
94
|
+
self.pushButton_ext_remove.clicked.connect(self.on_ext_remove)
|
|
95
|
+
self.listWidget_ext.currentRowChanged.connect(self.on_ext_selected)
|
|
96
|
+
self.listWidget_ext.itemChanged.connect(self.on_ext_modified)
|
|
97
|
+
# lme4 buttons
|
|
98
|
+
self.pushButton_lme4_install.clicked.connect(self.on_lme4_install)
|
|
99
|
+
self.pushButton_lme4_search.clicked.connect(self.on_lme4_search_r)
|
|
100
|
+
# tab changed
|
|
101
|
+
self.tabWidget.currentChanged.connect(self.on_tab_changed)
|
|
102
|
+
|
|
103
|
+
def reload(self):
|
|
104
|
+
"""Read configuration or set default parameters"""
|
|
105
|
+
for key, widget, default in self.config_pairs:
|
|
106
|
+
value = self.settings.value(key, default)
|
|
107
|
+
value = value or default
|
|
108
|
+
if isinstance(widget, QtWidgets.QCheckBox):
|
|
109
|
+
widget.setChecked(bool(int(value)))
|
|
110
|
+
elif isinstance(widget, QtWidgets.QLineEdit):
|
|
111
|
+
widget.setText(value)
|
|
112
|
+
elif widget is self.dcor_servers:
|
|
113
|
+
self.dcor_servers.clear()
|
|
114
|
+
self.dcor_servers.addItems(value)
|
|
115
|
+
self.dcor_servers.setCurrentIndex(0)
|
|
116
|
+
else:
|
|
117
|
+
raise NotImplementedError("No rule for '{}'".format(key))
|
|
118
|
+
|
|
119
|
+
# peculiarities of developer mode
|
|
120
|
+
devmode = bool(int(self.settings.value("advanced/developer mode", 0)))
|
|
121
|
+
self.dcor_use_ssl.setVisible(devmode) # show "use ssl" in dev mode
|
|
122
|
+
|
|
123
|
+
if self.tabWidget.currentWidget() is self.tab_r:
|
|
124
|
+
self.reload_lme4()
|
|
125
|
+
|
|
126
|
+
self.reload_ext()
|
|
127
|
+
|
|
128
|
+
def reload_ext(self):
|
|
129
|
+
"""Reload the list of extensions"""
|
|
130
|
+
# extensions
|
|
131
|
+
row = self.listWidget_ext.currentRow()
|
|
132
|
+
self.listWidget_ext.blockSignals(True)
|
|
133
|
+
self.listWidget_ext.clear()
|
|
134
|
+
have_extensions = bool(self.extensions)
|
|
135
|
+
self.widget_ext_controls.setVisible(have_extensions)
|
|
136
|
+
if have_extensions:
|
|
137
|
+
for ii, ext in enumerate(self.extensions):
|
|
138
|
+
lwitem = QtWidgets.QListWidgetItem(ext.title,
|
|
139
|
+
self.listWidget_ext)
|
|
140
|
+
lwitem.setFlags(QtCore.Qt.ItemFlag.ItemIsEditable
|
|
141
|
+
| QtCore.Qt.ItemFlag.ItemIsSelectable
|
|
142
|
+
| QtCore.Qt.ItemFlag.ItemIsEnabled
|
|
143
|
+
| QtCore.Qt.ItemFlag.ItemIsUserCheckable)
|
|
144
|
+
lwitem.setCheckState(QtCore.Qt.CheckState.Checked
|
|
145
|
+
if ext.enabled
|
|
146
|
+
else QtCore.Qt.CheckState.UnChecked)
|
|
147
|
+
lwitem.setData(100, ext.hash)
|
|
148
|
+
self.listWidget_ext.setCurrentRow(0)
|
|
149
|
+
if row + 1 > self.listWidget_ext.count() or row < 0:
|
|
150
|
+
row = 0
|
|
151
|
+
self.listWidget_ext.setCurrentRow(row)
|
|
152
|
+
self.listWidget_ext.blockSignals(False)
|
|
153
|
+
self.on_ext_selected()
|
|
154
|
+
|
|
155
|
+
@show_wait_cursor
|
|
156
|
+
def reload_lme4(self, install=False):
|
|
157
|
+
"""Reload information about lme4, optionally installing it"""
|
|
158
|
+
# Before we do anything, we have to find a persistent writable
|
|
159
|
+
# location where we can install lme4 and set the environment variable
|
|
160
|
+
# R_LIBS_USER accordingly.
|
|
161
|
+
r_libs_user = self.settings.value("lme4/r libs user", None)
|
|
162
|
+
if r_libs_user is None or not pathlib.Path(r_libs_user).exists():
|
|
163
|
+
r_libs_user = pathlib.Path(
|
|
164
|
+
QStandardPaths.writableLocation(
|
|
165
|
+
QStandardPaths.StandardLocation.AppLocalDataLocation)
|
|
166
|
+
) / "r-libs"
|
|
167
|
+
r_libs_user.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
r_libs_user = str(r_libs_user)
|
|
169
|
+
self.settings.setValue("lme4/r libs user", r_libs_user)
|
|
170
|
+
rsetup.set_r_lib_path(r_libs_user)
|
|
171
|
+
|
|
172
|
+
# set the binary
|
|
173
|
+
binary = self.lme4_rpath.text()
|
|
174
|
+
if pathlib.Path(binary).is_file():
|
|
175
|
+
rsetup.set_r_path(binary)
|
|
176
|
+
|
|
177
|
+
# enable/disable lme4-install button
|
|
178
|
+
self.pushButton_lme4_install.setEnabled(rsetup.has_r())
|
|
179
|
+
|
|
180
|
+
# check lme4 package status
|
|
181
|
+
if not rsetup.has_r():
|
|
182
|
+
r_version = "unknown"
|
|
183
|
+
lme4_st = "unknown"
|
|
184
|
+
else:
|
|
185
|
+
r_version = rsetup.get_r_version()
|
|
186
|
+
if rsetup.has_lme4():
|
|
187
|
+
lme4_st = "installed"
|
|
188
|
+
else:
|
|
189
|
+
lme4_st = "not installed"
|
|
190
|
+
|
|
191
|
+
if install and lme4_st == "not installed":
|
|
192
|
+
self.setEnabled(False)
|
|
193
|
+
rsetup.require_lme4()
|
|
194
|
+
self.setEnabled(True)
|
|
195
|
+
# update interface with installed lme4
|
|
196
|
+
self.reload_lme4(install=False)
|
|
197
|
+
else:
|
|
198
|
+
# update user interface
|
|
199
|
+
self.pushButton_lme4_install.setVisible(lme4_st == "not installed")
|
|
200
|
+
self.label_r_version.setText(r_version)
|
|
201
|
+
self.label_lme4_installed.setText(lme4_st)
|
|
202
|
+
|
|
203
|
+
@QtCore.pyqtSlot()
|
|
204
|
+
def on_dcor_enc_token(self):
|
|
205
|
+
"""Load an encrypted DCOR access token and store the certificate"""
|
|
206
|
+
# get path
|
|
207
|
+
path, _ = QtWidgets.QFileDialog.getOpenFileName(
|
|
208
|
+
parent=self,
|
|
209
|
+
caption="SSpecify encrypted DCOR access token",
|
|
210
|
+
directory=self.settings.value("paths/encrypted access token", ""),
|
|
211
|
+
filter="DCOR access token (*.dcor-access)")
|
|
212
|
+
if not path:
|
|
213
|
+
return
|
|
214
|
+
path = pathlib.Path(path)
|
|
215
|
+
self.settings.setValue("paths/encrypted access token",
|
|
216
|
+
str(path.parent))
|
|
217
|
+
# get password
|
|
218
|
+
pwd, cont = QtWidgets.QInputDialog.getText(
|
|
219
|
+
self,
|
|
220
|
+
"Password required",
|
|
221
|
+
f"Please enter the encryption password for {path.name}!",
|
|
222
|
+
QtWidgets.QLineEdit.EchoMode.Password)
|
|
223
|
+
pwd = pwd.strip()
|
|
224
|
+
if not pwd or not cont:
|
|
225
|
+
return
|
|
226
|
+
# get info
|
|
227
|
+
host = access_token.get_hostname(path, pwd)
|
|
228
|
+
api_key = access_token.get_api_key(path, pwd)
|
|
229
|
+
cert = access_token.get_certificate(path, pwd)
|
|
230
|
+
# write certificate to our global DCscope certs directory
|
|
231
|
+
ca_path = pathlib.Path(
|
|
232
|
+
QStandardPaths.writableLocation(
|
|
233
|
+
QStandardPaths.StandardLocation.AppDataLocation)
|
|
234
|
+
) / "certificates"
|
|
235
|
+
(ca_path / f"{host}.cert").write_bytes(cert)
|
|
236
|
+
# store other metadata
|
|
237
|
+
self.settings.setValue("dcor/api key", api_key)
|
|
238
|
+
|
|
239
|
+
servers = self.settings.value("dcor/servers", ["dcor.mpl.mpg.de"])
|
|
240
|
+
if host in servers:
|
|
241
|
+
servers.remove(host)
|
|
242
|
+
servers.insert(0, host.strip("/"))
|
|
243
|
+
self.settings.setValue("dcor/servers", servers)
|
|
244
|
+
self.reload()
|
|
245
|
+
|
|
246
|
+
@QtCore.pyqtSlot(bool)
|
|
247
|
+
def on_ext_enabled(self, enabled):
|
|
248
|
+
"""Enable or disable an extension (signal from checkbox widget)"""
|
|
249
|
+
item = self.listWidget_ext.currentItem()
|
|
250
|
+
ehash = item.data(100)
|
|
251
|
+
with ExtensionErrorWrapper(ehash):
|
|
252
|
+
self.extensions.extension_set_enabled(ehash, enabled)
|
|
253
|
+
self.reload_ext()
|
|
254
|
+
self.feature_changed.emit()
|
|
255
|
+
|
|
256
|
+
@QtCore.pyqtSlot()
|
|
257
|
+
def on_ext_load(self):
|
|
258
|
+
"""Load an extension from the file system"""
|
|
259
|
+
format_string = " ".join(f"*{su}" for su in SUPPORTED_FORMATS)
|
|
260
|
+
paths, _ = QtWidgets.QFileDialog.getOpenFileNames(
|
|
261
|
+
parent=self,
|
|
262
|
+
caption="Select an extension file",
|
|
263
|
+
directory=self.settings.value("paths/extension", ""),
|
|
264
|
+
filter=f"Supported extension files ({format_string})")
|
|
265
|
+
if paths:
|
|
266
|
+
for pp in paths:
|
|
267
|
+
with ExtensionErrorWrapper(pp):
|
|
268
|
+
self.extensions.import_extension_from_path(pp)
|
|
269
|
+
self.reload_ext()
|
|
270
|
+
self.feature_changed.emit()
|
|
271
|
+
|
|
272
|
+
@QtCore.pyqtSlot()
|
|
273
|
+
def on_ext_remove(self):
|
|
274
|
+
"""Remove an extension"""
|
|
275
|
+
ehash = self.listWidget_ext.currentItem().data(100)
|
|
276
|
+
self.extensions.extension_remove(ehash)
|
|
277
|
+
self.reload_ext()
|
|
278
|
+
self.feature_changed.emit()
|
|
279
|
+
|
|
280
|
+
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
|
|
281
|
+
def on_ext_modified(self, item):
|
|
282
|
+
"""Enable or disable an extension (signal from listWidget)"""
|
|
283
|
+
ehash = item.data(100)
|
|
284
|
+
enabled = bool(item.checkState())
|
|
285
|
+
with ExtensionErrorWrapper(ehash):
|
|
286
|
+
self.extensions.extension_set_enabled(ehash, enabled)
|
|
287
|
+
self.listWidget_ext.setCurrentItem(item)
|
|
288
|
+
self.reload_ext()
|
|
289
|
+
self.feature_changed.emit()
|
|
290
|
+
|
|
291
|
+
@QtCore.pyqtSlot()
|
|
292
|
+
def on_ext_selected(self):
|
|
293
|
+
"""Display details for an extension (signal from listWidget)"""
|
|
294
|
+
item = self.listWidget_ext.currentItem()
|
|
295
|
+
if item is not None:
|
|
296
|
+
ehash = item.data(100)
|
|
297
|
+
ext = self.extensions[ehash]
|
|
298
|
+
self.checkBox_ext_enabled.blockSignals(True)
|
|
299
|
+
self.checkBox_ext_enabled.setChecked(ext.enabled)
|
|
300
|
+
self.checkBox_ext_enabled.blockSignals(False)
|
|
301
|
+
with ExtensionErrorWrapper(ehash):
|
|
302
|
+
if ext.enabled: # only load the extension if enabled
|
|
303
|
+
ext.load()
|
|
304
|
+
self.label_ext_name.setText(ext.title)
|
|
305
|
+
item.setText(ext.title)
|
|
306
|
+
self.label_ext_description.setText(ext.description)
|
|
307
|
+
|
|
308
|
+
@QtCore.pyqtSlot()
|
|
309
|
+
def on_lme4_install(self):
|
|
310
|
+
self.reload_lme4(install=True)
|
|
311
|
+
|
|
312
|
+
@QtCore.pyqtSlot()
|
|
313
|
+
def on_lme4_search_r(self):
|
|
314
|
+
if platform.system() == "Windows":
|
|
315
|
+
filters = "R executable (R.exe)"
|
|
316
|
+
else:
|
|
317
|
+
filters = "R executable (R)"
|
|
318
|
+
path, _ = QtWidgets.QFileDialog.getOpenFileName(
|
|
319
|
+
self, "Executable", ".", filters)
|
|
320
|
+
if path:
|
|
321
|
+
self.lme4_rpath.setText(path)
|
|
322
|
+
|
|
323
|
+
@QtCore.pyqtSlot()
|
|
324
|
+
def on_settings_apply(self):
|
|
325
|
+
"""Save current changes made in UI to settings and reload UI"""
|
|
326
|
+
for key, widget, default in self.config_pairs:
|
|
327
|
+
if isinstance(widget, QtWidgets.QCheckBox):
|
|
328
|
+
value = int(widget.isChecked())
|
|
329
|
+
if key == "advanced/developer mode":
|
|
330
|
+
devmode = int(self.settings.value(
|
|
331
|
+
"advanced/developer mode", 0))
|
|
332
|
+
if devmode != value:
|
|
333
|
+
msg = QtWidgets.QMessageBox()
|
|
334
|
+
msg.setIcon(QtWidgets.QMessageBox.Icon.Information)
|
|
335
|
+
msg.setText("Please restart DCscope for the changes "
|
|
336
|
+
+ "to take effect.")
|
|
337
|
+
msg.setWindowTitle("Restart DCscope")
|
|
338
|
+
msg.exec()
|
|
339
|
+
elif isinstance(widget, QtWidgets.QLineEdit):
|
|
340
|
+
value = widget.text().strip()
|
|
341
|
+
elif widget is self.dcor_servers:
|
|
342
|
+
curtext = self.dcor_servers.currentText()
|
|
343
|
+
items = self.settings.value(key, default)
|
|
344
|
+
if curtext in items:
|
|
345
|
+
# We do it again below to be on the safe side
|
|
346
|
+
items.remove(curtext)
|
|
347
|
+
for bad_start in ["https://", "http://"]:
|
|
348
|
+
if curtext.startswith(bad_start):
|
|
349
|
+
curtext = curtext[len(bad_start):]
|
|
350
|
+
if curtext in items:
|
|
351
|
+
# We do it again with the stripped version
|
|
352
|
+
items.remove(curtext)
|
|
353
|
+
items.insert(0, curtext)
|
|
354
|
+
value = items
|
|
355
|
+
else:
|
|
356
|
+
raise NotImplementedError("No rule for '{}'".format(key))
|
|
357
|
+
self.settings.setValue(key, value)
|
|
358
|
+
|
|
359
|
+
# reload UI to give visual feedback
|
|
360
|
+
self.reload()
|
|
361
|
+
|
|
362
|
+
@QtCore.pyqtSlot()
|
|
363
|
+
def on_settings_restore(self):
|
|
364
|
+
self.settings.clear()
|
|
365
|
+
self.reload()
|
|
366
|
+
|
|
367
|
+
@QtCore.pyqtSlot()
|
|
368
|
+
def on_tab_changed(self):
|
|
369
|
+
if self.tabWidget.currentWidget() is self.tab_extensions:
|
|
370
|
+
# Managing extensions has nothing to do with other settings.
|
|
371
|
+
enabled = False
|
|
372
|
+
else:
|
|
373
|
+
enabled = True
|
|
374
|
+
|
|
375
|
+
if self.tabWidget.currentWidget() is self.tab_r:
|
|
376
|
+
self.reload_lme4()
|
|
377
|
+
|
|
378
|
+
self.btn_apply.setEnabled(enabled)
|
|
379
|
+
self.btn_cancel.setEnabled(enabled)
|
|
380
|
+
self.btn_restore.setEnabled(enabled)
|