使用自定义模型和委托的自定义 ComboBox
Custom ComboBox using custom model and delegate
我有一个形式为 (string, integer, bool ), 像这样:
[ ('Item 1', 12, True), ('Item 2', 156, True), ('Item 3', 19, False) ]
我想按以下方式在 QComboBox 中显示:
- 元组的第一个元素左对齐
- 数字右对齐颜色较深
- 如果第三个元素设置为
True
,则会显示一个彩色矩形(将替换为图标)。
如果设置为 False
,则不显示矩形,但 space 应保持为空,而不是向左移动文本
这是我到目前为止所拥有的:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MyModel(QAbstractListModel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.data = [
('Item 1', 12, True),
('Item 2', 156, True),
('Item 3', 19, False)
]
def data(self, index, role=None):
if role == Qt.DisplayRole:
text = self.data[index.row()][0]
return text
if role == Qt.DecorationRole:
status = self.data[index.row()][2]
if status:
return QColor('#22FF35')
def rowCount(self, parent=None, *args, **kwargs):
return len(self.data)
class MyDelegate(QStyledItemDelegate):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def paint(self, painter, option, index):
painter.save()
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)
style = options.widget.style()
style.drawItemText(painter, options.widget.rect(), Qt.AlignLeft,
options.widget.palette(), True, options.text,
QPalette.Text)
painter.restore()
class MyComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedSize(200, 75)
self.model = MyModel()
self.setModel(self.model)
self.setItemDelegate(MyDelegate())
if __name__ == '__main__':
app = QApplication([])
window = MyComboBox()
window.show()
app.exec()
代码显然不完整,没有实现我的想法。
出现了一些问题,例如:
如何将第一项和第二项都传递给委托人?
如果模型 returns 是列表或元组,委托中的 options.text
将为空。
在这种情况下,只需将右侧的文本绘制在默认文本之上:
TEXT_ROLE, VALUE_ROLE, STATUS_ROLE = ((Qt.UserRole + i + 1) for i in range(3))
class MyModel(QAbstractListModel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.data = [
("Item 1", 12, True),
("Item 2", 156, True),
("Item 3", 19, False),
("Item 4", 126, True),
("Item 5", 100, False),
]
def data(self, index, role=None):
if 0 <= index.row() < self.rowCount():
item = self.data[index.row()]
if role in (TEXT_ROLE, Qt.DisplayRole):
return item[0]
elif role == VALUE_ROLE:
return item[1]
elif role == STATUS_ROLE:
return item[2]
def rowCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return len(self.data)
class MyDelegate(QStyledItemDelegate):
RIGHT_MARGIN = 4
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
status = index.data(STATUS_ROLE)
option.features |= QStyleOptionViewItem.HasDecoration
pixmap = QPixmap(option.decorationSize)
pixmap.fill(QColor("#22FF35" if status else "transparent"))
option.icon = QIcon(pixmap)
def paint(self, painter, option, index):
value = index.data(VALUE_ROLE)
super().paint(painter, option, index)
painter.drawText(
option.rect.adjusted(0, 0, -self.RIGHT_MARGIN, 0),
Qt.AlignRight | Qt.AlignVCenter,
str(value),
)
我有一个形式为 (string, integer, bool ), 像这样:
[ ('Item 1', 12, True), ('Item 2', 156, True), ('Item 3', 19, False) ]
我想按以下方式在 QComboBox 中显示:
- 元组的第一个元素左对齐
- 数字右对齐颜色较深
- 如果第三个元素设置为
True
,则会显示一个彩色矩形(将替换为图标)。
如果设置为False
,则不显示矩形,但 space 应保持为空,而不是向左移动文本
这是我到目前为止所拥有的:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MyModel(QAbstractListModel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.data = [
('Item 1', 12, True),
('Item 2', 156, True),
('Item 3', 19, False)
]
def data(self, index, role=None):
if role == Qt.DisplayRole:
text = self.data[index.row()][0]
return text
if role == Qt.DecorationRole:
status = self.data[index.row()][2]
if status:
return QColor('#22FF35')
def rowCount(self, parent=None, *args, **kwargs):
return len(self.data)
class MyDelegate(QStyledItemDelegate):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def paint(self, painter, option, index):
painter.save()
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)
style = options.widget.style()
style.drawItemText(painter, options.widget.rect(), Qt.AlignLeft,
options.widget.palette(), True, options.text,
QPalette.Text)
painter.restore()
class MyComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedSize(200, 75)
self.model = MyModel()
self.setModel(self.model)
self.setItemDelegate(MyDelegate())
if __name__ == '__main__':
app = QApplication([])
window = MyComboBox()
window.show()
app.exec()
代码显然不完整,没有实现我的想法。
出现了一些问题,例如:
如何将第一项和第二项都传递给委托人?
如果模型 returns 是列表或元组,委托中的 options.text
将为空。
在这种情况下,只需将右侧的文本绘制在默认文本之上:
TEXT_ROLE, VALUE_ROLE, STATUS_ROLE = ((Qt.UserRole + i + 1) for i in range(3))
class MyModel(QAbstractListModel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.data = [
("Item 1", 12, True),
("Item 2", 156, True),
("Item 3", 19, False),
("Item 4", 126, True),
("Item 5", 100, False),
]
def data(self, index, role=None):
if 0 <= index.row() < self.rowCount():
item = self.data[index.row()]
if role in (TEXT_ROLE, Qt.DisplayRole):
return item[0]
elif role == VALUE_ROLE:
return item[1]
elif role == STATUS_ROLE:
return item[2]
def rowCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return len(self.data)
class MyDelegate(QStyledItemDelegate):
RIGHT_MARGIN = 4
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
status = index.data(STATUS_ROLE)
option.features |= QStyleOptionViewItem.HasDecoration
pixmap = QPixmap(option.decorationSize)
pixmap.fill(QColor("#22FF35" if status else "transparent"))
option.icon = QIcon(pixmap)
def paint(self, painter, option, index):
value = index.data(VALUE_ROLE)
super().paint(painter, option, index)
painter.drawText(
option.rect.adjusted(0, 0, -self.RIGHT_MARGIN, 0),
Qt.AlignRight | Qt.AlignVCenter,
str(value),
)