[英]QCustomPlot Interact with a single point on a graph
我正在使用QCustomPlot
并在屏幕上绘制了多个图形,我希望能够单击并指向然后能够获取我单击的点的数据或坐标等,我知道这对整个来说都是可能的使用QCP::iSelectPlottables
绘制自己,但这是否仅适用于单个点,或者是否有人找到了解决方法来使之成为可能。
There is no simple way to do that.没有简单的方法可以做到这一点。 At least there is no such functionality in QCustomPlot.至少在 QCustomPlot 中没有这样的功能。
But you can create class representing single point (derived from QCPItemEllipse
, for example) and move it with mouse.但是您可以创建表示单点的类(例如,从QCPItemEllipse
派生)并用鼠标移动它。
I've got similiar functionality in my (not released yet) software, so look and learn... It also can move with shift-modifier (changing only one coordinate of initial position).我的(尚未发布)软件中有类似的功能,所以看看和学习......它也可以用 shift-modifier 移动(只改变初始位置的一个坐标)。 Plus it changes cursor when it moves to item (and border of item it moves to).此外,当它移动到项目(以及它移动到的项目的边框)时,它会改变光标。
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::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());
}
}
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);
}
Sorry for almost no comments in code.抱歉,代码中几乎没有注释。 I'm just too lazy for Russian to English translation.我只是懒得把俄文翻译成英文。
I hope you'll get everything anyways.我希望你无论如何都能得到一切。
great contribution from @Obey-Kun, I'd like further discussion if anyone is interested in, especially Kun:) @Obey-Kun 的巨大贡献,如果有人感兴趣,我想进一步讨论,尤其是 Kun :)
(1) the basic idea is to (1) 基本思想是
PlotPoint
,创建一个椭圆项PlotPoint
,horizontal
& vertical
(for orthogonal moving)与它一起有 2 个horizontal
和vertical
直线项目(用于正交移动)mCenterTracer
(center of ellipse, for connecting Mouse Action).和一个跟踪项mCenterTracer
(椭圆的中心,用于连接鼠标操作)。(2)according to the plot.cpp(part), I imagine the mechanism might be: (2)根据plot.cpp(part),我想机制可能是:
However, I am confused with logic in the但是,我对其中的逻辑感到困惑
void Plot::mouseMoveEvent(QMouseEvent *event) void Plot::mouseMoveEvent(QMouseEvent *event)
. . It looks like a hover detection procedure , because everything is done under Qt::NoButton
.the user move the mouse(with no buttons pressed) to detect whether the mouse is on a plotPoint, if so, transfer *plotPoint
to *mPointUnderCursor
.它看起来像一个悬停检测程序,因为一切都在Qt::NoButton
下完成。用户移动鼠标(没有按下任何按钮)来检测鼠标是否在 plotPoint 上,如果是, *plotPoint
*mPointUnderCursor
传输到*mPointUnderCursor
。 then Press then Move then Release.然后按下然后移动然后释放。
Is there anything that I miss?有什么我想念的吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.