Qt图形视图框架:将自定义图形项放入布局

只有继承布局项QGraphicsLayoutItem的对象才能放入布局。继承QGraphicsLayoutItem后需要重写setGeometry()、sizeHint()这两个函数。

自定义滑动条图形项为例,继承布局项。代码如下:

#ifndef SLIDERGRAPHICSPIXMAPITEM_H
#define SLIDERGRAPHICSPIXMAPITEM_H

#include <QGraphicsObject>
#include <QGraphicsLayoutItem>

class sliderGraphicsPixmapItem : public QGraphicsObject , public QGraphicsLayoutItem
{
    Q_OBJECT
public:
    sliderGraphicsPixmapItem(bool isInLayout,QString uuid,Qt::Orientation orientation);
    void setSliderOrientation(Qt::Orientation orientation)
    {
        this->sliderOrientation = orientation;
        update();
    }
    void setRange(const int maxValue,const int minValue)
    {
        this->maxValue = maxValue;
        this->minValue = minValue;
        update();
    }
    virtual QRectF boundingRect()const override;
    virtual void setGeometry(const QRectF &rect)override;

protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant &value)override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)override;
    virtual QPainterPath shape()const override;
    void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
    void hoverMoveEvent(QGraphicsSceneHoverEvent* event)override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent* event)override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)override;
    virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF())const override;

private:
    bool IsInResizeArea(const QPointF& pos);
    QSizeF itemSize;
    bool isResizing;  //是否正在改变大小的过程中
    QString uuid;
    Qt::Orientation sliderOrientation;
    int maxValue{100};
    int minValue;
    int nowValue{50};
    QRect thisSliderRect;//滑块区域
    bool pressThisSlider;//是否按下滑块
    bool inThisSliderRect;//鼠标在滑块上
    QRect sliderRect;
    bool isInLayout{false};//是否要添加到布局里
};

#endif // SLIDERGRAPHICSPIXMAPITEM_H
 #include "slidergraphicspixmapitem.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QDebug>

const qreal g_cResizePos[] = {9, 6, 3};

sliderGraphicsPixmapItem::sliderGraphicsPixmapItem(bool isInLayout, QString uuid, Qt::Orientation orientation)
    :QGraphicsObject(),
     QGraphicsLayoutItem(nullptr,false)
{
    setAcceptHoverEvents(true);
    setFlag(QGraphicsItem::ItemIsMovable,!isInLayout);
    setFlag(QGraphicsItem::ItemSendsScenePositionChanges);//图形项可发送位置变化信号
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemIsFocusable);
    this->itemSize = QSizeF(250,80);
    isResizing = false;
    this->uuid = uuid;
    setZValue(1);
    this->sliderOrientation = orientation;
    maxValue = 40;
    nowValue = 25;
    minValue = 15;
    this->isInLayout = isInLayout;

    setGraphicsItem(this);//不加这个图形项不会显示出来
}

QRectF sliderGraphicsPixmapItem::boundingRect() const
{
    return  QRectF(0, 0, itemSize.width() + 10, itemSize.height() + 10);
}

QPainterPath sliderGraphicsPixmapItem::shape() const
{
    QPainterPath path;
    path.addRect(QRectF(0, 0, itemSize.width(), itemSize.height() + 10));
    return path;
}

void sliderGraphicsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        QPointF point = event->pos();
        if(IsInResizeArea(point))
        {
            isResizing = true;
        }

        if(thisSliderRect.contains(point.x(),point.y()))
        {
            pressThisSlider = true;
            if(!isInLayout)
            {
                setFlag(QGraphicsItem::ItemIsMovable,false);
            }
        }
        else
        {
            pressThisSlider = false;
            if(!isInLayout)
            {
                setFlag(QGraphicsItem::ItemIsMovable,true);
            }
        }

        QGraphicsObject::mousePressEvent(event);
        return;
    }
    else if(event->button() == Qt::RightButton)
    {
        return;
    }
    return QGraphicsObject::mousePressEvent(event);
}

void sliderGraphicsPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
    if(pressThisSlider)
        pressThisSlider = false;

    if (event->button() == Qt::LeftButton && isResizing)
        isResizing = false;
    else
        QGraphicsObject::mouseReleaseEvent(event);
}

QVariant sliderGraphicsPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if ((change == ItemPositionChange || change == ItemPositionHasChanged) && scene()) // 控件发生移动
    {
        QPointF newPos = value.toPointF();
        QRectF rect(0, 0, scene()->width(), scene()->height());
        if (!rect.contains(newPos))//左上角
        {
            newPos.setX(qMin(rect.width(), qMax(newPos.x(), 0.0)));
            newPos.setY(qMin(rect.height(), qMax(newPos.y(), 0.0)));
            return newPos;
        }

        QRectF thisRectF = boundingRect();
        QPointF nowPos = QPointF(newPos.x() + thisRectF.width(),newPos.y());
        if(!rect.contains(nowPos))//右上角
        {
            newPos.setX(rect.width() - thisRectF.width());
            this->setPos(newPos);
            return newPos;
        }

        nowPos = QPointF(newPos.x(),newPos.y() + thisRectF.height());
        if(!rect.contains(nowPos))//左下角
        {
            newPos.setY(rect.height() - thisRectF.height());
            this->setPos(newPos);
            return newPos;
        }
    }
    return QGraphicsItem::itemChange(change, value);
}

void sliderGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(widget);
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);

    QRectF thisRectF = boundingRect();
    if(!isInLayout)
    {
        if(option->state & QStyle::State_Selected)
        {
            painter->setPen(QColor("#D8D8D8"));
            painter->drawRect(thisRectF);
            setZValue(2);
            painter->fillRect(thisRectF, QBrush(Qt::transparent));
        }
        else
        {
            setZValue(1);
            painter->fillRect(thisRectF, QBrush(Qt::transparent));
        }
    }

    QRect rect = thisRectF.toRect();
    painter->save();
    painter->setPen(Qt::transparent);
    if(sliderOrientation == Qt::Horizontal)
    {
        sliderRect = QRect(5,(rect.height() - 12) / 2,rect.width() - 10,12);
        painter->save();
        QColor slightlyOpaqueBlack(0, 0, 0, 63);
        painter->setBrush(slightlyOpaqueBlack);
        painter->drawRect(sliderRect);
        painter->restore();

        painter->save();
        painter->setBrush(QColor("#00beac"));
        int nowValueWidth = static_cast<int>(static_cast<float>(sliderRect.width() * (nowValue - minValue))  / static_cast<float>(maxValue - minValue));
        sliderRect.setWidth(nowValueWidth);
        painter->drawRoundRect(sliderRect);

        int thisSliderRectX = nowValueWidth + 5;
        if(thisSliderRectX < 5)
            thisSliderRectX = 5;
        if(thisSliderRectX > (5 + rect.width() - 20))
            thisSliderRectX = 5 + rect.width() - 20 - 10;
        thisSliderRect = QRect(thisSliderRectX,sliderRect.y() - 4,20,20);

        if(pressThisSlider || inThisSliderRect)
            painter->setBrush(QColor("#01968c"));
        painter->drawEllipse(thisSliderRect);
        painter->restore();
    }
    else
    {
        sliderRect = QRect((rect.width() - 12) / 2,5,12,rect.height() - 10);
        painter->save();
        QColor slightlyOpaqueBlack(0, 0, 0, 63);
        painter->setBrush(slightlyOpaqueBlack);
        painter->drawRect(sliderRect);
        painter->restore();
        painter->save();
        painter->setBrush(QColor("#00beac"));
        int nowValueHeight = static_cast<int>(static_cast<float>(sliderRect.height() * (nowValue - minValue))  / static_cast<float>(maxValue - minValue));
        sliderRect.setHeight(nowValueHeight);
        painter->drawRoundRect(sliderRect);

        int thisSliderRectY = nowValueHeight + 5;
        if(thisSliderRectY < 5)
            thisSliderRectY = 5;
        if(thisSliderRectY > (5 + rect.height() - 20))
            thisSliderRectY = 5 + rect.height() - 20 - 10;

        thisSliderRect = QRect(sliderRect.x() - 4,thisSliderRectY,20,20);

        if(pressThisSlider || inThisSliderRect)
            painter->setBrush(QColor("#01968c"));
        painter->drawEllipse(thisSliderRect);
        painter->restore();
    }
    painter->restore();

    if(!isInLayout)
    {
        if(option->state & QStyle::State_Selected)
        {
            qreal w = thisRectF.width();
            qreal h = thisRectF.height();
            painter->setPen(Qt::red);
            for (int i = 0; i < 3; ++i)//三角形
                painter->drawLine(static_cast<int>(w - g_cResizePos[i]) , static_cast<int>(h), static_cast<int>(w), static_cast<int>(h - g_cResizePos[i]));
        }
    }

    painter->restore();
}

void sliderGraphicsPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    QPointF point = event->pos();

    if (isResizing || (IsInResizeArea(point) && isSelected()))
        setCursor(Qt::SizeFDiagCursor);
    else
        setCursor(Qt::ArrowCursor);

    if(thisSliderRect.contains(point.x(),point.y()))
    {
        inThisSliderRect = true;
    }
    else
    {
        inThisSliderRect = false;
    }
    update();

    QGraphicsObject::hoverMoveEvent(event);
}

void sliderGraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    if(pressThisSlider)
    {
        int temp = 0;
        if(sliderOrientation == Qt::Horizontal)
        {
            temp = static_cast<int>(event->pos().x() - 5) * (maxValue - minValue) / static_cast<float>(boundingRect().width() - 10);
        }
        else
        {
            temp = static_cast<int>(event->pos().y() - 5) * (maxValue - minValue) / static_cast<float>(boundingRect().height() - 10);
        }
        if(temp < 0)
            temp = 0;
        if(temp > (maxValue - minValue))
            temp = (maxValue - minValue);

        nowValue = temp + minValue;
        update();
    }

    if (isResizing & !isInLayout)
    {
        qreal w = event->pos().x();
        qreal h = event->pos().y();
        if (w > 0)
            itemSize.setWidth(w);
        if (h > 0)
            itemSize.setHeight(h);
        prepareGeometryChange();
    }
    else
    {
        QGraphicsObject::mouseMoveEvent(event);
    }
}

bool sliderGraphicsPixmapItem::IsInResizeArea(const QPointF& pos)
{
    return (pos.x() - itemSize.width() + g_cResizePos[0]) > (itemSize.height() - pos.y());
}

//小部件设置布局的时候调用,缓存大小。
QSizeF sliderGraphicsPixmapItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint)const
{
    auto p = parentItem()->toGraphicsObject();
    qDebug()<<p->metaObject()->className()<<which;

    switch ( which )
    {
        case Qt::MinimumSize:
        return QSizeF(10,10);
        case Qt::PreferredSize:
            return this->boundingRect().size();
        case Qt::MaximumSize:
            return QSizeF(10000,10000);
        default:
            return this->boundingRect().size();
    }
    return constraint;
}

void sliderGraphicsPixmapItem::setGeometry(const QRectF &rect)
{
    prepareGeometryChange();
    QGraphicsLayoutItem::setGeometry(rect);
    setPos(rect.topLeft());

    QSizeF effectiveSize = rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
                                .boundedTo(effectiveSizeHint(Qt::MaximumSize));
    itemSize = QRectF(rect.topLeft(), effectiveSize).size();
}

然后是小部件代理。也做了一些修改。代码如下:

#ifndef GRAPHICSPROXYWIDGET_H
#define GRAPHICSPROXYWIDGET_H

#include <QGraphicsProxyWidget>

class GraphicsProxyWidget : public QGraphicsProxyWidget
{
    Q_OBJECT
public:
    GraphicsProxyWidget(QGraphicsItem *parent = nullptr);
    void setCenterWidget(QWidget * w);
    void setCenterLayout(QGraphicsLayout * layout);
protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant &value)override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)override;
    virtual QRectF boundingRect()const override;
    void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
    void hoverMoveEvent(QGraphicsSceneHoverEvent* event)override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent* event)override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)override;
    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)override;
private:
    QSizeF itemSize;
    QSizeF itemMinSize;
    bool IsInResizeArea(const QPointF& pos);
    bool isResizing;
    QPointF dragPosition;
    bool grabbedByWidget;
};

#endif // GRAPHICSPROXYWIDGET_H
#include "graphicsproxywidget.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsLayout>

const qreal g_cResizePos[] = {9, 6, 3};

GraphicsProxyWidget::GraphicsProxyWidget(QGraphicsItem *parent)
    :QGraphicsProxyWidget(parent)
{
    setAcceptHoverEvents(true);
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemSendsScenePositionChanges);//图形项可发送位置变化信号
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemIsFocusable);
    this->itemSize = QSizeF(100,100);
}

bool GraphicsProxyWidget::IsInResizeArea(const QPointF& pos)
{
    return (pos.x() - itemSize.width() + g_cResizePos[0]) > (itemSize.height() - pos.y());
}

QRectF GraphicsProxyWidget::boundingRect() const
{
    return  QRectF(0, 0, itemSize.width() + 5, itemSize.height() + 5);
}

void GraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        QPointF pos = event->pos();
        if(IsInResizeArea(pos))
        {
            isResizing = true;
        }
        else
        {
            if(widget())
            {
                if (auto alienWidget = widget()->childAt(pos.toPoint()))
                {
                    QGraphicsProxyWidget::mousePressEvent(event);
                    grabbedByWidget = true;
                }
                else
                {
                    QGraphicsItem::mousePressEvent(event);
                    grabbedByWidget = false;
                }
            }
            else
            {
                QGraphicsItem::mousePressEvent(event);
                grabbedByWidget = false;
            }
        }
    }
}

void GraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    if(widget())
    {
        if (auto alienWidget = widget()->childAt(event->pos().toPoint()))
        {
            QGraphicsProxyWidget::mouseDoubleClickEvent(event);
            grabbedByWidget = true;
        }
        else
        {
            QGraphicsItem::mouseDoubleClickEvent(event);
            grabbedByWidget = false;
        }
    }
    else
    {
        QGraphicsItem::mouseDoubleClickEvent(event);
        grabbedByWidget = false;
    }
}

void GraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && isResizing)
        isResizing = false;
    else
    {
        if (grabbedByWidget)
            QGraphicsProxyWidget::mouseReleaseEvent(event);
        else
            QGraphicsItem::mouseReleaseEvent(event);
    }

    grabbedByWidget = false;
}

QVariant GraphicsProxyWidget::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if ((change == ItemPositionChange || change == ItemPositionHasChanged) && scene()) // 控件发生移动
    {
        QPointF newPos = value.toPointF();
        QRectF rect(0, 0, scene()->width(), scene()->height());
        if (!rect.contains(newPos))//左上角
        {
            newPos.setX(qMin(rect.width(), qMax(newPos.x(), 0.0)));
            newPos.setY(qMin(rect.height(), qMax(newPos.y(), 0.0)));
            return newPos;
        }

        QRectF thisRectF = boundingRect();
        QPointF nowPos = QPointF(newPos.x() + thisRectF.width(),newPos.y());
        if(!rect.contains(nowPos))//右上角
        {
            newPos.setX(rect.width() - thisRectF.width());
            this->setPos(newPos);
            return newPos;
        }

        nowPos = QPointF(newPos.x(),newPos.y() + thisRectF.height());
        if(!rect.contains(nowPos))//左下角
        {
            newPos.setY(rect.height() - thisRectF.height());
            this->setPos(newPos);
            return newPos;
        }
    }
    return QGraphicsItem::itemChange(change, value);
}

void GraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing,true);
    painter->setRenderHint(QPainter::SmoothPixmapTransform,true);
    painter->setRenderHint(QPainter::TextAntialiasing,true);

    QRectF thisRectF = boundingRect();
    painter->fillRect(option->rect,QBrush(QColor("#e0e0e0")));

    if(option->state & QStyle::State_Selected)
    {
        qreal w = thisRectF.width();
        qreal h = thisRectF.height();
        painter->setPen(Qt::red);
        for (int i = 0; i < 3; ++i)//三角形
            painter->drawLine(static_cast<int>(w - g_cResizePos[i]) , static_cast<int>(h), static_cast<int>(w), static_cast<int>(h - g_cResizePos[i]));
    }

    painter->restore();
    QGraphicsProxyWidget::paint(painter,option,widget);
}

void GraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    if (isResizing || (IsInResizeArea(event->pos()) && isSelected()))
    {
        setCursor(Qt::SizeFDiagCursor);
        QGraphicsObject::hoverMoveEvent(event);
    }
    else
    {
        if(widget())
        {
            if (auto alienWidget = widget()->childAt(event->pos().toPoint()))
            {
                setCursor(alienWidget->cursor());
            }
            else
                setCursor(Qt::ArrowCursor);
        }
        else
            setCursor(Qt::ArrowCursor);

        QGraphicsProxyWidget::hoverMoveEvent(event);
    }
}

void GraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    if (isResizing)
    {
        qreal w = event->pos().x();
        qreal h = event->pos().y();
        if (w > itemMinSize.width())
            itemSize.setWidth(w);
        if (h > itemMinSize.height())
            itemSize.setHeight(h);
        resize(w,h);
        if(layout())
        {
            layout()->invalidate();
        }
        prepareGeometryChange();
    }
    else
    {
        if (grabbedByWidget)
        {
            QGraphicsProxyWidget::mouseMoveEvent(event);
        }
        else
            QGraphicsItem::mouseMoveEvent(event);
    }
}

void GraphicsProxyWidget::setCenterWidget(QWidget * w)
{
    setWidget(w);
    itemSize = w->sizeHint();
    itemMinSize = w->minimumSize();
    update();
}

void GraphicsProxyWidget::setCenterLayout(QGraphicsLayout * layout)
{
    setLayout(layout);
    setContentsMargins(10,10,10,10);

    adjustSize();
    itemSize = this->sizeHint(Qt::PreferredSize);
    itemMinSize = this->sizeHint(Qt::MinimumDescent);
}

效果:

    QGraphicsGridLayout *layout = new QGraphicsGridLayout;
    layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 0, 0);
    layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 0, 1);
    layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 1, 0);
    layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 1, 1);

    GraphicsProxyWidget *form = new GraphicsProxyWidget;
    form->setCenterLayout(layout);
    gphs->addItem(form);

效果勉强还可以\(^o^)/。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值