天天看点

Qt平台使用fft解析肌电传感器信号

备注:该程序在串口解析时偶尔还会出现问题

Qt平台使用fft解析肌电传感器信号
Qt平台使用fft解析肌电传感器信号

.pro

#-------------------------------------------------
#
# Project created by QtCreator 2021-03-09T09:33:42
#
#-------------------------------------------------

QT       += core gui serialport printsupport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = untitled
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        graph.cpp \
        main.cpp \
        mainwindow.cpp \
        qcustomplot.cpp \
        resolver.cpp \
        serial.cpp

HEADERS += \
        fftw3.h \
        graph.h \
        mainwindow.h \
        qcustomplot.h \
        resolver.h \
        serial.h

FORMS += \
        mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

LIBS += -L$$PWD/./ -llibfftw3-3
INCLUDEPATH += $$PWD/.
DEPENDPATH += $$PWD/.
           

fftw3.h是fftw的自带头文件,工程中需要包含

graph.h

#ifndef GRAPH_H
#define GRAPH_H

#include <QMainWindow>
#include "qcustomplot.h"



#endif // GRAPH_H
           

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "serial.h"
#include "graph.h"
#include "resolver.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    QVector<double> x,y0,y1;

signals:
    void start_resolver();

public slots:
    void slot_graph();

private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();
    void readyRead_slot(QByteArray data);
    void on_actionserial_triggered(bool checked);
    void on_dockWidget_visibilityChanged(bool visible);

    void on_pushButton_3_clicked();

private:
    Ui::MainWindow *ui;
    serial *myserial;
    resolver *m_resolver;
    QThread *m_thread;

    void graph_init();
    void customplot1_add_graph(void);
    void customplot2_add_graph(void);
};

#endif // MAINWINDOW_H
           

qcustomplot.h是Qt中用来显示波形的外部库qcustomplot的头文件

resolver.h 

#ifndef RESOLVER_H
#define RESOLVER_H

#include <QObject>
#include <QThread>
#include "fftw3.h"

#define N 256
#define UART_BUF_SIZE   200

class resolver : public QObject
{
    Q_OBJECT
public:
    explicit resolver(QObject *parent = nullptr);
    ~resolver();

signals:
    void start_graph();

public slots:
    void do_resolver();

private:
    void do_fft();
    void do_resolver2(char *char_data,uint8_t len);
};

struct channel_data
{
    uint16_t average_value;
    uint16_t current_value;
    uint16_t power;
    uint16_t Shredded;
};

#endif // RESOLVER_H
           

serial.h

#ifndef SERIAL_H
#define SERIAL_H

#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include "qserialport.h"
#include "QString"


class serial:public QObject
{
    Q_OBJECT

public:
    serial(QObject *o=0);
    ~serial();
    bool openPort(QString portName,qint32 baudRate,QSerialPort::DataBits dataBits,
                  QSerialPort::Parity parity,QSerialPort::StopBits stopBits,
                  QSerialPort::FlowControl flowControl);
    bool isOpen();
    void closePort();
    QStringList getAvailablePortNameList();

    void write(QString str);
    void write(const char *data, qint64 len);
    QString ByteArrayToHexString(QByteArray data);

signals:
    void readyRead(QByteArray data);

private:
    QSerialPort *m_port;

    void readyRead_slot();



};

#endif // SERIAL_H
           

graph.cpp

#include "graph.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVector>
#include <stdio.h>

extern fftw_complex *in, *out;

/*-------------------------------------------*/
//画动态曲线时,传入数据采用addData,通过定时器多次调用,并在之后调用ui->widget->replot();
//动态曲线可以通过另一种设置坐标的方法解决坐标问题:
//setRange ( double  position, double  size, Qt::AlignmentFlag  alignment  )
//参数分别为:原点,偏移量,对其方式,有兴趣的读者可自行尝试,欢迎垂询
/*-------------------------------------------*/

void MainWindow::graph_init()
{
    x.resize(N);
    for(int i=0;i<N;i++)
    {
        x[i]=i;
    }
    y0.resize(N);
    y1.resize(N);

        //设定背景为黑色
        //ui->widget->setBackground(QBrush(Qt::black));
        //设置X轴文字标注
        ui->widget->xAxis->setLabel("time");
        ui->widget_2->xAxis->setLabel("time");
        //设置Y轴文字标注
        ui->widget->yAxis->setLabel("short HbO2");
        ui->widget_2->yAxis->setLabel("short HbO2");
        //设置X轴坐标范围
        ui->widget->xAxis->setRange(0,N);
        ui->widget_2->xAxis->setRange(0,N/2);
        //设置Y轴坐标范围
        ui->widget->yAxis->setRange(0,3000);
        ui->widget_2->yAxis->setRange(0,3000);
        //在坐标轴右侧和上方画线,和X/Y轴一起形成一个矩形
        //ui->widget->axisRect()->setupFullAxesBox();
        //设置基本坐标轴(左侧Y轴和下方X轴)可拖动、可缩放、曲线可选、legend可选、设置伸缩比例,使所有图例可见
        ui->widget->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes |
                                          QCP::iSelectLegend | QCP::iSelectPlottables);
        ui->widget_2->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes |
                                          QCP::iSelectLegend | QCP::iSelectPlottables);
        //设定右上角图形标注可见
        //ui->widget->legend->setVisible(true);
        //设定右上角图形标注的字体
        //ui->widget->legend->setFont(QFont("Helvetica", 9));

        /*曲线0*/
        customplot1_add_graph();
        customplot2_add_graph();

        //draw test
//        for(int i=0;i<100;i++)
//        {
//            y0[0] = 2*i+1;
//            ui->widget->graph(0)->addData(x,y0);
//            x[0]=i;
//        }
}

void MainWindow::customplot1_add_graph()
{
    //添加图形
    ui->widget->addGraph();
    //设置画笔
    QPen pen0(Qt::red, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    ui->widget->graph(0)->setPen(pen0);
    //ui->widget->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ScatterShape::ssPlus,5));
    //设置画刷,曲线和X轴围成面积的颜色
    //ui->widget->graph(0)->setBrush(QBrush(QColor(255,255,0)));
    //设置右上角图形标注名称
    ui->widget->graph(0)->setName("曲线");
    //传入数据,setData的两个参数类型为double
    //ui->widget->graph(0)->setLineStyle(QCPGraph::lsLine);//lsNone/lsLine/lsStepLeft/lsStepRight/lsStepCenter/lsImpulse
    //ui->widget->graph(0)->addData(x,y);
//    y0[0]=0;
//    ui->widget->graph(0)->setData(x,y0);
    //根据图像最高点最低点自动缩放坐标轴
    //Y轴
    //ui->widget->graph(0)->rescaleValueAxis(true);
    //X轴
    //ui->widget->graph(0)->rescaleKeyAxis(true);
    //X、Y轴
    //ui->widget->graph(0)->rescaleAxes(true);
}

void MainWindow::customplot2_add_graph()
{
    //添加图形
    ui->widget_2->addGraph();
    //设置画笔
    QPen pen0(Qt::blue, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    ui->widget_2->graph(0)->setPen(pen0);
    //ui->widget->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ScatterShape::ssPlus,5));
    //设置画刷,曲线和X轴围成面积的颜色
    //ui->widget->graph(0)->setBrush(QBrush(QColor(255,255,0)));
    //设置右上角图形标注名称
    ui->widget_2->graph(0)->setName("曲线");
}

void MainWindow::slot_graph()
{
    for(int i=0;i<N;i++)
    {
        y0[i]=in[i][0];
        y1[i]=out[i][0];
    }

    ui->widget->graph(0)->setData(x,y0);
    ui->widget->replot();

    ui->widget_2->graph(0)->setData(x,y1);
    ui->widget_2->replot();
}
           

main.cpp不做解释

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QDebug"

char uart_buf[UART_BUF_SIZE];
uint16_t uart_buf_point=0;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    myserial(new serial)
{
    ui->setupUi(this);

    QStringList baudRate;
    baudRate<<"115200"<<"9600";
    ui->comboBox_2->addItems(baudRate);

    m_thread=new QThread;
    connect(m_thread,&QThread::started,this,
            [this]
    {
        qDebug()<<"thread started";
    },Qt::UniqueConnection);
    connect(m_thread,&QThread::finished,this,
            [this]
    {
        qDebug()<<"thread finished";
    },Qt::UniqueConnection);
    connect(m_thread,&QThread::destroyed,this,
            [this]
    {
        qDebug()<<"thread destroyed";
    },Qt::UniqueConnection);
    m_resolver=new resolver();
    m_resolver->moveToThread(m_thread);
    connect(m_thread, &QThread::finished, this, &QObject::deleteLater,Qt::UniqueConnection);
    connect(m_thread, &QThread::finished, m_thread, &QObject::deleteLater,Qt::UniqueConnection);
    connect(this,&MainWindow::start_resolver,m_resolver,&resolver::do_resolver,Qt::UniqueConnection);
    connect(m_resolver,&resolver::start_graph,this,&MainWindow::slot_graph,Qt::UniqueConnection);
    m_thread->start();
    emit start_resolver();

    //初始化graph
    graph_init();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete myserial;
    delete m_resolver;
    m_thread->quit();
    m_thread->wait();
    delete m_thread;
}

/*搜索串口*/
void MainWindow::on_pushButton_clicked()
{
    QStringList SerialName=myserial->getAvailablePortNameList();

    ui->comboBox->clear();
    ui->comboBox->addItems(SerialName);
}

/*打开/关闭串口*/
void MainWindow::on_pushButton_2_clicked()
{
    if(ui->pushButton_2->text()=="打开串口")
    {
        ui->pushButton_2->setText("关闭串口");
        bool ok;
        myserial->openPort(ui->comboBox->currentText(),
                         ui->comboBox_2->currentText().toInt(&ok,10),
                         QSerialPort::Data8,
                         QSerialPort::NoParity,
                         QSerialPort::OneStop,
                         QSerialPort::NoFlowControl);
        //connect(myserial,&serial::readyRead,this,&MainWindow::readyRead_slot);
        connect(myserial, SIGNAL(readyRead(QByteArray)), this, SLOT(readyRead_slot(QByteArray)),Qt::UniqueConnection);
    }
    else if(ui->pushButton_2->text()=="关闭串口")
    {
        ui->pushButton_2->setText("打开串口");
        myserial->closePort();
        disconnect(myserial, SIGNAL(readyRead(QByteArray)), this, SLOT(readyRead_slot(QByteArray)));
    }
}

/*接收到串口数据处理槽函数*/
void MainWindow::readyRead_slot(QByteArray data)
{
    ui->textBrowser->moveCursor(QTextCursor::End);
    ui->textBrowser->insertPlainText(data);
    char *char_data=data.data();

    if(data.length()+uart_buf_point<=UART_BUF_SIZE)
    {
        memcpy(&(uart_buf[uart_buf_point]),char_data,data.length());
        uart_buf_point+=data.length();
        if(uart_buf_point==UART_BUF_SIZE)
        {
            uart_buf_point=0;
        }
    }
    else
    {
        if(data.length()>UART_BUF_SIZE)
        {
            //qDebug()<<"data.length()>UART_BUF_SIZE";
            return;
        }
        //qDebug()<<"接续存放";
        uint16_t part1_size=UART_BUF_SIZE-uart_buf_point;
        uint16_t part2_size=data.length()-part1_size;
        memcpy(&(uart_buf[uart_buf_point]),char_data,part1_size);
        uart_buf_point=0;
        memcpy(&(uart_buf[uart_buf_point]),char_data+part1_size,part2_size);
        uart_buf_point+=part2_size;
    }
}

//serial窗口的action被点击
void MainWindow::on_actionserial_triggered(bool checked)
{
    if(checked==1){
        ui->dockWidget->show();
    }
    else {
        ui->dockWidget->hide();
    }
}

//dockWidget显示被改变
void MainWindow::on_dockWidget_visibilityChanged(bool visible)
{
    if(!visible)
    {
        ui->menu->actions().at(0)->setChecked(0);
    }
    if(visible)
    {
        ui->menu->actions().at(0)->setChecked(1);
    }
}

void MainWindow::on_pushButton_3_clicked()
{
    QByteArray tmp("12,34,56,78,21,43,65,87",23);
    tmp.append('\n');
    readyRead_slot(tmp);
}
           

qcustomplot.cpp是Qt中用来显示波形的外部库qcustomplot的源码文件

resolver.cpp

#include "resolver.h"
#include "QDebug"
#include "QString"
#include "math.h"

uint16_t counter=0;
fftw_complex *in, *out;//声明复数类型的输入数据in1_c和FFT变换的结果out1_c
uint16_t read_uart_buf_point=0;
extern uint16_t uart_buf_point;
extern char uart_buf[];

resolver::resolver(QObject *parent) : QObject(parent)
{
    in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)* N);//申请动态内存
    out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)* N);
    memset(in,0,sizeof(fftw_complex)* N);
    memset(out,0,sizeof(fftw_complex)* N);
}

resolver::~resolver()
{
    fftw_free(in);
    fftw_free(out);//释放内存
}

void resolver::do_resolver()
{
    uint16_t start=0;
    char *buf;
    uint16_t size=0;

    while(1)
    {
        if(read_uart_buf_point!=uart_buf_point)
        {
            if(uart_buf[read_uart_buf_point]=='\n')
            {
                if(read_uart_buf_point>start)
                {
                    size=read_uart_buf_point+1-start;
                    buf=(char *)malloc(size);
                    memcpy((char *)buf,(char *)&(uart_buf[start]),size);
                }
                else
                {
                    //qDebug()<<"接续读";
                    uint16_t p1_size=UART_BUF_SIZE-start;
                    uint16_t p2_size=read_uart_buf_point+1;
                    size=p1_size+p2_size;
                    buf=(char *)malloc(p1_size+p2_size);
                    memcpy((char *)buf,(char *)&(uart_buf[start]),p1_size);
                    memcpy((char *)buf+p1_size,(char *)&(uart_buf[0]),p2_size);
                }

                do_resolver2(buf,size);
                free(buf);
                start=read_uart_buf_point+1;
                if(start<UART_BUF_SIZE)
                {

                }
                else
                {
                    start=0;
                }
                read_uart_buf_point=start;
            }
            else
            {
                if(read_uart_buf_point<UART_BUF_SIZE)
                {
                    read_uart_buf_point++;
                }
                else
                {
                    //qDebug()<<"read_uart_buf_point=0";
                    read_uart_buf_point=0;
                }
            }
        }
        else
        {
            //qDebug()<<"缓冲区为空";
        }
    }
}

#define FOR_GO(data,cnt)    ((data[cnt]=='0')||(data[cnt]=='1')||(data[cnt]=='2')||(data[cnt]=='3')||(data[cnt]=='4')||(data[cnt]=='5')||(data[cnt]=='6')||(data[cnt]=='7')||(data[cnt]=='8')||(data[cnt]=='9'))
#define FOR_CK(data,cnt)    ((data[cnt]!=',')&&(data[cnt]!='\r')&&(data[cnt]!='\n'))
void resolver::do_resolver2(char *char_data,uint8_t len)
{
    struct channel_data ch1_data,ch2_data;
    int start,next,which=0;
    char *buf=NULL;
    int tmp_value=0;

    QString str;
    for(int i=0;i<len;i++)
    {
        str.append(char_data[i]);
    }
    qDebug()<<str;

    for(start=0;FOR_GO(char_data,start);start=next+1)
    {
        for(next=start;FOR_GO(char_data,next);++next)
        {
            if (next >= len)
            {
                qDebug()<<"too long";
                goto err;
            }
        }

        if(FOR_CK(char_data,next))
        {
            qDebug()<<"check1";
            goto err;
        }
        if(next-start)
        {
            which++;
        }
        else
        {
            qDebug()<<"check2";
            goto err;
        }

        buf=(char *)malloc(next-start+1);
        memcpy((char *)buf,(char *)&(char_data[start]),next-start);
        buf[next-start]='\0';
        tmp_value=atoi(buf);
        //qDebug()<<tmp_value;
        if(buf!=NULL)
        {
            free(buf);
        }

        switch(which)
        {
            case 1:
                ch1_data.average_value=tmp_value;
                break;
            case 2:
                ch1_data.current_value=tmp_value;
                break;
            case 3:
                ch1_data.power=tmp_value;
                break;
            case 4:
                ch1_data.Shredded=tmp_value;
                break;
            case 5:
                ch2_data.average_value=tmp_value;
                break;
            case 6:
                ch2_data.current_value=tmp_value;
                break;
            case 7:
                ch2_data.power=tmp_value;
                break;
            case 8:
                ch2_data.Shredded=tmp_value;
                if((char_data[next]=='\r')||(char_data[next]=='\n'))
                {
                    goto ok;
                }
                else
                {
                    qDebug()<<"8";
                    goto err;
                }
                break;
        }
    }
    qDebug()<<"?";

    err:
    qDebug()<<"err";
    if(counter==0)
    {

    }
    if(counter>=N)
    {
        counter=0;
    }
    else
    {
        in[counter][0]=in[counter-1][0];
        //qDebug()<<in[counter][0];
        counter++;
    }
    goto fft;

    ok:
    if(counter==0)
    {
        in[counter][0]=ch1_data.current_value;
        //qDebug()<<in[counter][0];
        counter++;
    }
    if(counter>=N)
    {
        counter=0;
        in[counter][0]=ch1_data.current_value;
        //qDebug()<<in[counter][0];
        counter++;
    }
    else
    {
        in[counter][0]=ch1_data.current_value;
        //qDebug()<<in[counter][0];
        counter++;
    }
    goto fft;

    fft:
    if(counter==N)
    {
        do_fft();
    }
}

void resolver::do_fft()
{
    fftw_plan p;//声明变换策略

//    for(int i=0;i<N/4;i++)
//    {
//        in[4*i][0]=1;
//        in[4*i+1][0]=0;
//        in[4*i+2][0]=-1;
//        in[4*i+3][0]=0;
//    }

    p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);//返回变换策略
    fftw_execute(p);//执行变换
    fftw_destroy_plan(p);//销毁策略
    out[0][0]=out[0][0]/N;

    for(int i=1;i<N;i++)
    {
        out[i][0]=sqrt(out[i][0]*out[i][0]+out[i][1]*out[i][1])/(N/2);
    }

//    for (int i = 0; i < N; i++)
//    {
//         qDebug()<<i<<"= "<<out[i][0]<<out[i][1];
//    }
    emit start_graph();
}
           

serial.cpp

#include "serial.h"
#include "QDebug"

serial::serial(QObject *o)
{
    m_port = new QSerialPort();

    connect(m_port,&QSerialPort::readyRead,this,&serial::readyRead_slot,Qt::UniqueConnection);

}

serial::~serial()
{
    delete m_port;
    disconnect(m_port,&QSerialPort::readyRead,this,&serial::readyRead_slot);
}

/**
* @functionName   openPort
* @brief         打开串口 根据名字 波特率 数据位 校验位 停止位 流控制
* @return        打开成功还是失败
*/
bool serial::openPort(QString portName, qint32 baudRate,
                         QSerialPort::DataBits dataBits, QSerialPort::Parity parity,
                           QSerialPort::StopBits stopBits, QSerialPort::FlowControl flowControl)
{

    m_port->setPortName(portName);
    m_port->setBaudRate(baudRate);
    m_port->setDataBits(dataBits);
    m_port->setParity(parity);
    m_port->setStopBits(stopBits);
    m_port->setFlowControl(flowControl);

    if(m_port->open(QIODevice::ReadWrite))
    {
        return true;
    }
    else
        return false;
}

/**
* @projectName   testMyClass
* @brief         是否打开串口
*/
bool serial::isOpen()
{
    return m_port->isOpen();
}

/**
* @projectName   testMyClass
* @brief         关闭串口
*/
void serial::closePort()
{
    m_port->clear();
    m_port->close();
}

/**
* @projectName   testMyClass
* @brief         获取可用的串口名字
*/
QStringList serial::getAvailablePortNameList()
{
    QStringList portName;
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        portName<<info.portName();

    }
    return portName;
}

/**
* @projectName   testMyClass
* @brief         串口写数据
*/
void serial::write(QString str)
{
    m_port->write(str.toUtf8().data());
}

void serial::write(const char *data, qint64 len)
{
    m_port->write(data,len);
}

/**
* @projectName   testMyClass
* @brief         QByteArray 转换为 QString
*/
QString serial::ByteArrayToHexString(QByteArray data)
{
    QString ret(data.toHex().toUpper());
    int len = ret.length()/2;
    for(int i=1;i<len;i++)
    {
        ret.insert(2*i+i-1," ");
    }

    return ret;
}

/**
* @projectName   testMyClass
* @brief         串口读取到数据的信号
*/
void serial::readyRead_slot()
{
    QByteArray data = m_port->readAll();
    emit readyRead(data);
}
           

ui_mainwindow.h

/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.12.3
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDockWidget>
#include <QtWidgets/QFrame>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QTextBrowser>
#include <QtWidgets/QToolBar>
#include <QtWidgets/QWidget>
#include <qcustomplot.h>

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
public:
    QAction *actionserial;
    QWidget *centralWidget;
    QGridLayout *gridLayout;
    QFrame *line;
    QCustomPlot *widget;
    QCustomPlot *widget_2;
    QMenuBar *menuBar;
    QMenu *menu;
    QToolBar *mainToolBar;
    QDockWidget *dockWidget;
    QWidget *dockWidgetContents_6;
    QGridLayout *gridLayout_2;
    QTabWidget *tabWidget;
    QWidget *tab_3;
    QGridLayout *gridLayout_4;
    QTextBrowser *textBrowser;
    QLineEdit *lineEdit;
    QComboBox *comboBox;
    QComboBox *comboBox_2;
    QPushButton *pushButton;
    QPushButton *pushButton_2;
    QPushButton *pushButton_3;
    QWidget *tab_4;
    QGridLayout *gridLayout_5;
    QTextBrowser *textBrowser_2;
    QStatusBar *statusBar;

    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
        MainWindow->resize(676, 502);
        MainWindow->setStyleSheet(QString::fromUtf8("#MainWindow\n"
"{\n"
"background-color: rgb(60, 60, 60);\n"
"}"));
        actionserial = new QAction(MainWindow);
        actionserial->setObjectName(QString::fromUtf8("actionserial"));
        actionserial->setCheckable(true);
        actionserial->setChecked(false);
        centralWidget = new QWidget(MainWindow);
        centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
        centralWidget->setStyleSheet(QString::fromUtf8("#centralWidget\n"
"{\n"
"background-color: rgb(255, 255, 255);\n"
"}"));
        gridLayout = new QGridLayout(centralWidget);
        gridLayout->setSpacing(6);
        gridLayout->setContentsMargins(11, 11, 11, 11);
        gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
        line = new QFrame(centralWidget);
        line->setObjectName(QString::fromUtf8("line"));
        line->setStyleSheet(QString::fromUtf8("background-color: rgb(0, 0, 0);"));
        line->setFrameShape(QFrame::VLine);
        line->setFrameShadow(QFrame::Sunken);

        gridLayout->addWidget(line, 1, 2, 3, 1);

        widget = new QCustomPlot(centralWidget);
        widget->setObjectName(QString::fromUtf8("widget"));
        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        sizePolicy.setHeightForWidth(widget->sizePolicy().hasHeightForWidth());
        widget->setSizePolicy(sizePolicy);
        widget->setCursor(QCursor(Qt::CrossCursor));
        widget->setAutoFillBackground(false);
        widget->setStyleSheet(QString::fromUtf8(""));

        gridLayout->addWidget(widget, 1, 0, 2, 1);

        widget_2 = new QCustomPlot(centralWidget);
        widget_2->setObjectName(QString::fromUtf8("widget_2"));
        sizePolicy.setHeightForWidth(widget_2->sizePolicy().hasHeightForWidth());
        widget_2->setSizePolicy(sizePolicy);
        widget_2->setCursor(QCursor(Qt::CrossCursor));
        widget_2->setAutoFillBackground(false);
        widget_2->setStyleSheet(QString::fromUtf8(""));

        gridLayout->addWidget(widget_2, 3, 0, 2, 1);

        MainWindow->setCentralWidget(centralWidget);
        menuBar = new QMenuBar(MainWindow);
        menuBar->setObjectName(QString::fromUtf8("menuBar"));
        menuBar->setGeometry(QRect(0, 0, 676, 23));
        menuBar->setCursor(QCursor(Qt::PointingHandCursor));
        menuBar->setStyleSheet(QString::fromUtf8("#menuBar\n"
"{\n"
"background-color: rgb(200, 200, 200);\n"
"}"));
        menu = new QMenu(menuBar);
        menu->setObjectName(QString::fromUtf8("menu"));
        MainWindow->setMenuBar(menuBar);
        mainToolBar = new QToolBar(MainWindow);
        mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
        mainToolBar->setMinimumSize(QSize(0, 30));
        mainToolBar->setCursor(QCursor(Qt::PointingHandCursor));
        mainToolBar->setAutoFillBackground(false);
        mainToolBar->setStyleSheet(QString::fromUtf8("color: rgb(255, 255, 255);\n"
"mainToolBar{background-color: rgb(60, 60, 60);};"));
        MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
        dockWidget = new QDockWidget(MainWindow);
        dockWidget->setObjectName(QString::fromUtf8("dockWidget"));
        dockWidget->setEnabled(true);
        dockWidget->setFocusPolicy(Qt::NoFocus);
        dockWidget->setContextMenuPolicy(Qt::DefaultContextMenu);
        dockWidget->setAcceptDrops(false);
        dockWidget->setLayoutDirection(Qt::LeftToRight);
        dockWidget->setAutoFillBackground(false);
        dockWidget->setStyleSheet(QString::fromUtf8("#dockWidget\n"
"{\n"
"background-color: rgb(60, 60, 60);\n"
"	color: rgb(255, 255, 255);\n"
"}"));
        dockWidget->setFeatures(QDockWidget::DockWidgetClosable);
        dockWidget->setAllowedAreas(Qt::BottomDockWidgetArea);
        dockWidgetContents_6 = new QWidget();
        dockWidgetContents_6->setObjectName(QString::fromUtf8("dockWidgetContents_6"));
        dockWidgetContents_6->setStyleSheet(QString::fromUtf8("#dockWidgetContents_6\n"
"{\n"
"background-color: rgb(60, 60, 60);\n"
"}"));
        gridLayout_2 = new QGridLayout(dockWidgetContents_6);
        gridLayout_2->setSpacing(6);
        gridLayout_2->setContentsMargins(11, 11, 11, 11);
        gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2"));
        gridLayout_2->setContentsMargins(-1, -1, -1, 0);
        tabWidget = new QTabWidget(dockWidgetContents_6);
        tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
        tabWidget->setAutoFillBackground(false);
        tabWidget->setStyleSheet(QString::fromUtf8(""));
        tabWidget->setTabPosition(QTabWidget::South);
        tabWidget->setTabShape(QTabWidget::Rounded);
        tabWidget->setElideMode(Qt::ElideNone);
        tabWidget->setTabBarAutoHide(false);
        tab_3 = new QWidget();
        tab_3->setObjectName(QString::fromUtf8("tab_3"));
        tab_3->setStyleSheet(QString::fromUtf8(""));
        gridLayout_4 = new QGridLayout(tab_3);
        gridLayout_4->setSpacing(6);
        gridLayout_4->setContentsMargins(11, 11, 11, 11);
        gridLayout_4->setObjectName(QString::fromUtf8("gridLayout_4"));
        textBrowser = new QTextBrowser(tab_3);
        textBrowser->setObjectName(QString::fromUtf8("textBrowser"));
        textBrowser->setEnabled(true);
        sizePolicy.setHeightForWidth(textBrowser->sizePolicy().hasHeightForWidth());
        textBrowser->setSizePolicy(sizePolicy);
        textBrowser->setMinimumSize(QSize(0, 20));
        textBrowser->viewport()->setProperty("cursor", QVariant(QCursor(Qt::IBeamCursor)));

        gridLayout_4->addWidget(textBrowser, 0, 0, 1, 5);

        lineEdit = new QLineEdit(tab_3);
        lineEdit->setObjectName(QString::fromUtf8("lineEdit"));

        gridLayout_4->addWidget(lineEdit, 1, 0, 1, 5);

        comboBox = new QComboBox(tab_3);
        comboBox->setObjectName(QString::fromUtf8("comboBox"));
        sizePolicy.setHeightForWidth(comboBox->sizePolicy().hasHeightForWidth());
        comboBox->setSizePolicy(sizePolicy);

        gridLayout_4->addWidget(comboBox, 2, 0, 1, 1);

        comboBox_2 = new QComboBox(tab_3);
        comboBox_2->setObjectName(QString::fromUtf8("comboBox_2"));
        sizePolicy.setHeightForWidth(comboBox_2->sizePolicy().hasHeightForWidth());
        comboBox_2->setSizePolicy(sizePolicy);

        gridLayout_4->addWidget(comboBox_2, 2, 1, 1, 1);

        pushButton = new QPushButton(tab_3);
        pushButton->setObjectName(QString::fromUtf8("pushButton"));
        sizePolicy.setHeightForWidth(pushButton->sizePolicy().hasHeightForWidth());
        pushButton->setSizePolicy(sizePolicy);

        gridLayout_4->addWidget(pushButton, 2, 2, 1, 1);

        pushButton_2 = new QPushButton(tab_3);
        pushButton_2->setObjectName(QString::fromUtf8("pushButton_2"));
        sizePolicy.setHeightForWidth(pushButton_2->sizePolicy().hasHeightForWidth());
        pushButton_2->setSizePolicy(sizePolicy);

        gridLayout_4->addWidget(pushButton_2, 2, 3, 1, 1);

        pushButton_3 = new QPushButton(tab_3);
        pushButton_3->setObjectName(QString::fromUtf8("pushButton_3"));
        sizePolicy.setHeightForWidth(pushButton_3->sizePolicy().hasHeightForWidth());
        pushButton_3->setSizePolicy(sizePolicy);

        gridLayout_4->addWidget(pushButton_3, 2, 4, 1, 1);

        tabWidget->addTab(tab_3, QString());
        tab_4 = new QWidget();
        tab_4->setObjectName(QString::fromUtf8("tab_4"));
        gridLayout_5 = new QGridLayout(tab_4);
        gridLayout_5->setSpacing(6);
        gridLayout_5->setContentsMargins(11, 11, 11, 11);
        gridLayout_5->setObjectName(QString::fromUtf8("gridLayout_5"));
        textBrowser_2 = new QTextBrowser(tab_4);
        textBrowser_2->setObjectName(QString::fromUtf8("textBrowser_2"));

        gridLayout_5->addWidget(textBrowser_2, 0, 0, 1, 1);

        tabWidget->addTab(tab_4, QString());

        gridLayout_2->addWidget(tabWidget, 1, 0, 1, 1);

        dockWidget->setWidget(dockWidgetContents_6);
        MainWindow->addDockWidget(static_cast<Qt::DockWidgetArea>(8), dockWidget);
        statusBar = new QStatusBar(MainWindow);
        statusBar->setObjectName(QString::fromUtf8("statusBar"));
        statusBar->setMinimumSize(QSize(0, 20));
        statusBar->setStyleSheet(QString::fromUtf8("color: rgb(255, 255, 255);"));
        MainWindow->setStatusBar(statusBar);

        menuBar->addAction(menu->menuAction());
        menu->addAction(actionserial);
        mainToolBar->addAction(actionserial);
        mainToolBar->addSeparator();

        retranslateUi(MainWindow);

        tabWidget->setCurrentIndex(0);


        QMetaObject::connectSlotsByName(MainWindow);
    } // setupUi

    void retranslateUi(QMainWindow *MainWindow)
    {
        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "\344\270\273\351\241\265", nullptr));
        actionserial->setText(QApplication::translate("MainWindow", "serial", nullptr));
