qcutomplot实现游标工具栏toolTip

8 篇文章 0 订阅

QCustomPlot到目前为止已经更新至2.1.0版本了。我看了QCustomPlot源码 目前性能方面在不开opengl下相比之前版本有了很大的提升。实测8条曲线,1秒刷新一次完全没有压力。 整个图库的架构很强。首先分成6个layer,每个layer绘制一张pixmap图片,并且每张pixmap上都有相应的布局都继承自QCPLayerable,最后在QCustomPlot重载paintEvent事件中一一绘制6个Layer的pixmap。目前唯一感觉不够的是:能不能用多线程把6个layer的pixmap绘制出来 最后同步到gui线程中去。言归正传先上效果图:

首先仿造QItemText 新建一个QItemTip类.h文件

class QCP_LIB_DECL QCPItemTip : public QCPAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QColor color READ color WRITE setColor)
  Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor)
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  Q_PROPERTY(QFont font READ font WRITE setFont)
  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
  Q_PROPERTY(Qt::Alignment positionAlignment READ positionAlignment WRITE setPositionAlignment)
  Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment)
  Q_PROPERTY(double rotation READ rotation WRITE setRotation)
  Q_PROPERTY(QMargins padding READ padding WRITE setPadding)
  /// \endcond
public:
  explicit QCPItemTip(QCustomPlot *parentPlot);
  virtual ~QCPItemTip() Q_DECL_OVERRIDE;
  
    struct ToolTipData{
        QColor _color;
        QString _text;
        QString _value;
        double _mouseY;
        bool _multiAxis;
    };
  // getters:
  QColor color() const { return mColor; }
  QColor selectedColor() const { return mSelectedColor; }
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  QBrush brush() const { return mBrush; }
  QBrush selectedBrush() const { return mSelectedBrush; }
  QFont font() const { return mFont; }
  QFont selectedFont() const { return mSelectedFont; }
  Qt::Alignment positionAlignment() const { return mPositionAlignment; }
  Qt::Alignment textAlignment() const { return mTextAlignment; }
  double rotation() const { return mRotation; }
  QMargins padding() const { return mPadding; }
  
  // setters;
  void setColor(const QColor &color);
  void setSelectedColor(const QColor &color);
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setSelectedBrush(const QBrush &brush);
  void setFont(const QFont &font);
  void setSelectedFont(const QFont &font);
  void setData(const QVector<QCPItemTip::ToolTipData> &tData);
  void setPositionAlignment(Qt::Alignment alignment);
  void setTextAlignment(Qt::Alignment alignment);
  void setRotation(double degrees);
  void setPadding(const QMargins &padding);
  
  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
  
  QCPItemPosition * const position;
  QCPItemAnchor * const topLeft;
  QCPItemAnchor * const top;
  QCPItemAnchor * const topRight;
  QCPItemAnchor * const right;
  QCPItemAnchor * const bottomRight;
  QCPItemAnchor * const bottom;
  QCPItemAnchor * const bottomLeft;
  QCPItemAnchor * const left;
  
protected:
  enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft};
  
  // property members:
  QColor mColor, mSelectedColor;
  QPen mPen, mSelectedPen;
  QBrush mBrush, mSelectedBrush;
  QFont mFont, mSelectedFont;
  Qt::Alignment mPositionAlignment;
  Qt::Alignment mTextAlignment;
  double mRotation;
  QMargins mPadding;
  
  QVector<ToolTipData> mtData;
  // reimplemented virtual methods:
  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
  virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE;
  
  // non-virtual methods:
  QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const;
  QFont mainFont() const;
  QColor mainColor() const;
  QPen mainPen() const;
  QBrush mainBrush() const;
};

.cpp文件

QCPItemTip::QCPItemTip(QCustomPlot *parentPlot) :
  QCPAbstractItem(parentPlot),
  position(createPosition(QLatin1String("position"))),
  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
  top(createAnchor(QLatin1String("top"), aiTop)),
  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
  right(createAnchor(QLatin1String("right"), aiRight)),
  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
  left(createAnchor(QLatin1String("left"), aiLeft)),
  mPositionAlignment(Qt::AlignCenter),
  mTextAlignment(Qt::AlignTop|Qt::AlignLeft),
  mRotation(0)
{
  position->setCoords(0, 0);

  setPen(Qt::NoPen);
  setSelectedPen(Qt::NoPen);
  setBrush(Qt::NoBrush);
  setSelectedBrush(Qt::NoBrush);
  setColor(Qt::black);
  setSelectedColor(Qt::blue);
}

