From f6e93abe57dc9015d9148fa11978828fc31a72ae Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Oct 2022 17:17:55 +0100 Subject: [PATCH 1/2] Added test for connect_list_selection and fix remaining PyQt6 issue --- echo/qt/connect.py | 2 +- echo/qt/tests/test_connect_list_selection.py | 135 +++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 echo/qt/tests/test_connect_list_selection.py diff --git a/echo/qt/connect.py b/echo/qt/connect.py index 2e88a1681..43ed9de1f 100644 --- a/echo/qt/connect.py +++ b/echo/qt/connect.py @@ -541,7 +541,7 @@ def update_widget(self, value, force=False): # We interpret None data as being disabled rows (used for headers) if isinstance(choice, ChoiceSeparator): - item.setFlags(Qt.ItemFlags(int(item.flags()) & int(~(Qt.ItemIsSelectable | Qt.ItemIsEnabled)))) + item.setFlags(item.flags() & ~(Qt.ItemIsSelectable | Qt.ItemIsEnabled)) if len(self._widget.selectedItems()) == 0: current_index = -1 diff --git a/echo/qt/tests/test_connect_list_selection.py b/echo/qt/tests/test_connect_list_selection.py new file mode 100644 index 000000000..b1caf7154 --- /dev/null +++ b/echo/qt/tests/test_connect_list_selection.py @@ -0,0 +1,135 @@ +import pytest +import numpy as np + +from qtpy import QtWidgets +from qtpy.QtCore import Qt + +from echo.core import CallbackProperty +from echo.selection import SelectionCallbackProperty, ChoiceSeparator +from echo.qt.connect import connect_list_selection + + +class Example(object): + a = SelectionCallbackProperty(default_index=1) + b = CallbackProperty() + + +def test_connect_list_selection(): + + t = Example() + + a_prop = getattr(type(t), 'a') + a_prop.set_choices(t, [4, 3.5]) + a_prop.set_display_func(t, lambda x: 'value: {0}'.format(x)) + + list_widget = QtWidgets.QListWidget() + + c1 = connect_list_selection(t, 'a', list_widget) # noqa + + assert list_widget.item(0).text() == 'value: 4' + assert list_widget.item(1).text() == 'value: 3.5' + assert list_widget.item(0).data(Qt.UserRole).data == 4 + assert list_widget.item(1).data(Qt.UserRole).data == 3.5 + + list_widget.setCurrentItem(list_widget.item(1)) + assert t.a == 3.5 + + list_widget.setCurrentItem(list_widget.item(0)) + assert t.a == 4 + + list_widget.setCurrentItem(list_widget.item(-1)) + assert t.a is None + + t.a = 3.5 + assert len(list_widget.selectedItems()) == 1 + assert list_widget.selectedItems()[0] is list_widget.item(1) + + t.a = 4 + assert len(list_widget.selectedItems()) == 1 + assert list_widget.selectedItems()[0] is list_widget.item(0) + + with pytest.raises(ValueError) as exc: + t.a = 2 + assert exc.value.args[0] == 'value 2 is not in valid choices: [4, 3.5]' + + t.a = None + assert len(list_widget.selectedItems()) == 0 + + # Changing choices should change Qt list_widget box. Let's first try with a case + # in which there is a matching data value in the new list_widget box + + t.a = 3.5 + assert len(list_widget.selectedItems()) == 1 + assert list_widget.selectedItems()[0] is list_widget.item(1) + + a_prop.set_choices(t, (4, 5, 3.5)) + assert list_widget.count() == 3 + + assert t.a == 3.5 + assert len(list_widget.selectedItems()) == 1 + assert list_widget.selectedItems()[0] is list_widget.item(2) + + assert list_widget.item(0).text() == 'value: 4' + assert list_widget.item(1).text() == 'value: 5' + assert list_widget.item(2).text() == 'value: 3.5' + assert list_widget.item(0).data(Qt.UserRole).data == 4 + assert list_widget.item(1).data(Qt.UserRole).data == 5 + assert list_widget.item(2).data(Qt.UserRole).data == 3.5 + + # Now we change the choices so that there is no matching data - in this case + # the index should change to that given by default_index + + a_prop.set_choices(t, (4, 5, 6)) + + assert t.a == 5 + assert len(list_widget.selectedItems()) == 1 + assert list_widget.selectedItems()[0] is list_widget.item(1) + assert list_widget.count() == 3 + + assert list_widget.item(0).text() == 'value: 4' + assert list_widget.item(1).text() == 'value: 5' + assert list_widget.item(2).text() == 'value: 6' + assert list_widget.item(0).data(Qt.UserRole).data == 4 + assert list_widget.item(1).data(Qt.UserRole).data == 5 + assert list_widget.item(2).data(Qt.UserRole).data == 6 + + # Finally, if there are too few choices for the default_index to be valid, + # pick the last item in the list_widget + + a_prop.set_choices(t, (9,)) + + assert t.a == 9 + assert len(list_widget.selectedItems()) == 1 + assert list_widget.selectedItems()[0] is list_widget.item(0) + assert list_widget.count() == 1 + + assert list_widget.item(0).text() == 'value: 9' + assert list_widget.item(0).data(Qt.UserRole).data == 9 + + # Now just make sure that ChoiceSeparator works + + separator = ChoiceSeparator('header') + a_prop.set_choices(t, (separator, 1, 2)) + + assert list_widget.count() == 3 + assert list_widget.item(0).text() == 'header' + assert list_widget.item(0).data(Qt.UserRole).data is separator + + # And setting choices to an empty iterable shouldn't cause issues + + a_prop.set_choices(t, ()) + assert list_widget.count() == 0 + + # Try including an array in the choices + a_prop.set_choices(t, (4, 5, np.array([1, 2, 3]))) + + +def test_connect_list_widget_selection_invalid(): + + t = Example() + + list_widget = QtWidgets.QListWidget() + + with pytest.raises(TypeError) as exc: + connect_list_selection(t, 'b', list_widget) + assert exc.value.args[0] == 'connect_list_selection requires a SelectionCallbackProperty' From 46139f2e80ecf44f32582e63a3829dfab01864ef Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Oct 2022 23:29:22 +0100 Subject: [PATCH 2/2] Check for equality or identity --- echo/qt/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/echo/qt/connect.py b/echo/qt/connect.py index 43ed9de1f..51c97ad5a 100644 --- a/echo/qt/connect.py +++ b/echo/qt/connect.py @@ -516,7 +516,7 @@ def update_widget(self, value, force=False): choice_labels = getattr(type(self._instance), self._prop).get_choice_labels(self._instance) for idx in range(len(choices)): - if choices[idx] is value: + if choices[idx] is value or (choices[idx] == value) is True: break else: idx = -1