├── .gitattributes ├── .gitignore ├── QtChart.pro ├── README.md ├── callout.cpp ├── callout.h ├── chartview.cpp ├── chartview.h ├── main.cpp ├── mainwidget.cpp ├── mainwidget.h └── mainwidget.ui /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.user 3 | -------------------------------------------------------------------------------- /QtChart.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2017-11-16T09:53:11 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets charts serialport 10 | 11 | TARGET = QtChart 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | 26 | SOURCES += \ 27 | main.cpp \ 28 | mainwidget.cpp \ 29 | chartview.cpp \ 30 | callout.cpp 31 | 32 | HEADERS += \ 33 | mainwidget.h \ 34 | chartview.h \ 35 | callout.h 36 | 37 | FORMS += \ 38 | mainwidget.ui 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QtChart 2 | -------------------------------------------------------------------------------- /callout.cpp: -------------------------------------------------------------------------------- 1 | #include "callout.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | Callout::Callout(QChart *chart): 10 | QGraphicsItem(chart), 11 | chart(chart) 12 | { 13 | 14 | } 15 | 16 | QRectF Callout::boundingRect() const 17 | { 18 | QPointF anchor = mapFromParent(chart->mapToPosition(this->anchor)); 19 | QRectF rect; 20 | rect.setLeft(qMin(this->rect.left(), anchor.x())); 21 | rect.setRight(qMax(this->rect.right(), anchor.x())); 22 | rect.setTop(qMin(this->rect.top(), anchor.y())); 23 | rect.setBottom(qMax(this->rect.bottom(), anchor.y())); 24 | 25 | return rect; 26 | } 27 | 28 | void Callout::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 29 | { 30 | Q_UNUSED(option) 31 | Q_UNUSED(widget) 32 | 33 | QPainterPath path; 34 | path.addRoundedRect(rect, 5, 5); 35 | 36 | QPointF anchor = mapFromParent(chart->mapToPosition(this->anchor)); 37 | if (!rect.contains(anchor)) { 38 | QPointF point1, point2; 39 | 40 | // establish the position of the anchor point in relation to rect 41 | bool above = anchor.y() <= rect.top(); 42 | bool aboveCenter = anchor.y() > rect.top() && anchor.y() <= rect.center().y(); 43 | bool belowCenter = anchor.y() > rect.center().y() && anchor.y() <= rect.bottom(); 44 | bool below = anchor.y() > rect.bottom(); 45 | 46 | bool onLeft = anchor.x() <= rect.left(); 47 | bool leftOfCenter = anchor.x() > rect.left() && anchor.x() <= rect.center().x(); 48 | bool rightOfCenter = anchor.x() > rect.center().x() && anchor.x() <= rect.right(); 49 | bool onRight = anchor.x() > rect.right(); 50 | 51 | // get the nearest rect corner. 52 | qreal x = (onRight + rightOfCenter) * rect.width(); 53 | qreal y = (below + belowCenter) * rect.height(); 54 | bool cornerCase = (above && onLeft) || (above && onRight) || (below && onLeft) || (below && onRight); 55 | bool vertical = qAbs(anchor.x() - x) > qAbs(anchor.y() - y); 56 | 57 | qreal x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * !vertical * (onLeft * 10 - onRight * 20); 58 | qreal y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20);; 59 | point1.setX(x1); 60 | point1.setY(y1); 61 | 62 | qreal x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * !vertical * (onLeft * 20 - onRight * 10);; 63 | qreal y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10);; 64 | point2.setX(x2); 65 | point2.setY(y2); 66 | 67 | path.moveTo(point1); 68 | path.lineTo(anchor); 69 | path.lineTo(point2); 70 | path = path.simplified(); 71 | } 72 | 73 | painter->setBrush(QColor(255, 255, 255)); 74 | painter->drawPath(path); 75 | painter->drawText(textRect, text); 76 | } 77 | 78 | void Callout::mousePressEvent(QGraphicsSceneMouseEvent *event) 79 | { 80 | event->setAccepted(true); 81 | } 82 | 83 | void Callout::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 84 | { 85 | if (event->buttons() & Qt::LeftButton){ 86 | setPos(mapToParent(event->pos() - event->buttonDownPos(Qt::LeftButton))); 87 | event->setAccepted(true); 88 | } else { 89 | event->setAccepted(false); 90 | } 91 | } 92 | 93 | void Callout::setText(const QString &text) 94 | { 95 | this->text = text; 96 | QFontMetrics metrics(font); 97 | textRect = metrics.boundingRect(QRect(0, 0, 150, 150), Qt::AlignLeft, text); 98 | textRect.translate(5, 5); 99 | prepareGeometryChange(); 100 | rect = textRect.adjusted(-5, -5, 5, 5); 101 | } 102 | 103 | void Callout::setAnchor(QPointF point) 104 | { 105 | anchor = point; 106 | } 107 | 108 | void Callout::updateGeometry() 109 | { 110 | prepareGeometryChange(); 111 | setPos(chart->mapToPosition(anchor) + QPoint(10, -50)); 112 | } 113 | -------------------------------------------------------------------------------- /callout.h: -------------------------------------------------------------------------------- 1 | #ifndef __CALLOUT_H__ 2 | #define __CALLOUT_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | QT_BEGIN_NAMESPACE 9 | class QGraphicsSceneMouseEvent; 10 | QT_END_NAMESPACE 11 | 12 | QT_CHARTS_BEGIN_NAMESPACE 13 | class QChart; 14 | QT_CHARTS_END_NAMESPACE 15 | 16 | QT_CHARTS_USE_NAMESPACE 17 | 18 | class Callout : public QGraphicsItem 19 | { 20 | public: 21 | Callout(QChart *parent); 22 | 23 | void setText(const QString &text); 24 | void setAnchor(QPointF point); 25 | void updateGeometry(); 26 | 27 | QRectF boundingRect() const; 28 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); 29 | 30 | protected: 31 | void mousePressEvent(QGraphicsSceneMouseEvent *event); 32 | void mouseMoveEvent(QGraphicsSceneMouseEvent *event); 33 | 34 | private: 35 | QString text; 36 | QRectF textRect; 37 | QRectF rect; 38 | QPointF anchor; 39 | QFont font; 40 | QChart *chart; 41 | }; 42 | 43 | #endif /* __CALLOUT_H__ */ 44 | -------------------------------------------------------------------------------- /chartview.cpp: -------------------------------------------------------------------------------- 1 | #include "chartview.h" 2 | 3 | ChartView::ChartView(QChart *chart, QWidget *parent) : 4 | QChartView(chart, parent), 5 | isClicking(false), 6 | xOld(0), yOld(0) 7 | { 8 | setRubberBand(QChartView::RectangleRubberBand); 9 | } 10 | 11 | void ChartView::mousePressEvent(QMouseEvent *event) 12 | { 13 | if (event->button() & Qt::LeftButton) { 14 | isClicking = true; 15 | } else if (event->button() & Qt::RightButton) { 16 | chart()->zoomReset(); 17 | } 18 | 19 | QChartView::mousePressEvent(event); 20 | } 21 | 22 | void ChartView::mouseMoveEvent(QMouseEvent *event) 23 | { 24 | int x, y; 25 | 26 | if (isClicking) { 27 | if (xOld == 0 && yOld == 0) { 28 | 29 | } else { 30 | x = event->x() - xOld; 31 | y = event->y() - yOld; 32 | chart()->scroll(-x, y); 33 | } 34 | 35 | xOld = event->x(); 36 | yOld = event->y(); 37 | 38 | return; 39 | } 40 | 41 | QChartView::mouseMoveEvent(event); 42 | } 43 | 44 | void ChartView::mouseReleaseEvent(QMouseEvent *event) 45 | { 46 | if (isClicking) { 47 | xOld = yOld = 0; 48 | isClicking = false; 49 | } 50 | 51 | /* Disable original right click event */ 52 | if (!(event->button() & Qt::RightButton)) { 53 | QChartView::mouseReleaseEvent(event); 54 | } 55 | } 56 | 57 | void ChartView::keyPressEvent(QKeyEvent *event) 58 | { 59 | switch (event->key()) { 60 | case Qt::Key_Left: 61 | chart()->scroll(-10, 0); 62 | break; 63 | case Qt::Key_Right: 64 | chart()->scroll(10, 0); 65 | break; 66 | case Qt::Key_Up: 67 | chart()->scroll(0, 10); 68 | break; 69 | case Qt::Key_Down: 70 | chart()->scroll(0, -10); 71 | break; 72 | default: 73 | keyPressEvent(event); 74 | break; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /chartview.h: -------------------------------------------------------------------------------- 1 | #ifndef __CHARTVIEW_H__ 2 | #define __CHARTVIEW_H__ 3 | 4 | #include 5 | #include 6 | 7 | QT_CHARTS_USE_NAMESPACE 8 | 9 | class ChartView : public QChartView 10 | { 11 | public: 12 | ChartView(QChart *chart, QWidget *parent = 0); 13 | 14 | protected: 15 | void keyPressEvent(QKeyEvent *event); 16 | void mousePressEvent(QMouseEvent *event); 17 | void mouseMoveEvent(QMouseEvent *event); 18 | void mouseReleaseEvent(QMouseEvent *event); 19 | 20 | private: 21 | bool isClicking; 22 | 23 | int xOld; 24 | int yOld; 25 | }; 26 | 27 | #endif /* __CHARTVIEW_H__ */ 28 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwidget.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWidget w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /mainwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwidget.h" 2 | #include "ui_mainwidget.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | MainWidget::MainWidget(QWidget *parent) : 11 | QWidget(parent), 12 | ui(new Ui::MainWidget), 13 | chart(new QChart), 14 | tip(0), 15 | timer(new QTimer), 16 | count(0), 17 | isStopping(false) 18 | { 19 | ui->setupUi(this); 20 | 21 | initUI(); 22 | 23 | timer->setInterval(50); 24 | timer->start(); 25 | 26 | initSlot(); 27 | } 28 | 29 | MainWidget::~MainWidget() 30 | { 31 | delete ui; 32 | } 33 | 34 | void MainWidget::wheelEvent(QWheelEvent *event) 35 | { 36 | if (event->delta() > 0) { 37 | chart->zoom(1.1); 38 | } else { 39 | chart->zoom(10.0/11); 40 | } 41 | 42 | QWidget::wheelEvent(event); 43 | } 44 | 45 | void MainWidget::initUI() 46 | { 47 | ui->comboBox->addItem("TCP"); 48 | ui->comboBox->addItem("UDP"); 49 | 50 | auto ports = QSerialPortInfo::availablePorts(); 51 | for (auto port : ports) { 52 | ui->comboBox->addItem(port.portName()); 53 | } 54 | 55 | initChart(); 56 | } 57 | 58 | void MainWidget::initChart() 59 | { 60 | series = new QLineSeries; 61 | 62 | chart->addSeries(series); 63 | 64 | // series->setUseOpenGL(true); 65 | 66 | chart->createDefaultAxes(); 67 | chart->axisY()->setRange(-10, 10); 68 | chart->axisX()->setRange(0, 96); 69 | 70 | chart->axisX()->setTitleFont(QFont("Microsoft YaHei", 10, QFont::Normal, true)); 71 | chart->axisY()->setTitleFont(QFont("Microsoft YaHei", 10, QFont::Normal, true)); 72 | chart->axisX()->setTitleText("Time/sec"); 73 | chart->axisY()->setTitleText("Speed/m"); 74 | 75 | chart->axisX()->setGridLineVisible(false); 76 | chart->axisY()->setGridLineVisible(false); 77 | 78 | /* hide legend */ 79 | chart->legend()->hide(); 80 | 81 | chartView = new ChartView(chart); 82 | chartView->setRenderHint(QPainter::Antialiasing); 83 | 84 | ui->mainHorLayout->addWidget(chartView); 85 | } 86 | 87 | void MainWidget::initSlot() 88 | { 89 | connect(timer, SIGNAL(timeout()), this, SLOT(timerSlot())); 90 | connect(ui->stopBtn, SIGNAL(clicked(bool)), this, SLOT(buttonSlot())); 91 | connect(series, SIGNAL(hovered(QPointF, bool)), this, SLOT(tipSlot(QPointF,bool))); 92 | } 93 | 94 | void MainWidget::updateData() 95 | { 96 | int i; 97 | QVector oldData = series->pointsVector(); 98 | QVector data; 99 | 100 | if (oldData.size() < 97) { 101 | data = series->pointsVector(); 102 | } else { 103 | /* 添加之前老的数据到新的vector中,不复制最前的数据,即每次替换前面的数据 104 | * 由于这里每次只添加1个数据,所以为1,使用时根据实际情况修改 105 | */ 106 | for (i = 1; i < oldData.size(); ++i) { 107 | data.append(QPointF(i - 1 , oldData.at(i).y())); 108 | } 109 | } 110 | 111 | qint64 size = data.size(); 112 | /* 这里表示插入新的数据,因为每次只插入1个,这里为i < 1, 113 | * 但为了后面方便插入多个数据,先这样写 114 | */ 115 | for(i = 0; i < 1; ++i){ 116 | data.append(QPointF(i + size, 10 * sin(M_PI * count * 4 / 180))); 117 | } 118 | 119 | series->replace(data); 120 | 121 | count++; 122 | } 123 | 124 | void MainWidget::timerSlot() 125 | { 126 | if (QObject::sender() == timer) { 127 | updateData(); 128 | } 129 | } 130 | 131 | void MainWidget::buttonSlot() 132 | { 133 | if (QObject::sender() == ui->stopBtn) { 134 | if (!isStopping) { 135 | timer->stop(); 136 | } else { 137 | timer->start(); 138 | } 139 | isStopping = !isStopping; 140 | } 141 | } 142 | 143 | void MainWidget::tipSlot(QPointF position, bool isHovering) 144 | { 145 | if (tip == 0) 146 | tip = new Callout(chart); 147 | 148 | if (isHovering) { 149 | tip->setText(QString("X: %1 \nY: %2 ").arg(position.x()).arg(position.y())); 150 | tip->setAnchor(position); 151 | tip->setZValue(11); 152 | tip->updateGeometry(); 153 | tip->show(); 154 | } else { 155 | tip->hide(); 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /mainwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWIDGET_H 2 | #define MAINWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "chartview.h" 11 | #include "callout.h" 12 | 13 | QT_CHARTS_USE_NAMESPACE 14 | 15 | namespace Ui { 16 | class MainWidget; 17 | } 18 | 19 | class MainWidget : public QWidget 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit MainWidget(QWidget *parent = 0); 25 | ~MainWidget(); 26 | 27 | private: 28 | void wheelEvent(QWheelEvent *event); 29 | 30 | void initUI(); 31 | void initChart(); 32 | void initSlot(); 33 | 34 | void updateData(); 35 | 36 | Ui::MainWidget *ui; 37 | 38 | ChartView *chartView; 39 | QChart *chart; 40 | Callout *tip; 41 | QLineSeries *series; 42 | 43 | QTimer *timer; 44 | 45 | quint16 count; 46 | 47 | bool isStopping; 48 | 49 | private slots: 50 | void timerSlot(); 51 | void buttonSlot(); 52 | void tipSlot(QPointF position, bool isHovering); 53 | 54 | }; 55 | 56 | #endif // MAINWIDGET_H 57 | -------------------------------------------------------------------------------- /mainwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 640 11 | 12 | 13 | 14 | MainWidget 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 输入源: 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Qt::Horizontal 42 | 43 | 44 | 45 | 40 46 | 20 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 停止 55 | 56 | 57 | 58 | 59 | 60 | 61 | Qt::Horizontal 62 | 63 | 64 | 65 | 40 66 | 20 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 设置 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | --------------------------------------------------------------------------------