【问题标题】:Item separator in combo with model与模型组合的项目分隔符
【发布时间】:2021-10-21 05:33:15
【问题描述】:

我有一个下拉菜单,其中包含热图和纯色名称。它还有一个“自定义...”选项,选中时会打开一个颜色选择器。我想将“自定义...”项与十六进制颜色分开。

不使用模型时,添加分隔符很容易:

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_())

【问题讨论】:

    标签: python pyqt pyqt5 qcombobox


    【解决方案1】:

    您必须实现模型的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_())
    

    【讨论】:

    • 这里有很多好东西,谢谢。如何让装饰出现在组合显示文本中?下拉时会出现装饰,但一旦选择,只会显示文本。
    猜你喜欢
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 2019-04-14
    • 2017-06-26
    相关资源
    最近更新 更多