auto-options-python 0.1.6__tar.gz → 0.1.7__tar.gz

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.
Files changed (47) hide show
  1. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/PKG-INFO +3 -5
  2. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/pyproject.toml +3 -4
  3. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/auto_options_python.egg-info/PKG-INFO +3 -5
  4. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/auto_options_python.egg-info/SOURCES.txt +5 -0
  5. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/auto_options_python.egg-info/requires.txt +0 -1
  6. auto_options_python-0.1.7/src/autooptions/_tests/test_layouts.py +80 -0
  7. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_tests/test_options.py +2 -1
  8. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_tests/test_qtutil.py +39 -75
  9. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_tests/test_widget.py +59 -41
  10. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_version.py +3 -3
  11. auto_options_python-0.1.7/src/autooptions/layouts/__init__.py +29 -0
  12. auto_options_python-0.1.7/src/autooptions/layouts/base_layout.py +81 -0
  13. auto_options_python-0.1.7/src/autooptions/layouts/grid_layout.py +46 -0
  14. auto_options_python-0.1.7/src/autooptions/layouts/vertical_layout.py +46 -0
  15. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/options.py +250 -89
  16. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/qtutil.py +130 -64
  17. auto_options_python-0.1.7/src/autooptions/widget.py +427 -0
  18. auto_options_python-0.1.6/src/autooptions/widget.py +0 -382
  19. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/.github/workflows/create_doc.yml +0 -0
  20. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/.github/workflows/test_and_deploy.yml +0 -0
  21. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/.gitignore +0 -0
  22. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/LICENSE +0 -0
  23. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/README.md +0 -0
  24. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/Makefile +0 -0
  25. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/make.bat +0 -0
  26. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/auto-options-python.rst +0 -0
  27. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/autooptions.array_util.rst +0 -0
  28. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/autooptions.napari_util.rst +0 -0
  29. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/autooptions.options.rst +0 -0
  30. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/autooptions.qtutil.rst +0 -0
  31. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/autooptions.widget.rst +0 -0
  32. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/conf.py +0 -0
  33. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/index.rst +0 -0
  34. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/modules.rst +0 -0
  35. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/user/installation.rst +0 -0
  36. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/user/introduction.rst +0 -0
  37. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/docs/source/user/user_documentation.rst +0 -0
  38. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/setup.cfg +0 -0
  39. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/auto_options_python.egg-info/dependency_links.txt +0 -0
  40. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/auto_options_python.egg-info/top_level.txt +0 -0
  41. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/__init__.py +0 -0
  42. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_tests/__init__.py +0 -0
  43. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_tests/test_array_util.py +0 -0
  44. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/_tests/test_napari_util.py +0 -0
  45. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/array_util.py +0 -0
  46. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/autooptions/napari_util.py +0 -0
  47. {auto_options_python-0.1.6 → auto_options_python-0.1.7}/src/scratch/options.py +0 -0
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: auto-options-python
3
- Version: 0.1.6
4
- Summary: Mange command options in python and create QWidgets for them.
5
- Author: Volker Baecker
6
- Author-email: volker.baecker@mri.cnrs.fr
3
+ Version: 0.1.7
4
+ Summary: Manage command options in Python and create QWidgets for them.
5
+ Author-email: Volker Baecker <volker.baecker@mri.cnrs.fr>, Clément Benedetti <clement.benedetti@mri.cnrs.fr>
7
6
  License: MIT License
8
7
 
9
8
  Copyright (c) 2025 Montpellier Ressources Imagerie
@@ -47,7 +46,6 @@ Requires-Python: >=3.10
47
46
  Description-Content-Type: text/markdown
48
47
  License-File: LICENSE
49
48
  Requires-Dist: appdirs
50
- Requires-Dist: pyqt5
51
49
  Requires-Dist: qtpy
52
50
  Requires-Dist: napari
53
51
  Requires-Dist: pyperclip
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "auto-options-python"
3
3
  dynamic = ["version"]
4
- description = "Mange command options in python and create QWidgets for them."
4
+ description = "Manage command options in Python and create QWidgets for them."
5
5
  readme = "README.md"
6
6
  license = {file = "LICENSE"}
