From 8e965639e85e42deecf84842136d0fa255ec224b Mon Sep 17 00:00:00 2001 From: Adam Tyson Date: Tue, 16 Jan 2024 14:20:45 +0000 Subject: [PATCH 1/2] Move qtpy functionality from brainglobe-segmentation --- brainglobe_utils/qtpy/dialog.py | 29 +++++++++ brainglobe_utils/qtpy/interaction.py | 94 ++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 brainglobe_utils/qtpy/dialog.py create mode 100644 brainglobe_utils/qtpy/interaction.py diff --git a/brainglobe_utils/qtpy/dialog.py b/brainglobe_utils/qtpy/dialog.py new file mode 100644 index 0000000..69d24a0 --- /dev/null +++ b/brainglobe_utils/qtpy/dialog.py @@ -0,0 +1,29 @@ +from qtpy.QtWidgets import QMessageBox + + +def display_warning(widget, title, message): + """ + Display a warning in a pop-up that can be accepted or dismissed + """ + message_reply = QMessageBox.question( + widget, + title, + message, + QMessageBox.Yes | QMessageBox.Cancel, + ) + if message_reply == QMessageBox.Yes: + return True + else: + return False + + +def display_info(widget, title, message): + """ + Display information in a pop-up that can only be accepted + """ + QMessageBox.information( + widget, + title, + message, + QMessageBox.Ok, + ) diff --git a/brainglobe_utils/qtpy/interaction.py b/brainglobe_utils/qtpy/interaction.py new file mode 100644 index 0000000..81369bc --- /dev/null +++ b/brainglobe_utils/qtpy/interaction.py @@ -0,0 +1,94 @@ +from qtpy.QtWidgets import ( + QCheckBox, + QDoubleSpinBox, + QLabel, + QPushButton, + QSpinBox, +) + + +def add_button( + label, + layout, + connected_function, + *, + row: int = 0, + column: int = 0, + visibility=True, + minimum_width=0, + alignment="center", + tooltip=None, +): + button = QPushButton(label) + if alignment == "center": + pass + elif alignment == "left": + button.setStyleSheet("QPushButton { text-align: left; }") + elif alignment == "right": + button.setStyleSheet("QPushButton { text-align: right; }") + + button.setVisible(visibility) + button.setMinimumWidth(minimum_width) + + if tooltip: + button.setToolTip(tooltip) + layout.addWidget(button, row, column) + button.clicked.connect(connected_function) + return button + + +def add_checkbox( + layout, default, label, row: int = 0, column: int = 0, tooltip=None +): + box = QCheckBox() + box.setChecked(default) + if tooltip: + box.setToolTip(tooltip) + layout.addWidget(QLabel(label), row, column) + layout.addWidget(box, row, column + 1) + return box + + +def add_float_box( + layout, + default, + minimum, + maximum, + label, + step, + row: int = 0, + column: int = 0, + tooltip=None, +): + box = QDoubleSpinBox() + box.setMinimum(minimum) + box.setMaximum(maximum) + box.setValue(default) + box.setSingleStep(step) + if tooltip: + box.setToolTip(tooltip) + layout.addWidget(QLabel(label), row, column) + layout.addWidget(box, row, column + 1) + return box + + +def add_int_box( + layout, + default, + minimum, + maximum, + label, + row: int = 0, + column: int = 0, + tooltip=None, +): + box = QSpinBox() + box.setMinimum(minimum) + box.setMaximum(maximum) + # Not always set if not after min & max + box.setValue(default) + if tooltip: + box.setToolTip(tooltip) + layout.addWidget(QLabel(label), row, column) + layout.addWidget(box, row, column + 1) + return box From 0f1f5d61d688375ee5b4f1f4311489da9ca6370e Mon Sep 17 00:00:00 2001 From: Adam Tyson Date: Tue, 16 Jan 2024 16:15:25 +0000 Subject: [PATCH 2/2] Move qtpy functionality from cellfinder --- brainglobe_utils/qtpy/dialog.py | 4 +- brainglobe_utils/qtpy/interaction.py | 60 ++++++++++++++++++++--- tests/tests/test_qtpy/test_interaction.py | 39 +++++++++++++++ 3 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 tests/tests/test_qtpy/test_interaction.py diff --git a/brainglobe_utils/qtpy/dialog.py b/brainglobe_utils/qtpy/dialog.py index 69d24a0..9ba3cde 100644 --- a/brainglobe_utils/qtpy/dialog.py +++ b/brainglobe_utils/qtpy/dialog.py @@ -1,7 +1,7 @@ -from qtpy.QtWidgets import QMessageBox +from qtpy.QtWidgets import QMessageBox, QWidget -def display_warning(widget, title, message): +def display_warning(widget: QWidget, title: str, message: str) -> bool: """ Display a warning in a pop-up that can be accepted or dismissed """ diff --git a/brainglobe_utils/qtpy/interaction.py b/brainglobe_utils/qtpy/interaction.py index 81369bc..a5ac1d8 100644 --- a/brainglobe_utils/qtpy/interaction.py +++ b/brainglobe_utils/qtpy/interaction.py @@ -1,24 +1,31 @@ +from typing import Callable, List, Optional, Tuple + from qtpy.QtWidgets import ( QCheckBox, + QComboBox, QDoubleSpinBox, QLabel, + QLayout, QPushButton, QSpinBox, ) def add_button( - label, - layout, - connected_function, + label: str, + layout: QLayout, + connected_function: Callable, *, row: int = 0, column: int = 0, - visibility=True, - minimum_width=0, - alignment="center", - tooltip=None, -): + visibility: bool = True, + minimum_width: int = 0, + alignment: str = "center", + tooltip: Optional[str] = None, +) -> QPushButton: + """ + Add a button to *layout*. + """ button = QPushButton(label) if alignment == "center": pass @@ -32,6 +39,7 @@ def add_button( if tooltip: button.setToolTip(tooltip) + layout.addWidget(button, row, column) button.clicked.connect(connected_function) return button @@ -92,3 +100,39 @@ def add_int_box( layout.addWidget(QLabel(label), row, column) layout.addWidget(box, row, column + 1) return box + + +def add_combobox( + layout: QLayout, + label: str, + items: List[str], + row: int = 0, + column: int = 0, + label_stack: bool = False, + callback=None, + width: int = 150, +) -> Tuple[QComboBox, Optional[QLabel]]: + """ + Add a selection box to *layout*. + """ + if label_stack: + combobox_row = row + 1 + combobox_column = column + else: + combobox_row = row + combobox_column = column + 1 + combobox = QComboBox() + combobox.addItems(items) + if callback: + combobox.currentIndexChanged.connect(callback) + combobox.setMaximumWidth = width + + if label is not None: + combobox_label = QLabel(label) + combobox_label.setMaximumWidth = width + layout.addWidget(combobox_label, row, column) + else: + combobox_label = None + + layout.addWidget(combobox, combobox_row, combobox_column) + return combobox, combobox_label diff --git a/tests/tests/test_qtpy/test_interaction.py b/tests/tests/test_qtpy/test_interaction.py new file mode 100644 index 0000000..68fd1f3 --- /dev/null +++ b/tests/tests/test_qtpy/test_interaction.py @@ -0,0 +1,39 @@ +import pytest +from qtpy.QtWidgets import QGridLayout + +from brainglobe_utils.qtpy.interaction import add_button, add_combobox + + +@pytest.mark.parametrize("label_stack", [True, False]) +@pytest.mark.parametrize("label", ["A label", None]) +def test_add_combobox(label, label_stack): + """ + Smoke test for add_combobox for all conditional branches + """ + layout = QGridLayout() + combobox = add_combobox( + layout, + row=0, + label=label, + items=["item 1", "item 2"], + label_stack=label_stack, + ) + assert combobox is not None + + +@pytest.mark.parametrize( + argnames="alignment", argvalues=["center", "left", "right"] +) +def test_add_button(alignment): + """ + Smoke tests for add_button for all conditional branches + """ + layout = QGridLayout() + button = add_button( + layout=layout, + connected_function=lambda: None, + label="A button", + row=0, + alignment=alignment, + ) + assert button is not None