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/brainglobe_utils/qtpy/interaction.py b/brainglobe_utils/qtpy/interaction.py index a5ac1d8..0da8f62 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) @@ -109,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_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 diff --git a/tests/tests/test_qtpy/test_interaction.py b/tests/tests/test_qtpy/test_interaction.py index 68fd1f3..aa69299 100644 --- a/tests/tests/test_qtpy/test_interaction.py +++ b/tests/tests/test_qtpy/test_interaction.py @@ -1,39 +1,170 @@ import pytest -from qtpy.QtWidgets import QGridLayout +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() +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 + Smoke test for add_combobox. Tests if a combobox can be added to a layout + with/without a label, and with/without label_stack. """ - layout = QGridLayout() + qtbot.addWidget(box) + 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, row=0, label=label, - items=["item 1", "item 2"], + items=items, label_stack=label_stack, + callback=callback, ) + 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 + Smoke test for add_button. Tests if a button can be added to a layout with + the correct label/tooltip using different alignments. """ - 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 + + +def test_add_checkbox(qtbot, box): + """ + 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() + 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 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() + 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 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() + 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