Cursor에서 Python 입코딩으로 폼에 속도 조정 라디오버튼과 시작, 멈춤버튼 버튼 추가하기

소개

시도하고자 했던 것과 그 이유를 알려주세요.

텍스트를 WAV 음성으로 재생할 때 속도 조절을 라디오버튼을 사용하고, 시작버튼과 멈춤버튼으로 학습 상태를 선택하도록 했습니다.

진행 방법

어떤 도구를 사용했고, 어떻게 활용하셨나요?

Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.

Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.

Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)

Cursor에서 Python 입코딩을 사용했습니다.

기존 코딩이 된 파일에 다음과 같은 프롬프트로 슬라이더 대신에 라디오버튼(옵션버튼)으로 음성속도를 조절하도록 했습니다. 옵션버튼으로 프롬프트를 작성했는데 Cursor가 라디오버튼을 용어수정을 해서 그 다음부터는 라디오버튼으로 용어를 수정해서 프롬프트를 작성했습니다.

흰색 텍스트가 있는 검은색 화면
import asyncio
import edge_tts
import pandas as pd
import tkinter as tk
from tkinter import ttk
import sounddevice as sd
import soundfile as sf
import os
import threading
import queue

class WordDisplayApp:
    def __init__(self, root):
        self.root = root
        self.root.title("한국어/영어 문장 표시")
        
        # 현재 속도값 저장
        self.current_speed = 1.0
        
        # 현재 위치 저장을 위한 변수 추가
        self.current_index = 0
        
        # 창 크기 설정
        self.root.geometry("600x350")
        
        # 스타일 설정
        style = ttk.Style()
        style.configure("Word.TLabel", font=("맑은 고딕", 20, "bold"))
        style.configure("Speed.TLabel", font=("맑은 고딕", 10))
        style.configure("Start.TButton", font=("맑은 고딕", 12))
        
        # 속도 조절 프레임
        speed_frame = ttk.Frame(root)
        speed_frame.pack(pady=10)
        
        # 속도 레이블
        speed_label = ttk.Label(speed_frame, text="재생 속도:", style="Speed.TLabel")
        speed_label.pack(side="left", padx=5)
        
        # 속도 선택을 위한 변수
        self.speed_var = tk.StringVar(value="1")
        
        # 속도 라디오 버튼들
        speeds = [("1x", "1"), ("2x", "2"), ("3x", "3"), ("4x", "4")]
        for text, value in speeds:
            ttk.Radiobutton(speed_frame, text=text, value=value, 
                          variable=self.speed_var, 
                          command=self.update_speed).pack(side="left", padx=5)
        
        # 시작/멈춤 버튼 프레임
        button_frame = ttk.Frame(root)
        button_frame.pack(pady=10)
        
        # 시작 버튼과 멈춤 버튼
        self.start_button = ttk.Button(button_frame, text="시작", style="Start.TButton", command=self.start_playback)
        self.start_button.pack(side="left", padx=5)
        
        self.stop_button = ttk.Button(button_frame, text="멈춤", style="Start.TButton", command=self.stop_playback)
        self.stop_button.pack(side="left", padx=5)
        self.stop_button.config(state="disabled")
        
        # 단어 표시 레이블
        self.kor_label = ttk.Label(root, text="", style="Word.TLabel")
        self.kor_label.pack(pady=20)

        self.eng_label = ttk.Label(root, text="", style="Word.TLabel")
        self.eng_label.pack(pady=20)
        
        # 재생 시작 플래그
        self.started = False
        
        # 재생 중지 플래그
        self.stop_flag = False
        
        # 메시지 큐
        self.msg_queue = queue.Queue()
        
        # GUI 업데이트 타이머 시작
        self.update_gui()

    def update_speed(self):
        self.current_speed = float(self.speed_var.get())
        print(f"속도 업데이트: {self.current_speed}x")  # 디버깅용 출력

    def get_rate_string(self):
        # Edge TTS의 속도를 선택된 배수에 맞게 설정
        speed_multiplier = float(self.speed_var.get())
        
        # 선택된 배수에 따라 퍼센트 계산
        # 1x = 100%, 2x = 200%, 3x = 300%, 4x = 400%
        percentage = int(speed_multiplier * 100)
        
        # Edge TTS에 맞는 형식으로 변환
        relative_percentage = percentage - 100
        rate = f"+{relative_percentage}%"
        
        print(f"현재 속도: {speed_multiplier}x -> {percentage}% -> {rate}")  # 디버깅용 출력
        return rate

    def update_gui(self):
        try:
            while True:
                msg = self.msg_queue.get_nowait()
                if msg[0] == "words":
                    self.kor_label.config(text=msg[1])
                    self.eng_label.config(text=msg[2])
                elif msg[0] == "quit":
                    self.root.quit()
                    return
        except queue.Empty:
            pass
        # stop_flag와 관계없이 GUI 업데이트를 계속 실행
        self.root.after(100, self.update_gui)
    
    def start_playback(self):
        if not self.started:
            self.started = True
            self.stop_flag = False
            self.start_button.config(state="disabled")
            self.stop_button.config(state="normal")
            threading.Thread(target=self.run_async_loop, daemon=True).start()
    
    def run_async_loop(self):
        asyncio.run(self.play_words())
    
    async def play_words(self):
        try:
            df = pd.read_excel('영어한글.xlsx', header=None)
            
            for index, row in df.iterrows():
                if index < self.current_index:  # 저장된 위치까지 스킵
                    continue
                    
                if self.stop_flag:
                    break
                    
                kor_text = str(row.iloc[0])    
                eng_text = str(row.iloc[1])
                
                # GUI 업데이트를 위한 메시지 전송
                self.msg_queue.put(("words", kor_text, ""))
                
                # 한국어 음성 생성 및 재생
                rate = self.get_rate_string()
                kor_file = f'word_{index}_kor.wav'
                communicate_kor = edge_tts.Communicate(kor_text, "ko-KR-SunHiNeural", rate=rate)
                await communicate_kor.save(kor_file)
                await self.play_wav_file(kor_file)
                os.remove(kor_file)
                
                if self.stop_flag:
                    self.current_index = index
                    break
                
                # 0.5초 대기 후 영어 문장 표시
                await asyncio.sleep(0.5)
                # GUI 업데이트를 위한 메시지 전송
                self.msg_queue.put(("words", kor_text, eng_text))
                
                # 영어 음성 생성 및 재생
                eng_file = f'word_{index}_eng.wav'
                communicate_eng = edge_tts.Communicate(eng_text, "en-US-ChristopherNeural", rate=rate)
                await communicate_eng.save(eng_file)
                await self.play_wav_file(eng_file)
                os.remove(eng_file)
                
                if self.stop_flag:
                    self.current_index = index + 1
                    break
                
                await asyncio.sleep(1.0)
                self.current_index = index + 1
                
        except Exception as e:
            print(f"오류 발생: {e}")
        finally:
            if self.stop_flag:
                # 멈춤 시에는 현재 상태 유지
                self.started = False
            else:
                # 완전히 종료 시에는 모든 상태 초기화
                self.started = False
                self.current_index = 0
                self.msg_queue.put(("words", "", ""))
    
    async def play_wav_file(self, file_path):
        data, samplerate = sf.read(file_path)
        sd.play(data, samplerate)
        sd.wait()

    def stop_playback(self):
        self.stop_flag = True  # 멈춤 플래그 설정
        sd.stop()  # 현재 재생 중인 음성 중지
        self.started = False
        self.start_button.config(state="normal")
        self.stop_button.config(state="disabled")

def main():
    root = tk.Tk()
    app = WordDisplayApp(root)
    
    def on_closing():
        app.stop_flag = True
        root.destroy()
    
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    main()

결과와 배운 점

나 아침 내내 졸린데 한국어로 밥을 먹여
  1. 배운 점 : 폼, 슬라이더와 라디오버튼, 시작 버튼 등이 쉽게 생성되어서 편리했습니다

  2. 도움이 필요한 부분 : 재생 속도가 1배속과 2배속만 적용이 되고 3배속 이상은 모두 2배속입니다. 여러번 시도하고 있으나 적용이 안됩니다. 슬라이더를 사용할 때도 적용되지 않았습니다. 적용하려면 MP3로 변경하라고 제안합니다.

  3. 계획 : 입코딩을 하지만 Python 문법을 조금이라도 배워야 수정이 쉬울 것 같아서 Python을 공부하려고 합니다.

2

뉴스레터 무료 구독

👉 이 게시글도 읽어보세요