【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现

🎵 【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现

请添加图片描述

🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

请添加图片描述在这里插入图片描述

📖 概述

在数字音乐管理过程中,音频文件内嵌的封面图片往往会占用额外存储空间,特别是当我们需要批量处理大量音频文件时。本文介绍一款基于Python和PyQt5开发的跨平台音频封面删除工具,它支持多种音频格式(MP3、FLAC、M4A、OGG、WMA),提供三种不同的处理方式,并具备友好的图形用户界面。

本工具不仅能有效移除音频文件中的封面数据,还能保持音频质量无损,是音乐收藏家和数字资产管理者的实用工具。下面我们将从功能、实现原理、代码解析等多个维度进行详细介绍。

🛠️ 功能特点

  1. 多格式支持

    • MP3 (ID3标签)
    • FLAC (Vorbis注释)
    • M4A/MP4 (iTunes元数据)
    • OGG (Vorbis/Opus)
    • WMA (ASF容器)
  2. 三种处理方式

    • Mutagen库(推荐):Python专用音频元数据处理库
    • FFmpeg:专业音视频处理工具
    • 二进制处理:最后手段的直接文件操作
  3. 智能文件管理

    • 拖放文件夹支持
    • 自动扫描子目录
    • 可选输出目录设置
    • 文件类型过滤
  4. 可视化操作

    • 进度条显示
    • 处理结果统计
    • 错误处理机制

🖼️ 界面展示

在这里插入图片描述

图1:软件主界面,包含目录设置、文件列表和操作按钮
在这里插入图片描述
在这里插入图片描述

图2、图3:文件处理进度显示

🧰 使用说明

1. 准备工作

  • 安装Python 3.7+
  • 安装依赖库:
    pip install PyQt5 mutagen
    
    • (可选) 如需使用FFmpeg方式,需提前安装FFmpeg并加入系统PATH

2. 操作步骤

  1. 选择输入目录:点击"浏览"按钮或直接拖放文件夹到输入框
  2. 设置输出目录(可选):默认为输入目录下的"cleaned_audio"文件夹
  3. 选择处理方式
    • Mutagen:推荐方式,处理速度快且稳定
    • FFmpeg:适合复杂音频文件
    • 二进制:最后手段,兼容性较差
  4. 扫描文件:点击"扫描文件"按钮获取目录下所有支持的音频文件
  5. 选择处理范围
    • “处理选中”:仅处理列表中选中的文件
    • “处理全部”:批量处理所有扫描到的文件
  6. 查看结果:处理完成后会显示成功/失败统计,处理后的文件保存在输出目录

3. 注意事项

  • 处理前建议备份原始文件
  • 某些音频播放器可能需要重新扫描文件才能显示更改
  • FLAC文件的封面删除会同时移除所有内嵌图片

💻 代码深度解析

1. 核心技术栈

  • PyQt5:构建现代化GUI界面
  • Mutagen:音频元数据处理核心库
  • FFmpeg(可选):专业音视频处理
  • 标准库:os, sys, shutil等处理文件操作

2. 关键类说明

DraggableLineEdit (自定义拖放文本框)
class DraggableLineEdit(QLineEdit):def dragEnterEvent(self, event):if event.mimeData().hasUrls():event.acceptProposedAction()def dropEvent(self, event):for url in event.mimeData().urls():path = url.toLocalFile()if os.path.isdir(path):self.setText(path)break

实现文件夹拖放功能的核心代码,增强了用户体验

AudioCoverRemover (主窗口类)
def process_with_mutagen(self, input_path, output_path, ext):# 先复制文件if input_path != output_path:shutil.copy2(input_path, output_path)# 根据格式使用不同的处理方法if ext == "mp3":audio = MP3(output_path, ID3=ID3)if audio.tags:audio.tags.delall("APIC")audio.save()elif ext == "flac":audio = FLAC(output_path)if audio.pictures:audio.clear_pictures()audio.save()...

不同音频格式的封面删除逻辑,展示了Mutagen库的强大灵活性

3. 设计亮点

  1. 多方法兼容处理
    • 提供三种不同实现方式,确保最大兼容性
    • 自动选择最适合当前文件的方法
  2. 现代化UI设计
    • 自定义样式表美化界面
    • 响应式布局适应不同分辨率
    • 进度反馈增强用户体验
  3. 健壮的错误处理
    • 捕获并记录各种处理异常
    • 不影响整体批处理流程
  4. 跨平台支持
    • 兼容Windows/macOS/Linux
    • 自动处理路径分隔符差异

📥 源码下载

import os
import sys
import subprocess
import shutil
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC
from mutagen.flac import FLAC
from mutagen.mp4 import MP4
from mutagen.oggopus import OggOpus
from mutagen.oggvorbis import OggVorbis
from mutagen.asf import ASF
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QLineEdit, QFileDialog,QListWidget, QWidget, QProgressBar, QMessageBox,QCheckBox, QGroupBox, QComboBox)
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QColor, QPalette, QIconclass DraggableLineEdit(QLineEdit):def __init__(self, parent=None):super().__init__(parent)self.setAcceptDrops(True)def dragEnterEvent(self, event):if event.mimeData().hasUrls():event.acceptProposedAction()def dropEvent(self, event):for url in event.mimeData().urls():path = url.toLocalFile()if os.path.isdir(path):self.setText(path)breakclass AudioCoverRemover(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("🎵 音频封面删除工具")self.setGeometry(100, 100, 547, 608)# 支持的音频格式self.supported_formats = {'mp3': 'MP3音频','flac': 'FLAC无损音频','m4a': 'MP4/AAC音频','ogg': 'OGG音频','wma': 'WMA音频'}# 初始化变量self.audio_files = []self.current_method = "mutagen"# 设置UI样式self.setup_ui_style()# 初始化UIself.init_ui()# 设置窗口图标self.setWindowIcon(QIcon(self.get_icon_path()))def get_icon_path(self):"""获取图标路径(适配不同平台)"""if getattr(sys, 'frozen', False):# 打包后的路径base_path = sys._MEIPASSelse:# 开发时的路径base_path = os.path.dirname(os.path.abspath(__file__))return os.path.join(base_path, 'icon.png')def setup_ui_style(self):"""设置现代化UI样式"""palette = self.palette()palette.setColor(QPalette.Window, QColor(245, 245, 245))palette.setColor(QPalette.WindowText, QColor(60, 60, 60))palette.setColor(QPalette.Base, QColor(255, 255, 255))palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240))palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 220))palette.setColor(QPalette.ToolTipText, Qt.black)palette.setColor(QPalette.Text, Qt.black)palette.setColor(QPalette.Button, QColor(70, 160, 230))palette.setColor(QPalette.ButtonText, Qt.white)palette.setColor(QPalette.BrightText, Qt.red)palette.setColor(QPalette.Highlight, QColor(70, 160, 230))palette.setColor(QPalette.HighlightedText, Qt.white)self.setPalette(palette)self.setStyleSheet("""QGroupBox {border: 1px solid #dcdcdc;border-radius: 6px;margin-top: 12px;padding-top: 18px;font-weight: bold;color: #505050;}QGroupBox::title {subcontrol-origin: margin;left: 12px;padding: 0 5px;}QPushButton {background-color: #46a0f0;color: white;border: none;padding: 7px 14px;border-radius: 5px;min-width: 90px;font-size: 13px;}QPushButton:hover {background-color: #3a8cd0;}QPushButton:pressed {background-color: #2e78b0;}QPushButton:disabled {background-color: #cccccc;color: #888888;}QListWidget {border: 1px solid #dcdcdc;border-radius: 5px;background: white;font-size: 13px;}QProgressBar {border: 1px solid #dcdcdc;border-radius: 5px;text-align: center;height: 20px;font-size: 12px;}QProgressBar::chunk {background-color: #46a0f0;border-radius: 4px;}QComboBox {border: 1px solid #dcdcdc;border-radius: 4px;padding: 3px;min-width: 120px;}QLineEdit {border: 1px solid #dcdcdc;border-radius: 4px;padding: 5px;}""")def init_ui(self):main_widget = QWidget()self.setCentralWidget(main_widget)layout = QVBoxLayout()layout.setContentsMargins(12, 12, 12, 12)layout.setSpacing(10)# 顶部控制区域top_layout = QHBoxLayout()# 方法选择method_layout = QHBoxLayout()method_layout.addWidget(QLabel("处理方法:"))self.method_combo = QComboBox()self.method_combo.addItems(["Mutagen (推荐)", "FFmpeg", "二进制处理"])method_layout.addWidget(self.method_combo)# 格式过滤format_layout = QHBoxLayout()format_layout.addWidget(QLabel("文件类型:"))self.format_combo = QComboBox()self.format_combo.addItems(["所有支持格式"] + list(self.supported_formats.values()))format_layout.addWidget(self.format_combo)top_layout.addLayout(method_layout)top_layout.addStretch()top_layout.addLayout(format_layout)# 目录设置dir_group = QGroupBox("目录设置")dir_layout = QVBoxLayout()dir_layout.setSpacing(10)# 输入目录(使用自定义的可拖放QLineEdit)input_layout = QHBoxLayout()input_layout.addWidget(QLabel("输入目录:"))self.input_path = DraggableLineEdit()self.input_path.setPlaceholderText("拖放文件夹到这里或点击浏览...")self.browse_input_btn = QPushButton("浏览")self.browse_input_btn.clicked.connect(self.browse_input)input_layout.addWidget(self.input_path, stretch=1)input_layout.addWidget(self.browse_input_btn)# 输出目录output_layout = QHBoxLayout()output_layout.addWidget(QLabel("输出目录:"))self.output_path = DraggableLineEdit()self.output_path.setPlaceholderText("默认: 输入目录下的'cleaned_audio'文件夹")self.browse_output_btn = QPushButton("浏览")self.browse_output_btn.clicked.connect(self.browse_output)output_layout.addWidget(self.output_path, stretch=1)output_layout.addWidget(self.browse_output_btn)dir_layout.addLayout(input_layout)dir_layout.addLayout(output_layout)dir_group.setLayout(dir_layout)# 文件列表self.file_list = QListWidget()self.file_list.setSelectionMode(QListWidget.MultiSelection)self.file_list.setMinimumHeight(250)# 进度条self.progress = QProgressBar()self.progress.setVisible(False)# 操作按钮btn_layout = QHBoxLayout()self.scan_btn = QPushButton("🔍 扫描文件")self.scan_btn.clicked.connect(self.scan_files)self.process_btn = QPushButton("⚡ 处理选中")self.process_btn.clicked.connect(self.process_selected)self.process_btn.setEnabled(False)self.process_all_btn = QPushButton("🚀 处理全部")self.process_all_btn.clicked.connect(self.process_all)self.process_all_btn.setEnabled(False)btn_layout.addWidget(self.scan_btn)btn_layout.addWidget(self.process_btn)btn_layout.addWidget(self.process_all_btn)# 添加到主布局layout.addLayout(top_layout)layout.addWidget(dir_group)layout.addWidget(self.file_list)layout.addWidget(self.progress)layout.addLayout(btn_layout)main_widget.setLayout(layout)self.update_buttons()def browse_input(self):path = QFileDialog.getExistingDirectory(self, "选择输入目录")if path:self.input_path.setText(path)def browse_output(self):path = QFileDialog.getExistingDirectory(self, "选择输出目录")if path:self.output_path.setText(path)def scan_files(self):input_dir = self.input_path.text()if not os.path.isdir(input_dir):QMessageBox.warning(self, "错误", "请输入有效的输入目录")returnself.audio_files = []self.file_list.clear()# 显示扫描进度self.progress.setVisible(True)self.progress.setRange(0, 0)  # 不确定进度模式QApplication.processEvents()# 获取选择的格式selected_format = self.format_combo.currentText()if selected_format == "所有支持格式":extensions = list(self.supported_formats.keys())else:extensions = [k for k, v in self.supported_formats.items() if v == selected_format]for root, _, files in os.walk(input_dir):for file in files:ext = os.path.splitext(file)[1][1:].lower()if ext in extensions:self.audio_files.append(os.path.join(root, file))self.file_list.addItems([os.path.basename(f) for f in self.audio_files])self.progress.setVisible(False)self.update_buttons()QMessageBox.information(self, "完成", f"找到 {len(self.audio_files)} 个音频文件")def process_selected(self):selected = self.file_list.selectedItems()if not selected:QMessageBox.warning(self, "警告", "请先选择要处理的文件")returnindices = [self.file_list.row(item) for item in selected]self.process_files(indices)def process_all(self):if not self.audio_files:QMessageBox.warning(self, "警告", "没有可处理的文件")returnreply = QMessageBox.question(self, "确认", f"确定要处理所有 {len(self.audio_files)} 个文件吗?",QMessageBox.Yes | QMessageBox.No)if reply == QMessageBox.Yes:self.process_files(range(len(self.audio_files)))def process_files(self, indices):method = self.method_combo.currentText().split()[0].lower()input_dir = self.input_path.text()output_dir = self.output_path.text() or os.path.join(input_dir, "cleaned_audio")total = len(indices)success = 0failed = 0self.progress.setVisible(True)self.progress.setMaximum(total)self.progress.setValue(0)os.makedirs(output_dir, exist_ok=True)for i, idx in enumerate(indices, 1):input_path = self.audio_files[idx]filename = os.path.basename(input_path)output_path = os.path.join(output_dir, filename)try:ext = os.path.splitext(input_path)[1][1:].lower()if method == "ffmpeg":result = self.process_with_ffmpeg(input_path, output_path)elif method == "mutagen":result = self.process_with_mutagen(input_path, output_path, ext)else:  # 二进制处理result = self.process_binary(input_path, output_path, ext)if result:success += 1else:failed += 1except Exception as e:print(f"处理失败 {input_path}: {str(e)}")failed += 1self.progress.setValue(i)QApplication.processEvents()self.progress.setVisible(False)QMessageBox.information(self, "完成",f"处理完成!\n成功: {success}\n失败: {failed}\n输出目录: {output_dir}")def process_with_ffmpeg(self, input_path, output_path):"""使用FFmpeg处理"""try:cmd = ["ffmpeg","-i", input_path,"-map", "0:a","-c:a", "copy","-map_metadata", "-1",output_path,"-y"  # 覆盖输出文件]subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)return Trueexcept Exception as e:print(f"FFmpeg处理失败: {str(e)}")return Falsedef process_with_mutagen(self, input_path, output_path, ext):"""使用Mutagen处理不同格式的音频文件"""try:# 先复制文件if input_path != output_path:shutil.copy2(input_path, output_path)# 根据格式使用不同的处理方法if ext == "mp3":audio = MP3(output_path, ID3=ID3)if audio.tags:audio.tags.delall("APIC")audio.save()elif ext == "flac":audio = FLAC(output_path)if audio.pictures:audio.clear_pictures()audio.save()elif ext == "m4a":audio = MP4(output_path)if 'covr' in audio:del audio['covr']audio.save()elif ext == "ogg":try:audio = OggOpus(output_path)except:audio = OggVorbis(output_path)if 'metadata_block_picture' in audio:del audio['metadata_block_picture']audio.save()elif ext == "wma":audio = ASF(output_path)if hasattr(audio, 'tags') and 'WM/Picture' in audio.tags:del audio.tags['WM/Picture']audio.save()return Trueexcept Exception as e:print(f"Mutagen处理失败: {str(e)}")return Falsedef process_binary(self, input_path, output_path, ext):"""二进制方式处理(最后手段)"""try:if ext == "mp3":# MP3文件的简单二进制处理with open(input_path, "rb") as f:data = f.read()apic_pos = data.find(b"APIC")if apic_pos == -1:if input_path != output_path:shutil.copy2(input_path, output_path)return Truenew_data = data[:apic_pos] + data[apic_pos+4:]with open(output_path, "wb") as f:f.write(new_data)return Trueelse:# 其他格式直接复制(无法二进制处理)if input_path != output_path:shutil.copy2(input_path, output_path)return Falseexcept Exception as e:print(f"二进制处理失败: {str(e)}")return Falsedef update_buttons(self):has_files = bool(self.audio_files)self.process_btn.setEnabled(has_files)self.process_all_btn.setEnabled(has_files)def main():app = QApplication(sys.argv)# 设置应用程序样式app.setStyle('Fusion')window = AudioCoverRemover()window.show()sys.exit(app.exec_())if __name__ == "__main__":main()

🎯 性能优化建议

  1. 多线程处理
   # 可使用QThreadPool实现多线程处理from PyQt5.QtCore import QThreadPool, QRunnableclass Worker(QRunnable):def __init__(self, task_func):super().__init__()self.task_func = task_funcdef run(self):self.task_func()
  1. 缓存机制

    • 缓存已扫描文件列表
    • 实现增量处理功能
  2. 元数据分析

    • 添加封面大小统计功能
    • 支持预览被删除的封面

📝 总结

本文详细介绍了一款功能完善的音频封面删除工具的开发过程。通过结合PyQt5的GUI能力和Mutagen的音频处理能力,我们实现了一个用户友好且功能强大的应用程序。关键收获包括:

  1. 音频处理知识:深入理解了不同音频格式的元数据存储方式
  2. GUI开发技巧:掌握了现代化Qt界面设计方法
  3. 健壮性设计:学习了多种处理方法的兼容实现

该工具不仅具有实用价值,其开发过程也展示了Python在多媒体处理领域的强大能力。读者可以根据实际需求进一步扩展功能,如添加音频格式转换、元数据编辑等特性。

扩展思考:如何将此工具集成到自动化音乐管理流水线中?能否结合机器学习自动识别并分类音乐封面?


附录:完整代码

文中的完整Python代码已在前文展示,也可从GitHub仓库获取最新版本。建议在Python 3.7+环境中运行,并安装所有依赖库。

希望本文对您的音频处理项目有所启发!如有任何问题,欢迎在评论区讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA房屋租售管理系统房屋出租出售平台房屋销售房屋租赁房屋交易信息管理源码

一、源码描述 这是一套房屋租售管理源码,基于SpringBootVue框架,后端采用JAVA开发,源码功能完善,涵盖了房屋租赁、房屋销售、房屋交易等业务。 二、源码截图

一篇文章讲清楚mysql的聚簇索引、非聚簇索引、辅助索引

聚簇索引与非聚簇索引最大的区别就是: 聚簇索引的索引和数据是存放在一起的,都是在叶子结点; 非聚簇索引的索引和数据是分开存储的,叶子节点存放的是索引和指向数据文件的地址,通过叶子节点找到索引,再通…

使用ESPHome烧录固件到ESP32-C3并接入HomeAssistant

文章目录 一、安装ESPHome二、配置ESP32-C3控制灯1.主配置文件esp32c3-luat.yaml2.基础通用配置base.yaml3.密码文件secret.yaml4.围栏灯four_light.yaml5.彩灯rgb_light.yaml6.左右柱灯left_right_light.yaml 三、安装固件四、HomeAssistant配置ESPHome1.直接访问2.配置ESPHom…

什么是变量提升?

变量提升(Hoisting) 是 JavaScript 引擎在代码执行前的一个特殊行为,它会将变量声明和函数声明自动移动到当前作用域的顶部。但需要注意的是,只有声明会被提升,赋值操作不会提升。 ​​核心概念​​ 变量声明提升&…

【万字长文】深入浅出 LlamaIndex 和 LangChain:从RAG到智能体,轻松驾驭LLM应用开发

Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…

2025 后端自学UNIAPP【项目实战:旅游项目】3、API接口请求封装,封装后的简单测试以及实际使用

一、创建请求封装目录 选中自己的项目,右键鼠标---->新建---->目录---->名字自定义【我的是api】 二、创建两个js封装文件 选中封装的目录,右键鼠标---->新建---->js文件---->名字自定义【我的两个js文件分别是my_http和my_api】 三…

autojs和冰狐智能辅助该怎么选择?

最近打算做自动化脚本,在autojs和冰狐智能辅助中做选择,不知道该怎么选。没办法只能花费大量时间仔细研究了autojs和冰狐智能辅助,综合考虑功能需求、开发复杂度、编程经验及项目规模等因素。以下是两者的核心对比及选择建议,仅供…

python24-匿名函数

课程:B站大学 记录python学习,直到学会基本的爬虫,使用python搭建接口自动化测试就算学会了,在进阶webui自动化,app自动化 匿名函数 匿名函数实践是检验真理的唯一标准 匿名函数 匿名函数是指没有名字的函数&#xff…

Android 查看 Logcat (可纯手机方式 无需电脑)

安装 Logcat Reader Github Google Play 如果有电脑 使用其ADB方式可执行如下命令 后续无需安装Termux # 使用 ADB 授予 android.permission.READ_LOGS 权限给 Logcat Reader adb shell "pm grant com.dp.logcatapp android.permission.READ_LOGS && am force-…

驱动开发硬核特训 · Day 30(上篇):深入理解 I2C 总线驱动模型(以 at24 EEPROM 为例)

作者:嵌入式Jerry 视频教程请关注 B 站:“嵌入式Jerry” 一、写在前面 在上一阶段我们已经深入理解了字符设备驱动与设备模型之间的结合方式、sysfs 的创建方式以及平台驱动模型的实际运用。今天我们迈入总线驱动模型的世界,聚焦于 I2C 总线…

超详细讲解注意力机制、自注意力机制、多头注意力机制、通道注意力机制、空间注意力机制

在如今的机器学习和深度学习领域,注意力机制绝对是一个热度居高不下的话题。事实上,注意力机制并不是一个全新的概念,早在多年前就已经被提出并应用。比如在图像分类任务中,SENet 和 ECA-Net 等模型中都运用了注意力机制&#xff…

Wireshark基本使用

本文会对Wireshark做简单介绍,带大家熟悉一下Wireshark的界面,以及如何使用过滤器。 接着会带大家查看TCP五层模型下,带大家回顾各层首部的格式。 最后会演示 Wireshark 如何抓取三次握手和四次挥手包的过程。 目录 一.Wireshark简介 二…

加速项目落地(Trae编辑器)

目录 vscode安装python支持 vscode常用插件 Trae编辑器 两个界面合成 补充(QT开发的繁琐) AI编程哪家强?Cursor、Trae深度对比,超详细! - 知乎 Trae兼容vscode的插件,我们可以先在vscode里面装好再一…

stable diffusion的attention-map:提取和可视化跨注意力图

项目: wooyeolbaek/attention-map-diffusers: 🚀 Cross attention map tools for huggingface/diffusers 参考:【可视化必备技能(1)】SD / Flux 文生图模型的 Attention Map 可视化-CSDN博客

多环串级PID

文章目录 为什么要多环程序主函数内环外环 双环PID调参内环Kp调法Ki调法 外环Kp 以一定速度到达指定位置封装 为什么要多环 单环只能单一控制速度或者位置,如果想要同时控制多个量如速度,位置,角度,就需要多个PID 速度环一般PI…

基于Kubernetes的Apache Pulsar云原生架构解析与集群部署指南(上)

#作者:闫乾苓 文章目录 概念和架构概述主要特点消息传递核心概念Pulsar 的消息模型Pulsar 的消息存储与分发Pulsar 的高级特性架构BrokerBookKeeperZooKeeper 概念和架构 概述 Pulsar 是一个多租户、高性能的服务器到服务器消息传递解决方案。Pulsar 最初由雅虎开…

电子电气架构 --- 如何有助于提安全性并减少事故

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

rest_framework学习之认证 权限

权限 DRF提供如下几种常见权限: IsAuthenticated, 认证通过 IsAdminUser, 管理员权限 IsAuthenticatedOrReadOnly, 登录用户增删改 非登录用户只能查询 AllowAny,无需认证(默认) 在rest_framework的APIView基础类中&#xf…

【使用switch结构输出季节】2021-11-23

缘由用switch语句设计程序一年有12个月-编程语言-CSDN问答 void 使用switch结构输出季节(int y) {//缘由https://ask.csdn.net/questions/7577096?spm1005.2025.3001.5141std::cout << y << "\t";switch (y){case 3: case 4: case 5:std::cout <<…

主备Smart Link + Monitor Link组网技术详细配置

1.实验拓扑 2.使用设备 eNSP模拟建议下行设备三台使用S3700模拟&#xff08;全部使用S5700可能会出现流量丢失等异常问题。&#xff09; 3.实验配置 [SW1]dis cu # sysname SW1 # vlan batch 100 110 # interface Ethernet0/0/1port link-type accessport default vlan 100 …