QT做串口助手及QTThread学习经验

项目要求:

  用QT做一个串口助手,利用线程 阻塞通信,正常的收发数据并且显示:

  技能要求:QT界面 熟练掌握QT新建工程  要熟悉QT的界面布局,信号与槽机制、各种控件的使用,以及线程的的创建和线程函数的启动。

  使用开发环境:

  QT5.7.0+QT_create 4.0.2    windoW X86 环境下开发

  

我的 Widget.cpp 文件 也是比较核心的内容

 

#include "widget.h"
#include "mythread.h"
#include "ui_widget.h"
#include"qpushbutton.h"
#include<QThread>
#include<QDebug>
#include<QMessageBox>
#include<QMainWindow>


Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //固定窗口大小
    this->setFixedSize( this->width (),this->height ());

    scancom();//扫描可用端口

    mySerialPort = NULL;
    //创建一个串口对象
    //mySerialPort = new QSerialPort;
    //启动按照0.5s扫描
    //timerID = this->startTimer(3000);
    //初始化标志位
    isStop = false;
    //自定义线程不指定父亲
    myT = new MyThread;
    //分配子线程空间
    thread = new QThread(this);
    //将自定义线程移动到子线程
    myT->moveToThread(thread);


    connect(myT, &MyThread::MySignals, this, &Widget::dealSignals);

    qDebug() << "主线程号 11:" << QThread::currentThread() <<endl;
    //将线程启动信号和线程处理函数关联
    connect(this, &Widget::startThread, myT, &MyThread::myTimeout);
    //将关闭窗口信号和回收线程函数关联
    connect(this, &Widget::destroyed, this, &Widget::stopthread);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::scancom(void)//扫描可用端口并加入端口下拉框
{
    mySerialPort = new QSerialPort;//新建串口类对象
    if(NULL != mySerialPort)
    {
        foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())//查找可用串口。找不到存在串口是不会进入到foreach内部的,存在不一定可用.
        {
            QSerialPort availableport;
            int i;
            availableport.setPortName(info.portName());

            if(availableport.open(QIODevice::ReadWrite))//看串口是否可用(即可读写)。
            {
                int IsHaveInItemList =0;

                for(i=0; i<ui->comboBox_port->count(); i++)//看串口是否在列表中
                {
                    if(ui->comboBox_port->itemData(i) == availableport.portName())
                    {
                        IsHaveInItemList = 1;
                    }
                }

                if(IsHaveInItemList == 0)
                {
                    ui->comboBox_port->addItem(availableport.portName());//加UI的下拉框里面
                    qDebug()<<info.portName()<<endl;
                }
                availableport.close();
            }
       
        }
    }
    else
    {
        QMessageBox::warning(this,tr("警告"),tr("串口没有初始化成功"),QMessageBox::Ok);
    }
}


//实现关闭串口功能
void Widget::closecom(void)
{
    qDebug()<<"端口"<<ui->comboBox_port->currentText()<<"已关闭"<<endl;
    mySerialPort->close();//实现关闭

    //温柔的关闭线程
    thread->quit();

    myT->setFlag(true);
    thread->wait();

    //串口设置的下拉控件重新使能
    ui->comboBox_baudrate->setEditable(true);
    ui->comboBox_checkbit->setEditable(true);
    ui->comboBox_databit->setEditable(true);
    ui->comboBox_port->setEditable(true);
    ui->comboBox_stopbit->setEditable(true);

    ui->pushButton_comopen->setEnabled(true);//使能“open”
    ui->pushButton_comclose->setEnabled(false);//失能"close"

}

//实现打开串口功能
void Widget::opencom(void)
{
    qDebug()<<"端口"<<ui->comboBox_port->currentText()<<"已打开"<<endl;
    //mySerialPort->open();//实现打开
    //设置好标志位 打开
    myT->setFlag(false);

    if(NULL == mySerialPort)
    {
        mySerialPort = new QSerialPort();
    }

    if(ui->comboBox_port->count() != 0)//存在可用端口就进入
    {
        mySerialPort->setPortName(ui->comboBox_port->currentText());//设置端口名
        mySerialPort->open(QIODevice::ReadWrite);//打开串口
        mySerialPort->setBaudRate(ui->comboBox_baudrate->currentText().toInt());//设置波特率
        //初始化串口
        switch(ui->comboBox_databit->currentText().toInt())//设置数据位
        {
            case 5:
                    mySerialPort->setDataBits(QSerialPort::Data5);
                     break;
            case 6:
                    mySerialPort->setDataBits(QSerialPort::Data6);
                     break;
            case 7:
                    mySerialPort->setDataBits(QSerialPort::Data7);
                     break;
            case 8:
                    mySerialPort->setDataBits(QSerialPort::Data8);
                     break;
        default:
                    mySerialPort->setDataBits(QSerialPort::Data8);
                     break;
        }

         switch(ui->comboBox_checkbit->currentIndex())//设置校验位
         {
            case 0:
                    mySerialPort->setParity(QSerialPort::NoParity);
                    break;
            case 1:
                    mySerialPort->setParity(QSerialPort::OddParity);
                    break;
            case 2:
                    mySerialPort->setParity(QSerialPort::EvenParity);
                    break;
         }

         switch(ui->comboBox_stopbit->currentText().toInt())//设置停止位
         {
            case 1:
                 mySerialPort->setStopBits(QSerialPort::OneStop);
                 break;
         case 2:
                 mySerialPort->setStopBits(QSerialPort::TwoStop);
                 break;
         }

        mySerialPort->setFlowControl(QSerialPort::NoFlowControl);
        //mySerialPort->setReadBufferSize(100);//设置缓冲区的大小。如果读串口数据不能一次性读完,则数据会存入内部缓冲区。

        //串口设置的下拉控件重新使能
        ui->comboBox_baudrate->setEditable(false);
        ui->comboBox_checkbit->setEditable(false);
        ui->comboBox_databit->setEditable(false);
        ui->comboBox_port->setEditable(false);
        ui->comboBox_stopbit->setEditable(false);

        ui->pushButton_comopen->setEnabled(false);//使能“open”
        ui->pushButton_comclose->setEnabled(true);//失能"close"

    }
    else
    {
         QMessageBox::warning(this,tr("警告"),tr("无可用串口!"),QMessageBox::Ok);
    }
}

//清空发送区
void Widget::clearsend(void)
{
    ui->textEdit_send->clear();
}

//清空接收区
void Widget::clearrecv(void)
{
    ui->textEdit_recv->clear();
}

//从发送区写char数据
void Widget::writechr(void)
{
    //获取界面上的数据并转换成utf8格式的字节流
    QByteArray sendData = ui->textEdit_send->toPlainText().toUtf8();
    //转码格式用于汉字输入
    //System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding();
    //Byte[] writeBytes = utf8.GetBytes("你好世界");
    //serialPort.Write(writeBytes, 0, writeBytes.Length);
    if ((false == sendData.isEmpty() ) && (false == sendData.isNull()))
        {
            mySerialPort->write(sendData);
            //write 直接写函数
        }
}

//时间处理时从端口读数据,显示到接收区
/*void  Widget::readfromcom(void)
{
    QByteArray buf;
    //先判断有没有数据
    if(0 < mySerialPort->bytesAvailable())
    {
        buf = mySerialPort->readAll();

        if(buf.isEmpty() == 0)
        {
            qDebug()<<"1"<<endl;
            QString str = ui->textEdit_recv->toPlainText();
                str += tr(buf);
                ui->textEdit_recv->clear();
                ui->textEdit_recv->setText(str);
        }
    }
    buf.clear();
}*/

void Widget::on_pushButton_clearrecv_clicked(void)
{
    clearrecv();
}

void Widget::on_pushButton_clearsd_clicked(void)
{
    clearsend();
}

void Widget::on_pushButton_send_clicked(void)
{
    writechr();
}

void Widget::on_pushButton_comopen_clicked(void)
{
    //关闭扫描端口事件
   //this->killTimer(timerID);

   opencom();
   if(thread->isRunning() == true)
   {
       return ;
   }
   //判断一下 开启线程
   thread->start();
   //设置标志位
   myT->setFlag(false);
   //启动线程
   emit startThread(mySerialPort);
}

void Widget::on_pushButton_comclose_clicked(void)
{
    if(NULL != mySerialPort)
    {
     closecom();
     //关闭串口后启动扫描串口事件
     //timerID = this->startTimer(3000);
    }
}

//计时器的函数
/*void Widget::timerEvent(QTimerEvent *e)
{
    //内部有判断有没有数据来串口
    //读数据
    //Widget::readfromcom();
    delete mySerialPort;

    Widget::scancom();
}*/

void Widget::dealSignals(QByteArray data)
{
        qDebug()<<"1"<<endl;
        QString str = ui->textEdit_recv->toPlainText();
                str += tr(data);
                ui->textEdit_recv->clear();
                ui->textEdit_recv->setText(str);
              //  ui->textEdit_recv->append(str);
}

//实现关闭窗口槽函数
void Widget::stopthread()
{
    //判断开启过线程
    if(NULL != myT)
    {
    on_pushButton_comclose_clicked();//关闭串口
    delete myT;//回收线程分配的空间
    }
}

/********************************************************************************************************
/**********************************几个重点要注意的地方*****************************************************

1.注意创建子线程启动有两种方法,一种是添加QThread的 直接调 start()函数启动 唯一的run()函数就行 属于比较老 简单的方法
另一种是添加OBject文件 需要创建一个自定义线程(不指定父对象)和一个子线程指定父对象 将自定义线程move到子线程去,
利用signal和slot来启动线程函数。

2.QT中connect()的第五个参数,分情况 一:多线程队列,子线程和主线程不会在同一线程
                    二:直接 会将处理的槽函数放入信号发出者同一线程

3.QT中的 waitForReadyRead()阻塞函数只能检测到大于等于两个字节的消息,所以需要自己宁外加一判断
例如:
while(false == isStop)
   {
      if(sendSerialPort->waitForReadyRead(10) == true)
      {
          int  num = sendSerialPort->bytesAvailable();

         qDebug()<<"串口数据个数:"<<num<<endl;
         readbuf = sendSerialPort->read(num);

         if(num > 0)
         {
             emit MySignals(readbuf);
             qDebug()<<"读到的数据:"<<tr(readbuf)<<endl;
         }
      }//阻塞只能检测到大于两个bit的数 人工加了个判断
      else if(1 == sendSerialPort->bytesAvailable())
      {
         int sum = sendSerialPort->bytesAvailable();

         qDebug()<<"串口数据个数:"<<sum<<endl;
         readbuf = sendSerialPort->read(sum);

         if(1 == sum)
         {
             emit MySignals(readbuf);
             qDebug()<<"读到的数据:"<<tr(readbuf)<<endl;
         }
      }






posted on 2019-01-18 16:47  火辣辣的葱  阅读(3115)  评论(1编辑  收藏  举报

导航