From e42668d62a9269a6513a58fb4eafa6d05d2703a1 Mon Sep 17 00:00:00 2001 From: Kimberly Meechan Date: Thu, 4 Apr 2024 11:43:43 +0100 Subject: [PATCH 1/4] expand interaction.py type hints, docstrings and tests --- brainglobe_utils/qtpy/interaction.py | 48 +++++++++++++-------- tests/tests/test_qtpy/test_interaction.py | 52 ++++++++++++++++++----- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/brainglobe_utils/qtpy/interaction.py b/brainglobe_utils/qtpy/interaction.py index a5ac1d8..a27f028 100644 --- a/brainglobe_utils/qtpy/interaction.py +++ b/brainglobe_utils/qtpy/interaction.py @@ -46,8 +46,16 @@ def add_button( def add_checkbox( - layout, default, label, row: int = 0, column: int = 0, tooltip=None -): + layout: QLayout, + default: bool, + label: str, + row: int = 0, + column: int = 0, + tooltip: Optional[str] = None, +) -> QCheckBox: + """ + Add a checkbox to *layout*. + """ box = QCheckBox() box.setChecked(default) if tooltip: @@ -58,16 +66,19 @@ def add_checkbox( def add_float_box( - layout, - default, - minimum, - maximum, - label, - step, + layout: QLayout, + default: float, + minimum: float, + maximum: float, + label: str, + step: float, row: int = 0, column: int = 0, - tooltip=None, -): + tooltip: Optional[str] = None, +) -> QDoubleSpinBox: + """ + Add a spin box for float values to *layout*. + """ box = QDoubleSpinBox() box.setMinimum(minimum) box.setMaximum(maximum) @@ -81,15 +92,18 @@ def add_float_box( def add_int_box( - layout, - default, - minimum, - maximum, - label, + layout: QLayout, + default: int, + minimum: int, + maximum: int, + label: str, row: int = 0, column: int = 0, - tooltip=None, -): + tooltip: Optional[str] = None, +) -> QSpinBox: + """ + Add a spin box for integer values to *layout*. + """ box = QSpinBox() box.setMinimum(minimum) box.setMaximum(maximum) diff --git a/tests/tests/test_qtpy/test_interaction.py b/tests/tests/test_qtpy/test_interaction.py index 68fd1f3..5a71b9d 100644 --- a/tests/tests/test_qtpy/test_interaction.py +++ b/tests/tests/test_qtpy/test_interaction.py @@ -1,39 +1,71 @@ import pytest -from qtpy.QtWidgets import QGridLayout +from qtpy.QtWidgets import QGridLayout, QGroupBox from brainglobe_utils.qtpy.interaction import add_button, add_combobox +@pytest.fixture() +def box() -> QGroupBox: + """ + Return a QGroupBox with a grid layout. + """ + box = QGroupBox() + layout = QGridLayout() + box.setLayout(layout) + + return box + + @pytest.mark.parametrize("label_stack", [True, False]) @pytest.mark.parametrize("label", ["A label", None]) -def test_add_combobox(label, label_stack): +def test_add_combobox(qtbot, box, label, label_stack): """ Smoke test for add_combobox for all conditional branches """ - layout = QGridLayout() + qtbot.addWidget(box) + layout = box.layout() + items = ["item 1", "item 2"] + + # returns tuple of (combobox, combobox_label) combobox = add_combobox( layout, row=0, label=label, - items=["item 1", "item 2"], + items=items, label_stack=label_stack, ) + assert combobox is not None + assert combobox[0].count() == len(items) + if label is None: + assert combobox[1] is None + assert layout.count() == 1 + else: + assert combobox[1].text() == label + assert layout.count() == 2 -@pytest.mark.parametrize( - argnames="alignment", argvalues=["center", "left", "right"] -) -def test_add_button(alignment): + +@pytest.mark.parametrize("alignment", ["center", "left", "right"]) +def test_add_button(qtbot, box, alignment): """ Smoke tests for add_button for all conditional branches """ - layout = QGridLayout() + qtbot.addWidget(box) + layout = box.layout() + label = "A button" + tooltip = "A useful tooltip" + button = add_button( layout=layout, connected_function=lambda: None, - label="A button", + label=label, row=0, alignment=alignment, + tooltip=tooltip, ) + assert button is not None + assert layout.count() == 1 + assert button.text() == label + assert button.toolTip() == tooltip From 448c9abea4c0e62269651dad6a5f63a43efd18c8 Mon Sep 17 00:00:00 2001 From: Kimberly Meechan <24316371+K-Meech@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:59:41 +0100 Subject: [PATCH 2/4] add further tests for interaction.py --- brainglobe_utils/qtpy/interaction.py | 2 +- tests/tests/test_qtpy/test_interaction.py | 96 ++++++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/brainglobe_utils/qtpy/interaction.py b/brainglobe_utils/qtpy/interaction.py index a27f028..0da8f62 100644 --- a/brainglobe_utils/qtpy/interaction.py +++ b/brainglobe_utils/qtpy/interaction.py @@ -123,7 +123,7 @@ def add_combobox( row: int = 0, column: int = 0, label_stack: bool = False, - callback=None, + callback: Optional[Callable] = None, width: int = 150, ) -> Tuple[QComboBox, Optional[QLabel]]: """ diff --git a/tests/tests/test_qtpy/test_interaction.py b/tests/tests/test_qtpy/test_interaction.py index 5a71b9d..2793984 100644 --- a/tests/tests/test_qtpy/test_interaction.py +++ b/tests/tests/test_qtpy/test_interaction.py @@ -1,7 +1,13 @@ import pytest from qtpy.QtWidgets import QGridLayout, QGroupBox -from brainglobe_utils.qtpy.interaction import add_button, add_combobox +from brainglobe_utils.qtpy.interaction import ( + add_button, + add_checkbox, + add_combobox, + add_float_box, + add_int_box, +) @pytest.fixture() @@ -26,6 +32,10 @@ def test_add_combobox(qtbot, box, label, label_stack): layout = box.layout() items = ["item 1", "item 2"] + # callback function for whenever the current index of the combobox changes + def callback(): + pass + # returns tuple of (combobox, combobox_label) combobox = add_combobox( layout, @@ -33,6 +43,7 @@ def test_add_combobox(qtbot, box, label, label_stack): label=label, items=items, label_stack=label_stack, + callback=callback, ) assert combobox is not None @@ -69,3 +80,86 @@ def test_add_button(qtbot, box, alignment): assert layout.count() == 1 assert button.text() == label assert button.toolTip() == tooltip + + +def test_add_checkbox(qtbot, box): + """ + Smoke tests for add_checkbox for all conditional branches + """ + qtbot.addWidget(box) + layout = box.layout() + label = "A checkbox" + tooltip = "A useful tooltip" + + checkbox = add_checkbox( + layout=layout, default=True, label=label, tooltip=tooltip + ) + + assert checkbox is not None + # layout should contain 2 items: QLabel and QCheckbox + assert layout.count() == 2 + assert layout.itemAt(0).widget().text() == label + assert checkbox.toolTip() == tooltip + + +def test_add_float_box(qtbot, box): + """ + Smoke tests for add_float_box for all conditional branches + """ + qtbot.addWidget(box) + layout = box.layout() + label = "A float box" + tooltip = "A useful tooltip" + default = 0.5 + minimum = 0.0 + maximum = 1.0 + + floatbox = add_float_box( + layout=layout, + default=default, + minimum=minimum, + maximum=maximum, + label=label, + step=0.1, + tooltip=tooltip, + ) + + assert floatbox is not None + # layout should contain 2 items: QLabel and QDoubleSpinBox + assert layout.count() == 2 + assert layout.itemAt(0).widget().text() == label + assert floatbox.maximum() == maximum + assert floatbox.minimum() == minimum + assert floatbox.value() == default + assert floatbox.toolTip() == tooltip + + +def test_add_int_box(qtbot, box): + """ + Smoke tests for add_float_box for all conditional branches + """ + qtbot.addWidget(box) + layout = box.layout() + label = "An int box" + tooltip = "A useful tooltip" + default = 5 + minimum = 0 + maximum = 10 + + intbox = add_int_box( + layout=layout, + default=default, + minimum=minimum, + maximum=maximum, + label=label, + tooltip=tooltip, + ) + + assert intbox is not None + # layout should contain 2 items: QLabel and QSpinBox + assert layout.count() == 2 + assert layout.itemAt(0).widget().text() == label + assert intbox.maximum() == maximum + assert intbox.minimum() == minimum + assert intbox.value() == default + assert intbox.toolTip() == tooltip From 531012e2d14c502df3ff68c6b9e916064a17d315 Mon Sep 17 00:00:00 2001 From: Kimberly Meechan <24316371+K-Meech@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:21:34 +0100 Subject: [PATCH 3/4] add test for dialog.py --- brainglobe_utils/qtpy/dialog.py | 2 +- tests/tests/test_qtpy/test_dialog.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/tests/test_qtpy/test_dialog.py diff --git a/brainglobe_utils/qtpy/dialog.py b/brainglobe_utils/qtpy/dialog.py index 9ba3cde..ad4fe6c 100644 --- a/brainglobe_utils/qtpy/dialog.py +++ b/brainglobe_utils/qtpy/dialog.py @@ -17,7 +17,7 @@ def display_warning(widget: QWidget, title: str, message: str) -> bool: return False -def display_info(widget, title, message): +def display_info(widget: QWidget, title: str, message: str): """ Display information in a pop-up that can only be accepted """ diff --git a/tests/tests/test_qtpy/test_dialog.py b/tests/tests/test_qtpy/test_dialog.py new file mode 100644 index 0000000..de46c0c --- /dev/null +++ b/tests/tests/test_qtpy/test_dialog.py @@ -0,0 +1,27 @@ +import pytest +from qtpy.QtWidgets import QGroupBox, QMessageBox + +from brainglobe_utils.qtpy.dialog import display_warning + + +@pytest.mark.parametrize( + "messagebox_return", + [QMessageBox.Yes, QMessageBox.Cancel], + ids=["Yes", "Cancel"], +) +def test_display_warning(qtbot, monkeypatch, messagebox_return): + """ + Test display_warning returns True/False when accepted/cancelled. + """ + # Use monkeypatch to return a set value from the modal dialog. + monkeypatch.setattr( + QMessageBox, "question", lambda *args: messagebox_return + ) + + box = QGroupBox() + qtbot.addWidget(box) + response = display_warning(box, "warning", "an example message") + if messagebox_return == QMessageBox.Yes: + assert response is True + else: + assert response is False From 65df12a56aef20f55cf593370ce22922d8d0a6c3 Mon Sep 17 00:00:00 2001 From: Kimberly Meechan <24316371+K-Meech@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:11:44 +0100 Subject: [PATCH 4/4] expand docstrings for interaction tests --- tests/tests/test_qtpy/test_interaction.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/tests/test_qtpy/test_interaction.py b/tests/tests/test_qtpy/test_interaction.py index 2793984..aa69299 100644 --- a/tests/tests/test_qtpy/test_interaction.py +++ b/tests/tests/test_qtpy/test_interaction.py @@ -26,7 +26,8 @@ def box() -> QGroupBox: @pytest.mark.parametrize("label", ["A label", None]) def test_add_combobox(qtbot, box, label, label_stack): """ - Smoke test for add_combobox for all conditional branches + Smoke test for add_combobox. Tests if a combobox can be added to a layout + with/without a label, and with/without label_stack. """ qtbot.addWidget(box) layout = box.layout() @@ -60,7 +61,8 @@ def callback(): @pytest.mark.parametrize("alignment", ["center", "left", "right"]) def test_add_button(qtbot, box, alignment): """ - Smoke tests for add_button for all conditional branches + Smoke test for add_button. Tests if a button can be added to a layout with + the correct label/tooltip using different alignments. """ qtbot.addWidget(box) layout = box.layout() @@ -84,7 +86,8 @@ def test_add_button(qtbot, box, alignment): def test_add_checkbox(qtbot, box): """ - Smoke tests for add_checkbox for all conditional branches + Smoke test for add_checkbox. Tests if a checkbox can be added to a layout + with the correct label/tooltip. """ qtbot.addWidget(box) layout = box.layout() @@ -104,7 +107,8 @@ def test_add_checkbox(qtbot, box): def test_add_float_box(qtbot, box): """ - Smoke tests for add_float_box for all conditional branches + Smoke test for add_float_box. Tests if a float spinbox can be added to a + layout with the correct label, tooltip, minimum, maximum and default value. """ qtbot.addWidget(box) layout = box.layout() @@ -136,7 +140,8 @@ def test_add_float_box(qtbot, box): def test_add_int_box(qtbot, box): """ - Smoke tests for add_float_box for all conditional branches + Smoke test for add_int_box. Tests if an int spinbox can be added to a + layout with the correct label, tooltip, minimum, maximum and default value. """ qtbot.addWidget(box) layout = box.layout()