观前提醒,本文主要内容为Python实现MQTT服务器连接及话题发布&Python窗体实现

MQTT服务器的搭建详见http://www.guyuehome.com/44993

设计目标:

  • 实现遥控水龙头开关
  • 实现遥控水龙头定时开关
  • 实现可视化的交互界面

根据上述设计目标,该程序大致可分为三个部分:

  • MQTT连接与发布实现
  • 交互窗体及控件实现
  • 程序主逻辑实现

一、Python实现MQTT连接及话题发布

在Python中,连接到MQTT服务器以及实现消息的发布和订阅通常使用paho-mqtt客户端库。以下是使用这个库实现连接、发布和订阅的详细解释:

连接到MQTT服务器

通过以下步骤即可实现对MQTT服务器的连接

  • 导入paho-mqtt库中的客户端模块。
  • 创建一个新的mqtt.Client实例。
  • 设置连接回调函数,该回调在客户端连接到服务器时被调用。
  • 连接到MQTT服务器,提供MQTT服务器的地址和端口。
  • 启动客户端的网络循环。
import paho.mqtt.client as mqtt_client

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)

client = mqtt_client.Client("client-id")
client.on_connect = on_connect
client.connect("broker_address", 1883)
client.loop_start()
  • import paho.mqtt.client as mqtt_client: 这行代码引入了 Paho MQTT 客户端库,并将其重命名为 mqtt_client 以便在后面的代码中使用。
  • def on_connect(client, userdata, flags, rc): 这行定义了一个名为 on_connect 的回调函数,它在客户端连接到 MQTT 代理(Broker)时被调用。该函数接收四个参数:客户端实例对象、用户定义的任何类型的数据、连接响应标志和连接结果代码。
  • if rc == 0: 这行代码检查连接结果代码 (rc)。如果 rc 等于 0,表示连接成功。
  • client = mqtt_client.Client("client-id"): 这行创建一个 MQTT 客户端对象,并为其分配一个用户定义的客户端 ID。
  • client.on_connect = on_connect: 这行将 on_connect 函数设置为连接到 MQTT 代理时的回调函数。
  • client.connect("broker_address", 1883): 这行使客户端尝试连接到运行在指定地址 ("broker_address") 和端口 (1883) 的 MQTT 代理。
  • client.loop_start(): 这行启动了一个新的线程,用于处理网络循环的调度和重连。这是非阻塞的,会在后台运行。

发布消息

def publish_message():
    client.publish("python/mqtt", "Hello MQTT")
  • def publish_message(): 这行代码定义了一个名为 publish_message 的函数,它不需要任何参数。
  • client.publish("python/mqtt", "Hello MQTT"): 这行代码使用 publish 方法向 "python/mqtt" 这个主题发布一个消息。消息的内容是 "Hello MQTT"。

订阅话题

  • 设置消息回调函数,该回调在收到订阅话题的消息时被调用。
  • 使用subscribe方法订阅一个话题。
def on_message(client, userdata, message):
    print(f"Received message '{str(message.payload.decode())}' on topic '{message.topic}'")

client.on_message = on_message
client.subscribe("topic")
  • def on_message(client, userdata, message): 这行定义了一个名为 on_message 的回调函数,当客户端从订阅的主题接收到消息时调用此函数。函数接收三个参数:客户端实例对象、用户定义的任何类型的数据和接收到的消息对象。
  • print(f"Received message '{str(message.payload.decode())}' on topic '{message.topic}'"): 这行代码在接收到消息时打印出接收到的消息内容和主题。消息内容通过调用 message.payload.decode() 从字节格式转换为字符串格式。
  • client.on_message = on_message: 这行将 on_message 函数设置为接收到来自订阅主题的消息时的回调函数。
  • client.subscribe("topic"): 这行代码让客户端订阅名为 "topic" 的主题。当有新消息发布到此主题时,on_message 函数将被调用。

完整代码

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("Connected successfully to MQTT broker")
    else:
        print("Connection failed with code %d" % rc)

def publish_message():
    client.publish("python/mqtt", "Hello MQTT")

client = mqtt.Client()
client.on_connect = on_connect
client.connect("mqtt.eclipse.org", 1883, 60)

# Starting the loop
client.loop_start()

publish_message()

在这段代码中,我们创建了一个mqtt.Client实例,连接到了MQTT服务器,并且发布了一个消息。注意,我们使用了client.loop_start()来启动网络循环,这个调用会在后台线程中处理网络事件,包括重新连接和重新发送。

