串口调试助手2

明白了,我们需要确保当接收到的数据超过20个字节时,超出部分在循环结束时也进行显示,而不是保留在 buffer 中。为了实现这一点,我们可以在每次累积到20个字节时立即显示数据,并且在循环结束时将剩余的任何数据也显示出来。

以下是更新后的完整代码示例

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=3, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar()
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')
    
    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))
    
    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break
    
    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)
    
    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x400")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

主要改动点
修改 check_queue 方法:
使用一个 while 循环来处理累积的数据 (buffer),直到其长度小于20个字节。
每次累积到20个字节时,调用 display_received_data 显示前20个字节,并从 buffer 中移除这20个字节。
在循环结束后,如果有剩余数据(少于20个字节),则立即显示这些数据。
具体逻辑解释
累积数据:从队列中取出所有数据并累积到 buffer 中。
处理累积数据:使用 while 循环不断检查 buffer 的长度,如果长度大于等于20,则提取前20个字节并调用 display_received_data 显示这些数据,然后从 buffer 中移除这20个字节。
剩余数据处理:在 while 循环结束后,如果有剩余数据(少于20个字节),也会调用 display_received_data 显示这些数据。

好的,我们可以在现有的应用程序中增加一个名为“读取距离”的按钮。当点击该按钮时,程序将以十六进制格式发送指定的数据(01 03 10 00 00 02 C0 CB),并显示发送和接收到的数据。

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar(value=True)  # 默认勾选十六进制发送
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 创建读取距离按钮
        self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED)
        self.read_distance_button.grid(row=6, column=1, padx=5, pady=5, sticky='w')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')
    
    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))
    
    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_distance(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break
    
    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)
    
    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_distance_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x400")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

主要改动点
新增“读取距离”按钮:
在第6行 (row=6) 的 Checkbutton 右侧添加了一个新的按钮 read_distance_button,用于发送特定的十六进制命令。
使用 self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED) 创建按钮,并将其放置在网格布局中。
实现 read_distance 方法:
当点击“读取距离”按钮时,会调用 read_distance 方法。
该方法将特定的十六进制命令(01 03 10 00 00 02 C0 CB)转换为字节并发送到串口。
同时,它会在接收数据显示区域显示发送的命令。
更新 update_buttons_state 方法:
确保“读取距离”按钮的状态与串口的打开/关闭状态同步。当串口关闭时,按钮应禁用;当串口打开时,按钮应启用。
使用方法
运行程序:启动应用程序后,接收数据显示区域会显示在窗口的上部,其他控件则显示在窗口的下部。
选择串口和波特率:
选择要使用的串口和波特率(默认为 9600)。
如果需要刷新可用串口,请点击“刷新”按钮。
打开串口:点击“打开”按钮以打开串口。
点击“读取距离”按钮:
当串口打开后,点击“读取距离”按钮。
这将发送特定的十六进制命令(01 03 10 00 00 02 C0 CB),并在接收数据显示区域显示发送的命令。
接收到的数据也会在接收数据显示区域显示出来。
发送自定义数据:
在“发送数据”文本框中输入要发送的数据。
如果需要以十六进制格式发送数据,请确保勾选“十六进制发送”复选框。
点击“发送”按钮发送数据。
接收数据显示:
接收到的数据会在累积到20个字节时一次性显示,或者在接收到少于20个字节时立即显示。
如果勾选了“十六进制显示”,则数据将以十六进制格式显示;否则,数据将被解码为字符串并显示。
滚动条会自动滚动到最新数据的位置。
关闭串口:点击“关闭”按钮关闭串口。

明白了,您希望将“读取温度”按钮发送的命令更改为与“读取距离”按钮相同的十六进制命令 01 03 10 00 00 02 C0 CB。下面是更新后的代码,其中“读取温度”按钮的功能已经修改为发送相同的命令。

更新后的完整代码示例

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar(value=True)  # 默认勾选十六进制发送
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 创建读取距离按钮
        self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED)
        self.read_distance_button.grid(row=6, column=1, padx=5, pady=5, sticky='w')
        
        # 创建读取温度按钮
        self.read_temperature_button = tk.Button(root, text="读取温度", command=self.read_temperature, state=tk.DISABLED)
        self.read_temperature_button.grid(row=6, column=2, padx=5, pady=5, sticky='w')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')
    
    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))
    
    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_distance(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_temperature(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"  # 使用与读取距离相同的命令
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break
    
    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)
    
    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_distance_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_temperature_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x400")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

运行程序:启动应用程序后,接收数据显示区域会显示在窗口的上部,其他控件则显示在窗口的下部。
选择串口和波特率:
选择要使用的串口和波特率(默认为 9600)。
如果需要刷新可用串口,请点击“刷新”按钮。
打开串口:点击“打开”按钮以打开串口。
点击“读取距离”或“读取温度”按钮:
当串口打开后,点击“读取距离”或“读取温度”按钮。
这两个按钮现在都发送相同的十六进制命令(01 03 10 00 00 02 C0 CB),并在接收数据显示区域显示发送的命令。
接收到的数据也会在接收数据显示区域显示出来。
发送自定义数据:
在“发送数据”文本框中输入要发送的数据。
如果需要以十六进制格式发送数据,请确保勾选“十六进制发送”复选框。
点击“发送”按钮发送数据。
接收数据显示:
接收到的数据会在累积到20个字节时一次性显示,或者在接收到少于20个字节时立即显示。
如果勾选了“十六进制显示”,则数据将以十六进制格式显示;否则,数据将被解码为字符串并显示。
滚动条会自动滚动到最新数据的位置。
关闭串口:点击“关闭”按钮关闭串口。

为了在点击“读取距离”按钮后显示接收到的数据的第5和第6字节,并将其转换为十进制格式(单位为毫米),我们需要做以下几件事:

添加一个新的显示窗口:用于显示从接收到的数据中提取并转换后的距离值。
修改 read_distance 方法:在发送命令后,解析接收到的数据,并更新新的显示窗口。
处理接收到的数据:确保正确地解析数据,并将特定字节转换为十进制数值。
下面是更新后的完整代码示例,其中包含了一个新的标签用于显示读取的距离值:

更新后的完整代码示例

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar(value=True)  # 默认勾选十六进制发送
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 创建读取距离按钮和显示窗口
        self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED)
        self.read_distance_button.grid(row=6, column=1, padx=5, pady=5, sticky='w')
        
        self.distance_label = tk.Label(root, text="距离: - mm")
        self.distance_label.grid(row=6, column=2, padx=5, pady=5, sticky='w')
        
        # 创建读取温度按钮
        self.read_temperature_button = tk.Button(root, text="读取温度", command=self.read_temperature, state=tk.DISABLED)
        self.read_temperature_button.grid(row=7, column=1, padx=5, pady=5, sticky='w')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')
    
    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))
    
    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_distance(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 清空距离显示
            self.distance_label.config(text="距离: - mm")
    
    def read_temperature(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"  # 使用与读取距离相同的命令
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break
    
    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)
    
    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
        
        # 解析距离数据并更新显示
        if len(data) >= 6:
            distance_bytes = data[4:6]
            distance_mm = int.from_bytes(distance_bytes, byteorder='big')
            self.distance_label.config(text=f"距离: {distance_mm} mm")

    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_distance_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_temperature_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x400")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

运行程序:启动应用程序后,接收数据显示区域会显示在窗口的上部,其他控件则显示在窗口的下部。
选择串口和波特率:
选择要使用的串口和波特率(默认为 9600)。
如果需要刷新可用串口,请点击“刷新”按钮。
打开串口:点击“打开”按钮以打开串口。
点击“读取距离”按钮:
当串口打开后,点击“读取距离”按钮。
该按钮会发送特定的十六进制命令(01 03 10 00 00 02 C0 CB),并在接收数据显示区域显示发送的命令。
接收到的数据会在累积到一定长度时一次性显示,或者在接收到少于20个字节时立即显示。
接收到的数据中的第5和第6字节会被解析为距离值(单位为毫米),并更新到新的显示标签中。
发送自定义数据:
在“发送数据”文本框中输入要发送的数据。
如果需要以十六进制格式发送数据,请确保勾选“十六进制发送”复选框。
点击“发送”按钮发送数据。
接收数据显示:
接收到的数据会在累积到20个字节时一次性显示,或者在接收到少于20个字节时立即显示。
如果勾选了“十六进制显示”,则数据将以十六进制格式显示;否则,数据将被解码为字符串并显示。
滚动条会自动滚动到最新数据的位置。
关闭串口:点击“关闭”按钮关闭串口。

好的,您希望在点击“读取距离”按钮后显示接收到的数据的第4和第5字节,并将其转换为十进制数值(单位为毫米)。我们需要对之前的代码进行一些调整来实现这个需求。

主要改动点
修改解析数据的位置:将原先解析第5和第6字节的地方改为解析第4和第5字节。
更新 display_received_data 方法:确保正确提取并处理新的字节位置。
下面是更新后的

完整代码示例:

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar(value=True)  # 默认勾选十六进制发送
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 创建读取距离按钮和显示窗口
        self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED)
        self.read_distance_button.grid(row=6, column=1, padx=5, pady=5, sticky='w')
        
        self.distance_label = tk.Label(root, text="距离: - mm")
        self.distance_label.grid(row=6, column=2, padx=5, pady=5, sticky='w')
        
        # 创建读取温度按钮
        self.read_temperature_button = tk.Button(root, text="读取温度", command=self.read_temperature, state=tk.DISABLED)
        self.read_temperature_button.grid(row=7, column=1, padx=5, pady=5, sticky='w')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')
    
    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))
    
    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_distance(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 清空距离显示
            self.distance_label.config(text="距离: - mm")
    
    def read_temperature(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"  # 使用与读取距离相同的命令
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break
    
    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)
    
    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
        
        # 解析距离数据并更新显示
        if len(data) >= 5:
            distance_bytes = data[3:5]  # 修改为第4和第5字节
            distance_mm = int.from_bytes(distance_bytes, byteorder='big')
            self.distance_label.config(text=f"距离: {distance_mm} mm")

    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_distance_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_temperature_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x400")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

为了在点击“读取温度”按钮后显示接收到的数据的第6和第7字节,并将其转换为十进制数值(单位为摄氏度),我们需要对代码进行一些调整。具体来说,我们将添加一个新的标签用于显示温度值,并在解析接收到的数据时提取并处理第6和第7字节。

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar(value=True)  # 默认勾选十六进制发送
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 创建读取距离按钮和显示窗口
        self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED)
        self.read_distance_button.grid(row=6, column=1, padx=5, pady=5, sticky='w')
        
        self.distance_label = tk.Label(root, text="距离: - mm")
        self.distance_label.grid(row=6, column=2, padx=5, pady=5, sticky='w')
        
        # 创建读取温度按钮和显示窗口
        self.read_temperature_button = tk.Button(root, text="读取温度", command=self.read_temperature, state=tk.DISABLED)
        self.read_temperature_button.grid(row=7, column=1, padx=5, pady=5, sticky='w')
        
        self.temperature_label = tk.Label(root, text="温度: - ℃")
        self.temperature_label.grid(row=7, column=2, padx=5, pady=5, sticky='w')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')
    
    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))
    
    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部
    
    def read_distance(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 清空距离显示
            self.distance_label.config(text="距离: - mm")
    
    def read_temperature(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"  # 使用与读取距离相同的命令
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 清空温度显示
            self.temperature_label.config(text="温度: - ℃")
    
    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break
    
    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)
    
    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
        
        # 解析距离数据并更新显示
        if len(data) >= 5:
            distance_bytes = data[3:5]  # 修改为第4和第5字节
            distance_mm = int.from_bytes(distance_bytes, byteorder='big')
            self.distance_label.config(text=f"距离: {distance_mm} mm")
        
        # 解析温度数据并更新显示
        if len(data) >= 7:
            temperature_bytes = data[5:7]  # 修改为第6和第7字节
            temperature_celsius = int.from_bytes(temperature_bytes, byteorder='big')
            self.temperature_label.config(text=f"温度: {temperature_celsius} ℃")

    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_distance_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_temperature_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x450")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

为了确保只有在点击“读取距离”按钮后才显示距离,以及只有在点击“读取温度”按钮后才显示温度,我们需要对代码进行一些调整。具体来说,我们将添加标志位来跟踪用户是否点击了相应的按钮,并仅在这些标志位为真时解析并显示数据。

以下是更新后的完整代码示例:

更新后的完整代码示例

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import queue

class SerialToolApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口工具小助手")
        
        # 创建接收数据显示区域(包含滚动条),并放置在窗口上部
        self.receive_text_frame = tk.Frame(root)
        self.receive_text_frame.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky='nsew')
        
        self.receive_text = tk.Text(self.receive_text_frame, height=10, width=40)
        self.receive_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scrollbar = tk.Scrollbar(self.receive_text_frame, orient=tk.VERTICAL, command=self.receive_text.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.receive_text.config(yscrollcommand=self.scrollbar.set)
        
        # 创建串口选择下拉框
        self.port_label = tk.Label(root, text="选择串口:")
        self.port_label.grid(row=1, column=0, padx=5, pady=5, sticky='e')
        self.port_combobox = ttk.Combobox(root)
        self.port_combobox.grid(row=1, column=1, padx=5, pady=5, sticky='w')
        self.refresh_ports()
        
        # 创建刷新按钮
        self.refresh_button = tk.Button(root, text="刷新", command=self.refresh_ports)
        self.refresh_button.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        
        # 创建波特率选择下拉框,默认波特率为9600
        self.baud_label = tk.Label(root, text="波特率:")
        self.baud_label.grid(row=2, column=0, padx=5, pady=5, sticky='e')
        self.baud_combobox = ttk.Combobox(root, values=[9600, 19200, 38400, 57600, 115200])
        self.baud_combobox.current(0)  # 默认选择9600
        self.baud_combobox.grid(row=2, column=1, padx=5, pady=5, sticky='w')
        
        # 创建打开/关闭按钮
        self.open_button = tk.Button(root, text="打开", command=self.open_serial)
        self.open_button.grid(row=3, column=0, padx=5, pady=5, sticky='e')
        self.close_button = tk.Button(root, text="关闭", command=self.close_serial, state=tk.DISABLED)
        self.close_button.grid(row=3, column=1, padx=5, pady=5, sticky='w')
        
        # 创建发送数据输入框和发送按钮
        self.send_label = tk.Label(root, text="发送数据:")
        self.send_label.grid(row=4, column=0, padx=5, pady=5, sticky='e')
        self.send_entry = tk.Entry(root)
        self.send_entry.grid(row=4, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制发送选择框
        self.hex_send_var = tk.BooleanVar(value=True)  # 默认勾选十六进制发送
        self.hex_send_checkbutton = tk.Checkbutton(root, text="十六进制发送", variable=self.hex_send_var)
        self.hex_send_checkbutton.grid(row=5, column=0, padx=5, pady=5, sticky='e')
        
        # 创建发送按钮
        self.send_button = tk.Button(root, text="发送", command=self.send_data, state=tk.DISABLED)
        self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky='w')
        
        # 创建十六进制显示选择框
        self.hex_receive_var = tk.BooleanVar()
        self.hex_receive_checkbutton = tk.Checkbutton(root, text="十六进制显示", variable=self.hex_receive_var)
        self.hex_receive_checkbutton.grid(row=6, column=0, padx=5, pady=5, sticky='e')
        
        # 创建读取距离按钮和显示窗口
        self.read_distance_button = tk.Button(root, text="读取距离", command=self.read_distance, state=tk.DISABLED)
        self.read_distance_button.grid(row=6, column=1, padx=5, pady=5, sticky='w')
        
        self.distance_label = tk.Label(root, text="距离: - mm")
        self.distance_label.grid(row=6, column=2, padx=5, pady=5, sticky='w')
        
        # 创建读取温度按钮和显示窗口
        self.read_temperature_button = tk.Button(root, text="读取温度", command=self.read_temperature, state=tk.DISABLED)
        self.read_temperature_button.grid(row=7, column=1, padx=5, pady=5, sticky='w')
        
        self.temperature_label = tk.Label(root, text="温度: - ℃")
        self.temperature_label.grid(row=7, column=2, padx=5, pady=5, sticky='w')
        
        # 初始化串口对象和队列
        self.ser = None
        self.read_queue = queue.Queue()
        self.read_thread = None
        
        # 标志位
        self.read_distance_flag == False
        self.read_temperature_flag/XMLSchemaValidator == False

    def refresh_ports(self):
        """刷新可用串口列表"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combobox['values'] = ports
        if ports:
            self.port_combobox.current(0)
        else:
            self.port_combobox.set('')

    def open_serial(self):
        """打开串口"""
        try:
            port = self.port_combobox.get()
            baudrate = int(self.baud_combobox.get())
            self.ser = serial.Serial(port, baudrate, timeout=1)
            self.update_buttons_state(False)
            self.receive_text.insert(tk.END, f"已打开串口 {port} @ {baudrate}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 启动读取线程
            self.read_thread = threading.Thread(target=self.read_from_serial)
            self.read_thread.daemon = True
            self.read_thread.start()
            
            # 开始定期检查队列中的数据
            self.check_queue()
        except serial.SerialException as e:
            messagebox.showerror("错误", str(e))

    def close_serial(self):
        """关闭串口"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.update_buttons_state(True)
            self.receive_text.insert(tk.END, "已关闭串口\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部

    def send_data(self):
        """发送数据"""
        if self.ser and self.ser.is_open:
            data = self.send_entry.get()
            if self.hex_send_var.get():
                try:
                    # 将输入的十六进制字符串转换为字节
                    bytes_data = bytes.fromhex(data)
                    self.ser.write(bytes_data)
                    self.receive_text.insert(tk.END, f"发送 (HEX): {data}\n")
                    self.receive_text.see(tk.END)  # 自动滚动到底部
                except ValueError:
                    messagebox.showerror("错误", "请输入有效的十六进制数据")
            else:
                # 发送普通字符串
                data += '\n'
                self.ser.write(data.encode())
                self.receive_text.insert(tk.END, f"发送: {data}")
                self.receive_text.see(tk.END)  # 自动滚动到底部

    def read_distance(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 设置读取距离标志位
            self.read_distance_flag = True
            self.read_temperature_flag = False

    def read_temperature(self):
        """以十六进制发送特定命令并显示发送和接收到的数据"""
        if self.ser and self.ser.is_open:
            hex_command = "01 03 10 00 00 02 C0 CB"  # 使用与读取距离相同的命令
            bytes_command = bytes.fromhex(hex_command.replace(" ", ""))
            self.ser.write(bytes_command)
            self.receive_text.insert(tk.END, f"发送 (HEX): {hex_command}\n")
            self.receive_text.see(tk.END)  # 自动滚动到底部
            
            # 设置读取温度标志位
            self.read_temperature_flag = True
            self.read_distance_flag = False

    def read_from_serial(self):
        """从串口读取数据并放入队列"""
        while self.ser and self.ser.is_open:
            try:
                data = self.ser.read(self.ser.in_waiting or 1)
                if data:
                    self.read_queue.put(data)
            except serial.SerialException as e:
                messagebox.showerror("错误", str(e))
                break

    def check_queue(self):
        """检查队列中的数据并更新显示"""
        buffer = b""
        while not self.read_queue.empty():
            data = self.read_queue.get()
            buffer += data
            while len(buffer) >= 20:
                self.display_received_data(buffer[:20])
                buffer = buffer[20:]
        
        if buffer:
            self.display_received_data(buffer)
        
        # 定期调用此方法以持续检查队列
        self.root.after(100, self.check_queue)

    def display_received_data(self, data):
        """根据设置显示接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_data = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert(tk.END, f"接收 (HEX): {hex_data}\n")
        else:
            # 普通字符串显示
            self.receive_text.insert(tk.END, f"接收: {data.decode('utf-8', errors='replace')}")
        self.receive_text.see(tk.END)  # 自动滚动到底部
        
        # 解析距离数据并更新显示
        if self.read_distance_flag and len(data) >= 5:
            distance_bytes = data[3:5]  # 修改为第4和第5字节
            distance_mm = int.from_bytes(distance_bytes, byteorder='big')
            self.distance_label.config(text=f"距离: {distance_mm} mm")
        
        # 解析温度数据并更新显示
        if self.read_temperature_flag and len(data) >= 7:
            temperature_bytes = data[5:7]  # 修改为第6和第7字节
            temperature_celsius = int.from_bytes(temperature_bytes, byteorder='big')
            self.temperature_label.config(text=f"温度: {temperature_celsius} ℃")

    def update_buttons_state(self, is_open):
        """更新按钮状态"""
        self.open_button.config(state=tk.NORMAL if is_open else tk.DISABLED)
        self.close_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.send_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_distance_button.config(state=tk.DISABLED if is_open else tk.NORMAL)
        self.read_temperature_button.config(state=tk.DISABLED if is_open else tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("400x450")  # 设置初始窗口大小
    app = SerialToolApp(root)
    root.mainloop()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值