7
7
  authors = [
8
- {name = "Volker Baecker"},
9
- {email = "volker.baecker@mri.cnrs.fr"},
8
+ {name = "Volker Baecker", email = "volker.baecker@mri.cnrs.fr"},
9
+ {name = "Clément Benedetti", email = "clement.benedetti@mri.cnrs.fr"}
10
10
  ]
11
11
  classifiers = [
12
12
  "Development Status :: 4 - Beta",
@@ -30,7 +30,6 @@ requires-python = ">=3.10"
30
30
  # See best practices: https://napari.org/stable/plugins/building_a_plugin/best_practices.html
31
31
  dependencies = [
32
32
  "appdirs",
33
- "pyqt5",
34
33
  "qtpy",
35
34
  "napari",
36
35
  "pyperclip",
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: auto-options-python
3
- Version: 0.1.6
4
- Summary: Mange command options in python and create QWidgets for them.
5
- Author: Volker Baecker
6
- Author-email: volker.baecker@mri.cnrs.fr
3
+ Version: 0.1.7
4
+ Summary: Manage command options in Python and create QWidgets for them.
5
+ Author-email: Volker Baecker <volker.baecker@mri.cnrs.fr>, Clément Benedetti <clement.benedetti@mri.cnrs.fr>
7
6
  License: MIT License
8
7
 
9
8
  Copyright (c) 2025 Montpellier Ressources Imagerie
@@ -47,7 +46,6 @@ Requires-Python: >=3.10
47
46
  Description-Content-Type: text/markdown
48
47
  License-File: LICENSE
49
48
  Requires-Dist: appdirs
50
- Requires-Dist: pyqt5
51
49
  Requires-Dist: qtpy
52
50
  Requires-Dist: napari
53
51
  Requires-Dist: pyperclip
@@ -32,8 +32,13 @@ src/autooptions/qtutil.py
32
32
  src/autooptions/widget.py
33
33
  src/autooptions/_tests/__init__.py
34
34
  src/autooptions/_tests/test_array_util.py
35
+ src/autooptions/_tests/test_layouts.py
35
36
  src/autooptions/_tests/test_napari_util.py
36
37
  src/autooptions/_tests/test_options.py
37
38
  src/autooptions/_tests/test_qtutil.py
38
39
  src/autooptions/_tests/test_widget.py
40
+ src/autooptions/layouts/__init__.py
41
+ src/autooptions/layouts/base_layout.py
42
+ src/autooptions/layouts/grid_layout.py
43
+ src/autooptions/layouts/vertical_layout.py
39
44
  src/scratch/options.py
@@ -0,0 +1,80 @@
1
+ import pytest
2
+ import os
3
+ import numpy as np
4
+
5
+ from autooptions.widget import OptionsWidget
6
+ from autooptions.options import Options
7
+ from autooptions.layouts import LayoutFactory
8
+
9
+
10
+ class TestLayoutUtils:
11
+
12
+ @staticmethod
13
+ def getLayoutsList():
14
+ return list(LayoutFactory.availableLayouts.keys())
15
+
16
+
17
+ class TestLayouts:
18
+
19
+ def onSomethingHappened(self, value):
20
+ self.lastValue = value
21
+
22
+ @pytest.fixture()
23
+ def options(self):
24
+ options = Options("Autooptions Test", "Layouts Test")
25
+ options.addImage("image", value=None, transient=True, optional=(True, False))
26
+ options.addLabels("labels", value="labels_layer", transient=True)
27
+ options.addPoints("points", value="points_layer", transient=True)
28
+ options.addFFT("fft", value=None, transient=True)
29
+ options.addStr("group", value="lps", optional=(True, True))
30
+ options.addInt("size xy", value=3)
31
+ options.addInt("size z", value=1)
32
+ options.addChoice(
33
+ "footprint",
34
+ choices=["none", "cube", "ball", "octahedron"],
35
+ callback=self.onSomethingHappened,
36
+ )
37
+ options.addFloat("sigma", value=1.34)
38
+ options.addBool("do it", value=True)
39
+ yield options
40
+
41
+ @pytest.mark.parametrize("layout_type", TestLayoutUtils.getLayoutsList())
42
+ def testLayoutInstanciation(self, options, make_napari_viewer_proxy, layout_type):
43
+ viewer = make_napari_viewer_proxy()
44
+ widget = OptionsWidget(viewer, options, layout_type=layout_type, client=self)
45
+ assert widget.options is options
46
+ assert widget.viewer is viewer
47
+ assert widget.client is self
48
+ assert type(widget.layout()) is LayoutFactory.availableLayouts[layout_type]
49
+
50
+ @pytest.mark.parametrize("layout_type", TestLayoutUtils.getLayoutsList())
51
+ @pytest.mark.parametrize("same_row", [None, [], ["labels", "points"], ["size z"]])
52
+ def testLayoutLength(
53
+ self, options, make_napari_viewer_proxy, layout_type, same_row
54
+ ):
55
+ viewer = make_napari_viewer_proxy()
56
+ widget = OptionsWidget(
57
+ viewer, options, layout_type=layout_type, sameRowSet=same_row, client=self
58
+ )
59
+ n_widgets = len(options)
60
+ expected = n_widgets - (len(same_row) if same_row else 0)
61
+ assert widget.layout().size() == expected
62
+
63
+ @pytest.mark.parametrize("field_width", [50, 300])
64
+ def testLayoutSizingFixed(self, options, make_napari_viewer_proxy, field_width):
65
+ viewer = make_napari_viewer_proxy()
66
+ args = {"name": "vertical", "max_width": field_width}
67
+ widget = OptionsWidget(viewer, options, layout_type=args, client=self)
68
+ for _, w in widget.widgets.values():
69
+ assert w.maximumWidth() == field_width
70
+
71
+ @pytest.mark.parametrize("layout_type", TestLayoutUtils.getLayoutsList())
72
+ def testWidgetsHierarchy(self, options, make_napari_viewer_proxy, layout_type):
73
+ viewer = make_napari_viewer_proxy()
74
+ widget = OptionsWidget(viewer, options, layout_type=layout_type, client=self)
75
+ for cb, w in widget.widgets.values():
76
+ assert w.parent() is widget
77
+ assert w in widget.children()
78
+ if cb is not None:
79
+ assert cb.parent() is widget
80
+ assert cb in widget.children()
@@ -157,11 +157,12 @@ class TestOptions:
157
157
 
158
158
 
159
159
  def testGetBaseOption(self, options):
160
- option = options.getBaseOption(7, True, None, self.onSomething)
160
+ option = options._getBaseOption(7, True, None, self.onSomething, [True, False])
161
161
  assert option["value"] == 7
162
162
  assert option["transient"] == True
163
163
  assert option["position"] == 6
164
164
  assert option["callback"] == "onSomething"
165
+ assert option["optional"] == [True, False]
165
166
 
166
167
 
167
168
  def test_GetPosition(self, options):
@@ -7,21 +7,16 @@ from autooptions.qtutil import WidgetTool
7
7
  from autooptions.qtutil import TableView
8
8
  from autooptions.qtutil import PlotWidget
9
9
 
10
-
11
-
12
10
  COUNTER_TEXT_CHANGED = 0
13
11
 
14
12
 
15
-
16
13
  class ParentWidget(QWidget):
17
14
 
18
-
19
15
  def __init__(self):
20
16
  super().__init__()
21
17
  self.setWindowTitle("Test")
22
18
 
23
19
 
24
-
25
20
  def handleTextChanged(text):
26
21
  global COUNTER_TEXT_CHANGED
27
22
  COUNTER_TEXT_CHANGED = COUNTER_TEXT_CHANGED + 1
@@ -29,57 +24,38 @@ def handleTextChanged(text):
29
24
 
30
25
  def testGetLineInput(make_napari_viewer_proxy):
31
26
  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
27
+ label, inputWidget, cbActive = WidgetTool.getLineInput(
28
+ "your guess", 10, callback=handleTextChanged
29
+ )
42
30
  assert label.text() == "your guess"
43
31
  counter = COUNTER_TEXT_CHANGED
44
32
  assert inputWidget.text() == str(10)
45
33
  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
34
+ # Editing in the interface -> sends the signal
35
+ # Programmatically changing the value -> does not send the signal
36
+ assert COUNTER_TEXT_CHANGED == counter
37
+ assert cbActive is None
49
38
 
50
39
 
51
40
  def testGetLineInputNoCallback(make_napari_viewer_proxy):
52
41
  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
42
+ label, inputWidget, cbActive = WidgetTool.getLineInput("your guess", 10)
63
43
  assert label.text() == "your guess"
64
44
  counter = COUNTER_TEXT_CHANGED
65
45
  assert inputWidget.text() == str(10)
66
46
  inputWidget.setText("20")
67
- assert inputWidget.maximumWidth() == 50
68
47
  assert COUNTER_TEXT_CHANGED == counter
48
+ assert cbActive is None # by default, options are not optional
69
49
 
70
50
 
71
51
  def testGetComboBox(make_napari_viewer_proxy, mocker):
72
52
  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
53
+ callback = mocker.stub(name="onSelectedFruitChanged")
54
+ label, comboWidget, _ = WidgetTool.getComboInput(
55
+ "fruits",
56
+ ["apple", "orange", "banana"],
57
+ callback=callback
58
+ )
83
59
  assert label.text() == "fruits"
84
60
  allItems = [comboWidget.itemText(i) for i in range(comboWidget.count())]
85
61
  assert allItems == ["apple", "orange", "banana"]
@@ -89,12 +65,12 @@ def testGetComboBox(make_napari_viewer_proxy, mocker):
89
65
  callback.assert_called_once_with("banana")
90
66
 
91
67
 
92
- def testReplaceItemsInComboBox(make_napari_viewer_proxy, mocker):
68
+ def testReplaceItemsInComboBox(make_napari_viewer_proxy):
93
69
  make_napari_viewer_proxy()
94
- parent = ParentWidget()
95
- label, comboWidget = WidgetTool.getComboInput(parent,
96
- "fruits",
97
- ["apple", "orange", "banana"])
70
+ _, comboWidget, _ = WidgetTool.getComboInput(
71
+ "fruits",
72
+ ["apple", "orange", "banana"]
73
+ )
98
74
  WidgetTool.replaceItemsInComboBox(comboWidget, ["lemon", "ananas"])
99
75
  allItems = [comboWidget.itemText(i) for i in range(comboWidget.count())]
100
76
  assert allItems == ["lemon", "ananas"]
@@ -108,34 +84,22 @@ def testReplaceItemsInComboBox(make_napari_viewer_proxy, mocker):
108
84
 
109
85
  def testGetCheckBox(make_napari_viewer_proxy, mocker):
110
86
  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
87
+ callback = mocker.stub(name="onRemoveBackgroundChanged")
88
+ label, checkbox, _ = WidgetTool.getCheckbox(
89
+ "remove background",
90
+ True,
91
+ callback=callback
92
+ )
122
93
  assert label.text() == "remove background"
123
94
  assert checkbox.isChecked()
124
- assert checkbox.maximumWidth() == 50
125
95
  checkbox.setChecked(False)
126
96
  callback.assert_called_once_with(False)
127
- label2, checkbox2 = WidgetTool.getCheckbox(parent,
128
- "smooth",
129
- False,
130
- 50,
131
- None)
97
+ _, checkbox2, _ = WidgetTool.getCheckbox("smooth", False, callback=None)
132
98
  assert not checkbox2.isChecked()
133
99
 
134
100
 
135
-
136
101
  class TestTableView:
137
102
 
138
-
139
103
  def testConstructor(self, make_napari_viewer_proxy):
140
104
  make_napari_viewer_proxy()
141
105
  table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
@@ -144,7 +108,6 @@ class TestTableView:
144
108
  tableView2 = TableView(None)
145
109
  assert not tableView2.data
146
110
 
147
-
148
111
  def testSetData(self, make_napari_viewer_proxy):
149
112
  make_napari_viewer_proxy()
150
113
  table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
@@ -153,7 +116,6 @@ class TestTableView:
153
116
  tableView.setData(table2)
154
117
  assert tableView.data == table2
155
118
 
156
-
157
119
  def testResetView(self, make_napari_viewer_proxy):
158
120
  make_napari_viewer_proxy()
159
121
  table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
@@ -161,32 +123,39 @@ class TestTableView:
161
123
  tableView.resetView()
162
124
  assert tableView.data == table
163
125
 
164
-
165
126
  def testKeyPressEvent(self, make_napari_viewer_proxy):
166
127
  pyperclip.copy("")
167
128
  make_napari_viewer_proxy()
168
129
  table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
169
130
  tableView = TableView(table)
170
131
  tableView.selectAll()
171
- event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_C, Qt.KeyboardModifier.ControlModifier, "copy")
132
+ event = QKeyEvent(
133
+ QEvent.Type.KeyPress,
134
+ Qt.Key.Key_C,
135
+ Qt.KeyboardModifier.ControlModifier,
136
+ "copy",
137
+ )
172
138
  tableView.keyPressEvent(event)
173
139
  text = pyperclip.paste()
174
140
  assert "23.87" in text
175
141
  assert "24553" in text
176
142
 
177
-
178
143
  def testKeyPressEventNoCopy(self, make_napari_viewer_proxy):
179
144
  pyperclip.copy("")
180
145
  make_napari_viewer_proxy()
181
146
  table = {"area": [23.87, 65.28, 12.98], "mean": [19992, 2233, 24553]}
182
147
  tableView = TableView(table)
183
148
  tableView.selectAll()
184
- event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_C, Qt.KeyboardModifier.ShiftModifier, "copy")
149
+ event = QKeyEvent(
150
+ QEvent.Type.KeyPress,
151
+ Qt.Key.Key_C,
152
+ Qt.KeyboardModifier.ShiftModifier,
153
+ "copy",
154
+ )
185
155
  tableView.keyPressEvent(event)
