Skip to content
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
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ behavior they verify rather than listed separately.
Escape, and outside clicks release filter focus so global shortcuts resume.
- Display metadata columns containing multiple values in the Cluster and
Similarity Views instead of leaving their cells blank.
- Sort text columns holding numbers, such as the channel column `ch`, by
numeric value in the Cluster and Similarity Views. Channel 2 no longer
appears after channel 10.

### Changed

Expand Down
30 changes: 30 additions & 0 deletions phy/gui/tests/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,36 @@ def test_table_change_and_sort_2(qtbot, table):
_assert(table.get_ids, [9, 8, 7, 6, 4, 3, 2, 1, 0, 5])


def test_table_sort_numeric_strings(qtbot):
# The `ch` column of the cluster view holds channel labels, which are strings.
# A plain string comparison sorts '10' before '2', so numeric strings have to be
# compared as numbers.
data = [{'id': i, 'ch': ch} for i, ch in enumerate(['2', '10', '1', '21', '3'])]
table = Table(columns=['id', 'ch'], value_names=['id', 'ch'], data=data)
_wait_until_table_ready(qtbot, table)

table.sort_by('ch', 'asc')
_assert(table.get_ids, [2, 0, 4, 1, 3])

table.sort_by('ch', 'desc')
_assert(table.get_ids, [3, 1, 4, 0, 2])

table.close()


def test_table_sort_mixed_strings(qtbot):
# In a column that mixes numbers and free text, the numbers come first in numeric
# order and the remaining entries keep their string ordering.
data = [{'id': i, 'label': label} for i, label in enumerate(['mua', '10', 'good', '2'])]
table = Table(columns=['id', 'label'], value_names=['id', 'label'], data=data)
_wait_until_table_ready(qtbot, table)

table.sort_by('label', 'asc')
_assert(table.get_ids, [3, 1, 2, 0])

table.close()


def test_table_change_metadata_preserves_sort(qtbot):
data = [
{'id': 0, 'count': 30, 'group': 'noise'},
Expand Down
22 changes: 22 additions & 0 deletions phy/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import inspect
import json
import logging
import math
import re
import sys
from contextlib import contextmanager
Expand Down Expand Up @@ -330,6 +331,25 @@ def predicate(row):
return predicate, True


def _text_sort_key(value):
"""Return a sort key that orders numeric text by value rather than character by character.

Columns such as `ch` hold channel labels, which are strings, so a plain string
comparison places '10' before '2'. Numeric entries sort first, in numeric order, and
the remaining entries keep their string ordering.

"""
text = value if isinstance(value, str) else str(value)
try:
number = float(text)
except ValueError:
return (1, 0.0, text)
# NaN has no consistent ordering, so keep it with the non-numeric entries.
if math.isnan(number):
return (1, 0.0, text)
return (0, number, '')


class _TableModel(QAbstractTableModel):
"""Model backing the native Qt table."""

Expand Down Expand Up @@ -438,6 +458,8 @@ def lessThan(self, left, right):
return False
if right_value is None:
return True
if isinstance(left_value, str) or isinstance(right_value, str):
return _text_sort_key(left_value) < _text_sort_key(right_value)
try:
return bool(left_value < right_value)
except TypeError:
Expand Down
Loading