开门见山,这里是QtNote。注意,本文中的所有代码均取自我写的ModBus程序,阅读时请适当忽略上下文。
由于项目需要,我需要使用ModBus TCP协议与RTU通信,所以需要使用套接字。套接字的使用现在只是实现了特定的功能,容错性什么的暂时没有考虑。
Socket编程的步骤就是 连接 -》 发送消息 -》 接收消息 (出错处理)。
QT对Socket进行了封装,封装为了QTcpSocket类,调用这个类的 connectToHost
函数即可对目标进行一次连接尝试。(如果连接不上会返回连接错误)
我写了一个自己的MyTcp,在这个类里面有一个QTcpSocket变量。
通过Qt的信号与槽的机制,将消息发送成功与接收消息绑定在一起。
connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(ReadData()));
通过学习我知道了TCP协议氛围 块 和 行 ,ModBus是面向行的,一行内的信息是使用QByteArray编辑的(参照发送消息的函数)。
发送消息使用如下函数
void MyTCP::SendRequest(qint8 slaveaddress, qint8 functioncode, qint16 startaddress, qint16 datanumber) { QByteArray line; QDataStream out(&line, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_DefaultCompiledVersion); out << qint8(00) << qint8(00) << qint8(00) << qint8(00);//protocal identifier 4byte qint16 messagelen = sizeof(slaveaddress) + sizeof(functioncode) + sizeof(startaddress) + sizeof(datanumber); out << messagelen;//High 8-bit ModBus & Low 8-bit message length out << slaveaddress;//slave address out << functioncode;//function code out << startaddress;//High 8-bit & Low 8-bit of start address out << datanumber;//High 8-bit & Low 8-bit of data number tcpSocket.write(line); }
注,这里都是针对ModBus发送的信息。可以参照我翻译的RTU文档(还未翻译完)。接收消息如下。
void MyTCP::ReadData() { QDataStream in(&tcpSocket); in.setVersion(QDataStream::Qt_DefaultCompiledVersion); qint64 availabelnum = tcpSocket.bytesAvailable(); QByteArray res = tcpSocket.readAll(); }
由于Modbus返回的信息都是特定格式的字节数据,有的数据会分为高低两块,所以需要位运算来将数据合并。其实这里叫C++位运算更合适一点。下面是位运算操作符。
"&"与 "|"或 ">>"右移 "<<"左移
下面是一个高位与低位合并的函数:
qint16 MyTCP::Combine(qint8 high, qint8 low) { return (high << 8 | low); }
注:本部分摘自网络,因为ModBus只用到了其中一部分,不全。添加表头:
QStandardItemModel *model = new QStandardItemModel(); model->setColumnCount(2); model->setHeaderData(0,Qt::Horizontal,QString::fromLocal8Bit("卡号")); model->setHeaderData(1,Qt::Horizontal,QString::fromLocal8Bit("姓名"));
设置表格属性:
ui->tableView->setModel(model); //表头信息显示居左 ui->tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); //设置列宽不可变 ui->tableView->horizontalHeader()->setResizeMode(0,QHeaderView::Fixed); ui->tableView->horizontalHeader()->setResizeMode(1,QHeaderView::Fixed); ui->tableView->setColumnWidth(0,101); ui->tableView->setColumnWidth(1,102);
注:在进行表格设置时必须是“ui->tableView->setModel(model);”在前,属性具体设置在后,
反之则设置不会生效。如上述代码所示。
添加行(添加三行一样的信息):
for(int i = 0; i < 3; i++) { model->setItem(i,0,new QStandardItem("2009441676")); //设置字符颜色 model->item(i,0)->setForeground(QBrush(QColor(255, 0, 0))); //设置字符位置 model->item(i,0)->setTextAlignment(Qt::AlignCenter); model->setItem(i,1,new QStandardItem(QString::fromLocal8Bit("哈哈"))); }
删除行:
//x是指定删除哪一行 model->removeRow(x); //删除所有行 model->removeRows(0,model->rowCount());
一般如果能够使用Tab键来更换焦点的话会很方便,我发现QLineEdit自带了Tab键的响应,或者说是MainWindow,具体机制没有研究。我进行了布局之后Tab键顺序混乱了,然后我发现了问题在ui_modbus.h文件里面,先上代码。
SlaveAddress = new QLineEdit(centralWidget); SlaveAddress->setObjectName(QStringLiteral("SlaveAddress")); SlaveAddress->setFocusPolicy(Qt::StrongFocus); gridLayout->addWidget(SlaveAddress, 0, 1, 1, 1); FuntionCode = new QLineEdit(centralWidget); FuntionCode->setObjectName(QStringLiteral("FunctionCode")); gridLayout->addWidget(FuntionCode, 1, 1, 1, 1); StartAddress = new QLineEdit(centralWidget); StartAddress->setObjectName(QStringLiteral("StartAddress")); gridLayout->addWidget(StartAddress, 2, 1, 1, 1); DataNumber = new QLineEdit(centralWidget); DataNumber->setObjectName(QStringLiteral("DataNumber")); gridLayout->addWidget(DataNumber, 3, 1, 1, 1); OKButton = new QPushButton(centralWidget); OKButton->setObjectName(QStringLiteral("OKButton")); gridLayout->addWidget(OKButton, 4, 1, 1, 1);
添加的顺序就是光标移动的顺序。即SlaveAddress->FunctionCode->StartAddress->DataNumber->OKButton。其实这并不是QLineEdit独有的,QPushButton也可以。然后我还发现了选中OKButton按钮之后按Enter键没有反应,按Space倒是响应了。