186
156
  text = pyperclip.paste()
187
157
  assert text == ""
188
158
 
189
-
190
159
  def testCopyDataToClipboard(self, make_napari_viewer_proxy):
191
160
  pyperclip.copy("")
192
161
  make_napari_viewer_proxy()
@@ -204,7 +173,6 @@ class TestTableView:
204
173
 
205
174
  class TestPlotWidget:
206
175
 
207
-
208
176
  def testConstructor(self, make_napari_viewer_proxy):
209
177
  viewer = make_napari_viewer_proxy()
210
178
  plot = PlotWidget(viewer)
@@ -212,7 +180,6 @@ class TestPlotWidget:
212
180
  assert plot.X == []
213
181
  assert plot.Y == []
214
182
 
215
-
216
183
  def testAddData(self, make_napari_viewer_proxy):
217
184
  viewer = make_napari_viewer_proxy()
218
185
  plot = PlotWidget(viewer)
@@ -221,7 +188,6 @@ class TestPlotWidget:
221
188
  assert [1, 4, 9] in plot.Y
222
189
  assert "r+" in plot.formatStrings
223
190
 
224
-
225
191
  def testClearData(self, make_napari_viewer_proxy):
226
192
  viewer = make_napari_viewer_proxy()
227
193
  plot = PlotWidget(viewer)
