在好例子网,分享、交流、成长!
您当前所在位置:首页Python 开发实例Python语言基础 → 串口通讯 Py简易版

串口通讯 Py简易版

Python语言基础

下载此实例
  • 开发语言:Python
  • 实例大小:7.50KB
  • 下载次数:0
  • 浏览次数:0
  • 发布时间:2025-08-11
  • 实例类别:Python语言基础
  • 发 布 人:NewIdeas
  • 文件格式:.py
  • 所需积分:2
 相关标签: 串口通讯 简易 python 串口 通讯

实例介绍

【实例简介】使用Pyserial库构建的简易版串口通讯
【实例截图】from clipboard
【核心代码】
# serial_gui.py
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import serial
import serial.tools.list_ports

class SerialApp:
    def __init__(self, root):
        self.root = root
        self.root.title("串口通信助手")
        self.root.geometry("650x500")
        self.serial_port = None  # 串口对象

        self.create_widgets()
        self.refresh_ports()  # 自动扫描可用端口

    def create_widgets(self):
        # === 参数设置区 ===
        param_frame = ttk.LabelFrame(self.root, text="串口参数", padding=10)
        param_frame.pack(padx=10, pady=10, fill=tk.X)

        # 端口
        ttk.Label(param_frame, text="端口:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
        self.port_combo = ttk.Combobox(param_frame, state="readonly", width=15)
        self.port_combo.grid(row=0, column=1, padx=5, pady=5)

        self.refresh_btn = ttk.Button(param_frame, text="刷新", command=self.refresh_ports)
        self.refresh_btn.grid(row=0, column=2, padx=5, pady=5)

        # 波特率
        ttk.Label(param_frame, text="波特率:").grid(row=0, column=3, sticky=tk.W, padx=5, pady=5)
        self.baud_combo = ttk.Combobox(param_frame, values=[
            "300", "600", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
        ], state="readonly", width=12)
        self.baud_combo.set("115200")
        self.baud_combo.grid(row=0, column=4, padx=5, pady=5)

        # 数据位
        ttk.Label(param_frame, text="数据位:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
        self.bytesize_combo = ttk.Combobox(param_frame, values=["5", "6", "7", "8"], state="readonly", width=15)
        self.bytesize_combo.set("8")
        self.bytesize_combo.grid(row=1, column=1, padx=5, pady=5)

        # 停止位
        ttk.Label(param_frame, text="停止位:").grid(row=1, column=3, sticky=tk.W, padx=5, pady=5)
        self.stopbits_combo = ttk.Combobox(param_frame, values=["1", "1.5", "2"], state="readonly", width=12)
        self.stopbits_combo.set("1")
        self.stopbits_combo.grid(row=1, column=4, padx=5, pady=5)

        # 校验位
        ttk.Label(param_frame, text="校验位:").grid(row=2, column=0, sticky=tk.W, padx=5, pady=5)
        self.parity_combo = ttk.Combobox(param_frame, values=["无", "偶", "奇"], state="readonly", width=15)
        self.parity_combo.set("无")
        self.parity_combo.grid(row=2, column=1, padx=5, pady=5)

        # 打开/关闭按钮
        self.open_btn = ttk.Button(param_frame, text="打开串口", command=self.open_serial)
        self.open_btn.grid(row=2, column=3, columnspan=2, pady=5)

        # === 接收区 ===
        recv_frame = ttk.LabelFrame(self.root, text="接收数据", padding=10)
        recv_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)

        self.recv_text = scrolledtext.ScrolledText(recv_frame, height=12, state='disabled')
        self.recv_text.pack(fill=tk.BOTH, expand=True)

        # === 发送区 ===
        send_frame = ttk.LabelFrame(self.root, text="发送数据", padding=10)
        send_frame.pack(padx=10, pady=10, fill=tk.X)
        # 输入框
        self.send_text = tk.Entry(send_frame, width=60)
        self.send_text.pack(side=tk.LEFT, padx=5, pady=5)
        self.send_text.bind("<Return>", self.send_data)  # 回车发送
        # 是否添加回车换行
        self.add_newline_var = tk.BooleanVar(value=False)  # 默认BU开启
        self.newline_check = ttk.Checkbutton(
            send_frame,
            text="回车换行",
            variable=self.add_newline_var
            )
        self.newline_check.pack(side=tk.RIGHT, padx=5, pady=5)
        # 发送按钮
        self.send_btn = ttk.Button(send_frame, text="发送", command=self.send_data)
        self.send_btn.pack(side=tk.RIGHT, padx=5, pady=5)
       
    def refresh_ports(self):
        """刷新可用串口"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combo['values'] = ports
        if ports:
            self.port_combo.current(0)
        else:
            self.port_combo.set("")

    def open_serial(self):
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.close()
            self.open_btn.config(text="打开串口")
            messagebox.showinfo("串口状态", "串口已关闭")
            return

        try:
            port = self.port_combo.get()
            baud = int(self.baud_combo.get())
            bytesize = int(self.bytesize_combo.get())
            stopbits = float(self.stopbits_combo.get())

            parity_map = {"无": serial.PARITY_NONE, "偶": serial.PARITY_EVEN, "奇": serial.PARITY_ODD}
            parity = parity_map[self.parity_combo.get()]

            self.serial_port = serial.Serial(
                port=port,
                baudrate=baud,
                bytesize=bytesize,
                stopbits=stopbits,
                parity=parity,
                timeout=1
            )
            self.open_btn.config(text="关闭串口")
            messagebox.showinfo("串口状态", f"已打开 {port}")
            # 启动接收线程
            self.root.after(100, self.read_serial)

        except Exception as e:
            messagebox.showerror("错误", f"无法打开串口: {str(e)}")

    def read_serial(self):
        """非阻塞读取串口数据"""
        if self.serial_port and self.serial_port.is_open:
            try:
                if self.serial_port.in_waiting > 0:
                    data = self.serial_port.read(self.serial_port.in_waiting).decode('utf-8', errors='replace')
                    self.display_recv(data)
            except Exception as e:
                self.display_recv(f"[错误: {str(e)}]\n")
        # 继续轮询
        if self.serial_port and self.serial_port.is_open:
            self.root.after(50, self.read_serial)

    def display_recv(self, text):
        """在接收区显示数据"""
        self.recv_text.config(state='normal')
        self.recv_text.insert(tk.END, text)
        self.recv_text.see(tk.END)
        self.recv_text.config(state='disabled')

    def send_data(self, event=None):
        """发送数据"""
        if not self.serial_port or not self.serial_port.is_open:
            messagebox.showwarning("警告", "请先打开串口")
            return

        data = self.send_text.get().strip()
        if not data:
            return
# 根据选项决定是否添加回车换行
        if self.add_newline_var.get():
        # 添加 \r\n(CR LF)
            send_bytes = (data '\r\n').encode('utf-8')
        else:
            send_bytes = data.encode('utf-8')

        try:
            self.serial_port.write(send_bytes)
     
     # 显示发送内容(用 \r\n 表示回车换行)
            data = data '\r\n' if self.add_newline_var.get() else data
            self.display_recv(f"[发送]: {data}\n")
            self.send_text.delete(0, tk.END)
        except Exception as e:
            messagebox.showerror("发送失败", str(e))

    def on_closing(self):
        """关闭窗口时清理资源"""
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.close()
        self.root.destroy()

# === 主程序入口 ===
if __name__ == "__main__":
    root = tk.Tk()
    app = SerialApp(root)

    # 绑定窗口关闭事件
    root.protocol("WM_DELETE_WINDOW", app.on_closing)

    root.mainloop()

实例下载地址

串口通讯 Py简易版

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

发表评论

(您的评论需要经过审核才能显示)

查看所有0条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警