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註冊即可享有一切福利!
會員福利
免費電子報
會員搶先看
主題訂閱
好文收藏