@@ -229,7 +195,6 @@ class TestPlotWidget:
229
195
  plot.clear()
230
196
  assert True
231
197
 
232
-
233
198
  def testDisplay(self, make_napari_viewer_proxy):
234
199
  viewer = make_napari_viewer_proxy()
235
200
  plot = PlotWidget(viewer)
@@ -242,4 +207,3 @@ class TestPlotWidget:
242
207
  plot2.display()
243
208
  assert plot2.ax.get_xlabel() == plot2.xLabel
244
209
  assert plot2.ax.get_ylabel() == plot2.yLabel
245
-
@@ -4,129 +4,147 @@ import numpy as np
4
4
  from autooptions.widget import OptionsWidget
5
5
  from autooptions.options import Options
6
6
 
7
+
7
8
  class FakeEvent:
8
9
  modifiers = {}
9
10
 
10
11
 
11
12
  class TestOptionsWidget:
12
13
 
13
-
14
-
15
14
  def onSomethingHappened(self, value):
16
15
  self.lastValue = value
17
16
 
18
-
19
17
  @pytest.fixture()
20
18
  def options(self):
21
19
  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)
20
+ options.addImage("image", value=None, transient=True, optional=(True, False))
21
+ options.addLabels(
22
+ "labels",
23
+ value="labels_layer",
24
+ transient=True,
25
+ optional=(True, True)
26
+ )
27
+ options.addPoints(
28
+ "points",
29
+ value="points_layer",
30
+ transient=True,
31
+ optional=(False, False)
32
+ )
33
+ options.addFFT(
34
+ "fft",
35
+ value=None,
36
+ transient=True,
37
+ optional=(False, True)
38
+ )
26
39
  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)
40
+ options.addInt("size xy", value=3)
41
+ options.addInt("size z", value=1)
42
+ options.addChoice(
43
+ "footprint",
44
+ choices=["none", "cube", "ball", "octahedron"],
45
+ callback=self.onSomethingHappened,
46
+ )
47
+ options.addFloat("sigma", value=1.34)
48
+ options.addBool("do it", value=True)
32
49
  yield options
33
50
 
51
+ def testOptionsCheckboxes(self, options, make_napari_viewer_proxy):
52
+ viewer = make_napari_viewer_proxy()
53
+ widget = OptionsWidget(viewer, options, client=self)
54
+ viewer.window.add_dock_widget(widget, name=options.optionsName)
55
+ for name, (cb, _) in widget.widgets.items():
56
+ if options.isOptional(name):
57
+ assert cb is not None
58
+ else:
59
+ assert cb is None
34
60
 
35
61
  def testConstructor(self, options, make_napari_viewer_proxy):
36
62
  viewer = make_napari_viewer_proxy()
37
- widget = OptionsWidget(viewer, options, self)
63
+ widget = OptionsWidget(viewer, options, client=self)
38
64
  assert widget.options is options
39
65
  assert widget.viewer is viewer
40
66
  assert widget.client is self
41
67
 
42
-
43
68
  def testAddApplyButton(self, options, make_napari_viewer_proxy, mocker):
44
69
  viewer = make_napari_viewer_proxy()
45
- widget = OptionsWidget(viewer, options, self)
70
+ widget = OptionsWidget(viewer, options, client=self)
46
71
  assert widget.getApplyButton() is None
47
- callback = mocker.stub(name='onApplyButtonPressed')
72
+ callback = mocker.stub(name="onApplyButtonPressed")
48
73
  widget.addApplyButton(callback)
49
74
  assert not widget.getApplyButton() is None
50
75
  assert callback.call_count == 0
51
76
 
52
-
53
77
  def testAddOKButton(self, options, make_napari_viewer_proxy, mocker):
54
78
  viewer = make_napari_viewer_proxy()
55
- widget = OptionsWidget(viewer, options, self)
79
+ widget = OptionsWidget(viewer, options, client=self)
56
80
  assert widget.getOKButton() is None
57
- callback = mocker.stub(name='onOKButtonPressed')
81
+ callback = mocker.stub(name="onOKButtonPressed")
58
82
  widget.addOKButton(callback)
59
83
  assert not widget.getOKButton() is None
60
84
  assert callback.call_count == 0
61
85
 
62
-
63
86
  def testAddCancelButton(self, options, make_napari_viewer_proxy, mocker):
64
87
  viewer = make_napari_viewer_proxy()
65
- widget = OptionsWidget(viewer, options, self)
88
+ widget = OptionsWidget(viewer, options, client=self)
66
89
  assert widget.getCancelButton() is None
67
- callback = mocker.stub(name='onCancelButtonPressed')
90
+ callback = mocker.stub(name="onCancelButtonPressed")
68
91
  widget.addCancelButton(callback)
69
92
  assert not widget.getCancelButton() is None
70
93
  assert callback.call_count == 0
71
94
 
72
-
73
95
  def test_OnApplyButtonClicked(self, options, make_napari_viewer_proxy, mocker):
74
96
  viewer = make_napari_viewer_proxy()
75
- widget = OptionsWidget(viewer, options, self)
76
- callback = mocker.stub(name='self.apply')
97
+ widget = OptionsWidget(viewer, options, client=self)
98
+ callback = mocker.stub(name="self.apply")
77
99
  widget.addApplyButton(callback)
78
- options.setValue('group', 'xps')
100
+ options.setValue("group", "xps")
79
101
  if os.path.exists(options.optionsPath):
80
102
  os.remove(options.optionsPath)
81
103
  widget._onApplyButtonClicked()
82
104
  assert os.path.exists(options.optionsPath)
83
105
  assert options.value("group") == "lps"
84
106
 
85
-
86
107
  def test_OnOKButtonClicked(self, options, make_napari_viewer_proxy, mocker):
87
108
  viewer = make_napari_viewer_proxy()
88
- widget = OptionsWidget(viewer, options, self)
89
- callback = mocker.stub(name='self.ok')
109
+ widget = OptionsWidget(viewer, options, client=self)
110
+ callback = mocker.stub(name="self.ok")
90
111
  widget.addOKButton(callback)
