利用百度物联网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("", 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("", 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.运行截图 

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

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

 3.打包为exe

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

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

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

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

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

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

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


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

你可能感兴趣的:(python,物联网,ui)