簡體   English   中英

QCustomPlot 與圖形上的單個點交互

[英]QCustomPlot Interact with a single point on a graph

我正在使用QCustomPlot並在屏幕上繪制了多個圖形,我希望能夠單擊並指向然后能夠獲取我單擊的點的數據或坐標等,我知道這對整個來說都是可能的使用QCP::iSelectPlottables繪制自己,但這是否僅適用於單個點,或者是否有人找到了解決方法來使之成為可能。

沒有簡單的方法可以做到這一點。 至少在 QCustomPlot 中沒有這樣的功能。

但是您可以創建表示單點的類(例如,從QCPItemEllipse派生)並用鼠標移動它。

我的(尚未發布)軟件中有類似的功能,所以看看和學習......它也可以用 shift-modifier 移動(只改變初始位置的一個坐標)。 此外,當它移動到項目(以及它移動到的項目的邊框)時,它會改變光標。

情節點.h

class PlotPoint : public QCPItemEllipse
{
    Q_OBJECT
public:
    explicit PlotPoint(QCustomPlot *parentPlot, int halfSize = 5);

    QPointF pos() const;
    const QColor &color() const;
    void setColor(const QColor &color);
    void startMoving(const QPointF &mousePos, bool shiftIsPressed);

public slots:
    void setVisible(bool on);

signals:
    void activated(); ///< emitted on mouse over
    void disactivated(); ///< emitted when cursor leave us

    void moved(const QPointF &pos);
    void stoppedMoving();

public slots:
    void move(double x, double y, bool signalNeeded = true);
    void movePx(double x, double y);
    void setActive(bool isActive);

private slots:
    void onMouseMove(QMouseEvent *event);
    void stopMoving();
    void moveToWantedPos();
    void onShiftStateChanged(bool shiftPressed);

private:
    QCPItemTracer *mCenterTracer;
    QPointF mGripDelta;
    QPointF mInitialPos;
    bool mIsChangingOnlyOneCoordinate;
    QList<QCPAbstractItem *> mHelperItems;
    QPointF mLastWantedPos;
    QTimer *mMoveTimer;
    QPointF mCurWantedPosPx;
};

繪圖點.cpp

PlotPoint::PlotPoint(QCustomPlot *parentPlot, int halfSize)
    : QCPItemEllipse(parentPlot)
    , mCenterTracer(new QCPItemTracer(parentPlot))
    , mGripDelta()
    , mInitialPos()
    , mLastWantedPos()
    , mMoveTimer(new QTimer(this))
    , mCurWantedPosPx()
{
    mCenterTracer->setStyle(QCPItemTracer::tsNone);

    topLeft->setParentAnchor(mCenterTracer->position);
    bottomRight->setParentAnchor(mCenterTracer->position);
    topLeft->setType(QCPItemPosition::ptAbsolute);
    bottomRight->setType(QCPItemPosition::ptAbsolute);

    topLeft->setCoords(-halfSize, -halfSize);
    bottomRight->setCoords(halfSize, halfSize);

    setSelectable(true); // plot moves only selectable points, see Plot::mouseMoveEvent
    setColor(QColor(qrand()%256, qrand()%256, qrand()%256, 100));
    setPen(QPen(Qt::black));
    setSelectedPen(QPen(Qt::black, 3));

    mMoveTimer->setInterval(25); // 40 FPS
    connect(mMoveTimer, SIGNAL(timeout()), this, SLOT(moveToWantedPos()));
}

QPointF PlotPoint::pos() const
{
    return mCenterTracer->position->coords();
}

const QColor &PlotPoint::color() const
{
    return brush().color();
}

void PlotPoint::setColor(const QColor& color)
{
    setBrush(color);
    setSelectedBrush(color);
}

