【Pyqt5】水平布局与垂直布局及其交叉展示及实战音乐播放器UI
感受一下Pyqt5的水平布局与垂直布局及其交叉展示
需求:
- 4个按钮水平排放
- 4个按钮垂直排放
- 水平排放与垂直排放并用
- 实战:音乐播放器UI
水平排放
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayoutclass MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口标题self.setWindowTitle("mdwsw")# 创建一个水平布局layout = QHBoxLayout()# 创建4个按钮button1 = QPushButton("Button 1")button2 = QPushButton("Button 2")button3 = QPushButton("Button 3")button4 = QPushButton("Button 4")# 将按钮添加到布局中layout.addWidget(button1)layout.addWidget(button2)layout.addWidget(button3)layout.addWidget(button4)# 设置窗口的布局self.setLayout(layout)# 设置窗口大小self.setGeometry(100, 100, 400, 100)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)window = MyWindow()window.show()sys.exit(app.exec_())
效果展示
垂直排放
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayoutclass MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口标题self.setWindowTitle("mdwsw")# 创建一个水平布局layout = QVBoxLayout()# 创建4个按钮button1 = QPushButton("Button 1")button2 = QPushButton("Button 2")button3 = QPushButton("Button 3")button4 = QPushButton("Button 4")# 将按钮添加到布局中layout.addWidget(button1)layout.addWidget(button2)layout.addWidget(button3)layout.addWidget(button4)# 设置窗口的布局self.setLayout(layout)# 设置窗口大小self.setGeometry(100, 100, 400, 100)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)window = MyWindow()window.show()sys.exit(app.exec_())
效果展示
交叉展示
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayoutclass MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口标题self.setWindowTitle("mdwsw")# 创建一个垂直布局layout_v = QVBoxLayout()# 创建一个水平布局layout_h = QHBoxLayout()# 创建4个按钮button1 = QPushButton("Button 1")button2 = QPushButton("Button 2")button3 = QPushButton("Button 3")button4 = QPushButton("Button 4")# 将按钮添加到布局中layout_h.addWidget(button1)layout_h.addWidget(button2)layout_h.addWidget(button3)layout_h.addWidget(button4)# 将水平布局添加到垂直布局中layout_v.addLayout(layout_h)# 创建4个按钮button5 = QPushButton("Button 5")button6 = QPushButton("Button 6")button7 = QPushButton("Button 7")button8 = QPushButton("Button 8")layout_v.addWidget(button5)layout_v.addWidget(button6)layout_v.addWidget(button7)layout_v.addWidget(button8)# 设置窗口的布局self.setLayout(layout_v)# 设置窗口大小self.setGeometry(100, 100, 400, 200)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)window = MyWindow()window.show()sys.exit(app.exec_())
效果展示
实战
结合上面的知识,制作一个音乐播放器UI
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QSlider, QLabel
)
from PyQt5.QtCore import Qtclass MusicPlayer(QWidget):def __init__(self):super().__init__()# 设置窗口标题self.setWindowTitle("Music Player_Mdwsw")# 创建主布局main_layout = QVBoxLayout()# 歌曲信息self.song_info = QLabel("当前播放: 未知 - 未知")self.song_info.setAlignment(Qt.AlignCenter)main_layout.addWidget(self.song_info)# 进度条self.progress_slider = QSlider(Qt.Horizontal)self.progress_slider.setRange(0, 100) # 假设歌曲长度为 100self.progress_slider.setValue(0) # 初始进度为 0main_layout.addWidget(self.progress_slider)# 控制按钮布局control_layout = QHBoxLayout()# 上一首按钮self.prev_button = QPushButton("上一首")control_layout.addWidget(self.prev_button)# 播放/暂停按钮self.play_pause_button = QPushButton("播放")control_layout.addWidget(self.play_pause_button)# 停止按钮self.stop_button = QPushButton("停止")control_layout.addWidget(self.stop_button)# 下一首按钮self.next_button = QPushButton("下一首")control_layout.addWidget(self.next_button)# 将控制按钮布局添加到主布局main_layout.addLayout(control_layout)# 音量控制布局volume_layout = QHBoxLayout()# 音量标签volume_label = QLabel("音量:")volume_layout.addWidget(volume_label)# 音量滑块self.volume_slider = QSlider(Qt.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(50) # 初始音量为 50volume_layout.addWidget(self.volume_slider)# 将音量控制布局添加到主布局main_layout.addLayout(volume_layout)# 设置主布局self.setLayout(main_layout)# 设置窗口大小self.setGeometry(100, 100, 400, 200)# 连接按钮信号和槽函数self.play_pause_button.clicked.connect(self.toggle_play_pause)self.stop_button.clicked.connect(self.stop_music)self.prev_button.clicked.connect(self.prev_song)self.next_button.clicked.connect(self.next_song)self.volume_slider.valueChanged.connect(self.adjust_volume)self.progress_slider.valueChanged.connect(self.seek_position)# 槽函数def toggle_play_pause(self):if self.play_pause_button.text() == "播放":self.play_pause_button.setText("暂停")print("播放音乐")else:self.play_pause_button.setText("播放")print("暂停音乐")def stop_music(self):self.play_pause_button.setText("播放")print("停止播放")def prev_song(self):print("切换到上一首")def next_song(self):print("切换到下一首")def adjust_volume(self, value):print(f"调整音量: {value}")def seek_position(self, value):print(f"跳转到进度: {value}%")# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)player = MusicPlayer()player.show()sys.exit(app.exec_())
效果展示
点击触发后日志展示:
跳转到进度: 40%
切换到上一首
播放音乐
停止播放
切换到下一首
跳转到进度: 41%
调整音量: 56
定制一个属于自己的音乐播放器
选择音频文件播放
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QSlider, QLabel, QFileDialog
)
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtMultimediaWidgets import QVideoWidgetclass MusicPlayer(QWidget):def __init__(self):super().__init__()# 初始化媒体播放器self.media_player = QMediaPlayer()self.playlist = [] # 播放列表self.current_index = 0 # 当前播放的歌曲索引# 设置窗口标题self.setWindowTitle("本地音乐播放器")# 创建主布局main_layout = QVBoxLayout()# 歌曲信息self.song_info = QLabel("当前播放: 无")self.song_info.setAlignment(Qt.AlignCenter)main_layout.addWidget(self.song_info)# 进度条self.progress_slider = QSlider(Qt.Horizontal)self.progress_slider.setRange(0, 100)self.progress_slider.setValue(0)self.progress_slider.sliderMoved.connect(self.seek_position)main_layout.addWidget(self.progress_slider)# 控制按钮布局control_layout = QHBoxLayout()# 上一首按钮self.prev_button = QPushButton("上一首")self.prev_button.clicked.connect(self.prev_song)control_layout.addWidget(self.prev_button)# 播放/暂停按钮self.play_pause_button = QPushButton("播放")self.play_pause_button.clicked.connect(self.toggle_play_pause)control_layout.addWidget(self.play_pause_button)# 停止按钮self.stop_button = QPushButton("停止")self.stop_button.clicked.connect(self.stop_music)control_layout.addWidget(self.stop_button)# 下一首按钮self.next_button = QPushButton("下一首")self.next_button.clicked.connect(self.next_song)control_layout.addWidget(self.next_button)# 将控制按钮布局添加到主布局main_layout.addLayout(control_layout)# 音量控制布局volume_layout = QHBoxLayout()# 音量标签volume_label = QLabel("音量:")volume_layout.addWidget(volume_label)# 音量滑块self.volume_slider = QSlider(Qt.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(50)self.volume_slider.valueChanged.connect(self.adjust_volume)volume_layout.addWidget(self.volume_slider)# 将音量控制布局添加到主布局main_layout.addLayout(volume_layout)# 添加文件选择按钮self.file_button = QPushButton("选择音乐文件")self.file_button.clicked.connect(self.open_file_dialog)main_layout.addWidget(self.file_button)# 设置主布局self.setLayout(main_layout)# 设置窗口大小self.setGeometry(100, 100, 400, 200)# 定时器用于更新进度条self.timer = QTimer()self.timer.timeout.connect(self.update_progress)# 打开文件对话框def open_file_dialog(self):file_dialog = QFileDialog()file_dialog.setFileMode(QFileDialog.ExistingFiles)file_dialog.setNameFilter("音乐文件 (*.mp3 *.wav)")if file_dialog.exec_():files = file_dialog.selectedFiles()self.playlist = filesself.current_index = 0self.load_song(self.current_index)# 加载歌曲def load_song(self, index):if 0 <= index < len(self.playlist):song_path = self.playlist[index]self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(song_path)))self.song_info.setText(f"当前播放: {song_path.split('/')[-1]}")self.play_pause_button.setText("播放")self.media_player.play()# 切换播放/暂停def toggle_play_pause(self):if self.media_player.state() == QMediaPlayer.PlayingState:self.media_player.pause()self.play_pause_button.setText("播放")else:self.media_player.play()self.play_pause_button.setText("暂停")# 停止播放def stop_music(self):self.media_player.stop()self.play_pause_button.setText("播放")# 上一首def prev_song(self):if self.current_index > 0:self.current_index -= 1self.load_song(self.current_index)# 下一首def next_song(self):if self.current_index < len(self.playlist) - 1:self.current_index += 1self.load_song(self.current_index)# 调整音量def adjust_volume(self, value):self.media_player.setVolume(value)# 跳转到指定位置def seek_position(self, value):if self.media_player.duration() > 0:position = int((value / 100) * self.media_player.duration())self.media_player.setPosition(position)# 更新进度条def update_progress(self):if self.media_player.duration() > 0:progress = int((self.media_player.position() / self.media_player.duration()) * 100)self.progress_slider.setValue(progress)# 播放状态改变时启动或停止定时器def media_state_changed(self, state):if state == QMediaPlayer.PlayingState:self.timer.start(1000) # 每秒更新一次进度条else:self.timer.stop()# 初始化媒体播放器信号连接def init_media_player(self):self.media_player.stateChanged.connect(self.media_state_changed)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)player = MusicPlayer()player.init_media_player() # 初始化媒体播放器信号连接player.show()sys.exit(app.exec_())
效果展示
优化:选择文件夹,识别文件夹内音频文件,播放音频
import sys
import os
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QSlider, QLabel, QFileDialog
)
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContentclass MusicPlayer(QWidget):def __init__(self):super().__init__()# 初始化媒体播放器self.media_player = QMediaPlayer()self.playlist = [] # 播放列表self.current_index = 0 # 当前播放的歌曲索引# 设置窗口标题self.setWindowTitle("本地音乐播放器")# 创建主布局main_layout = QVBoxLayout()# 歌曲信息self.song_info = QLabel("当前播放: 无")self.song_info.setAlignment(Qt.AlignCenter)main_layout.addWidget(self.song_info)# 进度条self.progress_slider = QSlider(Qt.Horizontal)self.progress_slider.setRange(0, 100)self.progress_slider.setValue(0)self.progress_slider.sliderMoved.connect(self.seek_position)main_layout.addWidget(self.progress_slider)# 控制按钮布局control_layout = QHBoxLayout()# 上一首按钮self.prev_button = QPushButton("上一首")self.prev_button.clicked.connect(self.prev_song)control_layout.addWidget(self.prev_button)# 播放/暂停按钮self.play_pause_button = QPushButton("播放")self.play_pause_button.clicked.connect(self.toggle_play_pause)control_layout.addWidget(self.play_pause_button)# 停止按钮self.stop_button = QPushButton("停止")self.stop_button.clicked.connect(self.stop_music)control_layout.addWidget(self.stop_button)# 下一首按钮self.next_button = QPushButton("下一首")self.next_button.clicked.connect(self.next_song)control_layout.addWidget(self.next_button)# 将控制按钮布局添加到主布局main_layout.addLayout(control_layout)# 音量控制布局volume_layout = QHBoxLayout()# 音量标签volume_label = QLabel("音量:")volume_layout.addWidget(volume_label)# 音量滑块self.volume_slider = QSlider(Qt.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(20)self.volume_slider.valueChanged.connect(self.adjust_volume)volume_layout.addWidget(self.volume_slider)# 将音量控制布局添加到主布局main_layout.addLayout(volume_layout)# 添加文件夹选择按钮self.folder_button = QPushButton("选择文件夹")self.folder_button.clicked.connect(self.open_folder_dialog)main_layout.addWidget(self.folder_button)# 设置主布局self.setLayout(main_layout)# 设置窗口大小self.setGeometry(100, 100, 400, 200)# 定时器用于更新进度条self.timer = QTimer()self.timer.timeout.connect(self.update_progress)# 打开文件夹对话框def open_folder_dialog(self):folder_dialog = QFileDialog()folder_path = folder_dialog.getExistingDirectory(self, "选择文件夹")if folder_path:self.load_songs_from_folder(folder_path)else:self.load_songs_from_folder(r"D:\BaiduNetdiskDownload\儿歌")# 从文件夹加载音频文件def load_songs_from_folder(self, folder_path):self.playlist = []supported_formats = [".mp3", ".wav"] # 支持的音频格式for root, dirs, files in os.walk(folder_path):for file in files:if any(file.endswith(format) for format in supported_formats):self.playlist.append(os.path.join(root, file))if self.playlist:self.current_index = 0self.load_song(self.current_index)else:self.song_info.setText("未找到支持的音频文件")# 加载歌曲def load_song(self, index):if 0 <= index < len(self.playlist):song_path = self.playlist[index]self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(song_path)))self.song_info.setText(f"当前播放: {os.path.basename(song_path)}")self.play_pause_button.setText("播放")self.media_player.play()# 切换播放/暂停def toggle_play_pause(self):if self.media_player.state() == QMediaPlayer.PlayingState:self.media_player.pause()self.play_pause_button.setText("播放")else:self.media_player.play()self.play_pause_button.setText("暂停")# 停止播放def stop_music(self):self.media_player.stop()self.play_pause_button.setText("播放")# 上一首def prev_song(self):if self.current_index > 0:self.current_index -= 1self.load_song(self.current_index)# 下一首def next_song(self):if self.current_index < len(self.playlist) - 1:self.current_index += 1self.load_song(self.current_index)# 调整音量def adjust_volume(self, value):self.media_player.setVolume(value)# 跳转到指定位置def seek_position(self, value):if self.media_player.duration() > 0:position = int((value / 100) * self.media_player.duration())self.media_player.setPosition(position)# 更新进度条def update_progress(self):if self.media_player.duration() > 0:progress = int((self.media_player.position() / self.media_player.duration()) * 100)self.progress_slider.setValue(progress)# 播放状态改变时启动或停止定时器def media_state_changed(self, state):if state == QMediaPlayer.PlayingState:self.timer.start(1000) # 每秒更新一次进度条else:self.timer.stop()# 初始化媒体播放器信号连接def init_media_player(self):self.media_player.stateChanged.connect(self.media_state_changed)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)player = MusicPlayer()player.init_media_player() # 初始化媒体播放器信号连接player.show()sys.exit(app.exec_())
再优化:播放当前结束,自动跳转下一首;
import sys
import os
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QSlider, QLabel, QFileDialog
)
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContentclass MusicPlayer(QWidget):def __init__(self):super().__init__()# 初始化媒体播放器self.media_player = QMediaPlayer()self.playlist = [] # 播放列表self.current_index = 0 # 当前播放的歌曲索引# 设置窗口标题self.setWindowTitle("本地音乐播放器")# 创建主布局main_layout = QVBoxLayout()# 歌曲信息self.song_info = QLabel("当前播放: 无")self.song_info.setAlignment(Qt.AlignCenter)main_layout.addWidget(self.song_info)# 进度条self.progress_slider = QSlider(Qt.Horizontal)self.progress_slider.setRange(0, 100)self.progress_slider.setValue(0)self.progress_slider.sliderMoved.connect(self.seek_position)main_layout.addWidget(self.progress_slider)# 控制按钮布局control_layout = QHBoxLayout()# 上一首按钮self.prev_button = QPushButton("上一首")self.prev_button.clicked.connect(self.prev_song)control_layout.addWidget(self.prev_button)# 播放/暂停按钮self.play_pause_button = QPushButton("播放")self.play_pause_button.clicked.connect(self.toggle_play_pause)control_layout.addWidget(self.play_pause_button)# 停止按钮self.stop_button = QPushButton("停止")self.stop_button.clicked.connect(self.stop_music)control_layout.addWidget(self.stop_button)# 下一首按钮self.next_button = QPushButton("下一首")self.next_button.clicked.connect(self.next_song)control_layout.addWidget(self.next_button)# 将控制按钮布局添加到主布局main_layout.addLayout(control_layout)# 音量控制布局volume_layout = QHBoxLayout()# 音量标签volume_label = QLabel("音量:")volume_layout.addWidget(volume_label)# 音量滑块self.volume_slider = QSlider(Qt.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(20)self.volume_slider.valueChanged.connect(self.adjust_volume)volume_layout.addWidget(self.volume_slider)# 将音量控制布局添加到主布局main_layout.addLayout(volume_layout)# 添加文件夹选择按钮self.folder_button = QPushButton("选择文件夹")self.folder_button.clicked.connect(self.open_folder_dialog)main_layout.addWidget(self.folder_button)# 设置主布局self.setLayout(main_layout)# 设置窗口大小self.setGeometry(100, 100, 400, 200)# 定时器用于更新进度条self.timer = QTimer()self.timer.timeout.connect(self.update_progress)# 初始化媒体播放器信号连接self.init_media_player()# 打开文件夹对话框def open_folder_dialog(self):folder_dialog = QFileDialog()folder_path = folder_dialog.getExistingDirectory(self, "选择文件夹")if folder_path:self.load_songs_from_folder(folder_path)else:self.load_songs_from_folder(r"D:\BaiduNetdiskDownload\儿歌")# 从文件夹加载音频文件def load_songs_from_folder(self, folder_path):self.playlist = []supported_formats = [".mp3", ".wav"] # 支持的音频格式for root, dirs, files in os.walk(folder_path):for file in files:if any(file.endswith(format) for format in supported_formats):self.playlist.append(os.path.join(root, file))if self.playlist:self.current_index = 0self.load_song(self.current_index)else:self.song_info.setText("未找到支持的音频文件")# 加载歌曲def load_song(self, index):if 0 <= index < len(self.playlist):song_path = self.playlist[index]self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(song_path)))self.song_info.setText(f"当前播放: {os.path.basename(song_path)}")self.play_pause_button.setText("播放")self.media_player.play()# 切换播放/暂停def toggle_play_pause(self):if self.media_player.state() == QMediaPlayer.PlayingState:self.media_player.pause()self.play_pause_button.setText("播放")else:self.media_player.play()self.play_pause_button.setText("暂停")# 停止播放def stop_music(self):self.media_player.stop()self.play_pause_button.setText("播放")# 上一首def prev_song(self):if self.current_index > 0:self.current_index -= 1self.load_song(self.current_index)# 下一首def next_song(self):if self.current_index < len(self.playlist) - 1:self.current_index += 1self.load_song(self.current_index)else:# 如果已经是最后一首,停止播放self.stop_music()# 调整音量def adjust_volume(self, value):self.media_player.setVolume(value)# 跳转到指定位置def seek_position(self, value):if self.media_player.duration() > 0:position = int((value / 100) * self.media_player.duration())self.media_player.setPosition(position)# 更新进度条def update_progress(self):if self.media_player.duration() > 0:progress = int((self.media_player.position() / self.media_player.duration()) * 100)self.progress_slider.setValue(progress)# 播放状态改变时启动或停止定时器def media_state_changed(self, state):if state == QMediaPlayer.PlayingState:self.timer.start(1000) # 每秒更新一次进度条else:self.timer.stop()# 媒体状态改变时检查是否播放结束def media_status_changed(self, status):if status == QMediaPlayer.EndOfMedia:self.next_song() # 自动播放下一首# 初始化媒体播放器信号连接def init_media_player(self):self.media_player.stateChanged.connect(self.media_state_changed)self.media_player.mediaStatusChanged.connect(self.media_status_changed)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)player = MusicPlayer()player.show()sys.exit(app.exec_())
时间显示
import sys
import os
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QSlider, QLabel, QFileDialog
)
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContentclass MusicPlayer(QWidget):def __init__(self):super().__init__()# 初始化媒体播放器self.media_player = QMediaPlayer()self.playlist = [] # 播放列表self.current_index = 0 # 当前播放的歌曲索引# 设置窗口标题self.setWindowTitle("本地音乐播放器")# 创建主布局main_layout = QVBoxLayout()# 歌曲信息self.song_info = QLabel("当前播放: 无")self.song_info.setAlignment(Qt.AlignCenter)main_layout.addWidget(self.song_info)# 时间显示self.time_label = QLabel("00:00 / 00:00")self.time_label.setAlignment(Qt.AlignCenter)main_layout.addWidget(self.time_label)# 进度条self.progress_slider = QSlider(Qt.Horizontal)self.progress_slider.setRange(0, 100)self.progress_slider.setValue(0)self.progress_slider.sliderMoved.connect(self.seek_position)main_layout.addWidget(self.progress_slider)# 控制按钮布局control_layout = QHBoxLayout()# 上一首按钮self.prev_button = QPushButton("上一首")self.prev_button.clicked.connect(self.prev_song)control_layout.addWidget(self.prev_button)# 播放/暂停按钮self.play_pause_button = QPushButton("播放")self.play_pause_button.clicked.connect(self.toggle_play_pause)control_layout.addWidget(self.play_pause_button)# 停止按钮self.stop_button = QPushButton("停止")self.stop_button.clicked.connect(self.stop_music)control_layout.addWidget(self.stop_button)# 下一首按钮self.next_button = QPushButton("下一首")self.next_button.clicked.connect(self.next_song)control_layout.addWidget(self.next_button)# 将控制按钮布局添加到主布局main_layout.addLayout(control_layout)# 音量控制布局volume_layout = QHBoxLayout()# 音量标签volume_label = QLabel("音量:")volume_layout.addWidget(volume_label)# 音量滑块self.volume_slider = QSlider(Qt.Horizontal)self.volume_slider.setRange(0, 100)self.volume_slider.setValue(50)self.volume_slider.valueChanged.connect(self.adjust_volume)volume_layout.addWidget(self.volume_slider)# 将音量控制布局添加到主布局main_layout.addLayout(volume_layout)# 添加文件夹选择按钮self.folder_button = QPushButton("选择文件夹")self.folder_button.clicked.connect(self.open_folder_dialog)main_layout.addWidget(self.folder_button)# 设置主布局self.setLayout(main_layout)# 设置窗口大小self.setGeometry(100, 100, 400, 250)# 定时器用于更新进度条和时间显示self.timer = QTimer()self.timer.timeout.connect(self.update_progress_and_time)# 初始化媒体播放器信号连接self.init_media_player()# 打开文件夹对话框def open_folder_dialog(self):folder_dialog = QFileDialog()folder_path = folder_dialog.getExistingDirectory(self, "选择文件夹")if folder_path:self.load_songs_from_folder(folder_path)# 从文件夹加载音频文件def load_songs_from_folder(self, folder_path):self.playlist = []supported_formats = [".mp3", ".wav"] # 支持的音频格式for root, dirs, files in os.walk(folder_path):for file in files:if any(file.endswith(format) for format in supported_formats):self.playlist.append(os.path.join(root, file))if self.playlist:self.current_index = 0self.load_song(self.current_index)else:self.song_info.setText("未找到支持的音频文件")# 加载歌曲def load_song(self, index):if 0 <= index < len(self.playlist):song_path = self.playlist[index]self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(song_path)))self.song_info.setText(f"当前播放: {os.path.basename(song_path)}")self.play_pause_button.setText("播放")self.media_player.play()# 切换播放/暂停def toggle_play_pause(self):if self.media_player.state() == QMediaPlayer.PlayingState:self.media_player.pause()self.play_pause_button.setText("播放")else:self.media_player.play()self.play_pause_button.setText("暂停")# 停止播放def stop_music(self):self.media_player.stop()self.play_pause_button.setText("播放")# 上一首def prev_song(self):if self.current_index > 0:self.current_index -= 1self.load_song(self.current_index)# 下一首def next_song(self):if self.current_index < len(self.playlist) - 1:self.current_index += 1self.load_song(self.current_index)else:# 如果已经是最后一首,停止播放self.stop_music()# 调整音量def adjust_volume(self, value):self.media_player.setVolume(value)# 跳转到指定位置def seek_position(self, value):if self.media_player.duration() > 0:position = int((value / 100) * self.media_player.duration())self.media_player.setPosition(position)# 更新进度条和时间显示def update_progress_and_time(self):if self.media_player.duration() > 0:# 更新进度条progress = int((self.media_player.position() / self.media_player.duration()) * 100)self.progress_slider.setValue(progress)# 更新时间显示current_time = self.format_time(self.media_player.position())total_time = self.format_time(self.media_player.duration())self.time_label.setText(f"{current_time} / {total_time}")# 格式化时间(毫秒 -> 分钟:秒)def format_time(self, milliseconds):seconds = int(milliseconds / 1000)minutes = seconds // 60seconds = seconds % 60return f"{minutes:02}:{seconds:02}"# 播放状态改变时启动或停止定时器def media_state_changed(self, state):if state == QMediaPlayer.PlayingState:self.timer.start(1000) # 每秒更新一次进度条和时间else:self.timer.stop()# 媒体状态改变时检查是否播放结束def media_status_changed(self, status):if status == QMediaPlayer.EndOfMedia:self.next_song() # 自动播放下一首# 初始化媒体播放器信号连接def init_media_player(self):self.media_player.stateChanged.connect(self.media_state_changed)self.media_player.mediaStatusChanged.connect(self.media_status_changed)# 主程序
if __name__ == "__main__":app = QApplication(sys.argv)player = MusicPlayer()player.show()sys.exit(app.exec_())
效果展示
相关文章:
【Pyqt5】水平布局与垂直布局及其交叉展示及实战音乐播放器UI
感受一下Pyqt5的水平布局与垂直布局及其交叉展示 需求: 4个按钮水平排放4个按钮垂直排放水平排放与垂直排放并用实战:音乐播放器UI 水平排放 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayoutclass MyWindo…...
Java 中 getCanonicalName、getSimpleName、getName、getTypeName 的区别
1. 核心区别总结 方法作用数组类型示例非数组类型示例getName()返回 JVM 内部格式的类全名,适用于反射操作(如 Class.forName())int[] → [IString → java.lang.StringgetTypeName()返回更友好的类型名称,对数组递归处理组件类型…...
uni-app打包h5并部署到nginx,路由模式history
uni-app打包有些坑,当时运行的基础路径填写了./,导致在二级页面刷新之后,页面直接空白。就只能换一个路径了,nginx也要跟着改,下面是具体步骤。 manifest.json配置web 运行路径写/h5/,或者写你们网站的目…...
数据结构与算法(哈希表——两个数组的交集)
原题 349. 两个数组的交集 - 力扣(LeetCode) 给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1: 输入:nums1 [1,2,2,1], nums2 […...
P1259 黑白棋子的移动【java】【AC代码】
有 2n 个棋子排成一行,开始为位置白子全部在左边,黑子全部在右边,如下图为 n5 的情况: 移动棋子的规则是:每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但…...
一些docker命令
一、基础命令 查看 Docker 版本 docker --version 或 docker version:显示 Docker 客户端和服务器的版本信息。 查看 Docker 系统信息 docker info:显示 Docker 系统的详细信息,包括镜像、容器数量、存储驱动类型等。 Docker 服务管理 s…...
云服务器新手配置内网穿透服务(frp)
首先你得有一个公网服务器,有了它你就可以借助它,将自己电脑进行配置内网穿透,让自己内网电脑也可以异地轻松访问。网上教程较多,特此记录我自己的配置,避免迷路,我这里只记录我自己云服务小白,…...
linux ptrace 图文详解(二) PTRACE_TRACEME 跟踪程序
目录 一、基础介绍 二、PTRACE_TRACE 实现原理 三、代码实现 四、总结 (代码:linux 6.3.1,架构:arm64) One look is worth a thousand words. —— Tess Flanders 一、基础介绍 GDB(GNU Debugger&…...
Maven安装、idea集成Maven、Maven依赖管理、Maven生命周期
一. Maven介绍 1. Maven是一款用于管理和构建Java项目的工具,是Apache旗下的一个开源项目,它基于项目对象模型(POM)的概念,通过一小段描述信息来管理项目的构建 2. Maven作用: (1) 依赖管理:方便快捷的管理项目依赖的资…...
【xv6操作系统】系统调用与traps机制解析及实验设计
【xv6操作系统】系统调用与traps机制解析及实验设计 系统调用相关理论系统调用追溯系统调用实验设计Sysinfo🚩系统调用总结(结合trap机制) traptrap机制trap代码流程Backtrace实验alarm实验 系统调用 相关理论 隔离性(isolation)…...
S7-1200 G2移植旧版本S7-1200程序的具体方法示例
S7-1200 G2移植旧版本S7-1200程序的具体方法示例 前期概要: S7-1200 G2必须基于TIA博途V20,之前的程序可通过移植的方式在新硬件上使用。 该移植工具可自动将TIA Portal 项目从 S7-1200 移植到更新的S7-1200 G2。 注意: 该插件支持在同一TIA Portal项目实例内将软件和/或硬…...
海量数据查询加速:Presto、Trino、Apache Arrow
1. 引言 在大数据分析场景下,查询速度往往是影响业务决策效率的关键因素。随着数据量的增长,传统的行存储数据库难以满足低延迟的查询需求,因此,基于列式存储、向量化计算等技术的查询引擎应运而生。本篇文章将深入探讨 Presto、Trino、Apache Arrow 三种主流的查询优化工…...
vscode远程连接服务器并运行项目里的.ipynb文件 如何在 Jupyter Notebook 中切换/使用 conda 虚拟环境?
【最全指南】如何在 Jupyter Notebook 中切换/使用 conda 虚拟环境? 最好用的方法! 使用 nb_conda_kernels 添加所有环境 第二种方法其实也挺不错的。有个缺点是,你新建一个环境,就要重复操作一次。 而这个方法就是一键添加所有…...
二阶优化方法详解
前言 本文隶属于专栏《机器学习数学通关指南》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见《机器学习数学通关指南》 ima 知识库 知识库广场搜索&#…...
C++中通过虚函数实现多态的原理
C中通过虚函数实现多态的原理 我们都知道C是通过虚函数实现多态的,那么其中的原理是什么呢? 在C中,多态性是一种重要的特性,它允许通过基类指针或引用来调用派生类中的函数。多态性主要分为两种:编译时多态ÿ…...
阿里云服务器购买及环境搭建宝塔部署springboot和vue项目
云服务器ECS_云主机_服务器托管_计算-阿里云 一、前言 对于新手或者学生党来说,有时候就想租一个云服务器来玩玩或者练练手,duck不必花那么多钱去租个服务器。这些云服务厂商对学生和新手还是相当友好的。下面将教你如何快速搭建自己的阿里云服务器&…...
【学习笔记】中缀表达式转后缀表达式及计算
C实现中缀表达式转后缀表达式及后缀表达式的计算 在C中,实现中缀表达式转换为后缀表达式(逆波兰表达式)以及后缀表达式的计算是一个非常经典的问题。它不仅涉及到栈(Stack)数据结构的使用,还涉及到对运算符…...
【机器人-基础知识】标定 - 相机标定全解
https://blog.csdn.net/MengYa_Dream/article/details/120233806 1. 相机标定的定义 相机标定是确定相机成像过程中各个参数的过程,它的核心目标是建立从三维世界坐标系到二维图像坐标系的数学映射关系。这一过程包括求解: 内参:描述相机内部光学特性(如焦距、主点位置、像…...
Java 8 + Tomcat 9.0.102 的稳定环境搭建方案,适用于生产环境
一、安装 Java 8 安装 OpenJDK 8 bash sudo apt update sudo apt install openjdk-8-jdk -y 验证安装 bash java -version 应输出类似: openjdk version “1.8.0_412” OpenJDK Runtime Environment (build 1.8.0_412-8u412-ga-1~22.04-b08) OpenJDK 64-Bit Server VM (bui…...
探索 C 语言枚举类型的奇妙世界
目录 一、枚举类型的定义二、枚举类型变量的声明和初始化2.1 先定义枚举类型,再声明变量2.2 定义枚举类型的同时声明变量 三、自定义枚举常量的值四、枚举类型的特点五、注意事项 在C语言中,枚举类型( enum)是一种用户自定义的数…...
buu-ciscn_2019_ne_5-好久不见50
1. 背景分析 目标程序是一个存在漏洞的二进制文件,我们可以通过以下方式利用漏洞获取 shell: 程序中存在 system() 函数,但没有明显的 /bin/sh 字符串。 使用工具(如 ROPgadget)发现程序中有 sh 字符串,可…...
HCIA-ACL实验
前提条件:实现底层互通 转发层面 1、基本ACL ①要求PC3不能访问网段192.168.2.0的网段,PC4和客户端能正常访问服务器 ②AR2配置 acl 2000 rule deny source 192.168.1.1 0 匹配流量 int g 0/0/0 traffic-filter inbound acl 2000 接口调用…...
Java入职篇(2)——开发流程以及专业术语
Java入职篇(2)——开发流程以及专业术语 开发流程 开发术语 测试用例(用例) 测试人员写的测试方案,基本上就是编写的测试过程,以及测试的预取结果 灰度测试 现在小部分范围内使用,然后逐步…...
三相逆变器不控整流场景简要分析
0 三相逆变器拓扑 LCL三相逆变器简要拓扑如下图所示,其他类型如多电平逆变器类似。 1 原理说明 软件在进行直流母线电压Udc的给定取值时,考虑到电压利用率,通常会比电网线电压的峰值稍微高些,比如取线电压峰值的1.0x倍&#x…...
语言识别模型whisper学习笔记
语言识别模型whisper学习笔记 Whisper 是由 OpenAI 于 2022年9月 推出的开源自动语音识别(ASR)系统,旨在实现高精度、多语言的语音转文本及翻译任务。其核心目标是解决传统语音识别模型在噪声环境、口音多样性及多语言场景下的局限性。 一、…...
centos 换阿里云yum
1、备份原有的Yum源配置文件 在更换Yum源之前,先备份CentOS系统中默认的Yum源配置文件,以便在需要时恢复。默认的Yum源配置文件位于 /etc/yum.repos.d/ 目录下,通常包含 CentOS-Base.repo、CentOS-Debuginfo.repo、CentOS-Vault.repo 等文件…...
Jmeter的简单使用
前置工作 确保java8 版本以上jmeter下载路径(选择Binaries):https://jmeter.apache.org/download_jmeter.cgi直接解压,找到bin下面的文件:jmeter.bat(可选)汉化,修改 jmeter.proper…...
CSS元素层叠顺序规则
CSS元素层叠顺序规则 看图说话总结: background/borderz-index(<0)blockfloatinline/inline-blockz-index(0,auto)z-index (>0)...
用Maven创建只有POM文件的项目
使用 mvn 创建一个仅包含 pom.xml 文件的父项目,可以借助 maven-archetype-quickstart 原型,然后移除不必要的文件,或者直接通过命令生成最简的 pom.xml 文件。以下是具体操作步骤: 一、方法一:使用原型创建后清理 1…...
使用Python在Word中生成多种不同类型的图表
目录 工具与环境配置 在 Word 中创建图表的步骤 在Word中创建柱形图 在Word中创建条形图 在Word中创建折线图 在Word中创建饼图 在Word中创建散点图 在Word中创建气泡图 在 Word 文档中插入图表不仅能更直观地呈现数据,还能提升文档的可读性和专业性。常见的…...
Webpack构建流程详解优化前端性能\Dev-Server与Proxy\网络攻击\HMR
简版 核心流程图 根据,Webpack的构建流程分为初始化、编译和输出三个阶段。初始化阶段读取配置、加载插件、实例化Compiler。编译阶段(构建依赖关系)涉及Compiler类的运行,生成Compilation对象,处理模块依赖。输出阶…...
Python 实现的采集诸葛灵签
Python 实现的采集诸葛灵签 项目介绍 这是一个基于 Python 开发的诸葛灵签数据采集和展示项目。通过爬虫技术获取诸葛神签的签文和解签内容,并提供数据存储和查询功能。 项目结构 zhuge/├── zhuge_scraper.py # 爬虫主程序├── zhuge_pages/ # 数据存储目录…...
ESP-IDF ubuntu版本 V5.2
1.MobaXterm 这个软件方面粘贴,文件拷贝 MobaXterm 2.安装之前请确保你安装了Python 和 pip V5.2需要python3.8和pip mkdir esp32 cd esp32 git clone https://gitee.com/EspressifSystems/esp-gitee-tools.git cd esp-gitee-tools ./jihu-mirror.sh set cd .. git clone …...
Opencv之掩码实现图片抠图
掩码实现图片抠图 目录 掩码实现图片抠图1 掩码1.1 概念1.2 创建掩码1.3抠图思路 2 代码测试 1 掩码 1.1 概念 掩码(Mask)是一种用于指定图像处理操作区域的工具。掩码通常是一个与图像尺寸相同的二值图像,其中像素值为0表示不处理ÿ…...
警惕!Ollama大模型工具的安全风险及应对策略
文章目录 **Ollama的安全隐患:不容忽视的风险****未授权访问:门户洞开的风险****数据泄露:敏感信息的外泄****漏洞利用:历史遗留的隐患** **安全加固:守护数据与服务的防线****限制监听范围:内网隔离的保护…...
MySQL -- 表的约束
概念引入:真正的约束表字段的是数据类型,但是数据类型的约束方式比较单一的,所以需要一些额外的一些约束,用于表示数据的合法性,在只有数据类型一种约束的情况下,我们比较难保证数据是百分百合法。通过添加…...
详解数据库范式
范式 1. 第一范式(1NF)2. 第二范式(2NF)3. 第三范式(3NF)4. BC范式(BCNF,Boyce-Codd Normal Form)5. 第四范式(4NF)6. 第五范式(5NF&a…...
Nginx + Keepalived 高可用集群
一、NginxKeepalived 原理 1.1.Nginx 负载均衡机制 Nginx 是一款轻量级且高性能的 Web 服务器和反向代理服务器,在负载均衡方面有着卓越的表现。其具备强大的七层流量管理能力,能够基于 URL、Cookie、HTTP 头信息等对请求进行精准路由。例如࿰…...
循环遍历 Java 集合中元素的方法总结
循环遍历 Java 集合中元素的方法 在 Java 中,有多种方法可以遍历集合中的元素。以下是几种常见的遍历方法及其优缺点: 1. for-each 循环 语法: for (ElementType element : collection) {// 处理 element }适用场景:所有集合类型…...
树莓派上的 TensorFlow Lite:从零开始的摄像头图像识别
**** 1. 引言 随着人工智能(AI)和机器学习(ML)的发展,越来越多的开发者希望在嵌入式设备(如树莓派)上运行 AI 模型,实现目标检测、人脸识别等功能。TensorFlow Lite(TF…...
金融时间序列分析(Yahoo Finance API实战)
这里写目录标题 金融时间序列分析(Yahoo Finance API实战)1. 引言2. 项目背景与意义3. 数据集介绍4. GPU加速在数据处理中的应用5. 交互式GUI设计与加速处理6. 系统整体架构7. 数学公式与指标计算8. 完整代码实现9. 代码自查与BUG排查10. 总结与展望金融时间序列分析(Yahoo …...
Python 正则表达式模块 re
Python 正则表达式模块 re flyfish 一、正则表达式基础 1. 什么是正则表达式? 正则表达式(Regular Expression, RE)是一种用于匹配、查找和替换文本模式的工具,由普通字符(如字母、数字)和特殊字符&…...
Vue生命周期
一、Vue的生命周期及其阶段 Vue生命周期:一个Vue实例从 创建 到 销毁 的整个过程。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。 生命周期的四个阶段:① 创建 ② 挂…...
vue3数据双向绑定解析
Vue 3 的双向绑定原理主要基于 Proxy 和 Reflect,核心源码在 reactivity 模块中。 1. 核心模块:reactivity reactivity 模块负责响应式数据的实现,主要包括以下几个文件: reactive.ts:处理对象和数组的响应式。ref.t…...
Gemini 2.0 全面解析:技术突破、应用场景与竞争格局
摘要 2025年3月,谷歌正式发布Gemini 2.0大模型,凭借其在多模态处理、代码生成和长上下文理解等领域的突破性进展,迅速成为AI领域的焦点。本文将深入剖析Gemini 2.0的技术架构、应用场景及与Grok3、DeepSeek R1、ChatGPT-4.5等竞品的对比&…...
【Linux系统编程】管道
目录 1、什么是管道2、管道的种类3、数据的读写3.1、管道通信3.2、管道的命令实例: 4、无名管道4.1、pipe() 无名管道的创建示例:简单读写示例:加入进程示例:通过 管道(pipe) 实现 父子进程之间的双向通信 …...
LeeCode题库第643题
643.子数组最大平均数I 项目场景: 给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。 任何误差小于 10-5 的答案都将被视为正确答案。 示例 1: 输入ÿ…...
数据炼丹与硬件互动:预测湿度的武学之道
前言 在这茫茫数据江湖中,高手过招,唯有融合机器学习与物联网之精髓,方能于风云变幻间自成一派。本文正是为各位江湖同道献上的秘籍,既有数据炼丹(预处理、模型训练)之奥义,也有硬件互通&#…...
【SpringBoot】MD5加盐算法的详解
目录 一、什么是加盐算法 二、如何实现加盐算法 2.1 加盐算法代码实现 2.2 注册页面中进行密码加盐 2.3 登录页面进行加盐的解密 2.4 注册和登录 一、什么是加盐算法 加盐算法是一种用于增强密码安全性的技术。这种技术通过在密码存储过程中添加一个随机生成的盐值&…...
IP风险度自检,互联网的安全“指南针”
IP地址就像我们的网络“身份证”,而IP风险度则是衡量这个“身份证”安全性的重要指标。它关乎着我们的隐私保护、账号安全以及网络体验,今天就让我们一起深入了解一下IP风险度。 什么是IP风险度 IP风险度是指一个IP地址可能暴露用户真实身份或被网络平台…...