|

台語MP3播放器(Claude + Python):弓蕉(kin)的台語秘密

   
作者:Ted Lee

1979 年,在高雄某處的弓 蕉園裡,埋藏了一樁天大的秘密。根據當年幸運逃出的生還者口述,那片偌大弓蕉園的深處,由一條身長數尺的「Python」千年來一直守護著一件傳說中的上古兵器「Claude」。江湖中早有傳言:「得 Claude 者得天下。」也就是這樣,在那年的某個夜晚,一群群如海水般的人潮不斷地來到了弓蕉園,開始了一場腥風血雨的人間殺戮。到底是誰最後能找到 Claude 的隱身之處呢?能解開這個謎團的,據說是一位操有標準老泉腔的少年英雄……

用哪個 ChatGPT 來生?

世界上 GPT 百百款,為何我們獨挑 Claude 這枝花?其實,根據先前使用 Copilot 生成 micro:bit Python 程式碼的經驗,我們最早是在 Copilot 上下以下咒語來生的:

(人設)你是 Python 程式專家

(任務)Tkinter 視窗版的 MP3 播放器,選擇多首 MP3 曲目後,顯示在畫面上,連續隨機播放這些曲目不停止。正在播放的曲目要用顏色標記

(輸出)Python 程式碼,要含詳細註解,並生成 Mermaid 流程圖

但是,天不從人願。工具總是有其侷限性:有一好就不一定有二好。Copilot 生出的程式碼,再怎麼叫它修改就是一直不能「自動播放」。

正當準備放棄之際,突然靈光一現:把程式碼貼給其他版本的 GPT 來幫助抓蟲呢?

於是乎產出了以下符合需求的程式碼(在陳會安老師設計的 fChart Thonny 上的執行畫面如圖 1):

MP3 Player_Coipilot+Claude.py


import tkinter as tk

from tkinter import filedialog

import pygame

import random

import os

# 初始化 Pygame 的混音器

pygame.mixer.init()

# 建立主視窗

root = tk.Tk()

root.title("弓蕉園的秘密 字詞放送 Powered by 李俊德 Lí Tsùn-ti")

# 取得螢幕寬度和高度

screen_width = root.winfo_screenwidth()

screen_height = root.winfo_screenheight()

# 設定視窗大小為螢幕的 70%

window_width = int(screen_width * 0.7)

window_height = int(screen_height * 0.7)

# 計算視窗的初始位置,使其位於畫面的中上方

x_position = int((screen_width - window_width) / 2)

y_position = int(screen_height * 0.1) # 設定為螢幕高度的 10% 處

# 設定視窗的位置和大小

root.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")

# 儲存選擇的檔案路徑和播放狀態

file_paths =

current_index = 0

is_paused = False

is_playing = False

def select_files():

global file_paths, current_index

# 打開文件對話框讓用戶選擇 MP3 檔案

new_files = list(filedialog.askopenfilenames(filetypes=))

if new_files:

file_paths = new_files

# 隨機排列播放順序

random.shuffle(file_paths)

current_index = 0

update_file_listbox()

load_and_play_current()

def load_and_play_current():

global is_playing

if file_paths:

# 加載當前 MP3 檔案

pygame.mixer.music.load(file_paths)

# 播放音樂

pygame.mixer.music.play()

is_playing = True

highlight_current_track()

def pause_music():

global is_paused

if not is_playing:

return

if is_paused:

pygame.mixer.music.unpause()

pause_button.config(text="暫停")

else:

pygame.mixer.music.pause()

pause_button.config(text="繼續")

is_paused = not is_paused

def exit_program():

pygame.mixer.music.stop()

root.destroy()

def play_next():

global current_index, is_playing

if not file_paths:

return

current_index = (current_index + 1) % len(file_paths)

if is_playing:

load_and_play_current()

def update_file_listbox():

file_listbox.delete(0, tk.END)

for path in file_paths:

# 只顯示檔案名稱而不是完整路徑

filename = os.path.basename(path)

file_listbox.insert(tk.END, filename)

def highlight_current_track():

for i in range(file_listbox.size()):

if i == current_index:

file_listbox.itemconfig(i, {'bg':'yellow'})

else:

file_listbox.itemconfig(i, {'bg':'white'})

def check_music_end():

if is_playing and not pygame.mixer.music.get_busy() and not is_paused:

# 當前曲目播放結束且不是暫停狀態時,播放下一首

play_next()

# 每 100 毫秒檢查一次播放狀態

root.after(100, check_music_end)

# 建立選擇檔案按鈕

select_button = tk.Button(root, text="選擇 MP3 檔案", command=select_files)

select_button.pack(pady=10)

# 顯示選擇的檔案路徑

file_listbox = tk.Listbox(root, selectmode=tk.SINGLE)

file_listbox.pack(pady=10, fill=tk.BOTH, expand=True)

# 建立暫停按鈕

pause_button = tk.Button(root, text="暫停", command=pause_music)

pause_button.pack(pady=10)

# 建立結束按鈕,放在右上方

exit_button = tk.Button(root, text="結束", command=exit_program)

exit_button.place(relx=1.0, x=-10, y=10, anchor='ne')

# 啟動檢查播放結束事件的循環

root.after(100, check_music_end)

# 啟動主迴圈

root.mainloop()

圖 1:Copilot+Claude 生成的「弓蕉園的秘密 字詞放送(MP3 Player_Coipilot+Claude.py)」播放器

因此,我們得到了一個結論:

哪個 GPT 最好:混成的(hybrid) 上讚啦!

Claude 生成的程式碼

將以上的相同咒語輸入到 Claude 中,我們可以得到下列以物件導向程式(Object-Oriented Programming,OOP)風格的程式碼,其執行的畫面如圖 2 所示。

OOP.py

import tkinter as tk

from tkinter import ttk, filedialog

import pygame

import random

import os

#from mutagen.mp3 import MP3

from threading import Thread

import time

class MP3Player:

def __init__(self):

# 初始化播放器

pygame.mixer.init()

# 創建主窗口

self.root = tk.Tk()

self.root.title("MP3 播放器")

self.root.geometry("1700x1300")

# 播放列表相關變量

self.playlist = # 儲存音樂文件路徑

self.current_playing = None # 當前播放的索引

self.is_playing = False

self.setup_ui()

self.setup_threads()

def setup_ui(self):

"""設置用戶界面"""

# 創建按鈕框架

button_frame = ttk.Frame(self.root)

button_frame.pack(pady=10)

# 添加控制按鈕

ttk.Button(button_frame, text="添加音樂", command=self.add_music).pack(side=tk.LEFT, padx=5)

ttk.Button(button_frame, text="開始播放", command=self.start_playing).pack(side=tk.LEFT, padx=5)

ttk.Button(button_frame, text="停止播放", command=self.stop_playing).pack(side=tk.LEFT, padx=5)

# 創建播放列表

self.playlist_box = tk.Listbox(self.root, width=70, height=20)

self.playlist_box.pack(pady=10)

# 創建當前播放標籤

self.current_label = ttk.Label(self.root, text="當前未播放")

self.current_label.pack(pady=10)

def setup_threads(self):

"""設置後台線程"""

self.playing_thread = Thread(target=self.playing_loop, daemon=True)

self.playing_thread.start()

def add_music(self):

"""添加音樂文件到播放列表"""

files = filedialog.askopenfilenames(

filetypes=,

title="選擇音樂文件"

)

for file in files:

self.playlist.append(file)

# 只顯示文件名而不是完整路徑

self.playlist_box.insert(tk.END, os.path.basename(file))

def start_playing(self):

"""開始播放"""

if not self.is_playing and self.playlist:

self.is_playing = True

def stop_playing(self):

"""停止播放"""

self.is_playing = False

pygame.mixer.music.stop()

self.current_playing = None

self.update_current_playing()

def playing_loop(self):

"""播放循環(在後台線程中運行)"""

while True:

if self.is_playing and not pygame.mixer.music.get_busy():

if self.playlist:

# 隨機選擇下一首

next_index = random.randint(0, len(self.playlist) - 1)

# 加載並播放音樂

try:

pygame.mixer.music.load(self.playlist)

pygame.mixer.music.play()

self.current_playing = next_index

self.update_current_playing()

except:

print(f"無法播放文件: {self.playlist}")

time.sleep(0.1) # 避免過度佔用 CPU

def update_current_playing(self):

"""更新界面顯示當前播放歌曲"""

# 重置所有項目的顏色

for i in range(self.playlist_box.size()):

self.playlist_box.itemconfig(i, {'bg': 'white'})

if self.current_playing is not None:

# 設置當前播放項目的背景色

self.playlist_box.itemconfig(self.current_playing, {'bg': 'light blue'})

self.current_label.config(text=f"正在播放: {os.path.basename(self.playlist)}")

else:

self.current_label.config(text="當前未播放")

def run(self):

"""運行應用程序"""

self.root.mainloop()

if __name__ == "__main__":

player = MP3Player()

player.run()

 

本文為會員限定文章

立即加入會員! 全站文章無限看~

                               

已經是會員? 按此登入

只需不到短短一分鐘...

輸入您的信箱與ID註冊即可享有一切福利!

會員福利
1

免費電子報

2

會員搶先看

3

主題訂閱

4

好文收藏

Ted Lee

Author: Ted Lee

從工程師轉任中學教師,又為了捍衛教育理念,投身成為 STEAM 教育工作者,自稱「無可救藥的人文教育理想主義者」的李俊德(Ted Lee)。

Share This Post On

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *