Search

μ•ŒλžŒ ν”„λ‘œκ·Έλž¨

μ•ŒλžŒ ν”„λ‘œκ·Έλž¨ - GPTxPython

μ•ŒλžŒ 섀정을 λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯ν•˜μ—¬, κ΄€λ¦¬ν•˜λŠ” μ•ŒλžŒ ν”„λ‘œκ·Έλž¨

ν”„λ‘œκ·Έλž¨

ν”„λ‘¬ν”„νŠΈ

페λ₯΄μ†Œλ‚˜ - 파이썬 개발자 μž‘μ—… - μ•ŒλžŒ ν”„λ‘œκ·Έλž¨ 개발 λ§₯락 - μ–Έμ–΄ : 파이썬 - 버전 : 3.13 - ꡬ쑰 : GUI ν”„λ‘œκ·Έλž¨ - κΈ°λŠ₯ * 닀쀑 μ•ŒλžŒ 관리 ν”„λ‘œκ·Έλž¨ * μ €μž₯된 μ•ŒλžŒ λͺ©λ‘ 확인 * MySQL λ°μ΄ν„°λ² μ΄μŠ€λ‘œ μ•ŒλžŒ 정보 관리 * μ•ŒλžŒ 정보 관리 : μ•ŒλžŒ 이름 λ³€κ²½, μ•ŒλžŒ μ†Œλ¦¬ λ³€κ²½, μ•ŒλžŒ λ‚ μ§œ μ‹œκ°„ λ³€κ²½ * μ•ŒλžŒ 관리 λŒ€μ‹œλ³΄λ“œ : λ“±λ‘λœ μ•ŒλžŒ 리슀트둜 확인, μˆ˜μ • μ‚­μ œ μ•„μ΄μ½˜ λ²„νŠΌμœΌλ‘œ 관리 * μ•ŒλžŒ 리슀트의 μΉ΄λ“œ 클릭 μ‹œ μˆ˜μ •ν™”λ©΄μœΌλ‘œ 이동 * μ•ŒλžŒ μˆ˜μ • ν™”λ©΄ : 이름, μ†Œλ¦¬, λ‚ μ§œμ‹œκ°„ λ³€κ²½ * DB 접속 μ‹œ alarm ν…Œμ΄λΈ” 생성 * κΈ°λ³Έ μ•ŒλžŒ μ†Œλ¦¬λŠ” c:/alarm/ ν΄λ”μ—μ„œ 선택 * 졜초 μ•ŒλžŒ μ†Œλ¦¬λŠ” c:/alaram/κΈ°λ³Έ.mp3 ν˜•μ‹ - μ•ŒλžŒ μ‹œκ°„ 도달 μ‹œ, μœˆλ„μš° notification μ•Œλ¦ΌμœΌλ‘œ μ•ŒλžŒ 이름과 μ‹œκ°„ 좜λ ₯ - μ§€μ •ν•œ μ•ŒλžŒ μ†Œλ¦¬λ₯Ό μ•ŒλžŒλ„κΈ° λ²„νŠΌ 클릭 μ „κΉŒμ§€ 반볡 μž¬μƒ μ•ŒλžŒ mp3 파일의 μž¬μƒμ‹œκ°„μ„ ν™•μΈν•˜μ—¬ ν•œ λ²ˆμ— μž¬μƒμ΄ λŠλ‚œ λ’€ λ‹€μŒ μž¬μƒ 반볡 μ˜ˆμ‹œ - μ•ŒλžŒ 등둝 λ²„νŠΌ 클릭 μ‹œ, μ•ŒλžŒ 이름과 μ•ŒλžŒ λ‚ μ§œ μ‹œκ°„ μ„ νƒν•˜κ³  μ•ŒλžŒ λ‚ μ§œμ‹œκ°„μ€ ν…μŠ€νŠΈ μž…λ ₯말고 달λ ₯ μ‹œκ³„ μž…λ ₯으둜 μ•ŒλžŒ μ†Œλ¦¬λ₯Ό μ„ νƒμƒμžμ—μ„œ 선택, 기본은 c:/alaram/κΈ°λ³Έ.mp3 μ΄λ ‡κ²Œ λ“±λ‘ν•œ μ•ŒλžŒμ€ λŒ€μ‹œλ³΄λ“œμ— 좜λ ₯되고, μˆ˜μ • μ‚­μ œλ²„νŠΌμœΌλ‘œ 관리 λŒ€μ‹œλ³΄λ“œμ—μ„œ ν•΄λ‹Ή μ•ŒλžŒ 더블클릭 μ‹œ, ν”„λ‘œκ·Έλ ˆμŠ€λ°”μ™€ 라벨둜 남은 μ‹œκ°„ ν‘œμ‹œν•˜λŠ” ν™”λ©΄μœΌλ‘œ 이동 μΊ”λ²„μŠ€ λͺ¨λ“œλ‘œ 응닡
Plain Text
볡사

μ½”λ“œ

v.2
import tkinter as tk from tkinter import messagebox, ttk from tkcalendar import DateEntry import mysql.connector import pygame import threading import datetime import time import os from win10toast import ToastNotifier # MySQL μ—°κ²° μ„€μ • conn = mysql.connector.connect( host="localhost", user="aloha", password="123456", database="aloha" ) cursor = conn.cursor() # alarm ν…Œμ΄λΈ” 생성 cursor.execute('''CREATE TABLE IF NOT EXISTS alarm ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), datetime DATETIME, sound VARCHAR(255) )''') conn.commit() # pygame μ΄ˆκΈ°ν™” pygame.mixer.init() # μ•Œλ¦Ό ν•¨μˆ˜ (별도 μŠ€λ ˆλ“œλ‘œ μ‹€ν–‰) def show_notification(title, msg): def _toast(): n = ToastNotifier() n.show_toast(title, msg, duration=5, threaded=False) threading.Thread(target=_toast, daemon=True).start() # μ•ŒλžŒ 클래슀 class AlarmApp: def __init__(self, root): self.root = root self.root.title("μ•ŒλžŒ 관리 ν”„λ‘œκ·Έλž¨") self.root.geometry("600x500") self.alarm_frame = ttk.Frame(root) self.alarm_frame.pack(fill="both", expand=True) ttk.Label(self.alarm_frame, text="πŸ”” μ•ŒλžŒ 관리 λŒ€μ‹œλ³΄λ“œ", font=("Arial", 16, "bold")).pack(pady=10) self.tree = ttk.Treeview(self.alarm_frame, columns=("name", "datetime", "sound"), show='headings') self.tree.heading("name", text="μ•ŒλžŒ 이름") self.tree.heading("datetime", text="μ•ŒλžŒ μ‹œκ°„") self.tree.heading("sound", text="μ†Œλ¦¬ 파일") self.tree.pack(fill="both", expand=True, padx=10, pady=10) self.tree.bind("<Double-1>", self.edit_alarm) btn_frame = ttk.Frame(self.alarm_frame) btn_frame.pack(pady=10) ttk.Button(btn_frame, text="βž• μ•ŒλžŒ μΆ”κ°€", command=self.add_alarm_window).grid(row=0, column=0, padx=5) ttk.Button(btn_frame, text="πŸ—‘οΈ μ‚­μ œ", command=self.delete_alarm).grid(row=0, column=1, padx=5) self.load_alarms() self.start_alarm_checker() # μ•ŒλžŒ μƒνƒœ 관리 self.is_alarm_playing = False self.alarm_popup = None def load_alarms(self): for i in self.tree.get_children(): self.tree.delete(i) cursor.execute("SELECT * FROM alarm") for row in cursor.fetchall(): self.tree.insert('', 'end', values=row[1:]) def add_alarm_window(self): win = tk.Toplevel(self.root) win.title("μ•ŒλžŒ μΆ”κ°€") win.geometry("400x300") ttk.Label(win, text="μ•ŒλžŒ 이름").pack(pady=5) name_entry = ttk.Entry(win) name_entry.pack(pady=5) ttk.Label(win, text="λ‚ μ§œ 선택").pack(pady=5) date_entry = DateEntry(win, date_pattern='yyyy-mm-dd') date_entry.pack(pady=5) ttk.Label(win, text="μ‹œκ°„ 선택 (HH:MM)").pack(pady=5) time_entry = ttk.Entry(win) time_entry.pack(pady=5) ttk.Label(win, text="μ†Œλ¦¬ 파일 선택").pack(pady=5) sounds = [f for f in os.listdir('C:/alarm') if f.endswith('.mp3')] sound_var = tk.StringVar(value="κΈ°λ³Έ.mp3") sound_combo = ttk.Combobox(win, textvariable=sound_var, values=sounds, state="readonly") sound_combo.pack(pady=5) def save_alarm(): name = name_entry.get() date = date_entry.get() time_str = time_entry.get() sound = os.path.join('C:/alarm', sound_var.get()) alarm_time = f"{date} {time_str}:00" cursor.execute("INSERT INTO alarm (name, datetime, sound) VALUES (%s, %s, %s)", (name, alarm_time, sound)) conn.commit() win.destroy() self.load_alarms() ttk.Button(win, text="μ €μž₯", command=save_alarm).pack(pady=10) def delete_alarm(self): selected = self.tree.focus() if not selected: messagebox.showwarning("선택 μ—†μŒ", "μ‚­μ œν•  μ•ŒλžŒμ„ μ„ νƒν•˜μ„Έμš”.") return name = self.tree.item(selected)['values'][0] cursor.execute("DELETE FROM alarm WHERE name=%s", (name,)) conn.commit() self.load_alarms() def edit_alarm(self, event): selected = self.tree.focus() values = self.tree.item(selected)['values'] if not values: return win = tk.Toplevel(self.root) win.title("μ•ŒλžŒ μˆ˜μ •") win.geometry("400x300") ttk.Label(win, text="μ•ŒλžŒ 이름").pack(pady=5) name_entry = ttk.Entry(win) name_entry.insert(0, values[0]) name_entry.pack(pady=5) ttk.Label(win, text="λ‚ μ§œ 선택").pack(pady=5) date_entry = DateEntry(win, date_pattern='yyyy-mm-dd') date_entry.set_date(values[1].split(' ')[0]) date_entry.pack(pady=5) ttk.Label(win, text="μ‹œκ°„ 선택 (HH:MM)").pack(pady=5) time_entry = ttk.Entry(win) time_entry.insert(0, values[1].split(' ')[1][:5]) time_entry.pack(pady=5) ttk.Label(win, text="μ†Œλ¦¬ 파일 선택").pack(pady=5) sounds = [f for f in os.listdir('C:/alarm') if f.endswith('.mp3')] sound_var = tk.StringVar(value=os.path.basename(values[2])) sound_combo = ttk.Combobox(win, textvariable=sound_var, values=sounds, state="readonly") sound_combo.pack(pady=5) def update_alarm(): name = name_entry.get() date = date_entry.get() time_str = time_entry.get() sound = os.path.join('C:/alarm', sound_var.get()) alarm_time = f"{date} {time_str}:00" cursor.execute("UPDATE alarm SET datetime=%s, sound=%s WHERE name=%s", (alarm_time, sound, values[0])) conn.commit() win.destroy() self.load_alarms() ttk.Button(win, text="μˆ˜μ • μ €μž₯", command=update_alarm).pack(pady=10) def start_alarm_checker(self): def check(): while True: cursor.execute("SELECT name, datetime, sound FROM alarm") for (name, alarm_time, sound) in cursor.fetchall(): now = datetime.datetime.now().replace(second=0, microsecond=0) if now == alarm_time: show_notification("μ•ŒλžŒ", f"{name} - {alarm_time}") self.show_alarm_popup(name, sound) time.sleep(30) threading.Thread(target=check, daemon=True).start() def show_alarm_popup(self, name, sound_path): if self.alarm_popup and self.alarm_popup.winfo_exists(): return # 이미 νŒμ—…μ΄ μ—΄λ €μžˆμœΌλ©΄ λ¬΄μ‹œ self.alarm_popup = tk.Toplevel(self.root) self.alarm_popup.title("⏰ μ•Œλ¦Ό") self.alarm_popup.geometry("300x150") self.alarm_popup.attributes("-topmost", True) tk.Label( self.alarm_popup, text=f"{name} μ•ŒλžŒμ΄ μšΈλ¦½λ‹ˆλ‹€!", font=("맑은 κ³ λ”•", 14, "bold") ).pack(pady=20) tk.Button( self.alarm_popup, text="μ•ŒλžŒ μ’…λ£Œ", font=("맑은 κ³ λ”•", 12), bg="#ff5555", fg="white", relief="flat", command=self.stop_alarm_sound ).pack(pady=10) threading.Thread(target=self.play_alarm_sound, args=(sound_path,), daemon=True).start() def play_alarm_sound(self, sound_path): self.is_alarm_playing = True play_count = 0 pygame.mixer.init() while self.is_alarm_playing and play_count < 5: pygame.mixer.music.load(sound_path) pygame.mixer.music.play() while pygame.mixer.music.get_busy() and self.is_alarm_playing: time.sleep(0.1) play_count += 1 pygame.mixer.quit() self.is_alarm_playing = False def stop_alarm_sound(self): self.is_alarm_playing = False try: pygame.mixer.music.stop() pygame.mixer.quit() except: pass if self.alarm_popup and self.alarm_popup.winfo_exists(): self.alarm_popup.destroy() # 메인 μ‹€ν–‰ if __name__ == '__main__': root = tk.Tk() app = AlarmApp(root) root.mainloop()
Python
볡사

μ½”λ“œ

v.1
import os import tkinter as tk from tkinter import ttk, messagebox, filedialog from tkcalendar import Calendar, DateEntry from datetime import datetime, timedelta import threading import time import pygame import mysql.connector from plyer import notification # ===================== DB SETUP ===================== def connect_db(): # MySQL DB에 μ—°κ²°ν•˜κ³  alarm ν…Œμ΄λΈ” 생성 (status μΆ”κ°€) conn = mysql.connector.connect( host='localhost', user='python', password='123456', database='python' ) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS alarm ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), time DATETIME, sound_path TEXT, status VARCHAR(10) DEFAULT 'on' -- μ•ŒλžŒ μƒνƒœ (on/off) ) ''') conn.commit() return conn # ===================== ALARM PLAYER ===================== def play_alarm(sound_path): # μ•ŒλžŒ μ†Œλ¦¬λ₯Ό 반볡 μž¬μƒ (stop_eventκ°€ 섀정될 λ•ŒκΉŒμ§€) pygame.mixer.init() pygame.mixer.music.load(sound_path) while not stop_event.is_set(): pygame.mixer.music.play() time.sleep(pygame.mixer.Sound(sound_path).get_length()) def notify_user(title, msg): # μœˆλ„μš° μ•Œλ¦Όμ„ 띄움 notification.notify( title=title, message=msg, timeout=5 ) # ===================== MAIN APP ===================== class AlarmApp: def __init__(self, root): self.root = root self.root.title("μ•ŒλžŒ λŒ€μ‹œλ³΄λ“œ") self.conn = connect_db() self.cursor = self.conn.cursor() self.frame = ttk.Frame(root, padding=10) self.frame.pack(fill='both', expand=True) self.add_button = ttk.Button(self.frame, text="μ•ŒλžŒ 등둝", command=self.open_add_alarm_window) self.add_button.pack(pady=5) # μ•ŒλžŒ 리슀트 좜λ ₯용 TreeView ꡬ성 self.tree = ttk.Treeview(self.frame, columns=('Name', 'Time', 'Sound', 'Status'), show='headings') self.tree.heading('Name', text='μ•ŒλžŒ 이름') self.tree.heading('Time', text='μ•ŒλžŒ μ‹œκ°„') self.tree.heading('Sound', text='μ•ŒλžŒ μ†Œλ¦¬') self.tree.heading('Status', text='μƒνƒœ') self.tree.pack(fill='both', expand=True) self.tree.bind('<Double-1>', self.open_edit_alarm_window) self.load_alarms() self.check_alarms() def load_alarms(self): # DBμ—μ„œ μ•ŒλžŒ λͺ©λ‘μ„ λΆˆλŸ¬μ™€ TreeView에 ν‘œμ‹œ for row in self.tree.get_children(): self.tree.delete(row) self.cursor.execute("SELECT id, name, time, sound_path, status FROM alarm") for id, name, time_, sound, status in self.cursor.fetchall(): self.tree.insert('', 'end', iid=id, values=(name, time_, os.path.basename(sound), status)) def open_add_alarm_window(self): AlarmEditor(self.root, self.conn, self.load_alarms) def open_edit_alarm_window(self, event): selected = self.tree.focus() if selected: alarm_id = int(selected) AlarmEditor(self.root, self.conn, self.load_alarms, alarm_id) def check_alarms(self): # 주기적으둜 ν˜„μž¬ μ‹œκ°κ³Ό μ•ŒλžŒ μ‹œκ°„ λΉ„κ΅ν•˜μ—¬ μ‹€ν–‰ 쑰건 체크 def run(): while True: self.cursor.execute("SELECT id, name, time, sound_path FROM alarm WHERE status='on'") for alarm_id, name, alarm_time, sound in self.cursor.fetchall(): now = datetime.now() # μ•ŒλžŒ μ‹œκ°„μ΄ 지났고 아직 μšΈλ¦¬μ§€ μ•Šμ€ μ•ŒλžŒμ΄λ©΄ μ‹€ν–‰ if now >= alarm_time and not active_alarms.get(alarm_id): active_alarms[alarm_id] = True threading.Thread(target=self.trigger_alarm, args=(alarm_id, name, alarm_time, sound)).start() time.sleep(10) threading.Thread(target=run, daemon=True).start() def trigger_alarm(self, alarm_id, name, alarm_time, sound): # μ•ŒλžŒ μ‹€ν–‰: μ•Œλ¦Ό ν‘œμ‹œ 및 μ†Œλ¦¬ μž¬μƒ notify_user("μ•ŒλžŒ", f"{name} - {alarm_time.strftime('%Y-%m-%d %H:%M:%S')}") global stop_event stop_event = threading.Event() threading.Thread(target=play_alarm, args=(sound,), daemon=True).start() # μ•ŒλžŒ μ’…λ£Œμš© νŒμ—… μœˆλ„μš° stop_window = tk.Toplevel(self.root) stop_window.title("μ•ŒλžŒ 울림") ttk.Label(stop_window, text=f"μ•ŒλžŒ: {name}").pack(pady=10) ttk.Label(stop_window, text=f"μ‹œκ°„: {alarm_time}").pack(pady=5) stop_btn = ttk.Button(stop_window, text="μ•ŒλžŒ 끄기", command=lambda: self.stop_alarm(stop_window, alarm_id)) stop_btn.pack(pady=20) def stop_alarm(self, window, alarm_id): # μ•ŒλžŒ μ •μ§€ 처리: μ†Œλ¦¬ 쀑지 + DB μƒνƒœ λ³€κ²½ stop_event.set() pygame.mixer.music.stop() window.destroy() # μ•ŒλžŒ μƒνƒœλ₯Ό 'off'둜 μ—…λ°μ΄νŠΈ self.cursor.execute("UPDATE alarm SET status='off' WHERE id=%s", (alarm_id,)) self.conn.commit() self.load_alarms() # ===================== ALARM EDITOR ===================== class AlarmEditor: def __init__(self, root, conn, refresh_callback, alarm_id=None): self.conn = conn self.cursor = conn.cursor() self.refresh_callback = refresh_callback self.alarm_id = alarm_id self.top = tk.Toplevel(root) self.top.title("μ•ŒλžŒ μˆ˜μ •" if alarm_id else "μ•ŒλžŒ 등둝") # μ•ŒλžŒ 이름 μž…λ ₯ ttk.Label(self.top, text="μ•ŒλžŒ 이름").grid(row=0, column=0, padx=5, pady=5) self.name_entry = ttk.Entry(self.top) self.name_entry.grid(row=0, column=1, padx=5, pady=5) # λ‚ μ§œ 선택 ttk.Label(self.top, text="μ•ŒλžŒ λ‚ μ§œ").grid(row=1, column=0, padx=5, pady=5) self.date_entry = DateEntry(self.top, width=12, background='darkblue', foreground='white', borderwidth=2) self.date_entry.grid(row=1, column=1, padx=5, pady=5) # μ‹œκ°„ μž…λ ₯ (μ‹œ:λΆ„) ttk.Label(self.top, text="μ‹œκ°„ (HH:MM)").grid(row=2, column=0, padx=5, pady=5) self.time_entry = ttk.Entry(self.top) self.time_entry.insert(0, "08:00") # κΈ°λ³Έκ°’ μ„€μ • self.time_entry.grid(row=2, column=1, padx=5, pady=5) # μ•ŒλžŒ μ†Œλ¦¬ 선택 ttk.Label(self.top, text="μ•ŒλžŒ μ†Œλ¦¬").grid(row=3, column=0, padx=5, pady=5) self.sound_combo = ttk.Combobox(self.top, values=self.load_sounds()) self.sound_combo.set("κΈ°λ³Έ.mp3") self.sound_combo.grid(row=3, column=1, padx=5, pady=5) # μ €μž₯ λ²„νŠΌ save_btn = ttk.Button(self.top, text="μ €μž₯", command=self.save_alarm) save_btn.grid(row=4, column=0, pady=10) # μˆ˜μ • μ‹œ μ‚­μ œ λ²„νŠΌλ„ ν‘œμ‹œ if alarm_id: delete_btn = ttk.Button(self.top, text="μ‚­μ œ", command=self.delete_alarm) delete_btn.grid(row=4, column=1, pady=10) if alarm_id: self.load_alarm_data() def load_sounds(self): # μ•ŒλžŒ μ‚¬μš΄λ“œ ν΄λ”μ—μ„œ mp3 파일 λͺ©λ‘ κ°€μ Έμ˜€κΈ° sound_dir = "c:/alarm" return [f for f in os.listdir(sound_dir) if f.endswith(".mp3")] def load_alarm_data(self): # DBμ—μ„œ μ•ŒλžŒ 정보 λ‘œλ“œν•˜μ—¬ 폼에 μ„€μ • self.cursor.execute("SELECT name, time, sound_path FROM alarm WHERE id=%s", (self.alarm_id,)) name, time_, sound = self.cursor.fetchone() self.name_entry.insert(0, name) self.date_entry.set_date(time_) self.time_entry.delete(0, tk.END) self.time_entry.insert(0, time_.strftime("%H:%M")) self.sound_combo.set(os.path.basename(sound)) def save_alarm(self): # νΌμ—μ„œ μž…λ ₯ν•œ 데이터λ₯Ό μ €μž₯ν•˜κ±°λ‚˜ μ—…λ°μ΄νŠΈ name = self.name_entry.get() date_str = self.date_entry.get_date().strftime("%Y-%m-%d") time_str = self.time_entry.get() datetime_str = f"{date_str} {time_str}:00" sound = os.path.join("c:/alarm", self.sound_combo.get()) if self.alarm_id: self.cursor.execute( "UPDATE alarm SET name=%s, time=%s, sound_path=%s, status='on' WHERE id=%s", (name, datetime_str, sound, self.alarm_id) ) else: self.cursor.execute( "INSERT INTO alarm (name, time, sound_path, status) VALUES (%s, %s, %s, 'on')", (name, datetime_str, sound) ) self.conn.commit() self.refresh_callback() self.top.destroy() def delete_alarm(self): # μ•ŒλžŒ μ‚­μ œ 확인 ν›„ DBμ—μ„œ 제거 if messagebox.askyesno("μ‚­μ œ 확인", "이 μ•ŒλžŒμ„ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?"): self.cursor.execute("DELETE FROM alarm WHERE id=%s", (self.alarm_id,)) self.conn.commit() self.refresh_callback() self.top.destroy() # ===================== MAIN ===================== if __name__ == "__main__": active_alarms = {} # ν˜„μž¬ μ‹€ν–‰ 쀑인 μ•ŒλžŒ λͺ©λ‘ stop_event = threading.Event() # μ•ŒλžŒ 쀑지 이벀트 root = tk.Tk() app = AlarmApp(root) root.mainloop()
Python
볡사