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
autooptions/widget.py
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
from qtpy.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton
|
|
2
|
+
from napari.utils.events import Event
|
|
3
|
+
from qtpy.QtWidgets import QWidget
|
|
4
|
+
from autooptions.qtutil import WidgetTool
|
|
5
|
+
from autooptions.napari_util import NapariUtil
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OptionsWidget(QWidget):
|
|
10
|
+
"""
|
|
11
|
+
Automatically creates a widget from an options object. The option values can be changed in the dialog.
|
|
12
|
+
When the Apply or the Ok-button is pressed the values are copied from the dialog to the options object and saved
|
|
13
|
+
to the options file.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, viewer, options, client=None):
|
|
17
|
+
"""
|
|
18
|
+
Create a new options widget. Layer add and remove events are caught and
|
|
19
|
+
the combo-boxes are updated accordingly, depending on the layer types.
|
|
20
|
+
|
|
21
|
+
:param viewer: The napari viewer
|
|
22
|
+
:param options: The options from which the dialog is created
|
|
23
|
+
:param client: The client that handles callbacks when option values change
|
|
24
|
+
"""
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.setWindowTitle(options.optionsName)
|
|
27
|
+
self.viewer = viewer
|
|
28
|
+
self.client = client
|
|
29
|
+
self.options = options
|
|
30
|
+
self.fieldWidth = 50
|
|
31
|
+
self.napariUtil = NapariUtil(self.viewer)
|
|
32
|
+
self.imageLayers = self.napariUtil.getImageLayers()
|
|
33
|
+
self.fftLayers = self.napariUtil.getFFTLayers()
|
|
34
|
+
self.labelLayers = self.napariUtil.getLabelLayers()
|
|
35
|
+
self.pointLayers = self.napariUtil.getPointsLayers()
|
|
36
|
+
self.mainLayout = None
|
|
37
|
+
self.buttonsLayout = None
|
|
38
|
+
self.buttons = {}
|
|
39
|
+
self.imageComboBoxes = []
|
|
40
|
+
self.fftComboBoxes = []
|
|
41
|
+
self.labelComboBoxes = []
|
|
42
|
+
self.pointComboBoxes = []
|
|
43
|
+
self.widgets = {}
|
|
44
|
+
self.viewer.layers.events.inserted.connect(self._onLayerAddedOrRemoved)
|
|
45
|
+
self.viewer.layers.events.removed.connect(self._onLayerAddedOrRemoved)
|
|
46
|
+
self.input_layer = None
|
|
47
|
+
self._createLayout()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def addApplyButton(self, callback):
|
|
51
|
+
"""
|
|
52
|
+
Add an apply button to the options widget. When the button is pressed the value are copied from the dialog
|
|
53
|
+
to the options object. The dialog remains open.
|
|
54
|
+
|
|
55
|
+
:param callback: The client can register a callback that is called when the apply button is pressed
|
|
56
|
+
"""
|
|
57
|
+
button = self._createApplyButton(callback)
|
|
58
|
+
self.buttons['Apply'] = button
|
|
59
|
+
self._getButtonsLayout().addWidget(button)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def addOKButton(self, callback):
|
|
63
|
+
"""
|
|
64
|
+
Add an ok button to the options widget. When the button is pressed the value are copied from the dialog
|
|
65
|
+
to the options object. The dialog is closed.
|
|
66
|
+
|
|
67
|
+
:param callback: The client can register a callback that is called when the ok button is pressed
|
|
68
|
+
"""
|
|
69
|
+
button = self._createOKButton(callback)
|
|
70
|
+
self.buttons['Ok'] = button
|
|
71
|
+
self._getButtonsLayout().addWidget(button)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def addCancelButton(self, callback):
|
|
75
|
+
"""
|
|
76
|
+
Add a cancel button to the options widget. When the button is pressed the dialog is closed, the values in the
|
|
77
|
+
dialog are not copied to the options object.
|
|
78
|
+
|
|
79
|
+
:param callback: The client can register a callback that is called when the ok button is pressed
|
|
80
|
+
"""
|
|
81
|
+
button = self._createCancelButton(callback)
|
|
82
|
+
self.buttons['Cancel'] = button
|
|
83
|
+
self._getButtonsLayout().addWidget(button)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def getApplyButton(self):
|
|
87
|
+
"""
|
|
88
|
+
Answers the apply button if it exists and None otherwise.
|
|
89
|
+
"""
|
|
90
|
+
if 'Apply' in self.buttons.keys():
|
|
91
|
+
return self.buttons['Apply']
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def getOKButton(self):
|
|
96
|
+
"""
|
|
97
|
+
Answers the ok button if it exists and None otherwise.
|
|
98
|
+
"""
|
|
99
|
+
if 'Ok' in self.buttons.keys():
|
|
100
|
+
return self.buttons['Ok']
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def getCancelButton(self):
|
|
105
|
+
"""
|
|
106
|
+
Answers the cancel button if it exists and None otherwise.
|
|
107
|
+
"""
|
|
108
|
+
if 'Cancel' in self.buttons.keys():
|
|
109
|
+
return self.buttons['Cancel']
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def shut(self):
|
|
114
|
+
"""
|
|
115
|
+
Shut down the options widget. Removes the dock-widget from the window and closes the widget.
|
|
116
|
+
"""
|
|
117
|
+
self.viewer.window.remove_dock_widget(self)
|
|
118
|
+
self.close()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def getImageLayer(self, name):
|
|
122
|
+
"""
|
|
123
|
+
Answer the image layer that has the name of the value of the option with the given name. The option must be
|
|
124
|
+
a layer selection option (image, labels, points, fft).
|
|
125
|
+
|
|
126
|
+
:param name: The name of an option
|
|
127
|
+
:return: The layer that has the name corresponding to the value of the option
|
|
128
|
+
"""
|
|
129
|
+
layer = self.napariUtil.getLayerWithName(self.options.get(name)['value'])
|
|
130
|
+
return layer
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _callbackFor(self, name):
|
|
134
|
+
func = None
|
|
135
|
+
if self.client and name:
|
|
136
|
+
func = getattr(self.client, name)
|
|
137
|
+
return func
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _createLayout(self):
|
|
141
|
+
self.mainLayout = QVBoxLayout()
|
|
142
|
+
for name, item in self.options.items.items():
|
|
143
|
+
widget = None
|
|
144
|
+
if item['type'] == 'image':
|
|
145
|
+
layout, widget = self._getImageWidget(name, item)
|
|
146
|
+
self.mainLayout.addLayout(layout)
|
|
147
|
+
self.imageComboBoxes.append(widget)
|
|
148
|
+
if item['type'] == 'labels':
|
|
149
|
+
layout, widget = self._getLabelsWidget(name, item)
|
|
150
|
+
self.mainLayout.addLayout(layout)
|
|
151
|
+
self.labelComboBoxes.append(widget)
|
|
152
|
+
if item['type'] == 'points':
|
|
153
|
+
layout, widget = self._getPointsWidget(name, item)
|
|
154
|
+
self.mainLayout.addLayout(layout)
|
|
155
|
+
self.pointComboBoxes.append(widget)
|
|
156
|
+
if item['type'] == 'fft':
|
|
157
|
+
layout, widget = self._getFFTWidget(name, item)
|
|
158
|
+
self.mainLayout.addLayout(layout)
|
|
159
|
+
self.fftComboBoxes.append(widget)
|
|
160
|
+
if item['type'] == 'int':
|
|
161
|
+
layout, widget = self._getIntWidget(name, item)
|
|
162
|
+
self.mainLayout.addLayout(layout)
|
|
163
|
+
if item['type'] == 'float':
|
|
164
|
+
layout, widget = self._getFloatWidget(name, item)
|
|
165
|
+
self.mainLayout.addLayout(layout)
|
|
166
|
+
if item['type'] == 'choice':
|
|
167
|
+
layout, widget = self._getChoiceWidget(name, item)
|
|
168
|
+
self.mainLayout.addLayout(layout)
|
|
169
|
+
if item['type'] == 'str':
|
|
170
|
+
layout, widget = self._getStrWidget(name, item)
|
|
171
|
+
self.mainLayout.addLayout(layout)
|
|
172
|
+
if item['type'] == 'bool':
|
|
173
|
+
layout, widget = self._getBoolWidget(name, item)
|
|
174
|
+
self.mainLayout.addLayout(layout)
|
|
175
|
+
self.widgets[name] = widget
|
|
176
|
+
self.setLayout(self.mainLayout)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _getImageWidget(self, name, item):
|
|
180
|
+
layout = QHBoxLayout()
|
|
181
|
+
self.imageLayers = self.napariUtil.getImageLayers()
|
|
182
|
+
label, widget = WidgetTool.getComboInput(self,
|
|
183
|
+
name +":",
|
|
184
|
+
self.imageLayers,
|
|
185
|
+
callback=self._callbackFor(item['callback']))
|
|
186
|
+
layout.addWidget(label)
|
|
187
|
+
layout.addWidget(widget)
|
|
188
|
+
return layout, widget
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _getLabelsWidget(self, name, item):
|
|
192
|
+
layout = QHBoxLayout()
|
|
193
|
+
self.labelLayers = self.napariUtil.getLabelLayers()
|
|
194
|
+
label, widget = WidgetTool.getComboInput(self,
|
|
195
|
+
name +":",
|
|
196
|
+
self.labelLayers,
|
|
197
|
+
callback=self._callbackFor(item['callback']))
|
|
198
|
+
layout.addWidget(label)
|
|
199
|
+
layout.addWidget(widget)
|
|
200
|
+
return layout, widget
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _getPointsWidget(self, name, item):
|
|
204
|
+
layout = QHBoxLayout()
|
|
205
|
+
self.pointsLayers = self.napariUtil.getPointsLayers()
|
|
206
|
+
label, widget = WidgetTool.getComboInput(self,
|
|
207
|
+
name +":",
|
|
208
|
+
self.pointsLayers,
|
|
209
|
+
callback=self._callbackFor(item['callback']))
|
|
210
|
+
layout.addWidget(label)
|
|
211
|
+
layout.addWidget(widget)
|
|
212
|
+
return layout, widget
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _getFFTWidget(self, name, item):
|
|
216
|
+
layout = QHBoxLayout()
|
|
217
|
+
self.fftLayers = self.napariUtil.getFFTLayers()
|
|
218
|
+
label, widget = WidgetTool.getComboInput(self,
|
|
219
|
+
name +":",
|
|
220
|
+
self.fftLayers,
|
|
221
|
+
callback=self._callbackFor(item['callback']))
|
|
222
|
+
layout.addWidget(label)
|
|
223
|
+
layout.addWidget(widget)
|
|
224
|
+
return layout, widget
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _getIntWidget(self, name, item):
|
|
228
|
+
layout = QHBoxLayout()
|
|
229
|
+
label, widget = WidgetTool.getLineInput(self,
|
|
230
|
+
name +":",
|
|
231
|
+
item['value'],
|
|
232
|
+
self.fieldWidth,
|
|
233
|
+
self._callbackFor(item['callback']))
|
|
234
|
+
layout.addWidget(label)
|
|
235
|
+
layout.addWidget(widget)
|
|
236
|
+
return layout, widget
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _getFloatWidget(self, name, item):
|
|
240
|
+
layout = QHBoxLayout()
|
|
241
|
+
label, widget = WidgetTool.getLineInput(self,
|
|
242
|
+
name +":",
|
|
243
|
+
item['value'],
|
|
244
|
+
self.fieldWidth,
|
|
245
|
+
self._callbackFor(item['callback']))
|
|
246
|
+
layout.addWidget(label)
|
|
247
|
+
layout.addWidget(widget)
|
|
248
|
+
return layout, widget
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _getStrWidget(self, name, item):
|
|
252
|
+
layout = QHBoxLayout()
|
|
253
|
+
label, widget = WidgetTool.getLineInput(self,
|
|
254
|
+
name +":",
|
|
255
|
+
item['value'],
|
|
256
|
+
self.fieldWidth,
|
|
257
|
+
self._callbackFor(item['callback']))
|
|
258
|
+
layout.addWidget(label)
|
|
259
|
+
layout.addWidget(widget)
|
|
260
|
+
return layout, widget
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _getChoiceWidget(self, name, item):
|
|
264
|
+
layout = QHBoxLayout()
|
|
265
|
+
label, widget = WidgetTool.getComboInput(self,
|
|
266
|
+
name +":",
|
|
267
|
+
item['choices'],
|
|
268
|
+
callback=self._callbackFor(item['callback']))
|
|
269
|
+
widget.setCurrentText(item['value'])
|
|
270
|
+
layout.addWidget(label)
|
|
271
|
+
layout.addWidget(widget)
|
|
272
|
+
return layout, widget
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _getBoolWidget(self, name, item):
|
|
276
|
+
layout = QHBoxLayout()
|
|
277
|
+
label, widget = WidgetTool.getCheckbox(self,
|
|
278
|
+
name,
|
|
279
|
+
item['value'],
|
|
280
|
+
self.fieldWidth,
|
|
281
|
+
self._callbackFor(item['callback']))
|
|
282
|
+
layout.addWidget(label)
|
|
283
|
+
layout.addWidget(widget)
|
|
284
|
+
return layout, widget
|
|
285
|
+
|
|
286
|
+
def _getButtonsLayout(self):
|
|
287
|
+
if not self.buttonsLayout:
|
|
288
|
+
self.buttonsLayout = QHBoxLayout()
|
|
289
|
+
self.mainLayout.addLayout(self.buttonsLayout)
|
|
290
|
+
return self.buttonsLayout
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _createApplyButton(self, callback):
|
|
294
|
+
button = self._createButton("&Apply", callback)
|
|
295
|
+
button.clicked.connect(self._onApplyButtonClicked)
|
|
296
|
+
return button
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _createOKButton(self, callback):
|
|
300
|
+
button = self._createButton("&Ok", callback)
|
|
301
|
+
button.clicked.connect(self._onOKButtonClicked)
|
|
302
|
+
return button
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _createCancelButton(self, callback):
|
|
306
|
+
button = self._createButton("&Cancel", callback)
|
|
307
|
+
button.clicked.connect(self._onCancelButtonClicked)
|
|
308
|
+
return button
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@classmethod
|
|
312
|
+
def _createButton(cls, name, callback):
|
|
313
|
+
button = QPushButton(name)
|
|
314
|
+
if callback:
|
|
315
|
+
button.clicked.connect(callback)
|
|
316
|
+
return button
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _onApplyButtonClicked(self):
|
|
320
|
+
self._transferValues()
|
|
321
|
+
self.options.save()
|
|
322
|
+
self.options.load()
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _onOKButtonClicked(self):
|
|
326
|
+
self._transferValues()
|
|
327
|
+
self.options.save()
|
|
328
|
+
self.options.load()
|
|
329
|
+
self.shut()
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _onCancelButtonClicked(self):
|
|
333
|
+
self.shut()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _transferValues(self):
|
|
337
|
+
for name, item in self.options.items.items():
|
|
338
|
+
widget = self.widgets[name]
|
|
339
|
+
if item['type'] in ['image', 'choice', 'fft']:
|
|
340
|
+
text = widget.currentText()
|
|
341
|
+
item['value'] = text
|
|
342
|
+
if item['type'] == 'int':
|
|
343
|
+
item['value'] = int(widget.text().strip())
|
|
344
|
+
if item['type'] == 'float':
|
|
345
|
+
item['value'] = float(widget.text().strip())
|
|
346
|
+
if item['type'] == 'str':
|
|
347
|
+
item['value'] = widget.text()
|
|
348
|
+
if item['type'] == 'bool':
|
|
349
|
+
item['value'] = widget.isChecked()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _onLayerAddedOrRemoved(self, event: Event):
|
|
353
|
+
self._updateLayerSelectionComboBoxes()
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _updateLayerSelectionComboBoxes(self):
|
|
357
|
+
imageLayers = self.napariUtil.getImageLayers()
|
|
358
|
+
labelLayers = self.napariUtil.getLabelLayers()
|
|
359
|
+
pointLayers = self.napariUtil.getPointsLayers()
|
|
360
|
+
fftLayers = self.napariUtil.getFFTLayers()
|
|
361
|
+
for comboBox in self.imageComboBoxes:
|
|
362
|
+
WidgetTool.replaceItemsInComboBox(comboBox, imageLayers)
|
|
363
|
+
for comboBox in self.labelComboBoxes:
|
|
364
|
+
WidgetTool.replaceItemsInComboBox(comboBox, labelLayers)
|
|
365
|
+
for comboBox in self.pointComboBoxes:
|
|
366
|
+
WidgetTool.replaceItemsInComboBox(comboBox, pointLayers)
|
|
367
|
+
for comboBox in self.fftComboBoxes:
|
|
368
|
+
WidgetTool.replaceItemsInComboBox(comboBox, fftLayers)
|
|
369
|
+
|
|
370
|
+
|
scratch/options.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import napari
|
|
2
|
+
from autooptions.options import Options
|
|
3
|
+
from autooptions.widget import OptionsWidget
|
|
4
|
+
|
|
5
|
+
class Client:
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.viewer = napari.Viewer()
|
|
9
|
+
self.options = None
|
|
10
|
+
self.widget = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def onInputChanged(self, value):
|
|
14
|
+
if not value.isnumeric():
|
|
15
|
+
self.widget.widgets["size xy"].setText(str(self.options.value("size xy")))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def showOptions(self):
|
|
19
|
+
# Create a Qt widget, which will be our window.
|
|
20
|
+
self.options = Options("Test_Auto_Options", "Median Filter")
|
|
21
|
+
self.options.addImage('image', value=None, transient=True)
|
|
22
|
+
self.options.addInt('size xy', value=3, widget="input", callback=self.onInputChanged)
|
|
23
|
+
self.options.addInt('size z', value=1, widget="input")
|
|
24
|
+
self.options.addChoice('footprint', value=None, choices=["none", "cube", "ball", "octahedron"])
|
|
25
|
+
self.options.addInt('radius', value=1, widget="input")
|
|
26
|
+
self.options.load()
|
|
27
|
+
self.widget = OptionsWidget(self.viewer, self.options, self)
|
|
28
|
+
self.widget.addApplyButton(None)
|
|
29
|
+
#self.widget.addOKButton(None)
|
|
30
|
+
#self.widget.addCancelButton(None)
|
|
31
|
+
self.viewer.window.add_dock_widget(self.widget, name=self.options.optionsName)
|
|
32
|
+
self.viewer.show()
|
|
33
|
+
napari.run()
|
|
34
|
+
|
|
35
|
+
client = Client()
|
|
36
|
+
client.showOptions()
|