QCustomPlot之Item的移动和缩放(十二)

首先看一下效果图
移动和缩放Item

移动

Item移动的主要思想是改变Item下所有QCPItemPosition的位置来达到移动的目的

void DPLPlot::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {

        if (auto *item = itemAt(event->pos(), true)) {
            emit mousePress(event);   // 由于我们直接返回了,所以要负责将鼠标点击信号发送出去

            // deselectAll();
            mMousePress = true;
            mLastPos = event->pos();
            item->setSelected(true);
            replot();
            return;   // 如果点击的是一个item直接返回,不然QCustomPlot会把事件传递给其它的层对象(例如:轴矩形)
        }
    }

    QCustomPlot::mousePressEvent(event);
}


void DPLPlot::mouseMoveEvent(QMouseEvent *event)
{
    QCustomPlot::mouseMoveEvent(event);

    if (mMousePress) {
        auto items = selectedItems();

        foreach (auto *item, items) {
            if (auto *sizeHandle = qobject_cast<QCPSizeHandle *>(item))
                mSizeHandleManager->handleItemResize(sizeHandle, event->pos() - mLastPos);  // 控制item缩放
            else
                mSizeHandleManager->handleItemMove(item, event->pos() - mLastPos);  // 控制item移动

        }
        mLastPos = event->pos();
        replot();
    }
}

void DPLPlot::mouseReleaseEvent(QMouseEvent *event)
{
    if (mMousePress) {
        mMousePress = false;
        if (auto *item = itemAt(event->pos(), true)) {
            emit mouseReleaseEvent(event); // 由于我们直接返回了,所以要负责将鼠标点击信号发送出去
            item->setSelected(false);
            replot();
            return;
        }
    }
    QCustomPlot::mouseReleaseEvent(event);
}

如下所示:我们改变item的所有QCPItemPosition的位置

void QCPSizeHandleManager::handleItemMove(QCPAbstractItem *item, const QPointF &delta)
{
    if (!item)
        return;

    auto positions = item->positions();
    foreach (auto *position, positions)
        position->setPixelPosition(position->pixelPosition() + delta);
}
缩放

与移动稍微不同的是,缩放只是移动一个QCPItemPosition的位置就可以了,不过为了让缩放点可视化,我们在缩放点位置新增了一个QCPSizeHandle,QCPSizeHandle是一个自定义的Item,如果你还不会自定义Item,请看上篇QCustomPlot之鼠标悬浮显示值(十一)

QCPSizeHandle::QCPSizeHandle(QCustomPlot *parentPlot)
    : QCPAbstractItem(parentPlot),
      position(createPosition(QLatin1String("position"))),
      mHovered(false)
{
//    position->setType(QCPItemPosition::ptAbsolute);
    setBrush(QColor("#436EEE"));
    setHoveredBrush(QColor("#1C86EE"));
    setSelectedBrush(QColor("#3A5FCD"));
    setSize(8);
}

QCPSizeHandle::~QCPSizeHandle()
{
}

void QCPSizeHandle::setBrush(const QBrush &brush)
{
    mBrush = brush;
}

void QCPSizeHandle::setSelectedBrush(const QBrush &brush)
{
    mSelectedBrush = brush;
}

void QCPSizeHandle::setHoveredBrush(const QBrush &brush)
{
    mHoveredBrush = brush;
}

void QCPSizeHandle::setSize(double size)
{
    mSize = size;
}

double QCPSizeHandle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
{
    Q_UNUSED(details)
    if (onlySelectable && !mSelectable)
        return -1;

    QPointF itemPos = position->pixelPosition();
    QRectF rect = QRectF(itemPos.x() - mSize * 0.5, itemPos.y() - mSize * 0.5, mSize, mSize);
    bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
    return rectDistance(rect, pos, filledRect);
}

