You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 lines
20 KiB

2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. #include "mainwindow.h"
  2. #include <QDateTime>
  3. #include <QMessageBox>
  4. #include <QtConcurrent>
  5. #include <QtSerialPort/QSerialPort>
  6. #include <QtSerialPort/QSerialPortInfo>
  7. #include "./ui_mainwindow.h"
  8. #include "electrocardiograph_tester.hpp"
  9. #include "logger.hpp"
  10. #include "qt_serial_datachannel.hpp"
  11. #include "widgetplot2d.h"
  12. #include "zexception.hpp"
  13. WidgetPlot2D *wp2d;
  14. using namespace std;
  15. using namespace iflytop;
  16. typedef enum {
  17. kone_lead_ecg,
  18. kthree_lead_ecg,
  19. kone_lead_ecg_v2,
  20. } device_type_t;
  21. static MainWindow *m_mainWindow;
  22. static QTDataChannel G_QTDataChannel;
  23. static device_type_t m_devicetype;
  24. #define TAG "MainWindow"
  25. static const char *fmt(const char *fmt, ...) {
  26. va_list args;
  27. va_start(args, fmt);
  28. static char buf[1024] = {0};
  29. vsnprintf(buf, sizeof(buf), fmt, args);
  30. va_end(args);
  31. return buf;
  32. }
  33. void MainWindow::log_output(QtMsgType type, const QMessageLogContext &context, const QString &msg) {}
  34. void MainWindow::doinui_slot(QFunction func) {
  35. if (func.get()) func.get()();
  36. }
  37. void MainWindow::instructionPreviewShow(const char *fmt, ...) {
  38. va_list args;
  39. va_start(args, fmt);
  40. char buf[1024] = {0};
  41. vsnprintf(buf, sizeof(buf), fmt, args);
  42. va_end(args);
  43. QString text(buf);
  44. QString info;
  45. info.append(QDateTime::currentDateTime().toString("hh:mm:ss.zzz "));
  46. info.append(" |");
  47. info.append(text);
  48. emit doinui_signal(QFunction([this, info]() { ui->instructionPreview->append(info); }));
  49. }
  50. void MainWindow::instructionPreviewClear() { ui->instructionPreview->document()->clear(); }
  51. void MainWindow::reportPreviewShow(const char *fmt, ...) {
  52. va_list args;
  53. va_start(args, fmt);
  54. char buf[1024] = {0};
  55. vsnprintf(buf, sizeof(buf), fmt, args);
  56. va_end(args);
  57. QString text(buf);
  58. QString info;
  59. info.append(QDateTime::currentDateTime().toString("hh:mm:ss.zzz "));
  60. info.append(text);
  61. emit doinui_signal(QFunction([this, info]() {
  62. if (ui->reportPreview->document()->lineCount() > 1000) {
  63. ui->reportPreview->document()->clear();
  64. }
  65. ui->reportPreview->append(info);
  66. }));
  67. }
  68. void MainWindow::blockDataUploadPreviewShow(const char *fmt, ...) {
  69. va_list args;
  70. va_start(args, fmt);
  71. char buf[1024] = {0};
  72. vsnprintf(buf, sizeof(buf), fmt, args);
  73. va_end(args);
  74. QString text(buf);
  75. QString info;
  76. info.append(QDateTime::currentDateTime().toString("hh:mm:ss.zzz "));
  77. info.append(text);
  78. emit doinui_signal(QFunction([this, info]() {
  79. if (ui->uploadDataPreview->document()->lineCount() > 1000) {
  80. ui->uploadDataPreview->document()->clear();
  81. }
  82. ui->uploadDataPreview->append(info);
  83. }));
  84. }
  85. void MainWindow::rawDataPreviewShow(const char *fmt, ...) {
  86. va_list args;
  87. va_start(args, fmt);
  88. char buf[1024] = {0};
  89. vsnprintf(buf, sizeof(buf), fmt, args);
  90. va_end(args);
  91. QString text(buf);
  92. QString info;
  93. info.append(QDateTime::currentDateTime().toString("hh:mm:ss.zzz"));
  94. info.append(text);
  95. emit doinui_signal(QFunction([this, info]() {
  96. if (ui->rawDataPreview->document()->lineCount() > 1000) {
  97. ui->rawDataPreview->document()->clear();
  98. }
  99. ui->rawDataPreview->append(info);
  100. }));
  101. }
  102. // uploadDataPreview
  103. #pragma pack(push, 1)
  104. typedef struct {
  105. uint16_t header;
  106. int16_t wave1;
  107. int16_t wave2;
  108. int16_t wave3;
  109. int16_t wave4;
  110. int16_t wave5;
  111. uint8_t check;
  112. uint16_t tail;
  113. } Wave_t;
  114. #pragma pack(pop)
  115. void MainWindow::displayInfo(bool suc, QString info) {}
  116. MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  117. /*******************************************************************************
  118. * QT初始化 *
  119. *******************************************************************************/
  120. ui->setupUi(this);
  121. m_mainWindow = this;
  122. qRegisterMetaType<int32_t>("int32_t");
  123. qRegisterMetaType<uint32_t>("uint32_t");
  124. qRegisterMetaType<float>("float");
  125. qRegisterMetaType<function<void()>>("function<void()>");
  126. qRegisterMetaType<QFunction>("QFunction");
  127. connect(this, SIGNAL(doinui_signal(QFunction)), this, SLOT(doinui_slot(QFunction)));
  128. // qInstallMessageHandler(log_output);
  129. wp2d = new WidgetPlot2D();
  130. wp2d->initGraphName(QStringList() << "心电");
  131. /*******************************************************************************
  132. * *
  133. *******************************************************************************/
  134. constructUI();
  135. /*******************************************************************************
  136. * *
  137. *******************************************************************************/
  138. G_QTDataChannel.init();
  139. ElectrocardiographTester::ins()->initialize(&G_QTDataChannel);
  140. }
  141. MainWindow::~MainWindow() { delete ui; }
  142. /*******************************************************************************
  143. * UI相关构造 *
  144. *******************************************************************************/
  145. void MainWindow::constructUI() {
  146. /*******************************************************************************
  147. * UI *
  148. *******************************************************************************/
  149. {
  150. const auto infos = QSerialPortInfo::availablePorts();
  151. for (const QSerialPortInfo &info : infos) {
  152. ui->serialPortCB->addItem(info.portName());
  153. }
  154. }
  155. ui->serialBaudrateCB->addItem("9600");
  156. ui->serialBaudrateCB->addItem("14400");
  157. ui->serialBaudrateCB->addItem("19200");
  158. ui->serialBaudrateCB->addItem("38400");
  159. ui->serialBaudrateCB->addItem("57600");
  160. ui->serialBaudrateCB->addItem("115200");
  161. ui->serialBaudrateCB->addItem("460800");
  162. ui->serialBaudrateCB->addItem("500000");
  163. ui->serialBaudrateCB->setCurrentIndex(6);
  164. connect(ui->serialPortRefreshKey, &QPushButton::clicked, this, [this](bool check) {
  165. ui->serialPortCB->clear();
  166. const auto infos = QSerialPortInfo::availablePorts();
  167. for (const QSerialPortInfo &info : infos) {
  168. ui->serialPortCB->addItem(info.portName());
  169. }
  170. });
  171. connect(ui->serialOpenKey, &QPushButton::clicked, this, [=](bool check) {
  172. // 打开串口
  173. if (ui->serialOpenKey->text() == "打开") {
  174. G_QTDataChannel.setPortName(ui->serialPortCB->currentText().toStdString());
  175. G_QTDataChannel.setBaudRate(ui->serialBaudrateCB->currentText().toInt());
  176. G_QTDataChannel.setDataBits(QSerialPort::Data8);
  177. G_QTDataChannel.setParity(QSerialPort::NoParity);
  178. G_QTDataChannel.setFlowControl(QSerialPort::NoFlowControl);
  179. G_QTDataChannel.setStopBits(QSerialPort::OneStop);
  180. if (!G_QTDataChannel.open()) {
  181. QMessageBox::about(NULL, "提示", "串口无法打开,串口不存在或已被占??");
  182. return;
  183. }
  184. ui->serialOpenKey->setText("关闭");
  185. // 下拉菜单控件使能
  186. ui->serialBaudrateCB->setEnabled(false);
  187. ui->serialPortCB->setEnabled(false);
  188. ui->serialPortRefreshKey->setEnabled(false);
  189. } else {
  190. G_QTDataChannel.close();
  191. ui->serialOpenKey->setText("打开");
  192. ui->serialBaudrateCB->setEnabled(true);
  193. ui->serialPortCB->setEnabled(true);
  194. ui->serialPortRefreshKey->setEnabled(true);
  195. }
  196. });
  197. /*******************************************************************************
  198. * UI *
  199. *******************************************************************************/
  200. {
  201. ui->deviceType->addItem("单导联一代(M1001)");
  202. ui->deviceType->addItem("三导联一代(M1002)");
  203. ui->deviceType->addItem("单导联二代(M1003)");
  204. ui->deviceType->setCurrentIndex(0);
  205. connect(ui->deviceType, &QComboBox::currentTextChanged, this, [this](const QString &text) {
  206. if (text == "单导联一代(M1001)") {
  207. m_devicetype = kone_lead_ecg;
  208. } else if (text == "三导联一代(M1002)") {
  209. m_devicetype = kthree_lead_ecg;
  210. } else if (text == "单导联二代(M1003)") {
  211. m_devicetype = kone_lead_ecg_v2;
  212. }
  213. });
  214. }
  215. // 事件填充
  216. ElectrocardiographTester::ins()->regReportCB([this](bool checkok, ify_hrs_packet_t *report_packet, size_t len) {
  217. int reportType = report_packet->cmd;
  218. switch (reportType) {
  219. case ify_hrs_report_heartrate_data: {
  220. heartrate_report_packet_t *heartrate_report = (heartrate_report_packet_t *)report_packet;
  221. if (m_devicetype == kone_lead_ecg) {
  222. static uint32_t lastpacket_index = 0;
  223. static uint32_t lostpacket = 0;
  224. if ((lastpacket_index + 5) != (heartrate_report->sample_data_index)) {
  225. lostpacket++;
  226. }
  227. lastpacket_index = heartrate_report->sample_data_index;
  228. reportPreviewShow("[preview data ] lost:%d index %d", lostpacket, heartrate_report->sample_data_index);
  229. if (checkok) {
  230. uint16_t data0 = (uint16_t)(heartrate_report->data[0]) + ((uint16_t)heartrate_report->data[1] << 8);
  231. uint16_t data1 = (uint16_t)(heartrate_report->data[2]) + ((uint16_t)heartrate_report->data[3] << 8);
  232. uint16_t data2 = (uint16_t)(heartrate_report->data[4]) + ((uint16_t)heartrate_report->data[5] << 8);
  233. uint16_t data3 = (uint16_t)(heartrate_report->data[6]) + ((uint16_t)heartrate_report->data[7] << 8);
  234. uint16_t data4 = (uint16_t)(heartrate_report->data[8]) + ((uint16_t)heartrate_report->data[9] << 8);
  235. emit doinui_signal(QFunction([this, data0, data1, data2, data3, data4]() { //
  236. wp2d->addData("心电", data0, 0);
  237. wp2d->addData("心电", data1, 2);
  238. wp2d->addData("心电", data2, 4);
  239. wp2d->addData("心电", data3, 6);
  240. wp2d->addData("心电", data4, 8);
  241. // wp2d->addData("心电", data2);
  242. // wp2d->addData("心电", data3);
  243. // wp2d->addData("心电", data4);
  244. }));
  245. }
  246. } else if (m_devicetype == kthree_lead_ecg) {
  247. } else if (m_devicetype == kone_lead_ecg_v2) {
  248. if (checkok) {
  249. uint32_t packetlen = len - sizeof(heartrate_report_packet_t);
  250. static uint32_t lastpacket_index = 0;
  251. static uint32_t lostpacket = 0;
  252. if ((lastpacket_index + 50) != (heartrate_report->sample_data_index)) {
  253. lostpacket++;
  254. }
  255. lastpacket_index = heartrate_report->sample_data_index;
  256. reportPreviewShow("[preview data ] lost:%d index %d %d", lostpacket, heartrate_report->sample_data_index, packetlen);
  257. int32_t *frame = (int32_t *)heartrate_report->data;
  258. for (int i = 0; i < 50; i++) {
  259. int32_t data = frame[i];
  260. emit doinui_signal(QFunction([this, data, i]() { wp2d->addData("心电", data, i * 2); }));
  261. }
  262. }
  263. }
  264. break;
  265. }
  266. case ify_hrs_report_battery_level: {
  267. reportPreviewShow("[battery_level ]");
  268. break;
  269. }
  270. case ify_hrs_report_low_battey_level: {
  271. reportPreviewShow("[low_battey ]");
  272. break;
  273. }
  274. case ify_hrs_report_sample_finish_end: {
  275. reportPreviewShow("[sample_finish ]");
  276. break;
  277. }
  278. case ify_hrs_report_sensor_drop_detect: {
  279. sensor_drop_event_report_packet_t *sensor_drop_report = (sensor_drop_event_report_packet_t *)report_packet->data;
  280. reportPreviewShow("[sensor_drop ] %s %s", zhex2binary(sensor_drop_report->drop_state0).c_str(), zhex2binary(sensor_drop_report->drop_state1).c_str());
  281. break;
  282. }
  283. case ify_hrs_report_record_upload_end: {
  284. uint32_t checksum = *(uint32_t *)report_packet->data;
  285. reportPreviewShow("[upload end ] checksum: 0x%08x", checksum);
  286. break;
  287. }
  288. default:
  289. break;
  290. }
  291. });
  292. ElectrocardiographTester::ins()->regCh4CheckSumPacketReport([this](uint32_t rxcnt, uint32_t report_packet_checksum) { //
  293. blockDataUploadPreviewShow("RXCNT %d CHECKSUM 0x%08x", rxcnt, report_packet_checksum);
  294. });
  295. ElectrocardiographTester::ins()->regRawDataCB([this](raw_data_type_t type, uint8_t *hex, uint32_t hexlen) {
  296. if (type == kcmd_cmd) {
  297. rawDataPreviewShow("[CMD ] %s", zhex2str(hex, hexlen).c_str());
  298. } else if (type == kcmd_receipt) {
  299. rawDataPreviewShow("[RECEIPT] %s", zhex2str(hex, hexlen).c_str());
  300. } else if (type == kcmd_report) {
  301. rawDataPreviewShow("[REPORT ] %s", zhex2str(hex, hexlen).c_str());
  302. } else if (type == kcmd_ch4_data) {
  303. // rawDataPreviewShow("[CH4 ] %s", zhex2str(hex, hexlen).c_str());
  304. }
  305. });
  306. }
  307. void MainWindow::processException(zexception &e) { instructionPreviewShow("%s:%s", e.what(), hrs_ecode2str((ify_hrs_error_code_t)e.ecode())); }
  308. void MainWindow::on_readDeviceVersion_clicked() {
  309. instructionPreviewClear();
  310. try {
  311. device_version_info_receipt_t version;
  312. ElectrocardiographTester::ins()->readDeviceVersion(&version);
  313. instructionPreviewShow("-------------- version ----------------");
  314. instructionPreviewShow(" blestack_version: %d", version.blestack_version);
  315. instructionPreviewShow(" bootloader_version: %d", version.bootloader_version);
  316. instructionPreviewShow(" firmware_version: %d", version.firmware_version);
  317. instructionPreviewShow(" hardware_version: %d", version.hardware_version);
  318. } catch (zexception &exception) {
  319. processException(exception);
  320. }
  321. }
  322. void MainWindow::on_readDeviceState_clicked() {
  323. instructionPreviewClear();
  324. try {
  325. device_state_receipt_t state;
  326. ElectrocardiographTester::ins()->readDeviceState(&state);
  327. instructionPreviewShow("-------------- state ----------------");
  328. instructionPreviewShow(" drop_state0: %s", zhex2binary(state.drop_state0).c_str());
  329. instructionPreviewShow(" drop_state1: %s", zhex2binary(state.drop_state1).c_str());
  330. instructionPreviewShow(" sampling_state: %d", state.device_state0.sampling_state);
  331. instructionPreviewShow(" report_state: %d", state.device_state0.report_state);
  332. instructionPreviewShow(" low_battery: %d", state.device_state0.low_battery);
  333. instructionPreviewShow(" full_storge: %d", state.device_state0.full_storge);
  334. instructionPreviewShow(" holder: %d", state.device_state0.holder);
  335. instructionPreviewShow(" powerlevel: %d", state.powerlevel);
  336. instructionPreviewShow(" storage_item_num: %d", state.storage_item_num);
  337. } catch (zexception &exception) {
  338. processException(exception);
  339. }
  340. }
  341. void MainWindow::on_readTime_clicked() {
  342. instructionPreviewClear();
  343. try {
  344. read_time_receipt_t time;
  345. ElectrocardiographTester::ins()->readTime(&time);
  346. instructionPreviewShow("-------------- time ----------------");
  347. instructionPreviewShow(" %d/%d/%d %d:%d:%d", time.year + 2000, time.month, time.day, time.hour, time.minute, time.second);
  348. } catch (zexception &exception) {
  349. processException(exception);
  350. }
  351. }
  352. void MainWindow::on_syncTime_clicked() {
  353. instructionPreviewClear();
  354. try {
  355. // 获取系统时间
  356. QDateTime now = QDateTime::currentDateTime();
  357. ElectrocardiographTester::ins()->syncTime(now.date().year() - 2000, now.date().month(), now.date().day(), now.time().hour(), now.time().minute(), now.time().second());
  358. instructionPreviewShow("sync time success!");
  359. } catch (zexception &exception) {
  360. processException(exception);
  361. }
  362. }
  363. std::string zhex2time(const uint8_t *hex, size_t len) {
  364. std::string str;
  365. if (len < 6) return str;
  366. str.append(fmt("%d/%d/%d %2d:%2d:%2d", hex[0] + 2000, hex[1], hex[2], hex[3], hex[4], hex[5]));
  367. return str;
  368. }
  369. void MainWindow::on_readAllRecords_clicked() {
  370. // 读取当前有多少条记录
  371. instructionPreviewClear();
  372. try {
  373. device_state_receipt_t state;
  374. ElectrocardiographTester::ins()->readDeviceState(&state);
  375. instructionPreviewShow("index recordid frameNum dataSize sensorNum captRate captPrec compAlgo checksum");
  376. for (int32_t i = 0; i < state.storage_item_num; i++) {
  377. read_record_info_receipt_t record;
  378. ElectrocardiographTester::ins()->readRecordsInfo(i, &record);
  379. instructionPreviewShow("%d %s %8d %8d %8d %8d %8d %8d 0x%08x", i, zhex2time(record.record_id, 6).c_str(), record.frameNum, record.dataSize, record.sensorNum, record.captureRate, record.capturePrecision, record.compressAlgorithm, record.checksum);
  380. }
  381. } catch (zexception &exception) {
  382. processException(exception);
  383. }
  384. }
  385. void MainWindow::on_startUploadRecord_clicked() {
  386. instructionPreviewClear();
  387. int reportIndex = ui->startUploadRecord_p0->toPlainText().toInt();
  388. try {
  389. read_record_info_receipt_t record;
  390. ElectrocardiographTester::ins()->readRecordsInfo(reportIndex, &record);
  391. ElectrocardiographTester::ins()->startUploadRecord(record.record_id);
  392. instructionPreviewShow("start upload record [%s] success", zhex2time(record.record_id, 6).c_str());
  393. } catch (zexception &exception) {
  394. processException(exception);
  395. }
  396. }
  397. void MainWindow::on_readSensorInfo_clicked() {
  398. instructionPreviewClear();
  399. try {
  400. sensor_info_receipt_t sensor;
  401. ElectrocardiographTester::ins()->readSensorInfo(&sensor);
  402. instructionPreviewShow("-------------- sensor ----------------");
  403. instructionPreviewShow(" sensor_num: %d", sensor.sensor_num);
  404. instructionPreviewShow(" sensor_precision: %d", sensor.sensor_precision);
  405. instructionPreviewShow(" sensor_sample_rate:%d", sensor.sensor_sample_rate);
  406. instructionPreviewShow(" sensor0_pos: %d", sensor.sensor0_pos);
  407. instructionPreviewShow(" sensor1_pos: %d", sensor.sensor1_pos);
  408. instructionPreviewShow(" sensor2_pos: %d", sensor.sensor2_pos);
  409. } catch (zexception &exception) {
  410. processException(exception);
  411. }
  412. }
  413. void MainWindow::on_readSN_clicked() {
  414. instructionPreviewClear();
  415. try {
  416. string sn;
  417. ElectrocardiographTester::ins()->readSn(sn);
  418. instructionPreviewShow("-------------- sn ----------------");
  419. instructionPreviewShow(" sn: %s", sn.c_str());
  420. } catch (zexception &exception) {
  421. processException(exception);
  422. }
  423. }
  424. void MainWindow::on_stopUploadRecord_clicked() {
  425. instructionPreviewClear();
  426. try {
  427. ElectrocardiographTester::ins()->stopUploadRecord();
  428. instructionPreviewShow("stop upload record success");
  429. } catch (zexception &exception) {
  430. processException(exception);
  431. }
  432. }
  433. void MainWindow::on_delRecord_clicked() {
  434. instructionPreviewClear();
  435. int reportIndex = ui->delRecord_p0->toPlainText().toInt();
  436. try {
  437. read_record_info_receipt_t record;
  438. ElectrocardiographTester::ins()->readRecordsInfo(reportIndex, &record);
  439. ElectrocardiographTester::ins()->delRecord(record.record_id);
  440. instructionPreviewShow("delete record [%s] success", zhex2time(record.record_id, 6).c_str());
  441. } catch (zexception &exception) {
  442. processException(exception);
  443. }
  444. }
  445. void MainWindow::on_startCapture_clicked() {
  446. instructionPreviewClear();
  447. try {
  448. ElectrocardiographTester::ins()->startCapture();
  449. instructionPreviewShow("start capture success");
  450. } catch (zexception &exception) {
  451. processException(exception);
  452. }
  453. }
  454. void MainWindow::on_stopCapture_clicked() {
  455. instructionPreviewClear();
  456. try {
  457. ElectrocardiographTester::ins()->stopCapture();
  458. instructionPreviewShow("stop capture success");
  459. } catch (zexception &exception) {
  460. processException(exception);
  461. }
  462. }
  463. void MainWindow::on_startRealtimeReport_clicked() {
  464. instructionPreviewClear();
  465. try {
  466. ElectrocardiographTester::ins()->startRealtimeReport();
  467. instructionPreviewShow("start realtime report success");
  468. wp2d->show();
  469. } catch (zexception &exception) {
  470. processException(exception);
  471. }
  472. }
  473. void MainWindow::on_stopRealtimeReport_clicked() {
  474. instructionPreviewClear();
  475. try {
  476. ElectrocardiographTester::ins()->stopRealtimeReport();
  477. instructionPreviewShow("stop realtime report success");
  478. wp2d->hide();
  479. } catch (zexception &exception) {
  480. processException(exception);
  481. }
  482. }
  483. void MainWindow::on_clearPreview_clicked() {
  484. ui->reportPreview->document()->clear();
  485. ui->uploadDataPreview->document()->clear();
  486. ui->rawDataPreview->document()->clear();
  487. ui->instructionPreview->document()->clear();
  488. }