Python全功能PDF工具箱GUI:支持转换、加密、旋转、图片提取、日志记录等多功能操作
使用Python打造一款集成 PDF转换、编辑、加密、解密、图片提取、日志追踪 等多个功能于一体的桌面工具应用(Tkinter + ttkbootstrap + PyPDF2 等库)。
✨项目背景与开发动机


在日常办公或学习中,我们经常会遇到各种关于PDF文件的操作需求,比如:
-
将 PDF 转换成 Word 文档编辑
-
合并多个PDF为一个
-
分割PDF,按页导出
-
去掉PDF水印、加密、解密
-
从PDF中提取图片
-
将PDF每一页转为图片
-
旋转/删除指定页面
-
记录最近处理文件和操作日志
虽然市场上存在很多PDF软件,但免费+轻量+跨平台的桌面工具仍较少。于是我决定用 Python 打造一个集所有功能于一身的 PDF 工具箱,方便自己和他人使用!
🛠️主要技术栈与依赖
| 模块 | 功能 |
|---|---|
tkinter + ttkbootstrap | GUI 构建 |
tkinterDnD2 | 拖拽支持 |
PyPDF2, pdfplumber, fitz(PyMuPDF) | PDF编辑核心 |
pdf2image, PIL | PDF转图片 |
docx | Word文档生成 |
threading, json, os | 多线程处理和状态记录 |
📂功能结构总览
该程序结构如下图(部分简化):
PDFToolboxApp
├── PDF 转 Word(支持去水印)
├── PDF 分割(每页单独导出)
├── PDF 合并
├── PDF 加密 / 解密
├── 提取 PDF 中的图片
├── 页面旋转
├── 删除指定页面
├── PDF 转图片
└── 查看日志记录
接下来我们分模块详细介绍每个功能实现。
🧾基础组件与通用功能
🔹 日志与历史文件记录
-
log_action(action, file)
每次用户进行操作(如打开、转换文件)都记录到日志文件pdf_toolbox_logs.txt中。 -
add_to_history(filepath)
最近打开文件记录在pdf_toolbox_history.json中,便于下次快速访问。 -
get_recent_history()
读取最近10个操作过的文件。
🔹 去水印功能
def remove_watermark(text):WATERMARK_KEYWORDS = ["Confidential", "Watermark", "Do Not Copy", "内部资料", "机密"]return '\n'.join(line for line in text.split('\n') if not any(wm in line for wm in WATERMARK_KEYWORDS))
📄PDF 转 Word(支持去水印)
pdfplumber.open(path) → page.extract_text() → remove_watermark() → Word文档
-
使用
pdfplumber提取 PDF 每一页文本内容 -
可勾选“去水印”选项过滤掉机密/水印文本
-
保存为
.docx格式,支持拖拽上传PDF
✂️PDF 分割
每页导出为独立文件:
for i, page in enumerate(reader.pages): writer.add_page(page) writer.write(f"page_{i+1}.pdf")
用户选择PDF并设置导出目录后,将每一页保存为独立PDF文件。
➕PDF 合并
多文件合并为一个:
for f in files: for p in PdfReader(f).pages: writer.add_page(p)
支持多选文件,按照选择顺序合并。
🔐PDF 加密 / 解密
-
加密: 输入密码 → 写入新PDF
-
解密: 输入正确密码 → 导出解密版本
writer.encrypt(password) reader.decrypt(password)
🖼️提取PDF图片
使用 fitz(PyMuPDF)逐页扫描PDF,提取嵌入图片并保存为 PNG:
for page_index in range(len(doc)): for img in doc[page_index].get_images(full=True): pix = fitz.Pixmap(doc, img[0]) pix.save(...)
🔄页面旋转
用户输入:
-
页码:
1,2,3 -
角度:
90
代码示例:
if i + 1 in pages: page.rotate(angle)
🗑️删除指定页面
输入页码列表,如 2,5,7,将这些页从PDF中删除并保存新版本。
🖼️PDF 转图片(每页转 PNG)
images = convert_from_path(file)
for i, img in enumerate(images): img.save(f"page_{i+1}.png")
适合需要提取PDF为PPT使用或网页插图等。
🧾日志查看
简单读取 pdf_toolbox_logs.txt 文件,展示操作记录,便于溯源。
with open(LOG_FILE) as f: self.log_text.insert(tk.END, f.read())
🧵多线程支持
通过 @threaded 装饰器包装耗时操作(如转换)避免 GUI 卡死:
def threaded(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs).start()
🎬运行入口
if __name__ == '__main__': app = TkinterDnD.Tk() PDFToolboxApp(app) app.mainloop()
使用 TkinterDnD2 支持拖拽,整体体验更为现代化。
✅总结亮点
-
支持所有常用 PDF 操作(转换、编辑、处理、日志)
-
界面清晰、操作直观、支持拖拽上传
-
轻量级、不依赖大型软件,可本地私有化使用
-
拥有日志与历史记录,适合办公自动化场景
📦部署建议
安装依赖:
pip install tkinterdnd2 ttkbootstrap PyPDF2 pdfplumber python-docx pymupdf pdf2image pillow
如果你要将其打包为桌面可执行程序:
pyinstaller -F -w pdf_toolbox.py
如果你觉得这个项目实用,欢迎点赞、评论支持 🧡
如需源码或扩展功能可以私信我,我会继续更新!
以下为完整代码:
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
from tkinterdnd2 import TkinterDnD, DND_FILES
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from PyPDF2 import PdfReader, PdfWriter
import pdfplumber
from docx import Document
import fitz # PyMuPDF
from pdf2image import convert_from_path
from PIL import Image
import os
import threading
import json
from datetime import datetime# 日志 & 历史记录配置
LOG_FILE = "pdf_toolbox_logs.txt"
HISTORY_FILE = "pdf_toolbox_history.json"def log_action(action: str, file: str = ""):with open(LOG_FILE, "a", encoding="utf-8") as f:now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")f.write(f"[{now}] {action}: {file}\n")def add_to_history(filepath: str):filepath = os.path.abspath(filepath)history = []if os.path.exists(HISTORY_FILE):with open(HISTORY_FILE, "r", encoding="utf-8") as f:try:data = json.load(f)history = data.get("recent_files", [])except:history = []if filepath not in history:history.insert(0, filepath)history = history[:10]with open(HISTORY_FILE, "w", encoding="utf-8") as f:json.dump({"recent_files": history}, f, indent=2, ensure_ascii=False)def get_recent_history():if os.path.exists(HISTORY_FILE):with open(HISTORY_FILE, "r", encoding="utf-8") as f:try:data = json.load(f)return data.get("recent_files", [])except:return []return []def remove_watermark(text):WATERMARK_KEYWORDS = ["Confidential", "Watermark", "Do Not Copy", "内部资料", "机密"]lines = text.split('\n')return '\n'.join(line for line in lines if not any(wm in line for wm in WATERMARK_KEYWORDS))def threaded(fn):def wrapper(*args, **kwargs):threading.Thread(target=fn, args=args, kwargs=kwargs).start()return wrapper
class PDFToolboxApp:def __init__(self, root):self.root = rootself.root.title("PDF 工具箱 🧰 全功能版")self.root.geometry("900x650")self.root.resizable(True, True)self.build_tabs()def build_tabs(self):self.notebook = ttk.Notebook(self.root, bootstyle="primary")self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)self.pages = {}features = {"PDF 转 Word": self.build_pdf_to_word_tab,"PDF 分割": self.build_split_tab,"PDF 合并": self.build_merge_tab,"PDF 加密": self.build_encrypt_tab,"PDF 解密": self.build_decrypt_tab,"提取图片": self.build_extract_images_tab,"页面旋转": self.build_rotate_tab,"删除页面": self.build_delete_pages_tab,"PDF 转图片": self.build_pdf_to_image_tab,"查看日志": self.build_log_viewer_tab}for title, builder in features.items():frame = ttk.Frame(self.notebook, padding=20)self.pages[title] = frameself.notebook.add(frame, text=title)builder(frame)def select_file(self, variable):file = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")])if file:variable.set(file)add_to_history(file)log_action("选择文件", file)def on_drop_file(self, event, variable):filepath = event.data.strip('{}')if os.path.isfile(filepath):variable.set(filepath)add_to_history(filepath)log_action("拖拽导入", filepath)def build_pdf_to_word_tab(self, frame):ttk.Label(frame, text="PDF 转 Word(支持拖拽、可去水印)", font=("Arial", 12, "bold")).pack(pady=10)self.pdf_path_var = tk.StringVar()entry = ttk.Entry(frame, textvariable=self.pdf_path_var)entry.pack(fill='x', expand=True, padx=10, pady=5)# 拖拽支持entry.drop_target_register(DND_FILES)entry.dnd_bind('<<Drop>>', lambda e: self.on_drop_file(e, self.pdf_path_var))ttk.Button(frame, text="选择 PDF 文件", command=lambda: self.select_file(self.pdf_path_var)).pack(pady=5)self.remove_wm_var = tk.BooleanVar(value=True)ttk.Checkbutton(frame, text="去除常见水印", variable=self.remove_wm_var).pack(pady=5)self.progress = ttk.Progressbar(frame, bootstyle="info-striped")self.progress.pack(fill='x', expand=True, padx=10, pady=10)ttk.Button(frame, text="转换为 Word", command=self.run_pdf_to_word).pack(pady=5)@threadeddef run_pdf_to_word(self):path = self.pdf_path_var.get()if not path.endswith(".pdf"):messagebox.showwarning("警告", "请选择 PDF 文件")returnsave_path = filedialog.asksaveasfilename(defaultextension=".docx")if not save_path:returndef update_progress(val): self.progress['value'] = valtry:document = Document()with pdfplumber.open(path) as pdf:for i, page in enumerate(pdf.pages):text = page.extract_text() or ""if self.remove_wm_var.get():text = remove_watermark(text)document.add_paragraph(text)update_progress((i + 1) / len(pdf.pages) * 100)document.save(save_path)messagebox.showinfo("完成", "Word 文件已保存!")log_action("PDF 转 Word", path)except Exception as e:messagebox.showerror("错误", str(e))def build_split_tab(self, frame):ttk.Label(frame, text="PDF 分割(每页导出为独立文件)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择 PDF 文件", command=self.split_pdf_gui).pack(pady=5)def split_pdf_gui(self):file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])if not file:returnoutput_dir = filedialog.askdirectory()if not output_dir:returntry:reader = PdfReader(file)for i, page in enumerate(reader.pages):writer = PdfWriter()writer.add_page(page)out_path = os.path.join(output_dir, f"page_{i+1}.pdf")with open(out_path, "wb") as f:writer.write(f)messagebox.showinfo("完成", "分割成功!")log_action("PDF 分割", file)except Exception as e:messagebox.showerror("错误", str(e))def build_merge_tab(self, frame):ttk.Label(frame, text="PDF 合并(多个 PDF 合为一个)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择多个 PDF 文件", command=self.merge_pdfs_gui).pack(pady=5)def merge_pdfs_gui(self):files = filedialog.askopenfilenames(filetypes=[("PDF Files", "*.pdf")])if not files:returnoutput = filedialog.asksaveasfilename(defaultextension=".pdf")if not output:returntry:writer = PdfWriter()for f in files:reader = PdfReader(f)for p in reader.pages:writer.add_page(p)with open(output, "wb") as f:writer.write(f)messagebox.showinfo("完成", "合并成功!")log_action("PDF 合并", ",".join(files))except Exception as e:messagebox.showerror("错误", str(e))def build_encrypt_tab(self, frame):ttk.Label(frame, text="PDF 加密(设置访问密码)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择 PDF 文件", command=self.encrypt_pdf_gui).pack(pady=5)def encrypt_pdf_gui(self):file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])if not file:returnpassword = simpledialog.askstring("加密密码", "请输入加密密码:", show="*")if not password:returnoutput = filedialog.asksaveasfilename(defaultextension=".pdf")if not output:returntry:reader = PdfReader(file)writer = PdfWriter()for page in reader.pages:writer.add_page(page)writer.encrypt(password)with open(output, "wb") as f:writer.write(f)messagebox.showinfo("完成", "加密成功!")log_action("PDF 加密", file)except Exception as e:messagebox.showerror("错误", str(e))def build_decrypt_tab(self, frame):ttk.Label(frame, text="PDF 解密(需要密码)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择加密 PDF 文件", command=self.decrypt_pdf_gui).pack(pady=5)def decrypt_pdf_gui(self):file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])if not file:returnpassword = simpledialog.askstring("解密密码", "请输入密码:", show="*")if not password:returnoutput = filedialog.asksaveasfilename(defaultextension=".pdf")if not output:returntry:reader = PdfReader(file)if reader.is_encrypted:reader.decrypt(password)writer = PdfWriter()for page in reader.pages:writer.add_page(page)with open(output, "wb") as f:writer.write(f)messagebox.showinfo("完成", "解密成功!")log_action("PDF 解密", file)except Exception as e:messagebox.showerror("错误", str(e))def build_extract_images_tab(self, frame):ttk.Label(frame, text="提取 PDF 中的所有图片", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择 PDF 文件", command=self.extract_images_gui).pack(pady=5)def extract_images_gui(self):file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])if not file:returnoutput_dir = filedialog.askdirectory()if not output_dir:returntry:doc = fitz.open(file)for page_index in range(len(doc)):for img_index, img in enumerate(doc[page_index].get_images(full=True)):xref = img[0]pix = fitz.Pixmap(doc, xref)output_path = os.path.join(output_dir, f"page{page_index+1}_img{img_index+1}.png")if pix.n < 5:pix.save(output_path)else:pix = fitz.Pixmap(fitz.csRGB, pix)pix.save(output_path)pix = Nonemessagebox.showinfo("完成", "图片提取成功!")log_action("提取图片", file)except Exception as e:messagebox.showerror("错误", str(e))def build_rotate_tab(self, frame):ttk.Label(frame, text="页面旋转(输入页码如 1,2,5)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择 PDF 文件", command=self.choose_rotate_file).pack(pady=5)self.rotate_pages_var = tk.StringVar()ttk.Entry(frame, textvariable=self.rotate_pages_var).pack(fill='x', padx=10, pady=5)self.rotate_angle_var = tk.StringVar(value="90")ttk.Entry(frame, textvariable=self.rotate_angle_var).pack(fill='x', padx=10, pady=5)ttk.Button(frame, text="执行旋转", command=self.rotate_pdf_gui).pack(pady=5)def choose_rotate_file(self):self.rotate_file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])def rotate_pdf_gui(self):if not hasattr(self, "rotate_file"):returnpages = list(map(int, self.rotate_pages_var.get().split(",")))angle = int(self.rotate_angle_var.get())output = filedialog.asksaveasfilename(defaultextension=".pdf")if not output:returntry:reader = PdfReader(self.rotate_file)writer = PdfWriter()for i, page in enumerate(reader.pages):if i + 1 in pages:page.rotate(angle)writer.add_page(page)with open(output, "wb") as f:writer.write(f)messagebox.showinfo("完成", "旋转成功!")log_action("页面旋转", self.rotate_file)except Exception as e:messagebox.showerror("错误", str(e))def build_delete_pages_tab(self, frame):ttk.Label(frame, text="删除指定页(如 2,4,5)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择 PDF 文件", command=self.choose_delete_file).pack(pady=5)self.delete_pages_var = tk.StringVar()ttk.Entry(frame, textvariable=self.delete_pages_var).pack(fill='x', padx=10, pady=5)ttk.Button(frame, text="删除并保存", command=self.delete_pages_gui).pack(pady=5)def choose_delete_file(self):self.delete_file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])def delete_pages_gui(self):if not hasattr(self, "delete_file"):returnpages_to_delete = list(map(int, self.delete_pages_var.get().split(",")))output = filedialog.asksaveasfilename(defaultextension=".pdf")if not output:returntry:reader = PdfReader(self.delete_file)writer = PdfWriter()for i, page in enumerate(reader.pages):if i + 1 not in pages_to_delete:writer.add_page(page)with open(output, "wb") as f:writer.write(f)messagebox.showinfo("完成", "删除成功!")log_action("删除页面", self.delete_file)except Exception as e:messagebox.showerror("错误", str(e))def build_pdf_to_image_tab(self, frame):ttk.Label(frame, text="PDF 转图片(每页保存为 PNG)", font=("Arial", 12)).pack(pady=10)ttk.Button(frame, text="选择 PDF 文件", command=self.convert_pdf_to_images).pack(pady=5)def convert_pdf_to_images(self):file = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])if not file:returnoutdir = filedialog.askdirectory()if not outdir:returntry:images = convert_from_path(file)for i, img in enumerate(images):img.save(os.path.join(outdir, f"page_{i+1}.png"), "PNG")messagebox.showinfo("完成", "转换成功!")log_action("PDF 转图片", file)except Exception as e:messagebox.showerror("错误", str(e))def build_log_viewer_tab(self, frame):ttk.Label(frame, text="操作日志查看", font=("Arial", 12)).pack(pady=10)self.log_text = tk.Text(frame, wrap="word", height=30)self.log_text.pack(fill='both', expand=True)ttk.Button(frame, text="刷新日志", command=self.load_logs).pack(pady=5)def load_logs(self):if os.path.exists(LOG_FILE):with open(LOG_FILE, "r", encoding="utf-8") as f:self.log_text.delete(1.0, tk.END)self.log_text.insert(tk.END, f.read())else:self.log_text.insert(tk.END, "暂无日志记录。")# 主程序入口
if __name__ == '__main__':app = TkinterDnD.Tk()PDFToolboxApp(app)app.mainloop()
相关文章:
Python全功能PDF工具箱GUI:支持转换、加密、旋转、图片提取、日志记录等多功能操作
使用Python打造一款集成 PDF转换、编辑、加密、解密、图片提取、日志追踪 等多个功能于一体的桌面工具应用(Tkinter ttkbootstrap PyPDF2 等库)。 ✨项目背景与开发动机 在日常办公或学习中,我们经常会遇到各种关于PDF文件的操作需求&#…...
[密码学实战]国密算法面试题解析及应用
以下是密码学领域常见的面试题及其详细解析,涵盖基础理论、算法实现与应用场景,帮助系统化备战技术面试 一、基础概念类 1. 密码学的主要目标是什么? 答案: 确保数据的机密性(加密防止窃听)、完整性(哈希校验防篡改)、认证性(数字签名验证身份)和不可否认性(签名防…...
React 受控表单绑定基础
React 中最常见的几个需求是: 渲染一组列表绑定点击事件表单数据与组件状态之间的绑定 受控表单绑定是理解表单交互的关键之一。 📍什么是受控组件? 在 React 中,所谓“受控组件”,指的是表单元素(如 &l…...
计算机视觉---相机标定
相机标定在机器人系统中的作用 1.确定相机的内部参数 相机的内部参数包括焦距、主点坐标、像素尺寸等。这些参数决定了相机成像的几何关系。通过标定,可以精确获取这些参数,从而将图像中的像素坐标与实际的物理坐标建立联系。例如,已知相机…...
LeetCode 443 压缩字符串
字符数组压缩算法详解:实现与分析 一、引言 在处理字符数组时,我们常常遇到需要对连续重复字符进行压缩的场景。这不仅可以节省存储空间,还能提升数据传输效率。本文将深入解析一个经典的字符数组压缩算法,通过详细的实现步骤和…...
datasheet数据手册-阅读方法
DataSheet Datasheet(数据手册):电子元器件或者芯片的数据手册,一般由厂家编写,格式一般为PDF,内容为电子分立元器件或者芯片的各项参数,电性参数,物理参数,甚至制造材料…...
AI绘制流程图,方法概述
1 deepseek 生成图片的mermaid格式代码,在kimi中进行绘图或在jupter notebook中绘制: 或在draw.io中进行绘制(mermaid代码) 2 svg是矢量图,可以插入到word """mermaid graph TDA[基线解算] --> B[北…...
ObjectOutputStream 深度解析
ObjectOutputStream 深度解析 ObjectOutputStream 是 Java IO 体系中的一个关键类,用于序列化(将对象转换为字节流),通常与 ObjectInputStream 配合使用,实现对象的持久化存储或网络传输。 1.作用:完成对象的序列化过程 2.它可以将JVM当中的Java对象序列化到文件中/网…...
git回滚指定版本并操作
你可以通过以下步骤切换到第三个版本。根据你的需求,有两种主要方法: 方法 1:临时查看第三个版本(不修改当前分支) 适用于仅查看或测试旧版本,不保留后续修改: 找到第三个版本的提交哈希&#…...
【AI插件开发】Notepad++ AI插件开发实践:支持配置界面
一、引用 此前的系列文章已基本完成了Notepad的AI插件的功能开发,但是此前使用的配置为JSON配置文件,不支持界面配置。 本章在此基础上集成支持配置界面,这样不需要手工修改配置文件,直接在界面上操作,方便快捷。 注…...
polkitd服务无法启动导致docker无法启动问题解决
问题docker服务无法启动,溯源发现是polkit服务没有正确运行 systemctl status polkit可以看到类似提示 Sep 18 02:58:24 server1 dbus[897]: [system] Failed to activate service org.freedesktop.PolicyKit1: timed out Sep 18 02:59:29 server1 systemd[1]: po…...
软件工程中数据一致性的探讨
软件工程中数据一致性的探讨 引言数据一致性:软件工程中的业务正确性与性能的权衡数据一致性为何重要业务正确性:事务的原子性与一致性ACID原则的基石分布式事务的挑战一致性级别:从强一致到最终一致 实践中的一致性权衡金融系统:…...
数据库原理及应用mysql版陈业斌实验四
🏝️专栏:Mysql_猫咪-9527的博客-CSDN博客 🌅主页:猫咪-9527-CSDN博客 “欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。” 目录 实验四索引与视图 1.实验数据如下 student 表(学生表&…...
华为OD机试真题——最长的顺子(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录全流程解析/备考攻略/经验…...
【HTML】html文件
HTML文件全解析:搭建网页的基石 在互联网的广袤世界里,每一个绚丽多彩、功能各异的网页背后,都离不开HTML文件的默默支撑。HTML,即超文本标记语言(HyperText Markup Language),作为网页创建的基…...
使用 XWPFDocument 生成表格时固定列宽度
一、XWPFDocument XWPFTable个性化属性 1.初始默认写法 XWPFTable table document.createTable(n, m); //在文档中创建一个n行m列的表格 table.setWidth("100%"); // 表格占页面100%宽度// 通过getRow获取行进行自定义设置 XWPFTableRow row table.getRow(0); XW…...
足球AI模型:一款用数据分析赛事的模型
2023 年欧冠决赛前,某体育数据平台的 AI 模型以 78% 的概率预测曼城夺冠 —— 最终瓜迪奥拉的球队首次捧起大耳朵杯。当足球遇上 AI,那些看似玄学的 "足球是圆的",正在被数据与算法拆解成可计算的概率命题。今天我们就来聊聊&#…...
【ESP32|音频】一文读懂WAV音频文件格式【详解】
简介 最近在学习I2S音频相关内容,无可避免会涉及到关于音频格式的内容,所以刚开始接触的时候有点一头雾水,后面了解了下WAV相关内容,大致能够看懂wav音频格式是怎么样的了。本文主要为后面ESP32 I2S音频系列文章做铺垫࿰…...
万向死锁的发生
我是标题 1.欧拉角2.万向死锁 参考:小豆8593 1.欧拉角 欧拉角在Unity中描述的是一种变换(Transform)共有3个轴体,默认顺序为x->y->z. 2.万向死锁 可以把万向死锁的情况理解成:由于轴体旋转的顺序是固定的&am…...
JavaScript学习教程,从入门到精通,JavaScript BOM (Browser Object Model) 详解(18)
JavaScript BOM (Browser Object Model) 详解 1. BOM 介绍 BOM (Browser Object Model) 是浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象。BOM的核心对象是window,它表示浏览器的一个实例。 BOM包含的主要对象: window…...
人工智能与云计算:技术融合与实践
1. 引言 人工智能(AI)和云计算是当今科技领域最具变革性的两项技术。AI通过模拟人类智能解决问题,而云计算则提供了弹性可扩展的计算资源。两者的结合创造了前所未有的可能性,使企业能够以更低的成本部署复杂的AI解决方案。 本文将探讨AI与云计算的技术融合,包括核心概念、…...
42.[前端开发-JavaScript高级]Day07-手写apply-call-bind-块级作用域
手写apply-call-bind <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevi…...
ObjectInputStream 终极解析与记忆指南
ObjectInputStream 终极解析与记忆指南 一、核心本质 ObjectInputStream 是 Java 提供的对象反序列化流,继承自 InputStream,用于读取由ObjectOutputStream序列化的Java对象。 核心特性速查表 特性说明继承链InputStream → ObjectInputStream核心功能实现Java对象反序列化…...
数据结构有哪些类型(对于数据结构的简述)
在学习计算机时,数据结构是不可忽视的一点,从考研时的408课程,再到工作中编写软件,网站,要想在计算机领域站住脚跟,数据结构是必备的 在这里,我对于数据结构进行了汇总,并简要描述&…...
Vscode 插件开发
文章目录 1、使用vscode官方插件生成框架,下载脚手架2、使用脚手架初始化项目,这里我选择的是js3、生成的文件结构如下,重要的就是以下两个文件4、代码5、打包使用6、发布官网地址7、publisher ID undefined provided in the extension manif…...
C# string和其他引用类型的区别
在C#中,字符串(String)和其他引用类型(Reference Types)之间有几个关键的区别,这些区别主要体现在它们的内存管理、赋值行为以及使用方式上。 1. 内存管理 字符串(String)࿱…...
RTT添加一个RTC时钟驱动,以DS1307为例
添加一个外部时钟芯片 这里多了一个选项 复制drv_rtc.c,重命名为drv_rtc_ds1307.c 添加到工程中 /*** @file drv_rtc_ds1307.c* @brief * @author jiache (wanghuan3037@fiberhome.com)* @version 1.0* @date 2025-01-08* * @copyright Copyright (c) 2025 58* */ #...
常见的低代码策略整理
低代码策略通过简化开发流程、降低技术门槛、提升效率,帮助用户快速构建灵活可靠的应用。这些策略的核心优势体现在以下方面: 快速交付与降本增效 减少编码需求:通过可视化配置(如变量替换、表达式函数)替代传统编码…...
从彩色打印单行标准九九表学习〖代码情书〗的书写范式(Python/DeepSeek)
写给python终端的情书,学习代码设计/书写秘笈。 笔记模板由python脚本于2025-04-17 12:49:08创建,本篇笔记适合有python编程基础的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值:在于输出思考与经验,而不仅仅是知识的简…...
QML与C++:基于ListView调用外部模型进行增删改查(附自定义组件)
目录 引言相关阅读项目结构文件组织 核心技术实现1. 数据模型设计联系人项目类 (datamodel.h)数据模型类 (datamodel.h)数据模型实现 (datamodel.cpp) 2. 主程序入口点 (main.cpp)3. 主界面设计 (Main.qml)4. 联系人对话框 (ContactDialog.qml)5. 自定义组件CustomTextField.qm…...
