新建工程
使用clion 新建工程
使用clion 创建一个QT工程,注意其中Qt CMake前缀路径的选择:
新建好的工程如下:
这时,如果直接编译会报一个错误:
经过谷歌查询,这个错误是由于CMake指定了一个Debug版本的QT,但是我并没有找到所谓的Debug版本的下载方式,所以这里CMake我们需要自己写,来规避一些错误。
修改cmake
打开CMakeLists.txt,修改如下:
1 cmake_minimum_required(VERSION 3.21) #cmake最低版本
2 project(QtWindowsHost) #工程名
3
4 set(CMAKE_CXX_STANDARD 14) #C++标准
5 # 开启QT用于预处理的组件
6 set(CMAKE_AUTOMOC ON)
7 set(CMAKE_AUTORCC ON)
8 set(CMAKE_AUTOUIC ON)
9 # 设置cmake模块的查询目录,注意这里的路径,到mingw路径即可
10 set(CMAKE_PREFIX_PATH C:/Qt/5.14.2/mingw73_64)
11 # 查找QT的模块
12 find_package(Qt5 COMPONENTS
13 Core
14 Gui
15 Widgets
16 REQUIRED)
17 # 添加源文件
18 add_executable(QtWindowsHost
19 main.cpp
20 )
21 # 添加模块
22 target_link_libraries(QtWindowsHost
23 Qt5::Core
24 Qt5::Gui
25 Qt5::Widgets
26 )
以上模板仅供参考。
这时候我们再去编译,就可以看到编译成功了,生成了一个默认的窗口:
项目设置
这里我们一切从简,只要一个简单的接收发送界面即可,也就是之前文章中所提到的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 include_directories(
2 ${PROJECT_SOURCE_DIR}/include
3 ${PROJECT_SOURCE_DIR}/Sources/Headers
4 )
修改mainwindow.cpp
此时我们直接编译会报错,所以需要自己修改一下mainwindow.cpp
,将最上方头文件的引入的地址进行修改:
这里需要说明的是,QT会把UI文件预处理成对应的.h文件,然后在CPP文件中引用,生成的.h文件与UI文件同目录,所以我们想要引用的话需要正确设置文件的位置,正如上面所说一样,UI文件都在Forms目录下,所以我们引入的时候也需要指定Forms目录
这时候编译,就可以看到原来报错找不到定义或头文件的地方,都不会再报错了,因为编译过后,ui_MainWindow.h
文件就会生成。
修改main.cpp
然后我们将main.cpp
修改为以下内容:
1 #include <QApplication>
2 #include "mainwindow.h"
3
4 int main (int argc, char *argv[])
5 {
6 QApplication a (argc, argv);
7 MainWindow w;
8 w.show ();
9 return QApplication::exec ();
10 }
编译运行,就能看到我们的结果啦:
在clion 中添加外部工具
在设置中的外部工具中,添加一个新的外部工具,指向designer.exe
:
然后我们就能对着UI文件右键,选择外部工具,最后使用QtDesigner打开它:
添加MQTT库
我们的demo是基于MQTT通信的,所以我们要添加一个第三方的动态库,也就是第三方的MQTT库。
添加文件
首先确保我们的工程目录结构如下:
然后在lib目录中添加MQTT库:
在include目录中添加头文件:
修改cmake
打开cmake文件,我们需要进行如下修改:
首先在 find_package中添加Network模块:
然后添加第三方库文件目录:
最后在target_link_libraries链接Network模块和MQTT库:
完整的cmake文件如下:
1 cmake_minimum_required(VERSION 3.21)
2 project(QtWindowsHost)
3 set(CMAKE_CXX_STANDARD 14)
4 set(CMAKE_AUTOMOC ON)
5 set(CMAKE_AUTORCC ON)
6 set(CMAKE_AUTOUIC ON)
7 set(CMAKE_PREFIX_PATH C:/Qt/5.14.2/mingw73_64)
8 include_directories(
9 ${PROJECT_SOURCE_DIR}/include
10 ${PROJECT_SOURCE_DIR}/Sources/Headers
11 )
12 find_package(Qt5 COMPONENTS
13 Core
14 Gui
15 Widgets
16 Network
17 REQUIRED)
18 link_directories(./lib)
19 add_executable(QtWindowsHost
20 Sources/main.cpp
21 Sources/mainwindow.cpp Sources/Headers/mainwindow.h Sources/Forms/mainwindow.ui)
22 target_link_libraries(QtWindowsHost
23 Qt5::Core
24 Qt5::Gui
25 Qt5::Widgets
26 Qt5::Network
27 Qt5Qmqtt
28 )
设计界面和逻辑
界面设计和逻辑就不多说了,直接上代码,UI文件可以使用TXT、Vscode等工具打开,然后就能复制粘贴了。
mainwindow.ui
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
3 <class>MainWindow</class>
4 <widget class="QWidget" name="MainWindow">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>452</width>
10 <height>384</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>MainWindow</string>
15 </property>
16 <layout class="QGridLayout" name="gridLayout">
17 <item row="0" column="0">
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <layout class="QHBoxLayout" name="horizontalLayout_5">
21 <item>
22 <widget class="QRadioButton" name="rb_status">
23 <property name="text">
24 <string>连接状态</string>
25 </property>
26 </widget>
27 </item>
28 <item>
29 <widget class="QPushButton" name="pb_connect">
30 <property name="text">
31 <string>Connect</string>
32 </property>
33 </widget>
34 </item>
35 </layout>
36 </item>
37 <item>
38 <layout class="QHBoxLayout" name="horizontalLayout_3">
39 <item>
40 <layout class="QVBoxLayout" name="verticalLayout">
41 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout">
43 <item>
44 <widget class="QLabel" name="label">
45 <property name="text">
46 <string>Topic:</string>
47 </property>
48 </widget>
49 </item>
50 <item>
51 <widget class="QLineEdit" name="le_pb_topic"/>
52 </item>
53 </layout>
54 </item>
55 <item>
56 <layout class="QHBoxLayout" name="horizontalLayout_2">
57 <item>
58 <widget class="QLabel" name="label_2">
59 <property name="text">
60 <string>Payload:</string>
61 </property>
62 </widget>
63 </item>
64 <item>
65 <widget class="QLineEdit" name="le_pu_payload"/>
66 </item>
67 </layout>
68 </item>
69 </layout>
70 </item>
71 <item>
72 <widget class="QPushButton" name="pushButton">
73 <property name="text">
74 <string>Publish</string>
75 </property>
76 </widget>
77 </item>
78 </layout>
79 </item>
80 <item>
81 <layout class="QHBoxLayout" name="horizontalLayout_4">
82 <item>
83 <widget class="QLabel" name="label_3">
84 <property name="text">
85 <string>Topic:</string>
86 </property>
87 </widget>
88 </item>
89 <item>
90 <widget class="QLineEdit" name="le_sub_topic"/>
91 </item>
92 <item>
93 <widget class="QPushButton" name="pushButton_2">
94 <property name="text">
95 <string>Subscribe</string>
96 </property>
97 </widget>
98 </item>
99 <item>
100 <widget class="QPushButton" name="pushButton_3">
101 <property name="text">
102 <string>UnSubscribe</string>
103 </property>
104 </widget>
105 </item>
106 </layout>
107 </item>
108 <item>
109 <layout class="QVBoxLayout" name="verticalLayout_2">
110 <item>
111 <widget class="QLabel" name="label_4">
112 <property name="text">
113 <string>LOG:</string>
114 </property>
115 </widget>
116 </item>
117 <item>
118 <widget class="QTextBrowser" name="te_log"/>
119 </item>
120 </layout>
121 </item>
122 </layout>
123 </item>
124 </layout>
125 </widget>
126 <layoutdefault spacing="6" margin="11"/>
127 <resources/>
128 <connections/>
129 </ui>
mainwindow.h
1 //
2 // Created by XinMouRen on 2022/1/25.
3 //
4
5 #ifndef _MAINWINDOW_H_
6 #define _MAINWINDOW_H_
7
8 #include <QWidget>
9 #include "qmqtt.h"
10 QT_BEGIN_NAMESPACE
11 namespace Ui
12 {
13 class MainWindow;
14 }
15 QT_END_NAMESPACE
16
17 class MainWindow : public QWidget {
18 Q_OBJECT
19
20 public:
21 explicit MainWindow (QWidget *parent = nullptr);
22 ~MainWindow () override;
23 QMQTT::Client *client;
24 void on_pb_connect_clicked();
25 void on_pushButton_clicked();
26 void on_pushButton_2_clicked();
27 void on_pushButton_3_clicked();
28 void doConnected(); //MQTT 连接成功
29 void doDisconnected();//MQTT连接断开
30 void doDataReceived(QMQTT::Message);//MQTT收到数据
31 private:
32 Ui::MainWindow *ui;
33 };
34
35 #endif //_MAINWINDOW_H_
mainwindow.cpp
1 //
2 // Created by XinMouRen on 2022/1/25.
3 //
4
5 // You may need to build the project (run Qt uic code generator) to get "ui_MainWindow.h" resolved
6
7 #include "Headers/mainwindow.h"
8 // #include "ui_MainWindow.h" 这是原来的
9 #include "Forms/ui_MainWindow.h"
10
11 MainWindow::MainWindow (QWidget *parent) :
12 QWidget (parent), ui (new Ui::MainWindow)
13 {
14 ui->setupUi (this);
15 client = new QMQTT::Client ();
16 client->setHostName ("127.0.0.1");
17 client->setPort (1883);
18 client->setClientId ("clientid");
19 client->setUsername ("user");
20 client->setPassword ("password");
21 ui->pushButton->setEnabled (false);
22 ui->pushButton_2->setEnabled (false);
23 connect (this->client, &QMQTT::Client::connected, this, &MainWindow::doConnected);
24 connect (this->client, &QMQTT::Client::disconnected, this, &MainWindow::doDisconnected);
25 connect (this->client, &QMQTT::Client::received, this, &MainWindow::doDataReceived);
26 }
27 void MainWindow::on_pb_connect_clicked ()
28 {
29
30 if (!client->isConnectedToHost ())
31 {
32 client->connectToHost ();
33 }
34 else
35 {
36 client->disconnectFromHost ();
37 }
38
39 }
40
41 void MainWindow::doConnected ()
42 {
43 ui->rb_status->setChecked (true);
44 ui->pb_connect->setText ("Disconnect");
45 ui->pushButton->setEnabled (true);
46 ui->pushButton_2->setEnabled (true);
47 }
48
49 void MainWindow::doDisconnected ()
50 {
51 ui->rb_status->setChecked (false);
52 ui->pb_connect->setText ("Connect");
53 ui->pushButton->setEnabled (false);
54 ui->pushButton_2->setEnabled (false);
55 }
56
57 void MainWindow::doDataReceived (const QMQTT::Message &message)
58 {
59 QString mes =
60 QString (message.id ()) + " " + QString (message.qos ()) + " " + message.topic () + " " + message.payload ()
61 + "\n";
62 ui->te_log->append (mes);
63 }
64
65 void MainWindow::on_pushButton_clicked ()
66 {
67 QString topic = ui->le_pb_topic->text ().trimmed ();
68 QString payload = ui->le_pu_payload->text ().trimmed ();
69 if (topic.isEmpty () || payload.isEmpty ())
70 {
71 qDebug () << "pub topic and payload is empty!";
72 return;
73 }
74 QMQTT::Message message (136, topic, payload.toUtf8 ());
75
76 client->publish (message);
77 }
78
79 void MainWindow::on_pushButton_2_clicked ()
80 {
81 QString topic = ui->le_sub_topic->text ().trimmed ();
82 if (topic.isEmpty ())
83 {
84 qDebug () << "sub topic and payload is empty!";
85 return;
86 }
87 qDebug () << topic;
88 client->subscribe (topic);
89 }
90
91 void MainWindow::on_pushButton_3_clicked ()
92 {
93 QString topic = ui->le_sub_topic->text ().trimmed ();
94 if (topic.isEmpty ())
95 {
96 qDebug () << "sub topic and payload is empty!";
97 return;
98 }
99 client->unsubscribe (topic);
100
101 }
102
103 MainWindow::~MainWindow ()
104 {
105 delete ui;
106 }
运行
将上述代码编译运行后,可以看到如下界面:
经测试,我们的MQTT通信也是正常的。