1. 介绍
QCustomPlot是一个用于绘图和数据可视化的Qt C ++小部件。 此绘图库专注于制作好看且高质量的2D绘图,图形和图表,以及为实时可视化应用程序提供高性能的绘图。
2. 下载
下载地址:http://www.qcustomplot.com/index.php/download
3. 集成到QtCreator / QtAssistant
解压得到的文件,见下图
打开 documentation添加 qcustomplot.qch 到QtCreator
4. 上传一个代码例程
工程文件如下:
widget.h 如下
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtSerialPort/QSerialPort>
#include <QSerialPortInfo>
#include <QColor>
#include <cstdlib>
#include "qcustomplot.h"
#define TextColor QColor(255,255,255)
#define Plot_NoColor QColor(0,0,0,0)
#define Plot_DotColor QColor(236,110,0)
#define Plot_LineColor QColor(180,252,253)
#define Plot_BGColor QColor(246,98,0,80)
#define TextWidth 1
#define LineWidth 2
#define DotWidth 10
#define HEAD 0xaa
#define TAIL 0x55
#define PACLEN 4
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
//打开串口
bool OpenPort(QString portName, /*串口号*/
QSerialPort::BaudRate baud, /*波特率*/
QSerialPort::Parity parity = QSerialPort::NoParity, /*奇偶校验*/
QSerialPort::DataBits databits = QSerialPort::Data8, /*数据位*/
QSerialPort::StopBits stopbits = QSerialPort::OneStop); /*停止位*/
//关闭串口
void ClosePort();
//初始化图像
void init_plot();
protected:
void closeEvent(QCloseEvent *e);
signals:
void start_write(bool b);
private slots:
void on_pb_open_clicked();
void on_pb_send_clicked();
void on_le_time_editingFinished();
void on_le_data_editingFinished();
void do_test(bool b);
void do_read();
private:
Ui::Widget *ui;
double x_key;
bool is_test;
int time;
QStringList data;
QSerialPort *m_serialport;
int have_readLen;
char buf[PACLEN];
};
#endif // WIDGET_H
widget.cpp 文件
#include "widget.h"
#include "ui_widget.h"
#include <QSortFilterProxyModel>
#include <QtTest/QTest>
#include <QRegExp>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
m_serialport = new QSerialPort;
//获得所有串口
QStringList list_info = QStringList();
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
list_info.append(info.portName());
}
ui->cb_name->addItems(list_info);
//波特率
QStringList list_baud = QStringList() << "115200"
<< "57600"
<< "38400"
<< "19200"
<< "9600"
<< "4800"
<< "2400"
<< "1200";
ui->cb_baud->addItems(list_baud);
//数据位
QStringList list_data = QStringList() << "8"
<< "7"
<< "6"
<< "5";
ui->cb_data->addItems(list_data);
//停止位
QStringList list_stop = QStringList() << "1"
<< "1.5"
<< "2";
ui->cb_stop->addItems(list_stop);
//校验位
QStringList list_parity = QStringList() << "No"
<< "Even"
<< "Odd"
<< "Space"
<< "Mark";
ui->cb_parity->addItems(list_parity);
ui->le_time->setText(QString("10"));
ui->le_data->setText(QString("a1"));
time = 10;
data.append(QString("a1"));
x_key = 0; //记录绘图的x坐标当前值
have_readLen = 0; //已经读到的数据长度
init_plot();
connect(m_serialport, SIGNAL(readyRead()), this, SLOT(do_read()));
connect(this, SIGNAL(start_write(bool)), this, SLOT(do_test(bool)));
}
Widget::~Widget()
{
delete ui;
if(m_serialport->isOpen())
ClosePort();
}
void Widget::closeEvent(QCloseEvent *e)
{
if(m_serialport->isOpen())
ClosePort();
}
bool Widget::OpenPort(QString portName,
QSerialPort::BaudRate baud,
QSerialPort::Parity parity,
QSerialPort::DataBits databits,
QSerialPort::StopBits stopbits)
{
m_serialport->setPortName(portName);
m_serialport->setBaudRate(baud);
m_serialport->setParity(parity);
m_serialport->setDataBits(databits);
m_serialport->setStopBits(stopbits);
if(m_serialport->open(QIODevice::ReadWrite))
{
return true;
}else {
return false;
}
}
void Widget::ClosePort()
{
m_serialport->close();
}
void Widget::on_pb_open_clicked()
{
if(ui->pb_open->text() == QString(tr("打开串口")))
{
QSerialPort::BaudRate baud_ = (QSerialPort::BaudRate)(ui->cb_baud->currentText().toInt());
QSerialPort::Parity parity_;
QString par = ui->cb_parity->currentText();
if(par == "No")
parity_ = QSerialPort::NoParity;
else if(par == "Even")
parity_ = QSerialPort::EvenParity;
else if(par == "Odd")
parity_ = QSerialPort::OddParity;
else if(par == "Space")
parity_ = QSerialPort::SpaceParity;
else if(par == "Mark")
parity_ = QSerialPort::MarkParity;
QSerialPort::DataBits data_ = (QSerialPort::DataBits)ui->cb_data->currentText().toInt();
QSerialPort::StopBits stop_;
QString sto = ui->cb_stop->currentText();
if(sto == "1")
stop_ = QSerialPort::OneStop;
else if(sto == "1.5")
stop_ = QSerialPort::OneAndHalfStop;
else if(sto == "2")
stop_ = QSerialPort::TwoStop;
bool is_open = OpenPort(ui->cb_name->currentText(), baud_, parity_, data_, stop_);
if(is_open)
{
ui->pb_open->setText(QString(tr("关闭串口")));
x_key = 0;
ui->graph_widget->removeGraph(0);
ui->graph_widget->addGraph();
ui->graph_widget->graph()->setPen(QPen(Plot_LineColor, LineWidth));
double upper = ui->graph_widget->xAxis->range().upper;
double lower = ui->graph_widget->xAxis->range().lower;
ui->graph_widget->xAxis->setRange(0, upper - lower);
}else{
ui->pb_open->setText(QString(tr("打开串口")));
}
}else{
ClosePort();
ui->pb_open->setText(QString(tr("打开串口")));
}
}
void Widget::on_pb_send_clicked()
{
if(!m_serialport->isOpen()) return;
if(ui->pb_send->text() == QString(tr("发送数据")))
{
ui->pb_send->setText(QString(tr("停止发送")));
ui->le_time->setEnabled(false);
ui->le_data->setEnabled(false);
is_test = true;
emit start_write(is_test);
}else{
ui->pb_send->setText(QString(tr("发送数据")));
ui->le_time->setEnabled(true);
ui->le_data->setEnabled(true);
is_test = false;
emit start_write(is_test);
}
}
void Widget::do_test(bool b)
{
int data_len = data.size();
char *data_new = new char[data_len];
bool ok;
for(int i=0; i<data_len; i++)
{
data_new[i] = (char)data.at(i).toInt(&ok, 16);
}
while (is_test) {
m_serialport->write(data_new, data_len);
QTest::qWait(time);
}
delete[] data_new;
}
void Widget::init_plot()
{
//设置四边都有坐标
ui->graph_widget->axisRect()->setupFullAxesBox(true);
//设置坐标颜色/坐标名称颜色
ui->graph_widget->yAxis->setLabelColor(TextColor);
ui->graph_widget->xAxis->setLabelColor(TextColor);
ui->graph_widget->xAxis->setTickLabelColor(TextColor);
ui->graph_widget->yAxis->setTickLabelColor(TextColor);
ui->graph_widget->xAxis->setBasePen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis->setBasePen(QPen(TextColor, TextWidth));
ui->graph_widget->xAxis->setTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis->setTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->xAxis->setSubTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis->setSubTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis2->setLabelColor(TextColor);
ui->graph_widget->xAxis2->setLabelColor(TextColor);
ui->graph_widget->xAxis2->setTickLabelColor(TextColor);
ui->graph_widget->yAxis2->setTickLabelColor(TextColor);
ui->graph_widget->xAxis2->setBasePen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis2->setBasePen(QPen(TextColor, TextWidth));
ui->graph_widget->xAxis2->setTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis2->setTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->xAxis2->setSubTickPen(QPen(TextColor, TextWidth));
ui->graph_widget->yAxis2->setSubTickPen(QPen(TextColor, TextWidth));
//设置画布背景色
QLinearGradient plotGradient;
plotGradient.setStart(0, 0);
plotGradient.setFinalStop(0, 350);
plotGradient.setColorAt(0, QColor(80, 80, 80));
plotGradient.setColorAt(1, QColor(50, 50, 50));
ui->graph_widget->setBackground(plotGradient);
//设置坐标背景色
QLinearGradient axisRectGradient;
axisRectGradient.setStart(0, 0);
axisRectGradient.setFinalStop(0, 350);
axisRectGradient.setColorAt(0, QColor(80, 80, 80));
axisRectGradient.setColorAt(1, QColor(30, 30, 30));
ui->graph_widget->axisRect()->setBackground(axisRectGradient);
//设置图例提示位置及背景色
ui->graph_widget->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignTop | Qt::AlignRight);
ui->graph_widget->legend->setBrush(QColor(255, 255, 255, 200));
ui->graph_widget->replot();
//设置可拖拽、横坐标可放大缩小
ui->graph_widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
ui->graph_widget->axisRect(0)->setRangeZoomFactor(2, 1);
//添加曲线
ui->graph_widget->addGraph();
ui->graph_widget->graph()->setPen(QPen(Plot_LineColor, LineWidth));
//设置横坐标
// ui->graph_widget->yAxis->grid()->deleteLater();
// ui->graph_widget->xAxis->grid()->deleteLater();
ui->graph_widget->xAxis->setRange(0, 20);
// ui->graph_widget->xAxis->setAutoTickStep(false);
// ui->graph_widget->xAxis->setTickStep(1);
// ui->graph_widget->xAxis->setAutoTickLabels(false);
ui->graph_widget->xAxis->setLabel(tr("时间 /%1msc").arg(ui->le_time->text()));
}
void Widget::on_le_time_editingFinished()
{
QRegExp rx("\\d+");
int pos = rx.indexIn(ui->le_time->text());
if(pos > -1)
{
QString time_ = rx.cap(0);
ui->le_time->setText(time_);
}else{
ui->le_time->setText(QString("10"));
}
ui->graph_widget->xAxis->setLabel(tr("时间 /%1msc").arg(ui->le_time->text()));
time = ui->le_time->text().toInt();
ui->graph_widget->replot();
}
void Widget::on_le_data_editingFinished()
{
QRegExp rx("([0-9a-fA-F]{2})");
QString str = ui->le_data->text();
QStringList list;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1) {
list << rx.cap(1);
pos += rx.matchedLength();
}
if(!list.isEmpty())
{
QString data_;
for(int i=0; i<list.size(); i++)
{
data_.append(list.at(i)).append(" ");
}
ui->le_data->setText(data_);
data.clear();
data = list;
}else{
ui->le_data->setText(QString("a1"));
data.clear();
data.append("a1");
}
}
void Widget::do_read()
{
have_readLen += m_serialport->read(buf+have_readLen, PACLEN-have_readLen);
if(have_readLen < PACLEN) {
return;
}
if(buf[0] != (char)HEAD)
{
memcpy(buf, buf+1, PACLEN-1);
have_readLen--;
return;
}
if(buf[PACLEN-1] != (char)TAIL)
{
have_readLen = 0;
return;
}
int value = (((int)buf[1])<<8 & 0x0000ff00);
value += (int)buf[2] & 0x000000ff;
ui->graph_widget->graph()->addData((double)x_key, (double)value);
ui->graph_widget->graph()->rescaleValueAxis(true);
double upper = ui->graph_widget->xAxis->range().upper;
double lower = ui->graph_widget->xAxis->range().lower;
if(x_key > upper)
{
ui->graph_widget->xAxis->setRange(x_key-(upper-lower), x_key);
}
ui->graph_widget->replot();
have_readLen = 0;
x_key += 1.0;
}
main.cpp文件
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.ui 文件,自己想办法将下面的代码覆盖进去
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>691</width>
<height>327</height>
</rect>
</property>
<property name="windowTitle">
<string>串口绘图</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>22</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>191</width>
<height>221</height>
</size>
</property>
<property name="title">
<string>串口设置</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>串口号:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cb_name"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>波特率:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cb_baud"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>数据位:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cb_data"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>停止位:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cb_stop"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>校验位:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cb_parity"/>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="pb_open">
<property name="text">
<string>打开串口</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="pb_send">
<property name="text">
<string>发送数据</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>8</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>191</width>
<height>80</height>
</size>
</property>
<property name="title">
<string>其他设置</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>151</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>发送周期:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_time">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>msc</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>151</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>发送数据:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_data">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>Hex</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCustomPlot" name="graph_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QCustomPlot</class>
<extends>QWidget</extends>
<header location="global">qcustomplot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
plot_serial.pro 文件
#-------------------------------------------------
#
# Project created by QtCreator 2016-12-14T08:44:10
#
#-------------------------------------------------
QT += core gui serialport printsupport testlib
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
TARGET = plot_serial
TEMPLATE = app
SOURCES += main.cpp\
widget.cpp \
qcustomplot.cpp
HEADERS += widget.h \
qcustomplot.h
FORMS += widget.ui
程序效果如下:
可用鼠标拖拽,滑轮水平x轴缩小放大等。