Python使用Tkinter编写串口通信调试工具

 更新时间:2026年01月29日 08:57:20   作者:xingzhemengyou1  
串口调试助手是一款基于Python和Tkinter开发的串口通信调试工具, 专为工程师和开发者设计,下面为大家详细介绍了如何使用Python实现串口通信调试工具,希望对大家有所帮助

简介

串口调试助手是一款基于Python和Tkinter开发的串口通信调试工具, 专为工程师和开发者设计。它提供了简洁清爽的用户界面, 支持完整的串口配置、数据收发、自动发送、日志记录等功能, 是进行串口通信测试和调试的理想工具。

效果图如下:

核心功能

串口配置:支持完整的串口参数配置,包括串口号、波特率、数据位、停止位、校验位等,满足各种串口通信需求。

数据接收:实时接收串口数据,支持文本和十六进制两种显示模式,自动统计接收字节数。

数据发送:支持手动发送和自动发送两种模式,可发送文本或十六进制数据,自动统计发送字节数。

自动发送:可设置自动发送间隔,支持自定义发送内容,适合进行周期性数据测试。

日志保存:支持将接收到的数据保存为日志文件,方便后续分析和调试。

配置管理:自动保存和加载配置参数,下次启动时自动恢复上次的设置。

功能详解

  • 串口参数配置支持波特率:300/600/1200/2400/4800/9600/14400/19200/38400/57600/115200
  • 数据位设置支持5/6/7/8位数据位
  • 停止位设置支持1/1.5/2位停止位
  • 校验位设置支持None/Even/Odd/Mark/Space五种校验方式
  • 十六进制显示接收区域支持十六进制和文本两种显示模式切换
  • 十六进制发送发送区域支持十六进制和文本两种发送模式切换
  • 数据统计实时显示RX(接收)和TX(发送)的字节数统计
  • 清空功能支持清空接收区域和发送区域的内容
  • 状态提示底部状态栏实时显示当前操作状态

使用说明

  • 选择串口在工具栏中选择要使用的串口号,点击"刷新"按钮可更新串口列表
  • 配置参数设置波特率、数据位、停止位、校验位等串口参数,确保与目标设备一致
  • 打开串口点击"打开串口"按钮,程序将尝试连接并打开指定的串口
  • 发送数据在发送区域输入要发送的数据,点击"发送"按钮即可发送,也可勾选"十六进制发送"发送十六进制数据
  • 接收数据接收区域会自动显示接收到的数据,可勾选"十六进制显示"以十六进制格式查看
  • 自动发送在自动发送区域勾选"启用自动发送",设置发送间隔,程序将自动周期性发送数据
  • 保存日志点击"保存日志"按钮,可将接收区域的内容保存为文本文件
  • 关闭串口使用完毕后,点击"关闭串口"按钮断开连接

技术特点

Python开发:基于Python 3.x开发,跨平台兼容

Tkinter界面:使用Tkinter构建简洁清爽的GUI界面

PySerial库:使用PySerial库实现串口通信

多线程处理:使用多线程实现异步数据接收

配置持久化:使用JSON格式保存和加载配置

简约设计:界面简洁清爽,操作直观便捷

运行要求

系统要求

Windows / Linux / macOS

# 安装依赖库
pip install pyserial
# 运行程序
python serial_assistant.py

界面说明

工具栏:位于窗口顶部,包含串口配置、打开/关闭按钮等控制组件,高度60px,简约风格设计。

接收区域:位于左侧,显示接收到的数据,支持十六进制显示,包含清空和保存日志按钮。

发送区域:位于右侧,用于输入和发送数据,支持十六进制发送,包含发送和清空按钮。

状态栏:位于窗口底部,显示当前状态、RX/TX计数等信息。

python 代码

"""
串口调试助手 - 简化版
功能:串口配置、数据收发、自动发送、日志记录等
UI:简洁清爽,易于使用
"""

import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog
import serial
import serial.tools.list_ports
import threading
import time
from datetime import datetime
import json
import os


class SerialAssistant:
    """串口调试助手主类"""
    
    def __init__(self, root):
        self.root = root
        self.root.title("串口调试助手")
        self.root.geometry("900x650")
        self.root.resizable(True, True)
        
        # 串口对象
        self.serial_port = None
        self.is_open = False
        self.receive_thread = None
        self.is_running = False
        self.auto_send_timer = None
        
        # 简化配色方案
        self.COLORS = {
            'primary': '#2196F3',
            'success': '#4CAF50',
            'danger': '#F44336',
            'warning': '#FF9800',
            'bg': '#FFFFFF',
            'text': '#000000',
            'border': '#CCCCCC',
            'bg_light': '#F5F5F5'
        }
        
        # 配置参数
        self.config = {
            'port': '',
            'baudrate': '9600',
            'bytesize': '8',
            'parity': 'None',
            'stopbits': '1',
            'send_hex': False,
            'receive_hex': False,
            'auto_send': False,
            'auto_send_interval': 1000,
            'auto_send_data': ''
        }
        
        # 加载配置
        self.load_config()
        
        # 创建界面
        self.create_widgets()
        
        # 刷新串口列表
        self.refresh_ports()
        
        # 窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)
    
    def create_widgets(self):
        """创建界面组件"""
        # 主容器
        main_container = tk.Frame(self.root, bg=self.COLORS['bg'])
        main_container.pack(fill="both", expand=True)
        
        # 顶部工具栏
        self.create_toolbar(main_container)
        
        # 中间内容区域
        content_area = tk.Frame(main_container, bg=self.COLORS['bg'])
        content_area.pack(fill="both", expand=True, padx=10, pady=10)
        
        # 左侧接收区域
        self.create_receive_area(content_area)
        
        # 右侧发送区域
        self.create_send_area(content_area)
        
        # 底部状态栏
        self.create_status_bar(main_container)
    
    def create_toolbar(self, parent):
        """创建顶部工具栏 - 简约风格"""
        toolbar = tk.Frame(parent, bg=self.COLORS['bg_light'], height=60)
        toolbar.pack(fill="x", side="top", padx=5, pady=5)
        toolbar.pack_propagate(False)
        
        # 第一行:标题和串口配置
        # 标题
        title_label = tk.Label(
            toolbar,
            text="串口调试助手",
            font=("Arial", 11, "bold"),
            fg=self.COLORS['primary'],
            bg=self.COLORS['bg_light']
        )
        title_label.grid(row=0, column=0, columnspan=2, pady=2, sticky="w")
        
        # 串口选择
        tk.Label(toolbar, text="串口:", bg=self.COLORS['bg_light']).grid(row=1, column=0, padx=2, pady=2)
        self.port_combo = ttk.Combobox(toolbar, width=10, state="readonly")
        self.port_combo.grid(row=1, column=1, padx=2, pady=2)
        
        # 波特率
        tk.Label(toolbar, text="波特率:", bg=self.COLORS['bg_light']).grid(row=1, column=2, padx=3)
        self.baudrate_combo = ttk.Combobox(
            toolbar,
            width=8,
            values=['300', '600', '1200', '2400', '4800', '9600', '14400', '19200', '38400', '57600', '115200'],
            state="readonly"
        )
        self.baudrate_combo.grid(row=1, column=3, padx=3)
        self.baudrate_combo.set(self.config['baudrate'])
        
        # 数据位
        tk.Label(toolbar, text="数据位:", bg=self.COLORS['bg_light']).grid(row=1, column=4, padx=3)
        self.bytesize_combo = ttk.Combobox(
            toolbar,
            width=4,
            values=['5', '6', '7', '8'],
            state="readonly"
        )
        self.bytesize_combo.grid(row=1, column=5, padx=3)
        self.bytesize_combo.set(self.config['bytesize'])
        
        # 停止位
        tk.Label(toolbar, text="停止位:", bg=self.COLORS['bg_light']).grid(row=1, column=6, padx=3)
        self.stopbits_combo = ttk.Combobox(
            toolbar,
            width=4,
            values=['1', '1.5', '2'],
            state="readonly"
        )
        self.stopbits_combo.grid(row=1, column=7, padx=3)
        self.stopbits_combo.set(self.config['stopbits'])
        
        # 校验位
        tk.Label(toolbar, text="校验位:", bg=self.COLORS['bg_light']).grid(row=2, column=0, padx=3)
        self.parity_combo = ttk.Combobox(
            toolbar,
            width=8,
            values=['None', 'Even', 'Odd', 'Mark', 'Space'],
            state="readonly"
        )
        self.parity_combo.grid(row=2, column=1, padx=3)
        self.parity_combo.set(self.config['parity'])
        
        # 刷新按钮
        refresh_btn = tk.Button(
            toolbar,
            text="刷新",
            command=self.refresh_ports,
            width=8
        )
        refresh_btn.grid(row=2, column=2, padx=5, pady=5)
        
        # 打开/关闭按钮
        self.open_btn = tk.Button(
            toolbar,
            text="打开串口",
            command=self.toggle_port,
            bg=self.COLORS['success'],
            fg='white',
            width=12
        )
        self.open_btn.grid(row=2, column=3, columnspan=2, padx=5, pady=5)
    
    def create_receive_area(self, parent):
        """创建接收区域 - 简约风格"""
        receive_frame = tk.Frame(parent, bg=self.COLORS['bg'])
        receive_frame.pack(side="left", fill="both", expand=True, padx=3)
        
        # 标题
        tk.Label(
            receive_frame,
            text="接收区域",
            font=("Arial", 10, "bold"),
            bg=self.COLORS['bg']
        ).pack(pady=5)
        
        # 接收选项
        options_frame = tk.Frame(receive_frame, bg=self.COLORS['bg'])
        options_frame.pack(fill="x", padx=5, pady=5)
        
        self.receive_hex_var = tk.BooleanVar(value=self.config['receive_hex'])
        tk.Checkbutton(
            options_frame,
            text="十六进制显示",
            variable=self.receive_hex_var,
            bg=self.COLORS['bg'],
            command=self.update_receive_hex
        ).pack(side="left")
        
        # 接收文本框
        self.receive_text = scrolledtext.ScrolledText(
            receive_frame,
            height=20,
            width=50,
            font=("Consolas", 10),
            state='disabled'
        )
        self.receive_text.pack(fill="both", expand=True, padx=5, pady=5)
        
        # 按钮区域
        btn_frame = tk.Frame(receive_frame, bg=self.COLORS['bg'])
        btn_frame.pack(fill="x", padx=5, pady=5)
        
        tk.Button(
            btn_frame,
            text="清空接收",
            command=self.clear_receive,
            width=10
        ).pack(side="left", padx=5)
        
        tk.Button(
            btn_frame,
            text="保存日志",
            command=self.save_log,
            width=10
        ).pack(side="left", padx=5)
    
    def create_send_area(self, parent):
        """创建发送区域 - 简约风格"""
        send_frame = tk.Frame(parent, bg=self.COLORS['bg'])
        send_frame.pack(side="right", fill="both", expand=True, padx=3)
        
        # 标题
        tk.Label(
            send_frame,
            text="发送区域",
            font=("Arial", 10, "bold"),
            bg=self.COLORS['bg']
        ).pack(pady=5)
        
        # 发送选项
        options_frame = tk.Frame(send_frame, bg=self.COLORS['bg'])
        options_frame.pack(fill="x", padx=5, pady=5)
        
        self.send_hex_var = tk.BooleanVar(value=self.config['send_hex'])
        tk.Checkbutton(
            options_frame,
            text="十六进制发送",
            variable=self.send_hex_var,
            bg=self.COLORS['bg'],
            command=self.update_send_hex
        ).pack(side="left")
        
        # 发送文本框 - 使用ScrolledText与接收区域保持一致
        self.send_text = scrolledtext.ScrolledText(
            send_frame,
            height=20,
            width=50,
            font=("Consolas", 10)
        )
        self.send_text.pack(fill="both", expand=True, padx=5, pady=5)
        self.send_text.insert("1.0", self.config['auto_send_data'])
        
        # 按钮区域 - 与接收区域保持一致的布局
        btn_frame = tk.Frame(send_frame, bg=self.COLORS['bg'])
        btn_frame.pack(fill="x", padx=5, pady=5)
        
        tk.Button(
            btn_frame,
            text="发送",
            command=self.send_data,
            bg=self.COLORS['primary'],
            fg='white',
            width=10
        ).pack(side="left", padx=5)
        
        tk.Button(
            btn_frame,
            text="清空发送",
            command=self.clear_send,
            width=10
        ).pack(side="left", padx=5)
        
        # 自动发送区域
        auto_frame = tk.LabelFrame(send_frame, text="自动发送", bg=self.COLORS['bg'])
        auto_frame.pack(fill="x", padx=5, pady=10)
        
        self.auto_send_var = tk.BooleanVar(value=self.config['auto_send'])
        tk.Checkbutton(
            auto_frame,
            text="启用自动发送",
            variable=self.auto_send_var,
            bg=self.COLORS['bg'],
            command=self.toggle_auto_send
        ).pack(pady=5)
        
        interval_frame = tk.Frame(auto_frame, bg=self.COLORS['bg'])
        interval_frame.pack(pady=5)
        
        tk.Label(interval_frame, text="间隔(ms):", bg=self.COLORS['bg']).pack(side="left")
        self.interval_entry = tk.Entry(interval_frame, width=10)
        self.interval_entry.pack(side="left", padx=5)
        self.interval_entry.insert(0, str(self.config['auto_send_interval']))
    
    def create_status_bar(self, parent):
        """创建状态栏"""
        status_bar = tk.Frame(parent, bg=self.COLORS['bg_light'], height=30)
        status_bar.pack(fill="x", side="bottom")
        status_bar.pack_propagate(False)
        
        self.status_label = tk.Label(
            status_bar,
            text="就绪",
            bg=self.COLORS['bg_light'],
            anchor="w"
        )
        self.status_label.pack(side="left", padx=10, pady=5)
        
        # 分隔线
        tk.Frame(status_bar, width=2, bg=self.COLORS['border']).pack(side="left", padx=10, fill="y")
        
        self.rx_count_label = tk.Label(
            status_bar,
            text="RX: 0",
            bg=self.COLORS['bg_light']
        )
        self.rx_count_label.pack(side="left", padx=10)
        
        self.tx_count_label = tk.Label(
            status_bar,
            text="TX: 0",
            bg=self.COLORS['bg_light']
        )
        self.tx_count_label.pack(side="left", padx=10)
    
    def refresh_ports(self):
        """刷新串口列表"""
        ports = serial.tools.list_ports.comports()
        port_list = [port.device for port in ports]
        self.port_combo['values'] = port_list
        if port_list and not self.port_combo.get():
            self.port_combo.current(0)
        self.update_status(f"发现 {len(port_list)} 个串口")
    
    def toggle_port(self):
        """打开/关闭串口"""
        if self.is_open:
            self.close_port()
        else:
            self.open_port()
    
    def open_port(self):
        """打开串口"""
        port = self.port_combo.get()
        if not port:
            messagebox.showwarning("警告", "请选择串口")
            return
        
        try:
            self.serial_port = serial.Serial(
                port=port,
                baudrate=int(self.baudrate_combo.get()),
                bytesize=int(self.bytesize_combo.get()),
                parity=self.parity_combo.get()[0] if self.parity_combo.get() != 'None' else 'N',
                stopbits=float(self.stopbits_combo.get()),
                timeout=1
            )
            self.is_open = True
            self.is_running = True
            self.open_btn.config(text="关闭串口", bg=self.COLORS['danger'])
            self.update_status(f"串口 {port} 已打开")
            
            # 启动接收线程
            self.receive_thread = threading.Thread(target=self.receive_data, daemon=True)
            self.receive_thread.start()
            
            # 保存配置
            self.save_config()
            
        except Exception as e:
            messagebox.showerror("错误", f"打开串口失败: {str(e)}")
            self.update_status(f"打开串口失败: {str(e)}")
    
    def close_port(self):
        """关闭串口"""
        if self.auto_send_var.get():
            self.toggle_auto_send()
        
        self.is_running = False
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.close()
        
        self.is_open = False
        self.open_btn.config(text="打开串口", bg=self.COLORS['success'])
        self.update_status("串口已关闭")
    
    def receive_data(self):
        """接收数据线程"""
        rx_count = 0
        
        while self.is_running:
            try:
                if self.serial_port and self.serial_port.is_open:
                    data = self.serial_port.read_all()
                    if data:
                        rx_count += len(data)
                        self.root.after(0, self.update_receive_text, data)
                        self.root.after(0, self.update_rx_count, rx_count)
                time.sleep(0.01)
            except Exception as e:
                self.root.after(0, self.update_status, f"接收错误: {str(e)}")
                break
    
    def update_receive_text(self, data):
        """更新接收文本"""
        self.receive_text.config(state='normal')
        
        if self.receive_hex_var.get():
            hex_str = ' '.join([f'{byte:02X}' for byte in data])
            self.receive_text.insert("end", hex_str + '\n')
        else:
            try:
                text = data.decode('utf-8', errors='ignore')
                self.receive_text.insert("end", text)
            except:
                hex_str = ' '.join([f'{byte:02X}' for byte in data])
                self.receive_text.insert("end", hex_str + '\n')
        
        self.receive_text.see("end")
        self.receive_text.config(state='disabled')
    
    def send_data(self):
        """发送数据"""
        if not self.is_open:
            messagebox.showwarning("警告", "请先打开串口")
            return
        
        data_str = self.send_text.get("1.0", "end-1c")
        if not data_str:
            messagebox.showwarning("警告", "请输入要发送的数据")
            return
        
        try:
            if self.send_hex_var.get():
                # 十六进制发送
                hex_str = data_str.replace(' ', '').replace('\n', '')
                data = bytes.fromhex(hex_str)
            else:
                # 文本发送
                data = data_str.encode('utf-8')
            
            self.serial_port.write(data)
            self.update_status(f"已发送 {len(data)} 字节")
            
            # 更新发送计数
            if not hasattr(self, 'tx_count'):
                self.tx_count = 0
            self.tx_count += len(data)
            self.update_tx_count(self.tx_count)
            
        except Exception as e:
            messagebox.showerror("错误", f"发送失败: {str(e)}")
            self.update_status(f"发送失败: {str(e)}")
    
    def toggle_auto_send(self):
        """切换自动发送"""
        if self.auto_send_var.get():
            try:
                interval = int(self.interval_entry.get())
                if interval < 10:
                    messagebox.showwarning("警告", "发送间隔不能小于10ms")
                    self.auto_send_var.set(False)
                    return
                
                self.config['auto_send'] = True
                self.config['auto_send_interval'] = interval
                self.auto_send_timer = self.root.after(interval, self.auto_send)
                self.update_status(f"自动发送已启动,间隔 {interval}ms")
                
            except ValueError:
                messagebox.showwarning("警告", "请输入有效的间隔时间")
                self.auto_send_var.set(False)
        else:
            self.config['auto_send'] = False
            if self.auto_send_timer:
                self.root.after_cancel(self.auto_send_timer)
                self.auto_send_timer = None
            self.update_status("自动发送已停止")
        
        self.save_config()
    
    def auto_send(self):
        """自动发送"""
        if self.auto_send_var.get() and self.is_open:
            self.send_data()
            interval = int(self.interval_entry.get())
            self.auto_send_timer = self.root.after(interval, self.auto_send)
    
    def clear_receive(self):
        """清空接收区"""
        self.receive_text.config(state='normal')
        self.receive_text.delete("1.0", "end")
        self.receive_text.config(state='disabled')
        self.update_status("接收区已清空")
    
    def clear_send(self):
        """清空发送区"""
        self.send_text.delete("1.0", "end")
        self.update_status("发送区已清空")
    
    def save_log(self):
        """保存日志"""
        content = self.receive_text.get("1.0", "end-1c")
        if not content:
            messagebox.showinfo("提示", "没有可保存的日志")
            return
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"serial_log_{timestamp}.txt"
        
        file_path = filedialog.asksaveasfilename(
            defaultextension=".txt",
            initialfile=filename,
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                messagebox.showinfo("成功", f"日志已保存到 {file_path}")
                self.update_status(f"日志已保存")
            except Exception as e:
                messagebox.showerror("错误", f"保存失败: {str(e)}")
    
    def update_status(self, message):
        """更新状态栏"""
        self.status_label.config(text=message)
    
    def update_rx_count(self, count):
        """更新接收计数"""
        self.rx_count_label.config(text=f"RX: {count}")
    
    def update_tx_count(self, count):
        """更新发送计数"""
        self.tx_count_label.config(text=f"TX: {count}")
    
    def update_receive_hex(self):
        """更新接收十六进制设置"""
        self.config['receive_hex'] = self.receive_hex_var.get()
        self.save_config()
    
    def update_send_hex(self):
        """更新发送十六进制设置"""
        self.config['send_hex'] = self.send_hex_var.get()
        self.save_config()
    
    def save_config(self):
        """保存配置"""
        self.config['port'] = self.port_combo.get()
        self.config['baudrate'] = self.baudrate_combo.get()
        self.config['bytesize'] = self.bytesize_combo.get()
        self.config['parity'] = self.parity_combo.get()
        self.config['stopbits'] = self.stopbits_combo.get()
        
        try:
            with open('serial_config.json', 'w', encoding='utf-8') as f:
                json.dump(self.config, f, indent=4, ensure_ascii=False)
        except Exception as e:
            print(f"保存配置失败: {e}")
    
    def load_config(self):
        """加载配置"""
        try:
            if os.path.exists('serial_config.json'):
                with open('serial_config.json', 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    self.config.update(config)
        except Exception as e:
            print(f"加载配置失败: {e}")
    
    def on_close(self):
        """窗口关闭事件"""
        if self.is_open:
            self.close_port()
        self.root.destroy()


def main():
    """主函数"""
    root = tk.Tk()
    app = SerialAssistant(root)
    root.mainloop()


if __name__ == "__main__":
    main()

配置文件serial_config.json

{
    "port": "COM1",
    "baudrate": "115200",
    "bytesize": "8",
    "parity": "None",
    "stopbits": "1",
    "send_hex": false,
    "receive_hex": false,
    "auto_send": true,
    "auto_send_interval": 1000,
    "auto_send_data": ""
}

以上就是Python使用Tkinter编写串口通信调试工具的详细内容,更多关于Python串口通信调试的资料请关注脚本之家其它相关文章!

相关文章

  • Python抽象属性使用解读(@property+@abstractmethod)

    Python抽象属性使用解读(@property+@abstractmethod)

    文章介绍了使用`@property`和`@abstractmethod`组合的Python编程模式,这种模式定义了一个只读的、强制子类提供的数据接口,子类可以通过属性或`@property`方法来实现这个接口,提供了灵活性和统一的调用方式
    2025-12-12
  • Opencv python 图片生成视频的方法示例

    Opencv python 图片生成视频的方法示例

    这篇文章主要介绍了Opencv python 图片生成视频的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • python网络爬虫精解之pyquery的使用说明

    python网络爬虫精解之pyquery的使用说明

    PyQuery是一个类似于jQuery的解析网页工具,使用lxml操作xml和html文档,它的语法和jQuery很像。和XPATH,Beautiful Soup比起来,PyQuery更加灵活,提供增加节点的class信息,移除某个节点,提取文本信息等功能
    2021-09-09
  • Python json格式化打印实现过程解析

    Python json格式化打印实现过程解析

    这篇文章主要介绍了Python json格式化打印实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 使用python绘制常用的图表

    使用python绘制常用的图表

    本文给大家介绍的是如何使用Python根据Excel表格数据绘制不同的图表的方法,非常的详细,有相同需求的小伙伴可以参考下
    2016-08-08
  • TensorFlow tf.nn.conv2d_transpose是怎样实现反卷积的

    TensorFlow tf.nn.conv2d_transpose是怎样实现反卷积的

    这篇文章主要介绍了TensorFlow tf.nn.conv2d_transpose是怎样实现反卷积的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • Python中Playwright的常用操作方法分享

    Python中Playwright的常用操作方法分享

    本文详细介绍了Playwright的常用操作方法,包括获取页面元素、点击按钮和链接等。这些方法可以帮助开发者更加高效地进行自动化测试和爬虫开发,需要的可以参考一下
    2023-05-05
  • python小数字符串转数字的五种方法

    python小数字符串转数字的五种方法

    本文主要介绍了python小数字符串转数字的五种方法,根据具体需求选择合适的方法进行小数字符串转数字,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Flask请求钩子与上下文及异常处理分项精解

    Flask请求钩子与上下文及异常处理分项精解

    这篇文章主要介绍了Flask请求钩子与上下文及异常处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-10-10
  • Python实现照片卡通化

    Python实现照片卡通化

    animegan2-pytorch机器学习项目可以实现照片动漫化,本文将为大家详细介绍一下如何使用这一项目,感兴趣的小伙伴快来跟随小编一起学习吧
    2021-12-12

最新评论