使用clion 新建工程
使用clion 创建一个QT工程,注意其中Qt CMake前缀路径的选择:

新建好的工程如下:

这时,如果直接编译会报一个错误:

经过谷歌查询,这个错误是由于CMake指定了一个Debug版本的QT,但是我并没有找到所谓的Debug版本的下载方式,所以这里CMake我们需要自己写,来规避一些错误。
修改cmake
打开CMakeLists.txt,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| cmake_minimum_required(VERSION 3.21) project(QtWindowsHost)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON)
set(CMAKE_PREFIX_PATH C:/Qt/5.14.2/mingw73_64)
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
add_executable(QtWindowsHost main.cpp )
target_link_libraries(QtWindowsHost Qt5::Core Qt5::Gui Qt5::Widgets )
|
以上模板仅供参考。

这时候我们再去编译,就可以看到编译成功了,生成了一个默认的窗口:

项目设置
这里我们一切从简,只要一个简单的接收发送界面即可,也就是之前文章中所提到的Demo。
重构目录
在这里,为了目录结构的清晰,我们简单重构一下我们的项目目录。重构后如下:

- 我们新建了一个Sources文件夹用于保存所有的QT源文件,在Sources下,Forms和Headers分别存放UI文件和.h头文件,所有的cpp源文件直接放在Sources目录下,这里在新建好文件夹之后,只需要把main.cpp直接拖拽到Sources下即可,clion 会自动帮我们处理cmake文件中路径的问题。
- lib目录存放我们之后需要的MQTT第三方库的 .dll文件和.a文件
- include目录存放第三方库的头文件
新建界面UI类
对着左侧项目根目录右键,选择新建,新建一个QT Ui类:

然后给新建的UI类起一个名字,这里就叫做MainWindow,基类我们选择Qwidget,此时我们可以看到,clion 会自动帮我们把新生成的文件添加到cmake中:

最后我们自己把对应的文件拖拽到对应的目录中即可:

在cmake中添加头文件目录
这时候我们修改了头文件的目录,所以CMake是无法自动寻找到Headers目录的,需要我们手动指定,在cmake中添加如下内容:
1 2 3 4
| include_directories( ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/Sources/Headers )
|

修改mainwindow.cpp
此时我们直接编译会报错,所以需要自己修改一下mainwindow.cpp,将最上方头文件的引入的地址进行修改:

这里需要说明的是,QT会把UI文件预处理成对应的.h文件,然后在CPP文件中引用,生成的.h文件与UI文件同目录,所以我们想要引用的话需要正确设置文件的位置,正如上面所说一样,UI文件都在Forms目录下,所以我们引入的时候也需要指定Forms目录
这时候编译,就可以看到原来报错找不到定义或头文件的地方,都不会再报错了,因为编译过后,ui_MainWindow.h文件就会生成。

修改main.cpp
然后我们将main.cpp修改为以下内容:
1 2 3 4 5 6 7 8 9 10
|
int main (int argc, char *argv[]) { QApplication a (argc, argv); MainWindow w; w.show (); return QApplication::exec (); }
|
编译运行,就能看到我们的结果啦:

在clion 中添加外部工具
在设置中的外部工具中,添加一个新的外部工具,指向designer.exe:

然后我们就能对着UI文件右键,选择外部工具,最后使用QtDesigner打开它:

添加MQTT库
我们的demo是基于MQTT通信的,所以我们要添加一个第三方的动态库,也就是第三方的MQTT库。
有关MQTT库的编译,可以看我之前的一篇文章(QT 安装与编译 MQTT),里面详细介绍了如何自己编译MQTT的库并在Qt Creator下部署。
添加文件
首先确保我们的工程目录结构如下:

然后在lib目录中添加MQTT库:

在include目录中添加头文件:

修改cmake
打开cmake文件,我们需要进行如下修改:
首先在 find_package中添加Network模块:

然后添加第三方库文件目录:

最后在target_link_libraries链接Network模块和MQTT库:

完整的cmake文件如下:
设计界面和逻辑
界面设计和逻辑就不多说了,直接上代码,UI文件可以使用TXT、Vscode等工具打开,然后就能复制粘贴了。
mainwindow.ui

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QWidget" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>452</width> <height>384</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <layout class="QHBoxLayout" name="horizontalLayout_5"> <item> <widget class="QRadioButton" name="rb_status"> <property name="text"> <string>连接状态</string> </property> </widget> </item> <item> <widget class="QPushButton" name="pb_connect"> <property name="text"> <string>Connect</string> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="label"> <property name="text"> <string>Topic:</string> </property> </widget> </item> <item> <widget class="QLineEdit" name="le_pb_topic"/> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QLabel" name="label_2"> <property name="text"> <string>Payload:</string> </property> </widget> </item> <item> <widget class="QLineEdit" name="le_pu_payload"/> </item> </layout> </item> </layout> </item> <item> <widget class="QPushButton" name="pushButton"> <property name="text"> <string>Publish</string> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> <widget class="QLabel" name="label_3"> <property name="text"> <string>Topic:</string> </property> </widget> </item> <item> <widget class="QLineEdit" name="le_sub_topic"/> </item> <item> <widget class="QPushButton" name="pushButton_2"> <property name="text"> <string>Subscribe</string> </property> </widget> </item> <item> <widget class="QPushButton" name="pushButton_3"> <property name="text"> <string>UnSubscribe</string> </property> </widget> </item> </layout> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QLabel" name="label_4"> <property name="text"> <string>LOG:</string> </property> </widget> </item> <item> <widget class="QTextBrowser" name="te_log"/> </item> </layout> </item> </layout> </item> </layout> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui>
|
mainwindow.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
#ifndef _MAINWINDOW_H_ #define _MAINWINDOW_H_
#include <QWidget> #include "qmqtt.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE
class MainWindow : public QWidget { Q_OBJECT
public: explicit MainWindow (QWidget *parent = nullptr); ~MainWindow () override; QMQTT::Client *client; void on_pb_connect_clicked(); void on_pushButton_clicked(); void on_pushButton_2_clicked(); void on_pushButton_3_clicked(); void doConnected(); void doDisconnected(); void doDataReceived(QMQTT::Message); private: Ui::MainWindow *ui; };
#endif
|
mainwindow.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
#include "Headers/mainwindow.h"
#include "Forms/ui_MainWindow.h"
MainWindow::MainWindow (QWidget *parent) : QWidget (parent), ui (new Ui::MainWindow) { ui->setupUi (this); client = new QMQTT::Client (); client->setHostName ("127.0.0.1"); client->setPort (1883); client->setClientId ("clientid"); client->setUsername ("user"); client->setPassword ("password"); ui->pushButton->setEnabled (false); ui->pushButton_2->setEnabled (false); connect (this->client, &QMQTT::Client::connected, this, &MainWindow::doConnected); connect (this->client, &QMQTT::Client::disconnected, this, &MainWindow::doDisconnected); connect (this->client, &QMQTT::Client::received, this, &MainWindow::doDataReceived); } void MainWindow::on_pb_connect_clicked () {
if (!client->isConnectedToHost ()) { client->connectToHost (); } else { client->disconnectFromHost (); }
}
void MainWindow::doConnected () { ui->rb_status->setChecked (true); ui->pb_connect->setText ("Disconnect"); ui->pushButton->setEnabled (true); ui->pushButton_2->setEnabled (true); }
void MainWindow::doDisconnected () { ui->rb_status->setChecked (false); ui->pb_connect->setText ("Connect"); ui->pushButton->setEnabled (false); ui->pushButton_2->setEnabled (false); }
void MainWindow::doDataReceived (const QMQTT::Message &message) { QString mes = QString (message.id ()) + " " + QString (message.qos ()) + " " + message.topic () + " " + message.payload () + "\n"; ui->te_log->append (mes); }
void MainWindow::on_pushButton_clicked () { QString topic = ui->le_pb_topic->text ().trimmed (); QString payload = ui->le_pu_payload->text ().trimmed (); if (topic.isEmpty () || payload.isEmpty ()) { qDebug () << "pub topic and payload is empty!"; return; } QMQTT::Message message (136, topic, payload.toUtf8 ());
client->publish (message); }
void MainWindow::on_pushButton_2_clicked () { QString topic = ui->le_sub_topic->text ().trimmed (); if (topic.isEmpty ()) { qDebug () << "sub topic and payload is empty!"; return; } qDebug () << topic; client->subscribe (topic); }
void MainWindow::on_pushButton_3_clicked () { QString topic = ui->le_sub_topic->text ().trimmed (); if (topic.isEmpty ()) { qDebug () << "sub topic and payload is empty!"; return; } client->unsubscribe (topic);
}
MainWindow::~MainWindow () { delete ui; }
|
运行
将上述代码编译运行后,可以看到如下界面:

经测试,我们的MQTT通信也是正常的。
至此,我们从clion 下创建工程并添加了第三方库的过程已经结束,项目代码已经上传到了GitHub,如果大家有需要可以直接下载下来,如果你的QT版本是5.14.2的话,应该是可以直接运行的。
代码仓库在我的博客里可以找到。
最后,MQTT客户端的代码基本上是来自某个大佬的仓库,但是时间久远忘记是哪个了,这个大佬用的是QT4,MQTT那里是在初始化的时候就填入地址,这会导致所填的地址只能是IP的形式,不能以域名的形式进行MQTT通信,有关这方面的内容可以看我的上一篇文章。