当前位置: 首页 > news >正文

Python-基于PyQt5,Pillow,pathilb,imageio,moviepy,sys的GIF(动图)制作工具

前言:在抖音,快手等社交平台上,我们常常见到各种各样的GIF动画。在各大评论区里面,GIF图片以其短小精悍、生动有趣的特点,被广泛用于分享各种有趣的场景、搞笑的瞬间、精彩的动作等,能够快速吸引我们的注意力,增强内容的传播性和互动性。生活中,我们将各种有趣的人物表情、动作、台词等制作成GIF表情包,既可以更生动地表达我们此时的情感和态度,也让聊天的过程拥有了更多的趣味性和幽默感。当然,GIF动画不止在娱乐领域里应用广泛,在计算机的网页设计中很多时候也会使用GIF动画可以为页面增添动态效果,使页面更加生动活泼,吸引用户的注意力。例如,可以在网页的标题、导航栏、按钮等元素中添加GIF动画,提升页面的视觉效果和用户体验等。总而言之,GIF动画在我们的日常生活中扮演着重要的角色,我们有必要了解GIF动画的制作方法及相关制作工具。话不多说,我们今天就来学习一下如何利用Python来制作一款GIF动画工具。

编程思路:本次编程我们将会调用到Python中的众多库:包括诸如PyQt5,pillow,moviepy等的第三方库和sys,pathlib等的标准库。PyQt5被用于创建一个图形用户界面 (GUI) 应用程序(具体为代码中的GifMakerGUI类)。我们将创建窗口和布局(这里包括GUI窗口的大小,位置等),创建GUI中的相关组件(如按钮,标签,菜单等),处理事件和信号(主要负责将用户触发的事件与GUI控件联系起来),应用程序的启动和运行等。Pillow是Python中很重要的一个图片处理库,利用它我们可以对图片进行图像操作(包括图片的加载,操作,保存等),图像转换(包括图像颜色表示模式的转换(如RGB转HSV),以及图像尺寸大小的设置),图像序列处理(保存图像序列为GIF或其他格式),图像合成等操作。与pillow不同,moviepy是一个视频文件处理库(具体来说,它可以进行视频剪辑(打开,截取视频文件,也能进行音频处理(合成音频剪辑,视频音频合并,音频文件保存等))。imageio库比较简单,它主要被用于处理图像序列(简单来说就是将一系列图像保存为动画文件,如本次的GIF)。

第一步:导入库

标准库:sys,pathlib。

第三方库:PyQt5,pillow,imageio,moviepy。

#导入库
import sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
from moviepy import VideoFileClip, CompositeAudioClip
import imageio
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,QLabel, QLineEdit, QPushButton, QFileDialog,QProgressBar, QMessageBox)
from PyQt5.QtCore import QThread, pyqtSignal

第二步:创建功能类

两个:

GifCreator类,在后台线程中创建GIF或MP4文件(主要取决于用户是否选择音频文件(不选则生成gif动图,选则生成MP4文件))。

GifMakerGUI类,用于创建一个图形用户界面(GUI)应用程序,允许我们选择源文件(视频或图片)、添加音频文件、设置输出路径和参数,并启动GIF或MP4文件(同上)的生成过程。

#创建gif生成类
class GifCreator(QThread):progress_updated = pyqtSignal(int)finished = pyqtSignal(str)error_occurred = pyqtSignal(str)#初始化函数def __init__(self, config):super().__init__()self.config = configself.font = Noneif config['text']:try:self.font = ImageFont.truetype("arial.ttf", 24)except:self.font = ImageFont.load_default()def run(self):try:if self.config['audio_path'] and not self.config['output'].endswith('.mp4'):self.error_occurred.emit("音频仅支持MP4输出格式")returnif self.config['source_type'] == 'video':self._create_from_video()else:self._create_from_images()if self.config['audio_path']:self._add_audio()self.finished.emit(self.config['output'])except Exception as e:self.error_occurred.emit(str(e))def _process_frame(self, frame, index):img = Image.fromarray(frame).resize(self.config['size'])if self.config['text']:draw = ImageDraw.Draw(img)draw.text((10, 10), self.config['text'], font=self.font, fill=(255, 0, 0))self.progress_updated.emit(int((index + 1) / self.total_frames * 100))return imgdef _create_from_video(self):with VideoFileClip(str(self.config['sources'][0])) as clip:if self.config['duration']:clip = clip.subclip(0, self.config['duration'])self.total_frames = int(clip.duration * self.config['fps'])frames = []for i, frame in enumerate(clip.iter_frames(fps=self.config['fps'])):frames.append(self._process_frame(frame, i))frames[0].save(self.config['output'],save_all=True,append_images=frames[1:],optimize=True,duration=1000 // self.config['fps'],loop=0)def _create_from_images(self):images = []self.total_frames = len(self.config['sources'])for i, img_path in enumerate(self.config['sources']):with Image.open(img_path) as img:img = img.convert('RGBA').resize(self.config['size'])if self.config['text']:draw = ImageDraw.Draw(img)draw.text((10, 10), self.config['text'], font=self.font, fill=(255, 0, 0))images.append(img)self.progress_updated.emit(int((i + 1) / self.total_frames * 100))imageio.mimsave(self.config['output'],[img.convert('P', palette=Image.ADAPTIVE) for img in images],fps=self.config['fps'],palettesize=256)def _add_audio(self):video_clip = VideoFileClip(self.config['output'])audio_clip = CompositeAudioClip([VideoFileClip(self.config['audio_path']).audio])final_clip = video_clip.set_audio(audio_clip)final_clip.write_videofile(self.config['output'], codec='libx264')#创建主程窗口类
class GifMakerGUI(QWidget):#初始化函数def __init__(self):super().__init__()self.initUI()self.worker = None#初始化用户界面def initUI(self):self.setWindowTitle('GIF制作工具(初级)')self.setGeometry(300, 300, 600, 400)layout = QVBoxLayout()# 源文件选择self.source_btn = QPushButton('选择源文件(视频/图片)')self.source_btn.clicked.connect(self.select_source)self.source_label = QLabel('未选择文件')layout.addWidget(self.source_btn)layout.addWidget(self.source_label)# 音频文件选择self.audio_btn = QPushButton('添加音频文件')self.audio_btn.clicked.connect(self.select_audio)self.audio_label = QLabel('未选择音频文件')layout.addWidget(self.audio_btn)layout.addWidget(self.audio_label)# 输出设置output_layout = QHBoxLayout()self.output_btn = QPushButton('选择输出路径')self.output_btn.clicked.connect(self.select_output)self.output_entry = QLineEdit()output_layout.addWidget(self.output_btn)output_layout.addWidget(self.output_entry)layout.addLayout(output_layout)# 参数设置params_layout = QHBoxLayout()self.fps_entry = QLineEdit('10')self.size_w_entry = QLineEdit('640')self.size_h_entry = QLineEdit('480')self.duration_entry = QLineEdit()self.text_entry = QLineEdit()params_layout.addWidget(QLabel('FPS:'))params_layout.addWidget(self.fps_entry)params_layout.addWidget(QLabel('宽:'))params_layout.addWidget(self.size_w_entry)params_layout.addWidget(QLabel('高:'))params_layout.addWidget(self.size_h_entry)layout.addLayout(params_layout)text_layout = QHBoxLayout()text_layout.addWidget(QLabel('文字水印:'))text_layout.addWidget(self.text_entry)layout.addLayout(text_layout)# 进度条self.progress = QProgressBar()layout.addWidget(self.progress)# 操作按钮self.start_btn = QPushButton('开始生成')self.start_btn.clicked.connect(self.start_process)layout.addWidget(self.start_btn)self.setLayout(layout)def select_source(self):files, _ = QFileDialog.getOpenFileNames(self, '选择需处理的文件', '','视频文件 (*.mp4 *.mov *.avi);;图片文件 (*.png *.jpg *.jpeg)')if files:self.source_label.setText(f'已选择 {len(files)} 个文件')self.source_files = [Path(f) for f in files]def select_audio(self):file, _ = QFileDialog.getOpenFileName(self, '选择音频文件', '', '音频文件 (*.mp3 *.wav)')if file:self.audio_label.setText(Path(file).name)self.audio_file = filedef select_output(self):file, _ = QFileDialog.getSaveFileName(self, '保存输出文件', '','GIF文件 (*.gif);;MP4文件 (*.mp4)')if file:self.output_entry.setText(file)def validate_input(self):required = [(self.source_files, '请选择源文件'),(self.output_entry.text(), '请设置输出路径'),(self.fps_entry.text().isdigit(), 'FPS必须为数字'),(self.size_w_entry.text().isdigit(), '宽度必须为数字'),(self.size_h_entry.text().isdigit(), '高度必须为数字')]for condition, message in required:if not condition:QMessageBox.warning(self, '输入错误', message)return Falsereturn Truedef start_process(self):if not self.validate_input():returnconfig = {'sources': self.source_files,'output': self.output_entry.text(),'fps': int(self.fps_entry.text()),'size': (int(self.size_w_entry.text()), int(self.size_h_entry.text())),'duration': None,  # 可添加持续时间设置'text': self.text_entry.text(),'audio_path': getattr(self, 'audio_file', None),'source_type': 'video' if self.source_files[0].suffix.lower() in ['.mp4', '.mov', '.avi'] else 'image'}self.worker = GifCreator(config)self.worker.progress_updated.connect(self.update_progress)self.worker.finished.connect(self.process_finished)self.worker.error_occurred.connect(self.show_error)self.start_btn.setEnabled(False)self.worker.start()def update_progress(self, value):self.progress.setValue(value)def process_finished(self, output_path):self.start_btn.setEnabled(True)QMessageBox.information(self, '完成', f'文件已生成:{output_path}')def show_error(self, message):self.start_btn.setEnabled(True)QMessageBox.critical(self, '错误', message)

第三步:创建驱动单元

我们需要创建一个单独的单元来驱动整个程序的正常运行,这就是驱动单元。

#驱动程序单元
if __name__ == '__main__':app = QApplication(sys.argv)ex = GifMakerGUI()ex.show()sys.exit(app.exec_())

第四步:完整代码展示

#导入库
import sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
from moviepy import VideoFileClip, CompositeAudioClip
import imageio
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,QLabel, QLineEdit, QPushButton, QFileDialog,QProgressBar, QMessageBox)
from PyQt5.QtCore import QThread, pyqtSignal#创建gif生成类
class GifCreator(QThread):progress_updated = pyqtSignal(int)finished = pyqtSignal(str)error_occurred = pyqtSignal(str)#初始化函数def __init__(self, config):super().__init__()self.config = configself.font = Noneif config['text']:try:self.font = ImageFont.truetype("arial.ttf", 24)except:self.font = ImageFont.load_default()def run(self):try:if self.config['audio_path'] and not self.config['output'].endswith('.mp4'):self.error_occurred.emit("音频仅支持MP4输出格式")returnif self.config['source_type'] == 'video':self._create_from_video()else:self._create_from_images()if self.config['audio_path']:self._add_audio()self.finished.emit(self.config['output'])except Exception as e:self.error_occurred.emit(str(e))def _process_frame(self, frame, index):img = Image.fromarray(frame).resize(self.config['size'])if self.config['text']:draw = ImageDraw.Draw(img)draw.text((10, 10), self.config['text'], font=self.font, fill=(255, 0, 0))self.progress_updated.emit(int((index + 1) / self.total_frames * 100))return imgdef _create_from_video(self):with VideoFileClip(str(self.config['sources'][0])) as clip:if self.config['duration']:clip = clip.subclip(0, self.config['duration'])self.total_frames = int(clip.duration * self.config['fps'])frames = []for i, frame in enumerate(clip.iter_frames(fps=self.config['fps'])):frames.append(self._process_frame(frame, i))frames[0].save(self.config['output'],save_all=True,append_images=frames[1:],optimize=True,duration=1000 // self.config['fps'],loop=0)def _create_from_images(self):images = []self.total_frames = len(self.config['sources'])for i, img_path in enumerate(self.config['sources']):with Image.open(img_path) as img:img = img.convert('RGBA').resize(self.config['size'])if self.config['text']:draw = ImageDraw.Draw(img)draw.text((10, 10), self.config['text'], font=self.font, fill=(255, 0, 0))images.append(img)self.progress_updated.emit(int((i + 1) / self.total_frames * 100))imageio.mimsave(self.config['output'],[img.convert('P', palette=Image.ADAPTIVE) for img in images],fps=self.config['fps'],palettesize=256)def _add_audio(self):video_clip = VideoFileClip(self.config['output'])audio_clip = CompositeAudioClip([VideoFileClip(self.config['audio_path']).audio])final_clip = video_clip.set_audio(audio_clip)final_clip.write_videofile(self.config['output'], codec='libx264')#创建主程窗口类
class GifMakerGUI(QWidget):#初始化函数def __init__(self):super().__init__()self.initUI()self.worker = None#初始化用户界面def initUI(self):self.setWindowTitle('GIF制作工具(初级)')self.setGeometry(300, 300, 600, 400)layout = QVBoxLayout()# 源文件选择self.source_btn = QPushButton('选择源文件(视频/图片)')self.source_btn.clicked.connect(self.select_source)self.source_label = QLabel('未选择文件')layout.addWidget(self.source_btn)layout.addWidget(self.source_label)# 音频文件选择self.audio_btn = QPushButton('添加音频文件')self.audio_btn.clicked.connect(self.select_audio)self.audio_label = QLabel('未选择音频文件')layout.addWidget(self.audio_btn)layout.addWidget(self.audio_label)# 输出设置output_layout = QHBoxLayout()self.output_btn = QPushButton('选择输出路径')self.output_btn.clicked.connect(self.select_output)self.output_entry = QLineEdit()output_layout.addWidget(self.output_btn)output_layout.addWidget(self.output_entry)layout.addLayout(output_layout)# 参数设置params_layout = QHBoxLayout()self.fps_entry = QLineEdit('10')self.size_w_entry = QLineEdit('640')self.size_h_entry = QLineEdit('480')self.duration_entry = QLineEdit()self.text_entry = QLineEdit()params_layout.addWidget(QLabel('FPS:'))params_layout.addWidget(self.fps_entry)params_layout.addWidget(QLabel('宽:'))params_layout.addWidget(self.size_w_entry)params_layout.addWidget(QLabel('高:'))params_layout.addWidget(self.size_h_entry)layout.addLayout(params_layout)text_layout = QHBoxLayout()text_layout.addWidget(QLabel('文字水印:'))text_layout.addWidget(self.text_entry)layout.addLayout(text_layout)# 进度条self.progress = QProgressBar()layout.addWidget(self.progress)# 操作按钮self.start_btn = QPushButton('开始生成')self.start_btn.clicked.connect(self.start_process)layout.addWidget(self.start_btn)self.setLayout(layout)def select_source(self):files, _ = QFileDialog.getOpenFileNames(self, '选择需处理的文件', '','视频文件 (*.mp4 *.mov *.avi);;图片文件 (*.png *.jpg *.jpeg)')if files:self.source_label.setText(f'已选择 {len(files)} 个文件')self.source_files = [Path(f) for f in files]def select_audio(self):file, _ = QFileDialog.getOpenFileName(self, '选择音频文件', '', '音频文件 (*.mp3 *.wav)')if file:self.audio_label.setText(Path(file).name)self.audio_file = filedef select_output(self):file, _ = QFileDialog.getSaveFileName(self, '保存输出文件', '','GIF文件 (*.gif);;MP4文件 (*.mp4)')if file:self.output_entry.setText(file)def validate_input(self):required = [(self.source_files, '请选择源文件'),(self.output_entry.text(), '请设置输出路径'),(self.fps_entry.text().isdigit(), 'FPS必须为数字'),(self.size_w_entry.text().isdigit(), '宽度必须为数字'),(self.size_h_entry.text().isdigit(), '高度必须为数字')]for condition, message in required:if not condition:QMessageBox.warning(self, '输入错误', message)return Falsereturn Truedef start_process(self):if not self.validate_input():returnconfig = {'sources': self.source_files,'output': self.output_entry.text(),'fps': int(self.fps_entry.text()),'size': (int(self.size_w_entry.text()), int(self.size_h_entry.text())),'duration': None,  # 可添加持续时间设置'text': self.text_entry.text(),'audio_path': getattr(self, 'audio_file', None),'source_type': 'video' if self.source_files[0].suffix.lower() in ['.mp4', '.mov', '.avi'] else 'image'}self.worker = GifCreator(config)self.worker.progress_updated.connect(self.update_progress)self.worker.finished.connect(self.process_finished)self.worker.error_occurred.connect(self.show_error)self.start_btn.setEnabled(False)self.worker.start()def update_progress(self, value):self.progress.setValue(value)def process_finished(self, output_path):self.start_btn.setEnabled(True)QMessageBox.information(self, '完成', f'文件已生成:{output_path}')def show_error(self, message):self.start_btn.setEnabled(True)QMessageBox.critical(self, '错误', message)#驱动程序单元
if __name__ == '__main__':app = QApplication(sys.argv)ex = GifMakerGUI()ex.show()sys.exit(app.exec_())

第五步:运行效果展示

第六步:操作指南

运行程序,待程序初始化完成弹出窗口后,点击"选择源文件(视频/图片)",在系统中选择你想要处理的视频/图片("添加音频文件"这一步可以省略,因为本次我们学习的是GIF制作,不需要这一步)。接着我们点击"选择输出路径",选择最终生成的GIF存放的位置。接着自行设置FPS(帧数),GIF高,宽以及"文字水印"的内容等。最后点击"开始生成"按钮,窗口会出现进对条提示处理进度,当进度条满100%后,需再等待一段时间(此时程序正在将处理好的文件存放在指定位置)。当窗口弹出小窗口提示"文件已生成",点击小窗口中的"OK"按钮。返回你设置的"选择输出路径"的存放路径,就可以看到生成的GIF动画。

第七步:注意事项

- FPS值越大,文件处理时间越长,请谨慎设置;GIF的高,宽同理。(已设置默认值)

- 水印暂不支持中文字体,后面会改进。

- 水印颜色默认为红色,位置为视频/图片左上方。

- 文件的大小同样会影响程序的处理时间。

后面我会对以上问题进行优化/处理,并添加更多新奇功能,敬请期待!

相关文章:

Python-基于PyQt5,Pillow,pathilb,imageio,moviepy,sys的GIF(动图)制作工具

前言:在抖音,快手等社交平台上,我们常常见到各种各样的GIF动画。在各大评论区里面,GIF图片以其短小精悍、生动有趣的特点,被广泛用于分享各种有趣的场景、搞笑的瞬间、精彩的动作等,能够快速吸引我们的注意…...

c++ stl 遍历算法和查找算法

概述&#xff1a; 算法主要由头文件<algorithm> <functional> <numeric> 提供 <algorithm> 是所有 STL 头文件中最大的一个&#xff0c;提供了超过 90 个支持各种各样算法的函数&#xff0c;包括排序、合并、搜索、去重、分解、遍历、数值交换、拷贝和…...

Java学习进阶路线

Java基础 Java Web 前端HTML/css/js&#xff0c;J2EE&#xff08;Servlet/jsp&#xff09;&#xff0c;数据库&#xff08;Mysql/oracle&#xff09; Java开发框架 Spring MVC/Mybatis/Herbernate/maven 《Java编程思想》 深入了解java基础 Java设计模式 《Effective j…...

探寻系统响应的奥秘:为何常用以 e 为底的指数组合表示

一、引言 在工程与科学领域的系统分析中&#xff0c;常常会发现系统响应多以 e e e 为底的指数组合来表示。从电路系统里的电流电压变化&#xff0c;到机械系统的振动情况&#xff0c;再到控制系统的动态特性&#xff0c;这种表示方法无处不在。那么&#xff0c;究竟是什么原…...

java 进阶教程_Java进阶教程 第2版

第2版前言 第1版前言 语言基础篇 第1章 Java语言概述 1.1 Java语言简介 1.1.1 Java语言的发展历程 1.1.2 Java的版本历史 1.1.3 Java语言与C&#xff0f;C 1.1.4 Java的特点 1.2 JDK和Java开发环境及工作原理 1.2.1 JDK 1.2.2 Java开发环境 1.2.3 Java工作原理 1.…...

Ext文件系统

文件内容属性 被打开的文件在内存中&#xff0c;没有被打开的文件在磁盘里文件系统的工作就是根据路径帮我们找到在磁盘上的文件 磁盘&#xff08;硬件&#xff09; 磁盘的存储结构 磁头在传动臂的运动下共同进退&#xff0c;向磁盘写入的时候是向柱面批量写入的 OS文件系统访…...

C++滑动窗口技术深度解析:核心原理、高效实现与高阶应用实践

目录 一、滑动窗口的核心原理 二、滑动窗口的两种类型 1. 固定大小的窗口 2. 可变大小的窗口 三、实现细节与关键点 1. 窗口的初始化 2. 窗口的移动策略 3. 结果的更新时机 四、经典问题与代码示例 示例 1&#xff1a;和 ≥ target 的最短子数组&#xff08;可变窗口…...

【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(四) -> 常见组件(一)

目录 1 -> List 1.1 -> 创建List组件 1.2 -> 添加滚动条 1.3 -> 添加侧边索引栏 1.4 -> 实现列表折叠和展开 1.5 -> 场景示例 2 -> dialog 2.1 -> 创建Dialog组件 2.2 -> 设置弹窗响应 2.3 -> 场景示例 3 -> form 3.1 -> 创建…...

【加餐】使⽤指针实现链表

【加餐】使⽤指针实现链表 ​ 面向过程方式和面向对象方式&#xff08;把面向过程的封装一下就行了&#xff09;是两种不同的编程方法论...

用 Python 绘制爱心形状的简单教程

1. 引言 在本教程中&#xff0c;我们将学习如何使用 Python 和 Matplotlib 库来绘制一个简单的爱心形状。这是一个有趣且简单的项目&#xff0c;适合初学者练习图形绘制和数据可视化。 2. 环境准备 首先&#xff0c;确保您的系统上安装了 Python 和 Matplotlib 库。如果还未…...

DeepSeek安装

安装运行环境 https://ollama.com/ 安装验证 cmd指令 ollama -v 安装运行模型 https://ollama.com/library/deepseek-r1:14b-qwen-distill-q4_K_M 例如&#xff1a; ollama run deepseek-r1:1.5b-qwen-distill-q4_K_M 结果 再次使用时&#xff0c;直接cmd运行上一步的ru…...

Git--使用教程

Git的框架讲解 Git 是一个分布式版本控制系统&#xff0c;其架构设计旨在高效地管理代码版本&#xff0c;支持分布式协作&#xff0c;并确保数据的完整性和安全性。 Git 的核心组件&#xff1a; 工作区&#xff08;Working Directory&#xff09;&#xff1a; - 作区是你在本…...

【HTML性能优化】提升网站加载速度:GZIP、懒加载与资源合并

系列文章目录 01-从零开始学 HTML&#xff1a;构建网页的基本框架与技巧 02-HTML常见文本标签解析&#xff1a;从基础到进阶的全面指南 03-HTML从入门到精通&#xff1a;链接与图像标签全解析 04-HTML 列表标签全解析&#xff1a;无序与有序列表的深度应用 05-HTML表格标签全面…...

C#从XmlDocument提取完整字符串

方法1&#xff1a;通过XmlDocument的OuterXml属性&#xff0c;见XmlDocument类 该方法获得的xml字符串是不带格式的&#xff0c;可读性差 方法2&#xff1a;利用XmlWriterSettings控制格式等一系列参数&#xff0c;见XmlWriterSettings类 例子&#xff1a; using System.IO; …...

wordpress每隔24小时 随机推荐一个指定分类下的置顶内容。

在WordPress中实现每隔24小时随机推荐一个指定分类下的置顶内容&#xff0c;可以通过以下步骤实现&#xff1a; 1. 创建自定义函数 在主题的functions.php文件中添加以下代码&#xff0c;用于创建一个定时任务&#xff0c;每隔24小时随机选择一个置顶文章并存储到选项中&…...

《chatwise:DeepSeek的界面部署》

ChatWise&#xff1a;DeepSeek的界面部署 摘要 本文详细描述了DeepSeek公司针对其核心业务系统进行的界面部署工作。从需求分析到技术实现&#xff0c;再到测试与优化&#xff0c;全面阐述了整个部署过程中的关键步骤和解决方案。通过本文&#xff0c;读者可以深入了解DeepSee…...

HTTP请求响应周期步骤

一个典型的 HTTP 请求/响应周期 从建立连接开始,经过客户端向服务器发送请求、服务器处理请求并返回响应,最终关闭连接。这个过程可以分为多个阶段,以下是详细的步骤: 一、建立连接(TCP连接) 客户端发起连接请求:在HTTP通信中,客户端通常是浏览器,首先通过 DNS 查询…...

synchronized, volatile 在 DCL 的作用

背景 最近在看设计模式&#xff0c;在单例模式的 Double Check Lock&#xff08;DCL&#xff09;中&#xff0c;存在两个关键字&#xff1a;volatile & synchronized。 之前都知道 DCL 怎么写&#xff0c;直接套娃。但是这两关键字在单例里面的作用还没深究过&#xff0c…...

Java进阶笔记(中级)

-----接Java进阶笔记&#xff08;初级&#xff09;----- 目录 集合多线程 集合 ArrayList 可以通过List来接收ArrayList对象&#xff08;因为ArrayList实现了List接口&#xff09; 方法&#xff1a;接口名 柄名 new 实现了接口的类(); PS: List list new ArrayList();遍历…...

人生总有终点,不必好高骛远

夕阳西下&#xff0c;我漫步在河堤上。河水缓缓流淌&#xff0c;倒映着天边最后一抹晚霞。岸边垂柳依依&#xff0c;枝条轻拂水面&#xff0c;荡起一圈圈涟漪。这涟漪由近及远&#xff0c;渐渐消散在暮色中&#xff0c;如同我们每个人在时间长河中泛起的微澜。 记得年少时&…...

C#中堆和栈的区别

C#中的堆&#xff08;Heap&#xff09;和栈&#xff08;Stack&#xff09;详解 基本概念 栈&#xff08;Stack&#xff09; 栈是一个后进先出&#xff08;LIFO&#xff09;的内存结构由系统自动分配和释放存储空间连续&#xff0c;大小固定主要用于存储值类型和对象引用 堆…...

如何利用i18n实现国际化

1.首先新建i18.js文件 // i18n配置 import { createI18n } from vue-i18n // import ElementPlus from element-plus import zhCn from element-plus/es/locale/lang/zh-cn import zh from ./zh-cn import en from ./en import ru from ./ru const messages {en_US: {...en,//…...

SpringMVC响应

第一章&#xff1a;数据处理及跳转 1. 结果跳转方式 ①.ModelAndView 设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 . <bean id"templateResolver" class"org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolv…...

深入理解特征值与稳定性密码:以弹簧 - 质量 - 阻尼典型二阶系统为例

从看特征值决定稳定性的原因 摘要 本文以弹簧 - 质量 - 阻尼系统这一典型二阶系统为研究对象&#xff0c;深入剖析特征值决定系统稳定性的内在原因。通过详细的数学推导和直观的物理意义阐释&#xff0c;全面揭示了特征值与系统稳定性之间的紧密关联&#xff0c;为理解和分析…...

python pandas 读取合并单元格并保留合并信息

读取合并单元格并保留合并信息 当我们只是使用 pandas 的 read_excel 方法读取 Excel 文件时&#xff0c;我们可能会遇到一个很棘手的问题&#xff1a;合并单元格的信息将会丢失&#xff0c;从而导致我们的数据出现重复或缺失的情况。 在本篇文章中将介绍使用 pandas 正确地读…...

Go-Gin Web 框架完整教程

1. 环境准备 1.1 Go 环境安装 Go 语言&#xff08;或称 Golang&#xff09;是一个开源的编程语言&#xff0c;由 Google 开发。在开始使用 Gin 框架之前&#xff0c;我们需要先安装 Go 环境。 安装步骤&#xff1a; 访问 Go 官网下载页面&#xff1a;https://golang.org/dl…...

机器学习专业毕设选题推荐合集 人工智能

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…...

Java程序员 面试如何介绍项目经验?

项目经历是面试过程中重点问的&#xff0c;但是很多人在回答的时候往往会有问题&#xff1a; 重点是介绍项目&#xff0c;而忽略了个人的经历。 经历是你做了什么、你怎么做的、做完后的结果。例如&#xff1a;项目中的哪些部分是你做的&#xff1f;你是不是核心人员&#xf…...

YONBIP后端环境搭建-IDEA

1、IDEA环境搭建 1.1、插件安装 打开设置窗口&#xff0c;添加自定义插件存储库路径。 https://nccdev.yonyou.com/ide/idea/latest/updatePlugin.xml 在 Marketplace 中搜索 YonBuilder Premium开发者工具 &#xff0c;点击安装。 1.2、Home配置 点击Home配置按钮&#xf…...

Java 微服务实用指南(一)

Java 微服务&#xff1a;基础 要真正理解 Java 微服务&#xff0c;就必须从最基本的东西开始&#xff1a;为人诟病的 Java 大型单体应用是什么&#xff0c;它的优点和缺点是什么。 什么是 Java 大型单体应用&#xff1f; 假设你正在为一家银行或一家金融科技初创公司工作。你为…...

Windows图形界面(GUI)-QT-C/C++ - QT Frame

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 一、概述 二、使用场景 1. 分隔内容区域 2. 装饰性边框 3. 自定义控件容器 三、常见样式 1. 框架形状&#xff08;Shape&#xff09; 2. 框架阴影&#xff08;Shadow&#xff09;…...

优选算法合集————双指针(专题二)

好久都没给大家带来算法专题啦&#xff0c;今天给大家带来滑动窗口专题的训练 题目一&#xff1a;长度最小的子数组 题目描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, …...

WebSocket协议里客户端发送给服务器的数据会用4字节的掩码循环异或的分析

首先&#xff0c;我需要回顾WebSocket协议中对掩码处理的具体要求。根据RFC 6455&#xff0c;客户端发送到服务器的帧必须使用掩码&#xff0c;而服务器发送的帧不需要掩码。掩码是4字节的&#xff0c;应用于有效载荷数据&#xff0c;每个字节依次与掩码的对应字节异或&#xf…...

【字节青训营-9】:初探字节微服务框架 Hertz 基础使用及进阶(下)

本文目录 一、Hertz中间件Recovery二、Hertz中间件跨资源共享三、Hertz 响应四、Hertz请求五、Hertz中间件Session 一、Hertz中间件Recovery Recovery中间件是Hertz框架预置的中间件&#xff0c;使用server.Default()可以默认注册该中间件&#xff0c;为Hertz框架提供panic回复…...

新版AndroidStudio 修改 jdk版本

一、问题 之前&#xff0c;在安卓项目中配置JDK和Gradle的过程非常直观&#xff0c;只需要进入Android Studio的File菜单中的Project Structure即可进行设置&#xff0c;十分方便。 如下图可以在这修改JDK: 但是升级AndroidStudio之后&#xff0c;比如我升级到了Android Stu…...

cocos spine执行动画报错Cannot read properties of null (reading ‘data‘)

cocos v3.8.3 当想this.spine.setAnimation(0, "action1", false);播放spine动画时报错↓ 解决方法一&#xff1a; 在setAnimation之前调用this.spine.__preload() 解决方法二&#xff1a; 不要让spine或其父节点通过active显隐...

笔记:新能源汽车零部件功率级测试怎么进行?

摘要:本文旨在梳理主机厂对新能源汽车核心零部件功率级测试需求,通过试验室的主流设备仪器集成,快速实现试验方案搭建,并体现测试测量方案的时效性、便捷性优势。目标是通过提升实现设备的有效集成能力、实现多设备测试过程的有效协同、流程化测试,可快速采集、分析当前数…...

【starrocks学习】之将starrocks表同步到hive

