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;
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容