与模型组合的项目分隔符
Item separator in combo with model
我有一个下拉列表,其中包含热图和纯色名称。它还有一个 'Custom...' 选项,选中后会打开颜色选择器。我想将 'Custom...' 项与十六进制颜色分开。
不使用模型时,添加分隔符很容易:
if self.count() > 1:
self.insertSeparator(self.count()-1)
如何在使用模型填充组合时插入分隔符?
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_())
您必须实施模型的 setData()
和 insertRow()
方法,因为 insertSeparator()
插入数据(空字符串和空 QIcon)。例如,您可以为此使用 QStandardItemModel。
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_())
另一方面,模型不是必需的,因为图标的逻辑可以由委托处理:
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_())
如果你想在组合框显示上显示图标,那么解决方案可以修改 QStandardItemModel 的数据方法,但更有效的选择是创建图标并在创建 QStandardItem 时设置它:
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_())
如果您要实现自己的模型,您只需要 return 当角色是 AccessibleDescriptionRole 时,在模型的 data() 方法中 return 文字字符串“separator”(或者在 c++ QLatin1String("separator") 中对于应该作为分隔符的行。
if role == Qt.AccessibleDescriptionRole:
return 'separator'
这就是 QComboBoxDelegate 寻找的决定是否绘制分隔符的内容。这也使 screen-reader 软件更容易访问您的组合框。
我有一个下拉列表,其中包含热图和纯色名称。它还有一个 'Custom...' 选项,选中后会打开颜色选择器。我想将 'Custom...' 项与十六进制颜色分开。
不使用模型时,添加分隔符很容易:
if self.count() > 1:
self.insertSeparator(self.count()-1)
如何在使用模型填充组合时插入分隔符?
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_())
您必须实施模型的 setData()
和 insertRow()
方法,因为 insertSeparator()
插入数据(空字符串和空 QIcon)。例如,您可以为此使用 QStandardItemModel。
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_())
另一方面,模型不是必需的,因为图标的逻辑可以由委托处理:
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_())
如果你想在组合框显示上显示图标,那么解决方案可以修改 QStandardItemModel 的数据方法,但更有效的选择是创建图标并在创建 QStandardItem 时设置它:
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_())
如果您要实现自己的模型,您只需要 return 当角色是 AccessibleDescriptionRole 时,在模型的 data() 方法中 return 文字字符串“separator”(或者在 c++ QLatin1String("separator") 中对于应该作为分隔符的行。
if role == Qt.AccessibleDescriptionRole:
return 'separator'
这就是 QComboBoxDelegate 寻找的决定是否绘制分隔符的内容。这也使 screen-reader 软件更容易访问您的组合框。