运行程序,通过输出我们可以判断程序已经成功连接到了MQTT服务器并发布话题:

二、Python实现窗体显示和控件

使用Python的Tkinter库来创建一个简单的图形用户界面(GUI)应用程序。这个程序将显示一个窗体,窗体内包含一个输入栏(文本框)和一个按钮。用户可以在文本框中输入文本,然后点击按钮后,输入的文本将显示在窗体内的一个标签上。

步骤1:导入Tkinter库

首先,我们需要导入Tkinter库以及tkinter.messagebox模块,后者用于显示弹出消息框。

import tkinter as tk
from tkinter import messagebox

步骤2:创建主窗口

接着,我们将创建一个主窗口,并设置它的标题和大小。

window = tk.Tk()
window.title("GUI App with Tkinter")
window.geometry('400x200')
  • window = tk.Tk(): 这行代码创建了一个 Tkinter 窗口对象,这个窗口是所有其他 Tkinter 组件(如按钮、标签等)的容器。通常我们称这个窗口为主窗口或根窗口。
  • window.title("GUI App with Tkinter"): 这行代码设置了窗口的标题为 "GUI App with Tkinter"。这个标题将出现在窗口的标题栏上。
  • window.geometry('400x200'): 这行代码设置了窗口的初始大小为 400 像素宽和 200 像素高。geometry 方法接收一个字符串参数,该字符串的格式为 'widthxheight',其中 width 和 height 是窗口的宽度和高度的像素值。

步骤3:添加输入栏和按钮

我们需要一个文本框让用户输入内容,以及一个按钮来执行一个动作(在这种情况下,是显示输入的文本)。

# 输入栏
entry = tk.Entry(window, width=50)
entry.pack()

# 按钮执行的动作
def on_button_click():
    user_input = entry.get()
    messagebox.showinfo("Message", f"You entered: {user_input}")

# 按钮
button = tk.Button(window, text="Display Input", command=on_button_click)
button.pack()
  • entry = tk.Entry(window, width=50): 这行代码创建了一个 Entry 组件,它是一个基本的文本输入框,用户可以在其中输入文本。参数 window 表示这个 Entry 组件是 window 的子组件,width=50 设置了输入框的宽度为 50 个字符。
  • entry.pack(): 这行代码将输入框添加到窗口中。pack 是一个布局管理器,用于控制组件的位置和大小。调用 pack 后,组件会自动调整大小以适应其内容,并放在其父组件中的合适位置。
  • def on_button_click(): 这行代码定义了一个函数,这个函数将在按钮被点击时执行。
  • user_input = entry.get(): 这行代码从输入框中获取用户输入的文本。
  • messagebox.showinfo("Message", f"You entered: {user_input}"): 这行代码调用 messagebox.showinfo 函数来显示一个信息对话框,对话框的标题是 "Message",内容是 "You entered: " 后接用户输入的文本。
  • button = tk.Button(window, text="Display Input", command=on_button_click): 这行代码创建了一个 Button 组件,按钮的文本是 "Display Input",当按钮被点击时,command 参数指定的 on_button_click 函数将被执行。
  • button.pack(): 这行代码将按钮添加到窗口中。

步骤4:启动事件循环
最后,我们需要启动应用程序的事件循环,这样它才能等待用户的操作,比如按键点击。

window.mainloop()

将上述代码放在一起,运行:

import tkinter as tk
from tkinter import messagebox

# 创建主窗口
window = tk.Tk()
window.title("GUI App with Tkinter")
window.geometry('400x200')

# 输入栏
entry = tk.Entry(window, width=50)
entry.pack()

# 按钮执行的动作
def on_button_click():
    user_input = entry.get()
    messagebox.showinfo("Message", f"You entered: {user_input}")

# 按钮
button = tk.Button(window, text="Display Input", command=on_button_click)
button.pack()

# 启动事件循环
window.mainloop()

三、完整程序实现:

完整程序:

import tkinter as tk
from tkinter import ttk
from paho.mqtt import client as mqtt_client
import time

# MQTT服务器配置
broker = '192.168.3.222'
port = 1883
topic = "slt"
client_id = f'python-mqtt-{int(time.time())}'

# 连接MQTT服务器的函数
def connect_mqtt():
    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)

    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(broker, port)
    return client

# 发布消息的函数
def publish(client, msg):
    result = client.publish(topic, msg)
    status = result[0]
    if status == 0:
        print(f"Send `{msg}` to topic `{topic}`")
    else:
        print(f"Failed to send message to topic {topic}")

# 创建定时器,定时发布消息
def on_timer(client, interval, progress_var):
    publish(client, "0")
    progress_var.set(0)  # 重置进度条
    update_progress_bar(progress_var, interval, interval)  # 更新进度条

# 更新进度条的函数
def update_progress_bar(progress_var, interval, max_time):
    progress_step = 100 / (max_time / 1000)
    new_progress = progress_var.get() + progress_step
    if new_progress < 100:
        progress_var.set(new_progress)
        root.after(1000, update_progress_bar, progress_var, interval-1000, max_time)
    else:
        progress_var.set(100)  # 完成

# 开关按钮的回调函数
def on_publish(client, msg):
    publish(client, msg)

# 设置定时器时间的回调函数
def set_timer(progress_var):
    try:
        interval = int(timer_entry.get()) * 1000  # 将秒转换为毫秒
        progress_var.set(0)  # 重置进度条
        root.after(interval, on_timer, client, interval, progress_var)  # 设置定时器
    except ValueError:
        print("Please enter an integer value for the timer interval.")

# 创建GUI窗口
root = tk.Tk()
root.title("MQTT Publisher")
root.geometry("400x150")  # 设置窗口大小

# 定时器设置区域
timer_frame = tk.Frame(root)
timer_frame.pack(side=tk.TOP, fill=tk.X)

timer_label = tk.Label(timer_frame, text="Timer (s):")
timer_label.pack(side=tk.LEFT)

timer_entry = tk.Entry(timer_frame, width=5)
timer_entry.pack(side=tk.LEFT)

progress_var = tk.DoubleVar()  # 进度条变量
progress_bar = ttk.Progressbar(timer_frame, variable=progress_var, length=100, mode='determinate')
progress_bar.pack(side=tk.LEFT, padx=5)

btn_set_timer = tk.Button(timer_frame, text="Set Timer", width=10, command=lambda: set_timer(progress_var))
btn_set_timer.pack(side=tk.LEFT, padx=5)

# 创建开关按钮
btn_on = tk.Button(root, text="ON", width=10, height=2, command=lambda: on_publish(client, "1"))
btn_on.pack(pady=5)

btn_off = tk.Button(root, text="OFF", width=10, height=2, command=lambda: on_publish(client, "0"))
btn_off.pack(pady=5)

# 连接MQTT
client = connect_mqtt()
client.loop_start()

# 启动GUI循环
root.mainloop()

程序功能&逻辑分析:

这个Python程序使用了tkinter和paho-mqtt库来创建一个简单的图形用户界面(GUI),同时实现了MQTT客户端的功能。程序的逻辑可以分为几个主要部分

  • MQTT服务器配置
    定义了MQTT服务器的配置信息,包括服务器地址、端口和主题。
    程序为MQTT客户端生成了一个基于当前时间戳的唯一client_id。
  • MQTT连接与消息发布
    connect_mqtt()函数负责创建一个MQTT客户端实例,并设置连接回调函数on_connect。如果连接成功,将打印成功消息;如果连接失败,将打印错误代码。
    publish()函数用于向指定的MQTT主题发布消息。发布成功时会打印提示信息,失败则打印错误信息。
  • GUI与定时器逻辑
    程序创建了一个Tkinter窗口,其中包含定时器设置、进度条、以及开/关按钮。
    on_timer()函数作为定时器回调,每次定时器到期时将发布一个"0"消息,并重置进度条。
    update_progress_bar()函数负责定期更新进度条的进度,直到进度达到100%。
    on_publish()函数是按钮的回调,当按钮按下时,将发布"1"或"0"消息。
    set_timer()函数从文本框获取用户输入的定时器间隔(秒),并设置定时器。
  • GUI布局
    使用Tkinter的Frame、Label、Entry、Button和Progressbar组件创建了用户界面。
    progress_var是一个DoubleVar对象,用于与进度条组件进行数据绑定。
    使用pack()方法安排组件在窗口中的位置。
  • 程序运行
    程序首先尝试连接到MQTT服务器,并开始MQTT客户端的事件循环。
    启动Tkinter的主事件循环,等待用户操作。

整体上,这个程序是一个MQTT消息发布器,带有一个用户界面,允许用户开启/关闭消息发布,并设定一个定时器来周期性地发布消息。程序通过图形界面提供用户交互,同时在后台处理MQTT通信,展示了如何将MQTT通信与GUI应用程序结合起来。