-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EMSUSD-1856 Add filtering search to the collection widget
- Add a FilteredStringListModel class. - Use that model in the collection string list widgets. - Add a text edit widget to enter the search text - Connect the search changed signal to the filtered model. - Add a button to clear the search filter. - Add a flag on the filtered model to remember if it was entirely filtered out. - Properly re-filter the model when a new list of text is given. - Moved the filtered view in its own file. - Give the resizable lists a sensible default size.
- Loading branch information
1 parent
eaf38d0
commit cffff11
Showing
6 changed files
with
195 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
...ae/usd-shared-components/src/python/usdSharedComponents/common/filteredStringListModel.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from typing import Sequence | ||
|
||
try: | ||
from PySide6.QtCore import ( | ||
QStringListModel, | ||
Signal, | ||
) | ||
except: | ||
from PySide2.QtCore import ( | ||
QStringListModel, | ||
Signal, | ||
) | ||
|
||
|
||
class FilteredStringListModel(QStringListModel): | ||
''' | ||
A Qt string list model that can be filtered. | ||
''' | ||
filterChanged = Signal() | ||
|
||
def __init__(self, items: Sequence[str] = None, parent=None): | ||
super(FilteredStringListModel, self).__init__(items if items else [], parent) | ||
self._unfilteredItems = items | ||
self._isFilteredEmpty = False | ||
self._filter = "" | ||
|
||
def setStringList(self, items: Sequence[str]): | ||
''' | ||
Override base class implementation to properly rebuild | ||
the filtered list. | ||
''' | ||
self._unfilteredItems = items | ||
self._isFilteredEmpty = False | ||
super(FilteredStringListModel, self).setStringList(items) | ||
self._rebuildFilteredModel() | ||
|
||
def _rebuildFilteredModel(self): | ||
''' | ||
Rebuild the model by applying the filter. | ||
''' | ||
if not self._filter: | ||
filteredItems = self._unfilteredItems | ||
else: | ||
filters = [filter.lower() for filter in self._filter.split('*')] | ||
filteredItems = [item for item in self._unfilteredItems if self._isValidItem(item, filters)] | ||
self._isFilteredEmpty = bool(self._unfilteredItems) and not bool(filteredItems) | ||
# Note: don't call our own version, otehrwise we would get infinite recursion. | ||
super(FilteredStringListModel, self).setStringList(filteredItems) | ||
|
||
def _isValidItem(self, item: str, filters: Sequence[str]): | ||
''' | ||
Verify if the item passes all the filters. | ||
We search each given filter in sequence, each one must match | ||
somewhere in the item starting at the end of the point where | ||
the preceeding filter ended: | ||
''' | ||
index = 0 | ||
item = item.lower() | ||
for filter in filters: | ||
newIndex = item.find(filter, index) | ||
if newIndex < 0: | ||
return False | ||
index = newIndex + len(filter) | ||
return True | ||
|
||
def isFilteredEmpty(self): | ||
''' | ||
Verify if the model is empty because it was entirely filtered out. | ||
''' | ||
return self._isFilteredEmpty | ||
|
||
def setFilter(self, filter: str): | ||
''' | ||
Set the filter to be applied to the model and rebuild the model. | ||
''' | ||
if filter == self._filter: | ||
return | ||
self._filter = filter | ||
self._rebuildFilteredModel() | ||
self.filterChanged.emit() |
83 changes: 83 additions & 0 deletions
83
.../ae/usd-shared-components/src/python/usdSharedComponents/common/filteredStringListView.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from typing import Sequence, Union | ||
from .theme import Theme | ||
from .filteredStringListModel import FilteredStringListModel | ||
|
||
try: | ||
from PySide6.QtCore import ( | ||
QModelIndex, | ||
QPersistentModelIndex, | ||
QRect, | ||
QSize, | ||
QStringListModel, | ||
Qt, | ||
Signal, | ||
) | ||
from PySide6.QtGui import QPainter, QPaintEvent, QPen, QColor | ||
from PySide6.QtWidgets import QStyleOptionViewItem, QStyledItemDelegate, QListView | ||
except: | ||
from PySide2.QtCore import ( | ||
QModelIndex, | ||
QPersistentModelIndex, | ||
QRect, | ||
QSize, | ||
QStringListModel, | ||
Qt, | ||
Signal, | ||
) | ||
from PySide2.QtGui import QPainter, QPaintEvent, QPen, QColor # type: ignore | ||
from PySide2.QtWidgets import QStyleOptionViewItem, QStyledItemDelegate, QListView | ||
|
||
|
||
kNoObjectFoundLabel = 'No objects found' | ||
|
||
class FilteredStringListView(QListView): | ||
selectedItemsChanged = Signal() | ||
|
||
class Delegate(QStyledItemDelegate): | ||
def __init__(self, model: QStringListModel, parent=None): | ||
super(FilteredStringListView.Delegate, self).__init__(parent) | ||
self._model = model | ||
|
||
def sizeHint(self, option: QStyleOptionViewItem, index: Union[QModelIndex, QPersistentModelIndex]): | ||
s: int = Theme.instance().uiScaled(24) | ||
return QSize(s, s) | ||
|
||
def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: Union[QModelIndex, QPersistentModelIndex]): | ||
s: str = self._model.data(index, Qt.DisplayRole) | ||
Theme.instance().paintStringListEntry(painter, option.rect, s) | ||
|
||
def __init__(self, items: Sequence[str] = None, parent=None): | ||
super(FilteredStringListView, self).__init__(parent) | ||
self._model = FilteredStringListModel(items if items else [], self) | ||
self.setModel(self._model) | ||
|
||
self.setUniformItemSizes(True) | ||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) | ||
self.setTextElideMode(Qt.TextElideMode.ElideMiddle) | ||
self.setSelectionBehavior(QListView.SelectRows) | ||
self.setSelectionMode(QListView.MultiSelection) | ||
self.setContentsMargins(0,0,0,0) | ||
|
||
self.selectionModel().selectionChanged.connect(lambda: self.selectedItemsChanged.emit()) | ||
|
||
def drawFrame(self, painter: QPainter): | ||
pass | ||
|
||
def paintEvent(self, event: QPaintEvent): | ||
super(FilteredStringListView, self).paintEvent(event) | ||
if self._model.isFilteredEmpty(): | ||
painter = QPainter(self.viewport()) | ||
painter.setPen(QColor(128, 128, 128)) | ||
painter.drawText(self.rect(), Qt.AlignCenter, kNoObjectFoundLabel) | ||
|
||
@property | ||
def items(self) -> Sequence[str]: | ||
return self._model.stringList | ||
|
||
@items.setter | ||
def items(self, items: Sequence[str]): | ||
self._model.setStringList(items) | ||
|
||
@property | ||
def selectedItems(self) -> Sequence[str]: | ||
return [index.data(Qt.DisplayRole) for index in self.selectedIndexes()] |
84 changes: 6 additions & 78 deletions
84
lib/mayaUsd/resources/ae/usd-shared-components/src/python/usdSharedComponents/common/list.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters