起因:年后开始上班,一直坐电脑旁,时间长了颈椎和腰部都不舒服,感觉要隔一段时间提醒自己锻炼一下,但是又找不到合适的方法,最后想到用程序来播报语音提醒的方式来让自己起来活动。
使用场景
定时提醒:
-
例如每小时提醒用户休憩、喝水等。
-
背景音乐:定时播放背景音乐,营造氛围。
-
自动化任务:定时播放特定音频,用于自动化任务。
实现的功能如下:
1. 定时播放音频:
用户可以选择本地音频文件(
.mp3 和
.wav
格式)。
设置时间间隔(以分钟为单位),程序会按照设定的时间间隔定时播放音频。

2.
试听功能
:
用户可以在选择音频文件后立即试听,确保音频文件能够正常播放。

3. 保存和加载设置:
程序会保存用户选择的音频文件和时间间隔,并在下次启动时自动加载。

4.
开机自启动
:
用户可以选择是否让程序随系统自动启动。
如果通过开机自启动启动,程序会自动隐藏到状态栏。

5. 系统托盘支持:
用户可以选择将程序最小化到系统托盘,而不是直接关闭。
托盘图标支持自定义图标,并提供“恢复窗口”和“退出”选项。


运行界面如图:

全部代码如下:
import tkinter as tk
from tkinter import filedialog, messagebox
import pygame
import json
import os
import threading
import sys
import getpass
from PIL import Image, ImageDraw
import pystray
# 初始化pygame
pygame.mixer.init()
# 配置文件路径
CONFIG_FILE = "config.json"
class AudioTimerApp:
def __init__(self, root):
self.root = root
self.root.title("定时语音播报")
# 设置默认窗口大小
self.root.geometry("500x330") # 宽度500像素,高度330像素
# 设置程序图标
self.set_icon()
# 将窗口定位到屏幕正中心
self.center_window()
# 加载上次的设置
self.load_config()
# 创建界面元素
self.create_widgets()
# 启动定时器
self.start_timer()
# 重写关闭按钮事件
self.root.protocol("WM_DELETE_WINDOW", self.on_close)
# 检查是否通过开机自启动启动
if self.is_autostart_launch() and not self.is_first_launch():
self.minimize_to_tray() # 自动隐藏到状态栏
def create_widgets(self):
# 选择音频文件按钮
self.file_button = tk.Button(self.root, text="选择音频文件", command=self.select_file)
self.file_button.pack(pady=10)
# 显示当前选择的文件
self.file_label = tk.Label(self.root, text=f"当前文件: {os.path.basename(self.audio_file) if self.audio_file else '未选择'}")
self.file_label.pack()
# 试听按钮
self.preview_button = tk.Button(self.root, text="试听", command=self.preview_audio, state=tk.DISABLED)
self.preview_button.pack(pady=10)
# 时间间隔输入框
self.interval_label = tk.Label(self.root, text="时间间隔 (分钟):")
self.interval_label.pack(pady=10)
# 使用 Spinbox 替换 Entry
self.interval_spinbox = tk.Spinbox(self.root, from_=1, to=120, increment=1, width=5)
self.interval_spinbox.insert(0, str(self.interval // 60)) # 将秒转换为分钟
self.interval_spinbox.pack()
# 保存设置按钮
self.save_button = tk.Button(self.root, text="保存设置", command=self.save_settings)
self.save_button.pack(pady=20)
# 开机自启动选项
self.autostart_var = tk.BooleanVar(value=self.check_autostart())
self.autostart_check = tk.Checkbutton(self.root, text="开机自启动", variable=self.autostart_var, command=self.toggle_autostart)
self.autostart_check.pack(pady=10)
def select_file(self):
file_path = filedialog.askopenfilename(filetypes=[("音频文件", "*.mp3 *.wav")])
if file_path:
self.audio_file = file_path
self.file_label.config(text=f"当前文件: {os.path.basename(self.audio_file)}")
self.preview_button.config(state=tk.NORMAL) # 启用试听按钮
self.save_settings()
def preview_audio(self):
if self.audio_file:
try:
print(f"正在试听音频文件: {self.audio_file}") # 调试信息
pygame.mixer.music.load(self.audio_file)
pygame.mixer.music.play()
print("试听成功") # 调试信息
except Exception as e:
print(f"试听失败: {e}") # 调试信息
messagebox.showerror("错误", f"无法试听音频: {e}")
def start_timer(self):
if self.audio_file:
self.play_audio()
self.root.after(self.interval * 1000, self.start_timer)
def play_audio(self):
try:
print(f"正在加载音频文件: {self.audio_file}") # 调试信息
pygame.mixer.music.load(self.audio_file)
pygame.mixer.music.play()
print("音频播放成功") # 调试信息
except Exception as e:
print(f"音频播放失败: {e}") # 调试信息
messagebox.showerror("错误", f"无法播放音频: {e}")
def save_settings(self):
try:
interval_minutes = int(self.interval_spinbox.get())
if interval_minutes < 1:
messagebox.showerror("错误", "时间间隔不能小于1分钟")
return
self.interval = interval_minutes * 60 # 将分钟转换为秒
config = {
"audio_file": self.audio_file,
"interval": self.interval
}
with open(CONFIG_FILE, "w") as f:
json.dump(config, f)
messagebox.showinfo("成功", "设置已保存!") # 提示保存成功
self.start_timer() # 重新启动定时器
except ValueError:
messagebox.showerror("错误", "请输入有效的数字")
def load_config(self):
try:
with open(CONFIG_FILE, "r") as f:
config = json.load(f)
self.audio_file = config.get("audio_file", "")
self.interval = config.get("interval", 360) # 默认值为360秒(6分钟)
except FileNotFoundError:
self.audio_file = ""
self.interval = 360 # 默认值为360秒(6分钟)
def on_close(self):
# 弹出选择框,让用户选择最小化到托盘或退出
choice = messagebox.askyesnocancel("关闭程序", "是否最小化到托盘?
点击'是'最小化,'否'退出程序")
if choice is True: # 最小化到托盘
self.minimize_to_tray()
elif choice is False: # 退出
self.root.destroy() # 关闭程序
# 如果点击撤销,则不做任何操作
def minimize_to_tray(self):
# 隐藏主窗口
self.root.withdraw()
# 创建系统托盘图标
self.create_tray_icon()
def create_tray_icon(self):
# 加载自定义图标文件
def create_image():
try:
# 获取图标文件路径
if getattr(sys, 'frozen', False): # 判断是否打包成 exe
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(__file__)
icon_path = os.path.join(base_path, "tray_icon.ico")
# 加载图标文件
image = Image.open(icon_path)
return image
except Exception as e:
print(f"无法加载托盘图标: {e}")
# 如果图标文件不存在,使用默认图标
image = Image.new('RGB', (64, 64), 'white')
dc = ImageDraw.Draw(image)
dc.rectangle((16, 16, 48, 48), fill='blue')
return image
# 托盘菜单
menu = (
pystray.MenuItem('恢复窗口', self.restore_from_tray),
pystray.MenuItem('退出', self.quit_program),
)
# 创建托盘图标
self.tray_icon = pystray.Icon("AudioTimer", create_image(), "定时语音播报", menu)
self.tray_icon.run()
def restore_from_tray(self, icon, item):
# 恢复主窗口
self.root.deiconify()
# 停止托盘图标
self.tray_icon.stop()
def quit_program(self, icon, item):
# 退出程序
self.tray_icon.stop()
self.root.destroy()
def toggle_autostart(self):
if self.autostart_var.get():
self.enable_autostart()
else:
self.disable_autostart()
def enable_autostart(self):
# 获取当前用户的启动目录
startup_folder = os.path.join(os.getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
# 获取当前脚本的路径
script_path = os.path.abspath(sys.argv[0])
# 创建快捷方式
shortcut_path = os.path.join(startup_folder, "AudioTimer.lnk")
try:
import winshell
from win32com.client import Dispatch
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(shortcut_path)
shortcut.Targetpath = script_path
shortcut.WorkingDirectory = os.path.dirname(script_path)
shortcut.save()
print("开机自启动已启用")
except ImportError:
messagebox.showerror("错误", "无法启用开机自启动,请确保已安装 `pywin32` 和 `winshell`")
def disable_autostart(self):
# 获取当前用户的启动目录
startup_folder = os.path.join(os.getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
# 删除快捷方式
shortcut_path = os.path.join(startup_folder, "AudioTimer.lnk")
if os.path.exists(shortcut_path):
os.remove(shortcut_path)
print("开机自启动已禁用")
def check_autostart(self):
# 检查是否已设置开机自启动
startup_folder = os.path.join(os.getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
shortcut_path = os.path.join(startup_folder, "AudioTimer.lnk")
return os.path.exists(shortcut_path)
def is_autostart_launch(self):
# 检查是否通过开机自启动启动
startup_folder = os.path.join(os.getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
shortcut_path = os.path.join(startup_folder, "AudioTimer.lnk")
if os.path.exists(shortcut_path):
# 获取当前脚本的路径
script_path = os.path.abspath(sys.argv[0])
# 检查快捷方式的目标路径是否与当前脚本路径一致
try:
from win32com.client import Dispatch
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(shortcut_path)
return shortcut.Targetpath == script_path
except ImportError:
pass
return False
def is_first_launch(self):
# 检查是否是第一次启动
first_launch_file = "first_launch.txt"
if not os.path.exists(first_launch_file):
with open(first_launch_file, "w") as f:
f.write("1")
return True
return False
def set_icon(self):
# 设置程序图标
icon_path = "app.ico" # 图标文件路径
if os.path.exists(icon_path):
self.root.iconbitmap(icon_path)
else:
print(f"图标文件 {icon_path} 不存在,请确保图标文件与程序在同一目录下。")
def center_window(self):
# 获取屏幕尺寸
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
# 计算窗口居中的位置
window_width = 500 # 窗口宽度
window_height = 330 # 窗口高度
x = (screen_width // 2) - (window_width // 2)
y = (screen_height // 2) - (window_height // 2)
# 设置窗口位置
self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")
if __name__ == "__main__":
root = tk.Tk()
app = AudioTimerApp(root)
root.mainloop()
打包说明:
–add-data “tray_icon.ico;.”:将 tray_icon.ico 文件包含在打包的 exe 文件中,;. 表明将文件放在与 exe 一样的目录下。
–icon=app.ico:为 exe 文件设置图标。
pyinstaller --onefile --windowed --add-data "tray_icon.ico;." --icon=app.ico audio_timer.py
最后打包成exe文件,在windows上运行。
本程序是一个简易使用的定时语音播报工具,适合需要定时播放音频的场景。通过图形界面和系统托盘支持,用户可以方便地管理和操作程序。如果需要进一步扩展功能,可以在此基础上进行优化和开发。
附上已经打包好的程序放在网盘,需要的请自行下载:
链接: https://pan.baidu.com/s/1LcDpAmhmllraMWM89iYVHg?pwd=be62 提取码: be62
链接:https://pan.quark.cn/s/8abb75988466 提取码:CJes
















- 最新
- 最热
只看作者