91
112
  viewer.window.add_dock_widget(widget, name=options.optionsName)
92
- options.setValue('group', 'xps')
113
+ options.setValue("group", "xps")
93
114
  if os.path.exists(options.optionsPath):
94
115
  os.remove(options.optionsPath)
95
116
  widget._onOKButtonClicked()
96
117
  assert os.path.exists(options.optionsPath)
97
118
  assert options.value("group") == "lps"
98
119
 
99
-
100
120
  def test_OnCancelButtonClicked(self, options, make_napari_viewer_proxy, mocker):
101
121
  viewer = make_napari_viewer_proxy()
102
- widget = OptionsWidget(viewer, options, self)
103
- callback = mocker.stub(name='self.cancel')
122
+ widget = OptionsWidget(viewer, options, client=self)
123
+ callback = mocker.stub(name="self.cancel")
104
124
  widget.addCancelButton(callback)
105
125
  viewer.window.add_dock_widget(widget, name=options.optionsName)
106
- options.setValue('group', 'xps')
126
+ options.setValue("group", "xps")
107
127
  if os.path.exists(options.optionsPath):
108
128
  os.remove(options.optionsPath)
109
129
  widget._onCancelButtonClicked()
110
130
  assert not os.path.exists(options.optionsPath)
111
131
  assert options.value("group") == "xps"
112
132
 
113
-
114
133
  def test_OnLayerAddedOrRemoved(self, options, make_napari_viewer_proxy):
115
134
  viewer = make_napari_viewer_proxy()
116
- widget = OptionsWidget(viewer, options, self)
135
+ widget = OptionsWidget(viewer, options, client=self)
117
136
  viewer.window.add_dock_widget(widget, name=options.optionsName)
118
137
  data = np.random.rand(12, 12)
119
- viewer.add_image(data=data, name='data')
138
+ viewer.add_image(data=data, name="data")
120
139
  assert True
121
140
 
122
-
123
141
  def testGetImageLayer(self, options, make_napari_viewer_proxy):
124
142
  viewer = make_napari_viewer_proxy()
125
- widget = OptionsWidget(viewer, options, self)
143
+ widget = OptionsWidget(viewer, options, client=self)
126
144
  viewer.window.add_dock_widget(widget, name=options.optionsName)
127
145
  data = np.random.rand(12, 12)
128
- viewer.add_image(data=data, name='labels_layer')
146
+ viewer.add_image(data=data, name="labels_layer")
129
147
  layer = widget.getImageLayer("labels")
130
148
  assert layer is not None
131
149
  assert layer.name == "labels_layer"
132
- assert layer.data.shape == (12, 12)
150
+ assert layer.data.shape == (12, 12)
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.6'
22
- __version_tuple__ = version_tuple = (0, 1, 6)
21
+ __version__ = version = '0.1.7'
22
+ __version_tuple__ = version_tuple = (0, 1, 7)
23
23
 
24
- __commit_id__ = commit_id = 'g52deebf6d'
24
+ __commit_id__ = commit_id = 'g0de8a65d0'
@@ -0,0 +1,29 @@
1
+ from autooptions.layouts.grid_layout import GridLayout
2
+ from autooptions.layouts.vertical_layout import VerticalLayout
3
+
4
+
5
+ class LayoutFactory(object):
6
+
7
+ availableLayouts = {"grid": GridLayout, "vertical": VerticalLayout}
8
+
9
+ defaultLayout = GridLayout
10
+
11
+ def get(self, layout_type):
12
+ return self.availableLayouts.get(layout_type, self.defaultLayout)
13
+
14
+ @staticmethod
15
+ def createLayout(layout_type, same_row_set=None, parent=None):
16
+ if type(layout_type) is str:
17
+ layout_name = layout_type.lower()
18
+ layout_args = {}
19
+ elif type(layout_type) is dict:
20
+ layout_name = layout_type.get("name", "vertical").lower()
21
+ layout_args = {k: v for k, v in layout_type.items() if k != "name"}
22
+ else:
23
+ raise ValueError(f"Invalid layout_type: {layout_type}")
24
+ layoutConstructor = LayoutFactory.availableLayouts.get(
25
+ layout_name, LayoutFactory.defaultLayout
26
+ )
27
+ return layoutConstructor(
28
+ same_row_set=same_row_set, parent=parent, **layout_args
29
+ )