├── README.txt
├── SerialDebuger_QCustomPlot.pro
├── SerialDebuger_QCustomPlot.pro.user
├── axis_64px_1200251_easyicon.net.ico
├── chart_curve_32px_506507_easyicon.net.ico
├── datafilter.cpp
├── datafilter.h
├── debug_32px_1103189_easyicon.net.ico
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── mycombobox.cpp
├── mycombobox.h
├── pic.qrc
├── pic
├── ssCircle.png
├── ssCross.png
├── ssCrossCircle.png
├── ssCrossSquare.png
├── ssDiamond.png
├── ssDisc.png
├── ssNone.png
├── ssPeace.png
├── ssPlus.png
├── ssPlusCircle.png
├── ssPlusSquare.png
├── ssSquare.png
├── ssStar.png
├── ssTriangle.png
└── ssTriangleInverted.png
├── plot.cpp
├── plot.h
├── plot.ui
├── qcustomplot.cpp
├── qcustomplot.h
├── res.qrc
└── 测试帧.txt
/README.txt:
--------------------------------------------------------------------------------
1 | 基于QCustomPlot,使用Qt编写的串口波形实时绘图工具。
2 |
3 |
4 | 基本功能齐全,包括:
5 | 自动扫描可用串口端口,同时显示端口硬件信息(无须再查看设备管理器,方便选择),波特率可手动输入。
6 | 发送/接收字节、速率统计、帧统计、误码统计。
7 | 接收区每2000字节强制换行,避免一行的内容太多,导致CPU占用过高从而卡顿,已测试过累计千万字节不卡顿。(仅仅是在接收显示里加换行,实际的真实数据不受影响)
8 | 支持中文收发,默认GB2312编码,如需Unicode/UTF8/UTF16需要更改代码。(单片机中文编码大多使用GB2312,占用空间小)
9 | 收/发区,无缝16进制转换,GB2312中文不乱码。输入框可作为转换框,无须在计算后重复输入。
10 |
11 | 增加帧捕获功能,用户可自定义修改两字节的帧头,帧协议兼容匿名四轴调试上位机。
12 | 增加捕获帧调试界面,方便调试。
13 |
14 | 波形绘图最多可显示20条曲线。曲线连接方式,颜色、散点形状可任意设置。可隐藏,可加粗显示。
15 | 曲线连接方式支持:点、线连接、左台阶、右台阶、居中台阶、纵值绘图。
16 | 波形散点形状支持:x形、+形、空心圆、实心圆、矩形、菱形、星形、正三角、倒三角、x框、+框、x圆、+圆、和平。
17 | 动态绘图支持: X轴自动跟随、Y轴范围自适应。
18 | 图页显示范围可在右下角手动输入调整。
19 | 支持滚轮Y轴缩放、左键拖拽、单击曲线加粗显示,不支持拖拽缩放。
20 |
21 |
22 | 使用说明,功能帧格式,见我的博客:
23 | https://blog.csdn.net/Mark_md/article/details/109995490
--------------------------------------------------------------------------------
/SerialDebuger_QCustomPlot.pro:
--------------------------------------------------------------------------------
1 | QT += core gui serialport
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
4 |
5 | CONFIG += c++11
6 |
7 | # The following define makes your compiler emit warnings if you use
8 | # any Qt feature that has been marked deprecated (the exact warnings
9 | # depend on your compiler). Please consult the documentation of the
10 | # deprecated API in order to know how to port your code away from it.
11 | DEFINES += QT_DEPRECATED_WARNINGS
12 |
13 | RC_ICONS = debug_32px_1103189_easyicon.net.ico
14 |
15 | # You can also make your code fail to compile if it uses deprecated APIs.
16 | # In order to do so, uncomment the following line.
17 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
18 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
19 |
20 | SOURCES += \
21 | main.cpp \
22 | mainwindow.cpp \
23 | mycombobox.cpp \
24 | plot.cpp \
25 | qcustomplot.cpp
26 |
27 | HEADERS += \
28 | mainwindow.h \
29 | mycombobox.h \
30 | plot.h \
31 | qcustomplot.h
32 |
33 | FORMS += \
34 | mainwindow.ui \
35 | plot.ui
36 |
37 | # Default rules for deployment.
38 | qnx: target.path = /tmp/$${TARGET}/bin
39 | else: unix:!android: target.path = /opt/$${TARGET}/bin
40 | !isEmpty(target.path): INSTALLS += target
41 |
42 | RESOURCES += \
43 | res.qrc
44 |
--------------------------------------------------------------------------------
/SerialDebuger_QCustomPlot.pro.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | EnvironmentId
7 | {a0493980-663c-4399-bed9-6237f8a1eee5}
8 |
9 |
10 | ProjectExplorer.Project.ActiveTarget
11 | 0
12 |
13 |
14 | ProjectExplorer.Project.EditorSettings
15 |
16 | true
17 | false
18 | true
19 |
20 | Cpp
21 |
22 | CppGlobal
23 |
24 |
25 |
26 | QmlJS
27 |
28 | QmlJSGlobal
29 |
30 |
31 | 2
32 | UTF-8
33 | false
34 | 4
35 | false
36 | 80
37 | true
38 | true
39 | 1
40 | true
41 | false
42 | 0
43 | true
44 | true
45 | 0
46 | 8
47 | true
48 | 1
49 | true
50 | true
51 | true
52 | false
53 |
54 |
55 |
56 | ProjectExplorer.Project.PluginSettings
57 |
58 |
59 | -fno-delayed-template-parsing
60 |
61 | true
62 | Builtin.Questionable
63 |
64 | true
65 | Builtin.DefaultTidyAndClazy
66 | 2
67 |
68 |
69 |
70 | true
71 |
72 |
73 |
74 |
75 | ProjectExplorer.Project.Target.0
76 |
77 | Desktop Qt 5.12.9 MinGW 64-bit
78 | Desktop Qt 5.12.9 MinGW 64-bit
79 | qt.qt5.5129.win64_mingw73_kit
80 | 1
81 | 0
82 | 0
83 |
84 | true
85 | 0
86 | D:\Qt\Qt_Code\PRJ\SerialDebuger_QCustomPlot\build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Debug
87 | D:/Qt/Qt_Code/PRJ/SerialDebuger_QCustomPlot/build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Debug
88 |
89 |
90 | true
91 | QtProjectManager.QMakeBuildStep
92 |
93 | false
94 |
95 |
96 |
97 | true
98 | Qt4ProjectManager.MakeStep
99 |
100 | false
101 |
102 |
103 | false
104 |
105 | 2
106 | Build
107 | Build
108 | ProjectExplorer.BuildSteps.Build
109 |
110 |
111 |
112 | true
113 | Qt4ProjectManager.MakeStep
114 |
115 | true
116 | clean
117 |
118 | false
119 |
120 | 1
121 | Clean
122 | Clean
123 | ProjectExplorer.BuildSteps.Clean
124 |
125 | 2
126 | false
127 |
128 | Debug
129 | Qt4ProjectManager.Qt4BuildConfiguration
130 | 2
131 | 2
132 | 2
133 |
134 |
135 | true
136 | 2
137 | D:\Qt\Qt_Code\PRJ\SerialDebuger_QCustomPlot\build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Release
138 | D:/Qt/Qt_Code/PRJ/SerialDebuger_QCustomPlot/build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Release
139 |
140 |
141 | true
142 | QtProjectManager.QMakeBuildStep
143 |
144 | false
145 |
146 |
147 |
148 | true
149 | Qt4ProjectManager.MakeStep
150 |
151 | false
152 |
153 |
154 | false
155 |
156 | 2
157 | Build
158 | Build
159 | ProjectExplorer.BuildSteps.Build
160 |
161 |
162 |
163 | true
164 | Qt4ProjectManager.MakeStep
165 |
166 | true
167 | clean
168 |
169 | false
170 |
171 | 1
172 | Clean
173 | Clean
174 | ProjectExplorer.BuildSteps.Clean
175 |
176 | 2
177 | false
178 |
179 | Release
180 | Qt4ProjectManager.Qt4BuildConfiguration
181 | 0
182 | 0
183 | 2
184 |
185 |
186 | true
187 | 0
188 | D:\Qt\Qt_Code\PRJ\SerialDebuger_QCustomPlot\build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Profile
189 | D:/Qt/Qt_Code/PRJ/SerialDebuger_QCustomPlot/build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Profile
190 |
191 |
192 | true
193 | QtProjectManager.QMakeBuildStep
194 |
195 | false
196 |
197 |
198 |
199 | true
200 | Qt4ProjectManager.MakeStep
201 |
202 | false
203 |
204 |
205 | false
206 |
207 | 2
208 | Build
209 | Build
210 | ProjectExplorer.BuildSteps.Build
211 |
212 |
213 |
214 | true
215 | Qt4ProjectManager.MakeStep
216 |
217 | true
218 | clean
219 |
220 | false
221 |
222 | 1
223 | Clean
224 | Clean
225 | ProjectExplorer.BuildSteps.Clean
226 |
227 | 2
228 | false
229 |
230 | Profile
231 | Qt4ProjectManager.Qt4BuildConfiguration
232 | 0
233 | 0
234 | 0
235 |
236 | 3
237 |
238 |
239 | 0
240 | Deploy
241 | Deploy
242 | ProjectExplorer.BuildSteps.Deploy
243 |
244 | 1
245 |
246 | false
247 | ProjectExplorer.DefaultDeployConfiguration
248 |
249 | 1
250 |
251 |
252 | dwarf
253 |
254 | cpu-cycles
255 |
256 |
257 | 250
258 |
259 | -e
260 | cpu-cycles
261 | --call-graph
262 | dwarf,4096
263 | -F
264 | 250
265 |
266 | -F
267 | true
268 | 4096
269 | false
270 | false
271 | 1000
272 |
273 | true
274 |
275 | false
276 | false
277 | false
278 | false
279 | true
280 | 0.01
281 | 10
282 | true
283 | kcachegrind
284 | 1
285 | 25
286 |
287 | 1
288 | true
289 | false
290 | true
291 | valgrind
292 |
293 | 0
294 | 1
295 | 2
296 | 3
297 | 4
298 | 5
299 | 6
300 | 7
301 | 8
302 | 9
303 | 10
304 | 11
305 | 12
306 | 13
307 | 14
308 |
309 | 2
310 |
311 | Qt4ProjectManager.Qt4RunConfiguration:D:/Qt/Qt_Code/PRJ/SerialDebuger_QCustomPlot/SerialDebuger_QCustomPlot/SerialDebuger_QCustomPlot.pro
312 | D:/Qt/Qt_Code/PRJ/SerialDebuger_QCustomPlot/SerialDebuger_QCustomPlot/SerialDebuger_QCustomPlot.pro
313 |
314 | false
315 |
316 | false
317 | true
318 | true
319 | false
320 | false
321 | true
322 |
323 | D:/Qt/Qt_Code/PRJ/SerialDebuger_QCustomPlot/build-SerialDebuger_QCustomPlot-Desktop_Qt_5_12_9_MinGW_64_bit-Release
324 |
325 | 1
326 |
327 |
328 |
329 | ProjectExplorer.Project.TargetCount
330 | 1
331 |
332 |
333 | ProjectExplorer.Project.Updater.FileVersion
334 | 22
335 |
336 |
337 | Version
338 | 22
339 |
340 |
341 |
--------------------------------------------------------------------------------
/axis_64px_1200251_easyicon.net.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/axis_64px_1200251_easyicon.net.ico
--------------------------------------------------------------------------------
/chart_curve_32px_506507_easyicon.net.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/chart_curve_32px_506507_easyicon.net.ico
--------------------------------------------------------------------------------
/datafilter.cpp:
--------------------------------------------------------------------------------
1 | #include "datafilter.h"
2 |
3 | DataFilter::DataFilter()
4 | {
5 | // 要不要在析构中,初始化清0全局变量?
6 | }
7 |
8 | DataFilter::~DataFilter()
9 | {
10 |
11 | }
12 |
13 | QByteArray DataFilter::xFrameDataFilter(QByteArray *str)
14 | {
15 | int num = str->size();
16 | if(num)
17 | {
18 | for(int i=0; iat(i); // 从接收缓存区读取一个字节
21 |
22 | if (f_h_flag == 1) // 有帧头,判断帧尾,接收消息
23 | {
24 | if (f_t1_flag == 1) //有帧头,有帧尾1
25 | {
26 | if (chrtmp[tnum] == Frame_Tail2)
27 | {
28 | // 用户处理代码 //
29 | // 将接收到符合帧定义的帧,原路发送回去 //
30 | /*int i = 0;
31 | for (i = 0; i < (tnum + 1); i++)
32 | {
33 | //serialPutchar(hs1, chrtmp[i]); // 通过串口发送字节
34 | }*/
35 | QByteArray str1;
36 | for(int i=0; i<(tnum + 1); i++)
37 | {
38 | str1.append(chrtmp[i]);
39 | }
40 | return str1;
41 |
42 | /*// 16进制显示,并转换为大写
43 | QString str2 = str1.toHex().toUpper();
44 | // 添加空格
45 | QString str3;
46 | for(int i = 0; itxtSend->insertPlainText(str3);
52 | // 移动光标到文本结尾
53 | ui->txtSend->moveCursor(QTextCursor::End);*/
54 |
55 |
56 | // 处理完用户代码,重新接收计数 //
57 | tnum = 0;
58 | }
59 | else
60 | {
61 | f_t1_flag = 0;
62 | tnum ++;
63 | }
64 | }
65 | else // 有帧头,无帧尾1
66 | {
67 | if (chrtmp[tnum] == Frame_Tail1)
68 | {
69 | f_t1_flag = 1;
70 | tnum ++;
71 | }
72 | else // 接收消息包中间内容
73 | {
74 | tnum ++;
75 | }
76 | }
77 | }
78 | else // 没有接收到帧头
79 | {
80 | if (f_h1_flag == 1) //没有帧头,有帧头1。下一步判断是否为第2个字节
81 | {
82 | if (chrtmp[tnum] == Frame_Header2) // 如果为帧头的第2个字节,接收到帧头标志位标志位置1,tnum自增
83 | {
84 | f_h_flag = 1;
85 | tnum ++;
86 | }
87 | else
88 | {
89 | f_h1_flag = 0;
90 | tnum = 0;
91 | }
92 | }
93 | else //没有帧头,没有有帧头1。下一步判断,是否为帧头的第1个字节
94 | {
95 | if (chrtmp[tnum] == Frame_Header1) // 如果为帧头的第1个字节,标志位置1,tnum自增
96 | {
97 | f_h1_flag = 1;
98 | tnum ++;
99 | }
100 | else // 否则,标志位清0,tnum清0
101 | {
102 | tnum = 0;
103 | }
104 | }
105 | }
106 |
107 | // 大于MaxFrameLength个字节的帧不接收
108 | if (tnum > (MaxFrameLength - 1) )
109 | {
110 | tnum = 0;
111 | f_h1_flag = 0;
112 | f_h_flag = 0;
113 | f_t1_flag = 0;
114 | continue;
115 | }
116 | }
117 | }
118 | }
119 |
120 | /*
121 | int main(void)
122 | {
123 |
124 |
125 | while (1)
126 | {
127 | snum = serialDataAvail(hs1); // 获取串口接收缓存区的可用字节数
128 | if(snum > 0)
129 | {
130 | chrtmp[tnum] = serialGetchar(hs1); // 从接收缓存区读取一个字节
131 |
132 | if (f_h_flag == 1) // 有帧头,判断帧尾,接收消息
133 | {
134 | if (f_t1_flag == 1) //有帧头,有帧尾1
135 | {
136 | if (chrtmp[tnum] == Frame_Tail2)
137 | {
138 | // 用户处理代码 //
139 | // 将接收到符合帧定义的帧,原路发送回去 //
140 | int i = 0;
141 | for (i = 0; i < (tnum + 1); i++)
142 | {
143 | serialPutchar(hs1, chrtmp[i]); // 通过串口发送字节
144 | }
145 |
146 | // 处理完用户代码,重新接收计数 //
147 | tnum = 0;
148 | }
149 | else
150 | {
151 | f_t1_flag = 0;
152 | tnum ++;
153 | }
154 | }
155 | else // 有帧头,无帧尾1
156 | {
157 | if (chrtmp[tnum] == Frame_Tail1)
158 | {
159 | f_t1_flag = 1;
160 | tnum ++;
161 | }
162 | else // 接收消息包中间内容
163 | {
164 | tnum ++;
165 | }
166 | }
167 | }
168 | else // 没有接收到帧头
169 | {
170 | if (f_h1_flag == 1) //没有帧头,有帧头1。下一步判断是否为第2个字节
171 | {
172 | if (chrtmp[tnum] == Frame_Header2) // 如果为帧头的第2个字节,接收到帧头标志位标志位置1,tnum自增
173 | {
174 | f_h_flag = 1;
175 | tnum ++;
176 | }
177 | else
178 | {
179 | f_h1_flag = 0;
180 | tnum = 0;
181 | }
182 | }
183 | else //没有帧头,没有有帧头1。下一步判断,是否为帧头的第1个字节
184 | {
185 | if (chrtmp[tnum] == Frame_Header1) // 如果为帧头的第1个字节,标志位置1,tnum自增
186 | {
187 | f_h1_flag = 1;
188 | tnum ++;
189 | }
190 | else // 否则,标志位清0,tnum清0
191 | {
192 | tnum = 0;
193 | }
194 | }
195 | }
196 |
197 | // 大于MaxFrameLength个字节的帧不接收
198 | if (tnum > (MaxFrameLength - 1) )
199 | {
200 | tnum = 0;
201 | f_h1_flag = 0;
202 | f_h_flag = 0;
203 | f_t1_flag = 0;
204 | continue;
205 | }
206 |
207 | }
208 | }
209 |
210 | return 0;
211 | */
212 |
--------------------------------------------------------------------------------
/datafilter.h:
--------------------------------------------------------------------------------
1 | #ifndef DATAFILTER_H
2 | #define DATAFILTER_H
3 |
4 | #include
5 |
6 | // 最大帧长度
7 | #define MaxFrameLength 10 // 对最大帧长度加以限定,防止接收到过长的帧数据
8 | // 完整的帧头,2个字节
9 | #define Frame_Header1 0x3A // 串口接收消息包的帧头的第1个字节
10 | #define Frame_Header2 0x3B // 串口接收消息包的帧头的第2个字节
11 | // 完整的帧尾,2个字节
12 | #define Frame_Tail1 0x7E // 串口接收消息包的帧尾的第1个字节
13 | #define Frame_Tail2 0x7F // 串口接收消息包的帧尾的第2个字节
14 |
15 |
16 | class DataFilter
17 | {
18 | public:
19 | DataFilter();
20 | ~DataFilter();
21 |
22 | QByteArray xFrameDataFilter(QByteArray *str);
23 |
24 | private:
25 | int snum = 0; // 系统串口接收缓存区的可用字节数
26 | int tnum = 0; // 用户串口接收区的数目
27 | unsigned char chrtmp[100]; // 用户串口接收区,将缓存的数据放入这里处理
28 | int f_h1_flag = 0; // 接收到帧头的第一个字节标志位
29 | int f_h_flag = 0; // 接收到帧头标志位
30 | int f_t1_flag = 0; // 接收到帧尾的第一个字节标志位
31 | };
32 |
33 | #endif // DATAFILTER_H
34 |
--------------------------------------------------------------------------------
/debug_32px_1103189_easyicon.net.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/debug_32px_1103189_easyicon.net.ico
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 |
3 | #include
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | QApplication a(argc, argv);
8 | MainWindow w;
9 | w.show();
10 | return a.exec();
11 | }
12 |
--------------------------------------------------------------------------------
/mainwindow.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include "ui_mainwindow.h"
3 | #include
4 |
5 | MainWindow::MainWindow(QWidget *parent)
6 | : QMainWindow(parent)
7 | , ui(new Ui::MainWindow)
8 | {
9 | ui->setupUi(this);
10 | setWindowTitle("Qt Serial Debugger");
11 |
12 | // 状态栏
13 | QStatusBar *sBar = statusBar();
14 | // 状态栏的收、发计数标签
15 | lblSendNum = new QLabel(this);
16 | lblRecvNum = new QLabel(this);
17 | lblSendRate = new QLabel(this);
18 | lblRecvRate = new QLabel(this);
19 | // 设置标签最小大小
20 | lblSendNum->setMinimumSize(100, 20);
21 | lblRecvNum->setMinimumSize(100, 20);
22 | lblSendRate->setMinimumSize(100, 20);
23 | lblRecvRate->setMinimumSize(100, 20);
24 | //statusBar()->showMessage("留言", 5000);// 留言显示,过期时间单位为ms,过期后不再有显示
25 | //statusBar()->setSizeGripEnabled(false); // 是否显示右下角拖放控制点,默认显示
26 | //statusBar()->setStyleSheet(QString("QStatusBar::item{border: 0px}")); // 设置不显示label的边框
27 | //lblSendNum->setAlignment(Qt::AlignHCenter);// 设置label属性
28 | //sBar->addPermanentWidget();//addSeparator();// 添加分割线,不能用
29 | // 状态栏显示计数值
30 | //lblSendNum->setText("S: 0");
31 | //lblRecvNum->setText("R: 0");
32 | setNumOnLabel(lblSendNum, "S: ", sendNum);
33 | setNumOnLabel(lblRecvNum, "R: ", recvNum);
34 | setNumOnLabel(lblSendRate, "Byte/s: ", 0);
35 | setNumOnLabel(lblRecvRate, "Byte/s: ", 0);
36 | // 从右往左依次添加
37 | sBar->addPermanentWidget(lblSendNum);
38 | sBar->addPermanentWidget(lblSendRate);
39 | sBar->addPermanentWidget(lblRecvNum);
40 | sBar->addPermanentWidget(lblRecvRate);
41 |
42 | // 状态栏添加超链接
43 | QLabel *lblLinkBlog = new QLabel(this);
44 | lblLinkBlog->setOpenExternalLinks(true);
45 | //lblLinkBlog->setText("博客");// 有下划线
46 | lblLinkBlog->setText(" 博客");// 无下划线
47 | QLabel *lblLinkSource = new QLabel(this);
48 | lblLinkSource->setOpenExternalLinks(true);
49 | //lblLinkSource->setText("源码下载");
50 | lblLinkSource->setText(" 源码下载");// 无下划线
51 | lblLinkBlog->setMinimumSize(40, 20);
52 | lblLinkSource->setMinimumSize(60, 20);
53 | // 从左往右依次添加
54 | sBar->addWidget(lblLinkBlog);
55 | sBar->addWidget(lblLinkSource);
56 |
57 | lblRecvFrameNum = new QLabel(this);
58 | lblFrameRate = new QLabel(this);
59 | lblRecvFrameNum->setMinimumSize(100, 20);
60 | lblFrameRate->setMinimumSize(80, 20);
61 | setNumOnLabel(lblRecvFrameNum, "FNum: ", recvFrameNum);
62 | setNumOnLabel(lblFrameRate, "FPS: ", recvFrameRate);
63 | // 从右往左依次添加
64 | sBar->addPermanentWidget(lblRecvFrameNum);
65 | sBar->addPermanentWidget(lblFrameRate);
66 |
67 | // 定时发送-定时器
68 | timSend = new QTimer;
69 | timSend->setInterval(1000);// 设置默认定时时长1000ms
70 | connect(timSend, &QTimer::timeout, this, [=](){
71 | on_btnSend_clicked();
72 | });
73 |
74 | // 发送速率、接收速率统计-定时器
75 | timRate = new QTimer;
76 | timRate->start(1000);
77 | connect(timRate, &QTimer::timeout, this, [=](){
78 | dataRateCalculate();
79 | });
80 |
81 | // 新建一串口对象
82 | mySerialPort = new QSerialPort(this);
83 |
84 | // 串口接收,信号槽关联
85 | connect(mySerialPort, SIGNAL(readyRead()), this, SLOT(serialPortRead_Slot()));
86 |
87 | // 隐藏高级收码显示区域
88 | ui->widget_5->hide();
89 | ui->frame_3->hide();
90 |
91 | // 新建波形显示界面
92 | plot = new Plot;
93 | }
94 |
95 | MainWindow::~MainWindow()
96 | {
97 | delete plot;
98 | delete ui;
99 | }
100 |
101 | // 绘图事件
102 | void MainWindow::paintEvent(QPaintEvent *)
103 | {
104 | // 绘图
105 | // 实例化画家对象,this指定绘图设备
106 | QPainter painter(this);
107 |
108 | // 设置画笔颜色
109 | QPen pen(QColor(0,0,0));
110 | // 设置画笔线宽(只对点线圆起作用,对文字不起作用)
111 | pen.setWidth(1);
112 | // 设置画笔线条风格,默认是SolidLine实线
113 | // DashLine虚线,DotLine点线,DashDotLine、DashDotDotLine点划线
114 | pen.setStyle(Qt::DashDotDotLine);
115 | // 让画家使用这个画笔
116 | painter.setPen(pen);
117 |
118 | //painter.drawLine(QPoint(ui->txtRec->x() + ui->txtRec->width(), ui->txtRec->y()), QPoint(this->width(), ui->txtRec->y()));
119 | //painter.drawLine(QPoint(ui->txtSend->x() + ui->txtSend->width(), ui->txtSend->y()), QPoint(this->width(), ui->txtSend->y()));
120 | painter.drawLine(QPoint(ui->statusbar->x(), ui->statusbar->y()-2), QPoint(this->width(), ui->statusbar->y()-2));
121 |
122 | }
123 |
124 | // 串口接收显示,槽函数
125 | void MainWindow::serialPortRead_Slot()
126 | {
127 | /*QString recBuf;
128 | recBuf = QString(mySerialPort->readAll());*/
129 |
130 | QByteArray recBuf;
131 | recBuf = mySerialPort->readAll();
132 |
133 | /* 帧过滤部分代码 */
134 | short wmValue[20] = {0};
135 | xFrameDataFilter(&recBuf, wmValue);
136 |
137 | // 调试信息输出,显示缓冲区内容(16进制显示)及接收标志位
138 | if(!ui->widget_5->isHidden()){
139 | QByteArray str1;
140 | //for(int i=0; i<(tnum + 1); i++)
141 | for(int i=0; itxtFrameTemp->setPlainText(str1.toHex().toUpper());
146 | str1 = str1.toHex().toUpper();
147 | QString str2;
148 | for(int i = 0; itxtFrameBuffer->setPlainText(str2);
154 | // 显示标志位
155 | ui->txtFrameTnum->setText(QString::number(tnum));
156 | ui->txtFrameH1->setText(QString::number(f_h1_flag));
157 | ui->txtFrameH->setText(QString::number(f_h_flag));
158 | ui->txtFrameFun->setText(QString::number(f_fun_word));
159 | ui->txtFrameLen->setText(QString::number(f_length));
160 | ui->txtFrameErrorNum->setText(QString::number(recvErrorNum));
161 | }
162 |
163 | // 接收字节计数
164 | recvNum += recBuf.size();
165 | // 状态栏显示计数值
166 | setNumOnLabel(lblRecvNum, "R: ", recvNum);
167 |
168 | // 判断是否为16进制接收,将以后接收的数据全部转换为16进制显示(先前接收的部分在多选框槽函数中进行转换。最好多选框和接收区组成一个自定义控件,方便以后调用)
169 | if(ui->chkRec->checkState() == false){
170 | // GB2312编码输入
171 | QString strb = QString::fromLocal8Bit(recBuf);//QString::fromUtf8(recBuf);//QString::fromLatin1(recBuf);
172 | // 在当前位置插入文本,不会发生换行。如果没有移动光标到文件结尾,会导致文件超出当前界面显示范围,界面也不会向下滚动。
173 | ui->txtRec->insertPlainText(strb);
174 | }else{
175 | // 16进制显示,并转换为大写
176 | QString str1 = recBuf.toHex().toUpper();//.data();
177 | // 添加空格
178 | QString str2;
179 | for(int i = 0; itxtRec->insertPlainText(str2);
185 | //ui->txtRec->insertPlainText(recBuf.toHex());
186 | }
187 |
188 | // 移动光标到文本结尾
189 | ui->txtRec->moveCursor(QTextCursor::End);
190 |
191 | // 将文本追加到末尾显示,会导致插入的文本换行
192 | /*ui->txtRec->appendPlainText(recBuf);*/
193 |
194 | /*// 在当前位置插入文本,不会发生换行。如果没有移动光标到文件结尾,会导致文件超出当前界面显示范围,界面也不会向下滚动。
195 | ui->txtRec->insertPlainText(recBuf);
196 | ui->txtRec->moveCursor(QTextCursor::End);*/
197 |
198 | // 利用一个QString去获取消息框文本,再将新接收到的消息添加到QString尾部,但感觉效率会比当前位置插入低。也不会发生换行
199 | /*QString txtBuf;
200 | txtBuf = ui->txtRec->toPlainText();
201 | txtBuf += recBuf;
202 | ui->txtRec->setPlainText(txtBuf);
203 | ui->txtRec->moveCursor(QTextCursor::End);*/
204 |
205 | // 利用一个QString去缓存接收到的所有消息,效率会比上面高一点。但清空接收的时候,要将QString一并清空。
206 | /*static QString txtBuf;
207 | txtBuf += recBuf;
208 | ui->txtRec->setPlainText(txtBuf);
209 | ui->txtRec->moveCursor(QTextCursor::End);*/
210 | }
211 |
212 | // 打开/关闭串口 槽函数
213 | void MainWindow::on_btnSwitch_clicked()
214 | {
215 |
216 | QSerialPort::BaudRate baudRate;
217 | QSerialPort::DataBits dataBits;
218 | QSerialPort::StopBits stopBits;
219 | QSerialPort::Parity checkBits;
220 |
221 | // 获取串口波特率
222 | baudRate = (QSerialPort::BaudRate)ui->cmbBaudRate->currentText().toUInt();
223 | // 获取串口数据位
224 | dataBits = (QSerialPort::DataBits)ui->cmbData->currentText().toUInt();
225 | // 获取串口停止位
226 | if(ui->cmbStop->currentText() == "1"){
227 | stopBits = QSerialPort::OneStop;
228 | }else if(ui->cmbStop->currentText() == "1.5"){
229 | stopBits = QSerialPort::OneAndHalfStop;
230 | }else if(ui->cmbStop->currentText() == "2"){
231 | stopBits = QSerialPort::TwoStop;
232 | }else{
233 | stopBits = QSerialPort::OneStop;
234 | }
235 |
236 | // 获取串口奇偶校验位
237 | if(ui->cmbCheck->currentText() == "无"){
238 | checkBits = QSerialPort::NoParity;
239 | }else if(ui->cmbCheck->currentText() == "奇校验"){
240 | checkBits = QSerialPort::OddParity;
241 | }else if(ui->cmbCheck->currentText() == "偶校验"){
242 | checkBits = QSerialPort::EvenParity;
243 | }else{
244 | checkBits = QSerialPort::NoParity;
245 | }
246 |
247 | // 想想用 substr strchr怎么从带有信息的字符串中提前串口号字符串
248 | // 初始化串口属性,设置 端口号、波特率、数据位、停止位、奇偶校验位数
249 | mySerialPort->setBaudRate(baudRate);
250 | mySerialPort->setDataBits(dataBits);
251 | mySerialPort->setStopBits(stopBits);
252 | mySerialPort->setParity(checkBits);
253 | //mySerialPort->setPortName(ui->cmbSerialPort->currentText());// 不匹配带有串口设备信息的文本
254 | // 匹配带有串口设备信息的文本
255 | QString spTxt = ui->cmbSerialPort->currentText();
256 | spTxt = spTxt.section(':', 0, 0);//spTxt.mid(0, spTxt.indexOf(":"));
257 | //qDebug() << spTxt;
258 | mySerialPort->setPortName(spTxt);
259 |
260 | // 根据初始化好的串口属性,打开串口
261 | // 如果打开成功,反转打开按钮显示和功能。打开失败,无变化,并且弹出错误对话框。
262 | if(ui->btnSwitch->text() == "打开串口"){
263 | if(mySerialPort->open(QIODevice::ReadWrite) == true){
264 | //QMessageBox::
265 | ui->btnSwitch->setText("关闭串口");
266 | // 让端口号下拉框不可选,避免误操作(选择功能不可用,控件背景为灰色)
267 | ui->cmbSerialPort->setEnabled(false);
268 | ui->cmbBaudRate->setEnabled(false);
269 | ui->cmbStop->setEnabled(false);
270 | ui->cmbData->setEnabled(false);
271 | ui->cmbCheck->setEnabled(false);
272 | }else{
273 | QMessageBox::critical(this, "错误提示", "串口打开失败!!!\r\n\r\n该串口可能被占用,请选择正确的串口\r\n或者波特率过高,超出硬件支持范围");
274 | }
275 | }else{
276 | mySerialPort->close();
277 | ui->btnSwitch->setText("打开串口");
278 | // 端口号下拉框恢复可选,避免误操作
279 | ui->cmbSerialPort->setEnabled(true);
280 | ui->cmbBaudRate->setEnabled(true);
281 | ui->cmbStop->setEnabled(true);
282 | ui->cmbData->setEnabled(true);
283 | ui->cmbCheck->setEnabled(true);
284 | }
285 |
286 | }
287 |
288 | // 发送按键槽函数
289 | // 如果勾选16进制发送,按照asc2的16进制发送
290 | void MainWindow::on_btnSend_clicked()
291 | {
292 | QByteArray sendData;
293 | // 判断是否为16进制发送,将发送区全部的asc2转换为16进制字符串显示,发送的时候转换为16进制发送
294 | if(ui->chkSend->checkState() == false){
295 | // 字符串形式发送,GB2312编码用以兼容大多数单片机
296 | sendData = ui->txtSend->toPlainText().toLocal8Bit();// GB2312编码输出
297 | //sendData = ui->txtSend->toPlainText().toUtf8();// Unicode编码输出
298 | //sendData = ui->txtSend->toPlainText().toLatin1();
299 | }else{
300 | // 16进制发送,不要用.data(),.data()返回的是字符数组,0x00在ASC2中的意义为NUL,也就是'\0'结束符,所以遇到0x00就会终止
301 | //sendData = QByteArray::fromHex(ui->txtSend->toPlainText().toUtf8());// Unicode编码输出
302 | sendData = QByteArray::fromHex(ui->txtSend->toPlainText().toLocal8Bit());// GB2312编码输出
303 | }
304 |
305 | // 如发送成功,会返回发送的字节长度。失败,返回-1。
306 | int a = mySerialPort->write(sendData);
307 | // 发送字节计数并显示
308 | if(a > 0)
309 | {
310 | // 发送字节计数
311 | sendNum += a;
312 | // 状态栏显示计数值
313 | setNumOnLabel(lblSendNum, "S: ", sendNum);
314 | }
315 |
316 | }
317 |
318 | // 状态栏标签显示计数值
319 | void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
320 | {
321 | // 标签显示
322 | QString strN;
323 | strN.sprintf("%ld", num);
324 | QString str = strS + strN;
325 | lbl->setText(str);
326 | }
327 |
328 | void MainWindow::on_btnClearRec_clicked()
329 | {
330 | ui->txtRec->clear();
331 | // 清除发送、接收字节计数
332 | sendNum = 0;
333 | recvNum = 0;
334 | tSend = 0;
335 | tRecv = 0;
336 | // 状态栏显示计数值
337 | setNumOnLabel(lblSendNum, "S: ", sendNum);
338 | setNumOnLabel(lblRecvNum, "R: ", recvNum);
339 | // 清空帧数量
340 | recvFrameNum=0, recvFrameRate=0, recvErrorNum=0, tFrame=0;
341 | setNumOnLabel(lblRecvFrameNum, "FNum: ", recvFrameNum);
342 | ui->txtFrameErrorNum->setText(QString::number(recvErrorNum));
343 | }
344 |
345 | void MainWindow::on_btnClearSend_clicked()
346 | {
347 | ui->txtSend->clear();
348 | // 清除发送字节计数
349 | sendNum = 0;
350 | tSend = 0;
351 | // 状态栏显示计数值
352 | setNumOnLabel(lblSendNum, "S: ", sendNum);
353 | }
354 |
355 | // 先前接收的部分在多选框状态转换槽函数中进行转换。(最好多选框和接收区组成一个自定义控件,方便以后调用)
356 | void MainWindow::on_chkRec_stateChanged(int arg1)
357 | {
358 | // 获取文本字符串
359 | QString txtBuf = ui->txtRec->toPlainText();
360 |
361 | // 获取多选框状态,未选为0,选中为2
362 | // 为0时,多选框未被勾选,接收区先前接收的16进制数据转换为asc2字符串格式
363 | if(arg1 == 0){
364 |
365 | //QString str1 = QByteArray::fromHex(txtBuf.toUtf8());
366 | //QString str1 = QByteArray::fromHex(txtBuf.toLocal8Bit());
367 | //把gb2312编码转换成unicode
368 | QString str1 = QTextCodec::codecForName("GB2312")->toUnicode(QByteArray::fromHex(txtBuf.toLocal8Bit()));
369 | // 文本控件清屏,显示新文本
370 | ui->txtRec->clear();
371 | ui->txtRec->insertPlainText(str1);
372 | // 移动光标到文本结尾
373 | ui->txtRec->moveCursor(QTextCursor::End);
374 |
375 | }else{// 不为0时,多选框被勾选,接收区先前接收asc2字符串转换为16进制显示
376 |
377 | //QString str1 = txtBuf.toUtf8().toHex().toUpper();// Unicode编码输出
378 | QString str1 = txtBuf.toLocal8Bit().toHex().toUpper();// GB2312编码输出
379 | // 添加空格
380 | QByteArray str2;
381 | for(int i = 0; itxtRec->clear();
388 | ui->txtRec->insertPlainText(str2);
389 | // 移动光标到文本结尾
390 | ui->txtRec->moveCursor(QTextCursor::End);
391 |
392 | }
393 | }
394 |
395 | // 先前发送区的部分在多选框状态转换槽函数中进行转换。(最好多选框和发送区组成一个自定义控件,方便以后调用)
396 | void MainWindow::on_chkSend_stateChanged(int arg1)
397 | {
398 | // 获取文本字符串
399 | QString txtBuf = ui->txtSend->toPlainText();
400 |
401 | // 获取多选框状态,未选为0,选中为2
402 | // 为0时,多选框未被勾选,将先前的发送区的16进制字符串转换为asc2字符串
403 | if(arg1 == 0){
404 |
405 | //QByteArray str1 = QByteArray::fromHex(txtBuf.toUtf8());//仅能处理Unicode编码,因为QString就是Unicode
406 | //QString str1 = QString::fromLocal8Bit(txtBuf.toUtf8());//仅能处理GB2312编码,Unicode的数据无论如何都会乱码
407 | //把gb2312编码转换成unicode
408 | QString str1 = QTextCodec::codecForName("GB2312")->toUnicode(QByteArray::fromHex(txtBuf.toLocal8Bit()));
409 | // 文本控件清屏,显示新文本
410 | ui->txtSend->clear();
411 | ui->txtSend->insertPlainText(str1);
412 | // 移动光标到文本结尾
413 | ui->txtSend->moveCursor(QTextCursor::End);
414 |
415 | }else{// 多选框被勾选,将先前的发送区的asc2字符串转换为16进制字符串
416 |
417 | //QByteArray str1 = txtBuf.toUtf8().toHex().toUpper();// Unicode编码输出
418 | QString str1 = txtBuf.toLocal8Bit().toHex().toUpper();// GB2312编码输出
419 | // 添加空格
420 | QString str2;
421 | for(int i = 0; itxtSend->clear();
428 | ui->txtSend->insertPlainText(str2);
429 | // 移动光标到文本结尾
430 | ui->txtSend->moveCursor(QTextCursor::End);
431 |
432 | }
433 | }
434 |
435 | // 定时发送开关 选择复选框
436 | void MainWindow::on_chkTimSend_stateChanged(int arg1)
437 | {
438 | // 获取复选框状态,未选为0,选中为2
439 | if(arg1 == 0){
440 | timSend->stop();
441 | // 时间输入框恢复可选
442 | ui->txtSendMs->setEnabled(true);
443 | }else{
444 | // 对输入的值做限幅,小于20ms会弹出对话框提示
445 | if(ui->txtSendMs->text().toInt() >= 20){
446 | timSend->start(ui->txtSendMs->text().toInt());// 设置定时时长,重新计数
447 | // 让时间输入框不可选,避免误操作(输入功能不可用,控件背景为灰色)
448 | ui->txtSendMs->setEnabled(false);
449 | }else{
450 | ui->chkTimSend->setCheckState(Qt::Unchecked);
451 | QMessageBox::critical(this, "错误提示", "定时发送的最小间隔为 20ms\r\n请确保输入的值 >=20");
452 | }
453 | }
454 | }
455 |
456 | // 发送速率、接收速率统计-定时器
457 | void MainWindow::dataRateCalculate(void)
458 | {
459 | sendRate = sendNum - tSend; // * ui->cmbData->currentText().toUInt();
460 | recvRate = recvNum - tRecv; // * ui->cmbData->currentText().toUInt();
461 | recvFrameRate = recvFrameNum - tFrame;
462 |
463 | setNumOnLabel(lblSendRate, "Byte/s: ", sendRate);
464 | setNumOnLabel(lblRecvRate, "Byte/s: ", recvRate);
465 | setNumOnLabel(lblFrameRate, "FPS: ", recvFrameRate);
466 | tSend = sendNum;
467 | tRecv = recvNum;
468 | tFrame = recvFrameNum;
469 | }
470 |
471 | // "显示波形界面" 按键槽函数
472 | // 记得把plot在析构中释放掉,不然很容易下次运行崩溃
473 | void MainWindow::on_pushButton_clicked()
474 | {
475 | plot->show();// 显示波形绘图窗口
476 | }
477 |
478 | /*
479 | void MainWindow::xFrameDataFilter(QByteArray *str)
480 | {
481 | int num = str->size();
482 | if(num)
483 | {
484 |
485 | }
486 | }*/
487 |
488 | // 帧过滤
489 | // 适用于有帧头、功能字、有效字段长度、校验位的接收,无帧尾
490 | void MainWindow::xFrameDataFilter(QByteArray *str, short value[])
491 | {
492 | int num = str->size();
493 | if(num)
494 | {
495 | for(int i=0; iat(i); // 从接收缓存区读取一个字节
498 | if (f_h_flag == 1) // 有帧头。判断功能字、有效字段长度,接收消息
499 | {
500 | if (f_fun_word) // 有帧头,有功能字
501 | {
502 | if (f_length) // 有帧头,有功能字,有有效字节长度
503 | {
504 | if((tnum-4) widget_5->isHidden()){
525 | QByteArray str1;
526 | for(int i=0; i<(tnum+1); i++)
527 | {
528 | str1.append(chrtmp[i]);
529 | }
530 | //ui->txtFrameEffective->appendPlainText(str1.toHex().toUpper());
531 | str1 = str1.toHex().toUpper();
532 | QString str2;
533 | for(int i = 0; itxtFrameEffective->appendPlainText(str2);
539 | }
540 |
541 | // 根据功能字进行功能解析,自动根据帧长度解析为对应的short值。
542 | if(f_fun_word == FunWord_WF)
543 | {
544 | for(int i=0; i<(f_length/2); i++)
545 | {
546 | value[i] = ((short)chrtmp[i*2+4] << 8) | chrtmp[i*2+4+1];
547 | }
548 | }
549 |
550 | // 显示波形(在这里显示可以处理多帧粘包,避免多帧粘包只显示一帧的情况)
551 | // 将解析出的short数组,传入波形图,进行绘图
552 | if(!plot->isHidden()){
553 | plot->ShowPlot_WaveForm(plot->pPlot1, value);
554 | }
555 |
556 | }else{
557 | ++recvErrorNum;// 误码帧数量计数
558 | }
559 |
560 | // 清0重新接收
561 | tnum = 0;
562 | // 清空标志位
563 | f_h1_flag = 0;
564 | f_h_flag = 0;
565 | f_fun_word = 0;
566 | f_length = 0;
567 |
568 | }
569 | // 把上面下面的判断标志位 == 1去掉
570 |
571 |
572 | } // 有帧头,有功能字,判断是否是有效字节长度
573 | else
574 | {
575 | if(chrtmp[tnum] <= ValidByteLength)
576 | {
577 | f_length = chrtmp[tnum];// 记录当前帧的有效字节长度
578 | tnum ++;
579 | }
580 | else
581 | {
582 | // 清0重新接收
583 | tnum = 0;
584 | // 清空标志位
585 | f_h1_flag = 0;
586 | f_h_flag = 0;
587 | f_fun_word = 0;
588 | }
589 | }
590 | }
591 | else // 有帧头,无功能字,判断是否为有效功能字
592 | {
593 | if ((chrtmp[tnum] == FunWord_WF) || chrtmp[tnum] == FunWord_SM)
594 | {
595 | f_fun_word = chrtmp[tnum];//记录功能字
596 | tnum ++;
597 | }
598 | else
599 | {
600 | // 清0重新接收
601 | tnum = 0;
602 | // 清空标志位
603 | f_h1_flag = 0;
604 | f_h_flag = 0;
605 | }
606 | }
607 | }
608 | else // 没有接收到帧头
609 | {
610 | if (f_h1_flag == 1) //没有帧头,有帧头1。下一步判断是否为第2个字节
611 | {
612 | if (chrtmp[tnum] == Frame_Header2) // 如果为帧头的第2个字节,接收到帧头标志位标志位置1,tnum自增
613 | {
614 | f_h_flag = 1;
615 | tnum ++;
616 | }
617 | else
618 | {
619 | // 这里再添加一个判断,出现 3A 3A 3B xx的情况,如果没有这个判断会重新计数,导致丢帧
620 | if(chrtmp[tnum] == Frame_Header1){
621 | f_h1_flag = 1;
622 | tnum = 1;
623 | }else{
624 | // 重新计数,但如果出现 3A 3A 3B xx的情况,会导致丢帧,要加上上面的判断
625 | f_h1_flag = 0;
626 | tnum = 0;
627 | }
628 | }
629 | }
630 | else //没有帧头,没有有帧头1。下一步判断,是否为帧头的第1个字节
631 | {
632 | if (chrtmp[tnum] == Frame_Header1) // 如果为帧头的第1个字节,标志位置1,tnum自增
633 | {
634 | f_h1_flag = 1;
635 | tnum ++;
636 | }
637 | else // 否则,标志位清0,tnum清0
638 | {
639 | tnum = 0;
640 | }
641 | }
642 | }
643 |
644 | static int xx=0;
645 | // 判断多长的数据没有换行符,如果超过2000,会人为向数据接收区添加换行,来保证CPU占用率不会过高,不会导致卡顿
646 | // 但由于是先插入换行,后插入接收到的数据,所以每一箩数据并不是2000
647 | if(chrtmp[tnum] != 0x0A){
648 | ++xx;
649 | if(xx > 2000){
650 | ui->txtRec->appendPlainText("");
651 | ui->txtRec->appendPlainText("");
652 | xx=0;
653 | }
654 | }else{
655 | xx=0;
656 | }
657 |
658 | // 大于MaxFrameLength个字节的帧不接收
659 | if (tnum > (MaxFrameLength - 1) )
660 | {
661 | tnum = 0;
662 | f_h1_flag = 0;
663 | f_h_flag = 0;
664 | f_t1_flag = 0;
665 | //f_fun_word = 0;
666 | //f_length = 0;
667 | continue;
668 | }
669 | }
670 | }
671 | }
672 |
673 | /*
674 | // 适用于有帧头帧尾、无功能字和有效字段长度的接收
675 | void MainWindow::xFrameDataFilter(QByteArray *str)
676 | {
677 | int num = str->size();
678 | if(num)
679 | {
680 | for(int i=0; iat(i); // 从接收缓存区读取一个字节
683 | if (f_h_flag == 1) // 有帧头,判断帧尾,接收消息
684 | {
685 | if (f_t1_flag == 1) //有帧头,有帧尾1
686 | {
687 | if (chrtmp[tnum] == Frame_Tail2)
688 | {
689 | tnum ++;
690 |
691 | // 接收到一帧有效帧
692 | // 用户处理代码 //
693 | // 根据接收到的数量,合成帧字段
694 | // 调试信息输出,显示有效帧的内容(16进制显示)
695 | if(!ui->widget_5->isHidden()){
696 | QByteArray str1;
697 | for(int i=0; itxtFrameEffective->appendPlainText(str1.toHex().toUpper());
702 | str1 = str1.toHex().toUpper();
703 | QString str2;
704 | for(int i = 0; itxtFrameEffective->appendPlainText(str2);
710 | }
711 |
712 | // 处理完用户代码,重新接收计数 //
713 | tnum = 0;
714 | // 清空标志位,之前一直忘了
715 | f_h1_flag = 0;
716 | f_h_flag = 0;
717 | f_t1_flag = 0;
718 |
719 | // 将接收到符合帧定义的帧,原路发送回去 //
720 | //return str1;
721 | //ui->lineEdit->setText(str1.toHex().toUpper());
722 |
723 | }
724 | else
725 | {
726 | f_t1_flag = 0;
727 | tnum ++;
728 | }
729 | }
730 | else // 有帧头,无帧尾1
731 | {
732 | if (chrtmp[tnum] == Frame_Tail1)
733 | {
734 | f_t1_flag = 1;
735 | tnum ++;
736 | }
737 | else // 接收消息包中间内容
738 | {
739 | tnum ++;
740 | }
741 | }
742 | }
743 | else // 没有接收到帧头
744 | {
745 | if (f_h1_flag == 1) //没有帧头,有帧头1。下一步判断是否为第2个字节
746 | {
747 | if (chrtmp[tnum] == Frame_Header2) // 如果为帧头的第2个字节,接收到帧头标志位标志位置1,tnum自增
748 | {
749 | f_h_flag = 1;
750 | tnum ++;
751 | }
752 | else
753 | {
754 | // 这里再添加一个判断,出现 3A 3A 3B xx的情况,如果没有这个判断会重新计数,导致丢帧
755 | if(chrtmp[tnum] == Frame_Header1){
756 | f_h1_flag = 1;
757 | tnum = 1;
758 | }else{
759 | // 重新计数,但如果出现 3A 3A 3B xx的情况,会导致丢帧,要加上上面的判断
760 | f_h1_flag = 0;
761 | tnum = 0;
762 | }
763 | }
764 | }
765 | else //没有帧头,没有有帧头1。下一步判断,是否为帧头的第1个字节
766 | {
767 | if (chrtmp[tnum] == Frame_Header1) // 如果为帧头的第1个字节,标志位置1,tnum自增
768 | {
769 | f_h1_flag = 1;
770 | tnum ++;
771 | }
772 | else // 否则,标志位清0,tnum清0
773 | {
774 | tnum = 0;
775 | }
776 | }
777 | }
778 |
779 | // 大于MaxFrameLength个字节的帧不接收
780 | if (tnum > (MaxFrameLength - 1) )
781 | {
782 | tnum = 0;
783 | f_h1_flag = 0;
784 | f_h_flag = 0;
785 | f_t1_flag = 0;
786 | continue;
787 | }
788 | }
789 | }
790 | }*/
791 |
792 | void MainWindow::on_btnFramaDebug_clicked()
793 | {
794 | if(ui->widget_5->isHidden()){
795 | ui->widget_5->show();
796 | ui->frame_3->show();
797 | }else{
798 | ui->widget_5->hide();
799 | ui->frame_3->hide();
800 | }
801 | }
802 |
--------------------------------------------------------------------------------
/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include "plot.h"
12 |
13 | // 接收缓冲区大小,单位字节
14 | #define BufferSize 50
15 | // 最大帧长度,单位字节
16 | #define MaxFrameLength 40+5 // 对最大帧长度加以限定,防止接收到过长的帧数据
17 | // 完整的帧头,2个字节
18 | #define Frame_Header1 0x3A // 串口接收消息包的帧头的第1个字节
19 | #define Frame_Header2 0x3B // 串口接收消息包的帧头的第2个字节
20 | // 完整的帧尾,2个字节
21 | #define Frame_Tail1 0x7E // 串口接收消息包的帧尾的第1个字节
22 | #define Frame_Tail2 0x7F // 串口接收消息包的帧尾的第2个字节
23 |
24 | // 功能字1,0x01,自定义波形显示
25 | #define FunWord_WF 0x01
26 | // 功能字2,0x02,信息绘图页面显示,暂时未用
27 | #define FunWord_SM 0x02
28 | // 帧数据中包含有效字节的最大长度
29 | #define ValidByteLength 40 // 对最大帧长度加以限定,防止接收到过长的帧数据
30 |
31 | QT_BEGIN_NAMESPACE
32 | namespace Ui { class MainWindow; }
33 | QT_END_NAMESPACE
34 |
35 | class MainWindow : public QMainWindow
36 | {
37 | Q_OBJECT
38 |
39 | public:
40 | MainWindow(QWidget *parent = nullptr);
41 | ~MainWindow();
42 |
43 | // 绘图事件
44 | void paintEvent(QPaintEvent *);
45 |
46 | private slots:
47 | void on_btnSwitch_clicked();
48 |
49 | void serialPortRead_Slot();
50 |
51 | void on_btnSend_clicked();
52 |
53 | void on_btnClearRec_clicked();
54 |
55 | void on_btnClearSend_clicked();
56 |
57 | void on_chkRec_stateChanged(int arg1);
58 |
59 | void on_chkSend_stateChanged(int arg1);
60 |
61 | void on_chkTimSend_stateChanged(int arg1);
62 |
63 | void on_pushButton_clicked();
64 |
65 | void on_btnFramaDebug_clicked();
66 |
67 | void dataRateCalculate(void);
68 |
69 | private:
70 | Ui::MainWindow *ui;
71 | // 波形绘图窗口
72 | Plot *plot = NULL;// 必须初始化为空,否则后面NEW判断的时候会异常结束
73 |
74 | QSerialPort *mySerialPort;
75 |
76 | // 发送、接收字节计数
77 | long sendNum=0, recvNum=0, tSend=0, tRecv=0;// 发送/接收数量,历史发送/接收数量,Byte
78 | long sendRate=0, recvRate=0;// 发送/接收速率,Byte/s
79 | long recvFrameNum=0, recvFrameRate=0, recvErrorNum=0, tFrame=0;// 接收的有效帧数,帧速率,误码帧数量,历史帧数量
80 | QLabel *lblSendNum, *lblRecvNum, *lblSendRate, *lblRecvRate, *lblRecvFrameNum, *lblFrameRate;
81 |
82 | void setNumOnLabel(QLabel *lbl, QString strS, long num);
83 |
84 | /* 与帧过滤有关的标志位 */
85 | //int snum = 0; // 系统串口接收缓存区的可用字节数
86 | int tnum = 0; // 用户串口接收缓存的指针位置
87 | unsigned char chrtmp[BufferSize]; // 用户串口接收缓存,将缓存的数据放入这里处理
88 | int f_h1_flag = 0; // 接收到帧头的第一个字节标志位
89 | int f_h_flag = 0; // 接收到帧头标志位
90 | int f_t1_flag = 0; // 接收到帧尾的第一个字节标志位
91 | // 即是标志位,也包含信息
92 | int f_fun_word = 0; // 功能字,限定为0x01、0x02
93 | int f_length = 0; // 帧数据中包含有效字节的长度
94 | void xFrameDataFilter(QByteArray *str, short value[]);
95 | //QByteArray xFrameDataFilter(QByteArray *str);
96 |
97 | // 定时发送-定时器
98 | QTimer *timSend;
99 | // 发送速率、接收速率统计-定时器
100 | QTimer *timRate;
101 | };
102 | #endif // MAINWINDOW_H
103 |
--------------------------------------------------------------------------------
/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 800
10 | 720
11 |
12 |
13 |
14 |
15 | 240
16 | 0
17 |
18 |
19 |
20 |
21 | 16777215
22 | 16777215
23 |
24 |
25 |
26 | MainWindow
27 |
28 |
29 |
30 | -
31 |
32 |
33 |
34 | 240
35 | 0
36 |
37 |
38 |
39 |
40 | 240
41 | 16777215
42 |
43 |
44 |
45 | Qt::NoFocus
46 |
47 |
48 |
49 |
50 |
51 | QFrame::Box
52 |
53 |
54 | QFrame::Sunken
55 |
56 |
57 |
-
58 |
59 |
60 | Qt::Vertical
61 |
62 |
63 |
64 | 20
65 | 40
66 |
67 |
68 |
69 |
70 | -
71 |
72 |
73 | 显示波形界面
74 |
75 |
76 |
77 | -
78 |
79 |
80 | 清除接收/统计
81 |
82 |
83 |
84 | -
85 |
86 |
87 | 16进制接收
88 |
89 |
90 |
91 | -
92 |
93 |
94 | 帧捕获调试
95 |
96 |
97 |
98 | -
99 |
100 |
101 | 16进制发送
102 |
103 |
104 |
105 | -
106 |
107 |
108 |
109 | 0
110 | 219
111 |
112 |
113 |
114 |
115 | 16777215
116 | 219
117 |
118 |
119 |
120 | QFrame::Box
121 |
122 |
123 | QFrame::Sunken
124 |
125 |
126 |
127 | 5
128 |
129 |
130 | 5
131 |
132 |
-
133 |
134 |
135 | 串口选择
136 |
137 |
138 |
139 | -
140 |
141 |
142 | -
143 |
144 |
145 |
-
146 |
147 |
148 | 波特率
149 |
150 |
151 |
152 | -
153 |
154 |
155 | true
156 |
157 |
158 | 10
159 |
160 |
161 | 20
162 |
163 |
-
164 |
165 | 1200
166 |
167 |
168 | -
169 |
170 | 2400
171 |
172 |
173 | -
174 |
175 | 4800
176 |
177 |
178 | -
179 |
180 | 9600
181 |
182 |
183 | -
184 |
185 | 14400
186 |
187 |
188 | -
189 |
190 | 19200
191 |
192 |
193 | -
194 |
195 | 38400
196 |
197 |
198 | -
199 |
200 | 43000
201 |
202 |
203 | -
204 |
205 | 57600
206 |
207 |
208 | -
209 |
210 | 76800
211 |
212 |
213 | -
214 |
215 | 115200
216 |
217 |
218 | -
219 |
220 | 128000
221 |
222 |
223 | -
224 |
225 | 230400
226 |
227 |
228 | -
229 |
230 | 256000
231 |
232 |
233 | -
234 |
235 | 460800
236 |
237 |
238 | -
239 |
240 | 921600
241 |
242 |
243 | -
244 |
245 | 1382400
246 |
247 |
248 |
249 |
250 | -
251 |
252 |
253 | 停止位
254 |
255 |
256 |
257 | -
258 |
259 |
-
260 |
261 | 1
262 |
263 |
264 | -
265 |
266 | 1.5
267 |
268 |
269 | -
270 |
271 | 2
272 |
273 |
274 |
275 |
276 | -
277 |
278 |
279 | 数据位
280 |
281 |
282 |
283 | -
284 |
285 |
286 | 3
287 |
288 |
-
289 |
290 | 5
291 |
292 |
293 | -
294 |
295 | 6
296 |
297 |
298 | -
299 |
300 | 7
301 |
302 |
303 | -
304 |
305 | 8
306 |
307 |
308 |
309 |
310 | -
311 |
312 |
313 | 奇偶校验
314 |
315 |
316 |
317 | -
318 |
319 |
-
320 |
321 | 无
322 |
323 |
324 | -
325 |
326 | 奇校验
327 |
328 |
329 | -
330 |
331 | 偶校验
332 |
333 |
334 |
335 |
336 | -
337 |
338 |
339 | 串口操作
340 |
341 |
342 |
343 | -
344 |
345 |
346 | 打开串口
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 | -
357 |
358 |
359 | 清除发送
360 |
361 |
362 |
363 | -
364 |
365 |
366 | Qt::Vertical
367 |
368 |
369 |
370 | 20
371 | 40
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 | -
380 |
381 |
382 |
383 | 0
384 | 100
385 |
386 |
387 |
388 |
389 | 16777215
390 | 100
391 |
392 |
393 |
394 | 串口数据 - 发送区
395 |
396 |
397 |
398 | 0
399 |
400 |
401 | 0
402 |
403 |
-
404 |
405 |
406 |
407 | 0
408 | 0
409 |
410 |
411 |
412 |
413 | 16777215
414 | 16777215
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 | -
423 |
424 |
425 | true
426 |
427 |
428 |
429 | 523
430 | 151
431 |
432 |
433 |
434 |
435 | 16777215
436 | 171
437 |
438 |
439 |
440 |
441 |
442 | 0
443 | 0
444 | 531
445 | 61
446 |
447 |
448 |
449 | 帧过滤 缓冲区内容
450 |
451 |
452 |
453 |
454 | 0
455 | 20
456 | 531
457 | 41
458 |
459 |
460 |
461 |
462 | 439
463 | 0
464 |
465 |
466 |
467 |
468 | 16777215
469 | 41
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 | 0
478 | 70
479 | 531
480 | 91
481 |
482 |
483 |
484 | 有效帧内容
485 |
486 |
487 |
488 |
489 | 0
490 | 20
491 | 531
492 | 71
493 |
494 |
495 |
496 |
497 |
498 |
499 | -
500 |
501 |
502 |
503 | 240
504 | 0
505 |
506 |
507 |
508 |
509 | 240
510 | 16777215
511 |
512 |
513 |
514 | QFrame::Box
515 |
516 |
517 | QFrame::Sunken
518 |
519 |
520 |
-
521 |
522 |
523 | -
524 |
525 |
526 | 缓冲区指针位置
527 |
528 |
529 |
530 | -
531 |
532 |
533 | 帧头首字节标志位
534 |
535 |
536 |
537 | -
538 |
539 |
540 | -
541 |
542 |
543 | 帧有效字节数
544 |
545 |
546 |
547 | -
548 |
549 |
550 | 帧头次字节标志位
551 |
552 |
553 |
554 | -
555 |
556 |
557 | -
558 |
559 |
560 | 帧功能字
561 |
562 |
563 |
564 | -
565 |
566 |
567 | -
568 |
569 |
570 | -
571 |
572 |
573 | -
574 |
575 |
576 | 误码帧数量
577 |
578 |
579 |
580 |
581 |
582 |
583 | -
584 |
585 |
586 |
587 | 240
588 | 100
589 |
590 |
591 |
592 |
593 | 240
594 | 100
595 |
596 |
597 |
598 | QFrame::Box
599 |
600 |
601 | QFrame::Sunken
602 |
603 |
604 |
-
605 |
606 |
607 |
608 | 0
609 | 60
610 |
611 |
612 |
613 |
614 | 125
615 | 16777215
616 |
617 |
618 |
619 | 发送
620 |
621 |
622 |
623 | -
624 |
625 |
626 |
627 | 70
628 | 16777215
629 |
630 |
631 |
632 |
633 | 0
634 |
635 |
636 | 0
637 |
638 |
-
639 |
640 |
641 |
642 | 60
643 | 16777215
644 |
645 |
646 |
647 | 1000
648 |
649 |
650 |
651 | -
652 |
653 |
654 |
655 | 20
656 | 16777215
657 |
658 |
659 |
660 |
661 | 12
662 |
663 |
664 |
665 | ms
666 |
667 |
668 |
669 |
670 |
671 |
672 | -
673 |
674 |
675 |
676 | 100
677 | 16777215
678 |
679 |
680 |
681 | 定时发送
682 |
683 |
684 |
685 |
686 |
687 |
688 | -
689 |
690 |
691 | 串口数据 - 接收区
692 |
693 |
694 |
695 | 0
696 |
697 |
698 | 0
699 |
700 |
-
701 |
702 |
703 | true
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
722 |
723 |
724 |
725 | 0
726 | 22
727 |
728 |
729 |
730 |
731 | 16777215
732 | 22
733 |
734 |
735 |
736 |
737 |
738 |
739 | myComboBox
740 | QComboBox
741 |
742 |
743 |
744 |
745 |
746 |
747 |
--------------------------------------------------------------------------------
/mycombobox.cpp:
--------------------------------------------------------------------------------
1 | #include "mycombobox.h"
2 |
3 | myComboBox::myComboBox(QWidget *parent) : QComboBox(parent)
4 | {
5 | // 扫描可用串口
6 | scanActivePort();
7 | }
8 |
9 | // 扫描可用串口
10 | void myComboBox::scanActivePort()
11 | {
12 | // 先清空列表项,防止多次刷新后重叠
13 | clear();
14 |
15 | // 串口端口号列表
16 | QStringList serialPortName;
17 |
18 | // 自动扫描当前可用串口,返回值追加到字符数组中
19 | foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
20 |
21 | //serialPortName << info.portName();// 不携带有串口设备信息的文本
22 |
23 | // 携带有串口设备信息的文本
24 | QString serialPortInfo = info.portName() + ": " + info.description();// 串口设备信息,芯片/驱动名称
25 | //QString serialPortInfo = info.portName() + ": " + info.manufacturer();// 串口设备制造商
26 | //QString serialPortInfo = info.portName() + ": " + info.serialNumber();// 串口设备的序列号,没什么用
27 | //QString serialPortInfo = info.portName() + ": " + info.systemLocation();// 串口设备的系统位置,没什么用
28 | serialPortName << serialPortInfo;
29 | }
30 | // 可用串口号,显示到串口选择下拉框中
31 | this->addItems(serialPortName);
32 | }
33 |
34 | // 重写鼠标点击事件
35 | void myComboBox::mousePressEvent(QMouseEvent *event)
36 | {
37 | if(event->button() == Qt::LeftButton)
38 | {
39 | // 扫描可用串口
40 | scanActivePort();
41 |
42 | // 弹出下拉框
43 | showPopup();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/mycombobox.h:
--------------------------------------------------------------------------------
1 | #ifndef MYCOMBOBOX_H
2 | #define MYCOMBOBOX_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class myComboBox : public QComboBox
10 | {
11 | Q_OBJECT
12 | public:
13 | explicit myComboBox(QWidget *parent = nullptr);
14 |
15 | // 重写鼠标点击事件
16 | void mousePressEvent(QMouseEvent *event);
17 |
18 | signals:
19 |
20 | private:
21 | void scanActivePort();
22 |
23 | };
24 |
25 | #endif // MYCOMBOBOX_H
26 |
--------------------------------------------------------------------------------
/pic.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/pic/ssCircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssCircle.png
--------------------------------------------------------------------------------
/pic/ssCross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssCross.png
--------------------------------------------------------------------------------
/pic/ssCrossCircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssCrossCircle.png
--------------------------------------------------------------------------------
/pic/ssCrossSquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssCrossSquare.png
--------------------------------------------------------------------------------
/pic/ssDiamond.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssDiamond.png
--------------------------------------------------------------------------------
/pic/ssDisc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssDisc.png
--------------------------------------------------------------------------------
/pic/ssNone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssNone.png
--------------------------------------------------------------------------------
/pic/ssPeace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssPeace.png
--------------------------------------------------------------------------------
/pic/ssPlus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssPlus.png
--------------------------------------------------------------------------------
/pic/ssPlusCircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssPlusCircle.png
--------------------------------------------------------------------------------
/pic/ssPlusSquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssPlusSquare.png
--------------------------------------------------------------------------------
/pic/ssSquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssSquare.png
--------------------------------------------------------------------------------
/pic/ssStar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssStar.png
--------------------------------------------------------------------------------
/pic/ssTriangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssTriangle.png
--------------------------------------------------------------------------------
/pic/ssTriangleInverted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhiliangMa/Qt_SerialPlot/a3b4af3d2159b62c275c3d4b55dc4452f41ef1a0/pic/ssTriangleInverted.png
--------------------------------------------------------------------------------
/plot.cpp:
--------------------------------------------------------------------------------
1 | #include "plot.h"
2 | #include "ui_plot.h"
3 | #include
4 |
5 | Plot::Plot(QWidget *parent) :
6 | QMainWindow(parent),
7 | ui(new Ui::Plot)
8 | {
9 | ui->setupUi(this);
10 | setWindowTitle("Plot");
11 |
12 | // 给widget绘图控件,设置个别名,方便书写
13 | pPlot1 = ui->winPlot;
14 | // 状态栏指针
15 | sBar = statusBar();
16 |
17 | // 初始化图表1
18 | QPlot_init(pPlot1);
19 | // 绘图图表的设置控件初始化,主要用于关联控件的信号槽
20 | QPlot_widget_init();
21 |
22 | // 创建定时器,用于定时生成曲线坐标点数据
23 | timer = new QTimer(this);
24 | timer->setInterval(10);
25 | connect(timer,SIGNAL(timeout()),this,SLOT(TimeData_Update()));
26 | //timer->start(10);
27 |
28 | // 关联控件初始化
29 | ui->txtPointOriginX->setEnabled(false);
30 | // 图表重绘后,刷新原点坐标和范围
31 | connect(pPlot1,SIGNAL(afterReplot()),this,SLOT(repPlotCoordinate()));
32 | }
33 |
34 | Plot::~Plot()
35 | {
36 | delete ui;
37 | }
38 |
39 |
40 | // 绘图图表初始化
41 | void Plot::QPlot_init(QCustomPlot *customPlot)
42 | {
43 | // 添加曲线名称
44 | QStringList lineNames;//设置图例的文本
45 | lineNames << "波形1" << "波形2" << "波形3" << "波形4" << "波形5" << "波形6" << "波形7" << "波形8" << "波形9" << "波形10"\
46 | << "波形11" << "波形12" << "波形13" << "波形14" << "波形15" << "波形16" << "波形17" << "波形18" << "波形19" << "波形20";
47 | // 曲线初始颜色
48 | QColor initColor[20] = {QColor(0,146,152), QColor(162,0,124), QColor(241,175,0), QColor(27,79,147), QColor(229,70,70),\
49 | QColor(0,140,94), QColor(178,0,31), QColor(91,189,43), QColor(0,219,219), QColor(172,172,172),\
50 | QColor(0,178,191), QColor(197,124,172), QColor(243,194,70), QColor(115,136,193), QColor(245,168,154),\
51 | QColor(152,208,185), QColor(223,70,41), QColor(175,215,136), QColor(157,255,255), QColor(0,0,0)};//QColor(255,255,255)};//白色
52 | // 图表添加20条曲线,并设置初始颜色,和图例名称
53 | for(int i=0; i<20; i++){
54 | pCurve[i] = customPlot->addGraph();
55 | pCurve[i]->setPen(QPen(QColor(initColor[i])));
56 | pCurve[i]->setName(lineNames.at(i));
57 | }
58 |
59 | // 设置背景颜色
60 | customPlot->setBackground(QColor(255,255,255));
61 | // 设置背景选择框颜色
62 | ui->btnColourBack->setStyleSheet(QString("border:0px solid;background-color: %1;").arg(QColor(255,255,255).name()));
63 |
64 | // 曲线选择框颜色,与曲线同步颜色。这样写太复杂了,用控件指针数组在下面写过了,记得要在addGraph()之后才有效。
65 | //ui->btnColourCurve1->setStyleSheet("border:0px solid;background-color:rgb(0,146,152)");
66 | //ui->btnColourCurve1->setStyleSheet(QString("border:0px solid;background-color: %1;").arg(initColor[0].name()));
67 | //ui->btnColourCurve20->setStyleSheet(QString("border:0px solid;background-color: %1;").arg(pCurve[]->pen().color().name()));
68 |
69 | // 设置坐标轴名称
70 | customPlot->xAxis->setLabel("X");
71 | customPlot->yAxis->setLabel("Y");
72 |
73 | // 设置x,y坐标轴显示范围
74 | pointCountX = ui->txtPointCountX->text().toUInt();
75 | pointCountY = ui->txtPointCountY->text().toUInt();
76 | customPlot->xAxis->setRange(0,pointCountX);
77 | customPlot->yAxis->setRange(pointCountY/2*-1,pointCountY/2);
78 |
79 | //customPlot->axisRect()->setupFullAxesBox();//四边安装轴并显示
80 | //customPlot->xAxis->ticker()->setTickOrigin(1);//改变刻度原点为1
81 | //customPlot->xAxis->setNumberFormat("gbc");//g灵活的格式,b漂亮的指数形式,c乘号改成×
82 | //customPlot->xAxis->setNumberPrecision(1);//精度1
83 | customPlot->xAxis->ticker()->setTickCount(ui->txtMainScaleNumX->text().toUInt());//11个主刻度
84 | customPlot->yAxis->ticker()->setTickCount(ui->txtMainScaleNumY->text().toUInt());//11个主刻度
85 | customPlot->xAxis->ticker()->setTickStepStrategy(QCPAxisTicker::tssReadability);//可读性优于设置
86 | customPlot->yAxis->ticker()->setTickStepStrategy(QCPAxisTicker::tssReadability);//可读性优于设置
87 |
88 | // 显示图表的图例
89 | customPlot->legend->setVisible(true);
90 |
91 | // 设置波形曲线的复选框字体颜色
92 | //ui->chkVisibleCurve1->setStyleSheet("QCheckBox{color:rgb(255,0,0)}");//设定前景颜色,就是字体颜色
93 |
94 | // 允许用户用鼠标拖动轴范围,以鼠标为中心滚轮缩放,点击选择图形:
95 | customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
96 | // 设置鼠标滚轮的缩放倍率,如果不设置默认为0.85,大于1反方向缩放
97 | //customPlot->axisRect()->setRangeZoomFactor(0.5);
98 | // 设置鼠标滚轮缩放的轴方向,仅设置垂直轴。垂直轴和水平轴全选使用:Qt::Vertical | Qt::Horizontal
99 | customPlot->axisRect()->setRangeZoom(Qt::Vertical);
100 | }
101 |
102 | // 绘图图表的设置控件初始化,主要用于关联控件的信号槽
103 | void Plot::QPlot_widget_init(void)
104 | {
105 | // 获取控件指针数组,方便设置时编码书写
106 | pChkVisibleCurve[0] = ui->chkVisibleCurve1; pBtnColourCurve[0] = ui->btnColourCurve1; pTxtValueCurve[0] = ui->txtValueCurve1; pRdoBoldCurve[0] = ui->rdoBoldCurve1;
107 | pChkVisibleCurve[1] = ui->chkVisibleCurve2; pBtnColourCurve[1] = ui->btnColourCurve2; pTxtValueCurve[1] = ui->txtValueCurve2; pRdoBoldCurve[1] = ui->rdoBoldCurve2;
108 | pChkVisibleCurve[2] = ui->chkVisibleCurve3; pBtnColourCurve[2] = ui->btnColourCurve3; pTxtValueCurve[2] = ui->txtValueCurve3; pRdoBoldCurve[2] = ui->rdoBoldCurve3;
109 | pChkVisibleCurve[3] = ui->chkVisibleCurve4; pBtnColourCurve[3] = ui->btnColourCurve4; pTxtValueCurve[3] = ui->txtValueCurve4; pRdoBoldCurve[3] = ui->rdoBoldCurve4;
110 | pChkVisibleCurve[4] = ui->chkVisibleCurve5; pBtnColourCurve[4] = ui->btnColourCurve5; pTxtValueCurve[4] = ui->txtValueCurve5; pRdoBoldCurve[4] = ui->rdoBoldCurve5;
111 | pChkVisibleCurve[5] = ui->chkVisibleCurve6; pBtnColourCurve[5] = ui->btnColourCurve6; pTxtValueCurve[5] = ui->txtValueCurve6; pRdoBoldCurve[5] = ui->rdoBoldCurve6;
112 | pChkVisibleCurve[6] = ui->chkVisibleCurve7; pBtnColourCurve[6] = ui->btnColourCurve7; pTxtValueCurve[6] = ui->txtValueCurve7; pRdoBoldCurve[6] = ui->rdoBoldCurve7;
113 | pChkVisibleCurve[7] = ui->chkVisibleCurve8; pBtnColourCurve[7] = ui->btnColourCurve8; pTxtValueCurve[7] = ui->txtValueCurve8; pRdoBoldCurve[7] = ui->rdoBoldCurve8;
114 | pChkVisibleCurve[8] = ui->chkVisibleCurve9; pBtnColourCurve[8] = ui->btnColourCurve9; pTxtValueCurve[8] = ui->txtValueCurve9; pRdoBoldCurve[8] = ui->rdoBoldCurve9;
115 | pChkVisibleCurve[9] = ui->chkVisibleCurve10; pBtnColourCurve[9] = ui->btnColourCurve10; pTxtValueCurve[9] = ui->txtValueCurve10; pRdoBoldCurve[9] = ui->rdoBoldCurve10;
116 | pChkVisibleCurve[10] = ui->chkVisibleCurve11; pBtnColourCurve[10] = ui->btnColourCurve11; pTxtValueCurve[10] = ui->txtValueCurve11; pRdoBoldCurve[10] = ui->rdoBoldCurve11;
117 | pChkVisibleCurve[11] = ui->chkVisibleCurve12; pBtnColourCurve[11] = ui->btnColourCurve12; pTxtValueCurve[11] = ui->txtValueCurve12; pRdoBoldCurve[11] = ui->rdoBoldCurve12;
118 | pChkVisibleCurve[12] = ui->chkVisibleCurve13; pBtnColourCurve[12] = ui->btnColourCurve13; pTxtValueCurve[12] = ui->txtValueCurve13; pRdoBoldCurve[12] = ui->rdoBoldCurve13;
119 | pChkVisibleCurve[13] = ui->chkVisibleCurve14; pBtnColourCurve[13] = ui->btnColourCurve14; pTxtValueCurve[13] = ui->txtValueCurve14; pRdoBoldCurve[13] = ui->rdoBoldCurve14;
120 | pChkVisibleCurve[14] = ui->chkVisibleCurve15; pBtnColourCurve[14] = ui->btnColourCurve15; pTxtValueCurve[14] = ui->txtValueCurve15; pRdoBoldCurve[14] = ui->rdoBoldCurve15;
121 | pChkVisibleCurve[15] = ui->chkVisibleCurve16; pBtnColourCurve[15] = ui->btnColourCurve16; pTxtValueCurve[15] = ui->txtValueCurve16; pRdoBoldCurve[15] = ui->rdoBoldCurve16;
122 | pChkVisibleCurve[16] = ui->chkVisibleCurve17; pBtnColourCurve[16] = ui->btnColourCurve17; pTxtValueCurve[16] = ui->txtValueCurve17; pRdoBoldCurve[16] = ui->rdoBoldCurve17;
123 | pChkVisibleCurve[17] = ui->chkVisibleCurve18; pBtnColourCurve[17] = ui->btnColourCurve18; pTxtValueCurve[17] = ui->txtValueCurve18; pRdoBoldCurve[17] = ui->rdoBoldCurve18;
124 | pChkVisibleCurve[18] = ui->chkVisibleCurve19; pBtnColourCurve[18] = ui->btnColourCurve19; pTxtValueCurve[18] = ui->txtValueCurve19; pRdoBoldCurve[18] = ui->rdoBoldCurve19;
125 | pChkVisibleCurve[19] = ui->chkVisibleCurve20; pBtnColourCurve[19] = ui->btnColourCurve20; pTxtValueCurve[19] = ui->txtValueCurve20; pRdoBoldCurve[19] = ui->rdoBoldCurve20;
126 |
127 | pCmbLineStyle[0] = ui->cmbLineStyle1; pCmbScatterStyle[0] = ui->cmbScatterStyle1;
128 | pCmbLineStyle[1] = ui->cmbLineStyle2; pCmbScatterStyle[1] = ui->cmbScatterStyle2;
129 | pCmbLineStyle[2] = ui->cmbLineStyle3; pCmbScatterStyle[2] = ui->cmbScatterStyle3;
130 | pCmbLineStyle[3] = ui->cmbLineStyle4; pCmbScatterStyle[3] = ui->cmbScatterStyle4;
131 | pCmbLineStyle[4] = ui->cmbLineStyle5; pCmbScatterStyle[4] = ui->cmbScatterStyle5;
132 | pCmbLineStyle[5] = ui->cmbLineStyle6; pCmbScatterStyle[5] = ui->cmbScatterStyle6;
133 | pCmbLineStyle[6] = ui->cmbLineStyle7; pCmbScatterStyle[6] = ui->cmbScatterStyle7;
134 | pCmbLineStyle[7] = ui->cmbLineStyle8; pCmbScatterStyle[7] = ui->cmbScatterStyle8;
135 | pCmbLineStyle[8] = ui->cmbLineStyle9; pCmbScatterStyle[8] = ui->cmbScatterStyle9;
136 | pCmbLineStyle[9] = ui->cmbLineStyle10; pCmbScatterStyle[9] = ui->cmbScatterStyle10;
137 | pCmbLineStyle[10] = ui->cmbLineStyle11; pCmbScatterStyle[10] = ui->cmbScatterStyle11;
138 | pCmbLineStyle[11] = ui->cmbLineStyle12; pCmbScatterStyle[11] = ui->cmbScatterStyle12;
139 | pCmbLineStyle[12] = ui->cmbLineStyle13; pCmbScatterStyle[12] = ui->cmbScatterStyle13;
140 | pCmbLineStyle[13] = ui->cmbLineStyle14; pCmbScatterStyle[13] = ui->cmbScatterStyle14;
141 | pCmbLineStyle[14] = ui->cmbLineStyle15; pCmbScatterStyle[14] = ui->cmbScatterStyle15;
142 | pCmbLineStyle[15] = ui->cmbLineStyle16; pCmbScatterStyle[15] = ui->cmbScatterStyle16;
143 | pCmbLineStyle[16] = ui->cmbLineStyle17; pCmbScatterStyle[16] = ui->cmbScatterStyle17;
144 | pCmbLineStyle[17] = ui->cmbLineStyle18; pCmbScatterStyle[17] = ui->cmbScatterStyle18;
145 | pCmbLineStyle[18] = ui->cmbLineStyle19; pCmbScatterStyle[18] = ui->cmbScatterStyle19;
146 | pCmbLineStyle[19] = ui->cmbLineStyle20; pCmbScatterStyle[19] = ui->cmbScatterStyle20;
147 |
148 | // 设置颜色选择框的初始背景颜色,与曲线同步颜色
149 | for(int i=0; i<20; i++){
150 | pBtnColourCurve[i]->setStyleSheet(QString("border:0px solid;background-color: %1;").arg(QColor(pCurve[i]->pen().color()).name()));
151 | }
152 |
153 | // 可见性选择框关联
154 | for(int i=0; i<20; i++){
155 | connect(pChkVisibleCurve[i], &QCheckBox::clicked, [=](){
156 | curveSetVisible(pPlot1, pCurve[i], pChkVisibleCurve[i]->checkState());
157 | });
158 | }
159 |
160 | // 颜色选择框关联
161 | for(int i=0; i<20; i++){
162 | connect(pBtnColourCurve[i], &QPushButton::clicked, [=](){
163 | curveSetColor(pPlot1, pCurve[i], pBtnColourCurve[i]);
164 | });
165 | }
166 |
167 | // 加粗显示多选框关联。尽量别用,会导致CPU使用率升高
168 | for(int i=0; i<20; i++){
169 | connect(pRdoBoldCurve[i], &QRadioButton::clicked, [=](){
170 | curveSetBold(pPlot1, pCurve[i], pRdoBoldCurve[i]->isChecked());
171 | });
172 | }
173 |
174 | // 曲线样式选择关联
175 | for(int i=0; i<20; i++){
176 | connect(pCmbLineStyle[i], &QComboBox::currentTextChanged, [=](){
177 | curveSetLineStyle(pPlot1, pCurve[i], pCmbLineStyle[i]->currentIndex());
178 | });
179 | }
180 |
181 | // 散点样式选择关联
182 | for(int i=0; i<20; i++){
183 | connect(pCmbScatterStyle[i], &QComboBox::currentTextChanged, [=](){
184 | curveSetScatterStyle(pPlot1, pCurve[i], pCmbScatterStyle[i]->currentIndex()+1);
185 | });
186 | }
187 |
188 | //QIcon ssCircleIcon (":/pic/ssCircle.png");
189 | //ui->cmbScatterStyle1->addItem(ssCircleIcon,"空心圆");
190 | for(int i=0; i<20; i++){
191 | pCmbScatterStyle[i]->setIconSize(QSize(25,17)); // 设置图片显示像素大小,不然会默认大小显示会模糊
192 | }
193 |
194 | }
195 |
196 | // 定时器溢出处理槽函数。用来生成曲线的坐标数据。
197 | void Plot::TimeData_Update(void)
198 | {
199 | // 生成坐标数据
200 | static float f;
201 | f += 0.01;
202 | //qDebug() << sin(f)*100;
203 | // 将坐标数据,传递给曲线
204 | ShowPlot_TimeDemo(pPlot1, sin(f)*100);
205 | }
206 |
207 | // 曲线更新绘图,定时器绘图演示
208 | void Plot::ShowPlot_TimeDemo(QCustomPlot *customPlot, double num)
209 | {
210 | cnt++;
211 | // 给曲线添加数据
212 | for(int i=0; i<10; i++){
213 | pTxtValueCurve[i]->setText(QString::number(num-i*10,'g',8));// 显示曲线当前值
214 | pCurve[i]->addData(cnt, num-i*10);
215 | }
216 | for(int i=10; i<20; i++){
217 | pTxtValueCurve[i]->setText(QString::number(num+(i-9)*10,'g',8));// 显示曲线当前值
218 | pCurve[i]->addData(cnt, num+(i-9)*10);
219 | }
220 |
221 | // 设置x坐标轴显示范围,使其自适应缩放x轴,x轴最大显示pointCountX个点。与chkTrackX复选框有关
222 | if(ui->chkTrackX->checkState()){
223 | //customPlot->xAxis->setRange((pCurve[0]->dataCount()>pointCountX)?(pCurve[0]->dataCount()-pointCountX):0, pCurve[0]->dataCount());
224 | setAutoTrackX(customPlot);
225 | }
226 | // 设置y坐标轴显示范围,使其自适应曲线缩放
227 | if(ui->chkAdjustY->checkState()){
228 | setAutoTrackY(customPlot);
229 | }
230 |
231 | // 更新绘图,这种方式在高填充下太浪费资源。有另一种方式rpQueuedReplot,可避免重复绘图。
232 | // 最好的方法还是将数据填充、和更新绘图分隔开。将更新绘图单独用定时器更新。例程数据量较少没用单独定时器更新,实际工程中建议大家加上。
233 | //customPlot->replot();
234 | customPlot->replot(QCustomPlot::rpQueuedReplot);
235 |
236 | static QTime time(QTime::currentTime());
237 | double key = time.elapsed()/1000.0; // 开始到现在的时间,单位秒
238 | ////计算帧数
239 | static double lastFpsKey;
240 | static int frameCount;
241 | ++frameCount;
242 | if (key-lastFpsKey > 1) // 每1秒求一次平均值
243 | {
244 | //状态栏显示帧数和数据总数
245 | ui->statusbar->showMessage(
246 | QString("%1 FPS, Total Data points: %2")
247 | .arg(frameCount/(key-lastFpsKey), 0, 'f', 0)
248 | .arg(customPlot->graph(0)->data()->size()+customPlot->graph(1)->data()->size())
249 | , 0);
250 | lastFpsKey = key;
251 | frameCount = 0;
252 | }
253 |
254 | }
255 |
256 | // 曲线更新绘图,波形数据绘图
257 | void Plot::ShowPlot_WaveForm(QCustomPlot *customPlot, short value[])
258 | {
259 | cnt++;
260 | // 给曲线添加数据
261 | for(int i=0; i<20; i++){
262 | //QString strNum = QString::number(num,'g',8);// double类型
263 | pTxtValueCurve[i]->setText(QString::number(value[i]));// 显示曲线当前值
264 | pCurve[i]->addData(cnt, value[i]);// 从原值获取数据
265 | //pCurve[i]->addData(cnt, pTxtValueCurve[i]->text().toShort());// 从输入框获取数据
266 | // 因为20条线重叠在一起,所以QCustomPlot输入为0时看起来像不显示,隐藏其他后观察单条曲线是可以看到显示的
267 | }
268 |
269 | // 设置x坐标轴显示范围,使其自适应缩放x轴,x轴最大显示pointCountX个点。与chkTrackX复选框有关
270 | if(ui->chkTrackX->checkState()){
271 | //customPlot->xAxis->setRange((pCurve[0]->dataCount()>pointCountX)?(pCurve[0]->dataCount()-pointCountX):0, pCurve[0]->dataCount());
272 | setAutoTrackX(customPlot);
273 | }
274 | // 设置y坐标轴显示范围,使其自适应曲线缩放
275 | if(ui->chkAdjustY->checkState()){
276 | setAutoTrackY(customPlot);
277 | }
278 |
279 | // 更新绘图,这种方式在高填充下太浪费资源。有另一种方式rpQueuedReplot,可避免重复绘图。
280 | // 最好的方法还是将数据填充、和更新绘图分隔开。将更新绘图单独用定时器更新。例程数据量较少没用单独定时器更新,实际工程中建议大家加上。
281 | //customPlot->replot();
282 | customPlot->replot(QCustomPlot::rpQueuedReplot);
283 |
284 | static QTime time(QTime::currentTime());
285 | double key = time.elapsed()/1000.0; // 开始到现在的时间,单位秒
286 | ////计算帧数
287 | static double lastFpsKey;
288 | static int frameCount;
289 | ++frameCount;
290 | if (key-lastFpsKey > 1) // 每1秒求一次平均值
291 | {
292 | //状态栏显示帧数和数据总数
293 | ui->statusbar->showMessage(
294 | QString("%1 FPS, Total Data points: %2")
295 | .arg(frameCount/(key-lastFpsKey), 0, 'f', 0)
296 | .arg(customPlot->graph(0)->data()->size()+customPlot->graph(1)->data()->size())
297 | , 0);
298 | lastFpsKey = key;
299 | frameCount = 0;
300 | }
301 |
302 | }
303 |
304 | /* 功能:隐藏/显示曲线n
305 | * QCustomPlot *pPlot:父控件,绘图图表
306 | * QCPGraph *pCurve:图表的曲线
307 | * int arg1:曲线的可见性,>0可见,0不可见
308 | * */
309 | void Plot::curveSetVisible(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1)
310 | {
311 | if(arg1){
312 | pCurve->setVisible(true);
313 | }else{
314 | pCurve->setVisible(false);
315 | }
316 | pPlot->replot(QCustomPlot::rpQueuedReplot);
317 | }
318 |
319 | /* 功能:弹出颜色对话框,设置曲线n的颜色
320 | * QCustomPlot *pPlot:父控件,绘图图表
321 | * QCPGraph *pCurve:图表的曲线
322 | * QPushButton *btn:曲线颜色选择框的按键,与曲线的颜色同步
323 | * */
324 | void Plot::curveSetColor(QCustomPlot *pPlot, QCPGraph *pCurve, QPushButton *btn)
325 | {
326 | // 获取当前颜色
327 | QColor bgColor(0,0,0);
328 | //bgColor = btn->palette().color(QPalette::Background);// 由pushButton的背景色获得颜色
329 | bgColor = pCurve->pen().color();// 由curve曲线获得颜色
330 | // 以当前颜色打开调色板,父对象,标题,颜色对话框设置项(显示Alpha透明度通道)
331 | //QColor color = QColorDialog::getColor(bgColor);
332 | QColor color = QColorDialog::getColor(bgColor, this,
333 | tr("颜色对话框"),
334 | QColorDialog::ShowAlphaChannel);
335 | // 判断返回的颜色是否合法。若点击x关闭颜色对话框,会返回QColor(Invalid)无效值,直接使用会导致变为黑色。
336 | if(color.isValid()){
337 | // 设置选择框颜色
338 | btn->setStyleSheet(QString("border:0px solid;background-color: %1;").arg(color.name()));
339 | // 设置曲线颜色
340 | QPen pen = pCurve->pen();
341 | pen.setBrush(color);
342 | pCurve->setPen(pen);
343 | //pCurve->setPen(QPen(color));
344 | }
345 | // 更新绘图
346 | pPlot->replot(QCustomPlot::rpQueuedReplot);
347 | }
348 |
349 | /* 功能:加粗显示曲线n
350 | * QCustomPlot *pPlot:父控件,绘图图表
351 | * QCPGraph *pCurve:图表的曲线
352 | * int arg1:曲线的粗细,>0粗,0细
353 | * */
354 | void Plot::curveSetBold(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1)
355 | {
356 | // 预先读取曲线的颜色
357 | QPen pen = pCurve->pen();
358 | //pen.setBrush(pCurve->pen().color());// 由curve曲线获得颜色
359 |
360 | if(arg1){
361 | pen.setWidth(3);
362 | pCurve->setPen(pen);
363 | }else{
364 | pen.setWidth(1);
365 | pCurve->setPen(pen);
366 | }
367 | pPlot->replot(QCustomPlot::rpQueuedReplot);
368 | }
369 |
370 | /* 功能:选择曲线样式(线,点,积)
371 | * QCustomPlot *pPlot:父控件,绘图图表
372 | * QCPGraph *pCurve:图表的曲线
373 | * int arg1:曲线样式(线,点,积)
374 | * */
375 | void Plot::curveSetLineStyle(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1)
376 | {
377 | // 设置曲线样式
378 | //customPlot->graph(19)->setLineStyle(QCPGraph::lsLine); // 数据点通过直线连接
379 | //customPlot->graph(19)->setLineStyle((QCPGraph::LineStyle)i);//设置线性
380 | //pCurve->setLineStyle(QCPGraph::LineStyle(arg1));
381 | pCurve->setLineStyle((QCPGraph::LineStyle)arg1);
382 | pPlot->replot(QCustomPlot::rpQueuedReplot);
383 | }
384 |
385 | /* 功能:选择散点样式(空心圆、实心圆、正三角、倒三角)
386 | * QCustomPlot *pPlot:父控件,绘图图表
387 | * QCPGraph *pCurve:图表的曲线
388 | * int arg1:散点样式(空心圆、实心圆、正三角、倒三角)
389 | * */
390 | void Plot::curveSetScatterStyle(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1)
391 | {
392 | // 设置散点样式
393 | //customPlot->graph(19)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5)); // 空心圆
394 | //pCurve->setScatterStyle(QCPScatterStyle::ScatterShape(arg1)); // 散点样式
395 | //pCurve->setScatterStyle((QCPScatterStyle::ScatterShape)arg1); // 散点样式
396 | if(arg1 <= 10){
397 | pCurve->setScatterStyle(QCPScatterStyle((QCPScatterStyle::ScatterShape)arg1, 5)); // 散点样式
398 | }else{ // 后面的散点图形略复杂,太小会看不清
399 | pCurve->setScatterStyle(QCPScatterStyle((QCPScatterStyle::ScatterShape)arg1, 8)); // 散点样式
400 | }
401 | pPlot->replot(QCustomPlot::rpQueuedReplot);
402 | }
403 |
404 | // 图例显示与否
405 | void Plot::on_chkShowLegend_stateChanged(int arg1)
406 | {
407 | if(arg1){
408 | // 显示图表的图例
409 | pPlot1->legend->setVisible(true);
410 | }else{
411 | // 不显示图表的图例
412 | pPlot1->legend->setVisible(false);
413 | }
414 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
415 | }
416 |
417 | // 绘图演示-曲线
418 | void Plot::on_chkDrawDemo_stateChanged(int arg1)
419 | {
420 | if(arg1){
421 | timer->start(10);
422 | }else{
423 | timer->stop();
424 | }
425 | }
426 |
427 | // 设置曲线x轴自动跟随
428 | void Plot::setAutoTrackX(QCustomPlot *pPlot)
429 | {
430 | pointCountX = ui->txtPointCountX->text().toUInt();
431 | if(pCurve[0]->dataCount() < pointCountX){
432 | pPlot->xAxis->setRange(0,pointCountX);
433 | }else{
434 | pPlot->xAxis->setRange((pCurve[0]->dataCount()>pointCountX)?(pCurve[0]->dataCount()-pointCountX):0, pCurve[0]->dataCount());
435 | }
436 | }
437 |
438 | // 设置曲线x轴手动设置范围(依照右下角输入框)
439 | void Plot::setManualSettingX(QCustomPlot *pPlot)
440 | {
441 | pointOriginX = ui->txtPointOriginX->text().toInt();
442 | pointCountX = ui->txtPointCountX->text().toUInt();
443 | pPlot->xAxis->setRange(pointOriginX, pointOriginX+pointCountX);
444 | }
445 |
446 | // 设置Y轴自适应
447 | void Plot::setAutoTrackY(QCustomPlot *pPlot)
448 | {
449 | pPlot->graph(0)->rescaleValueAxis();// y轴自适应,可放大可缩小
450 | for(int i=1; i<20; i++){
451 | pPlot->graph(i)->rescaleValueAxis(true);// y轴自适应,只能放大
452 | }
453 | }
454 |
455 | // 重新设置X轴显示的点数
456 | void Plot::on_txtPointCountX_returnPressed()
457 | {
458 | if(ui->chkTrackX->checkState()){
459 | setAutoTrackX(pPlot1);
460 | }else{
461 | setManualSettingX(pPlot1);
462 | }
463 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
464 | }
465 |
466 | void Plot::on_txtPointCountY_returnPressed()
467 | {
468 | pointCountY = ui->txtPointCountY->text().toUInt();
469 | pPlot1->yAxis->setRange(pointCountY/2*-1,pointCountY/2);
470 | ui->txtPointOriginY->setText(QString::number(pointCountY/2*-1));
471 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
472 | }
473 |
474 | void Plot::on_btnColourBack_clicked()
475 | {
476 | // 获取当前颜色
477 | QColor bgColor(0,0,0);
478 | bgColor = ui->btnColourBack->palette().color(QPalette::Background);// 由pushButton的背景色获得颜色
479 | // 以当前颜色打开调色板,父对象,标题,颜色对话框设置项(显示Alpha透明度通道)
480 | //QColor color = QColorDialog::getColor(bgColor);
481 | QColor color = QColorDialog::getColor(bgColor, this,
482 | tr("颜色对话框"),
483 | QColorDialog::ShowAlphaChannel);
484 |
485 | // 判断返回的颜色是否合法。若点击x关闭颜色对话框,会返回QColor(Invalid)无效值,直接使用会导致变为黑色。
486 | if(color.isValid()){
487 | // 设置背景颜色
488 | pPlot1->setBackground(color);
489 | // 设置背景选择框颜色
490 | ui->btnColourBack->setStyleSheet(QString("border:0px solid;background-color: %1;").arg(color.name()));
491 | }
492 | // 更新绘图
493 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
494 | }
495 |
496 | void Plot::on_txtPointOriginX_returnPressed()
497 | {
498 | setManualSettingX(pPlot1);
499 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
500 | }
501 |
502 | void Plot::on_chkTrackX_stateChanged(int arg1)
503 | {
504 | if(arg1){
505 | ui->txtPointOriginX->setEnabled(false);
506 | setAutoTrackX(pPlot1);
507 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
508 | }else{
509 | ui->txtPointOriginX->setEnabled(true);
510 | }
511 | }
512 |
513 | void Plot::on_chkAdjustY_stateChanged(int arg1)
514 | {
515 | if(arg1){
516 | ui->txtPointOriginY->setEnabled(false);
517 | ui->txtPointCountY->setEnabled(false);
518 | setAutoTrackY(pPlot1);
519 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
520 | }else{
521 | ui->txtPointOriginY->setEnabled(true);
522 | ui->txtPointCountY->setEnabled(true);
523 | }
524 | }
525 |
526 | void Plot::on_txtPointOriginY_returnPressed()
527 | {
528 | pointOriginY = ui->txtPointOriginY->text().toInt();
529 | pointCountY = ui->txtPointCountY->text().toUInt();
530 | pPlot1->yAxis->setRange(pointOriginY, pointOriginY+pointCountY);
531 | qDebug() << pointOriginY << pointCountY;
532 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
533 | }
534 |
535 | // 每次图表重绘后,都会更新当前显示的原点坐标与范围。与上次不同时才会更新显示,解决有曲线数据时无法输入y的参数的问题
536 | void Plot::repPlotCoordinate()
537 | {
538 | static int xOrigin, yOrigin, yCount;
539 | static int xOriginLast, yOriginLast, yCountLast;
540 |
541 | xOrigin = pPlot1->xAxis->range().lower;
542 | yOrigin = pPlot1->yAxis->range().lower;
543 | yCount = pPlot1->yAxis->range().size();
544 | // 与上次不同时才会更新显示,解决有曲线数据时无法输入y的参数的问题
545 | if(xOriginLast != xOrigin){
546 | ui->txtPointOriginX->setText(QString::number(xOrigin));
547 | }
548 | if(yOriginLast != yOrigin){
549 | ui->txtPointOriginY->setText(QString::number(yOrigin));
550 | }
551 | if(yCountLast != yCount){
552 | ui->txtPointCountY->setText(QString::number(yCount));
553 | }
554 | // 记录历史值
555 | xOriginLast = xOrigin;
556 | yOriginLast = yOrigin;
557 | yCountLast = yCount;
558 | }
559 |
560 | // 清空绘图
561 | void Plot::on_btnClearGraphs_clicked()
562 | {
563 | //pPlot1->clearGraphs(); // 清除图表的所有数据和设置,需要重新设置才能重新绘图
564 | //pPlot1->clearPlottables(); // 清除图表中所有曲线,需要重新添加曲线才能绘图
565 | for(int i=0; i<20; i++){
566 | pPlot1->graph(i)->data().data()->clear(); // 仅仅清除曲线的数据
567 | }
568 | cnt = 0;
569 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
570 | }
571 |
572 | // 设置X轴主刻度个数
573 | void Plot::on_txtMainScaleNumX_returnPressed()
574 | {
575 | pPlot1->xAxis->ticker()->setTickCount(ui->txtMainScaleNumX->text().toUInt());
576 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
577 | }
578 |
579 | // 设置Y轴主刻度个数
580 | void Plot::on_txtMainScaleNumY_returnPressed()
581 | {
582 | pPlot1->yAxis->ticker()->setTickCount(ui->txtMainScaleNumY->text().toUInt());
583 | pPlot1->replot(QCustomPlot::rpQueuedReplot);
584 | }
585 |
--------------------------------------------------------------------------------
/plot.h:
--------------------------------------------------------------------------------
1 | #ifndef PLOT_H
2 | #define PLOT_H
3 |
4 | #include
5 | #include "qcustomplot.h"
6 |
7 | namespace Ui {
8 | class Plot;
9 | }
10 |
11 | class Plot : public QMainWindow
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | explicit Plot(QWidget *parent = nullptr);
17 | ~Plot();
18 | // 绘图控件的指针
19 | QCustomPlot *pPlot1;
20 |
21 | void ShowPlot_TimeDemo(QCustomPlot *customPlot, double num);
22 | void ShowPlot_WaveForm(QCustomPlot *customPlot, short value[]);
23 |
24 | private slots:
25 | void TimeData_Update(void);
26 |
27 | void on_chkShowLegend_stateChanged(int arg1);
28 |
29 | void on_chkDrawDemo_stateChanged(int arg1);
30 |
31 | void on_txtPointCountX_returnPressed();
32 |
33 | void on_txtPointCountY_returnPressed();
34 |
35 | void on_btnColourBack_clicked();
36 |
37 | void on_txtPointOriginX_returnPressed();
38 |
39 | void on_chkTrackX_stateChanged(int arg1);
40 |
41 | void on_chkAdjustY_stateChanged(int arg1);
42 |
43 | void on_txtPointOriginY_returnPressed();
44 |
45 | void repPlotCoordinate();
46 |
47 | void on_btnClearGraphs_clicked();
48 |
49 | void on_txtMainScaleNumX_returnPressed();
50 |
51 | void on_txtMainScaleNumY_returnPressed();
52 |
53 | private:
54 | Ui::Plot *ui;
55 |
56 | // 状态栏指针
57 | QStatusBar *sBar;
58 | // 定时器指针
59 | QTimer *timer;
60 | // 绘图控件中曲线的指针
61 | QCPGraph *pCurve[20];
62 | // 绘图框X轴显示的坐标点数
63 | int pointOriginX=0;
64 | int pointOriginY=0;
65 | int pointCountX=0;
66 | int pointCountY=0;
67 |
68 | double cnt=0;// 当前绘图的X坐标
69 |
70 | // ui界面中,选择曲线可见性的checkBox的指针。方便用指针数组写代码,不然很占地方
71 | QCheckBox *pChkVisibleCurve[20];
72 | // ui界面中,选择曲线颜色的pushButton的指针。方便用指针数组写代码,不然很占地方
73 | QPushButton *pBtnColourCurve[20];
74 | // ui界面中,曲线当前值的lineEdit的指针。方便用指针数组写代码,不然很占地方
75 | QLineEdit *pTxtValueCurve[20];
76 | // ui界面中,选择曲线粗细的radioButton的指针。方便用指针数组写代码,不然很占地方
77 | QRadioButton *pRdoBoldCurve[20];
78 | // ui界面中,选择曲线样式的cmbLineStyle的指针。方便用指针数组写代码,不然很占地方
79 | QComboBox *pCmbLineStyle[20];
80 | // ui界面中,选择散点样式的cmbScatterStyle的指针。方便用指针数组写代码,不然很占地方
81 | QComboBox *pCmbScatterStyle[20];
82 |
83 | void QPlot_init(QCustomPlot *customPlot);
84 | void QPlot_widget_init(void);
85 |
86 | void curveSetVisible(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1);
87 | void curveSetColor(QCustomPlot *pPlot, QCPGraph *pCurve, QPushButton *btn);
88 | void curveSetBold(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1);
89 | void curveSetLineStyle(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1);
90 | void curveSetScatterStyle(QCustomPlot *pPlot, QCPGraph *pCurve, int arg1);
91 |
92 | void setAutoTrackX(QCustomPlot *pPlot);
93 | void setManualSettingX(QCustomPlot *pPlot);
94 | void setAutoTrackY(QCustomPlot *pPlot);
95 | };
96 |
97 | #endif // PLOT_H
98 |
--------------------------------------------------------------------------------
/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | debug_32px_1103189_easyicon.net.ico
4 | chart_curve_32px_506507_easyicon.net.ico
5 | pic/ssCircle.png
6 | pic/ssDisc.png
7 | pic/ssSquare.png
8 | pic/ssDiamond.png
9 | pic/ssCross.png
10 | pic/ssPlus.png
11 | pic/ssStar.png
12 | pic/ssTriangle.png
13 | pic/ssTriangleInverted.png
14 | pic/ssNone.png
15 | pic/ssCrossSquare.png
16 | pic/ssPlusSquare.png
17 | pic/ssCrossCircle.png
18 | pic/ssPlusCircle.png
19 | pic/ssPeace.png
20 | axis_64px_1200251_easyicon.net.ico
21 |
22 |
23 |
--------------------------------------------------------------------------------
/测试帧.txt:
--------------------------------------------------------------------------------
1 | 3A 3B 01 04 11 12 13 14 C4
2 | 4370 4884
3 |
4 | 3A 3B 01 04 FF 38 13 14 D8
5 | -200 4884
6 |
7 | 3A 3B 01 04 FF C8 13 14 68
8 | -56 4884
9 |
10 | 3A 3B 01 28 00 C8 01 90 02 58 03 20 03 E8 04 B0 05 78 06 40 07 08 07 D0 FF 38 FE 70 FD A8 FC E0 FC 18 FB 50 FA 88 F9 C0 F8 F8 F8 30 94
11 | 200 400 600 800 1000 1200 1400 1600 1800 2000 -200 -400 -600 -800 -1000-1200 -1400-1600 -1800 -2000
--------------------------------------------------------------------------------