114.01.14-schoolmusicplay-03-23.py
-有學校圖片
-打包
import tkinter as tk
from tkinter import filedialog, messagebox
import pygame
import os
import sys
import time
import sched
import threading
import random
from datetime import datetime, timedelta
import json
from PIL import Image, ImageTk # 引入 Pillow 庫
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("校園音響自動播放系統簡易版")
self.playlist = [] # 播放清單
self.schedule_task_ids = [] # 儲存排程任務 ID,用於清除排程
# 顯示圖片
self.image_path = "image.png" # 假設圖片名為 image.png 且與程式在同一資料夾
self.load_image()
# 創建一個框架 (Frame) 用來包住圖片和按鈕
image_button_frame = tk.Frame(root)
image_button_frame.pack(pady=0) # 設定間距
# 顯示圖片
self.image_label = tk.Label(image_button_frame, image=self.image)
self.image_label.pack(side="left", padx=10, pady=5) # 顯示圖片並設置間距
#self.image_label.place(anchor="nw", padx=10, pady=5) # 左上角對齊
# 當前日期時間顯示區
self.date_time_label = tk.Label(image_button_frame, text="", font=("Arial", 12))
#self.date_time_label.pack(anchor="ne", padx=10, pady=5)
self.date_time_label.pack(side="left", padx=10, pady=5)
# 當前日期時間顯示區
#self.date_time_label = tk.Label(root, text="", font=("Arial", 12))
#self.date_time_label.pack(anchor="ne", padx=10, pady=5)
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=5) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
#self.music_path_label = tk.Label(root, text="尚未選擇音檔")
#self.music_path_label.pack(pady=5)
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 按鈕區 (包含新功能)
self.button_frame = tk.Frame(root)
self.button_frame.pack(pady=10)
# 開始執行按鈕
self.start_button = tk.Button(self.button_frame, text="開始排程", command=self.start_scheduling)
self.start_button.pack(side="left", padx=10)
# 新功能的按鈕
self.clear_schedule_button = tk.Button(self.button_frame, text="清空目前排程", command=self.clear_schedule)
self.clear_schedule_button.pack(side="left", padx=10)
self.delete_selected_button = tk.Button(self.button_frame, text="刪除選擇的清單", command=self.delete_selected_playlist)
self.delete_selected_button.pack(side="left", padx=10)
self.delete_allselected_button = tk.Button(self.button_frame, text="刪除全部加入的清單", command=self.delete_selected_allplaylist)
self.delete_allselected_button.pack(side="left", padx=10)
self.save_schedule_button = tk.Button(self.button_frame, text="保存設定檔", command=self.save_schedule)
self.save_schedule_button.pack(side="left", padx=10)
self.load_schedule_button = tk.Button(self.button_frame, text="載入設定檔", command=self.load_schedule)
self.load_schedule_button.pack(side="left", padx=10)
# 排程狀況顯示區
self.schedule_status_label = tk.Label(root, text="當前排程狀況:", anchor="w")
self.schedule_status_label.pack(fill=tk.BOTH, padx=10, pady=5)
#建立人及時間
note_label = tk.Label(root, text="Copyright@2025 資訊組王清賢 FSPS-新北市中和區復興國小", font=("Arial", 10), fg="seagreen")
note_label.place(relx=0.99, rely=0.99, anchor='se')
# 更新日期和時間
self.update_date_time()
def load_image(self):
# 載入原始圖片並縮放為原圖的四分之一大
img = Image.open(self.image_path)
img = img.resize((img.width // 4, img.height // 4)) # 調整大小為四分之一
self.image = ImageTk.PhotoImage(img) # 轉換為可在Tkinter中使用的圖片
def update_date_time(self):
# 取得當前時間
current_time = datetime.now()
# 加入秒數格式化
date_str = current_time.strftime("%Y-%m-%d %H:%M:%S") # 加入秒數 %S
weekday = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][current_time.weekday()]
date_time_str = f"{date_str}-{weekday}"
# 更新日期時間標籤
self.date_time_label.config(text=date_time_str)
# 每隔 1000 毫秒(1 秒)更新一次
self.root.after(1000, self.update_date_time)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
self.schedule_task_ids.clear() # 清空已存在的任務ID
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
task_id = scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
self.schedule_task_ids.append(task_id)
# 開始排程
threading.Thread(target=scheduler.run).start()
# 更新排程狀況顯示
schedule_info = "\n".join([f"{item['music']} at {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})" for item in self.playlist])
self.schedule_status_label.config(text=f"當前排程狀況:\n{schedule_info}")
#def play_music(self, music_path):
# pygame.mixer.music.load(music_path)
# pygame.mixer.music.play()
# while pygame.mixer.music.get_busy():
# time.sleep(1)
def play_music(self, music_path):
try:
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
except pygame.error as e:
messagebox.showerror("錯誤", f"無法播放音樂檔案: {e}")
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
def delete_selected_playlist(self):
selected_index = self.playlist_listbox.curselection()
if selected_index:
selected_index = selected_index[0]
del self.playlist[selected_index]
self.playlist_listbox.delete(selected_index)
def delete_selected_allplaylist(self):
#清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
def clear_schedule(self):
# 取消所有排程任務
for task_id in self.schedule_task_ids[:]: # 迭代排程任務 ID 的副本
try:
scheduler.cancel(task_id) # 嘗試取消排程
except ValueError:
pass # 如果任務已經不存在,忽略該錯誤
self.schedule_task_ids.clear() # 清空任務 ID 列表
# 清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
# 更新排程狀況顯示
self.schedule_status_label.config(text="當前排程狀況:\n目前沒有排程")
messagebox.showinfo("提示", "已清空目前排程!")
def save_schedule(self):
file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(self.playlist, f, ensure_ascii=False, indent=4)
messagebox.showinfo("提示", "設定檔已保存!")
def load_schedule(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
self.playlist = json.load(f)
self.playlist_listbox.delete(0, tk.END)
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
114.01.14-schoolmusicplay-03-17.py
-加上建置人員的說明
--需再測試
114.01.14-schoolmusicplay-03-16.py
-測試:加上
--畫面"選擇MP3音檔"的上面靠右對齊,能顯示現在的日期時間,如"2025-01-13 11:00-星期一(要用中文呈現)",要如何修正程式?
--右上角加上時間顯示
--缺少建置人員的說明
import tkinter as tk
from tkinter import filedialog, messagebox
import pygame
import os
import time
import sched
import threading
import random
from datetime import datetime, timedelta
import json
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
self.schedule_task_ids = [] # 儲存排程任務 ID,用於清除排程
# 當前日期時間顯示區
self.date_time_label = tk.Label(root, text="", font=("Arial", 12))
self.date_time_label.pack(anchor="ne", padx=10, pady=5)
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=10) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=5)
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 按鈕區 (包含新功能)
self.button_frame = tk.Frame(root)
self.button_frame.pack(pady=10)
# 開始執行按鈕
self.start_button = tk.Button(self.button_frame, text="開始排程", command=self.start_scheduling)
self.start_button.pack(side="left", padx=10)
# 新功能的按鈕
self.clear_schedule_button = tk.Button(self.button_frame, text="清空目前排程", command=self.clear_schedule)
self.clear_schedule_button.pack(side="left", padx=10)
self.delete_selected_button = tk.Button(self.button_frame, text="刪除選擇的清單", command=self.delete_selected_playlist)
self.delete_selected_button.pack(side="left", padx=10)
self.delete_selected_button = tk.Button(self.button_frame, text="刪除全部加入的清單", command=self.delete_selected_allplaylist)
self.delete_selected_button.pack(side="left", padx=10)
self.save_schedule_button = tk.Button(self.button_frame, text="保存設定檔", command=self.save_schedule)
self.save_schedule_button.pack(side="left", padx=10)
self.load_schedule_button = tk.Button(self.button_frame, text="載入設定檔", command=self.load_schedule)
self.load_schedule_button.pack(side="left", padx=10)
# 排程狀況顯示區
self.schedule_status_label = tk.Label(root, text="當前排程狀況:", anchor="w")
self.schedule_status_label.pack(fill=tk.BOTH, padx=10, pady=5)
# 更新日期和時間
self.update_date_time()
def update_date_time(self):
# 取得當前時間
current_time = datetime.now()
# 加入秒數格式化
date_str = current_time.strftime("%Y-%m-%d %H:%M:%S") # 加入秒數 %S
weekday = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][current_time.weekday()]
date_time_str = f"{date_str}-{weekday}"
# 更新日期時間標籤
self.date_time_label.config(text=date_time_str)
# 每隔 1000 毫秒(1 秒)更新一次
self.root.after(1000, self.update_date_time)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
self.schedule_task_ids.clear() # 清空已存在的任務ID
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
task_id = scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
self.schedule_task_ids.append(task_id)
# 開始排程
threading.Thread(target=scheduler.run).start()
# 更新排程狀況顯示
schedule_info = "\n".join([f"{item['music']} at {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})" for item in self.playlist])
self.schedule_status_label.config(text=f"當前排程狀況:\n{schedule_info}")
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
def delete_selected_playlist(self):
selected_index = self.playlist_listbox.curselection()
if selected_index:
selected_index = selected_index[0]
del self.playlist[selected_index]
self.playlist_listbox.delete(selected_index)
def delete_selected_allplaylist(self):
#清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
def clear_schedule(self):
# 取消所有排程任務
for task_id in self.schedule_task_ids[:]: # 迭代排程任務 ID 的副本
try:
scheduler.cancel(task_id) # 嘗試取消排程
except ValueError:
pass # 如果任務已經不存在,忽略該錯誤
self.schedule_task_ids.clear() # 清空任務 ID 列表
# 清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
# 更新排程狀況顯示
self.schedule_status_label.config(text="當前排程狀況:\n目前沒有排程")
messagebox.showinfo("提示", "已清空目前排程!")
def save_schedule(self):
file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(self.playlist, f, ensure_ascii=False, indent=4)
messagebox.showinfo("提示", "設定檔已保存!")
def load_schedule(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
self.playlist = json.load(f)
self.playlist_listbox.delete(0, tk.END)
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
1140113-1140108-schoolmusicplay-03-15.py
--右上角缺少時間顯示
--缺少系統時間網路同步
--名稱修正
--僅能播放mp3檔
--
import tkinter as tk
from tkinter import filedialog, messagebox
import pygame
import os
import time
import sched
import threading
import random
from datetime import datetime, timedelta
import json
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
self.schedule_task_ids = [] # 儲存排程任務 ID,用於清除排程
# 當前日期時間顯示區
self.date_time_label = tk.Label(root, text="", font=("Arial", 12))
self.date_time_label.pack(anchor="ne", padx=10, pady=5)
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=10) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=5)
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 按鈕區 (包含新功能)
self.button_frame = tk.Frame(root)
self.button_frame.pack(pady=10)
# 開始執行按鈕
self.start_button = tk.Button(self.button_frame, text="開始排程", command=self.start_scheduling)
self.start_button.pack(side="left", padx=10)
# 新功能的按鈕
self.delete_selected_button = tk.Button(self.button_frame, text="刪除選擇的清單", command=self.delete_selected_playlist)
self.delete_selected_button.pack(side="left", padx=10)
self.delete_selected_button = tk.Button(self.button_frame, text="刪除全部加入的清單", command=self.delete_selected_allplaylist)
self.delete_selected_button.pack(side="left", padx=10)
self.clear_schedule_button = tk.Button(self.button_frame, text="清空目前排程", command=self.clear_schedule)
self.clear_schedule_button.pack(side="left", padx=10)
self.save_schedule_button = tk.Button(self.button_frame, text="保存設定檔", command=self.save_schedule)
self.save_schedule_button.pack(side="left", padx=10)
self.load_schedule_button = tk.Button(self.button_frame, text="載入設定檔", command=self.load_schedule)
self.load_schedule_button.pack(side="left", padx=10)
# 排程狀況顯示區
self.schedule_status_label = tk.Label(root, text="當前排程狀況:", anchor="w")
self.schedule_status_label.pack(fill=tk.BOTH, padx=10, pady=5)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
self.schedule_task_ids.clear() # 清空已存在的任務ID
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
task_id = scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
self.schedule_task_ids.append(task_id)
# 開始排程
threading.Thread(target=scheduler.run).start()
# 更新排程狀況顯示
schedule_info = "\n".join([f"{item['music']} at {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})" for item in self.playlist])
self.schedule_status_label.config(text=f"當前排程狀況:\n{schedule_info}")
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
def delete_selected_playlist(self):
selected_index = self.playlist_listbox.curselection()
if selected_index:
selected_index = selected_index[0]
del self.playlist[selected_index]
self.playlist_listbox.delete(selected_index)
def delete_selected_allplaylist(self):
#清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
#def clear_schedule(self):
# 取消所有排程任務
# for task_id in self.schedule_task_ids[:]: # 迭代排程任務 ID 的副本
# try:
# scheduler.cancel(task_id) # 嘗試取消排程
# except ValueError:
# pass # 如果任務已經不存在,忽略該錯誤
# self.schedule_task_ids.clear() # 清空任務 ID 列表
# messagebox.showinfo("提示", "已清空目前排程!")
def clear_schedule(self):
# 取消所有排程任務
for task_id in self.schedule_task_ids[:]: # 迭代排程任務 ID 的副本
try:
scheduler.cancel(task_id) # 嘗試取消排程
except ValueError:
pass # 如果任務已經不存在,忽略該錯誤
self.schedule_task_ids.clear() # 清空任務 ID 列表
# 清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
# 更新排程狀況顯示
self.schedule_status_label.config(text="當前排程狀況:\n目前沒有排程")
messagebox.showinfo("提示", "已清空目前排程!")
def save_schedule(self):
file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(self.playlist, f, ensure_ascii=False, indent=4)
messagebox.showinfo("提示", "設定檔已保存!")
def load_schedule(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
self.playlist = json.load(f)
self.playlist_listbox.delete(0, tk.END)
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
使用繁體中文,在wi111,使用python語言,使用UI界面,想製作一個校園音響自動播放程式,規畫如圖片所示,要如何產生可以實例使用的範例程式碼?
使用繁體中文,在win11下,要如何使用powershell來呼叫mediaplayer.exe,使用指令來播放mp3音檔,程式開啟後,自動播放一次?
使用繁體中文,在win11下,在powershell下,要如何使用利用建內的mediaplayer,使用指令來播放mp3的音檔?在背景執行,啟動後僅播放一次,並自動執行
ChatGPT 說:
ChatGPT
在 Windows 11 中,若要透過 PowerShell 使用內建的 Windows Media Player 播放 MP3 音檔並設置為背景執行,可以使用以下步驟:
我們可以利用 Start-Process 命令來執行 Windows Media Player 並指定要播放的 MP3 音檔。Windows Media Player 是內建的應用程式,通常它位於 C:\Program Files (x86)\Windows Media Player\wmplayer.exe。
以下是具體的 PowerShell 指令:
powershell
複製程式碼
Start-Process "C:\Program Files (x86)\Windows Media Player\wmplayer.exe" "C:\path\to\your\file.mp3"
請將 "C:\path\to\your\file.mp3" 替換為實際的 MP3 文件路徑。
要讓媒體播放器在背景執行並且只播放一次,可以加上 /play 參數,並且用 -WindowStyle Hidden 隱藏窗口讓其在背景運行。這樣,播放器啟動後將自動播放一次並且不會顯示在畫面上。
完整的指令如下:
powershell
複製程式碼
Start-Process "C:\Program Files (x86)\Windows Media Player\wmplayer.exe" -ArgumentList "C:\path\to\your\file.mp3" -WindowStyle Hidden
如果你希望這個指令在系統啟動時自動執行,可以將這個指令加入到 Windows 的啟動資料夾中,或者使用計劃任務來執行它。
透過「啟動」資料夾:
按 Win + R,輸入 shell:startup,並按 Enter,這會開啟啟動資料夾。
在此資料夾中建立一個新的 PowerShell 腳本檔案(.ps1),並將上述指令放入其中。
這樣系統啟動時就會自動執行該腳本。
透過計劃任務:
打開「任務排程器」 (Task Scheduler)。
創建一個新任務,選擇「在登入時」觸發,並設定動作為「啟動程式」,指向你的 PowerShell 腳本檔案。
這樣每次開機後,該 MP3 檔案就會在背景播放。
這樣就可以透過 PowerShell 在 Windows 11 中使用 Windows Media Player 播放 MP3 音檔,並且設置為背景執行,實現自動播放的功能。
接上,用 PowerShell 還能給不同的播放方式的範例嗎?如果要再加上依系統時間執行,要如何修正指令?
ChatGPT 說:
ChatGPT
當你在 PowerShell 中控制 Windows Media Player 時,除了基本的播放指令,還可以根據需求設定不同的播放模式,例如隨機播放、循環播放等。此外,如果你希望根據系統時間來執行某些操作,也可以進行一些時間判斷和設定。
以下是一些進階範例,展示如何根據不同需求進行播放設置並且依時間執行。
Windows Media Player 支援多種播放模式,雖然無法直接在命令行中設置所有播放模式(如循環播放或隨機播放),但是你可以在播放過程中使用一些簡單的控制選項。這些控制通常需要通過程式內部的操作來實現。
例 1: 隨機播放
要啟動 Windows Media Player 並播放一個 MP3 音檔,你可以設置隨機播放(但需要手動設置或透過腳本在播放器內控制)。若沒有原生命令行選項,這部分功能可能需要透過 COM 物件或 WMP API 來實現。以下為基本播放指令,隨機播放需要手動或程式內部控制。
powershell
複製程式碼
Start-Process "C:\Program Files (x86)\Windows Media Player\wmplayer.exe" "C:\path\to\your\file.mp3" -WindowStyle Hidden
例 2: 循環播放
同樣地,循環播放也無法直接從命令行設置,你可以手動在 Windows Media Player 內部啟用這個功能,或者用 COM 物件控制。以下是透過 PowerShell 控制 Windows Media Player 的範例,這樣你可以編程控制播放器。
powershell
複製程式碼
$WMP = New-Object -ComObject WMPlayer.OCX
$WMP.URL = "C:\path\to\your\file.mp3"
$WMP.Controls.play()
# 啟用循環播放
$WMP.settings.setMode("loop", $true)
如果你想要根據系統時間來決定是否執行播放,可以透過 PowerShell 輕鬆實現。舉例來說,如果你想在每天的某個時間(例如 9:00 AM)自動播放音樂,可以設置以下腳本:
例 3: 設定在特定時間執行
這段腳本會檢查當前時間,當時間到達 9:00 AM 時自動執行播放音樂的指令。
powershell
複製程式碼
# 獲取當前時間
$currentTime = Get-Date
# 設定想要執行的時間 (例如 9:00 AM)
$targetTime = (Get-Date).Date.AddHours(9)
# 計算間隔時間
$interval = $targetTime - $currentTime
# 若目標時間已過,則將時間設定為明天
if ($interval.TotalSeconds -lt 0) {
$interval = $targetTime.AddDays(1) - $currentTime
}
# 等待直到目標時間
Start-Sleep -Seconds $interval.TotalSeconds
# 播放音樂
Start-Process "C:\Program Files (x86)\Windows Media Player\wmplayer.exe" "C:\path\to\your\file.mp3" -WindowStyle Hidden
如果你希望自動化整個過程,並讓 PowerShell 在某個特定時間自動執行 MP3 播放,你可以利用 Windows 的「任務排程器」來達成。這樣,系統會依照設定的時間來執行指定的 PowerShell 腳本。
以下是如何透過 PowerShell 設定一個計劃任務,讓它在每天的 9:00 AM 播放 MP3 檔案:
powershell
複製程式碼
$Action = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "C:\path\to\your\script.ps1"
$Trigger = New-ScheduledTaskTrigger -Daily -At "9:00AM"
$Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Description "Play MP3 file at 9:00 AM"
Register-ScheduledTask -TaskName "PlayMP3At9AM" -InputObject $Task
這樣,script.ps1 會在每天的 9:00 AM 自動執行,並播放指定的 MP3 音檔。
如果你希望對 Windows Media Player 進行更細緻的控制(例如設定音量、播放進度等),你可以使用 COM 物件來操作播放器。以下是更多的範例:
控制音量
powershell
複製程式碼
$WMP.settings.volume = 50 # 設定音量為 50%
控制播放進度
powershell
複製程式碼
$WMP.Controls.currentPosition = 30 # 跳至播放 30 秒的位置
這樣,你就可以利用 PowerShell 自訂控制 Windows Media Player 的行為,並且根據系統時間來執行不同的操作。
這些範例展示了如何使用 PowerShell 控制 Windows Media Player 播放音樂、設置播放模式(如隨機播放、循環播放),以及如何根據系統時間來安排執行。在實際操作中,你可以根據需要調整腳本的邏輯,來達成自動化播放的目標。
接上,如果想要使用python語言,使用UI界面,讓使用者可以選擇音檔(mp3)並列表呈現,讓使用者可以指定時間(HH:MM),加上星期一、星期二~星欺日讓使用者可以自行勾選後,建立播放列表,並依播放列表自動執行,要如何處理?
ChatGPT 說:
ChatGPT
要實現這個需求,你可以使用 Python 搭配 GUI 库(如 tkinter)來設計一個簡單的用戶界面,讓使用者選擇 MP3 音檔,指定播放時間和日期(包括每週的選擇),並自動建立播放列表。這樣,用戶可以在指定的時間自動播放音樂,並按照播放列表中的設置進行播放。
GUI 介面:
使用 tkinter 設計一個簡單的用戶界面,讓使用者選擇 MP3 音檔、設置時間、選擇日期(星期一到星期日)。
顯示播放列表,顯示用戶選擇的 MP3 檔案與時間。
選擇音檔:
使用 tkinter.filedialog 讓使用者選擇 MP3 檔案。
設置時間和日期:
使用 tkinter 中的 Entry 或 Spinbox 控件讓用戶輸入播放時間(HH:MM 格式)。
使用勾選框 (Checkbutton) 來讓用戶選擇每週的星期幾。
自動執行:
根據用戶的設置,生成播放任務,並且利用 Windows 的「計劃任務」來在指定時間和日期執行播放。
播放功能:
使用 pygame 或 winsound 库來播放音樂。
以下是實現該需求的 Python 程式碼範例:
python
複製程式碼
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=10)
self.select_button = tk.Button(root, text="選擇檔案", command=self.select_music)
self.select_button.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=10)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 日期選擇區
self.weekdays_label = tk.Label(root, text="選擇播放的日期:")
self.weekdays_label.pack(pady=5)
self.weekdays = {
"Monday": tk.BooleanVar(),
"Tuesday": tk.BooleanVar(),
"Wednesday": tk.BooleanVar(),
"Thursday": tk.BooleanVar(),
"Friday": tk.BooleanVar(),
"Saturday": tk.BooleanVar(),
"Sunday": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(root, text=day, variable=var)
cb.pack()
self.weekday_checkbuttons.append(cb)
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_path_label.config(text=file_path)
def add_to_playlist(self):
music_path = self.music_path_label.cget("text")
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)}")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 加入排程
scheduler.enter(delay, 1, self.play_music, (item["music"],))
# 開始排程
threading.Thread(target=scheduler.run).start()
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
選擇音檔:使用 filedialog.askopenfilename 讓使用者選擇 MP3 音檔。
時間和日期設定:用 Entry 讓使用者輸入時間,並用 Checkbutton 讓使用者選擇每週的日期。
建立播放清單:將選擇的 MP3 音檔、時間和日期加到播放清單中,並顯示在 Listbox 中。
自動執行排程:使用 sched.scheduler 和 threading 計劃和運行排程,根據用戶的設定自動播放音樂。
用戶選擇音檔後,指定播放時間,並選擇要播放的星期。
透過「加入播放清單」按鈕,將設置的 MP3 音檔與時間、日期添加到播放清單。
使用「開始排程」按鈕,根據用戶選擇的日期與時間,自動排程播放音樂。
如果需要更複雜的功能,如循環播放、隨機播放等,可以透過 pygame.mixer.music 提供的其他方法來實現。
你也可以在這個基礎上加強音樂控制(如音量、暫停、停止等),或者將音樂播放紀錄儲存到檔案中以便未來查看。
這個範例展示了如何透過 Python 和 tkinter GUI 組件創建一個音樂排程器,並根據指定的時間和日期自動播放音樂。
使用繁體中文,在win11下,在powershell下,要如何使用利用建內的mediaplayer,使用指令來播放mp3的音檔?在背景執行,啟動後僅播放一次,並自動執行 ,用 PowerShell 還能給不同的播放方式的範例嗎?如果要再加上依系統時間執行,要如何修正指令? ,如果想要使用python語言,使用UI界面,讓使用者可以選擇音檔(mp3),選擇後可以列表呈現且且新增或刪除選擇的mp3檔,接著讓使用者可以指定時間(HH:MM),加上星期一、星期二~星欺日讓使用者可以自行勾選後,建立播放列表,並依播放列表自動執行,希望增加"刪除定時"、"清空排程"、"保存已列表排程"、"載入已列表排程",要如何處理?
接上,"# 音樂檔案選擇區",選擇完後的音檔,希望可以有一個文字框完成呈現並列表,且可以有刪除音檔的功能,要如何修正程式?
ChatGPT 說:
ChatGPT
為了實現選擇音檔後的顯示與刪除功能,我們可以對原本的程式進行一些修改,讓音檔列表呈現於一個 Listbox 中,並且為每個音檔加上刪除按鈕。這樣,用戶可以選擇刪除不需要的音檔。
音檔選擇區:選擇音檔後,將其顯示在 Listbox 中,而不是僅顯示單一音檔路徑。
刪除功能:在 Listbox 中加入「刪除」按鈕,讓用戶能夠從播放清單中移除選定的音檔。
python
複製程式碼
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=10)
self.select_button = tk.Button(root, text="選擇檔案", command=self.select_music)
self.select_button.pack(pady=5)
# 顯示選擇的音檔
self.music_listbox = tk.Listbox(root, height=5, width=50)
self.music_listbox.pack(pady=10)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 日期選擇區
self.weekdays_label = tk.Label(root, text="選擇播放的日期:")
self.weekdays_label.pack(pady=5)
self.weekdays = {
"Monday": tk.BooleanVar(),
"Tuesday": tk.BooleanVar(),
"Wednesday": tk.BooleanVar(),
"Thursday": tk.BooleanVar(),
"Friday": tk.BooleanVar(),
"Saturday": tk.BooleanVar(),
"Sunday": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(root, text=day, variable=var)
cb.pack()
self.weekday_checkbuttons.append(cb)
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
# 刪除選定音檔的按鈕
self.delete_button = tk.Button(root, text="刪除選定音檔", command=self.delete_selected_music)
self.delete_button.pack(pady=5)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path) # 將音檔加入 listbox
def delete_selected_music(self):
selected_index = self.music_listbox.curselection()
if selected_index:
self.music_listbox.delete(selected_index) # 刪除選中的音檔
else:
messagebox.showwarning("警告", "請選擇要刪除的音檔!")
def add_to_playlist(self):
selected_music = self.music_listbox.get(0, tk.END) # 獲取所有選擇的音檔路徑
time_str = self.time_entry.get()
if not selected_music:
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
for music in selected_music:
self.playlist.append({
"music": music,
"time": time_str,
"days": selected_days
})
# 更新播放清單顯示
self.update_playlist_display()
def update_playlist_display(self):
# 清空播放清單顯示
self.playlist_listbox.delete(0, tk.END)
# 更新播放清單
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])}")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 加入排程
scheduler.enter(delay, 1, self.play_music, (item["music"],))
# 開始排程
threading.Thread(target=scheduler.run).start()
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
音樂檔案選擇區 (select_music 方法):
用 filedialog.askopenfilename 選擇音檔後,將音檔路徑加入到 Listbox 中,並顯示在界面上。
刪除選定音檔功能 (delete_selected_music 方法):
Listbox 允許用戶選擇音檔,並通過「刪除選定音檔」按鈕將其從 Listbox 中刪除。
如果用戶未選擇音檔,會顯示警告消息。
播放清單更新 (add_to_playlist 和 update_playlist_display 方法):
當用戶添加音檔到播放清單後,會將音檔路徑、時間和日期顯示在 Listbox 中。
每次更新播放清單時,會清空 Listbox 並顯示當前播放清單的內容。
刪除選定音檔的功能:
用戶可以在 Listbox 中選擇一個或多個音檔,並通過「刪除選定音檔」按鈕將其移除。
所有選擇的音檔會顯示在 Listbox 中。用戶可以在播放清單中查看所有添加的音檔,並可以隨時刪除不需要的音檔。
這個程式能夠讓用戶選擇音檔、設置播放時間和日期,並且能夠查看和管理播放清單(包括刪除不需要的音檔)。用戶可以根據需求創建播放排程,並自動在指定的時間播放音樂。
接上,程式執行後的畫面大小,希望成設定成1024*768,要如何修正?
ChatGPT 說:
ChatGPT
要設定 Python 的 tkinter 視窗大小為 1024x768,可以使用 root.geometry() 方法來指定視窗的尺寸。這個方法允許你設定視窗的寬度和高度。
在你的程式中,將 root.geometry() 方法加在主視窗初始化的地方即可。
在 __init__ 方法中,視窗創建後添加 self.root.geometry("1024x768") 來設定視窗的大小。
python
複製程式碼
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
# 設定視窗大小為 1024x768
self.root.geometry("1024x768")
self.playlist = [] # 播放清單
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=10)
self.select_button = tk.Button(root, text="選擇檔案", command=self.select_music)
self.select_button.pack(pady=5)
# 顯示選擇的音檔
self.music_listbox = tk.Listbox(root, height=5, width=50)
self.music_listbox.pack(pady=10)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 日期選擇區
self.weekdays_label = tk.Label(root, text="選擇播放的日期:")
self.weekdays_label.pack(pady=5)
self.weekdays = {
"Monday": tk.BooleanVar(),
"Tuesday": tk.BooleanVar(),
"Wednesday": tk.BooleanVar(),
"Thursday": tk.BooleanVar(),
"Friday": tk.BooleanVar(),
"Saturday": tk.BooleanVar(),
"Sunday": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(root, text=day, variable=var)
cb.pack()
self.weekday_checkbuttons.append(cb)
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
# 刪除選定音檔的按鈕
self.delete_button = tk.Button(root, text="刪除選定音檔", command=self.delete_selected_music)
self.delete_button.pack(pady=5)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path) # 將音檔加入 listbox
def delete_selected_music(self):
selected_index = self.music_listbox.curselection()
if selected_index:
self.music_listbox.delete(selected_index) # 刪除選中的音檔
else:
messagebox.showwarning("警告", "請選擇要刪除的音檔!")
def add_to_playlist(self):
selected_music = self.music_listbox.get(0, tk.END) # 獲取所有選擇的音檔路徑
time_str = self.time_entry.get()
if not selected_music:
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
for music in selected_music:
self.playlist.append({
"music": music,
"time": time_str,
"days": selected_days
})
# 更新播放清單顯示
self.update_playlist_display()
def update_playlist_display(self):
# 清空播放清單顯示
self.playlist_listbox.delete(0, tk.END)
# 更新播放清單
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])}")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 加入排程
scheduler.enter(delay, 1, self.play_music, (item["music"],))
# 開始排程
threading.Thread(target=scheduler.run).start()
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
設置視窗大小:
在 __init__ 方法中的 self.root 設定視窗大小:self.root.geometry("1024x768")。這將使程式啟動後的視窗大小固定為 1024x768。
當你執行這段程式碼時,應用程式的視窗會顯示為 1024x768 的尺寸,並根據這個大小來顯示和操作其他 GUI 元素。
這樣就能確保視窗在啟動時會顯示為預設的大小 1024x768,適合在多數螢幕上顯示且不會過大或過小。
我們需要選擇一個佈局管理器,並且在整個父容器中統一使用。可以選擇將日期選擇區的佈局方式從 grid 改為 pack,或者將整個界面的佈局方式統一改為 grid。
方法 1:將日期選擇區改為 pack
這個方法最簡單,只需要將原來的 grid 改為 pack,並稍作調整。
python
複製程式碼
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=10)
self.select_button = tk.Button(root, text="選擇檔案", command=self.select_music)
self.select_button.pack(pady=5)
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=10)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame, height=10, width=50)
self.music_listbox.pack(side=tk.LEFT)
self.scrollbar = tk.Scrollbar(self.music_listbox_frame)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.music_listbox.yview)
# 刪除按鈕
self.delete_button = tk.Button(root, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(pady=5)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 日期選擇區,改為使用 pack 排列
self.weekdays_label = tk.Label(root, text="選擇播放的日期:")
self.weekdays_label.pack(pady=5)
self.weekdays = {
"Monday": tk.BooleanVar(),
"Tuesday": tk.BooleanVar(),
"Wednesday": tk.BooleanVar(),
"Thursday": tk.BooleanVar(),
"Friday": tk.BooleanVar(),
"Saturday": tk.BooleanVar(),
"Sunday": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(root, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 改為使用 pack 來排成一行
# 播放方式選擇區
self.playback_mode_label = tk.Label(root, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(root, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w")
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
# 開始排程
threading.Thread(target=scheduler.run).start()
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
日期選擇區改為使用 pack 排列:
Checkbutton 控制元件的排列方式由 grid 改為 pack,並使用 side=tk.LEFT 來讓所有日期選擇按鈕排列在一行中。
這樣,您就不會再碰到 pack 和 grid 混用的錯誤了。如果您希望使用 grid 排列整個視窗,可以將所有 pack 佈局的部分改為 grid,並確保所有部件都使用 grid 佈局。
希望這樣的修改能解決您的問題!如果還有其他問題,隨時告訴我!
1140108-schoolmusicplay-03-05.py
還需要改進的地方
-
-
-
-
-
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=10)
self.select_button = tk.Button(root, text="選擇檔案", command=self.select_music)
self.select_button.pack(pady=5)
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=12)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame, height=10, width=50)
self.music_listbox.pack(side=tk.LEFT)
self.music_listbox.pack(fill=tk.X)
self.scrollbar = tk.Scrollbar(self.music_listbox_frame)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.music_listbox.yview)
# 刪除按鈕
self.delete_button = tk.Button(root, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(pady=5)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=5)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"Monday": tk.BooleanVar(),
"Tuesday": tk.BooleanVar(),
"Wednesday": tk.BooleanVar(),
"Thursday": tk.BooleanVar(),
"Friday": tk.BooleanVar(),
"Saturday": tk.BooleanVar(),
"Sunday": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
# 開始排程
threading.Thread(target=scheduler.run).start()
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
要在現有的程式中加入「清空排程」、「保存設定檔」和「載入設定檔」的功能,以及顯示當前日期時間,並且保持原有功能,您可以按照以下步驟進行修改:
這些按鈕需要放在同一個 Frame 中,並且在這個區域下方顯示當前排程的狀況。
可以用 Label 顯示當前日期和時間,並定期更新它。
需要在程式中加入一些新的功能方法來實現這些需求。
python
複製程式碼
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
import json
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
# 顯示當前日期和時間
self.datetime_label = tk.Label(root, text="", font=("Arial", 12))
self.datetime_label.pack(pady=5)
self.update_datetime()
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=15) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=5)
# 創建一個用來顯示音樂列表的 Frame
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 創建一個 Frame 來放「開始排程」、「清空排程」、「保存設定檔」和「載入設定檔」的按鈕
schedule_buttons_frame = tk.Frame(root)
schedule_buttons_frame.pack(pady=10)
# 開始排程按鈕
self.start_button = tk.Button(schedule_buttons_frame, text="開始排程", command=self.start_scheduling)
self.start_button.pack(side="left", padx=5)
# 清空排程按鈕
self.clear_button = tk.Button(schedule_buttons_frame, text="清空排程", command=self.clear_schedule)
self.clear_button.pack(side="left", padx=5)
# 保存設定檔按鈕
self.save_button = tk.Button(schedule_buttons_frame, text="保存設定檔", command=self.save_schedule)
self.save_button.pack(side="left", padx=5)
# 載入設定檔按鈕
self.load_button = tk.Button(schedule_buttons_frame, text="載入設定檔", command=self.load_schedule)
self.load_button.pack(side="left", padx=5)
# 顯示當前排程
self.current_schedule_label = tk.Label(root, text="當前排程:")
self.current_schedule_label.pack(pady=5)
self.schedule_text = tk.Text(root, height=10, width=50)
self.schedule_text.pack(pady=5)
def update_datetime(self):
"""更新當前日期時間"""
now = datetime.now()
current_datetime = now.strftime("%Y-%m-%d %H:%M-%A")
self.datetime_label.config(text=current_datetime)
self.root.after(60000, self.update_datetime) # 每分鐘更新一次
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
# 開始排程
threading.Thread(target=scheduler.run).start()
def clear_schedule(self):
"""清空排程"""
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END)
self.schedule_text.delete(1.0, tk.END)
def save_schedule(self):
"""保存排程為 JSON 檔案"""
schedule_data = self.playlist
file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(schedule_data, f, ensure_ascii=False, indent=4)
def load_schedule(self):
"""載入 JSON 設定檔"""
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
schedule_data = json.load(f)
self.playlist = schedule_data
self.playlist_listbox.delete(0, tk.END)
for item in schedule_data:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
self.update_schedule_text()
def update_schedule_text(self):
"""更新排程顯示區"""
self.schedule_text.delete(1.0, tk.END)
for item in self.playlist:
self.schedule_text.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})\n")
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
新增顯示當前日期時間: 每分鐘更新一次當前日期和時間。
排程功能:
清空排程: 清除當前排程列表。
保存設定檔: 以 JSON 格式儲存排程設定。
載入設定檔: 從 JSON 文件讀取排程設定並顯示在播放清單中。
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
import json
import random
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
self.last_schedule_info = "" # 存儲最近一個排程的信息
# 顯示當前日期時間
self.datetime_label = tk.Label(root, text=self.get_current_datetime())
self.datetime_label.pack(anchor="ne", padx=10, pady=10) # 靠右顯示
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=15) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=5)
# 創建一個用來顯示音樂列表的 Frame
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 排程操作區 (清空排程、保存設定檔、載入設定檔)
scheduler_frame = tk.Frame(root)
scheduler_frame.pack(pady=15)
# 清空排程按鈕
self.clear_button = tk.Button(scheduler_frame, text="清空排程", command=self.clear_schedule)
self.clear_button.pack(side="left", padx=10)
# 保存設定檔按鈕
self.save_button = tk.Button(scheduler_frame, text="保存設定檔", command=self.save_schedule)
self.save_button.pack(side="left", padx=10)
# 載入設定檔按鈕
self.load_button = tk.Button(scheduler_frame, text="載入設定檔", command=self.load_schedule)
self.load_button.pack(side="left", padx=10)
# 顯示最近一個排程的狀況
self.status_label = tk.Label(root, text="最近排程狀況:尚未排程")
self.status_label.pack(pady=5)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
# 開始排程
threading.Thread(target=scheduler.run).start()
# 更新最近排程狀況
self.last_schedule_info = f"音樂:{self.playlist[-1]['music']},時間:{self.playlist[-1]['time']},播放模式:{self.playlist[-1]['playback_mode']}"
self.status_label.config(text=f"最近排程狀況:{self.last_schedule_info}")
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
def clear_schedule(self):
# 清空排程
global scheduler
scheduler = sched.scheduler(time.time, time.sleep)
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END)
self.status_label.config(text="最近排程狀況:尚未排程")
def save_schedule(self):
# 保存排程到 JSON 檔案
schedule_data = {
"playlist": self.playlist
}
with filedialog.asksaveasfile(defaultextension=".json", filetypes=[("JSON files", "*.json")]) as f:
if f:
json.dump(schedule_data, f, ensure_ascii=False, indent=4)
def load_schedule(self):
# 載入排程設定檔
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
schedule_data = json.load(f)
self.playlist = schedule_data.get("playlist", [])
self.playlist_listbox.delete(0, tk.END)
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
self.status_label.config(text="最近排程狀況:載入設定檔")
def get_current_datetime(self):
# 取得並格式化當前日期時間
return datetime.now().strftime("%Y-%m-%d %H:%M-%A")
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
1140113-問
登入
註冊
你說:
使用繁體中文,以下是可以正常執行的程式,"import tkinter as tk from tkinter import filedialog from tkinter import messagebox import pygame import os import time import sched import threading from datetime import datetime, timedelta # 初始化 pygame 音樂模塊 pygame.mixer.init() # 計劃排程器 scheduler = sched.scheduler(time.time, time.sleep) # 主視窗 class MusicSchedulerApp: def __init__(self, root): self.root = root self.root.title("Music Scheduler") self.playlist = [] # 播放清單 # 音樂檔案選擇區 self.music_label = tk.Label(root, text="選擇 MP3 音檔") self.music_label.pack(pady=5) # 創建一個框架 (Frame) 用來包住兩個按鈕 button_frame = tk.Frame(root) button_frame.pack(pady=15) # 設定間距 # 選擇音樂檔案按鈕 self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music) self.select_button.pack(side="left", padx=10) # 設定左右間距 # 刪除選擇音檔按鈕 self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music) self.delete_button.pack(side="left", padx=10) # 設定左右間距 self.music_listbox_frame = tk.Frame(root) self.music_listbox_frame.pack(pady=5) self.music_path_label = tk.Label(root, text="尚未選擇音檔") self.music_path_label.pack(pady=5) # 創建一個用來顯示音樂列表的 Frame self.music_listbox_frame = tk.Frame(root) self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 音樂列表顯示區 self.music_listbox = tk.Listbox(self.music_listbox_frame) self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動 # 創建一個滾動條 self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿 # 配置 Listbox 讓滾動條控制其垂直滾動 self.music_listbox.config(yscrollcommand=self.scrollbar.set) # 時間設定區 self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00") self.time_label.pack(pady=5) self.time_entry = tk.Entry(root) self.time_entry.pack(pady=5) # 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」 self.date_and_mode_frame = tk.Frame(root) self.date_and_mode_frame.pack(padx=10, pady=10) # 「選擇播放的日期」標籤 self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:") self.weekdays_label.pack(pady=1) # 設置日期選擇的 Checkbutton self.weekdays = { "星期一": tk.BooleanVar(), "星期二": tk.BooleanVar(), "星期三": tk.BooleanVar(), "星期四": tk.BooleanVar(), "星期五": tk.BooleanVar(), "星期六": tk.BooleanVar(), "星期日": tk.BooleanVar() } self.weekday_checkbuttons = [] for day, var in self.weekdays.items(): cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var) cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距 # 在同一 Frame 中放置「選擇播放方式」標籤 self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:") self.playback_mode_label.pack(pady=5) # 播放方式選擇的 Radiobutton self.playback_modes = ["定時播放", "隨機播放", "循環播放"] self.playback_mode_var = tk.StringVar(value=self.playback_modes[0]) for mode in self.playback_modes: rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode) rb.pack(anchor="w") # 使用左對齊來顯示選項 # 加入播放清單按鈕 self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist) self.add_button.pack(pady=10) # 顯示播放清單 self.playlist_label = tk.Label(root, text="播放清單") self.playlist_label.pack(pady=5) self.playlist_listbox = tk.Listbox(root, height=10, width=50) self.playlist_listbox.pack(pady=5) self.playlist_listbox.pack(fill=tk.BOTH) # 開始執行按鈕 self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling) self.start_button.pack(pady=10) def select_music(self): file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")]) if file_path: self.music_listbox.insert(tk.END, file_path) def delete_music(self): selected_music = self.music_listbox.curselection() if selected_music: self.music_listbox.delete(selected_music) def add_to_playlist(self): music_path = self.music_listbox.get(tk.ACTIVE) time_str = self.time_entry.get() if not music_path or not os.path.exists(music_path): messagebox.showerror("錯誤", "請選擇有效的音檔!") return if not time_str: messagebox.showerror("錯誤", "請輸入播放時間!") return # 檢查時間格式 try: datetime.strptime(time_str, "%H:%M") except ValueError: messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!") return selected_days = [day for day, var in self.weekdays.items() if var.get()] if not selected_days: messagebox.showerror("錯誤", "請選擇至少一個播放日期!") return # 加入播放清單 self.playlist.append({ "music": music_path, "time": time_str, "days": selected_days, "playback_mode": self.playback_mode_var.get() }) self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})") def start_scheduling(self): if not self.playlist: messagebox.showerror("錯誤", "播放清單為空!") return # 建立排程任務 for item in self.playlist: for day in item["days"]: # 計算從今天到目標日期的差異 target_time_str = item["time"] target_time = datetime.strptime(target_time_str, "%H:%M").time() # 計算目標時間對應的日期 current_time = datetime.now() days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7 target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff) delay = (target_datetime - current_time).total_seconds() # 根據播放模式調整排程 if item["playback_mode"] == "定時播放": scheduler.enter(delay, 1, self.play_music, (item["music"],)) elif item["playback_mode"] == "隨機播放": delay += random.randint(0, 3600) # 隨機延遲最多 1 小時 scheduler.enter(delay, 1, self.play_music, (item["music"],)) elif item["playback_mode"] == "循環播放": scheduler.enter(delay, 1, self.loop_music, (item["music"], delay)) # 開始排程 threading.Thread(target=scheduler.run).start() def play_music(self, music_path): pygame.mixer.music.load(music_path) pygame.mixer.music.play() while pygame.mixer.music.get_busy(): time.sleep(1) def loop_music(self, music_path, delay): pygame.mixer.music.load(music_path) pygame.mixer.music.play(-1) # 循環播放 while pygame.mixer.music.get_busy(): time.sleep(1) # 啟動應用程式 if __name__ == "__main__": root = tk.Tk() app = MusicSchedulerApp(root) root.mainloop()",在不更換原程式功能狀況下,希望在"開始排程"的右側加上一個"刪除選擇的清單"的功能,這個功能要把選定的清單刪除,希望在"開始排程"的右側加上一個"清空目前排程"的功能,這個功能要把現在已執行排程的部份刪除,在"清空排程"的右側增加"保存設定檔"的功能(希望用jsno的格式匯出),在"保存設定檔"的右側增加"載入設定檔",把json檔匯入,將新增加4個按鈕功能跟"開始排程"在最左側用frame的方式排成一排;在這排的下方,用label的方式呈現現在開始排程後,顯示準備要播收的排程狀況,另在畫面"選擇MP3音檔"的上面靠右對齊,能顯示現在的日期時間,如"2025-01-13 11:00-星期一(要用中文呈現)",,要如何修正程式?
要實現您要求的功能,可以對原程式做一些修改,主要涉及新增幾個按鈕功能並對排程進行相應的操作。此外,您還希望顯示當前日期和時間,並將按鈕排成一排並進行格式化。以下是如何實現這些功能的詳細步驟:
我們需要新增「刪除選擇的清單」、「清空目前排程」、「保存設定檔」、「載入設定檔」四個按鈕,並且在按鈕排成一排後,在下方顯示目前的排程狀況。
在畫面右上角顯示當前日期和時間,並且格式化為 YYYY-MM-DD HH:MM-星期X 的格式。
python
複製程式碼
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import pygame
import os
import time
import sched
import threading
from datetime import datetime, timedelta
import json
import random
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
# 顯示當前日期和時間
self.current_datetime_label = tk.Label(root, text=self.get_current_datetime(), font=("Arial", 10))
self.current_datetime_label.pack(anchor="ne", padx=10, pady=5) # 靠右上顯示
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=15) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=5)
# 創建一個用來顯示音樂列表的 Frame
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 新增的功能按鈕
control_frame = tk.Frame(root)
control_frame.pack(pady=10)
# 刪除選擇的清單
self.delete_playlist_button = tk.Button(control_frame, text="刪除選擇的清單", command=self.delete_selected_playlist)
self.delete_playlist_button.pack(side="left", padx=5)
# 清空目前排程
self.clear_schedule_button = tk.Button(control_frame, text="清空目前排程", command=self.clear_schedule)
self.clear_schedule_button.pack(side="left", padx=5)
# 保存設定檔
self.save_button = tk.Button(control_frame, text="保存設定檔", command=self.save_schedule)
self.save_button.pack(side="left", padx=5)
# 載入設定檔
self.load_button = tk.Button(control_frame, text="載入設定檔", command=self.load_schedule)
self.load_button.pack(side="left", padx=5)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
# 顯示排程狀況
self.schedule_status_label = tk.Label(root, text="排程狀況: 尚未開始排程", font=("Arial", 10))
self.schedule_status_label.pack(pady=10)
def get_current_datetime(self):
# 顯示當前日期和時間
current_time = datetime.now()
weekday_name = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
return f"{current_time.strftime('%Y-%m-%d %H:%M')}-{weekday_name[current_time.weekday()]}"
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
# 開始排程
threading.Thread(target=scheduler.run).start()
# 更新排程狀況顯示
self.schedule_status_label.config(text="排程狀況: 排程中...")
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
def delete_selected_playlist(self):
selected = self.playlist_listbox.curselection()
if selected:
self.playlist_listbox.delete(selected)
del self.playlist[selected[0]]
def clear_schedule(self):
global scheduler
scheduler = sched.scheduler(time.time, time.sleep) # 重設排程器
self.schedule_status_label.config(text="排程狀況: 排程已清空")
def save_schedule(self):
with open("schedule.json", "w", encoding="utf-8") as f:
json.dump(self.playlist, f, ensure_ascii=False, indent=4)
messagebox.showinfo("保存設定", "設定已成功保存到 schedule.json")
def load_schedule(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
self.playlist = json.load(f)
self.playlist_listbox.delete(0, tk.END)
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()
新增的四個功能按鈕:
刪除選擇的清單: 刪除播放清單中的選定項目。
清空目前排程: 清空排程。
保存設定檔: 將當前的排程以 JSON 格式保存到檔案中。
載入設定檔: 從 JSON 檔案中載入排程設定。
顯示當前日期和時間: 在主視窗的右上角顯示當前的日期和時間,格式為 YYYY-MM-DD HH:MM-星期X。
排程狀況顯示: 在開始排程後,顯示目前的排程狀況。
排程器重設: clear_schedule 按鈕會清空當前的排程。
保存和載入設定檔: 以 JSON 格式保存和載入排程設定。
1140113-1140108-schoolmusicplay-03-13.py
-已知問題,當"當前排程的狀況",不會同步更新,需處理
--清空排程是正常的
--
為了讓「清空目前排程」按鈕在清空排程後更新排程狀況顯示,你需要在 clear_schedule 方法裡面,除了清空排程任務和排程列表外,還要更新排程狀況顯示的文字,顯示「目前沒有排程」或是相應的清空訊息。
以下是修改過的 clear_schedule 方法,包含更新排程狀況顯示:
python
複製程式碼
def clear_schedule(self):
# 取消所有排程任務
for task_id in self.schedule_task_ids[:]: # 迭代排程任務 ID 的副本
try:
scheduler.cancel(task_id) # 嘗試取消排程
except ValueError:
pass # 如果任務已經不存在,忽略該錯誤
self.schedule_task_ids.clear() # 清空任務 ID 列表
# 清空播放清單
self.playlist.clear()
self.playlist_listbox.delete(0, tk.END) # 清空 Listbox
# 更新排程狀況顯示
self.schedule_status_label.config(text="當前排程狀況:\n目前沒有排程")
messagebox.showinfo("提示", "已清空目前排程!")
清空播放清單:在清空排程後,播放清單 (self.playlist) 也會被清空,同時清空 playlist_listbox 內的所有項目。
更新排程狀況顯示:在 self.schedule_status_label 顯示「目前沒有排程」的訊息,確保用戶能夠看到當前沒有排程任務的狀況。
這樣當你點擊「清空目前排程」按鈕時,排程清單會被清空,並且排程狀況顯示區也會即時更新顯示為「目前沒有排程」。
import tkinter as tk
from tkinter import filedialog, messagebox
import pygame
import os
import time
import sched
import threading
import random
from datetime import datetime, timedelta
import json
# 初始化 pygame 音樂模塊
pygame.mixer.init()
# 計劃排程器
scheduler = sched.scheduler(time.time, time.sleep)
# 主視窗
class MusicSchedulerApp:
def __init__(self, root):
self.root = root
self.root.title("Music Scheduler")
self.playlist = [] # 播放清單
self.schedule_task_ids = [] # 儲存排程任務 ID,用於清除排程
# 當前日期時間顯示區
self.date_time_label = tk.Label(root, text="", font=("Arial", 12))
self.date_time_label.pack(anchor="ne", padx=10, pady=5)
# 音樂檔案選擇區
self.music_label = tk.Label(root, text="選擇 MP3 音檔")
self.music_label.pack(pady=5)
# 創建一個框架 (Frame) 用來包住兩個按鈕
button_frame = tk.Frame(root)
button_frame.pack(pady=15) # 設定間距
# 選擇音樂檔案按鈕
self.select_button = tk.Button(button_frame, text="選擇檔案", command=self.select_music)
self.select_button.pack(side="left", padx=10) # 設定左右間距
# 刪除選擇音檔按鈕
self.delete_button = tk.Button(button_frame, text="刪除選擇音檔", command=self.delete_music)
self.delete_button.pack(side="left", padx=10) # 設定左右間距
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(pady=5)
self.music_path_label = tk.Label(root, text="尚未選擇音檔")
self.music_path_label.pack(pady=5)
self.music_listbox_frame = tk.Frame(root)
self.music_listbox_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 音樂列表顯示區
self.music_listbox = tk.Listbox(self.music_listbox_frame)
self.music_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Listbox 填滿 Frame 並可隨視窗變動
# 創建一個滾動條
self.scrollbar = tk.Scrollbar(self.music_listbox_frame, orient=tk.VERTICAL, command=self.music_listbox.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 滾動條縱向填滿
# 配置 Listbox 讓滾動條控制其垂直滾動
self.music_listbox.config(yscrollcommand=self.scrollbar.set)
# 時間設定區
self.time_label = tk.Label(root, text="設置播放時間 (HH:MM):如12:00")
self.time_label.pack(pady=5)
self.time_entry = tk.Entry(root)
self.time_entry.pack(pady=5)
# 建立一個 Frame 來包裝「選擇播放的日期」和「選擇播放方式」
self.date_and_mode_frame = tk.Frame(root)
self.date_and_mode_frame.pack(padx=10, pady=10)
# 「選擇播放的日期」標籤
self.weekdays_label = tk.Label(self.date_and_mode_frame, text="選擇播放的日期:")
self.weekdays_label.pack(pady=1)
# 設置日期選擇的 Checkbutton
self.weekdays = {
"星期一": tk.BooleanVar(),
"星期二": tk.BooleanVar(),
"星期三": tk.BooleanVar(),
"星期四": tk.BooleanVar(),
"星期五": tk.BooleanVar(),
"星期六": tk.BooleanVar(),
"星期日": tk.BooleanVar()
}
self.weekday_checkbuttons = []
for day, var in self.weekdays.items():
cb = tk.Checkbutton(self.date_and_mode_frame, text=day, variable=var)
cb.pack(side=tk.LEFT, padx=5, pady=5) # 放置在同一行內並設置間距
# 在同一 Frame 中放置「選擇播放方式」標籤
self.playback_mode_label = tk.Label(self.date_and_mode_frame, text="選擇播放方式:")
self.playback_mode_label.pack(pady=5)
# 播放方式選擇的 Radiobutton
self.playback_modes = ["定時播放", "隨機播放", "循環播放"]
self.playback_mode_var = tk.StringVar(value=self.playback_modes[0])
for mode in self.playback_modes:
rb = tk.Radiobutton(self.date_and_mode_frame, text=mode, variable=self.playback_mode_var, value=mode)
rb.pack(anchor="w") # 使用左對齊來顯示選項
# 加入播放清單按鈕
self.add_button = tk.Button(root, text="加入播放清單", command=self.add_to_playlist)
self.add_button.pack(pady=10)
# 顯示播放清單
self.playlist_label = tk.Label(root, text="播放清單")
self.playlist_label.pack(pady=5)
self.playlist_listbox = tk.Listbox(root, height=10, width=50)
self.playlist_listbox.pack(pady=5)
self.playlist_listbox.pack(fill=tk.BOTH)
# 按鈕區 (包含新功能)
self.button_frame = tk.Frame(root)
self.button_frame.pack(pady=10)
# 新功能的按鈕
self.delete_selected_button = tk.Button(self.button_frame, text="刪除選擇的清單", command=self.delete_selected_playlist)
self.delete_selected_button.pack(side="left", padx=10)
self.clear_schedule_button = tk.Button(self.button_frame, text="清空目前排程", command=self.clear_schedule)
self.clear_schedule_button.pack(side="left", padx=10)
self.save_schedule_button = tk.Button(self.button_frame, text="保存設定檔", command=self.save_schedule)
self.save_schedule_button.pack(side="left", padx=10)
self.load_schedule_button = tk.Button(self.button_frame, text="載入設定檔", command=self.load_schedule)
self.load_schedule_button.pack(side="left", padx=10)
# 開始執行按鈕
self.start_button = tk.Button(root, text="開始排程", command=self.start_scheduling)
self.start_button.pack(pady=10)
# 排程狀況顯示區
self.schedule_status_label = tk.Label(root, text="當前排程狀況:", anchor="w")
self.schedule_status_label.pack(fill=tk.BOTH, padx=10, pady=5)
def select_music(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if file_path:
self.music_listbox.insert(tk.END, file_path)
def delete_music(self):
selected_music = self.music_listbox.curselection()
if selected_music:
self.music_listbox.delete(selected_music)
def add_to_playlist(self):
music_path = self.music_listbox.get(tk.ACTIVE)
time_str = self.time_entry.get()
if not music_path or not os.path.exists(music_path):
messagebox.showerror("錯誤", "請選擇有效的音檔!")
return
if not time_str:
messagebox.showerror("錯誤", "請輸入播放時間!")
return
# 檢查時間格式
try:
datetime.strptime(time_str, "%H:%M")
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的時間格式 (HH:MM)!")
return
selected_days = [day for day, var in self.weekdays.items() if var.get()]
if not selected_days:
messagebox.showerror("錯誤", "請選擇至少一個播放日期!")
return
# 加入播放清單
self.playlist.append({
"music": music_path,
"time": time_str,
"days": selected_days,
"playback_mode": self.playback_mode_var.get()
})
self.playlist_listbox.insert(tk.END, f"{music_path} @ {time_str} on {', '.join(selected_days)} ({self.playback_mode_var.get()})")
def start_scheduling(self):
if not self.playlist:
messagebox.showerror("錯誤", "播放清單為空!")
return
# 建立排程任務
self.schedule_task_ids.clear() # 清空已存在的任務ID
for item in self.playlist:
for day in item["days"]:
# 計算從今天到目標日期的差異
target_time_str = item["time"]
target_time = datetime.strptime(target_time_str, "%H:%M").time()
# 計算目標時間對應的日期
current_time = datetime.now()
days_diff = (list(self.weekdays.keys()).index(day) - current_time.weekday()) % 7
target_datetime = datetime.combine(current_time.date(), target_time) + timedelta(days=days_diff)
delay = (target_datetime - current_time).total_seconds()
# 根據播放模式調整排程
if item["playback_mode"] == "定時播放":
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "隨機播放":
delay += random.randint(0, 3600) # 隨機延遲最多 1 小時
task_id = scheduler.enter(delay, 1, self.play_music, (item["music"],))
elif item["playback_mode"] == "循環播放":
task_id = scheduler.enter(delay, 1, self.loop_music, (item["music"], delay))
self.schedule_task_ids.append(task_id)
# 開始排程
threading.Thread(target=scheduler.run).start()
# 更新排程狀況顯示
schedule_info = "\n".join([f"{item['music']} at {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})" for item in self.playlist])
self.schedule_status_label.config(text=f"當前排程狀況:\n{schedule_info}")
def play_music(self, music_path):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(1)
def loop_music(self, music_path, delay):
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # 循環播放
while pygame.mixer.music.get_busy():
time.sleep(1)
def delete_selected_playlist(self):
selected_index = self.playlist_listbox.curselection()
if selected_index:
selected_index = selected_index[0]
del self.playlist[selected_index]
self.playlist_listbox.delete(selected_index)
#def clear_schedule(self):
# for task_id in self.schedule_task_ids:
# scheduler.cancel(task_id)
# self.schedule_task_ids.clear()
#messagebox.showinfo("提示", "已清空目前排程!")
def clear_schedule(self):
# 取消所有排程任務
for task_id in self.schedule_task_ids[:]: # 迭代排程任務 ID 的副本
try:
scheduler.cancel(task_id) # 嘗試取消排程
except ValueError:
pass # 如果任務已經不存在,忽略該錯誤
self.schedule_task_ids.clear() # 清空任務 ID 列表
messagebox.showinfo("提示", "已清空目前排程!")
def save_schedule(self):
file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(self.playlist, f, ensure_ascii=False, indent=4)
messagebox.showinfo("提示", "設定檔已保存!")
def load_schedule(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, "r", encoding="utf-8") as f:
self.playlist = json.load(f)
self.playlist_listbox.delete(0, tk.END)
for item in self.playlist:
self.playlist_listbox.insert(tk.END, f"{item['music']} @ {item['time']} on {', '.join(item['days'])} ({item['playback_mode']})")
# 啟動應用程式
if __name__ == "__main__":
root = tk.Tk()
app = MusicSchedulerApp(root)
root.mainloop()