QCPItemTip::~QCPItemTip()
{
}

/*!
  Sets the color of the text.
*/
void QCPItemTip::setColor(const QColor &color)
{
  mColor = color;
}

/*!
  Sets the color of the text that will be used when the item is selected.
*/
void QCPItemTip::setSelectedColor(const QColor &color)
{
  mSelectedColor = color;
}

/*!
  Sets the pen that will be used do draw a rectangular border around the text. To disable the
  border, set \a pen to Qt::NoPen.
  
  \see setSelectedPen, setBrush, setPadding
*/
void QCPItemTip::setPen(const QPen &pen)
{
  mPen = pen;
}

/*!
  Sets the pen that will be used do draw a rectangular border around the text, when the item is
  selected. To disable the border, set \a pen to Qt::NoPen.
  
  \see setPen
*/
void QCPItemTip::setSelectedPen(const QPen &pen)
{
  mSelectedPen = pen;
}

/*!
  Sets the brush that will be used do fill the background of the text. To disable the
  background, set \a brush to Qt::NoBrush.
  
  \see setSelectedBrush, setPen, setPadding
*/
void QCPItemTip::setBrush(const QBrush &brush)
{
  mBrush = brush;
}

/*!
  Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
  background, set \a brush to Qt::NoBrush.
  
  \see setBrush
*/
void QCPItemTip::setSelectedBrush(const QBrush &brush)
{
  mSelectedBrush = brush;
}

/*!
  Sets the font of the text.
  
  \see setSelectedFont, setColor
*/
void QCPItemTip::setFont(const QFont &font)
{
  mFont = font;
}

/*!
  Sets the font of the text that will be used when the item is selected.
  
  \see setFont
*/
void QCPItemTip::setSelectedFont(const QFont &font)
{
    mSelectedFont = font;
}

void QCPItemTip::setData(const QVector<QCPItemTip::ToolTipData> &tData)
{
    mtData = tData;
}

/*!
  Sets which point of the text rect shall be aligned with \a position.
  
  Examples:
  \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
  that the top of the text rect will be horizontally centered on \a position.
  \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
  bottom left corner of the text rect.
  
  If you want to control the alignment of (multi-lined) text within the text rect, use \ref
  setTextAlignment.
*/
void QCPItemTip::setPositionAlignment(Qt::Alignment alignment)
{
  mPositionAlignment = alignment;
}

/*!
  Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
*/
void QCPItemTip::setTextAlignment(Qt::Alignment alignment)
{
  mTextAlignment = alignment;
}

/*!
  Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
  around \a position.
*/
void QCPItemTip::setRotation(double degrees)
{
  mRotation = degrees;
}

/*!
  Sets the distance between the border of the text rectangle and the text. The appearance (and
  visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
*/
void QCPItemTip::setPadding(const QMargins &padding)
{
  mPadding = padding;
}

/* inherits documentation from base class */
double QCPItemTip::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
{
  Q_UNUSED(details)
//  if (onlySelectable && !mSelectable)
//    return -1;
  
//  // The rect may be rotated, so we transform the actual clicked pos to the rotated
//  // coordinate system, so we can use the normal rectDistance function for non-rotated rects:
//  QPointF positionPixels(position->pixelPosition());
//  QTransform inputTransform;
//  inputTransform.translate(positionPixels.x(), positionPixels.y());
//  inputTransform.rotate(-mRotation);
//  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
//  QPointF rotatedPos = inputTransform.map(pos);
//  QFontMetrics fontMetrics(mFont);
//  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
//  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
//  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
//  textBoxRect.moveTopLeft(textPos.toPoint());

//  return rectDistance(textBoxRect, rotatedPos, true);
    return 0.00;
}

/* inherits documentation from base class */
void QCPItemTip::draw(QCPPainter *painter)
{
    QPointF pos(position->pixelPosition());
    QTransform transform = painter->transform();
    transform.translate(pos.x(), /*pos.y()*/clipRect().top()*6);//
    if (!qFuzzyIsNull(mRotation))
    transform.rotate(mRotation);
    painter->setFont(mainFont());
    painter->setPen(mainColor());
    QRect maxRect(0,0,0,0);
    QRect maxValueRect(0,0,0,0);
    //qDebug() << "clipRect X: "<<clipRect().x();
    for (auto &v : mtData)
    {
        QRect rt = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, v._text);
        if( rt.width() > maxRect.width())
            maxRect = rt;
        rt = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|Qt::AlignRight, v._value);
        if( rt.width() > maxValueRect.width())
            maxValueRect = rt;
    }
    int idx = 1;
    int y = 0;
    if(mtData.size() >= 1)
        y = mtData.at(0)._mouseY;
    
    for (auto &k : mtData)
    {
        if(idx > 1)
        {    
            y += maxRect.height()+ mPadding.top()*2 + 1;
            maxRect.moveTop(y);
        }
        //qDebug() << " maxRect:" << maxRect.x() << maxRect.y() << maxRect.width() << maxRect.height();
        //QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, k._text);
        QRect textBoxRect = maxRect.adjusted( - mPadding.left(), -mPadding.top(), mPadding.right()+maxValueRect.width() +30, mPadding.bottom());
        //QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
        maxRect.moveTopLeft(/*textPos.toPoint()+*/QPoint(mPadding.left() + 35, mPadding.top()+ y));
        textBoxRect.moveTopLeft(/*textPos.toPoint()+ */QPoint(10, y));
        int clipPad = qCeil(mainPen().widthF());
        QRect boundingRect = textBoxRect.adjusted(-clipPad+ clipRect().x(), -clipPad, clipPad, clipPad);
//        qDebug() << " maxRect1:" << maxRect.x() << maxRect.y() << maxRect.width() << maxRect.height();
//        qDebug() << " textBoxRect:"<< textBoxRect.x() << textBoxRect.y()<< textBoxRect.width()<< textBoxRect.height() <<clipPad;
//        qDebug() << " bounding矩形:"<<boundingRect.x() << boundingRect.y()<< boundingRect.width()<< boundingRect.height();
//        qDebug() << "clicpRect"<< clipRect().x() << clipRect().y()<< clipRect().width() << clipRect().height();
        if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
        {
            painter->setTransform(transform);
        
            // 限制ToolTip不超过QCustomPlot的范围
            if (pos.x() + textBoxRect.width() >= mParentPlot->viewport().right())
                painter->translate(- textBoxRect.width(), 0);
            if (pos.y() + textBoxRect.height() * 2 >= mParentPlot->viewport().bottom())
                painter->translate(0, - textBoxRect.height());
            
            
            if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
                (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
            {
                
                //painter->setBrush(mainBrush());
                painter->setPen(QColor(169,169,169,200)/*Qt::NoPen*/);
                painter->fillRect(textBoxRect, mainBrush());//绘制文本框背景颜色
                painter->drawRect(textBoxRect);
                 qDebug() << "painter textRect: "<<textBoxRect.x() << textBoxRect.y()<< textBoxRect.width()<< textBoxRect.height()<<idx;
            }
            //painter->setPen(mainPen());
            QRect rt(textBoxRect.x() + 10,maxRect.y()+ maxRect.height()/4,10,10);
            painter->fillRect(rt, QBrush(k._color));
            painter->drawRect(rt);
            painter->setBrush(Qt::NoBrush);
            painter->setPen(QPen(mainColor()));
            painter->drawText(maxRect, Qt::TextDontClip|mTextAlignment, k._text);
            maxValueRect.moveTopLeft(QPoint(maxRect.right(), y + mPadding.top()));
            //painter->fillRect(maxValueRect, QBrush(k._color));
            painter->drawText(maxValueRect, Qt::TextDontClip|Qt::AlignRight, k._value);
        }
        idx++;
    }
}

/* inherits documentation from base class */
QPointF QCPItemTip::anchorPixelPosition(int anchorId) const
{
  // get actual rect points (pretty much copied from draw function):
  QPointF pos(position->pixelPosition());
  QTransform transform;
  transform.translate(pos.x(), pos.y());
  if (!qFuzzyIsNull(mRotation))
    transform.rotate(mRotation);
  QFontMetrics fontMetrics(mainFont());
  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, "");
  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
  textBoxRect.moveTopLeft(textPos.toPoint());
  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
  
  switch (anchorId)
  {
    case aiTopLeft:     return rectPoly.at(0);
    case aiTop:         return (rectPoly.at(0)+rectPoly.at(1))*0.5;
    case aiTopRight:    return rectPoly.at(1);
    case aiRight:       return (rectPoly.at(1)+rectPoly.at(2))*0.5;
    case aiBottomRight: return rectPoly.at(2);
    case aiBottom:      return (rectPoly.at(2)+rectPoly.at(3))*0.5;
    case aiBottomLeft:  return rectPoly.at(3);
    case aiLeft:        return (rectPoly.at(3)+rectPoly.at(0))*0.5;
  }
  
  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
  return {};
}

