Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ComboBox with unhashable choice data #89

Merged
merged 3 commits into from
Jan 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ coverage:
project:
default:
target: auto
threshold: 0.5% # coverage can drop by up to 0.5% while still posting success
threshold: 0.5% # coverage can drop by up to 0.5% while still posting success
patch:
default:
target: auto
threshold: 4% # coverage can drop by up to 0.5% while still posting success
threshold: 15% # coverage can drop by up to 15% while still posting success
comment:
require_changes: true # if true: only post the PR comment if coverage changes
require_changes: true # if true: only post the PR comment if coverage changes
31 changes: 20 additions & 11 deletions magicgui/backends/_qtpy/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,23 +351,32 @@ def _mgui_set_value(self, value) -> None:

def _mgui_set_choices(self, choices: Iterable[Tuple[str, Any]]) -> None:
"""Set current items in categorical type ``widget`` to ``choices``."""
# FIXME: still not clearing all old choices correctly.
if not list(choices):
choices_ = list(choices)
if not choices_:
self._qwidget.clear()
return

names = {x[0] for x in choices}
choice_names = [x[0] for x in choices_]
# remove choices that no longer exist
for i in range(self._qwidget.count()):
if self._qwidget.itemText(i) not in names:
if self._qwidget.itemText(i) not in choice_names:
self._qwidget.removeItem(i)
for name, data in choices:
if self._qwidget.findText(name) == -1:
# update choices
for name, data in choices_:
item_index = self._qwidget.findText(name)
# if it's not in the list, add a new item
if item_index == -1:
self._qwidget.addItem(name, data)
current = self._mgui_get_value()
if current not in {x[1] for x in choices}:
first = next(iter(choices))[1]
self._qwidget.setCurrentIndex(self._qwidget.findData(first))
self._qwidget.removeItem(self._qwidget.findData(current))
# otherwise update its data
else:
self._qwidget.setItemData(item_index, data)
# if the currently selected item is not in the new set,
# remove it and select the first item in the list
current = self._qwidget.itemText(self._qwidget.currentIndex())
if current not in choice_names:
first = choice_names[0]
self._qwidget.setCurrentIndex(self._qwidget.findText(first))
self._qwidget.removeItem(self._qwidget.findText(current))

def _mgui_get_choices(self) -> Tuple[Tuple[str, Any]]:
"""Show the widget."""
Expand Down
12 changes: 12 additions & 0 deletions tests/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,15 @@ def widget(x: "tests.MyInt"): # type: ignore # noqa
pass

assert widget.x.annotation is MyInt


def test_unhashable_choice_data():
"""Test that providing unhashable choice data is ok."""
combo = widgets.ComboBox()
assert not combo.choices
combo.choices = ("a", "b", "c")
assert combo.choices == ("a", "b", "c")
combo.choices = (("a", [1, 2, 3]), ("b", [1, 2, 5]))
assert combo.choices == ([1, 2, 3], [1, 2, 5])
combo.choices = ("x", "y", "z")
assert combo.choices == ("x", "y", "z")