QT下对硬件固件协议的测试小结及几个QT遇到的问题解决
为基于网络udp通信的固件协议进行测试,用QT编写测试工具。其间遇到了一些问题,并逐个解决。特记录在此。
1. 因为基于网络协议,所以需要获取IP,方便发送并接收UDP数据。但因虚拟网卡等,所以需要筛选IP。
QHostAddress addr; QList<QHostAddress> list = QNetworkInterface::allAddresses(); foreach (addr, list) { if(addr.protocol() == QAbstractSocket::IPv4Protocol){ if (addr.toString().contains("192.168")){ address = QHostAddress(addr.toString()); } } }
而在发送命令时,若是以广播方式发送,则也需要考虑使用哪块网卡进行广播。因此要先绑定ip。
udpCMDSocket = new QUdpSocket(this); udpCMDSocket->bind(QHostAddress(ip)); udpCMDSocket->writeDatagram(cmd.toLocal8Bit(), HubAddr, port);
2. 当在主UI上临时需要做高占用的任务如while循环时,可以用QApplication::processEvents()来响应界面上的待处理事件。
while (!binFile.atEnd() { QByteArray byte = binFile.read(len); 。。。 sendCMD2Port(cmd, HubAddr, port); Sleep(interval); QApplication::processEvents(); }
3. 打开文件,若已有路径显示已有路径
QString tmpBinfile; if (BinFilePath.size() == 0) tmpBinfile = QFileDialog::getOpenFileName(this, tr("Open Bin File"), ".", tr("Bin File(*.bin)")); else tmpBinfile = QFileDialog::getOpenFileName(this, tr("Open Bin File"), BinFilePath, tr("Bin File(*.bin)"));
4. 读取文件,生成QListWidget,并排序
bool CaseInsSort(const QString &info1, const QString &info2) { return info1.toLower() < info2.toLower(); } void loadCases() { QFile file(filename); if (file.open(QFile::ReadWrite|QIODevice::Text)){ QTextStream data(&file); QStringList fonts; QString line1; QString line2; while (!data.atEnd()){ line1 = data.readLine(); line2 = data.readLine(); fonts<<line1; caseHash.insert(line1,line2); } qSort(fonts.begin(), fonts.end(), CaseInsSort); caseList->addItems(fonts);//把各行添加到listwidget file.close(); } }
5. QListWidgetItem改名
void renameCase() { QListWidgetItem *caseItem = caseList->currentItem(); if (caseItem){ caseList->openPersistentEditor(caseItem); } }
6. 发送UDP命令
void sendCMD2Port(QString cmd, QHostAddress HubAddr, int port) { udpCMDSocket = new QUdpSocket(this); udpCMDSocket->writeDatagram(cmd.toLocal8Bit(), HubAddr, port); udpCMDSocket->close(); }
7. 监听线程独立于主线程,包括对监听数据处理都在run中
void listenPortThread::run()
{
QUdpSocket udpListenSocket;
bool result = udpListenSocket.bind(address, port,QUdpSocket::ShareAddress);//绑定到指定的端口号
if(!result)//若绑定不成功,给出出错信息
{
udpListenSocket.close();
stop();
return;
}
islisten = true;
m_bStop = false;
while(!m_bStop)
{
QByteArray array;
QHostAddress address;
quint16 port;
if (islisten){
while (udpListenSocket.hasPendingDatagrams()) {
array.resize(udpListenSocket.pendingDatagramSize());//根据可读数据来设置空间大小
udpListenSocket.readDatagram(array.data(),array.size(),&address,&port); //读取数据
QString str = array;
。。。
8.单次定时器控制,判断是否在时限内收到回复。到时间在timeout函数中判断。
QTimer::singleShot(1500,this,SLOT(timeout())); sendCMD2Port(cmd, HubAddr, port);
9. 解析收到的json回复,通过关键字查询结果
bool listenPortThread::parseJson(QByteArray array, QString *sendID, QString *result, QString key){ QJsonParseError jsonError; QJsonDocument doucment = QJsonDocument::fromJson(array, &jsonError); // 转化为 JSON 文档 if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误 if (doucment.isObject()) { // JSON 文档为对象 QJsonObject object = doucment.object(); // 转化为对象 if (!key.isEmpty() && object.contains(key)){ QJsonValue Jvalue = object.value(key); if (Jvalue.isString()) { QString strvalue = Jvalue.toString(); *result = strvalue; } }
10. 返回的json数据,包括base64数据时,解码并还原为float
QJsonValue Jdata = object1.value("data"); if (Jdata.isString()) { QString strdata = Jdata.toString(); QByteArray Hex = QByteArray::fromBase64(strdata.toLatin1()).toHex(); QString calidata = ""; for (int i=0; i<12; i++){ QString str =Hex.mid(i*8+6,2)+Hex.mid(i*8+4,2)+Hex.mid(i*8+2,2)+Hex.mid(i*8,2); //低位到高位 int iHex = str.toUInt(0,16); float fHex = *(float*)&iHex ; calidata += QString("%1, ").arg(fHex); } *result = calidata; }
11. 在pro文件中通过VERSION设定APP版本号,并在程序中获取APP版本号
VERSION = 0.0.0.1
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
In the main.cpp:
#include <QApplication>
QCoreApplication::setApplicationVersion(QString(APP_VERSION));
Wherever else in your sources, e.g. in the imaginary controller.cpp:
#include <QApplication>
QString yourAppVersion = QCoreApplication::applicationVersion();
12. 发布release版本程序,自动打包QT所需动态库
Windows开发环境下windeployqt工具 (如果你已经将Qt的bin目录加入PATH环境,那么你可以直接在命令行使用windeployqt调用.)。首先,将项目中的release文件中的可执行文件拷到一个新建的文件夹中,例如project.exe,用Qt自带的生成必备的dll文件的程序windeployqt,来把必要的动态库拷到该文件夹中,打开命令行,输入windeployqtproject.exe,这时候大部分的dll文件都自动拷贝过来了,但是如果项目还用了一些其他的SDK,比如OpenCV,Chartdir51等等,就需要手动将所需dll拷贝过来,如果不知道还需要哪些软件,可以用Dependency Walker来查看缺少哪些dll文件。
注意:如果发布的应用是Qt Quick Application应用,那么命令行需要加上QML的安装目录。命令中的D:\Qt\Qt5.5.1\qml是qml的安装目录,请换成你自己的qml安装目录!!!!!
windeployqt hello.exe --qmldir D:\Qt\Qt5.5.1\qml
接下来要高大上的朋友就可以用Enigma VirtualBox软件虚拟化工具将多个文件封装到应用程序主文件,从而制作成为单执行文件的绿色软件。
2. 工具-->外部-->linguist-->更新,来生成ts文件。但若直接使用,则qt可能会报错。因为没有找到VS的环境。所以
a. Create a translate.bat with the following single-line content:
"%programfiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" & lupdate.exe %1
and put it under %{CurrentProject:QT_INSTALL_BINS} (in my case C:\Qt\Qt5.9.1\5.9.1\msvc2017_64\bin). Make sure that the translate.bat contains the correct path to vcvars64.bat on your machine. You may as well put another version of vcvars if you need.
b. In Qt Creator select Tools/Options/Environment/External Tools and then Add Tool under Linguist category. Name it something like Create/Update TS files. Setup the fields for this entry as follows:
Executable: %{CurrentProject:QT_INSTALL_BINS}\translate.bat,
Arguments: %{CurrentProject:FilePath},
Working directory: %{CurrentProject:QT_INSTALL_BINS},
as shown here: External Tools, then apply the changes and close the Options window.
c. Go to Tools/External/Linguist, select Create/Update TS files and now it should do the job as usual.
3. 在linguist中编辑ts文件,加语言翻译
4. 在linguist直接发布,或用qt的工具-->外部-->linguist-->发布,来用ts文件生成qm文件
5. 在主程序中
QApplication app(argc, argv);
QTranslator translator;
bool ok=translator.load(":/qm/filename_CN.qm");
app.installTranslator(&translator);
14. qt全局宏变量
DEFINES += HELLO=\\\"$$PWD/\\\"
DEFINES += SUCCESS=\\\"string\\\"
在.cpp中测试代码如下:
#ifdef HELLO
qDebug()<<"HELLO="<<HELLO;
#else
qDebug()<<"bye";
#endif
上一篇: python安装qt