項目要求:
用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;
}
}