Issue
I have a drop down which contains heatmaps and solid color names. It also has a 'Custom...' option which will open a color picker when selected. I would like to separate the 'Custom...' item from the hex colors.
When not using a model, adding a separator is easy:
if self.count() > 1:
self.insertSeparator(self.count()-1)
How can I insert a separator when populating the combo using a model?
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
HEATMAPS = [
'viridis', 'inferno', 'ocean', 'hot', 'terrain', 'nipy_spectral',
]
COLORS = [
'#ffffff', '#00ff00', '#0000ff', '#ff0000', '#ffff00',
]
class IconModel(QtCore.QAbstractListModel):
def __init__(self, items=None, parent=None):
super().__init__(parent=parent)
if not items:
self._items = []
else:
self._items = items
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._items)
def data(self, index, role):
if not index.isValid():
return None
row = index.row()
if role == QtCore.Qt.DisplayRole:
return self._items[row]
elif role == QtCore.Qt.DecorationRole:
item = self._items[row]
if item[0] != '#':
return None
else:
h = item.lstrip('#')
rgb = tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
color = QtGui.QColor(*rgb)
pixmap = QtGui.QPixmap(16, 16)
pixmap.fill(color)
icon = QtGui.QIcon(pixmap)
return icon
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
choices = [*HEATMAPS, 'Custom...', *COLORS]
model = IconModel(choices)
combo_box = QtWidgets.QComboBox()
combo_box.setModel(model)
def on_current_index_changed(index):
text = combo_box.itemText(index)
data = combo_box.itemData(index, QtCore.Qt.UserRole)
print(index, text, data, flush=True)
combo_box.currentIndexChanged[int].connect(on_current_index_changed)
combo_box.show()
sys.exit(app.exec_())
Solution
You have to implement the setData()
and insertRow()
method of the model since insertSeparator()
inserts data (empty string and null QIcon). For example you can use QStandardItemModel for this.
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
HEATMAPS = [
"viridis",
"inferno",
"ocean",
"hot",
"terrain",
"nipy_spectral",
]
COLORS = [
"#ffffff",
"#00ff00",
"#0000ff",
"#ff0000",
"#ffff00",
]
class ColorDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
value = index.data()
if value.startswith("#"):
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
pixmap = QtGui.QPixmap(option.decorationSize)
pixmap.fill(QtGui.QColor(value))
option.icon = QtGui.QIcon(pixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
model = QtGui.QStandardItemModel()
for choice in HEATMAPS + ["Custom ..."] + COLORS :
item = QtGui.QStandardItem(choice)
model.appendRow(item)
combo_box = QtWidgets.QComboBox()
combo_box.setModel(model)
combo_box.setItemDelegate(ColorDelegate(model))
combo_box.insertSeparator(len(HEATMAPS))
combo_box.insertSeparator(len(HEATMAPS) + 2)
def on_current_index_changed(index):
text = combo_box.itemText(index)
data = combo_box.itemData(index, QtCore.Qt.UserRole)
print(index, text, data, flush=True)
combo_box.currentIndexChanged[int].connect(on_current_index_changed)
combo_box.show()
sys.exit(app.exec_())
On the other hand, the model is not necessary since the logic of the icon can be handled by the delegate:
app = QtWidgets.QApplication(sys.argv)
combo_box = QtWidgets.QComboBox()
combo_box.addItems(HEATMAPS + ["Custom..."] + COLORS)
combo_box.setItemDelegate(ColorDelegate(combo_box))
combo_box.insertSeparator(len(HEATMAPS))
combo_box.insertSeparator(len(HEATMAPS) + 2)
def on_current_index_changed(index):
text = combo_box.itemText(index)
data = combo_box.itemData(index, QtCore.Qt.UserRole)
print(index, text, data, flush=True)
combo_box.currentIndexChanged[int].connect(on_current_index_changed)
combo_box.show()
sys.exit(app.exec_())
If you want to show the icon on the combobox display then a solution could modify the data method of the QStandardItemModel but a more efficient option is to create the icon and set it when the QStandardItem is created:
app = QtWidgets.QApplication(sys.argv)
model = QtGui.QStandardItemModel()
for choice in HEATMAPS + ["Custom ..."]:
item = QtGui.QStandardItem(choice)
model.appendRow(item)
for color in COLORS:
item = QtGui.QStandardItem(choice)
pixmap = QtGui.QPixmap(16, 16)
pixmap.fill(QtGui.QColor(color))
icon = QtGui.QIcon(pixmap)
item.setIcon(icon)
model.appendRow(item)
combo_box = QtWidgets.QComboBox()
combo_box.setModel(model)
combo_box.insertSeparator(len(HEATMAPS))
combo_box.insertSeparator(len(HEATMAPS) + 2)
def on_current_index_changed(index):
text = combo_box.itemText(index)
data = combo_box.itemData(index, QtCore.Qt.UserRole)
print(index, text, data, flush=True)
combo_box.currentIndexChanged[int].connect(on_current_index_changed)
combo_box.show()
sys.exit(app.exec_())
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.