自定义QGraphicsItem选中样式

    xiaoxiao2023-06-03  79

    简述

    在 Scene 中添加 QGraphicsItem 后,当选中该 item 时,会看到边缘区域出现虚线,感觉不太美观。下面,我们来讲解如何去掉虚线并自定义选中样式。

    简述默认样式虚线的由来去掉虚线自定义选中样式

    默认样式

    以椭圆为例,其它如:矩形、多边形等 item 类似。

    // 构建一个椭圆 QGraphicsEllipseItem *pItem = new QGraphicsEllipseItem(); // 设置可选中、可移动 pItem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); // 设置样式(画笔 - 边框色 画刷 - 背景色) QPen pen = pItem->pen(); pen.setWidth(2); pen.setColor(QColor(0, 160, 230)); pItem->setPen(pen); pItem->setBrush(QColor(247, 160, 57)); // ......

    要出现选中效果,需要为 item 设置选中标识 QGraphicsItem::ItemIsSelectable。设置之后,选中 item,默认样式如下:

    边框区域出现虚线部分,下面我们来逐步分析。

    虚线的由来

    要知道虚线是怎么来的,最好的办法就是看源码。还是那句话 - 源码面前,了无秘密!

    void QGraphicsPathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_D(QGraphicsPathItem); Q_UNUSED(widget); painter->setPen(d->pen); painter->setBrush(d->brush); painter->drawPath(d->path); if (option->state & QStyle::State_Selected) qt_graphicsItem_highlightSelected(this, painter, option); }

    前半部分正是椭圆的样式,虚线部分主要是后半部分 qt_graphicsItem_highlightSelected() 所实现的内容,进去看看吧!

    static void qt_graphicsItem_highlightSelected( QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option) { const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); if (qFuzzyIsNull(qMax(murect.width(), murect.height()))) return; const QRectF mbrect = painter->transform().mapRect(item->boundingRect()); if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0)) return; qreal itemPenWidth; switch (item->type()) { case QGraphicsEllipseItem::Type: itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF(); break; case QGraphicsPathItem::Type: itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF(); break; case QGraphicsPolygonItem::Type: itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF(); break; case QGraphicsRectItem::Type: itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF(); break; case QGraphicsSimpleTextItem::Type: itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF(); break; case QGraphicsLineItem::Type: itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF(); break; default: itemPenWidth = 1.0; } const qreal pad = itemPenWidth / 2; const qreal penWidth = 0; // cosmetic pen const QColor fgcolor = option->palette.windowText().color(); const QColor bgcolor( // ensure good contrast against fgcolor fgcolor.red() > 127 ? 0 : 255, fgcolor.green() > 127 ? 0 : 255, fgcolor.blue() > 127 ? 0 : 255); painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine)); painter->setBrush(Qt::NoBrush); painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine)); painter->setBrush(Qt::NoBrush); painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); }

    来看最主要的部分:先使用颜色为 bgcolor 的画笔绘制实线,再使用窗体文本的颜色绘制虚线,而绘制的区域则是 boundingRect() 边界区域的各个方向分别向外扩展 pad 像素。

    去掉虚线

    既然知道了原理,那么去掉虚线就变得很容易。

    class CustomItem : public QGraphicsEllipseItem { protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE { QStyleOptionGraphicsItem op; op.initFrom(widget); // 判断选中时,设置状态为 State_None if (option->state & QStyle::State_Selected) op.state = QStyle::State_None; // 调用默认方法,进行原始绘制 QGraphicsEllipseItem::paint(painter, &op, widget); } };

    自定义选中样式

    去掉虚线后,选中时没有样式也不行,那就随便添加一个自定义样式吧,根据源码修改。

    class CustomItem : public QGraphicsEllipseItem { protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE { QStyleOptionGraphicsItem op; op.initFrom(widget); // 判断选中时,设置状态为 State_None if (option->state & QStyle::State_Selected) op.state = QStyle::State_None; ; // 调用默认方法,进行原始绘制 QGraphicsEllipseItem::paint(painter, &op, widget); // 选中时绘制 if (option->state & QStyle::State_Selected) { qreal itemPenWidth = pen().widthF(); const qreal pad = itemPenWidth / 2; const qreal penWidth = 0; // 边框区域颜色 QColor color = QColor(Qt::yellow); // 绘制实线 painter->setPen(QPen(color, penWidth, Qt::SolidLine)); painter->setBrush(Qt::NoBrush); painter->drawRect(boundingRect().adjusted(pad, pad, -pad, -pad)); // 绘制虚线 painter->setPen(QPen(color, 0, Qt::DashLine)); painter->setBrush(Qt::NoBrush); painter->drawRect(boundingRect().adjusted(pad, pad, -pad, -pad)); } } };

    这时,我们实现了一个选中时边框区域为黄色的虚线框(其他样式,同理)。很简单吧,所以呢,没事多瞅瞅源码吧O(∩_∩)O~!

    相关资源:Qt使用QGraphicsView实现滑动窗体效果
    最新回复(0)