QT学习过程及简单串口和UDP通信demo开发过程及代码分享

写在前面

Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,很容易扩展,允许真正地组件编程。Qt是同Windows平台上的 MFC,WPF,ATL等是同类型的东西。

以前接触过一点MFC,也用MFC写过一些简单的界面程序,所以就和MFC做一些对比。和QT对比起来,从学习者的角度,笔者感觉QT更加容易掌握,当然这可能与有MFC的基础有关系。不过确实QT的帮助系统做的比MFC(或者直接说VS)比较好,随时随地F1就可以,尽管这需要一点英文水平。另外,QT比MFC在消息和事件处理方面的更加容易理解,QT的信号和槽简直就是天才的想法。

笔者接触QT不到半个月,但凭着c++和MFC的基础,已经对QT整体有了一个大体的把握。笔者一直认为,学习东西要奔着需求才能进步神速。别人出国半年就比你国内十几年英语水平高,你不学你出门连酱油都打不了。目前正需要做一个可以同时和串口,UDP通信的demo,借着这个机会就去了解了下QT。

笔者的学习过程大概是这样的。第一步,把QT的帮助文档目录浏览一遍,重点看了界面,信号和槽,串口和UDP部分。第二步,在界面上增加各种控件并进行布局,熟悉各种控件的功能和属性,控件布局。比如说在文本框打印著名的“hello world”。第三步,设计子对话框并能在主界面打开,在子对话框上增加控件并布局,主要是串口号,波特率,UDP端口这些。第四步,实现串口和UDP的通信,可以搜到现成的代码块,这时需要边看帮助文档边写代码边上网查。第五步,代码重组,把面向过程的代码封装成类。界面美化,加背景,控件加图标等等。等把这个demo做出来时,笔者的QT大概入门了。

在做这个demo的时候,参考了论坛360safe的一个demo程序和qgc地面站(开源),还有串口猎人的程序(没有源码,只看界面),在这里进行感谢。笔者把做好的demo程序也放到论坛上,希望更多的人可以学习,也欢迎留言,如果有什么不对的地方还欢迎批评指正,共同进步。

DEMO效果展示

中文和英文切换:

这里写图片描述

这里写图片描述

子对话框(有点违和,是后来强行加进去的):

这里写图片描述

不同背景:

这里写图片描述

这里写图片描述

代码片段及注释

这部分主要是给笔者自己看的。笔者有个习惯(强迫症),喜欢删东西。所以做完的东西总是喜欢传到网上(当然是那种能公开的东西)。

中英文切换

//main.cpp
QTranslator translator_ch;
translator_ch.load(QString(":/qm/TripleDebugger.qm"));
app.installTranslator(&translator_ch);


//mainwindow.cpp
if(current_language == UI_ZH)
{
    current_language = UI_EN;
}
else if(current_language == UI_EN)
{
    current_language = UI_ZH;
}

switch(current_language)
{
case UI_EN:
    translator->load(QString(":/qm/TripleDebugger2.qm"));
    break;

case UI_ZH:
    translator->load(QString(":/qm/TripleDebugger.qm"));
    break;
}

title_widget->translateLanguage();
setup_widget->setupserialwidget->controltranslate();
setup_widget->setuptcpwidge->controltranslate();
setup_widget->setupudpwidge->controltranslate();
sendandreceive_widget->controltranslate();
advancereceive_widget->controltranslate();
advancesend_widget->controltranslate();

多界面重叠切换

statked_widget = new QStackedWidget();
statked_widget->addWidget(setup_widget);
statked_widget->addWidget(sendandreceive_widget);
statked_widget->addWidget(advancereceive_widget);
statked_widget->addWidget(advancesend_widget);
statked_widget->addWidget(readme_widget);
statked_widget->addWidget(about_widget);
statked_widget->setContentsMargins(0,0,0,0);

界面布局

int space = 10;
QVBoxLayout *v_label_layout = new QVBoxLayout();
v_label_layout->addWidget(labelserialnumber);
v_label_layout->addSpacing(space);
v_label_layout->addWidget(labelbaudrate);
v_label_layout->addSpacing(space);
v_label_layout->addWidget(labeldatabit);
v_label_layout->addSpacing(space);
v_label_layout->addWidget(labelstopbit);
v_label_layout->addSpacing(space);
v_label_layout->addWidget(labelparitybit);
v_label_layout->addSpacing(space);
v_label_layout->addWidget(pushButtonconnect);

QVBoxLayout *v_combobox_layout = new QVBoxLayout();
v_combobox_layout->addWidget(comboBoxserialnumber);
v_combobox_layout->addSpacing(space);
v_combobox_layout->addWidget(comboBoxbaudrate);
v_combobox_layout->addSpacing(space);
v_combobox_layout->addWidget(comboBoxdatabit);
v_combobox_layout->addSpacing(space);
v_combobox_layout->addWidget(comboBoxstopbit);
v_combobox_layout->addSpacing(space);
v_combobox_layout->addWidget(comboBoxparitybit);
v_combobox_layout->addSpacing(space);
v_combobox_layout->addWidget(pushButtondisconnect);

QHBoxLayout *h_setserial_layout = new QHBoxLayout();
h_setserial_layout->addLayout(v_label_layout);
h_setserial_layout->addLayout(v_combobox_layout);

QVBoxLayout *v_setserial_layout = new QVBoxLayout();
v_setserial_layout->addLayout(h_setserial_layout);
v_setserial_layout->addSpacing(space);
v_setserial_layout->addWidget(radioButtonintroduction);
v_setserial_layout->addSpacing(space);
v_setserial_layout->addWidget(radioButtonhardware);
v_setserial_layout->addSpacing(space);
v_setserial_layout->addWidget(radioButtonprotocol);
v_setserial_layout->addSpacing(space);
v_setserial_layout->addWidget(radioButtonasci);
v_setserial_layout->addStretch();

QHBoxLayout *main_layout = new QHBoxLayout();
main_layout->addLayout(v_setserial_layout);
main_layout->addWidget(textEdit);

setLayout(main_layout);

radioButtonintroduction->setChecked(true);
show_introduction();

串口打开

//信号和槽,把按钮和函数连接起来
connect(pushButtonconnect,SIGNAL(clicked(bool)),this,SLOT(connectserial()));

//类成员槽函数
private slots:
    void connectserial();
    void disconnectserial();

//槽函数
void SetupSerialWidge::connectserial()
{
    serialport->setPortName(getportnumber());
    serialport->setBaudRate(getportbaudrate());
    serialport->setDataBits(getportdatabit());
    serialport->setParity(getportparitybit());
    serialport->setStopBits(getportstopbit());

    if(!serialport->isOpen())
    {
        serialport->setFlowControl(QSerialPort::NoFlowControl);
        if(serialport->open(QIODevice::ReadWrite))
        {
            qDebug() << "successful open serial"<< serialport->portName();
            pushButtonconnect->setEnabled(false);
            pushButtondisconnect->setEnabled(true);
        }
        else
        {
            qDebug() << "failed";
        }

    }
    else
    {
        qDebug() << "the serial port already open";
    }
}

串口关闭

//信号和槽,把按钮和函数连接起来
connect(pushButtondisconnect,SIGNAL(clicked(bool)),this,SLOT(disconnectserial()));
//类成员槽函数
private slots:
    void connectserial();
    void disconnectserial();
//槽函数
void SetupSerialWidge::disconnectserial()
{
    if(serialport->isOpen())
    {
        serialport->close();
        pushButtonconnect->setEnabled(true);
        pushButtondisconnect->setEnabled(false);
    }
    else
    {
        qDebug() << "the serial port already closed";
    }
}

串口和UDP发送

if(tool == SERIAL)
{
    if(issendstr)   //以字符串方式发送
    {
        QString str = textEditsend->toPlainText();
        QByteArray ba = str.toLatin1();
        serialport->write(ba);
    }
    else//以十六进制方式发送
    {
        QString str = textEditsend->toPlainText();//从LineEdit得到字符串
        QByteArray ba;
        StringToHex(str,ba);//将str字符串转换为16进制的形式
        serialport->write(ba);//发送到串口
    }
}
if(tool == UDP)
{
    if(issendstr)   //以字符串方式发送
    {
        QString str = textEditsend->toPlainText();
        QByteArray ba = str.toLatin1();
        udp.udp_socket_tx->writeDatagram(ba, ba.size(), udp.IP_remote, udp.Port_Tx);
    }
    else//以十六进制方式发送
    {
        QString str = textEditsend->toPlainText();//从LineEdit得到字符串
        QByteArray ba;
        StringToHex(str,ba);//将str字符串转换为16进制的形式
        udp.udp_socket_tx->writeDatagram(ba, ba.size(), udp.IP_remote, udp.Port_Tx);
    }
}

串口接收

//serialport
connect(serialport,SIGNAL(readyRead()),this,SLOT(on_serialdataready()));

public slots:
    //serial
    void on_serialdataready();

void SendandReceiveWidge::on_serialdataready()
{
    if(mode == SENDANDRECEIVE)
    {
        if(isreceivestr)
        {
            QByteArray requestData;
            requestData = serialport->readAll();  //读取数据  串口读取出来的数据类型 是QByteArray 不是QString
            textEditreceive->insertPlainText(requestData);
            textEditreceive->moveCursor(QTextCursor::End);
        }
        else
        {
            QByteArray requestData = serialport->readAll();

            //显示到文本框
            QDataStream out(&requestData,QIODevice::ReadWrite);    //将字节数组读入
            QString temp;
            while(!out.atEnd())
            {
                qint8 outChar = 0;
                out>>outChar;   //每字节填充一次,直到结束
                //十六进制的转换
                QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
                temp+=str.toUpper()+" ";
            }
            textEditreceive->insertPlainText(temp);//每发送两个字符后添加一个空格
            textEditreceive->moveCursor(QTextCursor::End);
            if(textEditreceive->toPlainText().length()>30000)
            {
                textEditreceive->clear();
            }
        }
    }
}

UDP接收

connect(udp.udp_socket_rx, SIGNAL(readyRead()),this, SLOT(on_udpdataready()));

public slots:
    //udp
    void on_udpdataready();

void SendandReceiveWidge::on_udpdataready()
{
    if(mode == SENDANDRECEIVE)
    {
        if(isreceivestr)
        {
            QByteArray requestData;
            while(udp.udp_socket_rx->hasPendingDatagrams())
            {
                requestData.resize(udp.udp_socket_rx->pendingDatagramSize());
                QHostAddress sender;
                quint16 senderPort;
                udp.udp_socket_rx->readDatagram(requestData.data(), requestData.size(), &sender, &senderPort);
                textEditreceive->insertPlainText(requestData);
                textEditreceive->moveCursor(QTextCursor::End);
            }
        }
        else
        {
            QByteArray requestData;
            QString templager;
            while(udp.udp_socket_rx->hasPendingDatagrams())
            {
                requestData.resize(udp.udp_socket_rx->pendingDatagramSize());
                QHostAddress sender;
                quint16 senderPort;
                udp.udp_socket_rx->readDatagram(requestData.data(), requestData.size(), &sender, &senderPort);

                //显示到文本框
                QDataStream out(&requestData,QIODevice::ReadWrite);    //将字节数组读入
                QString temp;
                while(!out.atEnd())
                {
                    qint8 outChar = 0;
                    out>>outChar;   //每字节填充一次,直到结束
                    //十六进制的转换
                    QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
                    temp+=str.toUpper()+" ";
                }
                templager+=temp;
            }

            textEditreceive->insertPlainText(templager);//每发送两个字符后添加一个空格
            textEditreceive->moveCursor(QTextCursor::End);
            if(textEditreceive->toPlainText().length()>30000)
            {
                textEditreceive->clear();
            }
        }
    }
    else       //必须有,猜测可能是udp缓存区有数如果不读,udp对象内部会崩溃但是程序不会报错。
    {
        QByteArray requestData;
        while(udp.udp_socket_rx->hasPendingDatagrams())
        {
            requestData.resize(udp.udp_socket_rx->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;
            udp.udp_socket_rx->readDatagram(requestData.data(), requestData.size(), &sender, &senderPort);
        }
    }
}

数据解析

void AdvancedReceiveWidge::on_serialdataready()
{
    if(mode == ADVANCEDRECEIVE && isrun)
    {
        QByteArray requestData = serialport->readAll();
        QDataStream out(&requestData,QIODevice::ReadWrite);    //将字节数组读入
        while(!out.atEnd())
        {
            qint8 outChar = 0;
            out>>outChar;   //每字节填充一次,直到结束
            //十六进制的转换
            QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
            if(str == header||str.toUpper()==header)
            {
                count = 0;
                start = 1;
            }
            if(start == 1)
            {
                tempdata[count++] = str;
            }
            if(count == length && str == tail)
            {
                start = 0;
                info(tempdata);
            }
        }
    }
}

void AdvancedReceiveWidge::info(QString *tempdata)
{
    int x1,x2,x3,x4;
    bool ok;
    QString xx[4];
    xx[0] = tempdata[1]+tempdata[2];xx[1] = tempdata[3]+tempdata[4];
    xx[2] = tempdata[5]+tempdata[6];xx[3] = tempdata[7]+tempdata[8];
    x1 = xx[0].toInt(&ok,16);
    x2 = xx[1].toInt(&ok,16);
    x3 = xx[2].toInt(&ok,16);
    x4 = xx[3].toInt(&ok,16);
    QString show1 = QString::number(x1);QString show2 = QString::number(x2);QString show3 = QString::number(x3);QString show4 = QString::number(x4);
    QString show = show1+" "+show2+" "+show3+" "+show4+" ";
    textEdit->insertPlainText(show);
    textEdit->insertPlainText("\n");
    textEdit->setTextCursor(cursor);
}

void AdvancedReceiveWidge::StringToHex(QString str, QByteArray &senddata)
{
    int hexdata,lowhexdata;
    int hexdatalen = 0;
    int len = str.length();
    senddata.resize(len/2);
    char lstr,hstr;
    for(int i=0; i<len; )
    {
        //char lstr,
        hstr=str[i].toLatin1();
        if(hstr == ' ')
        {
            i++;
            continue;
        }
        i++;
        if(i >= len)
            break;
        lstr = str[i].toLatin1();
        hexdata = ConvertHexChar(hstr);
        lowhexdata = ConvertHexChar(lstr);
        if((hexdata == 16) || (lowhexdata == 16))
            break;
        else
            hexdata = hexdata*16+lowhexdata;
        i++;
        senddata[hexdatalen] = (char)hexdata;
        hexdatalen++;
    }
    senddata.resize(hexdatalen);
}

char AdvancedReceiveWidge::ConvertHexChar(char ch)
{
    if((ch >= '0') && (ch <= '9'))
            return ch-0x30;
        else if((ch >= 'A') && (ch <= 'F'))
            return ch-'A'+10;
        else if((ch >= 'a') && (ch <= 'f'))
            return ch-'a'+10;
        else return (-1);
}

所有源代码地址

开发环境win10,qtcreator4.2.0,qt5.7.1.
地址:资源刚刚上传,还在审核中,稍后在评论区上传。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仟人斩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值