auto-options-python 0.1.4__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.
- auto_options_python-0.1.4.dist-info/METADATA +58 -0
- auto_options_python-0.1.4.dist-info/RECORD +19 -0
- auto_options_python-0.1.4.dist-info/WHEEL +5 -0
- auto_options_python-0.1.4.dist-info/licenses/LICENSE +21 -0
- auto_options_python-0.1.4.dist-info/top_level.txt +2 -0
- autooptions/__init__.py +16 -0
- autooptions/_tests/__init__.py +0 -0
- autooptions/_tests/test_array_util.py +24 -0
- autooptions/_tests/test_napari_util.py +146 -0
- autooptions/_tests/test_options.py +172 -0
- autooptions/_tests/test_qtutil.py +245 -0
- autooptions/_tests/test_widget.py +132 -0
- autooptions/_version.py +24 -0
- autooptions/array_util.py +39 -0
- autooptions/napari_util.py +133 -0
- autooptions/options.py +311 -0
- autooptions/qtutil.py +270 -0
- autooptions/widget.py +370 -0
- scratch/options.py +36 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import pyperclip
|
|
2
|
+
from qtpy.QtWidgets import QWidget
|
|
3
|
+
from qtpy.QtGui import QKeyEvent
|
|
4
|
+
from qtpy.QtCore import QEvent
|
|
5
|
+
from qtpy.QtCore import Qt
|
|
6
|
+
from autooptions.qtutil import WidgetTool
|
|
7
|
+
from autooptions.qtutil import TableView
|
|
8
|
+
from autooptions.qtutil import PlotWidget
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
COUNTER_TEXT_CHANGED = 0
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ParentWidget(QWidget):
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.setWindowTitle("Test")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def handleTextChanged(text):
|
|
26
|
+
global COUNTER_TEXT_CHANGED
|
|
27
|
+
COUNTER_TEXT_CHANGED = COUNTER_TEXT_CHANGED + 1
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def testGetLineInput(make_napari_viewer_proxy):
|
|
31
|
+
make_napari_viewer_proxy()
|
|
32
|
+
parent = ParentWidget()
|
|
33
|
+
label, inputWidget = WidgetTool.getLineInput(parent,
|
|
34
|
+
"your guess",
|
|
35
|
+
10,
|
|
36
|
+
50,
|
|
37
|
+
handleTextChanged)
|
|
38
|
+
assert label in parent.children()
|
|
39
|
+
assert inputWidget in parent.children()
|
|
40
|
+
assert label.parent() == parent
|
|
41
|
+
assert inputWidget.parent() == parent
|
|
42
|
+
assert label.text() == "your guess"
|
|
43
|
+
counter = COUNTER_TEXT_CHANGED
|
|
44
|
+
assert inputWidget.text() == str(10)
|
|
45
|
+
inputWidget.setText("20")
|
|
46
|
+
assert COUNTER_TEXT_CHANGED == counter # Only editing in the interface sends the signal,
|
|
47
|
+
# not programmatically changing the value
|
|
48
|
+
assert inputWidget.maximumWidth() == 50
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def testGetLineInputNoCallback(make_napari_viewer_proxy):
|
|
52
|
+
make_napari_viewer_proxy()
|
|
53
|
+
parent = ParentWidget()
|
|
54
|
+
label, inputWidget = WidgetTool.getLineInput(parent,
|
|
55
|
+
"your guess",
|
|
56
|
+
10,
|
|
57
|
+
50,
|
|
58
|
+
None)
|
|
59
|
+
assert label in parent.children()
|
|
60
|
+
assert inputWidget in parent.children()
|
|
61
|
+
assert label.parent() == parent
|
|
62
|
+
assert inputWidget.parent() == parent
|
|
63
|
+
assert label.text() == "your guess"
|
|
64
|
+
counter = COUNTER_TEXT_CHANGED
|
|
65
|
+
assert inputWidget.text() == str(10)
|
|
66
|
+
inputWidget.setText("20")
|
|
67
|
+
assert inputWidget.maximumWidth() == 50
|
|
68
|
+
assert COUNTER_TEXT_CHANGED == counter
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def testGetComboBox(make_napari_viewer_proxy, mocker):
|
|
72
|
+
make_napari_viewer_proxy()
|
|
73
|
+
parent = ParentWidget()
|
|
74
|
+
callback = mocker.stub(name='onSelectedFruitChanged')
|
|
75
|
+
label, comboWidget = WidgetTool.getComboInput(parent,
|
|
76
|
+
"fruits",
|
|
77
|
+
["apple", "orange", "banana"],
|
|
78
|
+
callback=callback)
|
|
79
|
+
assert label in parent.children()
|
|
80
|
+
assert comboWidget in parent.children()
|
|
81
|
+
assert label.parent() == parent
|
|
82
|
+
assert comboWidget.parent() == parent
|
|
83
|
+
assert label.text() == "fruits"
|
|
84
|
+
allItems = [comboWidget.itemText(i) for i in range(comboWidget.count())]
|
|
85
|
+
assert allItems == ["apple", "orange", "banana"]
|
|
86
|
+
assert comboWidget.currentText() == "apple"
|
|
87
|
+
comboWidget.setCurrentText("banana")
|
|
88
|
+
assert comboWidget.currentText() == "banana"
|
|
89
|
+
callback.assert_called_once_with("banana")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def testReplaceItemsInComboBox(make_napari_viewer_proxy, mocker):
|
|
93
|
+
make_napari_viewer_proxy()
|
|
94
|
+
parent = ParentWidget()
|
|
95
|
+
label, comboWidget = WidgetTool.getComboInput(parent,
|
|
96
|
+
"fruits",
|
|
97
|
+
["apple", "orange", "banana"])
|
|
98
|
+
WidgetTool.replaceItemsInComboBox(comboWidget, ["lemon", "ananas"])
|
|
99
|
+
allItems = [comboWidget.itemText(i) for i in range(comboWidget.count())]
|
|
100
|
+
assert allItems == ["lemon", "ananas"]
|
|
101
|
+
assert comboWidget.currentText() == "lemon"
|
|
102
|
+
comboWidget.setCurrentIndex(1)
|
|
103
|
+
WidgetTool.replaceItemsInComboBox(comboWidget, ["apple", "ananas"])
|
|
104
|
+
allItems = [comboWidget.itemText(i) for i in range(comboWidget.count())]
|
|
105
|
+
assert allItems == ["apple", "ananas"]
|
|
106
|
+
assert comboWidget.currentText() == "ananas"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def testGetCheckBox(make_napari_viewer_proxy, mocker):
|
|
110
|
+
make_napari_viewer_proxy()
|
|
111
|
+
parent = ParentWidget()
|
|
112
|
+
callback = mocker.stub(name='onRemoveBackgroundChanged')
|
|
113
|
+
label, checkbox = WidgetTool.getCheckbox(parent,
|
|
114
|
+
"remove background",
|
|
115
|
+
True,
|
|
116
|
+
50,
|
|
117
|
+
callback)
|
|
118
|
+
assert label in parent.children()
|
|
119
|
+
assert checkbox in parent.children()
|
|
120
|
+
assert checkbox.parent() == parent
|
|
121
|
+
assert label.parent() == parent
|
|
122
|
+
assert label.text() == "remove background"
|
|
123
|
+
assert checkbox.isChecked()
|
|
124
|
+
assert checkbox.maximumWidth() == 50
|
|
125
|
+
checkbox.setChecked(False)
|
|
126
|
+
callback.assert_called_once_with(False)
|
|
127
|
+
label2, checkbox2 = WidgetTool.getCheckbox(parent,
|
|
128
|
+
"smooth",
|
|
129
|
+
False,
|
|
130
|
+
50,
|
|
131
|
+
None)
|
|
132
|
+
assert not checkbox2.isChecked()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class TestTableView:
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def testConstructor(self, make_napari_viewer_proxy):
|
|
140
|
+
make_napari_viewer_proxy()
|
|
141
|
+
table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
|
|
142
|
+
tableView = TableView(table)
|
|
143
|
+
assert tableView.data == table
|
|
144
|
+
tableView2 = TableView(None)
|
|
145
|
+
assert not tableView2.data
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def testSetData(self, make_napari_viewer_proxy):
|
|
149
|
+
make_napari_viewer_proxy()
|
|
150
|
+
table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
|
|
151
|
+
table2 = {"fruits": ["apple", "orange", "banana"], "animals": ["lion", "tiger"]}
|
|
152
|
+
tableView = TableView(table)
|
|
153
|
+
tableView.setData(table2)
|
|
154
|
+
assert tableView.data == table2
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def testResetView(self, make_napari_viewer_proxy):
|
|
158
|
+
make_napari_viewer_proxy()
|
|
159
|
+
table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
|
|
160
|
+
tableView = TableView(table)
|
|
161
|
+
tableView.resetView()
|
|
162
|
+
assert tableView.data == table
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def testKeyPressEvent(self, make_napari_viewer_proxy):
|
|
166
|
+
pyperclip.copy("")
|
|
167
|
+
make_napari_viewer_proxy()
|
|
168
|
+
table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
|
|
169
|
+
tableView = TableView(table)
|
|
170
|
+
tableView.selectAll()
|
|
171
|
+
event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_C, Qt.KeyboardModifier.ControlModifier, "copy")
|
|
172
|
+
tableView.keyPressEvent(event)
|
|
173
|
+
text = pyperclip.paste()
|
|
174
|
+
assert "23.87" in text
|
|
175
|
+
assert "24553" in text
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def testKeyPressEventNoCopy(self, make_napari_viewer_proxy):
|
|
179
|
+
pyperclip.copy("")
|
|
180
|
+
make_napari_viewer_proxy()
|
|
181
|
+
table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
|
|
182
|
+
tableView = TableView(table)
|
|
183
|
+
tableView.selectAll()
|
|
184
|
+
event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_C, Qt.KeyboardModifier.ShiftModifier, "copy")
|
|
185
|
+
tableView.keyPressEvent(event)
|
|
186
|
+
text = pyperclip.paste()
|
|
187
|
+
assert text == ""
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def testCopyDataToClipboard(self, make_napari_viewer_proxy):
|
|
191
|
+
pyperclip.copy("")
|
|
192
|
+
make_napari_viewer_proxy()
|
|
193
|
+
table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
|
|
194
|
+
tableView = TableView(table)
|
|
195
|
+
tableView.copyDataToClipboard()
|
|
196
|
+
text = pyperclip.paste()
|
|
197
|
+
assert text == ""
|
|
198
|
+
tableView.selectAll()
|
|
199
|
+
tableView.copyDataToClipboard()
|
|
200
|
+
text = pyperclip.paste()
|
|
201
|
+
assert "23.87" in text
|
|
202
|
+
assert "24553" in text
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class TestPlotWidget:
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def testConstructor(self, make_napari_viewer_proxy):
|
|
209
|
+
viewer = make_napari_viewer_proxy()
|
|
210
|
+
plot = PlotWidget(viewer)
|
|
211
|
+
assert plot.figure
|
|
212
|
+
assert plot.X == []
|
|
213
|
+
assert plot.Y == []
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def testAddData(self, make_napari_viewer_proxy):
|
|
217
|
+
viewer = make_napari_viewer_proxy()
|
|
218
|
+
plot = PlotWidget(viewer)
|
|
219
|
+
plot.addData([1, 2, 3], [1, 4, 9], "r+")
|
|
220
|
+
assert [1, 2, 3] in plot.X
|
|
221
|
+
assert [1, 4, 9] in plot.Y
|
|
222
|
+
assert "r+" in plot.formatStrings
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def testClearData(self, make_napari_viewer_proxy):
|
|
226
|
+
viewer = make_napari_viewer_proxy()
|
|
227
|
+
plot = PlotWidget(viewer)
|
|
228
|
+
plot.addData([1, 2, 3], [1, 4, 9], "r+")
|
|
229
|
+
plot.clear()
|
|
230
|
+
assert True
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def testDisplay(self, make_napari_viewer_proxy):
|
|
234
|
+
viewer = make_napari_viewer_proxy()
|
|
235
|
+
plot = PlotWidget(viewer)
|
|
236
|
+
plot.addData([1, 2, 3], [1, 4, 9], "r+")
|
|
237
|
+
plot.display()
|
|
238
|
+
assert plot.ax.get_xlabel() == plot.xLabel
|
|
239
|
+
assert plot.ax.get_ylabel() == plot.yLabel
|
|
240
|
+
plot2 = PlotWidget(viewer)
|
|
241
|
+
plot2.addData([1, 2, 3], [1, 4, 9])
|
|
242
|
+
plot2.display()
|
|
243
|
+
assert plot2.ax.get_xlabel() == plot2.xLabel
|
|
244
|
+
assert plot2.ax.get_ylabel() == plot2.yLabel
|
|
245
|
+
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
import numpy as np
|
|
4
|
+
from autooptions.widget import OptionsWidget
|
|
5
|
+
from autooptions.options import Options
|
|
6
|
+
|
|
7
|
+
class FakeEvent:
|
|
8
|
+
modifiers = {}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestOptionsWidget:
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def onSomethingHappened(self, value):
|
|
16
|
+
self.lastValue = value
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture()
|
|
20
|
+
def options(self):
|
|
21
|
+
options = Options("Autooptions Test", "Widget Test")
|
|
22
|
+
options.addImage('image', value=None, transient=True)
|
|
23
|
+
options.addLabels('labels', value='labels_layer', transient=True)
|
|
24
|
+
options.addPoints('points', value='points_layer', transient=True)
|
|
25
|
+
options.addFFT('fft', value=None, transient=True)
|
|
26
|
+
options.addStr("group", value="lps")
|
|
27
|
+
options.addInt('size xy', value=3)
|
|
28
|
+
options.addInt('size z', value=1)
|
|
29
|
+
options.addChoice('footprint', choices=["none", "cube", "ball", "octahedron"], callback=self.onSomethingHappened)
|
|
30
|
+
options.addFloat('sigma', value=1.34)
|
|
31
|
+
options.addBool('do it', value=True)
|
|
32
|
+
yield options
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def testConstructor(self, options, make_napari_viewer_proxy):
|
|
36
|
+
viewer = make_napari_viewer_proxy()
|
|
37
|
+
widget = OptionsWidget(viewer, options, self)
|
|
38
|
+
assert widget.options is options
|
|
39
|
+
assert widget.viewer is viewer
|
|
40
|
+
assert widget.client is self
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def testAddApplyButton(self, options, make_napari_viewer_proxy, mocker):
|
|
44
|
+
viewer = make_napari_viewer_proxy()
|
|
45
|
+
widget = OptionsWidget(viewer, options, self)
|
|
46
|
+
assert widget.getApplyButton() is None
|
|
47
|
+
callback = mocker.stub(name='onApplyButtonPressed')
|
|
48
|
+
widget.addApplyButton(callback)
|
|
49
|
+
assert not widget.getApplyButton() is None
|
|
50
|
+
assert callback.call_count == 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def testAddOKButton(self, options, make_napari_viewer_proxy, mocker):
|
|
54
|
+
viewer = make_napari_viewer_proxy()
|
|
55
|
+
widget = OptionsWidget(viewer, options, self)
|
|
56
|
+
assert widget.getOKButton() is None
|
|
57
|
+
callback = mocker.stub(name='onOKButtonPressed')
|
|
58
|
+
widget.addOKButton(callback)
|
|
59
|
+
assert not widget.getOKButton() is None
|
|
60
|
+
assert callback.call_count == 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def testAddCancelButton(self, options, make_napari_viewer_proxy, mocker):
|
|
64
|
+
viewer = make_napari_viewer_proxy()
|
|
65
|
+
widget = OptionsWidget(viewer, options, self)
|
|
66
|
+
assert widget.getCancelButton() is None
|
|
67
|
+
callback = mocker.stub(name='onCancelButtonPressed')
|
|
68
|
+
widget.addCancelButton(callback)
|
|
69
|
+
assert not widget.getCancelButton() is None
|
|
70
|
+
assert callback.call_count == 0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_OnApplyButtonClicked(self, options, make_napari_viewer_proxy, mocker):
|
|
74
|
+
viewer = make_napari_viewer_proxy()
|
|
75
|
+
widget = OptionsWidget(viewer, options, self)
|
|
76
|
+
callback = mocker.stub(name='self.apply')
|
|
77
|
+
widget.addApplyButton(callback)
|
|
78
|
+
options.setValue('group', 'xps')
|
|
79
|
+
if os.path.exists(options.optionsPath):
|
|
80
|
+
os.remove(options.optionsPath)
|
|
81
|
+
widget._onApplyButtonClicked()
|
|
82
|
+
assert os.path.exists(options.optionsPath)
|
|
83
|
+
assert options.value("group") == "lps"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_OnOKButtonClicked(self, options, make_napari_viewer_proxy, mocker):
|
|
87
|
+
viewer = make_napari_viewer_proxy()
|
|
88
|
+
widget = OptionsWidget(viewer, options, self)
|
|
89
|
+
callback = mocker.stub(name='self.ok')
|
|
90
|
+
widget.addOKButton(callback)
|
|
91
|
+
viewer.window.add_dock_widget(widget, name=options.optionsName)
|
|
92
|
+
options.setValue('group', 'xps')
|
|
93
|
+
if os.path.exists(options.optionsPath):
|
|
94
|
+
os.remove(options.optionsPath)
|
|
95
|
+
widget._onOKButtonClicked()
|
|
96
|
+
assert os.path.exists(options.optionsPath)
|
|
97
|
+
assert options.value("group") == "lps"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_OnCancelButtonClicked(self, options, make_napari_viewer_proxy, mocker):
|
|
101
|
+
viewer = make_napari_viewer_proxy()
|
|
102
|
+
widget = OptionsWidget(viewer, options, self)
|
|
103
|
+
callback = mocker.stub(name='self.cancel')
|
|
104
|
+
widget.addCancelButton(callback)
|
|
105
|
+
viewer.window.add_dock_widget(widget, name=options.optionsName)
|
|
106
|
+
options.setValue('group', 'xps')
|
|
107
|
+
if os.path.exists(options.optionsPath):
|
|
108
|
+
os.remove(options.optionsPath)
|
|
109
|
+
widget._onCancelButtonClicked()
|
|
110
|
+
assert not os.path.exists(options.optionsPath)
|
|
111
|
+
assert options.value("group") == "xps"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_OnLayerAddedOrRemoved(self, options, make_napari_viewer_proxy):
|
|
115
|
+
viewer = make_napari_viewer_proxy()
|
|
116
|
+
widget = OptionsWidget(viewer, options, self)
|
|
117
|
+
viewer.window.add_dock_widget(widget, name=options.optionsName)
|
|
118
|
+
data = np.random.rand(12, 12)
|
|
119
|
+
viewer.add_image(data=data, name='data')
|
|
120
|
+
assert True
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def testGetImageLayer(self, options, make_napari_viewer_proxy):
|
|
124
|
+
viewer = make_napari_viewer_proxy()
|
|
125
|
+
widget = OptionsWidget(viewer, options, self)
|
|
126
|
+
viewer.window.add_dock_widget(widget, name=options.optionsName)
|
|
127
|
+
data = np.random.rand(12, 12)
|
|
128
|
+
viewer.add_image(data=data, name='labels_layer')
|
|
129
|
+
layer = widget.getImageLayer("labels")
|
|
130
|
+
assert layer is not None
|
|
131
|
+
assert layer.name == "labels_layer"
|
|
132
|
+
assert layer.data.shape == (12, 12)
|
autooptions/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 4)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ArrayUtil:
|
|
6
|
+
"""A class to provide utils that do common operations on arrays.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def stripZeroRowsAndColumns(data, zero=0):
|
|
12
|
+
"""Return an array with all-zero rows and columns removed.
|
|
13
|
+
|
|
14
|
+
Returns a stripped array, with all rows and columns, in which each
|
|
15
|
+
element is zero, removed. Instead of rows and comumns with all zero
|
|
16
|
+
elements, rows and columns containing another number, string or
|
|
17
|
+
object at each position can be removed from the array.
|
|
18
|
+
|
|
19
|
+
:param data: A table from which empty rows and columns will be stripped
|
|
20
|
+
:type data: numpy.ndarray
|
|
21
|
+
:param zero: The element for which rows and columns will be removed
|
|
22
|
+
:return: A 3-tupel with
|
|
23
|
+
|
|
24
|
+
* the input array data with all-zero rows and columns removed
|
|
25
|
+
* A 1D array of the indices of the columns that are not all zero
|
|
26
|
+
in the input array
|
|
27
|
+
* A 1D array of the indices of the rows that are not all zero
|
|
28
|
+
in the input array
|
|
29
|
+
|
|
30
|
+
:rtype: (numpy.ndarray, numpy.ndarray, numpy.ndarray)
|
|
31
|
+
"""
|
|
32
|
+
rowIndices = np.where(~np.all(data == zero, axis=1))[0]
|
|
33
|
+
stripped = data[~np.all(data == zero, axis=1)]
|
|
34
|
+
stripped = np.array(list(zip(*stripped)))
|
|
35
|
+
columnIndices = np.where(~np.all(stripped == zero, axis=1))[0]
|
|
36
|
+
stripped = stripped[~np.all(stripped == zero, axis=1)]
|
|
37
|
+
stripped = np.array(list(zip(*stripped)))
|
|
38
|
+
return stripped, columnIndices, rowIndices
|
|
39
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from napari.layers.labels.labels import Labels
|
|
2
|
+
from napari.layers.points.points import Points
|
|
3
|
+
from napari.layers.image.image import Image
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NapariUtil:
|
|
8
|
+
""" Utility methods for the napari image viewer.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, viewer):
|
|
12
|
+
""" Constructor.
|
|
13
|
+
|
|
14
|
+
:param viewer: the napari viewer
|
|
15
|
+
:type viewer: napari.viewer.Viewer
|
|
16
|
+
"""
|
|
17
|
+
self.viewer = viewer
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def getImageLayers(self):
|
|
21
|
+
""" Return all image layers
|
|
22
|
+
|
|
23
|
+
:return: A list of the image layers in the viewer
|
|
24
|
+
:rtype: [napari.layers.image.Image.Image]
|
|
25
|
+
"""
|
|
26
|
+
return self.getLayersOfType(Image)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def getFFTLayers(self):
|
|
30
|
+
""" Return all fft layers
|
|
31
|
+
|
|
32
|
+
:return: A list of the fft layers in the viewer
|
|
33
|
+
:rtype: [napari.layers.image.Image.Image]
|
|
34
|
+
"""
|
|
35
|
+
imageLayers = self.getImageLayers()
|
|
36
|
+
imageLayers = [self.getLayerWithName(name) for name in imageLayers]
|
|
37
|
+
fftLayers = [layer.name for layer in imageLayers if 'fft' in layer.metadata.keys()]
|
|
38
|
+
return fftLayers
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def getLabelLayers(self):
|
|
42
|
+
""" Return all label layers
|
|
43
|
+
|
|
44
|
+
:return: A list of the label layers in the viewer
|
|
45
|
+
:rtype: [napari.layers.labels.labels.Labels]
|
|
46
|
+
"""
|
|
47
|
+
return self.getLayersOfType(Labels)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def getPointsLayers(self):
|
|
51
|
+
""" Return all point layers
|
|
52
|
+
|
|
53
|
+
:return: A list of the point layers in the viewer
|
|
54
|
+
:rtype: [napari.layers.points.points.Points]
|
|
55
|
+
"""
|
|
56
|
+
return self.getLayersOfType(Points)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def getLayersOfType(self, layerType):
|
|
60
|
+
""" Return all layers of type layerType in the viewer
|
|
61
|
+
|
|
62
|
+
:param layerType: A napari layer type like Labels or Points.
|
|
63
|
+
:return: A list of the layers with the given type
|
|
64
|
+
"""
|
|
65
|
+
layers = [layer.name for layer in self.viewer.layers if isinstance(layer, layerType)]
|
|
66
|
+
return layers
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def getDataOfLayerWithName(self, name):
|
|
70
|
+
""" Return the data of the layer with the given name.
|
|
71
|
+
|
|
72
|
+
:param name: The name of the layer
|
|
73
|
+
:type name: str
|
|
74
|
+
:return: The layer with the given name if it exists and None otherwise
|
|
75
|
+
"""
|
|
76
|
+
layer = self.getLayerWithName(name)
|
|
77
|
+
if layer:
|
|
78
|
+
return layer.data
|
|
79
|
+
else:
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def getLayerWithName(self, name):
|
|
84
|
+
""" Answer the layer with the given name, if it exists and None otherwise.
|
|
85
|
+
|
|
86
|
+
:param name: The name of a layer
|
|
87
|
+
:return: The layer from napari's layer list that has the given name
|
|
88
|
+
"""
|
|
89
|
+
for layer in self.viewer.layers:
|
|
90
|
+
if layer.name == name:
|
|
91
|
+
return layer
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def getDataAndScaleOfLayerWithName(self, name):
|
|
96
|
+
""" Answer the data, the scale and the unit of the layer with the given name.
|
|
97
|
+
|
|
98
|
+
:param name: The name of a layer
|
|
99
|
+
:return: A tupel with the data, the scale and the unit of the layer with the given name.
|
|
100
|
+
The unit is the unit of the first dimension. The unit is supposed to be the same for all dimensions.
|
|
101
|
+
"""
|
|
102
|
+
layer = self.getLayerWithName(name)
|
|
103
|
+
return layer.data, layer.scale, str(layer.units[0])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def getOriginalPath(layer):
|
|
108
|
+
""" Answer the source path of the layer if it represents an image opened from the filesystem and the
|
|
109
|
+
original path from the metadata if it is a derived image. If it is neither an image opened from a file nor
|
|
110
|
+
derived from one (for example an image programmatically created) answer None. For the metadata information to
|
|
111
|
+
be present, the image operations must set it.
|
|
112
|
+
|
|
113
|
+
:param layer: The input layer
|
|
114
|
+
:return: The path of the image in the filesystem if it is known
|
|
115
|
+
"""
|
|
116
|
+
if 'original_path' in layer.metadata.keys():
|
|
117
|
+
return layer.metadata['original_path']
|
|
118
|
+
if layer.source.path:
|
|
119
|
+
return layer.source.path
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def copyOriginalPath(srcLayer, destLayer):
|
|
125
|
+
"""Copy the orignal path from one layer to another. This should be used by operations that create images derived
|
|
126
|
+
from an input image.
|
|
127
|
+
|
|
128
|
+
:param srcLayer: The layer from which the original path is copied.
|
|
129
|
+
:param destLayer: The layer to which the original path is copied.
|
|
130
|
+
"""
|
|
131
|
+
path = NapariUtil.getOriginalPath(srcLayer)
|
|
132
|
+
destLayer.metadata['original_path'] = path
|
|
133
|
+
|