有没有大佬帮孩子看看问题出在哪啊啊啊啊

我写了一个python音乐播放器,在实现进度条功能时

为什么拖动进度条后,进度条后会退回原来的位置,而不是在拖动到的位置继续加载啊
求个大佬解答啊(qmq)
这是我写的代码(有注释)
 

import os
import sys
import tkinter as tk
from tkinter import filedialog, messagebox
import pygame
import json
from moviepy.editor import AudioFileClip
import keyboard  # 用于全局热键
import ctypes  # 隐藏控制台窗口
import time  # 用于时间逻辑
from PIL import Image, ImageTk  # 用于图像处理

# 隐藏控制台窗口
def hide_console():
    ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)

# 初始化 Pygame mixer
pygame.mixer.init()

# 加载配置
config_file = 'config.json'

def load_config():
    if os.path.exists(config_file):
        with open(config_file, 'r') as f:
            return json.load(f)
    return {"autostart": False}

def save_config(config):
    with open(config_file, 'w') as f:
        json.dump(config, f)

# 获取资源路径的函数
def resource_path(relative_path):
    """获取资源路径,兼容打包后引用"""
    try:
        base_path = sys._MEIPASS
    except AttributeError:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

# 音乐文件转换器
class MusicFileConverter(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        self.title("音乐文件转换器")
        self.geometry("400x200")
        self.configure(bg="#222831")

        self.selected_file = None
        self.parent = parent  # 父窗口引用

        self.label = tk.Label(self, text="选择要转换的文件或文件夹", bg="#222831", fg="#00adb5", font=("Arial", 14))
        self.label.pack(pady=10)

        self.select_button = tk.Button(self, text="选择文件", command=self.select_file, bg="#00adb5", fg="white", font=("Arial", 12))
        self.select_button.pack(pady=5)

        self.folder_button = tk.Button(self, text="选择文件夹", command=self.select_folder, bg="#00adb5", fg="white", font=("Arial", 12))
        self.folder_button.pack(pady=5)

        self.convert_button = tk.Button(self, text="转换为 MP3", command=self.convert_to_mp3, bg="#00adb5", fg="white", font=("Arial", 12))
        self.convert_button.pack(pady=20)

        self.result_label = tk.Label(self, text="", bg="#222831", fg="white", font=("Arial", 10))
        self.result_label.pack(pady=5)

    def select_file(self):
        """选择文件"""
        self.selected_file = filedialog.askopenfilename(filetypes=[
            ("音频文件", "*.wav;*.ogg;*.flac;*.m4a;*.mp4")
        ])
        if self.selected_file:
            self.label.config(text=f"选中: {self.selected_file}")

    def select_folder(self):
        """选择文件夹"""
        folder_path = filedialog.askdirectory()
        if folder_path:
            self.label.config(text=f"选中文件夹: {folder_path}")
            self.selected_file = folder_path

    def convert_to_mp3(self):
        """转换选中的文件为 MP3"""
        if not self.selected_file:
            messagebox.showwarning("警告", "请先选择文件或文件夹!")
            return

        if os.path.isfile(self.selected_file):
            # 是文件的情况下
            self.convert_single_file(self.selected_file)
        elif os.path.isdir(self.selected_file):
            # 是文件夹的情况下
            for f in os.listdir(self.selected_file):
                if not f.lower().endswith('.mp3'):  # 只转换非MP3文件
                    full_path = os.path.join(self.selected_file, f)
                    if os.path.isfile(full_path):
                        self.convert_single_file(full_path)

    def convert_single_file(self, file_path):
        """将单个文件转换为MP3"""
        try:
            base_name = os.path.splitext(os.path.basename(file_path))[0]
            dir_name = os.path.dirname(file_path)
            new_file_path = os.path.join(dir_name, f"{base_name}.mp3")

            # 使用moviepy进行文件转换
            audio_clip = AudioFileClip(file_path)
            audio_clip.write_audiofile(new_file_path, codec='mp3')
            audio_clip.close()

            # 更新界面显示结果
            self.result_label.config(text=f"成功转换: {new_file_path}")

            # 添加到主程序的播放列表
            self.parent.music_files.append(new_file_path)  # 将MP3文件添加到主程序
            self.parent.load_music()  # 重新加载音乐文件

            # 关闭转换器窗口
            self.destroy()
        except Exception as e:
            messagebox.showerror("错误", f"无法转换文件 {file_path}: {e}")

# 创建主窗口
root = tk.Tk()
root.title("音径播放器")
root.geometry("400x500")
root.configure(bg="#222831")  # 背景颜色

# 音乐播放状态
music_files = []  # 存储所有音乐文件
current_track = 0  # 当前播放的歌曲索引
loop_mode = False  # False 为顺序播放,True 为单曲循环
is_playing = False  # 记录播放/暂停状态
last_f6_time = 0  # 记录最后一次按下 F6 的时间
total_time = 0  # 总时间
rotation_angle = 0  # 旋转角度

# 新变量,用于表示音乐播放进度百分比
musictime = 0  # 进度条变量,范围从0到100
progress_value = 0  # 控制进度条的更新(0-100)

# 图标大小和位置设置
icon_size = 200  # 自定义旋转图标的大小
icon_x = 100  # 图标的X坐标
icon_y = 95  # 图标的Y坐标

# 加载图像(确保这些文件有透明背景)
play_image = tk.PhotoImage(file=resource_path("in/play.png"))  # 播放图标
pause_image = tk.PhotoImage(file=resource_path("in/pause.png"))  # 暂停图标
prev_image = tk.PhotoImage(file=resource_path("in/prev.png"))  # 上一首图标
next_image = tk.PhotoImage(file=resource_path("in/next.png"))  # 下一首图标

# 将下一首图标设为全局变量
next_button_image = next_image
rotating_icon_image = Image.open(resource_path("in/rotating_icon.png")).resize((icon_size, icon_size))  # 加载旋转图标并自定义大小

# 全局变量,用于跟踪进度条拖动状态
is_dragging = False  # 标记是否正在拖动进度条

def load_music():
    """打开文件对话框,选择音乐文件"""
    global music_files, current_track, total_time
    file_path = filedialog.askopenfilename(filetypes=[
        ("音频文件", "*.mp3;*.wav;*.ogg;*.flac;*.m4a;*.mp4")
    ])
    
    if file_path:
        music_files.append(file_path)  # 直接更新为选择的文件

    if music_files:
        try:
            load_current_track()
            messagebox.showinfo("音乐加载", "音乐已加载!可以开始播放。")
        except Exception as e:
            messagebox.showerror("错误", f"无法加载音乐文件: {e}")

def load_music_folder():
    """打开文件夹对话框,选择音乐文件夹"""
    global music_files, current_track
    folder_path = filedialog.askdirectory()  # 选择文件夹
    if folder_path:
        music_files = []
        
        # 仅加载MP3文件
        for f in os.listdir(folder_path):
            full_path = os.path.join(folder_path, f)
            if f.lower().endswith('.mp3'):
                music_files.append(full_path)  # 添加MP3文件

        if music_files:
            current_track = 0  # 重置播放索引
            load_current_track()
            messagebox.showinfo("文件夹加载", f"已加载 {len(music_files)} 首音乐!")
        else:
            messagebox.showwarning("警告", "所选文件夹中没有支持的音乐文件。")

def load_current_track():
    """加载当前播放的音乐"""
    global total_time  # 确保使用全局变量
    if music_files and 0 <= current_track < len(music_files):
        music_file = music_files[current_track]
        try:
            pygame.mixer.music.load(music_file)  # 直接加载MP3文件
        except pygame.error as e:
            messagebox.showerror("错误", f"无法加载音乐文件 {music_file}: {e}")
            return  # 如果加载失败,则退出此函数
        
        total_time = AudioFileClip(music_files[current_track]).duration  # 获取音乐总长度
        total_time_label.config(text=f"总时间: {format_time(total_time)}")  # 更新总时间标签

        # 获取当前播放位置,设置为0(音乐未播放,不需要设置位置)
        current_time = 0 
        current_time_label.config(text=f"当前时间: {format_time(current_time)}")  # 更新当前时间标签为实际时间
        
        # 播放当前歌曲
        play_music()  # 播放当前歌曲
        display_song_name()  # 显示歌曲名称
        stop_rotating()  # 停止旋转图标

def format_time(seconds):
    """将秒转换为HH:MM:SS格式"""
    minutes, seconds = divmod(int(seconds), 60)
    hours, minutes = divmod(minutes, 60)
    return f"{hours:02}:{minutes:02}:{seconds:02}"

def play_music():
    """播放音乐"""
    global is_playing
    if music_files:
        pygame.mixer.music.play()  # 播放音乐
        is_playing = True
        play_pause_button.config(image=pause_image)  # 切换到暂停图标
        start_rotating()  # 开始旋转图标
        update_progress()  # 开始更新时间
        # 增加播放完成的检测
        root.after(100, check_playing_status)  # 每100ms检查播放状态

def check_playing_status():
    """检查当前音乐是否播放完毕"""
    global current_track
    if not pygame.mixer.music.get_busy():  # 当音乐播放完毕
        if loop_mode:
            # 如果是单曲循环,则重新播放当前曲目
            load_current_track()
        else:
            # 如果是顺序播放,切换到下一首曲目
            next_track()  # 自动切换到下一曲
    else:
        root.after(100, check_playing_status)  # 继续检查播放状态

def pause_music():
    """暂停音乐"""
    global is_playing
    pygame.mixer.music.pause()
    is_playing = False
    play_pause_button.config(image=play_image)  # 切换到播放图标
    stop_rotating()  # 停止旋转图标

def unpause_music():
    """恢复音乐"""
    global is_playing
    if music_files:
        pygame.mixer.music.unpause()
        is_playing = True
        play_pause_button.config(image=pause_image)  # 切换为暂停图标
        start_rotating()  # 开始旋转图标
        update_progress()  # 继续更新时间

def close_application():
    """关闭应用程序"""
    pygame.mixer.music.stop()  # 停止音乐
    root.destroy()

def toggle_loop_mode():
    """切换播放模式"""
    global loop_mode
    loop_mode = not loop_mode
    mode = "单曲循环" if loop_mode else "顺序播放"
    messagebox.showinfo("播放模式", f"当前播放模式: {mode}")

def handle_f6_key():
    """处理 F6 键逻辑"""
    global last_f6_time, is_playing
    current_time = time.time()

    if (current_time - last_f6_time) <= 1:
        play_music()  # 重新播放音乐
    else:
        if not is_playing:
            unpause_music()  # 恢复播放
        else:
            pause_music()  # 暂停音乐
    
    last_f6_time = current_time

def previous_track():
    """切换到上一首歌曲"""
    global current_track
    if music_files:
        if current_track > 0:
            current_track -= 1
        else:
            messagebox.showinfo("无效操作", "已经是第一首歌曲,无法再切换到上一首。")

        pause_music()  # 在切换之前先暂停音乐
        root.after(1000, load_current_track)  # 1秒后加载当前曲目并播放

def next_track():
    """切换到下一首歌曲"""
    global current_track
    if music_files:
        if loop_mode:
            current_track += 1
            if current_track >= len(music_files):
                current_track = 0  # 如果超过最后一首,则回到第一首
        else:
            if current_track < len(music_files) - 1:
                current_track += 1  # 切换到下一首
            else:
                current_track = 0  # 如果到达最后一首,则循环回到第一首

        pause_music()  # 在切换之前先暂停音乐
        root.after(1000, load_current_track)  # 1秒后加载当前曲目并播放

def toggle_window():
    """切换窗口的显示与隐藏"""
    if root.state() == 'normal':
        root.withdraw()  # 隐藏窗口
    else:
        root.deiconify()  # 显示窗口
        root.lift()       # 保证窗口在最上面
        root.attributes('-topmost', True)  # 固定在最上层

# 更新进度条和当前播放时间
def update_progress():
    """更新播放状态和进度条"""
    global musictime, progress_value, total_time
    if is_playing and not is_dragging:  # 只有在不拖动时更新进度
        current_time = pygame.mixer.music.get_pos() / 1000  # 获取当前播放的时间(单位:秒)
        
        musictime = (current_time / total_time) * 100 if total_time > 0 else 0  # 更新旧变量的百分比
        
        # 将旧变量的值实时设置到新变量上
        progress_value = musictime  # 将旧变量值赋给新变量
        
        progress_bar.set(progress_value)  # 更新进度条的值
        current_time_label.config(text=f"当前时间: {format_time(current_time)}")  # 更新当前时间标签
        root.after(1000, update_progress)  # 每秒更新状态

# 旋转图标相关
rotation_angle = 0  # 旋转角度
icon_label = tk.Label(root, image=ImageTk.PhotoImage(rotating_icon_image), bg="#222831")  # 创建旋转图标标签
icon_label.place(x=icon_x, y=icon_y)  # 图标初始位置

def rotate_icon():
    """旋转图标"""
    global rotation_angle
    rotation_angle = (rotation_angle + 5) % 360  # 旋转角度
    rotated_icon = rotating_icon_image.rotate(rotation_angle)  # 旋转图标
    icon_label.img = ImageTk.PhotoImage(rotated_icon)  # 更新图像
    icon_label.configure(image=icon_label.img)  # 显示旋转后的图标
    if is_playing:  # 只有在播放时才继续旋转
        root.after(20, rotate_icon)  # 每20毫秒旋转一次

def start_rotating():
    """开始旋转图标"""
    rotate_icon()  # 启动旋转

def stop_rotating():
    """停止旋转图标"""
    icon_label.configure(image=ImageTk.PhotoImage(rotating_icon_image))  # 恢复到原始图标

def display_song_name():
    """显示歌曲名称"""
    if music_files and 0 <= current_track < len(music_files):
        song_name = os.path.splitext(os.path.basename(music_files[current_track]))[0]  # 获取文件名,不包括后缀
        song_name_label.config(text=f"歌曲名称: {song_name}")

# 创建 UI 组件
title_label = tk.Label(root, text="音径播放器", bg="#222831", fg="#00adb5", font=("Arial", 24, "bold"))
title_label.place(x=120, y=20)  # x坐标居中

# 转换器按钮
convert_btn = tk.Button(root, text="转换音频文件", command=lambda: MusicFileConverter(root), bg="#00adb5", fg="white", font=("Arial", 10))
convert_btn.place(x=10, y=30)  # 左上角

# “上一首”图标
prev_button = tk.Label(root, image=prev_image, bg="#222831")
prev_button.place(x=100, y=370)  # 使用place方法定位
prev_button.bind("<Button-1>", lambda e: previous_track())  # 点击事件

# 播放/暂停按钮
play_pause_button = tk.Button(root, image=play_image, command=handle_f6_key, bg="#222831", borderwidth=0)
play_pause_button.place(x=160, y=350)

# “下一首”图标
next_button = tk.Label(root, image=next_button_image, bg="#222831")
next_button.place(x=260, y=370)  # 使用place方法定位
next_button.bind("<Button-1>", lambda e: next_track())  # 点击事件

# 加载音乐按钮,居中显示
load_btn = tk.Button(root, text="加载音乐", command=load_music, bg="#00adb5", fg="white", font=("Arial", 14))
load_btn.place(x=150, y=450)  # 居中显示

# 加载文件夹按钮
load_folder_btn = tk.Button(root, text="加载文件夹", command=load_music_folder, bg="#00adb5", fg="white", font=("Arial", 14))
load_folder_btn.place(x=8, y=450)  # 将加载文件夹按钮放在左侧

# 切换播放模式按钮
loop_btn = tk.Button(root, text="切换播放模式", command=toggle_loop_mode, bg="#00adb5", fg="white", font=("Arial", 14))
loop_btn.place(x=260, y=450)  # 将循环播放按钮放在原“自启动”按钮的位置

# 当前时间标签
current_time_label = tk.Label(root, text="当前时间: 00:00:00", bg="#222831", fg="white")
current_time_label.place(x=10, y=420)  # 使用place方法定位

# 总时间标签
total_time_label = tk.Label(root, text="总时间: 00:00:00", bg="#222831", fg="white")
total_time_label.place(x=290, y=420)  # 使用place方法定位

# 歌曲名称标签
song_name_label = tk.Label(root, text="歌曲名称: ", bg="#222831", fg="white")
song_name_label.place(x=130, y=70)  # 在播放按钮下方显示

# 音乐进度条
progress_bar = tk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, bg="#00adb5", fg="white", sliderlength=15, cursor="hand2")
progress_bar.place(x=50, y=300, width=300)  # 设置滑块的位置信息

# 更新音乐播放位置时
def set_music_position(event):
    """设置音乐播放的位置,以进度条的值为基准"""
    global total_time
    if total_time > 0:
        new_position_seconds = (progress_bar.get() / 100) * total_time  # 获取进度条值对应的位置
        pygame.mixer.music.set_pos(new_position_seconds)  # 设置音乐播放位置

        # 更新当前时间标签
        current_time_label.config(text=f"当前时间: {format_time(new_position_seconds)}")  # 更新当前时间标签

# 处理鼠标按下事件
def on_progress_bar_click(event):
    global is_dragging
    is_dragging = True  # 标记为正在拖动
    set_music_position(event)  # 更新播放位置

# 处理鼠标松开事件
def on_progress_bar_release(event):
    global is_dragging  # 确保使用全局变量
    is_dragging = False  # 取消拖动标记
    set_music_position(event)  # 在释放鼠标时更新位置
    current_time_seconds = (progress_bar.get() / 100) * total_time  # 计算拖动后的当前歌曲时间
    current_time_label.config(text=f"当前时间: {format_time(current_time_seconds)}")  # 更新当前时间标签
    
    # 重新开始更新时间的函数
    update_progress(musictime)  # 从当前位置继续更新进度

# 绑定滑块的点击和释放事件
progress_bar.bind("<Button-1>", on_progress_bar_click)  # 鼠标按下事件
progress_bar.bind("<ButtonRelease-1>", on_progress_bar_release)  # 鼠标释放事件

def bind_global_keys():
    """绑定全局热键"""
    keyboard.add_hotkey('f5', toggle_window)  # 控制窗口的显示与隐藏
    keyboard.add_hotkey('f6', handle_f6_key)  # 控制播放和暂停

# 隐藏控制台窗口
hide_console()

# 绑定全局热键
bind_global_keys()

# 运行主循环
start_rotating()  # 启动旋转
update_progress()  # 启动进度更新
root.mainloop()

# 退出 pygame
pygame.mixer.quit()

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值