主控板led灯的RGB远程调节的图形交互界面
目录
1. 设计界面
下面这张图片是我最开始的设计稿
在设计界面的时候,首先分析自己的需求:
- 我需要实现的功能有哪些
- 我需要的插件有哪些
我需要的功能:
- 可以选择led灯的各种颜色
- 可以调整led灯的亮度
- 可以本地通过串口发送信息,也可以通过远程发布mqtt主题并使主控板进行订阅来发送信息
- 所有的调整都要实时通过串口或mqtt向主控板发送信息,使其板子上的led灯做出反应
- 根据参考文档实现真正的端口选择,以及写死波特率、数据位、停止位、校验位
我需要的插件:
- 一个调色盘可以帮助我选取各种我想要的颜色
- 一个亮度滑块帮我调节led灯的亮度
- 一个颜色滑块能让我快速的自己配置各种颜色
- 一个可以在本地和远程切换的按钮
- 加入一个端口选择按钮,使电脑有多个端口的时候,能准确的选择自己使用的那个
- 还可以加入一些文本显示插件,显示一些颜色信息,还有插件的信息,或者是串口的信息等等
因此我根据需求获得了我的第二版设计图,并根据设计图,利用tkinter
进行图形交互界面的设计
2. 实现步骤
2.1 插件和库的使用
serial
和 serial.tools.list_ports:
这些库用于串口通信。serial
用于打开和管理串口连接,serial.tools.list_ports
用于列出所有可用的串口端口。
tkinter
: 这是Python的标准GUI库,用于创建图形用户界面。它提供了各种小部件,如按钮、标签和滑块。
colorchooser
: tkinter
的一个子模块,用于提供一个颜色选择对话框,用户可以通过它选择颜色。
threading
: 这个库用于创建和管理线程。在这个程序中,线程用于定时发送数据和运行MQTT事件循环。
time
: 提供时间相关功能,这里用于线程的延迟操作。
paho.mqtt.client
: 这是一个MQTT客户端库,用于通过MQTT协议与MQTT服务器进行通信。
2.2 端口选择——参考文档
1. 获取可用的串口列表
程序使用 serial.tools.list_ports.comports()
获取所有可用的串口,并将这些串口显示在下拉菜单中。
self.port_list = list(serial.tools.list_ports.comports())
self.port_list
是一个包含当前系统中可用的所有串口的列表。每个列表元素是一个 ListPortInfo
对象,包含串口的相关信息。
2. 初始化 Combobox
控件
接下来,程序使用 Tkinter
的 Combobox
控件来创建一个下拉列表,供用户选择可用的串口。
self.comvalue = StringVar()
self.comboxlist = ttk.Combobox(root, textvariable=self.comvalue, state='readonly')
self.comvalue
是一个StringVar
对象,用于存储当前选择的串口值。self.comboxlist
是一个Combobox
控件,state='readonly'
表示该下拉列表是只读的,用户只能从预定义的选项中进行选择。
3. 设置下拉列表的选项
将获取到的串口信息赋值给 Combobox
的选项列表:
if len(self.port_list) <= 0:
self.comstatus = "No COM Found"
self.com1 = ()
else:
self.com1 = tuple(self.port_list)
self.comboxlist["values"] = self.com1
- 检查
self.port_list
列表的长度,判断是否有可用的串口设备。 self.com1
是一个包含所有可用串口的元组,它被赋值给Combobox
的values
属性。- 将
self.port_list
列表转换为元组,并赋值给self.com1
。 - 将
self.com1
中的串口设备设置为self.comboxlist
组合框(下拉框)的可选项["values"]
。
4.绑定Combobox
的选择事件
为了在用户选择串口后自动更新所选串口,程序为 Combobox
绑定了一个选择事件:
self.comboxlist.bind("<<ComboboxSelected>>", self.comread)
当用户选择一个串口时,self.comread
函数将被调用。
5. 更新所选串口
在 comread
函数中,程序获取用户选择的串口并将其存储在 self.com
中:
def comread(self, *args):
self.com = list(self.port_list[self.comboxlist.current()])[0]
self.comstatus = self.com
self.stlable["fg"] = "green"
self.stlable["text"] = self.com + " Select"
self
代表类的实例对象,用于访问类中的属性和方法。*args
是可变参数,用于接收多个不确定数量的参数。这种设计通常是为了兼容事件处理函数的调用,因为某些 GUI 框架在调用回调函数时会传递额外的参数(如事件对象)。这个方法将在用户从组合框中选择一个串口设备时被调用。self.comboxlist.current()
返回用户在组合框中选择的选项的索引(整数值)。self.port_list
是包含所有可用串口设备信息的列表。则self.port_list[self.comboxlist.current()]
就是根据用户选择的索引,从self.port_list
中获取相应的串口设备信息(通常是一个对象或字符串)。list()
将获取的串口设备信息转换为列表。[0]
获取该列表中的第一个元素,通常是串口设备的名称(如 “COM3” )。最终结果是将用户选择的串口设备名称存储到self.com
变量中。
2.3 颜色展示区域
# 左侧:颜色展示区域
color_code = tk.StringVar()
color_label = tk.Label(root, textvariable=color_code, bg="white", width=20, height=2)
color_label.grid(row=0, column=0, padx=30, pady=5)
color_code.set("颜色为: #ffffff")
-
StringVar()
创建了一个字符串变量,color_code
用于存储颜色代码。
set("颜色为: #ffffff")
设置初始值为 #ffffff(白色),并在界面上显示。
color_code
用于存储当前选中颜色的十六进制代码,并绑定到标签以显示当前颜色代码。 -
Label()
创建一个标签,绑定textvariable=color_code
以动态显示颜色代码。
bg="white"
设置初始背景颜色为白色。width=20
,height=2
设置标签的宽度和高度。grid(row=0, column=0, padx=30, pady=5)
使用网格布局,将标签放置在第0行第0列,并添加内边距。
color_label
用于显示当前选定的颜色,背景颜色根据选择的RGB值动态更新。
2.4 调色盘
# 左侧:调色盘按钮
palette_button = tk.Button(root, text="调色盘", command=choose_color)
palette_button.grid(row=1, column=0, padx=30, pady=5)
调色盘按钮触发choose_color
函数
def choose_color():
color = colorchooser.askcolor()[1]
if color:
r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)
#将十六进制的颜色字符串解析为 RGB 三个颜色分量的整数值。
red_slider.set(r)
green_slider.set(g)
blue_slider.set(b)
update_color_from_sliders()
colorchooser.askcolor()
是Tkinter
提供的一个函数,用于打开一个颜色选择对话框。
这个函数返回一个包含两个元素的元组:第一个元素是 RGB 值的元组 (R, G, B),第二个元素是选择的颜色的十六进制字符串表示,如 ‘#rrggbb’。[1]
从返回的元组中提取第二个元素,即用户选择的颜色的十六进制表示(‘#rrggbb’)。
color
变量将保存用户选择的颜色的十六进制字符串表示。- 弹出颜色选择对话框,让用户选择颜色。如果选择了颜色,解析其RGB值并更新相应的滑块。
并调用update_color_from_sliders
更新颜色显示。
def update_color_from_sliders():
r = red_slider.get()
g = green_slider.get()
b = blue_slider.get()
brightness = brightness_slider.get()
color = f'#{r:02x}{g:02x}{b:02x}'
color_label.config(bg=color)
color_code.set(f"颜色为: {color}")
send_color_data(r, g, b, brightness)
从红色、绿色、蓝色和亮度滑块中获取当前值。计算颜色的十六进制代码,并更新颜色标签的背景色和颜色代码标签。
并调用 send_color_data
发送当前颜色和亮度数据。
def send_color_data(r, g, b, brightness):
global ser, mqttc #定义串口和mqtt的全局变量
mode = mode_button.config('text')[-1]
data = f"R{r:03d}G{g:03d}B{b:03d}B{brightness:03d}\n"
if mode == "本地串口":
if ser and ser.is_open:
ser.write(data.encode('utf-8'))
print(f"通过串口发送数据: {data}") # 打印发送的数据到控制台
elif mode == "mqtt远程":
if mqttc:
mqttc.publish('jason', data, qos=1) # 发布主题到MQTT服务器
print(f"通过MQTT发送数据: {data}") # 打印发送的数据到控制台
2.5 RGB滑块
# 左侧:红色滑块和标签
tk.Label(root, text="Red", fg='red', bg='#ffffce').grid(row=3, column=0, sticky='w', padx=30)
red_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders())
red_slider.grid(row=3, column=0, padx=(70, 10))
# 左侧:绿色滑块和标签
tk.Label(root, text="Green", fg='green', bg='#ffffce').grid(row=4, column=0, sticky='w', padx=30)
green_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders())
green_slider.grid(row=4, column=0, padx=(70, 10))
# 左侧:蓝色滑块和标签
tk.Label(root, text="Blue", fg='blue', bg='#ffffce').grid(row=5, column=0, sticky='w', padx=30)
blue_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders())
blue_slider.grid(row=5, column=0, padx=(70, 10))
Scale()
创建滑块,用于选择RGB颜色值。from_=0, to=255
设置滑块的范围,从0到255,对应颜色的强度。orient="horizontal"
设置滑块为水平布局。
command=lambda x: update_color_from_sliders()
设置滑块变化时调用 update_color_from_sliders()
函数,更新颜色。
grid()
将滑块放置在相应的行和列,并添加适当的边距。
RGB滑块用于用于调整红色、绿色和蓝色的值,用户可以通过拖动滑块来选择所需的颜色。
2.6 模式切换按钮
# 右侧:本地与线上选择按钮
mode_button = tk.Button(root, text="本地串口", command=toggle_mode)
mode_button.grid(row=0, column=1, padx=30, pady=5)
模式切换按钮触发toggle_mode
函数
def toggle_mode():
current_mode = mode_button.config('text')[-1]
new_mode = "本地串口" if current_mode == "mqtt远程" else "mqtt远程"
mode_button.config(text=new_mode)
mode_button.config('text')
获取mode_button
按钮的当前配置项中与'text'
相关的值。
config()
函数在这里是用来获取按钮的配置,可以返回一个包含所有配置项的字典。如果只传入'text'
参数,它会返回与'text'
配置项相关的值(通常是一个包含按钮文本的元组)。[-1]
从返回的元组中提取最后一个元素,即按钮的当前文本内容。在此处,它要么是 “本地串口”,要么是 “mqtt远程”。current_mode
变量保存按钮的当前文本内容的最后一个字符,即 “口” 或 “远”。- 通过三元运算符表达式,根据当前模式确定新的模式,并将其存储在
new_mode
变量中。
2.7 端口选择下拉框
# 右侧:端口选择下拉按钮
port_list = list(serial.tools.list_ports.comports())
port_options = [port.device for port in port_list]
port_var = tk.StringVar()
port_dropdown = ttk.Combobox(root, textvariable=port_var, values=port_options, state='readonly')
port_dropdown.grid(row=1, column=1, padx=30, pady=5)
port_dropdown.bind("<<ComboboxSelected>>", comread)
StringVar()
创建字符串变量 port_var
,用于存储当前选中的串口端口。
Combobox()
创建下拉框,绑定 port_var
变量,并设置选项为 port_options
。
state='readonly'
设置下拉框为只读状态,用户只能选择列表中的端口。
bind("<<ComboboxSelected>>", comread)
绑定事件,当用户选择端口时,调用 comread
函数处理。
def comread(*args):
selected_port = port_var.get()
if selected_port:
port_status.set(f"所选端口: {selected_port}")
setup_serial(selected_port)
从下拉框中获取选中的串口端口。如果选择了端口,更新端口状态标签并调用 setup_serial
函数设置串口连接。
def setup_serial(port):
global ser
if ser and ser.is_open:
ser.close() # 关闭之前的串口连接
ser = serial.Serial(port, 9600)
配置和打开指定的串口端口。如果已有串口连接,先关闭它,然后重新打开新的连接。
2.8 亮度滑块
# 右侧:亮度滑块
brightness_value = tk.StringVar()
brightness_value.set("Brightness: 50")
brightness_slider = tk.Scale(root, from_=0, to=100, orient="horizontal", command=update_brightness)
brightness_slider.set(50)
brightness_slider.grid(row=4, column=1, padx=30, pady=5)
StringVar()
创建字符串变量 brightness_value
,用于存储和显示亮度值。
set("Brightness: 50")
设置初始值为“Brightness: 50”
。
Scale()
创建亮度滑块,范围从0到100。
orient="horizontal"
设置滑块为水平布局。
command=update_brightness
绑定 update_brightness
函数,滑块值变化时调用此函数更新亮度显示。
grid(row=4, column=1, padx=30, pady=5)
使用网格布局,将滑块放置在第4行第1列。
def update_brightness(value):
brightness_value.set(f"Brightness: {value}")
更新亮度值标签,显示当前的亮度值。
2.9 本地串口模式与MQTT远程模式
本地串口模式
在本地串口模式下,程序将通过串口向主控板发送数据:
- 模式切换: 通过点击界面上的“本地串口”按钮来切换到本地串口模式。按钮的文本将更改为“mqtt远程”,表明当前模式为本地串口。
- 串口选择: 用户通过下拉菜单选择串口。
comread
函数会在选择串口后调用setup_serial
函数来打开该串口。 - 数据发送:
send_color_data
函数会根据当前模式来决定如何发送数据。当模式为“本地串口”时,它会通过串口发送数据。数据格式为RrrrGgggBbbbBxxx
,其中Rrrr
、Gggg
和Bbbb
分别表示红色、绿色和蓝色的值,Bxxx
表示亮度值。发送的数据以 UTF-8 编码,并通过ser.write()
发送。 - 数据定时发送:
periodic_send
函数在后台线程中运行,每秒钟读取滑块的值并调用send_color_data
函数来发送数据。
MQTT远程模式
在MQTT远程模式下,程序将通过MQTT协议将数据发送到MQTT服务器:
- 模式切换: 通过点击界面上的“mqtt远程”按钮来切换到MQTT远程模式。按钮的文本将更改为“本地串口”,表明当前模式为MQTT远程。
- MQTT客户端设置:
setup_mqtt
函数用于初始化MQTT客户端。它创建了一个 mqttc 实例,设置了连接回调函数on_connect
,并连接到MQTT服务器(broker.emqx.io)
。这个服务器是公开的MQTT代理,你可以用它进行测试。 - 数据发送:
send_color_data
函数会根据当前模式来决定如何发送数据。当模式为“mqtt远程”时,它会通过MQTT客户端发布数据到主题jason
。数据格式与本地串口模式相同。 - MQTT事件循环:
mqtt_loop
函数启动MQTT客户端的事件循环,以便持续接收和处理MQTT消息。这个函数在后台线程中运行。
3. 完整代码
import serial
import serial.tools.list_ports
import tkinter as tk
from tkinter import ttk, colorchooser
import threading
import time
import paho.mqtt.client as mqtt # 导入paho.mqtt.client库用于MQTT通信
ser = None # 在全局范围内定义 ser 变量
mqttc = None # 在全局范围内定义 mqttc 变量
def update_color_from_sliders():
r = red_slider.get()
g = green_slider.get()
b = blue_slider.get()
brightness = brightness_slider.get()
color = f'#{r:02x}{g:02x}{b:02x}'
color_label.config(bg=color)
color_code.set(f"颜色为: {color}")
send_color_data(r, g, b, brightness)
def choose_color():
color = colorchooser.askcolor()[1]
if color:
r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)
red_slider.set(r)
green_slider.set(g)
blue_slider.set(b)
update_color_from_sliders()
def toggle_mode():
current_mode = mode_button.config('text')[-1]
new_mode = "本地串口" if current_mode == "mqtt远程" else "mqtt远程"
mode_button.config(text=new_mode)
def update_brightness(value):
brightness_value.set(f"Brightness: {value}")
def comread(*args):
selected_port = port_var.get()
if selected_port:
port_status.set(f"所选端口: {selected_port}")
setup_serial(selected_port)
def setup_serial(port):
global ser
if ser and ser.is_open:
ser.close() # 关闭之前的串口连接
ser = serial.Serial(port, 9600)
def setup_mqtt():
global mqttc
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.connect("broker.emqx.io", 1883, 60) # 连接到MQTT服务器
def send_color_data(r, g, b, brightness):
global ser, mqttc
mode = mode_button.config('text')[-1]
data = f"R{r:03d}G{g:03d}B{b:03d}B{brightness:03d}\n"
if mode == "本地串口":
if ser and ser.is_open:
ser.write(data.encode('utf-8'))
print(f"通过串口发送数据: {data}") # 打印发送的数据到控制台
elif mode == "mqtt远程":
if mqttc:
mqttc.publish('jason', data, qos=1) # 发布主题到MQTT服务器
print(f"通过MQTT发送数据: {data}") # 打印发送的数据到控制台
def periodic_send():
while True:
r = red_slider.get()
g = green_slider.get()
b = blue_slider.get()
brightness = brightness_slider.get()
send_color_data(r, g, b, brightness)
time.sleep(1)
# 链接到mqtt服务器
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected with result code {reason_code}")
def mqtt_loop():
mqttc.loop_forever()
root = tk.Tk()
root.title("RGB adjustment")
root.geometry("420x270")
root.configure(bg='#ffffce')
root.resizable(False, False)
# 左侧:颜色展示区域
color_code = tk.StringVar()
color_label = tk.Label(root, textvariable=color_code, bg="white", width=20, height=2)
color_label.grid(row=0, column=0, padx=30, pady=5)
color_code.set("颜色为: #ffffff")
# 左侧:调色盘按钮
palette_button = tk.Button(root, text="调色盘", command=choose_color)
palette_button.grid(row=1, column=0, padx=30, pady=5)
# 左侧:颜色滑块标签
tk.Label(root, text="三原色调色滑块:", bg='#ffffce').grid(row=2, column=0, padx=30, pady=2)
# 左侧:红色滑块和标签
tk.Label(root, text="Red", fg='red', bg='#ffffce').grid(row=3, column=0, sticky='w', padx=30)
red_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders())
red_slider.grid(row=3, column=0, padx=(70, 10))
# 左侧:绿色滑块和标签
tk.Label(root, text="Green", fg='green', bg='#ffffce').grid(row=4, column=0, sticky='w', padx=30)
green_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders())
green_slider.grid(row=4, column=0, padx=(70, 10))
# 左侧:蓝色滑块和标签
tk.Label(root, text="Blue", fg='blue', bg='#ffffce').grid(row=5, column=0, sticky='w', padx=30)
blue_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders())
blue_slider.grid(row=5, column=0, padx=(70, 10))
# 右侧:本地与线上选择按钮
mode_button = tk.Button(root, text="本地串口", command=toggle_mode)
mode_button.grid(row=0, column=1, padx=30, pady=5)
# 右侧:端口选择下拉按钮
port_list = list(serial.tools.list_ports.comports())
port_options = [port.device for port in port_list]
port_var = tk.StringVar()
port_dropdown = ttk.Combobox(root, textvariable=port_var, values=port_options, state='readonly')
port_dropdown.grid(row=1, column=1, padx=30, pady=5)
port_dropdown.bind("<<ComboboxSelected>>", comread)
# 右侧:端口选择状态
port_status = tk.StringVar()
port_status.set("所选端口: 无")
port_status_label = tk.Label(root, textvariable=port_status, bg='#ffffce')
port_status_label.grid(row=2, column=1, padx=30, pady=5)
#Label() 创建标签,绑定 textvariable=port_status 以动态显示端口状态。
# 右侧:亮度滑块标签
tk.Label(root, text="亮度调节滑块:", bg='#ffffce').grid(row=3, column=1, padx=30, pady=5)
# 右侧:亮度滑块
brightness_value = tk.StringVar()
brightness_value.set("Brightness: 50")
brightness_slider = tk.Scale(root, from_=0, to=100, orient="horizontal", command=update_brightness)
brightness_slider.set(50)
brightness_slider.grid(row=4, column=1, padx=30, pady=5)
# 添加右下角标签
footer_label = tk.Label(root, text="by jason", bg='#ffffce', fg='gray')
footer_label.grid(row=5, column=1, padx=30, pady=5, sticky='e')
# 启动定时发送线程
send_thread = threading.Thread(target=periodic_send, daemon=True)
send_thread.start()
# 启动MQTT循环的线程
setup_mqtt() # 初始化MQTT客户端
mqtt_thread = threading.Thread(target=mqtt_loop, daemon=True) # daemon=True代表主线程结束子线程也会结束
mqtt_thread.start()
root.mainloop()
2.0优化版
优化了用户不能自定义主题,导致多人无法同时使用的功能;优化了本地串口发送与线上mqtt发送模式难以区分的问题;优化了一个护眼的背景颜色;优化了颜色滑块调整所导致的无效发送。
import serial
import serial.tools.list_ports
import tkinter as tk
from tkinter import ttk, colorchooser
import paho.mqtt.client as mqtt # 导入paho.mqtt.client库用于MQTT通信
ser = None # 全局变量,表示串口对象,初始值为None
mqttc = None # 全局变量,表示MQTT客户端,初始值为None
# 更新颜色显示和发送颜色数据
def update_color_from_sliders():
# 获取滑块的RGB值和亮度值
r = red_slider.get()
g = green_slider.get()
b = blue_slider.get()
brightness = brightness_slider.get()
# 将RGB值转换为16进制颜色格式
color = f'#{r:02x}{g:02x}{b:02x}'
# 更新颜色展示区域的背景颜色
color_label.config(bg=color)
# 更新显示的颜色代码文本
color_code.set(f"颜色为: {color}")
# 发送颜色数据到串口或MQTT
#send_color_data(r, g, b, brightness)
# 打开调色板选择颜色
def choose_color():
# 弹出颜色选择对话框,返回用户选择的颜色
color = colorchooser.askcolor()[1]
# 如果选择了颜色,获取RGB值并设置滑块的值
if color:
r = int(color[1:3], 16) # 获取红色值
g = int(color[3:5], 16) # 获取绿色值
b = int(color[5:7], 16) # 获取蓝色值
# 更新RGB滑块的值
red_slider.set(r)
green_slider.set(g)
blue_slider.set(b)
# 调用函数更新显示区域和发送数据
update_color_from_sliders()
# 更新亮度值显示
def update_brightness(value):
# 根据滑块值更新亮度显示的文本
brightness_value.set(f"Brightness: {value}")
# 处理串口下拉框选择的端口号
def comread(*args):
# 获取用户选择的串口
selected_port = port_var.get()
# 如果选择了端口,显示所选端口并设置串口
if selected_port:
port_status.set(f"所选端口: {selected_port}")
setup_serial(selected_port)
# 配置串口连接
def setup_serial(port):
global ser # 使用全局ser变量
# 如果已经有打开的串口连接,先关闭
if ser and ser.is_open:
ser.close()
# 以9600的波特率打开所选串口
ser = serial.Serial(port, 9600)
# 配置MQTT连接
def setup_mqtt():
global mqttc # 使用全局mqttc变量
# 创建MQTT客户端
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
# 设置连接回调函数
mqttc.on_connect = on_connect
# 连接到MQTT服务器(使用EMQX公共Broker)
mqttc.connect("broker.emqx.io", 1883, 60)
#串口发送和mqtt端发送可能发生抢占吗,需不需要添加一个发送切换按钮,一段发送另一端自动关闭————乌龙
#服务器可能限流————滑块发送最多只能发送20个————重启exe
#依旧改掉滑块变更颜色会自动发送数据的问题————“硬件抖动”
# 发送颜色数据(串口或MQTT)
def send_color_data(r, g, b, brightness):
global ser, mqttc # 使用全局ser和mqttc变量
# 格式化要发送的数据,包含RGB值和亮度值
data = f"R{r:03d}G{g:03d}B{b:03d}B{brightness:03d}\n"
# 如果串口已打开,将数据发送到串口
if ser and ser.is_open:
ser.write(data.encode('utf-8'))
print(f"通过串口发送数据: {data}")
# 如果MQTT客户端存在,将数据发布到MQTT主题
if mqttc:
topic = mqtt_topic.get() # 获取用户输入的主题
mqttc.publish(topic, data, qos=1) # 发布数据到MQTT服务器
print(f"通过MQTT发送数据: {data} 到主题: {topic}")
# MQTT连接回调函数
def on_connect(client, userdata, flags, reason_code, properties):
# 打印连接结果
print(f"Connected with result code {reason_code}")
# 启动MQTT循环
def mqtt_loop():
mqttc.loop_forever() # 保持MQTT客户端的事件循环
# 主窗口设置
root = tk.Tk()
root.title("RGB adjustment") # 设置窗口标题
root.geometry("420x360") # 设置窗口大小
background_color = '#E3F2FD' # 背景颜色
root.configure(bg=background_color) # 应用背景颜色
root.resizable(False, False) # 禁用窗口缩放
# 左侧:颜色展示区域
color_code = tk.StringVar() # 定义一个字符串变量来存储颜色代码
color_label = tk.Label(root, textvariable=color_code, bg="white", width=20, height=2) # 定义颜色展示标签
color_label.grid(row=0, column=0, columnspan=2, padx=30, pady=5) # 布局颜色展示区域
color_code.set("颜色为: #ffffff") # 初始颜色为白色
# 左侧:调色盘按钮
palette_button = tk.Button(root, text="调色盘", command=choose_color, bg=background_color)
palette_button.grid(row=1, column=0, padx=30, pady=5)
# 左侧:颜色滑块标签
tk.Label(root, text="三原色调色滑块:", bg=background_color).grid(row=2, column=0, padx=30, pady=2)
# 左侧:红色滑块和标签
tk.Label(root, text="Red", fg='red', bg=background_color).grid(row=3, column=0, sticky='w', padx=30)
red_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders(),
bg=background_color)
red_slider.grid(row=3, column=0, padx=(70, 10))
# 左侧:绿色滑块和标签
tk.Label(root, text="Green", fg='green', bg=background_color).grid(row=4, column=0, sticky='w', padx=30)
green_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders(),
bg=background_color)
green_slider.grid(row=4, column=0, padx=(70, 10))
# 左侧:蓝色滑块和标签
tk.Label(root, text="Blue", fg='blue', bg=background_color).grid(row=5, column=0, sticky='w', padx=30)
blue_slider = tk.Scale(root, from_=0, to=255, orient="horizontal", command=lambda x: update_color_from_sliders(),
bg=background_color)
blue_slider.grid(row=5, column=0, padx=(70, 10))
# 左侧:亮度滑块标签
tk.Label(root, text="亮度调节滑块:", bg=background_color).grid(row=6, column=0, padx=30, pady=0)
# 左侧:亮度滑块
brightness_value = tk.StringVar()
brightness_value.set("Brightness: 50") # 初始亮度设置为50
brightness_slider = tk.Scale(root, from_=0, to=100, orient="horizontal", command=update_brightness, bg=background_color)
brightness_slider.set(50)
brightness_slider.grid(row=7, column=0, padx=30, pady=0)
# 右侧:本地串口发送按钮
serial_button = tk.Button(root, text="本地串口发送",
command=lambda: send_color_data(red_slider.get(), green_slider.get(), blue_slider.get(),
brightness_slider.get()), bg=background_color)
serial_button.grid(row=1, column=1, padx=30, pady=5)
# 右侧:端口选择下拉按钮(放在本地串口发送按钮下面)
port_list = list(serial.tools.list_ports.comports()) # 获取可用串口
port_options = [port.device for port in port_list] # 提取串口设备名称
port_var = tk.StringVar() # 创建变量存储选择的端口
port_dropdown = ttk.Combobox(root, textvariable=port_var, values=port_options, state='readonly') # 下拉框显示可用串口
port_dropdown.grid(row=2, column=1, padx=30, pady=5)
port_dropdown.bind("<<ComboboxSelected>>", comread) # 当选择端口时调用comread函数
# 右侧:端口选择状态
port_status = tk.StringVar()
port_status.set("所选端口: 无")
port_status_label = tk.Label(root, textvariable=port_status, bg=background_color)
port_status_label.grid(row=3, column=1, padx=30, pady=10)
# 右侧:MQTT发布主题输入框
tk.Label(root, text="MQTT主题:", bg=background_color).grid(row=6, column=1, padx=30, pady=2)
mqtt_topic = tk.StringVar()
mqtt_topic_entry = tk.Entry(root, textvariable=mqtt_topic)
mqtt_topic_entry.grid(row=7, column=1, padx=30, pady=5)
mqtt_topic.set("") # 设置默认主题为空字符串
# 右侧:通过MQTT发布按钮
mqtt_button = tk.Button(root, text="MQTT发送",
command=lambda: send_color_data(red_slider.get(), green_slider.get(), blue_slider.get(),
brightness_slider.get()), bg=background_color)
mqtt_button.grid(row=4, column=1, padx=30, pady=5)
# 加入提醒标签,用学号来作为自己的主题
footer_label2 = tk.Label(root, text="请用自己的学号作为MQTT主题",bg=background_color,fg='gray')
footer_label2.grid(row=5,column = 1,padx=30,pady=5,sticky='e')
# 添加右下角标签
footer_label = tk.Label(root, text="by Cgq", bg=background_color, fg='gray')
footer_label.grid(row=11, column=1, padx=0, pady=5, sticky='e')
# 启动MQTT
setup_mqtt() # 初始化MQTT客户端
root.mainloop() # 进入Tkinter主循环
4. 要实现主机端所需了解的参数
def setup_mqtt():
global mqttc
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.connect("broker.emqx.io", 1883, 60) # 连接到MQTT服务器
def setup_serial(port):
global ser
if ser and ser.is_open:
ser.close() # 关闭之前的串口连接
ser = serial.Serial(port, 9600)
数据格式:
R255G255B255B100
:R255
的255是红色滑块的参数、G255
的255是绿色滑块的参数、B255
的255是蓝色滑块的参数、B100
的100是亮度参数。
在文章的最后教大家一个小技巧——将python程序打包成exe文件
首先我们可以用pyinstaller
模块实现将python程序打包成exe文件。操作步骤如下:
- 安装
pyinstaller
模块
在pycharm中操作:file --> setting --> Project:你的py文件名--> Python Interpreter --> 点击+ --> 搜索pyinstaller --> 点击左下角安装即可
- 在pycharm的Terminal终端输入以下指令:
pyinstaller -F xxxx.py
- 【注】相关参数如下:
--icon=图标路径 例:pyinstaller -F --icon=my.ico xxxx.py
-F
打包成一个exe文件
-w
使用窗口,无控制台
-c
使用控制台,无窗口
-D
创建一个目录,里面包含exe以及其他一些依赖性文件
- 在项目的dist目录下可以看到生成了exe文件,直接在windows系统上就可以运行。
【注】exe文件本质上是将python解释器和程序打包到了一起,这样我们执行程序是就不用管windows系统是不是有python解释器了。