@@ -23,7 +23,7 @@ | |||
* 4. 使用UTF-8编码保存文本 | |||
* 5. 错误处理包含用户提示 | |||
*/ | |||
bool SaveDate(QWidget *parent, QTextEdit *edit) | |||
bool saveDate(QWidget *parent, QTextEdit *edit) | |||
{ | |||
// 弹出文件保存对话框,默认保存到用户主目录的note.txt文件 | |||
QString fileName = QFileDialog::getSaveFileName( | |||
@@ -88,7 +88,7 @@ bool SaveDate(QWidget *parent, QTextEdit *edit) | |||
* 3. 使用UTF-8编码读取内容 | |||
* 4. 错误处理包含用户提示 | |||
*/ | |||
bool ReadDate(QWidget *parent, QTextEdit *edit) | |||
bool readDate(QWidget *parent, QTextEdit *edit) | |||
{ | |||
// 弹出文件打开对话框,默认从用户主目录开始浏览 | |||
QString fileName = QFileDialog::getOpenFileName( | |||
@@ -32,7 +32,7 @@ | |||
* 3. 支持UTF-8编码格式保存 | |||
* 4. 操作失败时会显示错误提示 | |||
*/ | |||
bool SaveDate(QWidget *parent, QTextEdit *edit); | |||
bool saveDate(QWidget *parent, QTextEdit *edit); | |||
/** | |||
* @brief 从文件读取通信数据 | |||
@@ -45,6 +45,6 @@ bool SaveDate(QWidget *parent, QTextEdit *edit); | |||
* 3. 支持UTF-8编码格式读取 | |||
* 4. 操作失败时会显示错误提示 | |||
*/ | |||
bool ReadDate(QWidget *parent, QTextEdit *edit); | |||
bool readDate(QWidget *parent, QTextEdit *edit); | |||
#endif // COMMUNICATIONHISTORY_H |
@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE QtCreatorProject> | |||
<!-- Written by QtCreator 4.0.3, 2025-07-30T17:14:20. --> | |||
<!-- Written by QtCreator 4.0.3, 2025-08-01T15:38:03. --> | |||
<qtcreator> | |||
<data> | |||
<variable>EnvironmentId</variable> | |||
@@ -17,12 +17,12 @@ | |||
* @note 初始化默认Modbus参数:站地址=1,功能码=0x01(读线圈), | |||
* 起始地址=256,数据长度=1 | |||
*/ | |||
MyModbus::MyModbus() | |||
MyModbus::MyModbus(QObject *parent) : QObject(parent) | |||
, stationAddress_(0x01) | |||
, functionCode_(0x01) | |||
, startAddress_(0x01) | |||
, length_(0x01) | |||
{ | |||
this->stationAddress_ = 1; // 默认站地址 | |||
this->functionCode_ = 0x01; // 默认功能码(读线圈) | |||
this->startAdress_ = 256; // 默认起始地址 | |||
this->length_ = 1; // 默认数据长度 | |||
} | |||
/** | |||
@@ -32,34 +32,43 @@ MyModbus::MyModbus() | |||
* @param startAdress 起始地址 | |||
* @param length 数据长度 | |||
*/ | |||
void MyModbus::Set(quint16 stationAddress, quint16 functionCode, quint16 startAdress, quint16 length) | |||
bool MyModbus::setStation(quint16 stationAddress, quint16 functionCode, | |||
quint16 startAddress, quint16 length) | |||
{ | |||
this->stationAddress_ = stationAddress; | |||
this->functionCode_ = functionCode; | |||
this->startAdress_ = startAdress; | |||
this->length_ = length; | |||
if ((stationAddress > 0) && | |||
(functionCode > 0) && | |||
(startAddress >0) && | |||
(length > 0)) | |||
{ | |||
this->stationAddress_ = stationAddress; | |||
this->functionCode_ = functionCode; | |||
this->startAddress_ = startAddress; | |||
this->length_ = length; | |||
sendCommand_.clear(); | |||
sendCommand_.append(stationAddress_ % 256); | |||
sendCommand_.append(functionCode_ % 256); | |||
sendCommand_.append(startAddress_ / 256); | |||
sendCommand_.append(startAddress_ % 256); | |||
sendCommand_.append(length_ / 256); | |||
sendCommand_.append(length_ % 256); | |||
return true; | |||
} | |||
else | |||
{ | |||
return false; | |||
} | |||
} | |||
/** | |||
* @brief 生成读取线圈/寄存器的Modbus命令 | |||
* @note 命令结构:[站地址][功能码][起始地址Hi][起始地址Lo][长度Hi][长度Lo][CRC Lo][CRC Hi] | |||
*/ | |||
void MyModbus::ReadCoilAndReg() | |||
void MyModbus::readCoilAndReg() | |||
{ | |||
sendCommand_.clear(); | |||
// 构建标准读命令帧 | |||
sendCommand_.append(stationAddress_%256); | |||
sendCommand_.append(functionCode_%256); | |||
sendCommand_.append(startAdress_/256); | |||
sendCommand_.append(startAdress_%256); | |||
sendCommand_.append(length_/256); | |||
sendCommand_.append(length_%256); | |||
// 计算并附加CRC校验 | |||
quint16 temp = CalculateCrc(sendCommand_); | |||
sendCommand_.append(temp%256); // CRC低字节在前 | |||
sendCommand_.append(temp/256); // CRC高字节在后 | |||
quint16 temp = calculateCrc(sendCommand_); | |||
sendCommand_.append(temp % 256); // CRC低字节在前 | |||
sendCommand_.append(temp / 256); // CRC高字节在后 | |||
} | |||
/** | |||
@@ -67,18 +76,11 @@ void MyModbus::ReadCoilAndReg() | |||
* @param coils 要写入的线圈状态数组 | |||
* @note 命令结构:[站地址][0x0F][起始地址Hi][起始地址Lo][数量Hi][数量Lo][字节数][数据...][CRC] | |||
*/ | |||
void MyModbus::WriteCoil(QVector<bool> &coils) | |||
void MyModbus::writeCoil(QVector<bool> &coils) | |||
{ | |||
quint16 coilCount = coils.size(); | |||
int byteCount = (coilCount + 7) / 8; // 计算所需字节数(每字节8位) | |||
sendCommand_.clear(); | |||
sendCommand_.append(stationAddress_%256); | |||
sendCommand_.append(0x0F); // 写多个线圈功能码 | |||
sendCommand_.append(startAdress_/256); | |||
sendCommand_.append(startAdress_%256); | |||
sendCommand_.append(coilCount/256); // 线圈数量 | |||
sendCommand_.append(coilCount%256); | |||
sendCommand_.append(byteCount); // 后续字节数 | |||
// 打包线圈状态到字节 | |||
@@ -91,16 +93,17 @@ void MyModbus::WriteCoil(QVector<bool> &coils) | |||
// 只处理有效范围内的线圈 | |||
if (bitIndex < coils.size() && coils[bitIndex]) | |||
{ | |||
byte |= (1 << j); // 设置对应位 | |||
byte |= static_cast<quint8>(1 << j); // 设置对应位 | |||
} | |||
} | |||
sendCommand_.append(static_cast<char>(byte)); | |||
sendCommand_.append(byte); | |||
} | |||
// 计算并附加CRC校验 | |||
quint16 temp = CalculateCrc(sendCommand_); | |||
sendCommand_.append(temp%256); | |||
sendCommand_.append(temp/256); | |||
quint16 temp = calculateCrc(sendCommand_); | |||
sendCommand_.append(temp % 256); | |||
sendCommand_.append(temp / 256); | |||
} | |||
/** | |||
@@ -108,35 +111,28 @@ void MyModbus::WriteCoil(QVector<bool> &coils) | |||
* @param values 要写入的寄存器值数组 | |||
* @note 命令结构:[站地址][0x10][起始地址Hi][起始地址Lo][数量Hi][数量Lo][字节数][数据Hi][数据Lo...][CRC] | |||
*/ | |||
void MyModbus::WriteRegister(QVector<quint16> &values) | |||
void MyModbus::writeRegister(QVector<quint16> &values) | |||
{ | |||
sendCommand_.clear(); | |||
sendCommand_.append(stationAddress_%256); | |||
sendCommand_.append(0x10); // 写多个寄存器功能码 | |||
sendCommand_.append(startAdress_/256); | |||
sendCommand_.append(startAdress_%256); | |||
sendCommand_.append(values.size()/256); // 寄存器数量 | |||
sendCommand_.append(values.size()%256); | |||
sendCommand_.append(static_cast<char>(values.size() * 2)); // 后续字节数(每个寄存器2字节) | |||
sendCommand_.append(values.size() * 2); // 后续字节数(每个寄存器2字节) | |||
// 打包寄存器值(高位在前) | |||
for (quint16 v : values) | |||
{ | |||
sendCommand_.append(static_cast<char>((v >> 8) & 0xFF)); // 高字节 | |||
sendCommand_.append(static_cast<char>(v & 0xFF)); // 低字节 | |||
sendCommand_.append(v / 256); // 高字节 | |||
sendCommand_.append(v % 256); // 低字节 | |||
} | |||
// 计算并附加CRC校验 | |||
quint16 temp = CalculateCrc(sendCommand_); | |||
sendCommand_.append(temp%256); | |||
sendCommand_.append(temp/256); | |||
quint16 temp = calculateCrc(sendCommand_); | |||
sendCommand_.append(temp % 256); | |||
sendCommand_.append(temp / 256); | |||
} | |||
/** | |||
* @brief 获取生成的Modbus命令 | |||
* @return 完整的Modbus命令字节数组 | |||
*/ | |||
QByteArray MyModbus::SendCommand() | |||
QByteArray MyModbus::sendCommand() | |||
{ | |||
return sendCommand_; | |||
} | |||
@@ -147,7 +143,7 @@ QByteArray MyModbus::SendCommand() | |||
* @return 处理后的有效数据部分(移除地址/功能码/CRC) | |||
* @note 执行站地址匹配和CRC校验 | |||
*/ | |||
QByteArray MyModbus::Receive(const QByteArray &revMessage) | |||
QByteArray MyModbus::receive(const QByteArray &revMessage) | |||
{ | |||
receive_.clear(); | |||
@@ -164,7 +160,7 @@ QByteArray MyModbus::Receive(const QByteArray &revMessage) | |||
} | |||
// CRC校验检查 | |||
if (!CrcCheck(revMessage)) | |||
if (!crcCheck(revMessage)) | |||
{ | |||
return receive_; | |||
} | |||
@@ -177,7 +173,7 @@ QByteArray MyModbus::Receive(const QByteArray &revMessage) | |||
* @brief 检查Modbus异常响应 | |||
* @return 0-正常响应, >0-Modbus异常码, -1-响应报文错误 | |||
*/ | |||
int MyModbus::ErrorCheck() | |||
quint8 MyModbus::errorCheck() | |||
{ | |||
// 异常响应最小长度:地址1 + 异常功能码1 + 异常码1 + CRC2 | |||
if(receive_.size() < 5) | |||
@@ -197,7 +193,7 @@ int MyModbus::ErrorCheck() | |||
* @brief 解析读取线圈的响应数据 | |||
* @return 线圈状态数组 | |||
*/ | |||
QVector<bool> MyModbus::AnalReadCoil() | |||
QVector<bool> MyModbus::analReadCoil() | |||
{ | |||
QVector<bool> coil; | |||
// 最小长度检查:地址1+功能码1+字节数1+数据1+CRC2 | |||
@@ -231,7 +227,7 @@ QVector<bool> MyModbus::AnalReadCoil() | |||
* @brief 解析读取寄存器的响应数据 | |||
* @return 寄存器值数组 | |||
*/ | |||
QVector<quint16> MyModbus::AnalReadReg() | |||
QVector<quint16> MyModbus::analReadReg() | |||
{ | |||
QVector<quint16> registers; | |||
// 最小长度检查:地址1+功能码1+字节数1+数据2+CRC2 | |||
@@ -240,7 +236,7 @@ QVector<quint16> MyModbus::AnalReadReg() | |||
return registers; | |||
} | |||
int byteCount = receive_.at(2); // 数据字节数 | |||
int byteCount = static_cast<quint8>(receive_.at(2)); // 数据字节数 | |||
QByteArray data = receive_.mid(3, byteCount); // 提取数据部分 | |||
// 每两个字节组合为一个寄存器值(高位在前) | |||
@@ -248,7 +244,8 @@ QVector<quint16> MyModbus::AnalReadReg() | |||
{ | |||
quint16 high = static_cast<quint8>(data[i]); | |||
quint16 low = static_cast<quint8>(data[i+1]); | |||
registers.append((high << 8) | low); | |||
quint16 value = (high * 256) + low; | |||
registers.append(value); | |||
} | |||
return registers; | |||
} | |||
@@ -259,7 +256,7 @@ QVector<quint16> MyModbus::AnalReadReg() | |||
* @return CRC16校验值 | |||
* @note 使用MODBUS标准多项式0xA001(0x8005的反转) | |||
*/ | |||
quint16 MyModbus::CalculateCrc(const QByteArray &data) | |||
quint16 MyModbus::calculateCrc(const QByteArray &data) | |||
{ | |||
quint16 crc = 0xFFFF; // MODBUS初始值 | |||
const quint16 polynomial = 0xA001; // MODBUS多项式(0x8005的反转) | |||
@@ -289,7 +286,7 @@ quint16 MyModbus::CalculateCrc(const QByteArray &data) | |||
* @param data 完整接收数据(包含末尾CRC) | |||
* @return true-CRC校验通过, false-校验失败 | |||
*/ | |||
bool MyModbus::CrcCheck(const QByteArray &data) | |||
bool MyModbus::crcCheck(const QByteArray &data) | |||
{ | |||
//首先对接收报文的长度进行检验 | |||
if (data.size() < 3) | |||
@@ -304,9 +301,9 @@ bool MyModbus::CrcCheck(const QByteArray &data) | |||
quint8 receivedCrcHigh = static_cast<quint8>(data.at(data.length() - 1)); | |||
//计算返回的报文的crc | |||
quint16 crc = CalculateCrc(payload); | |||
quint8 calcCrcLow = crc & 0xFF; | |||
quint8 calcCrcHigh = (crc >> 8) & 0xFF; | |||
quint16 crc = calculateCrc(payload); | |||
quint8 calcCrcLow = crc % 256; | |||
quint8 calcCrcHigh = crc / 256; | |||
//比较计算的crc值和接收到的crc值是否一致 | |||
if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh) | |||
@@ -13,6 +13,7 @@ | |||
#ifndef MYMODBUS_H | |||
#define MYMODBUS_H | |||
#include <QObject> | |||
#include <QByteArray> | |||
#include <QVector> | |||
#include <QString> | |||
@@ -33,13 +34,14 @@ | |||
* @note 使用时需先通过Set()方法设置从站参数 | |||
*/ | |||
class MyModbus | |||
class MyModbus : public QObject | |||
{ | |||
Q_OBJECT | |||
public: | |||
/** | |||
* @brief 默认构造函数 | |||
*/ | |||
MyModbus(); | |||
MyModbus(QObject *parent = nullptr); | |||
/** | |||
* @brief 设置Modbus通信参数 | |||
@@ -48,7 +50,7 @@ public: | |||
* @param startAdress 起始地址 | |||
* @param length 数据长度 | |||
*/ | |||
void Set(quint16 stationAddress, quint16 functionCode, | |||
bool setStation(quint16 stationAddress, quint16 functionCode, | |||
quint16 startAdress, quint16 length); | |||
/** | |||
@@ -56,69 +58,69 @@ public: | |||
* | |||
* 根据当前参数生成读取命令(功能码01或03) | |||
*/ | |||
void ReadCoilAndReg(); | |||
void readCoilAndReg(); | |||
/** | |||
* @brief 生成写多个线圈命令 | |||
* @param coils 要写入的线圈状态数组 | |||
*/ | |||
void WriteCoil(QVector<bool> &coils); | |||
void writeCoil(QVector<bool> &coils); | |||
/** | |||
* @brief 生成写多个寄存器命令 | |||
* @param values 要写入的寄存器值数组 | |||
*/ | |||
void WriteRegister(QVector<quint16> &values); | |||
void writeRegister(QVector<quint16> &values); | |||
/** | |||
* @brief 获取待发送的命令报文 | |||
* @return 完整的Modbus命令报文 | |||
*/ | |||
QByteArray SendCommand(); | |||
QByteArray sendCommand(); | |||
/** | |||
* @brief 处理接收到的响应报文 | |||
* @param revMessage 接收到的原始报文 | |||
* @return 通过CRC校验的有效报文 | |||
*/ | |||
QByteArray Receive(const QByteArray &revMessage); | |||
QByteArray receive(const QByteArray &revMessage); | |||
/** | |||
* @brief 检查响应报文中的错误码 | |||
* @return 0表示正常,非0表示异常码 | |||
*/ | |||
int ErrorCheck(); | |||
quint8 errorCheck(); | |||
/** | |||
* @brief 解析读线圈响应报文 | |||
* @return 线圈状态数组 | |||
*/ | |||
QVector<bool> AnalReadCoil(); | |||
QVector<bool> analReadCoil(); | |||
/** | |||
* @brief 解析读寄存器响应报文 | |||
* @return 寄存器值数组 | |||
*/ | |||
QVector<quint16> AnalReadReg(); | |||
QVector<quint16> analReadReg(); | |||
/** | |||
* @brief 计算CRC校验值 | |||
* @param data 待计算的数据 | |||
* @return CRC校验值 | |||
*/ | |||
quint16 CalculateCrc(const QByteArray &data); | |||
quint16 calculateCrc(const QByteArray &data); | |||
/** | |||
* @brief 校验报文的CRC值 | |||
* @param data 待校验的报文 | |||
* @return true表示校验通过,false表示校验失败 | |||
*/ | |||
bool CrcCheck(const QByteArray &data); | |||
bool crcCheck(const QByteArray &data); | |||
private: | |||
quint16 stationAddress_; // 从站地址 | |||
quint16 functionCode_; // 功能码 | |||
quint16 startAdress_; // 起始地址 | |||
quint16 startAddress_; // 起始地址 | |||
quint16 length_; // 数据长度 | |||
QByteArray sendCommand_; // 待发送的命令报文 | |||
QByteArray receive_; // 接收到的响应报文 | |||
@@ -48,19 +48,7 @@ SerialCommunicator::SerialCommunicator(QObject *parent) : QObject(parent) | |||
*/ | |||
SerialCommunicator::~SerialCommunicator() | |||
{ | |||
//停止所有定时器,避免在对象销毁后触发定时器事件 | |||
if (recvTimer_->isActive()) | |||
{ | |||
recvTimer_->stop(); | |||
} | |||
if (resendTimer_->isActive()) | |||
{ | |||
resendTimer_->stop(); | |||
} | |||
close(); | |||
delete recvTimer_; | |||
delete resendTimer_; | |||
delete serialPort_; | |||
} | |||
/** | |||
@@ -144,16 +132,28 @@ bool SerialCommunicator::open() | |||
* @brief 关闭串口 | |||
* | |||
* @details 执行流程: | |||
* 1. 检查串口是否打开 | |||
* 2. 关闭串口 | |||
* 3. 发送状态变化信号 | |||
* 1. 关闭所有定时器 | |||
* 2. 清理待处理数据 | |||
* 3. 检查串口是否打开 | |||
* 4. 关闭串口 | |||
* 5. 发送状态变化信号 | |||
*/ | |||
void SerialCommunicator::close() | |||
{ | |||
if (serialPort_->isOpen()) | |||
{ | |||
//关闭所有定时器 | |||
recvTimer_->stop(); | |||
resendTimer_->stop(); | |||
// 清除可能存在的待处理数据 | |||
serialPort_->clear(); | |||
// 关闭串口 | |||
serialPort_->close(); | |||
emit statusChanged("串口断开"); | |||
comCount_ = 0; // 重置通信计数器 | |||
} | |||
} | |||
@@ -189,6 +189,10 @@ void SerialCommunicator::sendData(const QByteArray &data) | |||
*/ | |||
void SerialCommunicator::setMaxRetry(int count) | |||
{ | |||
if (count < 0) | |||
{ | |||
return; | |||
} | |||
maxRetry_ = count; | |||
} | |||
@@ -200,6 +204,10 @@ void SerialCommunicator::setMaxRetry(int count) | |||
*/ | |||
void SerialCommunicator::setRecvTimeout(int ms) | |||
{ | |||
if (ms < 0) | |||
{ | |||
return; | |||
} | |||
recvTimeout_ = ms; | |||
} | |||
@@ -209,6 +217,10 @@ void SerialCommunicator::setRecvTimeout(int ms) | |||
*/ | |||
void SerialCommunicator::setResendTimeout(int ms) | |||
{ | |||
if (ms < 0) | |||
{ | |||
return; | |||
} | |||
resendTimeout_ = ms; | |||
} | |||
@@ -41,7 +41,7 @@ public: | |||
* @brief 构造函数 | |||
* @param parent 父对象指针 | |||
*/ | |||
explicit SerialCommunicator(QObject *parent = nullptr); | |||
SerialCommunicator(QObject *parent = nullptr); | |||
/** | |||
* @brief 析构函数 | |||
@@ -17,7 +17,6 @@ | |||
#include <QString> | |||
#include <QVector> | |||
#include <QMessageBox> | |||
#include <synchapi.h> | |||
/** | |||
* @brief Widget 构造函数 | |||
@@ -29,45 +28,51 @@ | |||
*/ | |||
Widget::Widget(QWidget *parent) : | |||
QWidget(parent), | |||
ui_(new Ui::Widget) | |||
ui_(new Ui::Widget), | |||
serialComm_(new SerialCommunicator(this)), | |||
modbus_(new MyModbus(this)) | |||
{ | |||
modbus_ = new MyModbus(); | |||
ui_->setupUi(this); | |||
setFixedSize(700,500); //设置窗口界面大小 | |||
serialComm_ = new SerialCommunicator(this); // 初始化串口通信类 | |||
// 配置串口参数 | |||
serialComm_->setMaxRetry(3); // 最大重发3次 | |||
serialComm_->setRecvTimeout(50); // 接收超时50ms | |||
serialComm_->setResendTimeout(1000); // 重发间隔1s | |||
// 连接信号槽(串口->界面) | |||
connect(ui_->btn_connect, &QPushButton::clicked, this, &Widget::btnConnectClicked); | |||
connect(ui_->btn_write, &QPushButton::clicked, this, &Widget::onBtnWriteClicked); | |||
connect(ui_->btn_read, &QPushButton::clicked, this, &Widget::onBtnReadClicked); | |||
connect(ui_->btn_clear_read, &QPushButton::clicked, this, &Widget::onBtnClearreadClicked); | |||
connect(ui_->btn_clear_date, &QPushButton::clicked, this, &Widget::onBtnCleardateClicked); | |||
connect(ui_->btn_save_date, &QPushButton::clicked, this, &Widget::onBtnSavedateClicked); | |||
connect(ui_->btn_read_date, &QPushButton::clicked, this, &Widget::onBtnReaddateClicked); | |||
connect(ui_->btn_refresh, &QPushButton::clicked, this, &Widget::onBtnRefreshClicked); | |||
//当串口接收到完整数据后,SerialCommunicator会发出dataReceived信号 | |||
connect(serialComm_, &SerialCommunicator::dataReceived, this, &Widget::onSerialDataReceived); | |||
//当串口状态改变时,SerialCommunicator会发出statusChanged信号 | |||
connect(serialComm_, &SerialCommunicator::statusChanged, this, &Widget::onSerialStatusChanged); | |||
//当发送数据超过超时重发次数后仍未收到数据,SerialCommunicator会发出timeoutOccurred信号 | |||
connect(serialComm_, &SerialCommunicator::timeoutOccurred, this, &Widget::onSerialTimeout); | |||
//当串口连接出现错误时(例如串口突然断开),SerialCommunicator会发出physicalDisconnected信号 | |||
connect(serialComm_, &SerialCommunicator::physicalDisconnected, this, [=](){ | |||
ui_->btn_connect->setText("连接"); | |||
ui_->btn_read->setEnabled(false); | |||
ui_->btn_write->setEnabled(false); | |||
QMessageBox::warning(this, "警告", "串口已断开连接"); | |||
}); | |||
connect(ui_->btn_connect, &QPushButton::clicked, | |||
this, &Widget::btnConnectClicked); | |||
connect(ui_->btn_write, &QPushButton::clicked, | |||
this, &Widget::onBtnWriteClicked); | |||
connect(ui_->btn_read, &QPushButton::clicked, | |||
this, &Widget::onBtnReadClicked); | |||
connect(ui_->btn_clear_read, &QPushButton::clicked, | |||
this, &Widget::onBtnClearreadClicked); | |||
connect(ui_->btn_clear_date, &QPushButton::clicked, | |||
this, &Widget::onBtnCleardateClicked); | |||
connect(ui_->btn_save_date, &QPushButton::clicked, | |||
this, &Widget::onBtnSavedateClicked); | |||
connect(ui_->btn_read_date, &QPushButton::clicked, | |||
this, &Widget::onBtnReaddateClicked); | |||
connect(ui_->btn_refresh, &QPushButton::clicked, | |||
this, &Widget::onBtnRefreshClicked); | |||
connect(ui_->btn_batch_input, &QPushButton::clicked, | |||
this, &Widget::onBtnBatchInputClicked); | |||
connect(ui_->line_input, &QLineEdit::textChanged, | |||
this, &Widget::onTextChanged); | |||
//当串口接收到完整数据后,serialComm_会发出dataReceived信号 | |||
connect(serialComm_, &SerialCommunicator::dataReceived, | |||
this, &Widget::onSerialDataReceived); | |||
//当串口状态改变时,serialComm_会发出statusChanged信号 | |||
connect(serialComm_, &SerialCommunicator::statusChanged, | |||
this, &Widget::onSerialStatusChanged); | |||
//当发送数据超过超时重发次数后仍未收到数据,serialComm_会发出timeoutOccurred信号 | |||
connect(serialComm_, &SerialCommunicator::timeoutOccurred, | |||
this, &Widget::onSerialTimeout); | |||
//当串口连接出现错误时(例如串口突然断开),serialComm_会发出physicalDisconnected信号 | |||
connect(serialComm_, &SerialCommunicator::physicalDisconnected, | |||
this, &Widget::sicalDisconnected); | |||
//设置默认波特率、数据位、校验位 | |||
ui_->comboBox_baudRate->setCurrentIndex(3); | |||
ui_->comboBox_dataBit->setCurrentIndex(3); | |||
ui_->comboBox_xiaoyan->setCurrentIndex(2); | |||
ui_->combo_baud_rate->setCurrentIndex(3); | |||
ui_->combo_data_bit->setCurrentIndex(3); | |||
ui_->combo_check->setCurrentIndex(2); | |||
//串口未连接前锁定读取和写入按钮 | |||
ui_->btn_read->setEnabled(false); | |||
@@ -75,24 +80,67 @@ Widget::Widget(QWidget *parent) : | |||
//检查当前可用串口 | |||
QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts(); | |||
for(QSerialPortInfo serialInfo : serialList) | |||
for(const QSerialPortInfo &serialInfo : serialList) | |||
{ | |||
ui_->comboBox_serialNum->addItem(serialInfo.portName()); | |||
ui_->combo_serial_num->addItem(serialInfo.portName()); | |||
} | |||
} | |||
/** | |||
* @brief Widget 析构函数 | |||
* | |||
* 清理分配的资源:删除UI、Modbus和串口通信对象。 | |||
* 清理分配的资源:删除UI对象。 | |||
*/ | |||
Widget::~Widget() | |||
{ | |||
delete modbus_; | |||
delete serialComm_; | |||
serialComm_->close(); | |||
delete ui_; | |||
} | |||
bool Widget::readLineEdit() | |||
{ | |||
bool ok; | |||
//配置从站参数 | |||
quint16 stationAddress = ui_->line_station_address->text().toUShort(&ok); | |||
if (!ok) | |||
{ | |||
QMessageBox::warning(this, "输入错误", "从站地址应为大于0的整数"); | |||
return false; | |||
} | |||
quint16 functionCode; | |||
switch (ui_->combo_function_code->currentIndex()) | |||
{ | |||
case 0: | |||
functionCode = 0x01; break; | |||
case 1: | |||
functionCode = 0x03; break; | |||
case 2: | |||
functionCode = 0x0F; break; | |||
case 3: | |||
functionCode = 0x10; break; | |||
} | |||
quint16 startAddress = ui_->line_strat_address->text().toUShort(&ok); | |||
if (!ok || startAddress > 19999) | |||
{ | |||
QMessageBox::warning(this, "输入错误", "起始地址必须为0-19999之间的整数"); | |||
return false; | |||
} | |||
quint16 length = ui_->line_length->text().toUShort(&ok); | |||
if (!ok) | |||
{ | |||
QMessageBox::warning(this, "输入错误", "数据长度应为大于0的整数"); | |||
return false; | |||
} | |||
//将从站参数传入modbus中 | |||
if (!modbus_->setStation(stationAddress,functionCode,startAddress,length)) | |||
{ | |||
QMessageBox::warning(this, "输入错误", "从站数据配置应大于0"); | |||
return false; | |||
} | |||
return true; | |||
} | |||
/** | |||
* @brief 串口连接/断开按钮点击处理 | |||
* | |||
@@ -104,12 +152,12 @@ void Widget::btnConnectClicked() | |||
{ | |||
if (ui_->btn_connect->text() == "连接") | |||
{ | |||
// 获取界面配置的串口参数 | |||
QString portName = ui_->comboBox_serialNum->currentText(); | |||
qint32 baudRate = ui_->comboBox_baudRate->currentText().toInt(); | |||
QSerialPort::DataBits dataBits = QSerialPort::DataBits(ui_->comboBox_dataBit->currentText().toInt()); | |||
// 获取界面配置的串口参数2 | |||
QString portName = ui_->combo_serial_num->currentText(); | |||
QSerialPort::BaudRate baudRate = QSerialPort::BaudRate(ui_->combo_baud_rate->currentText().toInt()); | |||
QSerialPort::DataBits dataBits = QSerialPort::DataBits(ui_->combo_data_bit->currentText().toInt()); | |||
QSerialPort::Parity parity; | |||
switch (ui_->comboBox_xiaoyan->currentIndex()) | |||
switch (ui_->combo_check->currentIndex()) | |||
{ | |||
case 0: parity = QSerialPort::NoParity; break; | |||
case 1: parity = QSerialPort::OddParity; break; | |||
@@ -139,7 +187,6 @@ void Widget::btnConnectClicked() | |||
ui_->btn_read->setEnabled(false); | |||
ui_->btn_write->setEnabled(false); | |||
} | |||
} | |||
/** | |||
@@ -151,11 +198,12 @@ void Widget::btnConnectClicked() | |||
*/ | |||
void Widget::onBtnWriteClicked() | |||
{ | |||
switch (ui_->comboBox_gongnengma->currentIndex()) //判断当前功能码 | |||
// 校验起始地址 | |||
switch (ui_->combo_function_code->currentIndex()) //判断当前功能码 | |||
{ | |||
case 2: //写多个线圈 | |||
{ | |||
QString sendData = ui_->lineEdit->text().trimmed(); //读取输入框中的数据 | |||
QString sendData = ui_->line_input->text().trimmed(); //读取输入框中的数据 | |||
//判断输入框中是否为空 | |||
if (sendData.isEmpty()) | |||
{ | |||
@@ -180,23 +228,20 @@ void Widget::onBtnWriteClicked() | |||
coils.append(ch == '1'); | |||
} | |||
//配置从站参数 | |||
quint16 stationAddress = ui_->lineEdit_stationAddress->text().toInt(); | |||
quint16 functionCode = 0x0f; | |||
quint16 stratAddress = ui_->lineEdit_stratAddress->text().toInt(); | |||
quint16 length = ui_->lineEdit_length->text().toInt(); | |||
//判断输入数据长度与设置的长度是否一致 | |||
if (coils.size() != length) | |||
if (coils.size() != ui_->line_length->text().toUShort()) | |||
{ | |||
QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配"); | |||
return; | |||
} | |||
//将从站参数传入modbus中 | |||
modbus_->Set(stationAddress,functionCode,stratAddress,length); | |||
modbus_->WriteCoil(coils); //使用WriteCoil方法生成命令报文 | |||
serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 | |||
if (!readLineEdit()) | |||
{ | |||
return; | |||
} | |||
modbus_->writeCoil(coils); //使用WriteCoil方法生成命令报文 | |||
serialComm_->sendData(modbus_->sendCommand()); //发送生成的命令报文 | |||
//锁定按钮 | |||
ui_->btn_read->setEnabled(false); | |||
ui_->btn_write->setEnabled(false); | |||
@@ -206,7 +251,7 @@ void Widget::onBtnWriteClicked() | |||
case 3: //写多个寄存器 | |||
{ | |||
QString sendData = ui_->lineEdit->text().trimmed(); //读取数据 | |||
QString sendData = ui_->line_input->text().trimmed(); //读取数据 | |||
//判断输入框中是否为空 | |||
if (sendData.isEmpty()) | |||
{ | |||
@@ -221,7 +266,7 @@ void Widget::onBtnWriteClicked() | |||
// 检查输入是否是十进制 | |||
bool isDecimal = true; | |||
for (const QString &s : sl) | |||
for (QString s : sl) | |||
{ | |||
s.toInt(&ok); | |||
if (!ok) | |||
@@ -232,7 +277,7 @@ void Widget::onBtnWriteClicked() | |||
} | |||
//若判断输入的数据不是10进制,则按16进制数据处理 | |||
for (const QString &s : sl) | |||
for (QString s : sl) | |||
{ | |||
quint16 v; | |||
if (isDecimal) | |||
@@ -257,24 +302,21 @@ void Widget::onBtnWriteClicked() | |||
} | |||
values.append(v); | |||
} | |||
//配置从站参数 | |||
quint16 stationAddress = ui_->lineEdit_stationAddress->text().toInt(); | |||
quint16 functionCode = 0x10; | |||
quint16 stratAddress = ui_->lineEdit_stratAddress->text().toInt(); | |||
quint16 length = ui_->lineEdit_length->text().toInt(); | |||
//判断输入数据长度与设置的长度是否一致 | |||
if (values.size() != length) | |||
if (values.size() != ui_->line_length->text().toUShort()) | |||
{ | |||
QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配"); | |||
return; | |||
} | |||
//将从站参数传入modbus中 | |||
modbus_->Set(stationAddress,functionCode,stratAddress,length); | |||
modbus_->WriteRegister(values); //使用WriteRegister方法生成命令报文 | |||
serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 | |||
if (!readLineEdit()) | |||
{ | |||
return; | |||
} | |||
modbus_->writeRegister(values); //使用WriteRegister方法生成命令报文 | |||
serialComm_->sendData(modbus_->sendCommand()); //发送生成的命令报文 | |||
//锁定按钮 | |||
ui_->btn_read->setEnabled(false); | |||
ui_->btn_write->setEnabled(false); | |||
@@ -300,31 +342,19 @@ void Widget::onBtnWriteClicked() | |||
void Widget::onBtnReadClicked() | |||
{ | |||
//若当前操作设置错误则进行提示 | |||
if (ui_->comboBox_gongnengma->currentIndex() == 2 || | |||
ui_->comboBox_gongnengma->currentIndex() == 3) | |||
if (ui_->combo_function_code->currentIndex() == 2 || | |||
ui_->combo_function_code->currentIndex() == 3) | |||
{ | |||
QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器"); | |||
return; | |||
} | |||
//设置从站参数 | |||
quint16 stationAddress = ui_->lineEdit_stationAddress->text().toInt(); | |||
quint16 functionCode; | |||
quint16 stratAddress = ui_->lineEdit_stratAddress->text().toInt(); | |||
quint16 length = ui_->lineEdit_length->text().toInt(); | |||
if (ui_->comboBox_gongnengma->currentIndex() == 0) //读线圈 | |||
{ | |||
functionCode = 0x01; | |||
} | |||
else if(ui_->comboBox_gongnengma->currentIndex() == 1) //读寄存器 | |||
if (!readLineEdit()) | |||
{ | |||
functionCode = 0x03; | |||
return; | |||
} | |||
modbus_->readCoilAndReg(); //使用ReadCoilAndReg方法生成命令报文 | |||
//将从站参数传入modbus中 | |||
modbus_->Set(stationAddress,functionCode,stratAddress,length); | |||
modbus_->ReadCoilAndReg(); //使用ReadCoilAndReg方法生成命令报文 | |||
serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 | |||
serialComm_->sendData(modbus_->sendCommand()); //发送生成的命令报文 | |||
//锁定按钮 | |||
ui_->btn_read->setEnabled(false); | |||
ui_->btn_write->setEnabled(false); | |||
@@ -338,7 +368,7 @@ void Widget::onBtnReadClicked() | |||
*/ | |||
void Widget::onBtnSavedateClicked() | |||
{ | |||
SaveDate(this,ui_->textEdit_2); | |||
saveDate(this,ui_->text_information); | |||
} | |||
/** | |||
@@ -348,7 +378,7 @@ void Widget::onBtnSavedateClicked() | |||
*/ | |||
void Widget::onBtnReaddateClicked() | |||
{ | |||
ReadDate(this,ui_->textEdit_2); | |||
readDate(this,ui_->text_information); | |||
} | |||
/** | |||
@@ -356,7 +386,7 @@ void Widget::onBtnReaddateClicked() | |||
*/ | |||
void Widget::onBtnCleardateClicked() | |||
{ | |||
ui_->textEdit_2->clear(); | |||
ui_->text_information->clear(); | |||
} | |||
/** | |||
@@ -364,7 +394,15 @@ void Widget::onBtnCleardateClicked() | |||
*/ | |||
void Widget::onBtnClearreadClicked() | |||
{ | |||
ui_->textEdit->clear(); | |||
ui_->text_data->clear(); | |||
} | |||
void Widget::sicalDisconnected() | |||
{ | |||
ui_->btn_connect->setText("连接"); | |||
ui_->btn_read->setEnabled(false); | |||
ui_->btn_write->setEnabled(false); | |||
QMessageBox::warning(this, "警告", "串口已断开连接"); | |||
} | |||
/** | |||
@@ -376,7 +414,7 @@ void Widget::onBtnClearreadClicked() | |||
*/ | |||
void Widget::onSerialDataReceived(const QByteArray &data) | |||
{ | |||
QByteArray revMessage = modbus_->Receive(data); // 接收的原始数据交给Modbus解析 | |||
QByteArray revMessage = modbus_->receive(data); // 接收的原始数据交给Modbus解析 | |||
// 启用操作按钮 | |||
ui_->btn_read->setEnabled(true); | |||
ui_->btn_write->setEnabled(true); | |||
@@ -388,18 +426,20 @@ void Widget::onSerialDataReceived(const QByteArray &data) | |||
} | |||
//显示接收到的响应报文 | |||
ui_->textEdit_2->append("接收报文:" + revMessage.toHex().toUpper()); | |||
ui_->text_information->append("接收报文:" + revMessage.toHex().toUpper()); | |||
// 检查响应报文中是否带有错误码 | |||
int exCode = modbus_->ErrorCheck(); | |||
quint8 exCode = modbus_->errorCheck(); | |||
if (exCode) | |||
{ | |||
QString errorMsg; | |||
switch (exCode) { | |||
switch (exCode) | |||
{ | |||
case 0x01: errorMsg = "非法功能码"; break; | |||
case 0x02: errorMsg = "非法数据地址"; break; | |||
case 0x03: errorMsg = "非法数据值"; break; | |||
case 0x04: errorMsg = "从站设备故障"; break; | |||
case 0x11: errorMsg = "数据域个数异常"; break; | |||
default: errorMsg = "未知异常"; break; | |||
} | |||
QMessageBox::warning(this, "异常响应", | |||
@@ -408,25 +448,25 @@ void Widget::onSerialDataReceived(const QByteArray &data) | |||
} | |||
// 解析并显示数据(根据功能码) | |||
switch (ui_->comboBox_gongnengma->currentIndex()) | |||
switch (ui_->combo_function_code->currentIndex()) | |||
{ | |||
case 0: | |||
{ // 读线圈 | |||
QVector<bool> coils = modbus_->AnalReadCoil(); //使用AnalReadCoil方法解析线圈数据 | |||
ui_->textEdit->append("线圈状态:"); | |||
for (int i = 0; i < ui_->lineEdit_length->text().toInt(); i++) | |||
QVector<bool> coils = modbus_->analReadCoil(); //使用AnalReadCoil解析线圈数据 | |||
ui_->text_data->append("线圈状态:"); | |||
for (int i = 0; i < ui_->line_length->text().toInt(); i++) | |||
{ | |||
ui_->textEdit->append(QString("线圈%1: %2").arg(i+1).arg(coils[i] ? "1" : "0")); | |||
ui_->text_data->append(QString("线圈%1: %2").arg(i+1).arg(coils[i] ? "1" : "0")); | |||
} | |||
break; | |||
} | |||
case 1: | |||
{ // 读寄存器 | |||
QVector<quint16> regs = modbus_->AnalReadReg(); //使用AnalReadReg方法解析寄存器数据 | |||
ui_->textEdit->append("寄存器值:"); | |||
for (int i = 0; i < ui_->lineEdit_length->text().toInt(); i++) | |||
QVector<quint16> regs = modbus_->analReadReg(); //使用AnalReadReg解析寄存器数据 | |||
ui_->text_data->append("寄存器值:"); | |||
for (int i = 0; i < ui_->line_length->text().toInt(); i++) | |||
{ | |||
ui_->textEdit->append(QString("寄存器%1: %2").arg(i+1).arg(regs[i])); | |||
ui_->text_data->append(QString("寄存器%1: %2").arg(i+1).arg(regs[i])); | |||
} | |||
break; | |||
} | |||
@@ -441,7 +481,15 @@ void Widget::onSerialDataReceived(const QByteArray &data) | |||
*/ | |||
void Widget::onSerialStatusChanged(const QString &status) | |||
{ | |||
ui_->textEdit_2->append(status); // 显示状态信息(如连接成功、超时重发等) | |||
ui_->text_information->append(status); // 显示状态信息(如连接成功、超时重发等) | |||
} | |||
void Widget::onTextChanged() | |||
{ | |||
QString data = ui_->line_input->text(); | |||
data.remove(" "); | |||
data.remove(","); | |||
ui_->line_number->setText(QString::number(data.size())); | |||
} | |||
/** | |||
@@ -463,10 +511,27 @@ void Widget::onSerialTimeout() | |||
*/ | |||
void Widget::onBtnRefreshClicked() | |||
{ | |||
ui_->comboBox_serialNum->clear(); | |||
ui_->combo_serial_num->clear(); | |||
QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts(); | |||
for(QSerialPortInfo serialInfo : serialList) | |||
for(const QSerialPortInfo &serialInfo : serialList) | |||
{ | |||
ui_->combo_serial_num->addItem(serialInfo.portName()); | |||
} | |||
} | |||
void Widget::onBtnBatchInputClicked() | |||
{ | |||
bool ok; | |||
int number = ui_->line_data_num->text().toInt(&ok); | |||
if (!ok || number <= 0 || number > 1000) | |||
{ | |||
QMessageBox::warning(this, "输入错误", "请输入1-1000之间的整数"); | |||
return; | |||
} | |||
QString inputData = ui_->line_data->text(); | |||
for (int i = 0; i < number; i++) | |||
{ | |||
ui_->comboBox_serialNum->addItem(serialInfo.portName()); | |||
ui_->line_input->insert(inputData); | |||
} | |||
} |
@@ -38,12 +38,14 @@ public: | |||
* @brief 构造函数,初始化界面和通信对象 | |||
* @param parent 父窗口指针,默认为空 | |||
*/ | |||
explicit Widget(QWidget *parent = 0); | |||
Widget(QWidget *parent = 0); | |||
/** | |||
* @brief 析构函数,清理资源 | |||
*/ | |||
~Widget(); | |||
bool readLineEdit(); | |||
private slots: | |||
//连接/断开串口按钮槽函数 | |||
void btnConnectClicked(); | |||
@@ -66,6 +68,8 @@ private slots: | |||
//清空解析数据文本框按钮槽函数 | |||
void onBtnClearreadClicked(); | |||
void sicalDisconnected(); | |||
/** | |||
* @brief 串口数据接收槽函数 | |||
* @param data 接收到的字节数据 | |||
@@ -78,11 +82,15 @@ private slots: | |||
*/ | |||
void onSerialStatusChanged(const QString &status); | |||
void onTextChanged(); | |||
// 串口通信超时处理槽函数 | |||
void onSerialTimeout(); | |||
void onBtnRefreshClicked(); | |||
void onBtnBatchInputClicked(); | |||
private: | |||
Ui::Widget *ui_; // 指向用户界面的指针 | |||
SerialCommunicator *serialComm_; // 串口通信处理对象 | |||
@@ -69,7 +69,7 @@ | |||
</widget> | |||
</item> | |||
<item row="0" column="1"> | |||
<widget class="QLineEdit" name="lineEdit_stationAddress"> | |||
<widget class="QLineEdit" name="line_station_address"> | |||
<property name="text"> | |||
<string>1</string> | |||
</property> | |||
@@ -83,7 +83,7 @@ | |||
</widget> | |||
</item> | |||
<item row="1" column="1"> | |||
<widget class="QComboBox" name="comboBox_gongnengma"> | |||
<widget class="QComboBox" name="combo_function_code"> | |||
<item> | |||
<property name="text"> | |||
<string>读线圈</string> | |||
@@ -114,7 +114,7 @@ | |||
</widget> | |||
</item> | |||
<item row="2" column="1"> | |||
<widget class="QLineEdit" name="lineEdit_stratAddress"> | |||
<widget class="QLineEdit" name="line_strat_address"> | |||
<property name="text"> | |||
<string>256</string> | |||
</property> | |||
@@ -128,7 +128,7 @@ | |||
</widget> | |||
</item> | |||
<item row="3" column="1"> | |||
<widget class="QLineEdit" name="lineEdit_length"> | |||
<widget class="QLineEdit" name="line_length"> | |||
<property name="text"> | |||
<string>1</string> | |||
</property> | |||
@@ -149,7 +149,7 @@ | |||
<property name="title"> | |||
<string>串口配置</string> | |||
</property> | |||
<widget class="QWidget" name=""> | |||
<widget class="QWidget" name="layoutWidget"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>10</x> | |||
@@ -167,7 +167,7 @@ | |||
</widget> | |||
</item> | |||
<item row="0" column="1"> | |||
<widget class="QComboBox" name="comboBox_serialNum"/> | |||
<widget class="QComboBox" name="combo_serial_num"/> | |||
</item> | |||
<item row="1" column="0"> | |||
<widget class="QLabel" name="label_2"> | |||
@@ -177,7 +177,7 @@ | |||
</widget> | |||
</item> | |||
<item row="1" column="1"> | |||
<widget class="QComboBox" name="comboBox_baudRate"> | |||
<widget class="QComboBox" name="combo_baud_rate"> | |||
<item> | |||
<property name="text"> | |||
<string>1200</string> | |||
@@ -218,7 +218,7 @@ | |||
</widget> | |||
</item> | |||
<item row="2" column="1"> | |||
<widget class="QComboBox" name="comboBox_dataBit"> | |||
<widget class="QComboBox" name="combo_data_bit"> | |||
<item> | |||
<property name="text"> | |||
<string>5</string> | |||
@@ -249,7 +249,7 @@ | |||
</widget> | |||
</item> | |||
<item row="3" column="1"> | |||
<widget class="QComboBox" name="comboBox_xiaoyan"> | |||
<widget class="QComboBox" name="combo_check"> | |||
<item> | |||
<property name="text"> | |||
<string>无校验(N)</string> | |||
@@ -317,7 +317,7 @@ | |||
<property name="title"> | |||
<string>数据读取</string> | |||
</property> | |||
<widget class="QTextEdit" name="textEdit"> | |||
<widget class="QTextEdit" name="text_data"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>10</x> | |||
@@ -343,7 +343,7 @@ | |||
<property name="title"> | |||
<string>状态通知</string> | |||
</property> | |||
<widget class="QTextEdit" name="textEdit_2"> | |||
<widget class="QTextEdit" name="text_information"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>10</x> | |||
@@ -385,7 +385,7 @@ | |||
相邻寄存器的值之间用英文","分离。</string> | |||
</property> | |||
</widget> | |||
<widget class="QLineEdit" name="lineEdit"> | |||
<widget class="QLineEdit" name="line_input"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>10</x> | |||
@@ -436,6 +436,78 @@ | |||
</item> | |||
</layout> | |||
</widget> | |||
<widget class="QPushButton" name="btn_batch_input"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>590</x> | |||
<y>400</y> | |||
<width>93</width> | |||
<height>28</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>批量输入</string> | |||
</property> | |||
</widget> | |||
<widget class="QLineEdit" name="line_data_num"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>590</x> | |||
<y>440</y> | |||
<width>71</width> | |||
<height>21</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
<widget class="QLineEdit" name="line_data"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>590</x> | |||
<y>470</y> | |||
<width>71</width> | |||
<height>21</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
<widget class="QLabel" name="label_4"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>670</x> | |||
<y>440</y> | |||
<width>21</width> | |||
<height>16</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>个</string> | |||
</property> | |||
</widget> | |||
<widget class="QLabel" name="label_5"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>590</x> | |||
<y>350</y> | |||
<width>51</width> | |||
<height>41</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>数据数:</string> | |||
</property> | |||
</widget> | |||
<widget class="QLineEdit" name="line_number"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>640</x> | |||
<y>350</y> | |||
<width>51</width> | |||
<height>31</height> | |||
</rect> | |||
</property> | |||
<property name="readOnly"> | |||
<bool>true</bool> | |||
</property> | |||
</widget> | |||
</widget> | |||
<layoutdefault spacing="6" margin="11"/> | |||
<resources/> | |||