#ifndef QT_NO_STATUSTIP
        actionserial->setStatusTip(QApplication::translate("MainWindow", "\344\270\262\345\217\243\347\252\227\345\217\243", nullptr));
#endif // QT_NO_STATUSTIP
        menu->setTitle(QApplication::translate("MainWindow", "\347\252\227\345\217\243", nullptr));
#ifndef QT_NO_WHATSTHIS
        dockWidget->setWhatsThis(QApplication::translate("MainWindow", "<html><head/><body><p><br/></p></body></html>", nullptr));
#endif // QT_NO_WHATSTHIS
#ifndef QT_NO_ACCESSIBILITY
        dockWidget->setAccessibleName(QString());
#endif // QT_NO_ACCESSIBILITY
        dockWidget->setWindowTitle(QApplication::translate("MainWindow", "serial", nullptr));
        textBrowser->setHtml(QApplication::translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>", nullptr));
        pushButton->setText(QApplication::translate("MainWindow", "\346\220\234\347\264\242\344\270\262\345\217\243", nullptr));
        pushButton_2->setText(QApplication::translate("MainWindow", "\346\211\223\345\274\200\344\270\262\345\217\243", nullptr));
        pushButton_3->setText(QApplication::translate("MainWindow", "\345\217\221\351\200\201", nullptr));
        tabWidget->setTabText(tabWidget->indexOf(tab_3), QApplication::translate("MainWindow", "serial", nullptr));
        tabWidget->setTabText(tabWidget->indexOf(tab_4), QApplication::translate("MainWindow", "   \345\272\224\347\224\250\347\250\213\345\272\217\350\276\223\345\207\272", nullptr));
    } // retranslateUi

};

namespace Ui {
    class MainWindow: public Ui_MainWindow {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MAINWINDOW_H
           

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>676</width>
    <height>502</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>主页</string>
  </property>
  <property name="styleSheet">
   <string notr="true">#MainWindow
{
background-color: rgb(60, 60, 60);
}</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <property name="styleSheet">
    <string notr="true">#centralWidget
{
background-color: rgb(255, 255, 255);
}</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="1" column="2" rowspan="3">
     <widget class="Line" name="line">
      <property name="styleSheet">
       <string notr="true">background-color: rgb(0, 0, 0);</string>
      </property>
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
     </widget>
    </item>
    <item row="1" column="0" rowspan="2">
     <widget class="QCustomPlot" name="widget" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="cursor">
       <cursorShape>CrossCursor</cursorShape>
      </property>
      <property name="autoFillBackground">
       <bool>false</bool>
      </property>
      <property name="styleSheet">
       <string notr="true"/>
      </property>
     </widget>
    </item>
    <item row="3" column="0" rowspan="2">
     <widget class="QCustomPlot" name="widget_2" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="cursor">
       <cursorShape>CrossCursor</cursorShape>
      </property>
      <property name="autoFillBackground">
       <bool>false</bool>
      </property>
      <property name="styleSheet">
       <string notr="true"/>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>676</width>
     <height>23</height>
    </rect>
   </property>
   <property name="cursor">
    <cursorShape>PointingHandCursor</cursorShape>
   </property>
   <property name="styleSheet">
    <string notr="true">#menuBar
{
background-color: rgb(200, 200, 200);
}</string>
   </property>
   <widget class="QMenu" name="menu">
    <property name="title">
     <string>窗口</string>
    </property>
    <addaction name="actionserial"/>
   </widget>
   <addaction name="menu"/>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <property name="minimumSize">
    <size>
     <width>0</width>
     <height>30</height>
    </size>
   </property>
   <property name="cursor">
    <cursorShape>PointingHandCursor</cursorShape>
   </property>
   <property name="autoFillBackground">
    <bool>false</bool>
   </property>
   <property name="styleSheet">
    <string notr="true">color: rgb(255, 255, 255);
mainToolBar{background-color: rgb(60, 60, 60);};</string>
   </property>
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
   <addaction name="actionserial"/>
   <addaction name="separator"/>
  </widget>
  <widget class="QDockWidget" name="dockWidget">
   <property name="enabled">
    <bool>true</bool>
   </property>
   <property name="focusPolicy">
    <enum>Qt::NoFocus</enum>
   </property>
   <property name="contextMenuPolicy">
    <enum>Qt::DefaultContextMenu</enum>
   </property>
   <property name="acceptDrops">
    <bool>false</bool>
   </property>
   <property name="whatsThis">
    <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
   </property>
   <property name="accessibleName">
    <string/>
   </property>
   <property name="layoutDirection">
    <enum>Qt::LeftToRight</enum>
   </property>
   <property name="autoFillBackground">
    <bool>false</bool>
   </property>
   <property name="styleSheet">
    <string notr="true">#dockWidget
{
background-color: rgb(60, 60, 60);
	color: rgb(255, 255, 255);
}</string>
   </property>
   <property name="features">
    <set>QDockWidget::DockWidgetClosable</set>
   </property>
   <property name="allowedAreas">
    <set>Qt::BottomDockWidgetArea</set>
   </property>
   <property name="windowTitle">
    <string>serial</string>
   </property>
   <attribute name="dockWidgetArea">
    <number>8</number>
   </attribute>
   <widget class="QWidget" name="dockWidgetContents_6">
    <property name="styleSheet">
     <string notr="true">#dockWidgetContents_6
{
background-color: rgb(60, 60, 60);
}</string>
    </property>
    <layout class="QGridLayout" name="gridLayout_2">
     <property name="bottomMargin">
      <number>0</number>
     </property>
     <item row="1" column="0">
      <widget class="QTabWidget" name="tabWidget">
       <property name="autoFillBackground">
        <bool>false</bool>
       </property>
       <property name="styleSheet">
        <string notr="true"/>
       </property>
       <property name="tabPosition">
        <enum>QTabWidget::South</enum>
       </property>
       <property name="tabShape">
        <enum>QTabWidget::Rounded</enum>
       </property>
       <property name="currentIndex">
        <number>0</number>
       </property>
       <property name="elideMode">
        <enum>Qt::ElideNone</enum>
       </property>
       <property name="tabBarAutoHide">
        <bool>false</bool>
       </property>
       <widget class="QWidget" name="tab_3">
        <property name="styleSheet">
         <string notr="true"/>
        </property>
        <attribute name="title">
         <string>serial</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_4">
         <item row="0" column="0" colspan="5">
          <widget class="QTextBrowser" name="textBrowser">
           <property name="enabled">
            <bool>true</bool>
           </property>
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="minimumSize">
            <size>
             <width>0</width>
             <height>20</height>
            </size>
           </property>
           <property name="cursor" stdset="0">
            <cursorShape>IBeamCursor</cursorShape>
           </property>
           <property name="html">
            <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
           </property>
          </widget>
         </item>
         <item row="1" column="0" colspan="5">
          <widget class="QLineEdit" name="lineEdit"/>
         </item>
         <item row="2" column="0">
          <widget class="QComboBox" name="comboBox">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
          </widget>
         </item>
         <item row="2" column="1">
          <widget class="QComboBox" name="comboBox_2">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
          </widget>
         </item>
         <item row="2" column="2">
          <widget class="QPushButton" name="pushButton">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>搜索串口</string>
           </property>
          </widget>
         </item>
         <item row="2" column="3">
          <widget class="QPushButton" name="pushButton_2">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>打开串口</string>
           </property>
          </widget>
         </item>
         <item row="2" column="4">
          <widget class="QPushButton" name="pushButton_3">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>发送</string>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
       <widget class="QWidget" name="tab_4">
        <attribute name="title">
         <string>   应用程序输出</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_5">
         <item row="0" column="0">
          <widget class="QTextBrowser" name="textBrowser_2"/>
         </item>
        </layout>
       </widget>
      </widget>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QStatusBar" name="statusBar">
   <property name="minimumSize">
    <size>
     <width>0</width>
     <height>20</height>
    </size>
   </property>
   <property name="styleSheet">
    <string notr="true">color: rgb(255, 255, 255);</string>
   </property>
  </widget>
  <action name="actionserial">
   <property name="checkable">
    <bool>true</bool>
   </property>
   <property name="checked">
    <bool>false</bool>
   </property>
   <property name="text">
    <string>serial</string>
   </property>
   <property name="statusTip">
    <string>串口窗口</string>
   </property>
  </action>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <customwidgets>
  <customwidget>
   <class>QCustomPlot</class>
   <extends>QWidget</extends>
   <header location="global">qcustomplot.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>
           

 界面图片

Qt平台使用fft解析肌电传感器信号

继续阅读