QCustomPlot Взаимодействовать с одной точкой на графике

я использую QCustomPlot и иметь на графике несколько графиков, я хочу иметь возможность щелкать и указывать, а затем иметь возможность получать данные или координаты и т. д. точки, на которую я нажал, я знаю, что это возможно для всего самого графика, используя QCP::iSelectPlottablesно возможно ли это только для отдельной точки или кто-то нашел способ обойти это?

4

Решение

Нет простого способа сделать это. По крайней мере, такой функциональности нет в QCustomPlot.

Но вы можете создать класс, представляющий одну точку (производную от QCPItemEllipseнапример) и переместите его мышью.

У меня есть похожая функциональность в моем (еще не выпущенном) программном обеспечении, так что смотрите и учитесь … Он также может двигаться с помощью shift-модификатора (изменяя только одну координату начальной позиции). Кроме того, он меняет курсор при перемещении к элементу (и границе элемента, к которому он перемещается).

plotpoint.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;
};

plotpoint.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);
}

Извините за почти нет комментариев в коде. Мне просто лень переводить с русского на английский.

Я надеюсь, вы все получите в любом случае.

3

Другие решения

отличный вклад от @ Obey-Kun, я хотел бы продолжить обсуждение, если кому-то интересно, особенно Kun 🙂

(1) основная идея заключается в

  • создать элемент эллипса PlotPoint,
  • наряду с этим есть 2 прямых элемента
    horizontal&vertical(для ортогонального перемещения)
  • и один предмет трассировки mCenterTracer(центр эллипса, для соединения
    Действие мыши).

(2) в соответствии с plot.cpp (часть), я думаю, что механизм может быть:

  • Нажмите левая кнопка (для подтверждения есть одна точка, которую можно переместить)
  • переехать мышь. (для перемещения точки через трассировщик)
  • релиз левая кнопка (для размещения точки в последней требуемой позиции)

Однако меня смущает логика в

void Plot :: mouseMoveEvent (QMouseEvent * event)

. Это выглядит как процедура обнаружения зависания, потому что все сделано под Qt::NoButtonПользователь перемещает мышь (без нажатых кнопок), чтобы определить, находится ли мышь на plotPoint, и если это так, перенесет *plotPoint в *mPointUnderCursor, затем нажмите, затем переместите, затем отпустите.

Есть ли что-то, что я скучаю?

-1

По вопросам рекламы [email protected]