C++ (Qt)#ifndef CURVETRACKER_H#define CURVETRACKER_H #include "plotsupportglobal.h" #include <qwt/qwt_plot_picker.h> class QwtPlotCurve; namespace PlotSupport { // CurveTracker class CurveTracker : public QwtPlotPicker{ Q_OBJECT public: explicit CurveTracker(QWidget *canvas); protected: QwtText trackerTextF(const QPointF &pos) const final; QRect trackerRect(const QFont &font) const final; private: QString curveInfoAt(const QwtPlotCurve *qwtcurve, const QPointF &pos) const; QLineF curveLineAt(const QwtPlotCurve *qwtcurve, double x) const; private: QWidget *m_canvas; // not owned}; } // namespace PlotSupport #endif // CURVETRACKER_H
C++ (Qt)#include "curvetracker.h" #include <qwt/qwt_plot.h>#include <qwt/qwt_plot_curve.h>#include <qwt/qwt_picker_machine.h> namespace PlotSupport { // CurveTracker /*! \class PlotSupport::CurveTracker \inmodule PlotSupport \brief Класс отслеживания значений кривых. Это вспомогательный класс, который позволяет отслеживать значения кривых и отображать их в прямоугольнике. Также предоставляет перекрестие, которое двигается с курсором мыши.*/ CurveTracker::CurveTracker(QWidget *canvas) : QwtPlotPicker(canvas) , m_canvas(canvas){ setTrackerMode(CurveTracker::ActiveOnly); setRubberBand(CurveTracker::CrossRubberBand); setStateMachine(new QwtPickerTrackerMachine); QPen pen; pen.setColor(QColor(Qt::gray)); setRubberBandPen(pen);} QRect CurveTracker::trackerRect(const QFont &font) const{ QRect r = QwtPlotPicker::trackerRect(font); // Всегда позиционируем прямоугольник с информацией о значениях // кривых в верхнем левом углу графика. r.moveTo(0, 0); return r;} QwtText CurveTracker::trackerTextF(const QPointF &pos) const{ QString info; // Тут используем реверсную итерацию для того, чтобы // информация последней кривой была сверху, а первой - снизу // (в соответствии с очередностью осей кривых). const auto curves = plot()->itemList(QwtPlotItem::Rtti_PlotCurve); for (auto curveIt = curves.crbegin(); curveIt != curves.crend(); ++curveIt) { // Не отображаем значения скрытых кривых. if (!(*curveIt)->isVisible()) continue; const QString curveInfo = curveInfoAt(static_cast<const QwtPlotCurve *>(*curveIt), pos); if (!curveInfo.isEmpty()) { if (!info.isEmpty()) info += tr("<br>"); info += curveInfo; } } QwtText trackerText; const QPalette palette = m_canvas->palette(); trackerText.setBorderPen(QPen(palette.text().color(), 2)); trackerText.setBackgroundBrush(palette.dark()); trackerText.setText(info); return trackerText;} QString CurveTracker::curveInfoAt(const QwtPlotCurve *qwtcurve, const QPointF &pos) const{ const QLineF line = curveLineAt(qwtcurve, pos.x()); if (line.isNull()) return QString(); const double y = line.pointAt((pos.x() - line.p1().x()) / line.dx()).y(); const QString text = QString(tr("<font color=""%1""> <b>%3</b>: %2 </font>")) .arg(qwtcurve->pen().color().name()) .arg(y) .arg(qwtcurve->title().text());; return text;} QLineF CurveTracker::curveLineAt(const QwtPlotCurve *qwtcurve, double x) const{ QLineF line; const auto samplesCount = qwtcurve->dataSize(); if (samplesCount >= 2) { const QRectF boundingRect = qwtcurve->boundingRect(); if ((boundingRect.width() > 0) && (x >= boundingRect.left()) && (x <= boundingRect.right())) { auto lessfunc = [](const double x, const QPointF &pos) { return (x < pos.x()); }; int sampleIndex = qwtUpperSampleIndex<QPointF>(*qwtcurve->data(), x, lessfunc); if (sampleIndex == -1) { const auto lastSample = qwtcurve->sample(int(samplesCount) - 1); if (qFuzzyCompare(x, lastSample.x())) sampleIndex = int(samplesCount) - 1; } if (sampleIndex > 0) { line.setP1(qwtcurve->sample(sampleIndex - 1)); line.setP2(qwtcurve->sample(sampleIndex)); } } } return line;} } // namespace PlotSupport