基于Qt6.3做的一款适用于windows10以上的调试助手
1、布局
当面拉伸时 我们在选择布局时选择整体栅格布局
拉申界面时控件也会变换
当控件放到这几种布局格式后再不能再调整控件大小只会跟随这个布局大小变化而变化
2、一些控件
plainTexEdit 用来打印log 多行打印
EditMsg 用来单行输入输出
comboIP 用来选择下拉框
maintoolBar 添加action
着重注意这个点
具体怎么弄有点久了我也忘了
标签栏选项卡用下图控件
3.1、用到计算机硬件——网络
关于新建一个TCPsever的流程如下
1、首先在pro中添加网络
2、添加状态lable
LabListen=new QLabel(“监听状态”);
LabListen->setMinimumWidth(150);
ui->statusbar->addWidget(LabListen);
LabSocketState = new QLabel(“Socket 状态:”);
LabSocketState->setMinimumWidth(200);
ui->statusbar->addWidget(LabSocketState);
添加一个监听状态栏
和socket状态栏
这两个lable是不能直接用ui生成的(也许能我没找到方法)
3、获取本机IP添加到标题栏中
这里注意会获得多个IP如果你的电脑有多个网卡可以筛选使用
QString localIP = getLocalIP();
this->setWindowTitle(this->windowTitle()+“-----本机IP”+localIP);
QString MainWindow::getLocalIP()
{//获取本机IPv4地址
QString hostName=QHostInfo::localHostName();//本地主机名
QHostInfo hostInfo=QHostInfo::fromName(hostName);
QString localIP="";
QList<QHostAddress> addList=hostInfo.addresses();//
if (!addList.isEmpty())
for (int i=0;i<addList.count();i++)
{
QHostAddress aHost=addList.at(i);
if (QAbstractSocket::IPv4Protocol==aHost.protocol())
{
localIP=aHost.toString();
break;
}
}
return localIP;
}
4、创建tcpServer 和tcpSocket 对象
tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);
创建槽函数
connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
当有客户端连接时触发onNewConnection()函数
这个槽函数如下当触发时会实例化一个tcpSocket然后通过这个tcpSocket进行通信
具体来说,当有客户端尝试连接到 QTcpServer 时,连接将处于挂起状态,等待服务器接受。调用 nextPendingConnection() 函数将返回一个指向新客户端连接的 QTcpSocket 对象的指针。通过该 QTcpSocket 对象,服务器可以与客户端进行通信,发送和接收数据。
下面就是各种槽函数触发时需要进行的处理
注意**SIGNAL(connected()**不会被触发,所以直接调用onClientConnected函数否则不能用
void MainWindow::onNewConnection()
{
// ui->plainTextEdit->appendPlainText("有新连接");
tcpSocket = tcpServer->nextPendingConnection(); //创建socket
connect(tcpSocket, SIGNAL(connected()),
this, SLOT(onClientConnected()));
onClientConnected();//
connect(tcpSocket, SIGNAL(disconnected()),
this, SLOT(onClientDisconnected()));
connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStatechange(tcpSocket->state());
connect(tcpSocket,SIGNAL(readyRead()),
this,SLOT(onSocketReadyRead()));
}
socket连接后
void MainWindow::onClientConnected()
{
//当客户端接入时 文本框显示
ui->plainTextEdit->appendPlainText("**client socket connected");
ui->plainTextEdit->appendPlainText("**peer address:"+tcpSocket->peerAddress().toString());//获取对等端的IP如果你是客户端返回服务端IP 反之服务端返回客户端IP
ui->plainTextEdit->appendPlainText("peer port"+ QString::number(tcpSocket->peerPort()));
autoSynTime();
}
socket断开后
void MainWindow::onClientDisconnected()
{
//客户端断开连接时
ui->plainTextEdit->appendPlainText("**client socket disconnected");
tcpSocket->deleteLater();
}
socket状态发生变化
void MainWindow::onSocketStatechange(QAbstractSocket::SocketState socketState)
{//检测到socket变化
switch(socketState)
{
case QAbstractSocket::UnconnectedState:
LabSocketState->setText("scoket状态:UnconnectedState");//套接字未连接到任何主机。
break;
case QAbstractSocket::HostLookupState:
LabSocketState->setText("scoket状态:HostLookupState");//套接字正在查找主机地址。
break;
case QAbstractSocket::ConnectingState:
LabSocketState->setText("scoket状态:ConnectingState");//套接字正在尝试与主机建立连接。
break;
case QAbstractSocket::ConnectedState:
LabSocketState->setText("scoket状态:ConnectedState");//套接字已经成功连接到主机。
break;
case QAbstractSocket::BoundState:
LabSocketState->setText("scoket状态:BoundState");//套接字已经绑定到本地地址和端口。
break;
case QAbstractSocket::ClosingState:
LabSocketState->setText("scoket状态:ClosingState");//套接字正在关闭连接。
break;
case QAbstractSocket::ListeningState:
LabSocketState->setText("scoket状态:ListeningState"); //套接字处于监听状态,准备接受连接。
default:
break;
}
}
当有数据接收时
void MainWindow::onSocketReadyRead()
{
QByteArray buffer;
while (tcpSocket->bytesAvailable() > 0)
{
buffer.append(tcpSocket->readAll());
}
QString hexString = buffer.toHex().toUpper();//
// 在每两个字符之间插入空格
for (int i = 2; i < hexString.length(); i += 3)
{
hexString.insert(i, ' ');
}
QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
ui->plainTextEdit->appendPlainText(currentTime);
ui->plainTextEdit->appendPlainText("[IN] " + hexString);
quint8 err=checkDat(buffer);
if(!err)
{
//校验正确获取有效数据
processTcpdata(buffer);
}
}
点击两个action进行监听和断开
void MainWindow::on_actStart_triggered()
{
//开始监听
QString IP = ui->comboIP->currentText();//comboIP控件中currentText获取当前被选中的文本
quint16 port = ui->spinBox->value();//端口
QHostAddress addr(IP);
tcpServer->listen(addr,port);
ui->plainTextEdit->appendPlainText("**开始监听...");
ui->plainTextEdit->appendPlainText("**服务器地址:"
+tcpServer->serverAddress().toString());
ui->plainTextEdit->appendPlainText("**服务器端口:"
+QString::number(tcpServer->serverPort()));
ui->actStart->setEnabled(false);
ui->actStop->setEnabled(true);
LabListen->setText("监听状态:正在监听") ;
}
void MainWindow::on_actStop_triggered()
{//停止监听
if (tcpServer->isListening()) //tcpServer正在监听
{
tcpServer->close();//停止监听
ui->actStart->setEnabled(true);
ui->actStop->setEnabled(false);
LabListen->setText("监听状态:已停止监听");
}
}
tcp数据发送
void MainWindow::tcpSend(const QByteArray &str)
{
if(tcpSocket != nullptr&&tcpSocket->state()==QAbstractSocket::ConnectedState)
{
tcpSocket->write(str);
QString hexString = str.toHex().toUpper();
// 在每两个字符之间插入空格
for (int i = 2; i < hexString.length(); i += 3) {
hexString.insert(i, ' ');
}
QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
ui->plainTextEdit->appendPlainText(currentTime);
ui->plainTextEdit->appendPlainText("[OUT] " + hexString);
}
}
3.2、用到计算机硬件——COM串口
新建一个定时器触发槽函数每过1s检测串口信息变化
timer = new QTimer(this);
connect(timer, &QTimer::timeout,this,&MainWindow::oncheckSerialPortConnection);
timer->start(1000);
检测到端口数据变换后进行更新
void MainWindow::oncheckSerialPortConnection()
{
QStringList availablePorts;
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
availablePorts << info.portName()+" "+info.description();
}
// qDebug()<<availablePorts;
foreach(const QString &portName, availablePorts)
{
if (ui->comboIP_comPort->findText(portName)==-1)//检测不到返回-1
{
ui->comboIP_comPort->addItem(portName);
//ui->comboIP_comPort->update();
}
}
// 删除不再存在的串口
for (int i = 0; i < ui->comboIP_comPort->count(); ++i)
{
if (!availablePorts.contains(ui->comboIP_comPort->itemText(i))) {
ui->comboIP_comPort->removeItem(i);
--i; // 因为移除了一个项目,所以需要减少计数器
}
}
}
新建一个串口类并将其与槽函数连接 当有数据时处理数据
erialPortdata.append( serialPort->readAll()); 要把数据一个个添加然后筛选出一包有用数据
serialPort=new QSerialPort();
connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::onReadSerialData);
注意串口数据发送是一包数据如果是8位也就是一个字节过来的
void MainWindow::onReadSerialData()
{
serialPortdata.append( serialPort->readAll()); // 读取串口中的所有数据
while (serialPortdata.size() >= 12) {
// 查找包头的位置
int headerIndex = serialPortdata.indexOf(0xF4);
if (headerIndex == -1)
{
// 如果缓冲区中没有包头,则丢弃缓冲区中的数据
serialPortdata.clear();
return;
}
int footerIndex = serialPortdata.indexOf(0xFB, headerIndex + 1);
if (footerIndex != -1)
{
// 提取完整的数据包
QByteArray packet = serialPortdata.mid(headerIndex, footerIndex - headerIndex + 1);
// 处理完整的数据包
processPacket(packet);
// 从缓冲区中移除已处理的数据
serialPortdata.clear();
}
else
{
// 如果缓冲区中没有包尾,则等待更多的数据到达
break;
}
}
// if (data[1]==static_cast<char>(0xF4) && data[2] == static_cast<char> (0xF5))
// {
// qDebug()<<"包头校验正确";
// }
}
打开关闭串口
void MainWindow::on_pushButton_openCom_clicked()
{
if( ui->pushButton_openCom->text()=="打开串口")
{
QString portFullName = ui->comboIP_comPort->currentText();
int index = portFullName.indexOf(" "); // 查找第一个空格的位置
QString portName;
if(index==4)
{
portName = portFullName.left(index);
}
else
{
// 如果是5个字符,提取从第一个字符开始到第一个空格之间的子字符串
portName = portFullName.mid(0, index);
}
qDebug()<<portName;
serialPort->setPortName(portName);
serialPort->setBaudRate(QSerialPort::Baud9600);
serialPort->setDataBits(QSerialPort::Data8); // 设置数据位为8
serialPort->setParity(QSerialPort::NoParity); // 设置校验位为无校验
serialPort->setStopBits(QSerialPort::OneStop); // 设置停止位为1
if (serialPort->open(QIODevice::ReadWrite))
{
// 打开串口成功,可以进行串口通信
qDebug() << "Serial port opened successfully!";
ui->pushButton_openCom->setText("关闭串口");
}
else
{
// 打开串口失败
qDebug() << "Failed to open serial port!";
}
}
else if( ui->pushButton_openCom->text()=="关闭串口")
{
serialPort->close();
ui->pushButton_openCom->setText("打开串口");
}
}
串口发送函数
serialPort->write(portSendData);注意这发送的是字节流
4 数据类型转换
QByteArray 这是字节流 可以以char型读出写入
QString 字符串可以追加写入 += 或者QString .appende()
QStringList 字符串列表 每个元素就是字符串
我没系统学习过C++这里说法可能有误
将字符串转换成字节用.toInt
将字节流转换成字符串用QString::number
在字符串中选两个字符组成新的字符串str.mid(i,2);
将字符串中按某字符串进行分割存在QStringList 中
QStringList timeParts1 = stuTime1.split(“-”);
还需注意一点进行QByteArray 中比较时由于QByteArray 中元素是char类型 比较0x80及以上数据会出错要将比较的数据进行转换
比如(basicAarry.at(1)==static_cast(FAIL))
5 形参
函数的两种声明
void deviceIPToStringShow(const QByteArray deviceIPdata) const;
第一种是拷贝 形参不会改变当不同函数形参名字相同时也不会将其修改 可不用const修饰
void deviceIPToStringShow(const QByteArray &deviceIPdata) const;
第二种是直接引用这时注意要用const 修饰否则会发生当不同函数形参名字相同时会改变原有值
5 结语
这是第一个qt软件程序陆陆续续写了一周左右,由于之前没有学习过从零开始花费了很多时间尤其是在进行数据类型转换时校验的过程。中间还遇到协议更改相当于重新写了一遍协议。现在终于算是完成了软件到硬件的打通了,下一步有时间学习一下数据库。当然该demo程序或有很多不足在日后的使用中改进吧。
感谢GPT和QT5.9模版。
贵有恒何必五更起三更眠,最无益一日曝十日寒。