基于PyQt5的图形化界面开发——自制MQTT客户端软件

0. 前言

使用 PyQt5 练习写图形化界面,目标是写出一个使用MQTT协议通信的界面,包含 编辑配置、消息发布、消息订阅 三个功能。

操作系统:windows10 专业版
开发环境:Pycharm Conmunity 2022.3
Python版本:Python3.8
第三方库:PyQt5、paho

1. 第三方库的安装及注意事项

需要安装第三方库 PyQt5paho

如果你还不会安装第三方库,你可以参考我的这篇文章学习
Python第三方库安装——使用vscode、pycharm安装Python第三方库

注意!!! 工程结构必须如下:

在这里插入图片描述

2. Editor.py

编辑配置界面代码如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'Editor.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(641, 409)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(490, 290, 93, 28))
        self.pushButton.setObjectName("pushButton")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(260, 100, 171, 21))
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(260, 130, 91, 21))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(180, 100, 51, 20))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(190, 130, 51, 20))
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(170, 160, 81, 20))
        self.label_3.setObjectName("label_3")
        self.lineEdit_3 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_3.setGeometry(QtCore.QRect(260, 160, 171, 21))
        self.lineEdit_3.setObjectName("lineEdit_3")
        self.splitter = QtWidgets.QSplitter(self.centralwidget)
        self.splitter.setGeometry(QtCore.QRect(170, 20, 279, 28))
        self.splitter.setOrientation(QtCore.Qt.Horizontal)
        self.splitter.setObjectName("splitter")
        self.pushButton_3 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_4 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_4.setObjectName("pushButton_4")
        self.pushButton_5 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_5.setObjectName("pushButton_5")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(230, 220, 131, 20))
        self.label_4.setText("")
        self.label_4.setObjectName("label_4")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 641, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "编辑配置"))
        self.pushButton.setText(_translate("MainWindow", "保存配置"))
        self.label.setText(_translate("MainWindow", "Broker"))
        self.label_2.setText(_translate("MainWindow", "Port"))
        self.label_3.setText(_translate("MainWindow", "Client ID"))
        self.pushButton_3.setText(_translate("MainWindow", "消息订阅"))
        self.pushButton_4.setText(_translate("MainWindow", "消息发布"))
        self.pushButton_5.setText(_translate("MainWindow", "编辑配置"))

2.1 配置界面效果演示:

在这里插入图片描述

点击保存配置后将生成一个config.json文件,下一次将不需要重新配置,将默认匹配填充配置文件中的字段。

注意,此时最上方的界面切换函数并没有添加切换功能

3. Publish.py

发布消息界面代码如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'Publish.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(641, 409)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(490, 290, 93, 28))
        self.pushButton.setObjectName("pushButton")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(260, 100, 171, 21))
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(260, 130, 171, 21))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(200, 100, 41, 20))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(200, 130, 51, 20))
        self.label_2.setObjectName("label_2")
        self.splitter = QtWidgets.QSplitter(self.centralwidget)
        self.splitter.setGeometry(QtCore.QRect(170, 20, 279, 28))
        self.splitter.setOrientation(QtCore.Qt.Horizontal)
        self.splitter.setObjectName("splitter")
        self.pushButton_3 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_4 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_4.setObjectName("pushButton_4")
        self.pushButton_5 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_5.setObjectName("pushButton_5")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(230, 220, 211, 20))
        self.label_4.setText("")
        self.label_4.setObjectName("label_4")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(490, 70, 93, 28))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 641, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "消息发布"))
        self.pushButton.setText(_translate("MainWindow", "发布"))
        self.label.setText(_translate("MainWindow", "Topic"))
        self.label_2.setText(_translate("MainWindow", "Info"))
        self.pushButton_3.setText(_translate("MainWindow", "消息订阅"))
        self.pushButton_4.setText(_translate("MainWindow", "消息发布"))
        self.pushButton_5.setText(_translate("MainWindow", "编辑配置"))
        self.pushButton_2.setText(_translate("MainWindow", "连接服务器"))

3.1 消息发布界面演示

使用前需要先点击连接服务器与mqtt服务器进行连接

在这里插入图片描述

点击发布,将提示发布成功

在这里插入图片描述

使用MQTT客户端查看消息是否发布成功,如下,服务器有收到helloworld:

在这里插入图片描述
注意,此时最上方的界面切换函数并没有添加切换功能

4. Subcribe.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file '.\Subscribe.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(641, 409)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(490, 290, 93, 28))
        self.pushButton.setObjectName("pushButton")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(230, 100, 141, 21))
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(230, 140, 171, 21))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(170, 100, 51, 20))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(170, 140, 71, 20))
        self.label_2.setObjectName("label_2")
        self.splitter = QtWidgets.QSplitter(self.centralwidget)
        self.splitter.setGeometry(QtCore.QRect(170, 20, 279, 28))
        self.splitter.setOrientation(QtCore.Qt.Horizontal)
        self.splitter.setObjectName("splitter")
        self.pushButton_3 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_4 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_4.setObjectName("pushButton_4")
        self.pushButton_5 = QtWidgets.QPushButton(self.splitter)
        self.pushButton_5.setObjectName("pushButton_5")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(230, 220, 211, 20))
        self.label_4.setText("")
        self.label_4.setObjectName("label_4")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(490, 70, 93, 28))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 641, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "消息发布"))
        self.pushButton.setText(_translate("MainWindow", "订阅"))
        self.label.setText(_translate("MainWindow", "Topic"))
        self.label_2.setText(_translate("MainWindow", "Receive"))
        self.pushButton_3.setText(_translate("MainWindow", "消息订阅"))
        self.pushButton_4.setText(_translate("MainWindow", "消息发布"))
        self.pushButton_5.setText(_translate("MainWindow", "编辑配置"))
        self.pushButton_2.setText(_translate("MainWindow", "连接服务器"))

订阅消息部分用到了多线程,这样做的目的是防止界面卡死,因为界面是一直在死循环中接收消息的,使用线程的好处是不会引发线程冲突,我们最后的完整代码也依旧可以自由进行界面切换。

4.1 订阅消息效果演示:

同样的需要先连接服务器,我们使用现有的MQTT客户端进行测试:

在这里插入图片描述

可以看到我们自己编写的客户端收到了消息:

在这里插入图片描述
完成子功能测试后,我们可以开始添加界面切换功能了。

5. 界面切换——main.py

界面切换的原理其实就是在显示当前界面时,关闭或者是隐藏其他的所有界面。

例如,我们在发布消息的界面时,订阅消息和编辑配置这两个界面是隐藏起来的

上传视频有点麻烦,这里不上传演示视频了,直接上完整代码:

# code:utf-8
# Create by Maxtang
# 2023/4/21

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidgetItem
from functools import partial
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
import time

from paho.mqtt import client as mqtt_client

import Editor
import Publish
import Subscribe

class UI_Editor(QMainWindow,Editor.Ui_MainWindow):
    def __init__(self):
        super(UI_Editor, self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(lambda: {self.save()})

        self.pushButton_3.clicked.connect(lambda: {self.hide(), self.change(3)})
        self.pushButton_4.clicked.connect(lambda: {self.hide(), self.change(4)})
        self.pushButton_5.clicked.connect(lambda: {self.hide(), self.change(5)})

        try:
            with open("config.json","r+") as f:
                config = eval(f.read())
                self.lineEdit.setText(config["Broker"])
                self.lineEdit_2.setText(config["Port"])
                self.lineEdit_3.setText(config["Client_ID"])
        except:
            print("[!] read config error")
            pass

    def change(self,i):
        edit = UI_Editor()
        sub = UI_Subscribe()
        pub = UI_Publish()

        if i == 3:
            sub.show()
        if i == 4:
            pub.show()
        if i == 5:
            edit.show()

    def save(self):
        try:
            Broker = self.lineEdit.text()
            Port = self.lineEdit_2.text()
            Client_ID = self.lineEdit_3.text()

            with open("config.json","w+") as f:
                f.write(str({"Broker":Broker,"Port":Port,"Client_ID":Client_ID}))
                f.close()
            self.label_4.setText("已保存配置!")
        except:
            self.label_4.setText("保存失败!")

class UI_Publish(QMainWindow,Publish.Ui_MainWindow):
    def __init__(self):
        super(UI_Publish, self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(lambda: {self.publish(client)})
        self.pushButton_2.clicked.connect(lambda: {self.connect()})

        self.pushButton_3.clicked.connect(lambda: {self.hide(), self.change(3)})
        self.pushButton_4.clicked.connect(lambda: {self.hide(), self.change(4)})
        self.pushButton_5.clicked.connect(lambda: {self.hide(), self.change(5)})

    def change(self,i):
        edit = UI_Editor()
        sub = UI_Subscribe()
        pub = UI_Publish()

        if i == 3:
            sub.show()
        if i == 4:
            pub.show()
        if i == 5:
            edit.show()

    def publish(self,client):
        try:
            topic = self.lineEdit.text()
            msg = self.lineEdit_2.text()

            result = client.publish(topic, msg)
            # result: [0, 1]
            status = result[0]

            if status == 0:
                print(f"Send `{msg}` to topic `{topic}`")
                self.label_4.setText("[√]发送成功")
            else:
                print(f"Failed to send message to topic {topic}")

                self.label_4.setText("[×]发送失败")
        except:
            self.label_4.setText("[!]请先连接服务器!")


    def connect(self):
        try:
            def on_connect(client, userdata, flags, rc):
                if rc == 0:
                    print("Connected to MQTT Broker!")
                else:
                    print("Failed to connect, return code %d\n", rc)

            with open("config.json", "r+") as f:
                config = eval(f.read())
                Broker = config["Broker"]
                Port = int(config["Port"])
                Client_ID = config["Client_ID"]
                print(config)
                f.close()

            global client
            client = mqtt_client.Client(Client_ID)
            client.on_connect = on_connect
            client.connect(Broker, Port)

            self.label_4.setText("[√]连接成功")

        except:
            print("[!]Connect error")
            self.label_4.setText("[×]连接失败")
            pass

class UI_Subscribe(QMainWindow,Subscribe.Ui_MainWindow):
    def __init__(self):
        from threading import Thread
        super(UI_Subscribe, self).__init__()
        self.setupUi(self)

        self.pushButton.clicked.connect(lambda: {self.subscribe(client),Thread(target=client.loop_forever).start()})
        self.pushButton_2.clicked.connect(lambda: {self.connect()})

        self.pushButton_3.clicked.connect(lambda: {self.hide(), self.change(3)})
        self.pushButton_4.clicked.connect(lambda: {self.hide(), self.change(4)})
        self.pushButton_5.clicked.connect(lambda: {self.hide(), self.change(5)})

    def change(self,i):
        edit = UI_Editor()
        sub = UI_Subscribe()
        pub = UI_Publish()

        if i == 3:
            sub.show()
        if i == 4:
            pub.show()
        if i == 5:
            edit.show()

    def subscribe(self,client):
        def on_message(client, userdata, msg):
            print("ok")
            self.lineEdit_2.setText(msg.payload.decode())
            self.label_4.setText("[√]接收成功")
            time.sleep(1)

        topic = self.lineEdit.text()

        client.subscribe(topic)
        client.on_message = on_message

        self.label_4.setText("[√]订阅成功")

    def connect(self):
        try:
            def on_connect(client, userdata, flags, rc):
                if rc == 0:
                    print("Connected to MQTT Broker!")
                else:
                    print("Failed to connect, return code %d\n", rc)

            with open("config.json", "r+") as f:
                config = eval(f.read())
                Broker = config["Broker"]
                Port = int(config["Port"])
                Client_ID = config["Client_ID"]
                print(config)
                f.close()

            global client
            client = mqtt_client.Client(Client_ID)
            client.on_connect = on_connect
            client.connect(Broker, Port)

            self.label_4.setText("[√]连接成功")
        except:
            print("[!]Connect error")
            self.label_4.setText("[×]连接失败")
            pass

app = QtWidgets.QApplication(sys.argv)
main = UI_Subscribe()
main.show()
sys.exit(app.exec_())

6. 写在最后

如果你的代码运行报错:

1. 请检查是否安装第三方库 PyQt5 和 paho
2. 工程结构是否与我一致

在这里插入图片描述

其他PyQt5文章

基于PyQt5的图形化界面开发——自制MQTT客户端

基于PyQt5的图形化界面开发——Windows内存资源监视助手[附带编译exe教程]

基于PyQt5的图形化界面开发——模拟医院管理系统

基于PyQt5的图形化界面开发——自制ssh工具

基于PyQt5的图形化界面开发——PyQt示例_计算器

基于PyQt5的图形化界面开发——PyQt示例_扫雷

基于PyQt5的图形化界面开发——自制Redis图形化客户端(文末附源码)

基于PyQt5的图形化界面开发——堆栈动画演示

基于PyQt5的图形化界面开发——队列动画演示

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
PyQt5是一个用于创建图形用户界面的Python库,而MQTT是一种用于消息传输的轻量级通信协议。要在PyQt5中实现MQTT客户端,需要使用PyQt5的各种组件和类来构建界面,并使用MQTT库来实现与MQTT代理服务器的通信。 首先,我们需要在PyQt5中创建一个窗口,并在其中添加必要的组件,如标签、文本框、按钮等。然后,我们可以使用PyQt5的信号和槽机制来连接按钮的点击事件,以便在用户点击按钮时执行相应的操作。 接下来,我们需要使用MQTT库来实现MQTT客户端的功能。我们可以使用MQTT客户端的类来连接到MQTT代理服务器,并订阅和发布主题。我们还可以通过设置回调函数来处理接收到的消息。 在连接到MQTT代理服务器时,我们需要提供服务器的IP地址和端口号,并可以选择使用用户名和密码进行身份验证。一旦连接成功,我们可以使用订阅方法来订阅感兴趣的主题,并使用发布方法向指定的主题发布消息。 对于接收到的消息,我们可以在回调函数中对其进行处理,并将其显示在PyQt5窗口中的文本框或标签中。我们还可以使用发布方法来发布用户输入的消息。 最后,我们可以使用PyQt5的应用程序类来启动我们的MQTT客户端应用程序,并显示主窗口。当用户关闭窗口时,我们可以通过适当的方法来断开MQTT客户端与代理服务器的连接。 综上所述,我们可以利用PyQt5MQTT库来实现一个功能完善的MQTT客户端,以便与MQTT代理服务器进行通信。通过适当的界面设计和回调函数的编写,用户可以方便地订阅和发布主题,并处理接收到的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT_H2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值