void PlotPoint::startMoving(const QPointF &mousePos, bool shiftIsPressed)
{
    mGripDelta.setX(parentPlot()->xAxis->coordToPixel(mCenterTracer->position->key()) - mousePos.x());
    mGripDelta.setY(parentPlot()->yAxis->coordToPixel(mCenterTracer->position->value()) - mousePos.y());

    mInitialPos = pos();
    mLastWantedPos = mInitialPos;
    mCurWantedPosPx = QPointF();
    mIsChangingOnlyOneCoordinate = shiftIsPressed;

    mMoveTimer->start();

    QCPItemStraightLine *horizontal = new QCPItemStraightLine(parentPlot());
    horizontal->setAntialiased(false);
    horizontal->point1->setCoords(mInitialPos.x(), mInitialPos.y());
    horizontal->point2->setCoords(mInitialPos.x() + 1, mInitialPos.y());
    parentPlot()->addItem(horizontal);

    QCPItemStraightLine *vertical = new QCPItemStraightLine(parentPlot());
    vertical->setAntialiased(false);
    vertical->point1->setCoords(mInitialPos.x(), mInitialPos.y());
    vertical->point2->setCoords(mInitialPos.x(), mInitialPos.y() + 1);
    parentPlot()->addItem(vertical);

    static const QPen linesPen(Qt::darkGray, 0, Qt::DashLine);
    horizontal->setPen(linesPen);
    vertical->setPen(linesPen);

    mHelperItems << vertical << horizontal;

    if (!mIsChangingOnlyOneCoordinate) {
        vertical->setVisible(false);
        horizontal->setVisible(false);
    }

    connect(parentPlot(), SIGNAL(mouseMove(QMouseEvent*)),
            this, SLOT(onMouseMove(QMouseEvent*)));

    connect(parentPlot(), SIGNAL(mouseRelease(QMouseEvent*)),
            this, SLOT(stopMoving()));

    connect(parentPlot(), SIGNAL(shiftStateChanged(bool)),
            this, SLOT(onShiftStateChanged(bool)));

    parentPlot()->grabKeyboard();
    QApplication::setOverrideCursor(Qt::ClosedHandCursor);
}

void PlotPoint::setVisible(bool on)
{
    setSelectable(on);  // we are movable only when visible
    QCPItemEllipse::setVisible(on);
}

void PlotPoint::stopMoving()
{
    disconnect(parentPlot(), SIGNAL(mouseMove(QMouseEvent*)),
            this, SLOT(onMouseMove(QMouseEvent*)));

    disconnect(parentPlot(), SIGNAL(mouseRelease(QMouseEvent*)),
            this, SLOT(stopMoving()));

    disconnect(parentPlot(), SIGNAL(shiftStateChanged(bool)),
            this, SLOT(onShiftStateChanged(bool)));

    mMoveTimer->stop();
    moveToWantedPos();

    if (!mHelperItems.isEmpty()) {
        while (!mHelperItems.isEmpty()) {
            QCPAbstractItem *item = mHelperItems.takeFirst();
            mParentPlot->removeItem(item);
        }

        mParentPlot->replot();
    }

    parentPlot()->releaseKeyboard();
    QApplication::restoreOverrideCursor();

    emit stoppedMoving();
}

void PlotPoint::move(double x, double y, bool signalNeeded)
{
    mLastWantedPos.setX(x);
    mLastWantedPos.setY(y);
    if (mIsChangingOnlyOneCoordinate) {
        double x1 = parentPlot()->xAxis->coordToPixel(x);
        double x2 = parentPlot()->xAxis->coordToPixel(mInitialPos.x());
        double y1 = parentPlot()->yAxis->coordToPixel(y);
        double y2 = parentPlot()->yAxis->coordToPixel(mInitialPos.y());
        if (qAbs(x1 - x2) < qAbs(y1 - y2)) {
            x = mInitialPos.x();
        } else {
            y = mInitialPos.y();
        }
    }

    mCenterTracer->position->setCoords(x, y);

    parentPlot()->replot();

    if(signalNeeded) {
        emit moved(QPointF(x, y));
    }
}