/*! \internal
  
  Returns the point that must be given to the QPainter::drawText function (which expects the top
  left point of the text rect), according to the position \a pos, the text bounding box \a rect and
  the requested \a positionAlignment.
  
  For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
  will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
  drawn at that point, the lower left corner of the resulting text rect is at \a pos.
*/
QPointF QCPItemTip::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
{
  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
    return pos;
  
  QPointF result = pos; // start at top left
  if (positionAlignment.testFlag(Qt::AlignHCenter))
    result.rx() -= rect.width()/2.0;
  else if (positionAlignment.testFlag(Qt::AlignRight))
    result.rx() -= rect.width();
  if (positionAlignment.testFlag(Qt::AlignVCenter))
    result.ry() -= rect.height()/2.0;
  else if (positionAlignment.testFlag(Qt::AlignBottom))
    result.ry() -= rect.height();
  return result;
}

/*! \internal

  Returns the font that should be used for drawing text. Returns mFont when the item is not selected
  and mSelectedFont when it is.
*/
QFont QCPItemTip::mainFont() const
{
  return mSelected ? mSelectedFont : mFont;
}

/*! \internal

  Returns the color that should be used for drawing text. Returns mColor when the item is not
  selected and mSelectedColor when it is.
*/
QColor QCPItemTip::mainColor() const
{
  return mSelected ? mSelectedColor : mColor;
}

/*! \internal

  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
  and mSelectedPen when it is.
*/
QPen QCPItemTip::mainPen() const
{
  return mSelected ? mSelectedPen : mPen;
}

/*! \internal

  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
  is not selected and mSelectedBrush when it is.
*/
QBrush QCPItemTip::mainBrush() const
{
  return mSelected ? mSelectedBrush : mBrush;
}

主要改造的代码是在draw函数中。各位可以重新再整理整理精简一下代码。

使用方法:

    m_tracerText = new QCPItemTip(this);
    m_tracerText->setPositionAlignment(Qt::AlignTop|Qt::AlignLeft);
    m_tracerText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
    //m_tracer是 QCPItemTracer的对象 
    m_tracerText->position->setParentAnchor(m_tracer->position);
    m_tracerText->setFont(QFont(font().family(), 12));
    m_tracerText->setPen(QPen(QColor("#A9A9A9")));
    m_tracerText->setBrush(QColor(169,169,169,200));
    m_tracerText->setPadding(QMargins(2,2,2,2));//边界宽度
    m_tracerText->setLayer("overlay");

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
要在QCustomPlot实现十字游标功能,可以按照以下步骤进行: 1.创建两个QCPItemLine对象,一个代表水平线,一个代表垂直线,并将它们添加到QCustomPlot对象中。 2.在鼠标移动事件的处理函数中,获取鼠标的位置,并将水平线和垂直线移动到相应的位置。可以通过以下代码获取鼠标位置: ``` QMouseEvent* event; QPointF mousePos = customPlot->mapToGraph(event->pos()); ``` 3.更新十字游标的文本标签。可以通过以下代码更新文本标签: ``` QString label = QString("x: %1\ny: %2").arg(mousePos.x()).arg(mousePos.y()); customPlot->setToolTip(label); ``` 4.设置水平线和垂直线的样式和颜色。可以通过以下代码设置线条样式和颜色: ``` horizontalLine->setPen(QPen(Qt::red)); verticalLine->setPen(QPen(Qt::red)); ``` 完整的实现代码可以参考以下示例: ``` // 创建水平线和垂直线 QCPItemLine *horizontalLine = new QCPItemLine(customPlot); QCPItemLine *verticalLine = new QCPItemLine(customPlot); // 设置线条样式和颜色 horizontalLine->setPen(QPen(Qt::red)); verticalLine->setPen(QPen(Qt::red)); // 鼠标移动事件处理函数 void mouseMoveEvent(QMouseEvent *event) { // 获取鼠标位置 QPointF mousePos = customPlot->mapToGraph(event->pos()); // 移动水平线和垂直线 horizontalLine->start->setCoords(customPlot->xAxis->range().lower, mousePos.y()); horizontalLine->end->setCoords(customPlot->xAxis->range().upper, mousePos.y()); verticalLine->start->setCoords(mousePos.x(), customPlot->yAxis->range().lower); verticalLine->end->setCoords(mousePos.x(), customPlot->yAxis->range().upper); // 更新文本标签 QString label = QString("x: %1\ny: %2").arg(mousePos.x()).arg(mousePos.y()); customPlot->setToolTip(label); // 重新绘制图像 customPlot->replot(); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值