Skip to content

Commit

Permalink
refactor as a subclass of QColormapComboBox
Browse files Browse the repository at this point in the history
  • Loading branch information
psobolewskiPhD committed Jan 26, 2025
1 parent 5b0248e commit 5b0a483
Showing 1 changed file with 10 additions and 113 deletions.
123 changes: 10 additions & 113 deletions src/superqt/cmap/_cmap_filter_combo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,20 @@
QWidget,
)

from superqt.utils import signals_blocked

from ._cmap_combo import _CmapNameDialog
from ._cmap_combo import QColormapComboBox
from ._cmap_item_delegate import QColormapItemDelegate
from ._cmap_line_edit import QColormapLineEdit
from ._cmap_utils import try_cast_colormap

if TYPE_CHECKING:
from collections.abc import Sequence

from cmap._colormap import ColorStopsLike
from qtpy.QtGui import QKeyEvent


CMAP_ROLE = Qt.ItemDataRole.UserRole + 1


class QColormapFilterComboBox(QComboBox):
class QColormapFilterComboBox(QColormapComboBox):
"""A drop down menu for selecting colors that allows for text filtering.
Parameters
Expand All @@ -53,17 +49,17 @@ def __init__(
add_colormap_text: str = "Add Colormap...",
) -> None:
# init QComboBox
super().__init__(parent)
self._add_color_text: str = add_colormap_text
self._allow_user_colors: bool = allow_user_colormaps
self._last_cmap: Colormap | None = None
super().__init__(
parent,
allow_user_colormaps=allow_user_colormaps,
add_colormap_text=add_colormap_text,
)

# Create line edit and make it editable
# Ensure line edit is editable
self.setLineEdit(_PopupColormapLineEdit(self))
self.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
self.setEditable(True)
self.setDuplicatesEnabled(False)
# (must come before setCompleter)
self.setLineEdit(_PopupColormapLineEdit(self))

# use string list model as source model
self._source_model = QStringListModel(self)
Expand Down Expand Up @@ -91,117 +87,19 @@ def __init__(
self.model().rowsRemoved.connect(self._update_completer_model)
self.currentTextChanged.connect(self._on_text_changed)


self.currentIndexChanged.connect(self._on_index_changed)
# there's a little bit of a potential bug here:
# if the user clicks on the "Add Colormap..." item
# then an indexChanged signal will be emitted, but it may not
# actually represent a "true" change in the index if they dismiss the dialog
self.activated.connect(self._on_activated)

self.setUserAdditionsAllowed(allow_user_colormaps)

def userAdditionsAllowed(self) -> bool:
"""Returns whether the user can add custom colors."""
return self._allow_user_colors

def setUserAdditionsAllowed(self, allow: bool) -> None:
"""Sets whether the user can add custom colors.
If enabled, an "Add Colormap..." item will be added to the end of the
list. When clicked, a dialog will be shown to allow the user to select
a colormap from the
[cmap catalog](https://cmap-docs.readthedocs.io/en/latest/catalog/).
"""
self._allow_user_colors = bool(allow)

idx = self.findData(self._add_color_text, Qt.ItemDataRole.DisplayRole)
if idx < 0:
if self._allow_user_colors:
self.addItem(self._add_color_text)
elif not self._allow_user_colors:
self.removeItem(idx)

def clear(self) -> None:
super().clear()
self._update_completer_model()
self.setUserAdditionsAllowed(self._allow_user_colors)

def itemColormap(self, index: int) -> Colormap | None:
"""Returns the color of the item at the given index."""
return self.itemData(index, CMAP_ROLE)

def addColormap(self, cmap: ColorStopsLike) -> None:
"""Adds the colormap to the QComboBox."""
if (_cmap := try_cast_colormap(cmap)) is None:
raise ValueError(f"Invalid colormap value: {cmap!r}")

for i in range(self.count()):
if item := self.itemColormap(i):
if item.name == _cmap.name:
return # no duplicates # pragma: no cover

had_items = self.count() > int(self._allow_user_colors)
# add the new color and set the background color of that item
self.addItem(_cmap.name.rsplit(":", 1)[-1])
self.setItemData(self.count() - 1, _cmap, CMAP_ROLE)
if not had_items: # first item added
self._on_index_changed(self.count() - 1)

# make sure the "Add Colormap..." item is last
idx = self.findData(self._add_color_text, Qt.ItemDataRole.DisplayRole)
if idx >= 0:
with signals_blocked(self):
self.removeItem(idx)
self.addItem(self._add_color_text)

super().addColormap(cmap)
self._update_completer_model()

def addColormaps(self, colors: Sequence[Any]) -> None:
"""Adds colors to the QComboBox."""
for color in colors:
self.addColormap(color)

def currentColormap(self) -> Colormap | None:
"""Returns the currently selected Colormap or None if not yet selected."""
return self.currentData(CMAP_ROLE)

def setCurrentColormap(self, color: Any) -> None:
"""Adds the color to the QComboBox and selects it."""
if not (cmap := try_cast_colormap(color)):
raise ValueError(f"Invalid colormap value: {color!r}")

for idx in range(self.count()):
if (item := self.itemColormap(idx)) and item.name == cmap.name:
self.setCurrentIndex(idx)

def _on_activated(self, index: int) -> None:
if self.itemText(index) != self._add_color_text:
return

dlg = _CmapNameDialog(self, Qt.WindowType.Sheet)
if dlg.exec() and (cmap := dlg.combo.currentColormap()):
# add the color and select it, without adding duplicates
for i in range(self.count()):
if (item := self.itemColormap(i)) and cmap.name == item.name:
self.setCurrentIndex(i)
return
self.addColormap(cmap)
self.currentIndexChanged.emit(self.currentIndex())
elif self._last_cmap is not None:
# user canceled, restore previous color without emitting signal
idx = self.findData(self._last_cmap, CMAP_ROLE)
if idx >= 0:
with signals_blocked(self):
self.setCurrentIndex(idx)

def _on_index_changed(self, index: int) -> None:
colormap = self.itemData(index, CMAP_ROLE)
if isinstance(colormap, Colormap):
self.currentColormapChanged.emit(colormap)
self.lineEdit().setColormap(colormap)
self._last_cmap = colormap

def _update_completer_model(self) -> None:
"""Update the completer's model with current items."""
words = []
Expand All @@ -226,7 +124,6 @@ def keyPressEvent(self, e: QKeyEvent | None) -> None:
self.lineEdit().setText(completer.currentCompletion()) # type: ignore
return super().keyPressEvent(e)

CATEGORIES = ("sequential", "diverging", "cyclic", "qualitative", "miscellaneous")

class _PopupColormapLineEdit(QColormapLineEdit):
def mouseReleaseEvent(self, _: Any) -> None:
Expand Down

0 comments on commit 5b0a483

Please sign in to comment.