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;
         }
      }







免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM