使用python實現視頻播放器(支持拖動播放位置跳轉)

使用python實現視頻播放器(支持拖動播放位置跳轉)

Python實現視頻播放器,在我早期的博文中介紹或作為資料記錄過

Python實現視頻播放器 https://blog.csdn.net/cnds123/article/details/145926189

Python實現本地視頻/音頻播放器https://blog.csdn.net/cnds123/article/details/137874107

Python簡單GUI程序示例 中 “四、視頻播放器” https://blog.csdn.net/cnds123/article/details/122903311

但是,一直不盡人意。現在,再介紹一個。

這是一個基于 PyQt6 和 python-vlc 開發的視頻播放器,主要實現了我多次試圖實現未果的功能

——帶有播放進度條,不僅顯示播放進度,還支持拖動播放位置跳轉。

普通版視頻播放器

主要特點

? ? 播放畫面隨窗口縮放

??? 支持常見格式(MP4、AVI、MKV 等)

??? 通過文件對話框加載視頻文件

??? 播放/暫停、停止、播放進度跳轉、音量調節

??? 顯示視頻文件名、當前播放時間、總時長

播放進度條:顯示播放進度并支持拖動播放位置跳轉。

音量滑塊:音量滑塊調整音量大小。

需安裝以下 Python第三方庫:

python-vlc、 PyQt6

Windows中,還要安裝 VLC 播放器,其下載 地址 https://www.videolan.org/vlc/ 。否則,將報錯:缺少 libvlc.dll。

運行效果界面如下:

基本使用操作

??? 打開文件:點擊菜單欄 文件 > 打開文件(快捷鍵 Ctrl+O) 或底部 打開文件 按鈕

??? 播放/暫停:空格鍵 或 點擊 按鈕切換

??? 停止:停止 按鈕???

??? 進度跳轉:拖動進度條

??? 音量調節:拖動底部音量滑塊

源碼如下:

import sys
import time
import vlc
import os
from PyQt6 import QtWidgets, QtCore, QtGuiclass VLCPlayer(QtWidgets.QMainWindow):def __init__(self):super().__init__()self.instance = vlc.Instance()self.player = self.instance.media_player_new()self.timer = QtCore.QTimer(self)self.timer.timeout.connect(self.update_ui)self.current_file = Noneself.media_loaded = Falseself.init_ui()def init_ui(self):# 主窗口設置self.setWindowTitle("PyQt6 VLC Player")self.resize(800, 600)# 創建菜單欄menubar = self.menuBar()file_menu = menubar.addMenu("文件(&F)")# 添加"打開"動作open_action = QtGui.QAction("打開文件...", self)open_action.setShortcut("Ctrl+O")open_action.triggered.connect(self.open_file)file_menu.addAction(open_action)# 創建主容器和布局central_widget = QtWidgets.QWidget(self)self.setCentralWidget(central_widget)main_layout = QtWidgets.QVBoxLayout(central_widget)main_layout.setContentsMargins(0, 0, 0, 0)# 視頻標題標簽 self.title_label = QtWidgets.QLabel("當前未選擇媒體文件")self.title_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)self.title_label.setStyleSheet("font-size: 14px; color: #666; margin: 5px;")main_layout.addWidget(self.title_label)# 視頻顯示區域self.video_widget = QtWidgets.QWidget()self.video_widget.setStyleSheet("background-color: black;")main_layout.addWidget(self.video_widget, stretch=1)# 控制面板control_panel = QtWidgets.QWidget()control_layout = QtWidgets.QVBoxLayout(control_panel)# 進度條self.progress_bar = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)self.progress_bar.setMinimum(0)self.progress_bar.sliderMoved.connect(self.set_position)control_layout.addWidget(self.progress_bar)# 時間標簽self.time_label = QtWidgets.QLabel("00:00:00 / 00:00:00")control_layout.addWidget(self.time_label)# 控制按鈕button_layout = QtWidgets.QHBoxLayout()self.play_btn = QtWidgets.QPushButton("播放")self.play_btn.clicked.connect(self.toggle_play)self.stop_btn = QtWidgets.QPushButton("停止")self.stop_btn.clicked.connect(self.stop)self.open_btn = QtWidgets.QPushButton("打開文件")self.open_btn.clicked.connect(self.open_file)# 音量控制self.volume_slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(100)self.volume_slider.valueChanged.connect(self.set_volume)# 添加控件button_layout.addWidget(self.open_btn)button_layout.addWidget(self.play_btn)button_layout.addWidget(self.stop_btn)button_layout.addWidget(QtWidgets.QLabel("音量:"))button_layout.addWidget(self.volume_slider)control_layout.addLayout(button_layout)main_layout.addWidget(control_panel)# 設置VLC渲染if sys.platform == "win32":self.player.set_hwnd(int(self.video_widget.winId()))elif sys.platform == "linux":self.player.set_xwindow(self.video_widget.winId())elif sys.platform == "darwin":from PyQt6.QtGui import QCocoaNativeContextself.player.set_nsobject(int(QCocoaNativeContext(self.video_widget.winId()).nsview()))self.timer.start(200)def open_file(self):# 修復1:打開文件前先停止播放if self.player.is_playing():self.player.stop()self.media_loaded = Falseself.play_btn.setText("播放")file_dialog = QtWidgets.QFileDialog(self)file_dialog.setNameFilter("視頻文件 (*.mp4 *.avi *.mkv *.mov *.flv)")if file_dialog.exec():selected_files = file_dialog.selectedFiles()if selected_files:self.load_media(selected_files[0])def load_media(self, file_path):try:# 新增:停止定時器避免沖突self.timer.stop()# 修復2:確保徹底釋放舊媒體資源self.player.stop()self.player.set_media(None)  # 清除舊媒體引用# 重置狀態self.media_loaded = Falseself.play_btn.setText("播放")self.progress_bar.setValue(0)self.time_label.setText("00:00:00 / 00:00:00")# 加載新文件self.current_file = file_pathmedia = self.instance.media_new(file_path)self.player.set_media(media)# 更新標題file_name = os.path.basename(file_path)self.title_label.setText(f"當前播放: {file_name}")# 修復3:異步解析媒體信息(避免阻塞UI)media.parse_with_options(vlc.MediaParseFlag.network, 1000)# 設置進度條最大值self.progress_bar.setMaximum(media.get_duration())self.media_loaded = True# 顯示總時長total_time = time.strftime("%H:%M:%S", time.gmtime(media.get_duration() // 1000))self.time_label.setText(f"00:00:00 / {total_time}")# 新增:確保媒體加載完成后再啟定時器self.timer.start(200)except Exception as e:QtWidgets.QMessageBox.critical(self, "錯誤", f"無法加載文件:\n{str(e)}")self.media_loaded = Falseself.title_label.setText("媒體加載失敗")def toggle_play(self):if not self.media_loaded:self.open_file()return# 修復4:強制同步按鈕狀態if self.player.is_playing():self.player.pause()self.play_btn.setText("播放")else:self.player.play()self.play_btn.setText("暫停")def stop(self):self.player.stop()self.progress_bar.setValue(0)self.time_label.setText("00:00:00 / 00:00:00")self.play_btn.setText("播放")self.title_label.setText("播放已停止")def set_volume(self, value):self.player.audio_set_volume(value)def set_position(self, value):if self.player.is_seekable():self.player.set_position(value / self.progress_bar.maximum())def update_ui(self):if not self.media_loaded:return  # 新增:防止在無媒體時更新media_length = self.player.get_length()if media_length > 0:current_time = self.player.get_time()# 新增:檢測播放結束if current_time >= media_length - 500:  # 留50ms容差self.stop()returnself.progress_bar.setMaximum(media_length)self.progress_bar.setValue(current_time)total_time = time.strftime("%H:%M:%S", time.gmtime(media_length // 1000))current_time_str = time.strftime("%H:%M:%S", time.gmtime(current_time // 1000))self.time_label.setText(f"{current_time_str} / {total_time}")def closeEvent(self, event):self.player.stop()event.accept()if __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)player = VLCPlayer()player.show()sys.exit(app.exec())

專用版視頻播放控制器(視頻播控器)

主要添加視頻“加密”、“解密”功能

運行條件除了和上面的一樣外,還需安裝第三方庫pycryptodome

pycryptodome 是一個強大的加密庫,用于實現加密算法。

界面如下:

1)加密視頻

菜單欄點擊 安全 → 加密視頻

優先當前播放文件路徑,即:

??? 如果當前正在播放視頻且未加密 → 彈出確認對話框

??? 否則 → 彈出文件選擇對話框

加密文件的后綴 .vef,放在原文將后綴之后。命名規則為:原文件名.vef(如 video.mp4 → video.mp4.vef)

2)解密視頻

菜單欄點擊 安全 → 解密視頻

文件過濾:僅顯示 .vef 文件

解密文件,出現保存對話框,默認放置在原位置,默認用文件名(可改),若存放處有同名文件,提示是否替換。

命名規則:自動去除 .vef 后綴 → video.mp4

解密時,若輸入的密碼和加密時不一致,提示:密碼錯誤或文件損壞 → 立即終止并提示

源碼如下:

import sys
import time
import hashlib
import secrets
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import vlc
import os
from PyQt6 import QtWidgets, QtCore, QtGuiclass VLCPlayer(QtWidgets.QMainWindow):def __init__(self):super().__init__()self.instance = vlc.Instance()self.player = self.instance.media_player_new()self.timer = QtCore.QTimer(self)self.timer.timeout.connect(self.update_ui) # 連接定時器到更新方法self.current_file = Noneself.media_loaded = Falseself.temp_files = set() #用于跟蹤臨時解密文件self.init_ui()self.init_crypto()def init_ui(self):# 主窗口設置self.setWindowTitle("視頻播控器(特別專用版)")self.resize(800, 600)# 初始化菜單menubar = self.menuBar()file_menu = menubar.addMenu("文件(&F)")crypto_menu = menubar.addMenu("安全(&S)")# 文件操作open_action = self.create_action("打開文件...", "Ctrl+O", self.open_file)# exit_action = self.create_action("退出", "Ctrl+Q", lambda: self.close()) # 播放退出太慢# 加密解密操作encrypt_action = self.create_action("加密視頻...", "Ctrl+E", self.encrypt_video)decrypt_action = self.create_action("解密視頻...", "Ctrl+D", self.decrypt_video)# 添加菜單項file_menu.addAction(open_action)file_menu.addSeparator()# file_menu.addAction(exit_action)crypto_menu.addAction(encrypt_action)crypto_menu.addAction(decrypt_action)# 創建主容器和布局central_widget = QtWidgets.QWidget(self)self.setCentralWidget(central_widget)main_layout = QtWidgets.QVBoxLayout(central_widget)main_layout.setContentsMargins(0, 0, 0, 0)# 視頻標題標簽 self.title_label = QtWidgets.QLabel("當前未選擇媒體文件")self.title_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)self.title_label.setStyleSheet("font-size: 14px; color: #666; margin: 5px;")main_layout.addWidget(self.title_label)# 視頻顯示區域self.video_widget = QtWidgets.QWidget()self.video_widget.setStyleSheet("background-color: black;")main_layout.addWidget(self.video_widget, stretch=1)# 控制面板control_panel = QtWidgets.QWidget()control_layout = QtWidgets.QVBoxLayout(control_panel)# 進度條self.progress_bar = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)self.progress_bar.setMinimum(0)self.progress_bar.sliderMoved.connect(self.set_position)control_layout.addWidget(self.progress_bar)# 時間標簽self.time_label = QtWidgets.QLabel("00:00:00 / 00:00:00")control_layout.addWidget(self.time_label)# 控制按鈕button_layout = QtWidgets.QHBoxLayout()self.play_btn = QtWidgets.QPushButton("播放")self.play_btn.clicked.connect(self.toggle_play)self.stop_btn = QtWidgets.QPushButton("停止")self.stop_btn.clicked.connect(self.stop)self.open_btn = QtWidgets.QPushButton("打開文件")self.open_btn.clicked.connect(self.open_file)# 音量控制self.volume_slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(100)self.volume_slider.valueChanged.connect(self.set_volume)# 添加控件button_layout.addWidget(self.open_btn)button_layout.addWidget(self.play_btn)button_layout.addWidget(self.stop_btn)button_layout.addWidget(QtWidgets.QLabel("音量:"))button_layout.addWidget(self.volume_slider)control_layout.addLayout(button_layout)main_layout.addWidget(control_panel)# 設置VLC渲染if sys.platform == "win32":self.player.set_hwnd(int(self.video_widget.winId()))elif sys.platform == "linux":self.player.set_xwindow(self.video_widget.winId())elif sys.platform == "darwin":from PyQt6.QtGui import QCocoaNativeContextself.player.set_nsobject(int(QCocoaNativeContext(self.video_widget.winId()).nsview()))self.timer.start(200)def init_crypto(self):"""初始化加密參數"""self.key_derivation_iterations = 100000self.salt_size = 16self.nonce_size = 16self.tag_size = 16self.chunk_size = 64 * 1024  # 64KB塊處理def create_action(self, text, shortcut, callback):"""創建標準化菜單動作"""action = QtGui.QAction(text, self)action.setShortcut(shortcut)action.triggered.connect(callback)return action        def open_file(self):# 修復1:打開文件前先停止播放if self.player.is_playing():self.player.stop()self.media_loaded = Falseself.play_btn.setText("播放")file_dialog = QtWidgets.QFileDialog(self)file_dialog.setNameFilter("視頻文件 (*.mp4 *.avi *.mkv *.mov *.flv)")if file_dialog.exec():selected_files = file_dialog.selectedFiles()if selected_files:self.load_media(selected_files[0])def load_media(self, file_path):try:# 如果是加密文件需要特殊處理if file_path.lower().endswith('.vef'):QtWidgets.QMessageBox.warning(self, "警告", "請使用菜單中的解密功能打開加密文件")return# 新增:停止定時器避免沖突self.timer.stop()# 修復2:確保徹底釋放舊媒體資源self.player.stop()self.player.set_media(None)  # 清除舊媒體引用# 重置狀態self.media_loaded = Falseself.play_btn.setText("播放")self.progress_bar.setValue(0)self.time_label.setText("00:00:00 / 00:00:00")# 加載新文件self.current_file = file_pathmedia = self.instance.media_new(file_path)self.player.set_media(media)# 更新標題file_name = os.path.basename(file_path)self.title_label.setText(f"當前播放: {file_name}")# 修復3:異步解析媒體信息(避免阻塞UI)media.parse_with_options(vlc.MediaParseFlag.network, 1000)# 設置進度條最大值self.progress_bar.setMaximum(media.get_duration())self.media_loaded = True# 顯示總時長total_time = time.strftime("%H:%M:%S", time.gmtime(media.get_duration() // 1000))self.time_label.setText(f"00:00:00 / {total_time}")# 新增:確保媒體加載完成后再啟定時器self.timer.start(200)except Exception as e:QtWidgets.QMessageBox.critical(self, "錯誤", f"無法加載文件:\n{str(e)}")self.media_loaded = Falseself.title_label.setText("媒體加載失敗")def toggle_play(self):if not self.media_loaded:self.open_file()return# 修復4:強制同步按鈕狀態if self.player.is_playing():self.player.pause()self.play_btn.setText("播放")else:self.player.play()self.play_btn.setText("暫停")# ---------- 加密解密功能 ----------def _get_encrypted_filename(self, src_path):"""生成加密文件名(原文件名+.vef)"""return src_path + ".vef"  # 直接在原文件名后追加.vefdef encrypt_video(self):"""智能加密方法:優先處理當前播放文件"""# 自動檢測當前播放文件src_path = Noneif self.current_file and os.path.exists(self.current_file):# 檢查是否已經是加密文件if not self.current_file.lower().endswith('.vef'):reply = QtWidgets.QMessageBox.question(self, '加密確認', f"是否加密當前播放的文件?\n{os.path.basename(self.current_file)}",QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)if reply == QtWidgets.QMessageBox.StandardButton.Yes:src_path = self.current_file# 如果無當前可用文件,則選擇文件if not src_path:src_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "選擇要加密的視頻文件", "","視頻文件 (*.mp4 *.avi *.mkv *.mov *.flv)")if not src_path:return# 處理加密文件特殊情況if src_path.lower().endswith('.vef'):QtWidgets.QMessageBox.warning(self, "警告", "不能加密已加密文件")return# 獲取保存路徑default_name = self._get_encrypted_filename(os.path.basename(src_path))dest_path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "保存加密文件", os.path.join(os.path.dirname(src_path), default_name),  # 默認原目錄"加密視頻 (*.vef)")if not dest_path:return# 獲取密碼password, ok = QtWidgets.QInputDialog.getText(self, "輸入密碼", "設置加密密碼:", QtWidgets.QLineEdit.EchoMode.Password)if not ok or not password:return# 執行加密流程try:# 如果是當前播放文件,停止播放was_playing = Falseif src_path == self.current_file:was_playing = self.player.is_playing()self.player.stop()self.media_loaded = Falseself._encrypt_file(src_path, dest_path, password)# 成功提示msg = f"加密成功!\n原文件: {os.path.basename(src_path)}\n加密文件: {os.path.basename(dest_path)}"QtWidgets.QMessageBox.information(self, "完成", msg)# 如果加密的是當前文件,詢問是否加載加密文件if src_path == self.current_file:choice = QtWidgets.QMessageBox.question(self, "加載文件", "是否立即加載加密后的文件?",QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)if choice == QtWidgets.QMessageBox.StandardButton.Yes:self.load_media(dest_path)except Exception as e:self.show_error(f"加密失敗: {str(e)}")finally:# 恢復原始狀態(如果需要)if was_playing and src_path != dest_path:self.load_media(src_path)def _encrypt_file(self, src_path, dest_path, password):"""執行文件加密(新增)"""try:# 生成加密參數salt = secrets.token_bytes(self.salt_size)key = hashlib.pbkdf2_hmac('sha256',password.encode('utf-8'),salt,self.key_derivation_iterations,dklen=32)cipher = AES.new(key, AES.MODE_GCM)cipher.update(salt)# 分塊加密with open(src_path, 'rb') as fin, open(dest_path, 'wb') as fout:# 寫入加密頭fout.write(salt)fout.write(cipher.nonce)while True:chunk = fin.read(self.chunk_size)if not chunk:breakencrypted = cipher.encrypt(pad(chunk, AES.block_size))fout.write(encrypted)# 寫入認證標簽fout.write(cipher.digest())except PermissionError:raise RuntimeError("文件被其他程序占用,請關閉后重試")except Exception as e:raise RuntimeError(f"加密失敗: {str(e)}")def _get_decrypted_filename(self, src_path):"""生成解密文件名(去除.vef后綴)"""if src_path.lower().endswith('.vef'):return src_path[:-4]  # 去除.vefreturn src_path + "_decrypted"def decrypt_video(self):"""增強型解密方法"""try:# 選擇加密文件src_file, _ = QtWidgets.QFileDialog.getOpenFileName(self, "選擇要解密的文件", "", "加密視頻 (*.vef)")if not src_file:return# 生成默認保存路徑default_path = self._get_decrypted_filename(src_file)dest_file, _ = QtWidgets.QFileDialog.getSaveFileName(self, "保存解密文件",default_path,  # 默認原目錄+去后綴"視頻文件 (*.*)")if not dest_file:return# 檢查文件是否已存在if os.path.exists(dest_file):reply = QtWidgets.QMessageBox.question(self, "文件存在",f"目標文件已存在,是否覆蓋?\n{dest_file}",QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)if reply != QtWidgets.QMessageBox.StandardButton.Yes:return# 獲取密碼password, ok = QtWidgets.QInputDialog.getText(self, "輸入密碼", "解密密碼:", QtWidgets.QLineEdit.EchoMode.Password)if not ok or not password:return# 執行解密decrypted_path = self._decrypt_file(src_file, password, dest_file)if decrypted_path:QtWidgets.QMessageBox.information(self, "成功", f"文件解密成功!\n保存路徑: {decrypted_path}")self.load_media(decrypted_path)except Exception as e:self.show_error(f"解密失敗: {str(e)}")def _decrypt_file(self, src_path, password, dest_path):"""增強型解密方法(保存到指定路徑)"""try:with open(src_path, 'rb') as fin:salt = fin.read(self.salt_size)nonce = fin.read(self.nonce_size)key = self._derive_key(password, salt)cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)cipher.update(salt)with open(dest_path, 'wb') as fout:encrypted = fin.read()ciphertext, tag = encrypted[:-self.tag_size], encrypted[-self.tag_size:]# 分塊解密寫入chunk_size = self.chunk_size + AES.block_sizefor i in range(0, len(ciphertext), chunk_size):chunk = ciphertext[i:i+chunk_size]decrypted = unpad(cipher.decrypt(chunk), AES.block_size)fout.write(decrypted)# 驗證標簽cipher.verify(tag)return dest_pathexcept ValueError as ve:# 清理已寫入的部分文件if os.path.exists(dest_path):try:os.remove(dest_path)except:passraise ValueError("解密失敗 - 密碼錯誤或文件損壞") from veexcept Exception as e:if os.path.exists(dest_path):try:os.remove(dest_path)except:passraise RuntimeError(f"解密過程錯誤: {str(e)}") from edef _derive_key(self, password, salt):"""生成加密密鑰"""return hashlib.pbkdf2_hmac('sha256',password.encode('utf-8'),salt,self.key_derivation_iterations,dklen=32  # AES-256需要32字節密鑰)# ---------- 輔助功能 ----------def show_error(self, message):"""顯示錯誤提示"""QtWidgets.QMessageBox.critical(self, "錯誤", message)def closeEvent(self, event):"""關閉處理,清理臨時文件"""for temp_file in self.temp_files:try:if os.path.exists(temp_file):os.remove(temp_file)except Exception as e:print(f"刪除臨時文件失敗: {str(e)}")self.player.stop() # 停止播放器event.accept()  # def stop(self):self.player.stop()self.progress_bar.setValue(0)self.time_label.setText("00:00:00 / 00:00:00")self.play_btn.setText("播放")self.title_label.setText("播放已停止")def set_volume(self, value):self.player.audio_set_volume(value)def set_position(self, value):if self.player.is_seekable():self.player.set_position(value / self.progress_bar.maximum())def update_ui(self):if not self.media_loaded:return  # 新增:防止在無媒體時更新media_length = self.player.get_length()if media_length > 0:current_time = self.player.get_time()# 新增:檢測播放結束if current_time >= media_length - 500:  # 留50ms容差self.stop()returnself.progress_bar.setMaximum(media_length)self.progress_bar.setValue(current_time)total_time = time.strftime("%H:%M:%S", time.gmtime(media_length // 1000))current_time_str = time.strftime("%H:%M:%S", time.gmtime(current_time // 1000))self.time_label.setText(f"{current_time_str} / {total_time}")if __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)player = VLCPlayer()player.show()sys.exit(app.exec())

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76585.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76585.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76585.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用Python和Pygame創造粉色粒子愛心:3D渲染的藝術

引言 在計算機圖形學中,3D效果的2D渲染是一個迷人的領域。今天,我將分享一個使用Python和Pygame庫創建的粉色粒子愛心效果。這個項目不僅視覺效果驚艷,而且代碼簡潔易懂,非常適合圖形編程初學者學習3D渲染的基礎概念。 項目概述…

在匯編層面理解MESI

理解MESI協議在匯編層面的表現需要結合緩存一致性機制和處理器指令執行的行為。以下是分步驟的解釋: 1. MESI協議基礎 MESI是緩存行(Cache Line)狀態的協議,定義四種狀態: Modified(修改)&…

愛瑞編程2025暑期CSP集訓營開始招生啦!

一、什么是暑期CSP集訓營? 為全力備戰2025年9月CSP-J/S認證,舉辦的線下編程集訓活動。 旨在通過高強度編程訓練,幫助學員提升競賽能力,沖刺一等獎。 二、為什么參加集訓營? 高效編程特訓:封閉式學習&…

問題大集10-git使用commit提交中文顯示亂碼

(1)問題 (2)解決步驟 1) 設置全局編碼為 UTF-8 git config --global core.quotepath false git config --global i18n.commitEncoding utf-8 git config --global i18n.logOutputEncoding utf-8 2) 顯示或設…

當AI開始“思考“:大語言模型的文字認知三部曲

引言:從《黑客帝國》說起 1999年上映的科幻經典《黑客帝國》描繪了一個令人震撼的未來圖景——人類生活在一個由人工智能構造的數字矩陣中。當我們觀察現代大型語言模型的工作原理時,竟發現與這個虛構世界有著驚人的相似:人們正在用矩陣以及矩…

Golang改進后的任務調度系統分析

以下是整合了所有改進點的完整代碼實現: package mainimport ("bytes""context""fmt""io""log""net/http""sync""time""github.com/go-redis/redis/v8""github.com/robfig/…

前沿技術有哪些改變生活新趨勢

太陽能技術正在改變的生活 它讓移動設備有了新的能源選擇 太陽能板能直接把陽光轉成電能 這對戶外活動或者電力不便的地方特別有用 比如現在市面上有不少太陽能充電寶 小巧便攜 可以隨時給手機平板充電 需要注意的是 這些設備得放在太陽下才能工作 但它們確實能讓人在野外多用…

基于飛槳框架3.0本地DeepSeek-R1蒸餾版部署實戰

深度學習框架與大模型技術的融合正推動人工智能應用的新一輪變革。百度飛槳(PaddlePaddle)作為國內首個自主研發、開源開放的深度學習平臺,近期推出的3.0版本針對大模型時代的開發痛點進行了系統性革新。其核心創新包括“動靜統一自動并行”&…

C++設計模式-模板方法模式:從基本介紹,內部原理、應用場景、使用方法,常見問題和解決方案進行深度解析

一、基本介紹 模板方法模式(Template Method Pattern)是行為型設計模式,其核心思想是定義算法骨架,將具體步驟延遲到子類實現。如同烹飪菜譜的標準化流程:所有廚師遵循相同的操作流程(備料→烹飪→裝盤&am…

Spring Boot 自定義日志打印(日志級別、logback-spring.xml 文件、自定義日志打印解讀)

一、Logback 在 Spring Boot 中,日志框架默認使用的是 Logback,Spring Boot 提供了對日志配置的簡化 Spring Boot 默認會將日志輸出到控制臺,并且日志級別為 INFO 可以在 application.yaml 或 application.properties 文件中進行日志配置 …

Python 異步編程:如何將同步文件操作函數無縫轉換為異步版本

在 Python 的異步編程世界中,os.path 模塊的同步文件操作函數常常讓我們陷入兩難境地:直接使用它們會阻塞事件循環,降低程序性能;但這些函數又如此方便實用。今天,我將帶你探索如何巧妙地將這些同步函數轉換為異步版本,讓你的異步程序既能享受高效的事件處理,又能無縫利…

CUDA概覽

一、CUDA 是什么? CUDA(Compute Unified Device Architecture,計算統一設備架構)是 NVIDIA 于2006年推出的并行計算平臺與編程模型,旨在通過 GPU 的大規模并行計算能力加速科學計算、數據處理、人工智能等領域的計算任…

CSS3學習教程,從入門到精通, 學院網站完整項目 - HTML5 + CSS3 實現(25)

學院網站完整項目 - HTML5 CSS3 實現 下面是一個完整的學院網站項目,包含主頁、新聞列表頁、新聞詳情頁和視頻宣傳頁的實現。我將按照您的要求提供詳細的代碼和注釋。 項目結構 college-website/ ├── index.html # 主頁 ├── news-list.html …

Ubuntu離線安裝mysql

在 Ubuntu 24.04 上離線安裝 MySQL 的步驟如下(支持 MySQL 8.0 或 8.4): 一.安裝方法 此次安裝是按照方法一安裝,其它方法供參考: 安裝成功截圖: 安全配置截圖: sudo mysql_secure_installat…

SQL Server 2022 讀寫分離問題整合

跟著熱點整理一下遇到過的SQL Server的問題,這篇來聊聊讀寫分離遇到的和聽說過的問題。 一、讀寫分離實現方法 1. 原生高可用方案 1.1 Always On 可用性組(推薦方案) 配置步驟: -- 1. 啟用Always On功能 USE [master] GO ALT…

【前端掃盲】postman介紹及使用

Postman 是一款專為 API 開發與測試設計的 全流程協作工具,程序員可通過它高效完成接口調試、自動化測試、文檔管理等工作。以下是針對程序員的核心功能介紹和應用場景說明: 一、核心功能亮點 接口請求構建與調試 支持所有 HTTP 方法(GET/POS…

IdeaVim-AceJump

?AceJump 是一款專為IntelliJ IDEA平臺打造的開源插件,旨在通過簡單的快捷鍵操作幫助用戶快速跳轉到編輯器中的任何符號位置,如變量名、方法調用或特定的字符串?。無論是大型項目還是日常編程,AceJump 都能顯著提升你的代碼導航速度和效率。…

[C語言入門] 結構體

目錄 1. 啥是結構體 2. 啥是結構體變量 3. 創建結構體變量的小細節 3.1 創建全局結構體變量(不推薦) 3.2 創建局部結構體變量(不推薦) 3.3 創建局部結構體變量Plus 4. 結構體在內存里面咋存? 5. 結構體作為參數…

賢小二c#版Yolov5 yolov8 yolov10 yolov11自動標注工具 + 免python環境 GPU一鍵訓練包

賢小二c#版yolo標注訓練工具集 歡迎使用賢小二AI標注訓練系統v2.0 本課程所有演示程序全部免費 1、這節課程主要演示賢小二AI標注訓練系統的使用,以及標注數據時注意事項和技巧; 2、本程序采用c# Net8.0框架開發,是賢小二開發的一款Yolo標注…

二分類交叉熵損失

二分類交叉熵損失(Binary Cross-Entropy Loss)是用于二分類問題的常見損失函數。它衡量的是模型輸出的預測概率分布與真實標簽之間的差異。 1 二分類問題 在二分類問題中,每個樣本的目標輸出是 0 或 1,表示樣本屬于某一類或另一類…