利用百度物联网MQTT平台远程在线聊天室的实现,使用自带GUI,并通过Pyinstaller进行exe打包

一、项目简介

XIBI(随便取的)是一个简单的聊天工具,通过MQTT协议实现消息的发布和订阅。你可以通过它和连接到同一个MQTT服务器的其他小伙伴聊天。当然,你也可以把它当作一个学习MQTT协议的练手项目。

主要功能:

  1. 连接MQTT服务器:输入昵称,点击连接按钮,就可以加入聊天室。

  2. 发送消息:在输入框里输入你想说的话,按下回车键或者点击发送按钮,消息就会发送到所有连接到服务器的客户端。

  3. 接收消息:当其他用户发送消息时,你会实时收到并显示在聊天框中。

  4. 语音播报:收到新消息时,系统会用语音提醒你,让你不错过任何一条“撩人”的消息。

二、代码解析

1. 导入必要的库

  • paho.mqtt.client:用于MQTT通信。

  • tkinter:用于创建图形用户界面(GUI)。

  • pyttsx3:用于文本转语音,提醒你有新消息。

  • import paho.mqtt.client as mqtt
    import tkinter as tk
    from tkinter import scrolledtext
    import random
    import json
    import datetime  # 导入 datetime 模块以获取当前时间
    import pyttsx3  # 文本转语音库

    2. 初始化文本转语音引擎

    这里设置了语音的语速和音量,你可以根据自己的喜好调整。

    engine = pyttsx3.init()
    engine.setProperty("rate", 1000)  # 语速
    engine.setProperty("volume", 1.0)  # 音量 (0.0 到 1.0)

    3. MQTT配置

    这里配置了MQTT服务器的地址、端口、主题(最好发布和订阅一样,后续只需要通过id来避免自己的消息就好)以及设备的名称和密钥。你可以根据自己的MQTT服务器进行修改。

    BROKER = "auyppwt.iot.gz.baidubce.com"  # 接入点地址
    PORT = 1883  # 端口
    TOPIC_PUB = "自己设计的主题"  # 发布主题
    TOPIC_SUB = "自己设计的主题"  # 订阅主题
    
    DEVICE_NAME = "百度云物联网平台获得"  # 设备名称
    DEVICE_SECRET = "百度云物联网平台获得"  # 设备密钥

    4. 生成随机昵称

    为了防止你忘记输入昵称,贴心地为你生成了一个随机的昵称,比如“Xibi123456”。这个其实是client_id,为了避免出现两个客户端重复的情况,但是也可以修改为自己喜欢的,当然和自己的小伙伴聊天应该是不会重复的。

    def generate_random_name():
        return str(random.randint(100, 9999999))

    5. 获取当前时间

    每条消息都会带上时间戳,方便你查看消息的发送时间。

    def get_current_time():
        now = datetime.datetime.now()
        return now.strftime("[%H:%M:%S]")

    6. MQTT连接回调

    这个没什么好说的就是当成功连接到MQTT服务器时,系统会提示你“成功连接Xibi!请开始聊天吧”。如果连接失败,会显示错误代码。

    def on_connect(client, userdata, flags, rc, properties=None):
        if rc == 0:
            insert_output_box("成功连接Xibi!\n请开始聊天吧\n", "system")
            client.subscribe(TOPIC_SUB, qos=1)
        else:
            insert_output_box(f"连接失败,错误代码:{rc}\n", "error")

    7. MQTT消息接收回调

    当收到消息时,系统会解析消息内容并显示在聊天框中。如果是你自己发送的消息,系统会自动忽略。这个就是因为发布和订阅的是一个主题,所以如果id一样就不会显示内容。后续实现一对一聊天也可以通过id来判断。

    def on_message(client, userdata, msg):
        try:
            payload = json.loads(msg.payload.decode('utf-8'))
            id = payload.get("id", "未知设备")
            content = payload.get("str", "")
            if id == client_id_entry.get():
                return
            insert_output_box(f"{get_current_time()} {id}: {content}\n", "received")
            engine.say("收到一条新消息")
            engine.runAndWait()
        except json.JSONDecodeError:
            insert_output_box(f"{get_current_time()} 官方提示: {msg.payload.decode('utf-8')}\n", "received")
            engine.say("收到一条新消息")
            engine.runAndWait()
        except Exception as e:
            insert_output_box(f"{get_current_time()} 解析消息失败: {e}\n", "error")

    8. 连接和断开MQTT服务器

    连接和断开MQTT服务器的逻辑都在这里。连接成功后,昵称输入框会被禁用,按钮文本会变成“断开连接”。剩下的就是一些逻辑处理。

    def connect_mqtt():
        global client
        client_id = client_id_entry.get()
        if not client_id:
            insert_output_box("请输入昵称后连接!\n", "error")
            return
    
        client = mqtt.Client(client_id=client_id)
        client.username_pw_set(DEVICE_NAME, DEVICE_SECRET)
        client.on_connect = on_connect
        client.on_message = on_message
    
        try:
            client.connect(BROKER, PORT, keepalive=60)
            client.loop_start()
            insert_output_box(f"{get_current_time()} 正在连接...(昵称: {client_id})\n", "system")
            client_id_entry.config(state=tk.DISABLED)
            connect_button.config(text="断开连接", command=disconnect_mqtt)
        except Exception as e:
            insert_output_box(f"{get_current_time()} 连接失败: {e}\n", "error")
    
    def disconnect_mqtt():
        global client
        if client:
            client.loop_stop()
            client.disconnect()
            insert_output_box(f"{get_current_time()} 已断开与Xibi的连接。\n", "system")
        client_id_entry.config(state=tk.NORMAL)
        connect_button.config(text="连接", command=connect_mqtt)

    9. 发送消息

    发送消息的逻辑很简单,输入消息后按下回车键或者点击发送按钮,消息就会发送到MQTT服务器。会采用JSON格式发送,处理收到的消息也会按照JSON格式解析。

    def send_message():
        if not client:
            insert_output_box("请先连接XIBI!\n", "error")
            return
    
        message = input_box.get()
        if not message:
            insert_output_box("请输入消息!\n", "error")
            return
    
        try:
            payload = {
                "id": client_id_entry.get(),
                "str": message
            }
            client.publish(TOPIC_PUB, json.dumps(payload), qos=1)
            insert_output_box(f"{get_current_time()} {payload['id']}: {payload['str']}\n", "sent")
            input_box.delete(0, tk.END)
        except Exception as e:
            insert_output_box(f"{get_current_time()} 发送失败: {e}\n", "error")

    10. GUI界面

    GUI界面使用了tkinter库,界面简洁明了,功能齐全。你可以通过输入框输入消息,按下回车键或者点击发送按钮发送消息。

    root = tk.Tk()
    root.title("XIBI(线上交友)")
    root.geometry("500x400")
    root.resizable(False, False)
    
    client_id_label = tk.Label(root, text="昵称(不能中文):")
    client_id_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
    client_id_entry = tk.Entry(root, width=30)
    client_id_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
    client_id_entry.insert(0, 'Xibi'+generate_random_name())
    
    connect_button = tk.Button(root, text="连接", command=connect_mqtt, width=10)
    connect_button.grid(row=0, column=2, padx=5, pady=5, sticky="e")
    
    output_box = scrolledtext.ScrolledText(root, width=60, height=20, font=("微软雅黑", 10), state=tk.DISABLED)
    output_box.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky="nsew")
    
    output_box.tag_config("sent", foreground="green")
    output_box.tag_config("received", foreground="blue")
    output_box.tag_config("system", foreground="gray")
    output_box.tag_config("error", foreground="red")
    
    input_box = tk.Entry(root, width=50)
    input_box.grid(row=2, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
    input_box.bind("<Return>", on_enter_key)
    
    send_button = tk.Button(root, text="发送", command=send_message, width=10)
    send_button.grid(row=2, column=2, padx=5, pady=5, sticky="e")
    
    root.grid_columnconfigure(1, weight=1)
    root.grid_rowconfigure(1, weight=1)
    
    root.mainloop()
    
    if client:
        client.loop_stop()
        client.disconnect()

    三、总结

    通过这个项目,你不仅可以学习到如何使用Python和MQTT协议进行通信,还可以打造一个属于自己的线上交友工具。当然,XIBI只是一个简单的示例,你可以根据自己的需求进行扩展,比如添加私聊功能、表情包支持等等。如果有什么问题,欢迎在评论区留言,我会尽力解答。

    1.全部代码

    import paho.mqtt.client as mqtt
    import tkinter as tk
    from tkinter import scrolledtext
    import random
    import json
    import datetime  # 导入 datetime 模块以获取当前时间
    import pyttsx3  # 文本转语音库
    
    # 初始化文本转语音引擎
    engine = pyttsx3.init()
    
    # 设置语音属性
    engine.setProperty("rate", 1000)  # 语速
    engine.setProperty("volume", 1.0)  # 音量 (0.0 到 1.0)
    
    
    # MQTT 配置
    BROKER = "auyppwt.iot.gz.baidubce.com"  # 接入点地址
    PORT = 1883  # SSL端口
    TOPIC_PUB = "xx"  # 发布主题
    TOPIC_SUB = "xx"  # 订阅主题
    
    DEVICE_NAME = "xxx"  # 设备名称
    DEVICE_SECRET = "xxxx"  # 设备密钥
    
    # 全局变量
    client = None
    
    
    # 生成三位随机数作为初始名字
    def generate_random_name():
        return str(random.randint(100, 9999999))
    
    
    # 获取当前时间并格式化为 [HH:MM:SS] 格式
    def get_current_time():
        now = datetime.datetime.now()
        return now.strftime("[%H:%M:%S]")
    
    
    # MQTT 连接回调
    def on_connect(client, userdata, flags, rc, properties=None):
        if rc == 0:
            insert_output_box("成功连接Xibi!\n请开始聊天吧\n", "system")
            # 连接成功后订阅主题
            client.subscribe(TOPIC_SUB, qos=1)
    
        else:
            insert_output_box(f"连接失败,错误代码:{rc}\n", "error")
    
    
    # MQTT 消息接收回调
    def on_message(client, userdata, msg):
        try:
            # 解析 JSON 消息
            payload = json.loads(msg.payload.decode('utf-8'))
            id = payload.get("id", "未知设备")  # 获取 id,默认为 "未知设备"
            content = payload.get("str", "")  # 获取 str,默认为空
    
            # 如果接收到的 id 与当前客户端的 id 相同,则忽略
            if id == client_id_entry.get():
                return
            # 显示接收到的消息,并添加时间
            insert_output_box(f"{get_current_time()} {id}: {content}\n", "received")
            engine.say("收到一条新消息")
            engine.runAndWait()
        except json.JSONDecodeError:
            # 如果消息不是 JSON 格式,直接显示原始内容
            insert_output_box(f"{get_current_time()} 官方提示: {msg.payload.decode('utf-8')}\n", "received")
            engine.say("收到一条新消息")
            engine.runAndWait()
        except Exception as e:
            insert_output_box(f"{get_current_time()} 解析消息失败: {e}\n", "error")
    
    
    # 连接按钮点击事件
    def connect_mqtt():
        global client
        client_id = client_id_entry.get()  # 获取连接名字
        if not client_id:
            insert_output_box("请输入昵称后连接!\n", "error")
            return
    
        # 创建 MQTT 客户端
        client = mqtt.Client(client_id=client_id)
        client.username_pw_set(DEVICE_NAME, DEVICE_SECRET)
        client.on_connect = on_connect
        client.on_message = on_message
    
        try:
            client.connect(BROKER, PORT, keepalive=60)
            client.loop_start()
            insert_output_box(f"{get_current_time()} 正在连接...(昵称: {client_id})\n", "system")
    
            # 连接成功后,禁用输入框并更改按钮文本
            client_id_entry.config(state=tk.DISABLED)
            connect_button.config(text="断开连接", command=disconnect_mqtt)
        except Exception as e:
            insert_output_box(f"{get_current_time()} 连接失败: {e}\n", "error")
    
    
    # 断开连接按钮点击事件
    def disconnect_mqtt():
        global client
        if client:
            client.loop_stop()
            client.disconnect()
            insert_output_box(f"{get_current_time()} 已断开与Xibi的连接。\n", "system")
    
        # 断开连接后,启用输入框并更改按钮文本
        client_id_entry.config(state=tk.NORMAL)
        connect_button.config(text="连接", command=connect_mqtt)
    
    
    # 发送消息
    def send_message():
        if not client:
            insert_output_box("请先连接XIBI!\n", "error")
            return
    
        message = input_box.get()  # 获取输入框内容
        if not message:
            insert_output_box("请输入消息!\n", "error")
            return
    
        try:
            # 构造 JSON 格式的消息
            payload = {
                "id": client_id_entry.get(),  # 连接名字
                "str": message  # 输入的内容
            }
            client.publish(TOPIC_PUB, json.dumps(payload), qos=1)  # 发布 JSON 消息
            # 在输出框中显示发送的内容(解析后的格式),并添加时间
            insert_output_box(f"{get_current_time()} {payload['id']}: {payload['str']}\n", "sent")
            input_box.delete(0, tk.END)  # 清空输入框
        except Exception as e:
            insert_output_box(f"{get_current_time()} 发送失败: {e}\n", "error")
    
    
    # 回车键发送消息
    def on_enter_key(event):
        send_message()
    
    
    # 创建主窗口
    root = tk.Tk()
    root.title("XIBI(线上交友)")
    root.geometry("500x400")  # 固定窗口大小
    root.resizable(False, False)  # 禁止调整窗口大小
    
    # 连接名字输入框
    client_id_label = tk.Label(root, text="昵称(不能中文):")
    client_id_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
    client_id_entry = tk.Entry(root, width=30)
    client_id_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
    client_id_entry.insert(0, 'Xibi'+generate_random_name())  # 初始名字为三位随机数
    
    # 连接按钮
    connect_button = tk.Button(root, text="连接", command=connect_mqtt, width=10)
    connect_button.grid(row=0, column=2, padx=5, pady=5, sticky="e")
    
    # 输出框
    output_box = scrolledtext.ScrolledText(root, width=60, height=20, font=("微软雅黑", 10), state=tk.DISABLED)  # 初始状态为不可编辑
    output_box.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky="nsew")
    
    # 设置消息颜色
    output_box.tag_config("sent", foreground="green")  # 发送的消息为绿色
    output_box.tag_config("received", foreground="blue")  # 接收的消息为蓝色
    output_box.tag_config("system", foreground="gray")  # 系统消息为灰色
    output_box.tag_config("error", foreground="red")  # 错误消息为红色
    
    
    # 封装插入内容的函数
    def insert_output_box(text, tag=None):
        output_box.config(state=tk.NORMAL)  # 临时设置为可编辑
        output_box.insert(tk.END, text, tag)  # 插入内容
        output_box.config(state=tk.DISABLED)  # 恢复为不可编辑
        output_box.see(tk.END)  # 滚动到底部
    
    
    # 输入框
    input_box = tk.Entry(root, width=50)
    input_box.grid(row=2, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
    input_box.bind("<Return>", on_enter_key)  # 绑定回车键事件
    
    # 发送按钮
    send_button = tk.Button(root, text="发送", command=send_message, width=10)
    send_button.grid(row=2, column=2, padx=5, pady=5, sticky="e")
    
    # 配置网格布局权重
    root.grid_columnconfigure(1, weight=1)  # 使中间列(输入框)可以扩展
    root.grid_rowconfigure(1, weight=1)  # 使输出框所在行可以扩展
    
    # 运行主循环
    root.mainloop()
    
    # 断开连接
    if client:
        client.loop_stop()
        client.disconnect()
    

    2.运行截图 

     下图就是聊天界面,这相当于是一个群聊软件,只要拥有该客户端的人都能进来聊天。所以你们分享软件的时候需要注意。当然后续我会根据反馈情况,增加私聊功能。

     3.打包为exe

      接下来就是最重要的了,代码写好之后只能在你自己的电脑上运行,所以我们需要一个打包工具,把这个代码打包为exe格式的文件,就可以分享给小伙伴了。

      我推荐使用Pyinstaller进行打包,相比于其它打包方式更简单。具体下载过程可以查看下方博客,我就不详细解释了(如果需要我可以出一期)。

    用 Pyinstaller 模块将 Python 程序打包成 exe 文件(全网最全面最详细,万字详述)_pyinstaller打包-CSDN博客

    当下载完之后通过下方命令就能获得所需要的

    Pyinstaller -F -w -p site-packages包的位置 -i 图标.ico 文件.py


    好了,今天的分享就到这里,希望大家喜欢!如果你觉得这篇文章对你有帮助,别忘了点赞、收藏和分享哦!我们下期再见!👋

    作者:wss2210

    物联沃分享整理
    物联沃-IOTWORD物联网 » 利用百度物联网MQTT平台远程在线聊天室的实现,使用自带GUI,并通过Pyinstaller进行exe打包

    发表回复