在 QTreeView 中为不同的复选框设置不同的图像
Set different images for different checkboxes in a QTreeView
我将 QTreeView 子类化,我有两列有复选框。我想设置两个不同的图像:一个用于第一列,另一个用于第二列。我知道我可以更改样式表中的图像:
QTreeView::indicator:checked{
image: url(:/checked);
}
QTreeView::indicator:unchecked{
image: url(:/unchecked);
}
但它会更改树视图中的所有复选框。有没有办法用样式表来做,还是我需要使用委托?
简短回答:样式表无法做到这一点(据我所知)。它们在 Qt 中是一个非常不成熟的特性,而且似乎也没有对它们进行开发。
你能做什么:
样式表
您不能将属性分配给列或项目,也不能按索引访问列。
但是您可以使用一些伪状态选择器,例如 :first
、:middle
和 :last
:
QTreeView::indicator:first:checked{
background: red;
}
QTreeView::indicator:middle:checked{
background: blue;
}
QTreeView::indicator:unchecked{
background: lightgray;
}
为了简单起见,我使用了颜色而不是图像。
但是请注意,这些伪状态是实际当前可见的状态,因此如果允许用户对列重新排序,则列的样式可能会更改。例如,如果用户拖动 :middle
列之一并将其放在末尾,则框将不再是蓝色。
装饰角色
您可以使用 Qt::DecorationRole
伪造它。
为此,您必须通过继承 QTreeView
或安装 event filter 来接收 mousePressEvent
。然后,当用户单击图标区域时,您可以更改图标(通过 Qt::DecorationRole
+ emit dataChanged()
)。
这当然不适用于键盘。
自定义 ItemDelegate
子类 QStyledItemDelegate
并覆盖 paint()
,正如您所建议的。
自定义样式
如果您正在创建样式繁多的应用程序,您可能迟早要创建自定义样式。样式表只是不支持某些功能。
为此,继承 QProxyStyle
,覆盖 drawPrimitive
并在 QStyle::PE_IndicatorViewItemCheck
通过时处理绘图。您还将收到一个 QStyleOptionViewItem
,其中有一些有用的属性,例如 checkState
、features
(如果有复选框,则包含 QStyleOptionViewItem::HasCheckIndicator
),当然还有 index
这样您就可以确定要绘制哪种复选框。
编辑:附录
使用自定义 QStyledItemDelegate 的示例
void MyItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyledItemDelegate::paint(painter, option, index);
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
drawCheckBox(painter, checkRect, opt.checkState, index);
}
void MyItemDelegate::drawCheckBox(QPainter * painter, const QRect & checkRect, Qt::CheckState checkState, const QModelIndex & index) const
{
if (checkState == Qt::Checked)
{
switch (index.column())
{
case 0:
painter->fillRect(checkRect, Qt::red);
break;
default:
painter->fillRect(checkRect, Qt::blue);
}
}
else
{
painter->fillRect(checkRect, Qt::lightGray);
}
}
这个例子简单快捷。只需在 QStyledItemDelegate 绘制的复选框上绘制即可。但是需要您填满整个方框,否则将显示原件。
您可以尝试使用 QStyledItemDelegate 绘制除复选框以外的任何内容,然后再绘制复选框,但这有点难,如果您不想花太多时间,会给您留下一些小的绘图工件就可以了。
使用自定义 QProxyStyle 的示例
void MyStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * w) const
{
if (pe == QStyle::PE_IndicatorViewItemCheck)
{
const QStyleOptionViewItem * o = static_cast<const QStyleOptionViewItem *>(opt);
drawCheckBox(p, opt->rect, o->checkState, o->index);
return;
}
QProxyStyle::drawPrimitive(pe, opt, p, w);
}
drawCheckBox()
函数与第一个例子相同。
如您所见,这种方式更简单、更清晰,并且有 none 的缺点。您可以全局应用该样式,也可以只为单个小部件应用样式。
我将 QTreeView 子类化,我有两列有复选框。我想设置两个不同的图像:一个用于第一列,另一个用于第二列。我知道我可以更改样式表中的图像:
QTreeView::indicator:checked{
image: url(:/checked);
}
QTreeView::indicator:unchecked{
image: url(:/unchecked);
}
但它会更改树视图中的所有复选框。有没有办法用样式表来做,还是我需要使用委托?
简短回答:样式表无法做到这一点(据我所知)。它们在 Qt 中是一个非常不成熟的特性,而且似乎也没有对它们进行开发。
你能做什么:
样式表
您不能将属性分配给列或项目,也不能按索引访问列。
但是您可以使用一些伪状态选择器,例如 :first
、:middle
和 :last
:
QTreeView::indicator:first:checked{
background: red;
}
QTreeView::indicator:middle:checked{
background: blue;
}
QTreeView::indicator:unchecked{
background: lightgray;
}
为了简单起见,我使用了颜色而不是图像。
但是请注意,这些伪状态是实际当前可见的状态,因此如果允许用户对列重新排序,则列的样式可能会更改。例如,如果用户拖动 :middle
列之一并将其放在末尾,则框将不再是蓝色。
装饰角色
您可以使用 Qt::DecorationRole
伪造它。
为此,您必须通过继承 QTreeView
或安装 event filter 来接收 mousePressEvent
。然后,当用户单击图标区域时,您可以更改图标(通过 Qt::DecorationRole
+ emit dataChanged()
)。
这当然不适用于键盘。
自定义 ItemDelegate
子类 QStyledItemDelegate
并覆盖 paint()
,正如您所建议的。
自定义样式
如果您正在创建样式繁多的应用程序,您可能迟早要创建自定义样式。样式表只是不支持某些功能。
为此,继承 QProxyStyle
,覆盖 drawPrimitive
并在 QStyle::PE_IndicatorViewItemCheck
通过时处理绘图。您还将收到一个 QStyleOptionViewItem
,其中有一些有用的属性,例如 checkState
、features
(如果有复选框,则包含 QStyleOptionViewItem::HasCheckIndicator
),当然还有 index
这样您就可以确定要绘制哪种复选框。
编辑:附录
使用自定义 QStyledItemDelegate 的示例
void MyItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyledItemDelegate::paint(painter, option, index);
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
drawCheckBox(painter, checkRect, opt.checkState, index);
}
void MyItemDelegate::drawCheckBox(QPainter * painter, const QRect & checkRect, Qt::CheckState checkState, const QModelIndex & index) const
{
if (checkState == Qt::Checked)
{
switch (index.column())
{
case 0:
painter->fillRect(checkRect, Qt::red);
break;
default:
painter->fillRect(checkRect, Qt::blue);
}
}
else
{
painter->fillRect(checkRect, Qt::lightGray);
}
}
这个例子简单快捷。只需在 QStyledItemDelegate 绘制的复选框上绘制即可。但是需要您填满整个方框,否则将显示原件。
您可以尝试使用 QStyledItemDelegate 绘制除复选框以外的任何内容,然后再绘制复选框,但这有点难,如果您不想花太多时间,会给您留下一些小的绘图工件就可以了。
使用自定义 QProxyStyle 的示例
void MyStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * w) const
{
if (pe == QStyle::PE_IndicatorViewItemCheck)
{
const QStyleOptionViewItem * o = static_cast<const QStyleOptionViewItem *>(opt);
drawCheckBox(p, opt->rect, o->checkState, o->index);
return;
}
QProxyStyle::drawPrimitive(pe, opt, p, w);
}
drawCheckBox()
函数与第一个例子相同。
如您所见,这种方式更简单、更清晰,并且有 none 的缺点。您可以全局应用该样式,也可以只为单个小部件应用样式。