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,284 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from PyQt6 import QtCore
|
|
3
|
+
import pyqtgraph as pg
|
|
4
|
+
from pyqtgraph.graphicsItems.GradientEditorItem import Gradients
|
|
5
|
+
|
|
6
|
+
from ... import plot_cache
|
|
7
|
+
|
|
8
|
+
from .. import pipeline_plot
|
|
9
|
+
from ..widgets import SimplePlotWidget, SimpleViewBox
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QuickViewScatterWidget(SimplePlotWidget):
|
|
13
|
+
update_hover_pos = QtCore.pyqtSignal(QtCore.QPointF)
|
|
14
|
+
|
|
15
|
+
def __init__(self, *args, **kwargs):
|
|
16
|
+
self._view_box = QuickViewViewBox()
|
|
17
|
+
super(QuickViewScatterWidget, self).__init__(viewBox=self._view_box,
|
|
18
|
+
*args, **kwargs)
|
|
19
|
+
self._view_box.update_hover_pos.connect(self.update_hover_pos)
|
|
20
|
+
self.scatter = RTDCScatterPlot()
|
|
21
|
+
self.select = pg.PlotDataItem(x=[1], y=[2], symbol="o")
|
|
22
|
+
#: List of isoelasticity line plots
|
|
23
|
+
self.isoelastics = []
|
|
24
|
+
self.addItem(self.scatter)
|
|
25
|
+
self.addItem(self.select)
|
|
26
|
+
self.select.hide()
|
|
27
|
+
self.xscale = "linear"
|
|
28
|
+
self.yscale = "linear"
|
|
29
|
+
self.kde_type = "none",
|
|
30
|
+
self.kde_kwargs = {}
|
|
31
|
+
self.hue_type = "none"
|
|
32
|
+
self.hue_kwargs = {}
|
|
33
|
+
#: Boolean array identifying the plotted events w.r.t. the full
|
|
34
|
+
#: dataset
|
|
35
|
+
self.events_plotted = None
|
|
36
|
+
#: Unfiltered and not-downsampled x component of current scatter plot
|
|
37
|
+
self.data_x = None
|
|
38
|
+
#: Unfiltered and not-downsampled y component of current scatter plot
|
|
39
|
+
self.data_y = None
|
|
40
|
+
|
|
41
|
+
# polygon editing ROI
|
|
42
|
+
self.poly_line_roi = None
|
|
43
|
+
|
|
44
|
+
# Signals for mouse click
|
|
45
|
+
# let view box update the selected event in the scatter plot
|
|
46
|
+
self._view_box.set_scatter_point.connect(self.scatter.set_point)
|
|
47
|
+
# let view box update self.poly_line_roi
|
|
48
|
+
self._view_box.add_poly_vertex.connect(self.add_poly_vertex)
|
|
49
|
+
|
|
50
|
+
def activate_poly_mode(self, points=None):
|
|
51
|
+
if points is None:
|
|
52
|
+
points = []
|
|
53
|
+
if self.poly_line_roi is None:
|
|
54
|
+
self.poly_line_roi = pg.PolyLineROI([], closed=True)
|
|
55
|
+
self.poly_line_roi.setPen("k")
|
|
56
|
+
self.addItem(self.poly_line_roi)
|
|
57
|
+
if len(points):
|
|
58
|
+
mode = "poly-modify"
|
|
59
|
+
self.set_poly_points(points)
|
|
60
|
+
else:
|
|
61
|
+
mode = "poly-create"
|
|
62
|
+
self.set_mouse_click_mode(mode)
|
|
63
|
+
return self.poly_line_roi
|
|
64
|
+
|
|
65
|
+
def activate_scatter_mode(self):
|
|
66
|
+
if self.poly_line_roi is not None:
|
|
67
|
+
self.removeItem(self.poly_line_roi)
|
|
68
|
+
self.poly_line_roi = None
|
|
69
|
+
self.set_mouse_click_mode("scatter")
|
|
70
|
+
|
|
71
|
+
def add_poly_vertex(self, pos):
|
|
72
|
+
state = self.poly_line_roi.getState()
|
|
73
|
+
state["points"].append([pos.x(), pos.y()])
|
|
74
|
+
self.poly_line_roi.setState(state)
|
|
75
|
+
|
|
76
|
+
def get_poly_points(self):
|
|
77
|
+
if self.poly_line_roi is None:
|
|
78
|
+
raise ValueError("No polygon selection active!")
|
|
79
|
+
state = self.poly_line_roi.getState()
|
|
80
|
+
points = np.array([[p.x(), p.y()] for p in state["points"]])
|
|
81
|
+
# Take into account manual movements of the entire ROI (issue #115)
|
|
82
|
+
points[:, 0] += state["pos"][0]
|
|
83
|
+
points[:, 1] += state["pos"][1]
|
|
84
|
+
if self.xscale == "log":
|
|
85
|
+
points[:, 0] = 10**points[:, 0]
|
|
86
|
+
if self.yscale == "log":
|
|
87
|
+
points[:, 1] = 10**points[:, 1]
|
|
88
|
+
return points
|
|
89
|
+
|
|
90
|
+
def plot_data(self, rtdc_ds, slot, xax="area_um", yax="deform",
|
|
91
|
+
xscale="linear", yscale="linear", downsample=False,
|
|
92
|
+
hue_type="none", hue_kwargs=None, isoelastics=False,
|
|
93
|
+
lut_identifier=None):
|
|
94
|
+
self.rtdc_ds = rtdc_ds
|
|
95
|
+
self.slot = slot
|
|
96
|
+
self.xax = xax
|
|
97
|
+
self.yax = yax
|
|
98
|
+
self.xscale = xscale
|
|
99
|
+
self.yscale = yscale
|
|
100
|
+
self.hue_type = hue_type
|
|
101
|
+
self.hue_kwargs = hue_kwargs if hue_kwargs else {}
|
|
102
|
+
if hue_type != "kde":
|
|
103
|
+
self.kde_type = "none"
|
|
104
|
+
self.kde_kwargs = {}
|
|
105
|
+
else:
|
|
106
|
+
self.kde_type = hue_kwargs["kde_type"]
|
|
107
|
+
self.kde_kwargs = hue_kwargs.get("kde_kwargs")
|
|
108
|
+
x, y, kde, idx = plot_cache.get_scatter_data(
|
|
109
|
+
rtdc_ds=self.rtdc_ds,
|
|
110
|
+
downsample=downsample,
|
|
111
|
+
xax=self.xax,
|
|
112
|
+
yax=self.yax,
|
|
113
|
+
xscale=self.xscale,
|
|
114
|
+
yscale=self.yscale,
|
|
115
|
+
kde_type=self.kde_type,
|
|
116
|
+
kde_kwargs=self.kde_kwargs)
|
|
117
|
+
self.events_plotted = idx
|
|
118
|
+
#: unfiltered x data
|
|
119
|
+
self.data_x = self.rtdc_ds[self.xax]
|
|
120
|
+
#: unfiltered y data
|
|
121
|
+
self.data_y = self.rtdc_ds[self.yax]
|
|
122
|
+
if self.hue_type == "none":
|
|
123
|
+
brush = "k"
|
|
124
|
+
else:
|
|
125
|
+
# define colormap
|
|
126
|
+
brush = []
|
|
127
|
+
cmap = pg.ColorMap(*zip(*Gradients["viridis"]["ticks"]))
|
|
128
|
+
if self.hue_type == "kde":
|
|
129
|
+
for k in kde:
|
|
130
|
+
brush.append(cmap.mapToQColor(k))
|
|
131
|
+
elif self.hue_type == "feature":
|
|
132
|
+
fdata = self.rtdc_ds[self.hue_kwargs["feat"]][idx]
|
|
133
|
+
fdata -= fdata.min()
|
|
134
|
+
fdata = np.array(fdata, dtype=float) # cast int to float
|
|
135
|
+
fdata /= fdata.max()
|
|
136
|
+
for f in fdata:
|
|
137
|
+
brush.append(cmap.mapToQColor(f))
|
|
138
|
+
|
|
139
|
+
if x.size: # test for empty x/y (#37)
|
|
140
|
+
# set viewbox
|
|
141
|
+
pipeline_plot.set_viewbox(plot=self,
|
|
142
|
+
range_x=(x.min(), x.max()),
|
|
143
|
+
range_y=(y.min(), y.max()),
|
|
144
|
+
scale_x=self.xscale,
|
|
145
|
+
scale_y=self.yscale,
|
|
146
|
+
padding=0.05)
|
|
147
|
+
# set data
|
|
148
|
+
self.setData(x, y, brush=brush)
|
|
149
|
+
# set axes labels (replace with user-defined flourescence names)
|
|
150
|
+
left = pipeline_plot.get_axis_label_from_feature(
|
|
151
|
+
self.yax, slot_state=self.slot.__getstate__())
|
|
152
|
+
bottom = pipeline_plot.get_axis_label_from_feature(
|
|
153
|
+
self.xax, slot_state=self.slot.__getstate__())
|
|
154
|
+
self.plotItem.setLabels(left=left, bottom=bottom)
|
|
155
|
+
|
|
156
|
+
# Force updating the plot item size, otherwise axes labels
|
|
157
|
+
# may have an offset.
|
|
158
|
+
s = self.plotItem.size()
|
|
159
|
+
self.plotItem.resize(s.width()+1, s.height())
|
|
160
|
+
self.plotItem.resize(s)
|
|
161
|
+
|
|
162
|
+
# Isoelastics
|
|
163
|
+
# remove old isoelastics
|
|
164
|
+
for lp in self.isoelastics:
|
|
165
|
+
self.removeItem(lp)
|
|
166
|
+
if isoelastics:
|
|
167
|
+
cfg = self.rtdc_ds.config
|
|
168
|
+
self.isoelastics = pipeline_plot.add_isoelastics(
|
|
169
|
+
plot_item=self.plotItem,
|
|
170
|
+
axis_x=self.xax,
|
|
171
|
+
axis_y=self.yax,
|
|
172
|
+
channel_width=cfg["setup"]["channel width"],
|
|
173
|
+
pixel_size=cfg["imaging"]["pixel size"],
|
|
174
|
+
lut_identifier=lut_identifier)
|
|
175
|
+
|
|
176
|
+
def set_mouse_click_mode(self, mode):
|
|
177
|
+
allowed = ["scatter", "poly-create", "poly-modify"]
|
|
178
|
+
if mode not in allowed:
|
|
179
|
+
raise ValueError("Invalid mouse mode: {}, ".format(mode)
|
|
180
|
+
+ "expected one of {}".format(allowed))
|
|
181
|
+
if mode in ["poly-create", "poly-modify"]:
|
|
182
|
+
if self.poly_line_roi is None:
|
|
183
|
+
raise ValueError("Please set self.poly_line_roi before "
|
|
184
|
+
+ "setting the click mode!")
|
|
185
|
+
self._view_box.mode = mode
|
|
186
|
+
|
|
187
|
+
def set_poly_points(self, points):
|
|
188
|
+
if self.poly_line_roi is None:
|
|
189
|
+
raise ValueError("No polygon selection active!")
|
|
190
|
+
points = np.array(points, copy=True)
|
|
191
|
+
if points.size:
|
|
192
|
+
if self.xscale == "log":
|
|
193
|
+
points[:, 0] = np.log10(points[:, 0])
|
|
194
|
+
if self.yscale == "log":
|
|
195
|
+
points[:, 1] = np.log10(points[:, 1])
|
|
196
|
+
state = self.poly_line_roi.getState()
|
|
197
|
+
state["points"] = points.tolist()
|
|
198
|
+
self.poly_line_roi.setState(state)
|
|
199
|
+
|
|
200
|
+
def setData(self, x, y, **kwargs):
|
|
201
|
+
# convert to log-scale if applicable
|
|
202
|
+
if self.xscale == "log":
|
|
203
|
+
x = np.log10(x)
|
|
204
|
+
if self.yscale == "log":
|
|
205
|
+
y = np.log10(y)
|
|
206
|
+
# set data
|
|
207
|
+
self.scatter.setData(x=x, y=y, **kwargs)
|
|
208
|
+
|
|
209
|
+
def setSelection(self, event_index):
|
|
210
|
+
x = self.data_x[event_index]
|
|
211
|
+
y = self.data_y[event_index]
|
|
212
|
+
self.select.setData([x], [y])
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class QuickViewViewBox(SimpleViewBox):
|
|
216
|
+
set_scatter_point = QtCore.pyqtSignal(QtCore.QPointF)
|
|
217
|
+
add_poly_vertex = QtCore.pyqtSignal(QtCore.QPointF)
|
|
218
|
+
update_hover_pos = QtCore.pyqtSignal(QtCore.QPointF)
|
|
219
|
+
|
|
220
|
+
def __init__(self, *args, **kwargs):
|
|
221
|
+
super(QuickViewViewBox, self).__init__(*args, **kwargs)
|
|
222
|
+
self.mode = "scatter"
|
|
223
|
+
|
|
224
|
+
#: allowed right-click menu options with new name
|
|
225
|
+
self.right_click_actions["View All"] = "View All Content"
|
|
226
|
+
self.right_click_actions["Mouse Mode"] = "Change Mouse mode"
|
|
227
|
+
|
|
228
|
+
def mouseClickEvent(self, ev):
|
|
229
|
+
if ev.button() == QtCore.Qt.MouseButton.LeftButton:
|
|
230
|
+
pos = self.mapToView(ev.pos())
|
|
231
|
+
if self.mode == "scatter":
|
|
232
|
+
self.set_scatter_point.emit(pos)
|
|
233
|
+
elif self.mode == "poly-create":
|
|
234
|
+
self.add_poly_vertex.emit(pos)
|
|
235
|
+
ev.accept()
|
|
236
|
+
else:
|
|
237
|
+
# right mouse button shows menu
|
|
238
|
+
super(QuickViewViewBox, self).mouseClickEvent(ev)
|
|
239
|
+
|
|
240
|
+
def hoverEvent(self, ev):
|
|
241
|
+
if hasattr(ev, "_scenePos"):
|
|
242
|
+
pos = self.mapToView(ev.pos())
|
|
243
|
+
self.update_hover_pos.emit(pos)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class RTDCScatterPlot(pg.ScatterPlotItem):
|
|
247
|
+
def __init__(self, size=3, pen=None, brush=None, *args, **kwargs):
|
|
248
|
+
if pen is None:
|
|
249
|
+
pen = pg.mkPen(color=(0, 0, 0, 0))
|
|
250
|
+
if brush is None:
|
|
251
|
+
brush = pg.mkBrush("k")
|
|
252
|
+
super(RTDCScatterPlot, self).__init__(size=size,
|
|
253
|
+
pen=pen,
|
|
254
|
+
brush=brush,
|
|
255
|
+
symbol="s",
|
|
256
|
+
*args,
|
|
257
|
+
**kwargs)
|
|
258
|
+
self.setData(x=range(10), y=range(10), brush=brush)
|
|
259
|
+
|
|
260
|
+
def pointAt(self, pos):
|
|
261
|
+
"""Unlike `ScatterPlotItem.pointsAt`, return the closest point"""
|
|
262
|
+
x = pos.x()
|
|
263
|
+
y = pos.y()
|
|
264
|
+
|
|
265
|
+
pw = self.pixelWidth()
|
|
266
|
+
ph = self.pixelHeight()
|
|
267
|
+
|
|
268
|
+
# compute the distances of all points to x and y
|
|
269
|
+
dists = np.abs((self.data["x"] - x)/pw) \
|
|
270
|
+
+ np.abs((self.data["y"] - y)/ph)
|
|
271
|
+
ide = np.argmin(dists)
|
|
272
|
+
|
|
273
|
+
# calling `self.points` populates the data["item"] column.
|
|
274
|
+
return self.points()[ide]
|
|
275
|
+
|
|
276
|
+
def set_point(self, view_pos):
|
|
277
|
+
pos = self.mapFromView(view_pos)
|
|
278
|
+
pt = self.pointAt(pos)
|
|
279
|
+
self.ptClicked = pt
|
|
280
|
+
self.sigClicked.emit(self, self.ptClicked, None)
|
|
281
|
+
|
|
282
|
+
def mouseClickEvent(self, ev):
|
|
283
|
+
"""Override that does not handle events"""
|
|
284
|
+
ev.ignore() # clicks are handles by CustomViewBox
|
dcscope/gui/update.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import struct
|
|
4
|
+
import sys
|
|
5
|
+
import traceback
|
|
6
|
+
import urllib.request
|
|
7
|
+
|
|
8
|
+
from dclab.external.packaging import parse as parse_version
|
|
9
|
+
from PyQt6 import QtCore
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UpdateWorker(QtCore.QObject):
|
|
13
|
+
finished = QtCore.pyqtSignal()
|
|
14
|
+
data_ready = QtCore.pyqtSignal(dict)
|
|
15
|
+
|
|
16
|
+
@QtCore.pyqtSlot(str, str)
|
|
17
|
+
def processUpdate(self, version, ghrepo):
|
|
18
|
+
mdict = check_release(ghrepo, version)
|
|
19
|
+
if mdict["update available"]:
|
|
20
|
+
self.data_ready.emit(mdict)
|
|
21
|
+
self.finished.emit()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def check_for_update(version, ghrepo):
|
|
25
|
+
thread = QtCore.QThread()
|
|
26
|
+
obj = UpdateWorker()
|
|
27
|
+
obj.moveToThread(thread)
|
|
28
|
+
obj.finished.connect(thread.quit)
|
|
29
|
+
thread.start()
|
|
30
|
+
|
|
31
|
+
QtCore.QMetaObject.invokeMethod(obj, 'processUpdate',
|
|
32
|
+
QtCore.Qt.ConnectionType.QueuedConnection,
|
|
33
|
+
QtCore.Q_ARG(str, version),
|
|
34
|
+
QtCore.Q_ARG(str, ghrepo),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_release(ghrepo="user/repo", version=None, timeout=20):
|
|
39
|
+
"""Check GitHub repository for latest release"""
|
|
40
|
+
url = "https://api.github.com/repos/{}/releases/latest".format(ghrepo)
|
|
41
|
+
if "GITHUB_TOKEN" in os.environ:
|
|
42
|
+
hdr = {'authorization': os.environ["GITHUB_TOKEN"]}
|
|
43
|
+
else:
|
|
44
|
+
hdr = {}
|
|
45
|
+
web = "https://github.com/{}/releases".format(ghrepo)
|
|
46
|
+
errors = None # error messages (str)
|
|
47
|
+
update = False # whether an update is available
|
|
48
|
+
binary = None # download link to binary file
|
|
49
|
+
new_version = None # string identifying new version
|
|
50
|
+
try:
|
|
51
|
+
req = urllib.request.Request(url, headers=hdr)
|
|
52
|
+
data = urllib.request.urlopen(req, timeout=timeout).read()
|
|
53
|
+
except BaseException:
|
|
54
|
+
errors = traceback.format_exc()
|
|
55
|
+
else:
|
|
56
|
+
j = json.loads(data)
|
|
57
|
+
|
|
58
|
+
newversion = j["tag_name"]
|
|
59
|
+
|
|
60
|
+
if version is not None:
|
|
61
|
+
new = parse_version(newversion)
|
|
62
|
+
old = parse_version(version)
|
|
63
|
+
if new > old:
|
|
64
|
+
update = True
|
|
65
|
+
new_version = newversion
|
|
66
|
+
if hasattr(sys, "frozen"):
|
|
67
|
+
# determine which binary URL we need
|
|
68
|
+
if sys.platform == "win32":
|
|
69
|
+
nbit = 8 * struct.calcsize("P")
|
|
70
|
+
if nbit == 32:
|
|
71
|
+
dlid = "win_32bit_setup.exe"
|
|
72
|
+
else:
|
|
73
|
+
dlid = "win_64bit_setup.exe"
|
|
74
|
+
elif sys.platform == "darwin":
|
|
75
|
+
dlid = ".pkg"
|
|
76
|
+
else:
|
|
77
|
+
dlid = False
|
|
78
|
+
# search for binary download file
|
|
79
|
+
if dlid:
|
|
80
|
+
for a in j["assets"]:
|
|
81
|
+
if a["browser_download_url"].count(dlid):
|
|
82
|
+
binary = a["browser_download_url"]
|
|
83
|
+
break
|
|
84
|
+
mdict = {"releases url": web,
|
|
85
|
+
"binary url": binary,
|
|
86
|
+
"version": new_version,
|
|
87
|
+
"update available": update,
|
|
88
|
+
"errors": errors,
|
|
89
|
+
}
|
|
90
|
+
return mdict
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# flake8: noqa: F401
|
|
2
|
+
from .bg_thread import run_async
|
|
3
|
+
from .double_spin_box_nan import DoubleSpinBoxNan
|
|
4
|
+
from .feature_combobox import FeatureComboBox
|
|
5
|
+
from .get_path import get_directory
|
|
6
|
+
from .key_value_table_widget import KeyValueTableWidget
|
|
7
|
+
from .mdi_subwindow_wo_close import MDISubWindowWOButtons
|
|
8
|
+
from .qrangeslider import QRangeSlider
|
|
9
|
+
from .rangecontrol import RangeControl
|
|
10
|
+
from .simple_image_view import SimpleImageView
|
|
11
|
+
from .simple_plot_widget import (SimplePlotItem,
|
|
12
|
+
SimplePlotWidget,
|
|
13
|
+
SimpleViewBox)
|
|
14
|
+
from .so_colorbaritem import DCscopeColorBarItem
|
|
15
|
+
from .wait_cursor import show_wait_cursor, ShowWaitCursor
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""https://stackoverflow.com/questions/39304951/"""
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
from PyQt6 import QtCore
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Runner(QtCore.QThread):
|
|
8
|
+
"""Runs a function in the background"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, target, *args, **kwargs):
|
|
11
|
+
super().__init__(*args, **kwargs)
|
|
12
|
+
self._target = target
|
|
13
|
+
self._args = args
|
|
14
|
+
self._kwargs = kwargs
|
|
15
|
+
|
|
16
|
+
def run(self):
|
|
17
|
+
self._target(*self._args, **self._kwargs)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run_async(func):
|
|
21
|
+
"""Decorator for running a function in the background"""
|
|
22
|
+
@wraps(func)
|
|
23
|
+
def async_func(*args, **kwargs):
|
|
24
|
+
runner = Runner(func, *args, **kwargs)
|
|
25
|
+
# Keep the runner somewhere or it will be destroyed
|
|
26
|
+
func.__runner = runner
|
|
27
|
+
runner.start()
|
|
28
|
+
|
|
29
|
+
return async_func
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import importlib.resources
|
|
2
|
+
|
|
3
|
+
from PyQt6 import uic, QtCore, QtWidgets
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BulkList(QtWidgets.QWidget):
|
|
7
|
+
|
|
8
|
+
def __init__(self, parent, title=None, items=None, *args, **kwargs):
|
|
9
|
+
"""A checkable list with bulk (de-)selection button"""
|
|
10
|
+
super(BulkList, self).__init__(parent=parent, *args, **kwargs)
|
|
11
|
+
ref = importlib.resources.files(
|
|
12
|
+
"dcscope.gui.widgets") / "bulk_list.ui"
|
|
13
|
+
with importlib.resources.as_file(ref) as path_ui:
|
|
14
|
+
uic.loadUi(path_ui, self)
|
|
15
|
+
|
|
16
|
+
if title is not None:
|
|
17
|
+
self.set_title(title)
|
|
18
|
+
|
|
19
|
+
if items is not None:
|
|
20
|
+
self.set_items(items)
|
|
21
|
+
|
|
22
|
+
# select none by default
|
|
23
|
+
self.on_select_none()
|
|
24
|
+
|
|
25
|
+
# signals
|
|
26
|
+
self.toolButton_all.clicked.connect(self.on_select_all)
|
|
27
|
+
self.toolButton_none.clicked.connect(self.on_select_none)
|
|
28
|
+
|
|
29
|
+
def get_selection(self):
|
|
30
|
+
items = []
|
|
31
|
+
for ii in range(self.listWidget.count()):
|
|
32
|
+
wid = self.listWidget.item(ii)
|
|
33
|
+
if wid.checkState() == QtCore.Qt.CheckState.Checked:
|
|
34
|
+
items.append(wid.data(101))
|
|
35
|
+
return items
|
|
36
|
+
|
|
37
|
+
def set_items(self, items, labels=None):
|
|
38
|
+
"""Set the items of the list widget
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
items: list
|
|
43
|
+
A list of the items in the list. If `labels` is
|
|
44
|
+
None then this must be a list of strings. This
|
|
45
|
+
is what is returned by `get_selection`.
|
|
46
|
+
labels: list of str
|
|
47
|
+
If set, use these strings as placeholders in the
|
|
48
|
+
list widget.
|
|
49
|
+
"""
|
|
50
|
+
if labels is None:
|
|
51
|
+
labels = items
|
|
52
|
+
self.listWidget.clear()
|
|
53
|
+
for item, label in zip(items, labels):
|
|
54
|
+
wid = QtWidgets.QListWidgetItem(label)
|
|
55
|
+
wid.setData(101, item)
|
|
56
|
+
wid.setCheckState(QtCore.Qt.CheckState.Unchecked)
|
|
57
|
+
self.listWidget.addItem(wid)
|
|
58
|
+
|
|
59
|
+
def set_title(self, title):
|
|
60
|
+
"""Set the title of the group box"""
|
|
61
|
+
self.groupBox.setTitle(title)
|
|
62
|
+
|
|
63
|
+
@QtCore.pyqtSlot()
|
|
64
|
+
def on_select_all(self):
|
|
65
|
+
"""Select all items"""
|
|
66
|
+
self.toolButton_none.setVisible(True)
|
|
67
|
+
self.toolButton_all.setVisible(False)
|
|
68
|
+
for ii in range(self.listWidget.count()):
|
|
69
|
+
wid = self.listWidget.item(ii)
|
|
70
|
+
wid.setCheckState(QtCore.Qt.CheckState.Checked)
|
|
71
|
+
|
|
72
|
+
@QtCore.pyqtSlot()
|
|
73
|
+
def on_select_none(self):
|
|
74
|
+
"""Deselect all items"""
|
|
75
|
+
self.toolButton_none.setVisible(False)
|
|
76
|
+
self.toolButton_all.setVisible(True)
|
|
77
|
+
for ii in range(self.listWidget.count()):
|
|
78
|
+
wid = self.listWidget.item(ii)
|
|
79
|
+
wid.setCheckState(QtCore.Qt.CheckState.Unchecked)
|
|
@@ -0,0 +1,95 @@
|
|
|
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>400</width>
|
|
10
|
+
<height>300</height>
|
|
11
|
+
</rect>
|
|
12
|
+
</property>
|
|
13
|
+
<property name="windowTitle">
|
|
14
|
+
<string>Form</string>
|
|
15
|
+
</property>
|
|
16
|
+
<layout class="QVBoxLayout" name="verticalLayout">
|
|
17
|
+
<property name="leftMargin">
|
|
18
|
+
<number>0</number>
|
|
19
|
+
</property>
|
|
20
|
+
<property name="topMargin">
|
|
21
|
+
<number>0</number>
|
|
22
|
+
</property>
|
|
23
|
+
<property name="rightMargin">
|
|
24
|
+
<number>0</number>
|
|
25
|
+
</property>
|
|
26
|
+
<property name="bottomMargin">
|
|
27
|
+
<number>0</number>
|
|
28
|
+
</property>
|
|
29
|
+
<item>
|
|
30
|
+
<widget class="QGroupBox" name="groupBox">
|
|
31
|
+
<property name="sizePolicy">
|
|
32
|
+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
33
|
+
<horstretch>0</horstretch>
|
|
34
|
+
<verstretch>0</verstretch>
|
|
35
|
+
</sizepolicy>
|
|
36
|
+
</property>
|
|
37
|
+
<property name="title">
|
|
38
|
+
<string>List content</string>
|
|
39
|
+
</property>
|
|
40
|
+
<property name="checkable">
|
|
41
|
+
<bool>false</bool>
|
|
42
|
+
</property>
|
|
43
|
+
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
44
|
+
<item>
|
|
45
|
+
<widget class="QListWidget" name="listWidget"/>
|
|
46
|
+
</item>
|
|
47
|
+
<item>
|
|
48
|
+
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
49
|
+
<item>
|
|
50
|
+
<widget class="QLabel" name="label_help_selection">
|
|
51
|
+
<property name="text">
|
|
52
|
+
<string><a href="https://dcscope.readthedocs.io/en/stable/sec_qg_export_data.html#sec-qg-export-data-faq">Why are not all selected?</a></string>
|
|
53
|
+
</property>
|
|
54
|
+
<property name="openExternalLinks">
|
|
55
|
+
<bool>true</bool>
|
|
56
|
+
</property>
|
|
57
|
+
</widget>
|
|
58
|
+
</item>
|
|
59
|
+
<item>
|
|
60
|
+
<spacer name="horizontalSpacer">
|
|
61
|
+
<property name="orientation">
|
|
62
|
+
<enum>Qt::Horizontal</enum>
|
|
63
|
+
</property>
|
|
64
|
+
<property name="sizeHint" stdset="0">
|
|
65
|
+
<size>
|
|
66
|
+
<width>40</width>
|
|
67
|
+
<height>20</height>
|
|
68
|
+
</size>
|
|
69
|
+
</property>
|
|
70
|
+
</spacer>
|
|
71
|
+
</item>
|
|
72
|
+
<item>
|
|
73
|
+
<widget class="QToolButton" name="toolButton_none">
|
|
74
|
+
<property name="text">
|
|
75
|
+
<string>select none</string>
|
|
76
|
+
</property>
|
|
77
|
+
</widget>
|
|
78
|
+
</item>
|
|
79
|
+
<item>
|
|
80
|
+
<widget class="QToolButton" name="toolButton_all">
|
|
81
|
+
<property name="text">
|
|
82
|
+
<string>select all</string>
|
|
83
|
+
</property>
|
|
84
|
+
</widget>
|
|
85
|
+
</item>
|
|
86
|
+
</layout>
|
|
87
|
+
</item>
|
|
88
|
+
</layout>
|
|
89
|
+
</widget>
|
|
90
|
+
</item>
|
|
91
|
+
</layout>
|
|
92
|
+
</widget>
|
|
93
|
+
<resources/>
|
|
94
|
+
<connections/>
|
|
95
|
+
</ui>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from PyQt6 import QtWidgets, QtGui
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DoubleSpinBoxNan(QtWidgets.QDoubleSpinBox):
|
|
6
|
+
"""A doubleSpinBox that uses minimum() and maximum() as np.nan"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, *args, **kwargs):
|
|
9
|
+
super(DoubleSpinBoxNan, self).__init__(*args, **kwargs)
|
|
10
|
+
self._suffix = self.suffix() # remember initial suffix
|
|
11
|
+
self.validator = NanFloatValidator()
|
|
12
|
+
|
|
13
|
+
def validate(self, text, position):
|
|
14
|
+
return self.validator.validate(text, position, self.suffix())
|
|
15
|
+
|
|
16
|
+
def value(self):
|
|
17
|
+
value = super(DoubleSpinBoxNan, self).value()
|
|
18
|
+
if value == self.minimum() or value == self.maximum():
|
|
19
|
+
value = np.nan
|
|
20
|
+
return value
|
|
21
|
+
|
|
22
|
+
def valueFromText(self, text):
|
|
23
|
+
if text == "nan":
|
|
24
|
+
return np.nan
|
|
25
|
+
else:
|
|
26
|
+
return convert_string_to_nanfloat(text[:-len(self.suffix())])
|
|
27
|
+
|
|
28
|
+
def textFromValue(self, value):
|
|
29
|
+
if value == self.minimum() or value == self.maximum():
|
|
30
|
+
return "nan"
|
|
31
|
+
else:
|
|
32
|
+
return str(value)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NanFloatValidator(QtGui.QValidator):
|
|
36
|
+
def validate(self, text, position, suffix):
|
|
37
|
+
string = text[:-len(suffix)]
|
|
38
|
+
if string in ["n", "na", "nan"]:
|
|
39
|
+
text = "nan"
|
|
40
|
+
if valid_nanfloat_string(string):
|
|
41
|
+
return self.State.Acceptable, text, position
|
|
42
|
+
elif string == "":
|
|
43
|
+
return self.State.Intermediate, text, position
|
|
44
|
+
return self.State.Invalid, text, position
|
|
45
|
+
|
|
46
|
+
def fixup(self, text):
|
|
47
|
+
try:
|
|
48
|
+
val = convert_string_to_nanfloat(text)
|
|
49
|
+
except ValueError:
|
|
50
|
+
val = ""
|
|
51
|
+
return str(val)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def valid_nanfloat_string(string):
|
|
55
|
+
try:
|
|
56
|
+
convert_string_to_nanfloat(string)
|
|
57
|
+
except ValueError:
|
|
58
|
+
valid = False
|
|
59
|
+
else:
|
|
60
|
+
valid = True
|
|
61
|
+
return valid
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def convert_string_to_nanfloat(string):
|
|
65
|
+
try:
|
|
66
|
+
val = float(string)
|
|
67
|
+
except ValueError:
|
|
68
|
+
string = string.strip()
|
|
69
|
+
for iid in ["n", "na", "nan"]:
|
|
70
|
+
if string.startswith(iid):
|
|
71
|
+
val = np.nan
|
|
72
|
+
break
|
|
73
|
+
else:
|
|
74
|
+
raise ValueError("Not a valid nan-float!")
|
|
75
|
+
return val
|