void QCPSizeHandle::draw(QCPPainter *painter)
{
    QRectF rect(-mSize * 0.5, -mSize * 0.5, mSize, mSize);

    painter->translate(position->pixelPosition());
    painter->setClipRect(rect);

    painter->setPen(Qt::NoPen);
    painter->setBrush(mainBrush());
    painter->drawRect(rect);
}

QBrush QCPSizeHandle::mainBrush() const
{
    return selected() ? mSelectedBrush : (mHovered ? mHoveredBrush : mBrush);
}

如下所示:我们将QCPSizeHandle锚定在Item的QCPItemPosition上,这样我们可以不必关心QCPSizeHandle的位置,它总是跟随QCPItemPosition移动

void QCPSizeHandleManager::addItem(QCPAbstractItem *item, bool showHandlesLines)
{
    if (!item || mHandles.contains(item))
        return;

    if (item->positions().size() < 2)  // 要改变item的大小,最起码要两个位置
        return;

    QList<QCPSizeHandle *> handles;
    foreach (auto *position, item->positions()) {
            handles.push_back(addHandleToPosition(position));
    }

    ControlItemData data;
    data.showHandlesLines = showHandlesLines;
    data.handles = handles;
    mHandles.insert(item, data);
}
void QCPSizeHandleManager::handleItemResize(QCPSizeHandle *sizeHandle, const QPointF &delta)
{
    if (!sizeHandle)
        return;

    auto *parentPosition = static_cast<QCPItemPosition *>(sizeHandle->position->parentAnchor());
    if (!parentPosition)
        return;

    parentPosition->setPixelPosition(parentPosition->pixelPosition() + delta);
}

QCPSizeHandle *QCPSizeHandleManager::addHandleToPosition(QCPItemPosition *position)
{
    auto *handle = new QCPSizeHandle(mParentPlot);
    handle->position->setParentAnchor(position);  // 设置QCPSizeHandle的父锚点为position
    handle->setVisible(false);
    handle->setLayer(QLatin1String("overlay"));
    return handle;
}

同时为了绘制QCPSizeHandle之间的连线,我们让QCPSizeHandleManager继承自QCPLayerable,它需要重载applyDefaultAntialiasingHintdraw函数

void QCPSizeHandleManager::applyDefaultAntialiasingHint(QCPPainter *painter) const
{
    applyAntialiasingHint(painter, mAntialiased, QCP::aeOther);
}

void QCPSizeHandleManager::draw(QCPPainter *painter)
{
    QMapIterator<QCPAbstractItem *, ControlItemData> i(mHandles);
    while (i.hasNext()) {
        i.next();
        auto data = i.value();
        if (!data.showHandlesLines)
            continue;

        painter->setPen(data.connectHandlePen);
        QVector<QPointF> lines;
        foreach (auto *handle, data.handles)
            lines.push_back(handle->position->pixelPosition());
        painter->drawLines(lines);
//        painter->drawPolyline(QPolygonF(lines));
    }
}
完整的头文件
class QCP_LIB_DECL QCPSizeHandleManager : public QCPLayerable
{
    Q_OBJECT
public:
    explicit QCPSizeHandleManager(QCustomPlot *parent);
    ~QCPSizeHandleManager();

    void addItem(QCPAbstractItem *item, bool showHandlesLines = false);
    void addItems(const QList<QCPAbstractItem *> items, bool showHandlesLines = false);

public slots:
    void handleItemMove(QCPAbstractItem *item, const QPointF &delta);
    void handleItemResize(QCPSizeHandle *sizeHandle, const QPointF &delta);

protected:
    struct ControlItemData {
        bool showHandlesLines;
        bool moveable;
        bool resizeable;
        QPen connectHandlePen;
        QList<QCPSizeHandle*> handles;

        ControlItemData();
    };
    QMap<QCPAbstractItem *, ControlItemData> mHandles;

    QCPSizeHandle *addHandleToPosition(QCPItemPosition *position);

    virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;
    virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
};
  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值