目录 方法 1&#xff1a;通过HDFS导出数据 1. 将StarRocks表数据导出到HDFS 2. 在Hive中创建外部表 3. 验证数据 方法 2&#xff1a;使用Apache Spark同步 1. 添加StarRocks和Hive的依赖 2. 使用Spark读取StarRocks数据并写入Hive 3. 验证数据 方法 3&#xff1a;通过…...

Linux提权--SUDO提权

​sudo​ 是 Linux 中常用的特权管理工具&#xff0c;允许普通用户以其他用户&#xff08;通常是 root 用户&#xff09;的身份运行命令。如果配置不当&#xff0c;攻击者可能通过滥用 sudo​ 权限来提升自己的权限。 一.常见的 sudo 提权方法&#xff1a; 误配置的 sudo 权限&…...

【AIGC提示词系统】基于 DeepSeek R1 + Claude 的新年运势占卜系统设计与实现

提示词在最下方 DeepSeek R1调试了整体的提示词&#xff0c;使用Claude进行渲染 引言 在人工智能与传统文化交融的今天&#xff0c;如何让 AI 充分理解并传递东方玄学文化的精髓&#xff0c;成为一个极具挑战性的课题。本文将详细介绍一个基于 Claude 的新年运势占卜系统的设计…...

11. Global Object 全局对象的使用

Global Object 全局对象 1 引言2 制作全局对象3 调用全局对象4 扩展使用1 引言 全局对象适用于大量重复的对象,比如阀门,电机等,如果这些设备的基本逻辑与状态都是一样的,那么就可以使用全局对象的方法来做HMI,省时省力。并且在后期修改的时候只需要修改全局对象即可。 …...

Java synchronized锁升级

偏向锁、轻量级锁和重量级锁是Java中synchronized关键字的三种锁状态&#xff0c;用于优化多线程环境下的性能。以下是它们的简要说明&#xff1a; 1. 偏向锁&#xff08;Biased Locking&#xff09; 目的&#xff1a;减少无竞争时的锁开销。适用场景&#xff1a;只有一个线程…...

【Hadoop】Hadoop的HDFS

这里写目录标题 HDFS概述HDFS产出背景及定义HDFS产生背景HDFS定义 HDFS优缺点HDFS优点HDFS缺点 HDFS组成架构HDFS文件块大小 HDFS的Shell操作常用命令实操准备工作上传下载HDFS直接操作 HDFS的API操作客户端环境准备HDFS的API案例实操HDFS文件上传HDFS文件下载HDFS文件更名和移…...

JAVA异步的TCP 通讯-客户端

一、客户端代码示例 import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.ExecutorService; impo…...

4.回归与聚类算法 4.1线性回归

4.1.1 线性回归的原理 1 线性回归应用场景&#xff1a; 房价预测 销售额度预测 金融&#xff1a;贷款额度预测&#xff0c;利用线性回归以及系数分析因子 2 什么是线性回归 1&#xff09; 定义&#xff1a;利用回归方程&#xff08;函数&#xff09;对一个或者多个自变量…...

联想拯救者开机进入bios

如果你的联想拯救者&#xff08;Lenovo Legion&#xff09;笔记本电脑开机后直接进入 BIOS 设置界面&#xff0c;可能是以下原因之一导致的。以下是解决方法&#xff1a; 1. 检查启动顺序 进入 BIOS 后&#xff0c;找到 Boot&#xff08;启动&#xff09;选项卡。检查启动顺序…...

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(四)

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;贪心算法篇–CSDN博客 文章目录 前言例题1.合并区间2.无重叠的区间3.用最少数量的箭引爆气球…...

Junit5使用教程(3)

第三部分&#xff1a;JUnit 5 进阶 3. 动态测试 一、动态测试是什么&#xff1f; 动态测试&#xff08;Dynamic Test&#xff09;允许在运行时生成测试用例&#xff0c;而不是在编译时通过 Test 静态定义。它通过 TestFactory 注解标记的方法动态生成一组测试用例&#xff0…...

WPS中解除工作表密码保护(忘记密码)

1.下载vba插件 项目首页 - WPS中如何启用宏附wps.vba.exe下载说明分享:WPS中如何启用宏&#xff1a;附wps.vba.exe下载说明本文将详细介绍如何在WPS中启用宏功能&#xff0c;并提供wps.vba.exe文件的下载说明 - GitCode 并按照步骤安装 2.wps中点击搜索&#xff0c;输入开发…...

通向AGI之路:人工通用智能的技术演进与人类未来

文章目录 引言:当机器开始思考一、AGI的本质定义与技术演进1.1 从专用到通用:智能形态的范式转移1.2 AGI发展路线图二、突破AGI的五大技术路径2.1 神经符号整合(Neuro-Symbolic AI)2.2 世界模型架构(World Models)2.3 具身认知理论(Embodied Cognition)三、AGI安全:价…...