void PlotPoint::movePx(double x, double y)
{
    move(parentPlot()->xAxis->pixelToCoord(x),
        parentPlot()->yAxis->pixelToCoord(y));
}

void PlotPoint::setActive(bool isActive)
{
    setSelected(isActive);
    emit (isActive ? activated() : disactivated());
}

void PlotPoint::onMouseMove(QMouseEvent *event)
{
    mCurWantedPosPx = QPointF(event->localPos().x() + mGripDelta.x(),
                              event->localPos().y() + mGripDelta.y());
}

void PlotPoint::moveToWantedPos()
{
    if (!mCurWantedPosPx.isNull()) {
        movePx(mCurWantedPosPx.x(), mCurWantedPosPx.y());
        mCurWantedPosPx = QPointF();
    }
}

void PlotPoint::onShiftStateChanged(bool shiftPressed)
{
    if (shiftPressed != mIsChangingOnlyOneCoordinate) {
        mIsChangingOnlyOneCoordinate = shiftPressed;
        foreach (QCPAbstractItem *item, mHelperItems) {
            item->setVisible(shiftPressed);
        }
        move(mLastWantedPos.x(), mLastWantedPos.y());
    }
}

(部分)plot.cpp

void Plot::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && mPointUnderCursor) {
        mPointUnderCursor->startMoving(event->localPos(),
                                       event->modifiers().testFlag(Qt::ShiftModifier));
        return;
    }

    QCustomPlot::mousePressEvent(event);
}

void Plot::mouseMoveEvent(QMouseEvent *event)
{
    QCustomPlot::mouseMoveEvent(event);
    if (event->buttons() == Qt::NoButton) {
        PlotPoint *plotPoint = qobject_cast<PlotPoint*>(itemAt(event->localPos(), true));
        if (plotPoint != mPointUnderCursor) {
            if (mPointUnderCursor == NULL) {
                // cursor moved from empty space to item
                plotPoint->setActive(true);
                setCursor(Qt::OpenHandCursor);
            } else if (plotPoint == NULL) {
                // cursor move from item to empty space
                mPointUnderCursor->setActive(false);
                unsetCursor();
            } else {
                // cursor moved from item to item
                mPointUnderCursor->setActive(false);
                plotPoint->setActive(true);
            }
            mPointUnderCursor = plotPoint;
            replot();
        }
    }
}

void Plot::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Shift) {
        emit shiftStateChanged(true);
    }
    QCustomPlot::keyPressEvent(event);
}

void Plot::keyReleaseEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Shift) {
        emit shiftStateChanged(false);
    }
    QCustomPlot::keyReleaseEvent(event);
}

抱歉,代碼中幾乎沒有注釋。 我只是懶得把俄文翻譯成英文。

我希望你無論如何都能得到一切。

@Obey-Kun 的巨大貢獻,如果有人感興趣,我想進一步討論,尤其是 Kun :)

(1) 基本思想是

  • 創建一個橢圓項PlotPoint
  • 與它一起有 2 個horizontalvertical直線項目(用於正交移動)
  • 和一個跟蹤項mCenterTracer (橢圓的中心,用於連接鼠標操作)。

(2)根據plot.cpp(part),我想機制可能是:

  • 按下左鍵,(確認有一個可以移動的點)
  • 移動鼠標。(通過跟蹤器移動點)
  • 釋放左按鈕。(將點放置在最后想要的位置)

但是,我對其中的邏輯感到困惑

void Plot::mouseMoveEvent(QMouseEvent *event)

. 它看起來像一個懸停檢測程序,因為一切都在Qt::NoButton下完成。用戶移動鼠標(沒有按下任何按鈕)來檢測鼠標是否在 plotPoint 上,如果是, *plotPoint *mPointUnderCursor傳輸到*mPointUnderCursor 然后按下然后移動然后釋放。

有什么我想念的嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM