이번에는 간단하지만 간단하지 않은 알람을 구현해 보았다.

이것저것 생각해 보다가 알람을 구현해 보기로 마음먹고 구현해 보았다.
많은 개발자나 사람들이 그렇듯 디자인 센스가 부족해서 아주 기본적인 뼈대를 만들어놓고
내부적인 구현에 집중해 보기로 했다.
날짜와 시간 분을 선택하면 설정한 시간에 알람이 울리도록 설정해 보았다.
차후 추가적으로 원하는 음원 파일을 넣으면 그 음원 파일이 재생되는 것을 염두에 두고 구현해 두었다.

입력을 누르면 설정한 시간에 알람이 울리기 시작하고 메시지 창이 뜬다.

예를 선택하면 알람이 종료되며 생성된 윈도도 종료된다.
아니요를 선택하면 노래가 계속해서 재생된다.
시간을 잘못 입력했을 경우 시간 수정을 클릭하면 수정이 가능하다.
import tkinter as tk
from tkinter import messagebox
import time
import threading
import winsound
알람을 구현하기 위해서 import 한 모듈은 위와 같다.
여기서 windsound는 playsound로 대체가 가능하다.
playsound 모듈은 별도로 설치를 해주어야 하지만 mp3 파일을 재생할 수 있다.
다만 재생을 종료하는 메서드가 존재하지 않고 재생만 가능하므로 주의해서 사용할 필요가 있었다.
필자는 음원 재생을 위해서 winsound를 사용했다. winsound는 wav 파일만 재생할 수 있다는 단점이 있지만
다양한 메서드들과 정지 기능이 존재한다는 메리트가 있다. 또한 무한 반복 기능이나 비동기식 재생 기능이 존재하므로
사용하는 용도에 따라서 winsound가 더 도움이 될 것이라고 판단하여 선택하였다.
필자가 만드는 알람 기능은 time 모듈의 sleep 메서드를 이용해서 만들기 때문에 실행하면 생성한 창이 전혀 움직이지 않는 상태가 되었다.
이 상태를 방지하기 위해서 멀티프로세싱과 스레드 모듈을 찾아보았고 그중 스레드를 이용 하서 병렬처리하기로 하였다.
스레드를 사용하기 위해서는 threading 모듈을 불러와야 한다.
이외의 import 한 모듈은 차후 코드를 리뷰하며 설명하도록 하겠다.
root = tk.Tk()
root.geometry("320x160")
root.title("알람")
root.resizable(0,0)
생성할 윈도의 크기는 원하는 대로 선택하였다.
########## 프레임 생성 ###############
frame = tk.Frame(root, bg = "white", height = 10) # 최상단 프레임
frame.pack(fill = "both",side = "top")
baseFrame = tk.Frame(root, bg = "white", height = 100) # 버튼 및 리스트박스 구현, 배치를 위한 프레임
baseFrame.pack(fill = "both", expand = True)
timeStr = tk.StringVar() # 시간정보를 저장할 변수
t1 = threading.Thread(target=alarm, args = timeStr.get()) # 알람의 병렬처리를 위한 스레드 생성
label = tk.Label(frame, text = "시간 입력(Day HH:MM):")
label.grid(row = 0, column = 0, padx = 2 , pady = 2)
entry = tk.Entry(frame, textvariable = timeStr) # 시간 정보가 입력되어 시각적으로 확인이 가능하도록 구현
entry.grid(row = 0, column = 1)
btn = tk.Button(frame , text = "입력", command = lambda : t1.start()) # 클릭 시 알람 실행
btn.grid(row = 0, column = 2)
가장 최상단에서 입력된 값의 정보와 실행 버튼만 넣을 프레임을 생성하고 위에 배치하였다.
baseframe은 각종 버튼들을 배치하기 위해서 생성했다.
timeStr 변수는 입력된 시간 정보를 저장한다.
t1은 위에서 설명했듯이 알람이 실행되면 앱이 먹통이 되어버리기 때문에 병렬처리를 위해서 생성했다.
t1의 target은 실행할 함수를, args는 target의 매개변수가 존재할 경우 원하는 값을 처리하기 위해 넣었다.
입력 버튼을 생성하고 클릭되면 알람이 시작되도록 구현했다
#########################시간 선택#############################
dlistBox = tk.Listbox(baseFrame, width = 3 , height = 5)
for i in range(31):
dlistBox.insert(i,i+1)
hlistBox = tk.Listbox(baseFrame, width = 3, height = 5)
for i in range(24):
if i < 10:
hlistBox.insert(i,"0"+str(i)) # strftime의 1~10은 01~09로 시간을 반환하므로 0을 추가해줘야 매칭이 가능
else:
hlistBox.insert(i,i)
mlistBox = tk.Listbox(baseFrame, width = 5, height = 5)
for i in range(60):
if i < 10:
mlistBox.insert(i, "0" + str(i))
else:
mlistBox.insert(i, i)
dlistBox.grid(row = 1 , column = 0)
hlistBox.grid(row = 1 , column = 1)
mlistBox.grid(row = 1 , column = 2)
알람의 실행을 위해서 보다 직관적으로 시간 정보를 볼 수 있도록 리스트 박스를 이용하였다.
각 listbox.index 부분을 보면 "0" + str(i)를 값으로 설정하는데 이는 차후 설명할 함수에서 설명하겠다.
dlistLable = tk.Label(baseFrame,text = "Day")
dlistLable.grid(row = 0 , column = 0)
hlistLable = tk.Label(baseFrame,text = "Hour")
hlistLable.grid(row = 0 , column = 1)
mlistLable = tk.Label(baseFrame,text = "Minute")
mlistLable.grid(row = 0 , column = 2)
리스트 박스의 시간 선택에 도움이 될 수 있도록 라벨을 추가하여 시각 정보를 구현해 주었다.
btn = tk.Button(baseFrame, text = "확인", command =
lambda x = dlistBox.curselection(): update_day(x), width = 3)
btn.grid(row = 2, column =0,padx = 1,pady =1)
btn = tk.Button(baseFrame, text = "확인", command =
lambda x = dlistBox.curselection(): update_hour(x), width = 3)
btn.grid(row = 2, column = 1,padx = 1,pady =1)
btn = tk.Button(baseFrame, text = "확인", command =
lambda x = dlistBox.curselection(): update_minute(x), width = 4)
btn.grid(row = 2, column = 2, padx = 1, pady = 1)
listbox에 메서드 중 curselection이 존재하는데 이 메서드를 사용하면 클릭한 부분의 정보를 반환한다.
이 정보는 튜 프리의 형태이므로 update_xxxx 함수들에서 따로 처리를 해주었다.
def update_day(event):
timeStr.set(timeStr.get() + str(dlistBox.get(dlistBox.curselection())) + "일")
def update_hour(event):
timeStr.set(timeStr.get() + str(hlistBox.get(hlistBox.curselection())) + "시")
def update_minute(event):
timeStr.set(timeStr.get() + str(mlistBox.get(mlistBox.curselection())) + "분")
위 함수들에서 큐플 요소를 따로 구분시키고 문자열로 변환한 후 timeStr의 값으로 저장시킨다.
사용자의 편의를 위해서 일, 시, 분도 함께 추가하였다. 이러한 부분이 없을
경우 제대로 입력이 되는 것인지 구별이 안 갈 수 있기 때문이다.
btn = tk.Button(baseFrame, text = "알람 종료", command = message)
btn.grid(row = 2, column = 3, padx = 1, pady= 1)
clearbtn = tk.Button(baseFrame, text = "시간 수정", command = clear_time)
clearbtn.grid(row = 0, column = 4)
별도로 알람이 진행이 되는 도중에 알람을 종료하고 싶을 경우를 위해서 알람 종료 버튼을 하나 생성해 주었다.
또 시간 정보를 잘못 입력했을 경우를 대비해서 시간 정보를 초기화할 수 있는 버튼도 하나 생성했다.
def message():
msg = messagebox.askquestion("알람을 종료하겠습니까?","알람을 종료하시겠습니까?")
if msg == "yes":
winsound.PlaySound(None, winsound.SND_PURGE)
root.quit()
def play():
winsound.PlaySound("sound.WAV", winsound.SND_ASYNC)
def alarm():
while (1):
gmstr = time.strftime("%d일%H시%M분", time.localtime())
time.sleep(1)
if gmstr == timeStr.get():
label.config(text="일어날 시간!")
play()
message()
break
알람이 실행되면 위와 같은 구조의 함수가 호출된다. time.strftime은 localtime의 반환값을 원하는 서식 문자로 변환해 준다.
예를 들어 %d는 day를 %H는 시간을 표현하도록 해준다. 다만 이 과정 중 시, 분, 초는 0~10까지의 값이 01,02,03..... 과같이 표현된다.
따라서 아래의 등장하는 조건을 통과할 수 없다. 그러므로 0~ 10의 값은 "0"+str(i)와 같은 방식으로 맞춰주었다.
play 함수는 음악을 재생시켜준다. 첫 요소는 원하는 사운드 파일이 있는 경로와 이름을, 두 번째 요소는 동작 형식을 설정해 주어야 한다.
SND_ASYNC의 경우 비동기식 재생을 원할 때 사용한다. 이외에도 무한 반복 재생을 원할 경우 SND_LOOP를 사용해 주어야 한다.
message 함수를 보면 SND_PURGE를 요소로 받는데 이는 정지를 뜻한다.
import tkinter as tk
from tkinter import messagebox
import time
import threading
import winsound
def message():
msg = messagebox.askquestion("알람을 종료하겠습니까?","알람을 종료하시겠습니까?")
if msg == "yes":
winsound.PlaySound(None, winsound.SND_PURGE)
root.quit()
def alarm():
while (1):
gmstr = time.strftime("%d일%H시%M분", time.localtime())
time.sleep(1)
if gmstr == timeStr.get():
label.config(text="일어날 시간!")
play()
message()
break
def update_day(event):
timeStr.set(timeStr.get() + str(dlistBox.get(dlistBox.curselection())) + "일")
def update_hour(event):
timeStr.set(timeStr.get() + str(hlistBox.get(hlistBox.curselection())) + "시")
def update_minute(event):
timeStr.set(timeStr.get() + str(mlistBox.get(mlistBox.curselection())) + "분")
def clear_time():
timeStr.set('')
def play():
winsound.PlaySound("sound.WAV", winsound.SND_ASYNC)
root = tk.Tk()
root.geometry("320x160")
root.title("알람")
root.resizable(0,0)
########## 프레임 생성 ###############
frame = tk.Frame(root, bg = "white", height = 10) # 최상단 프레임
frame.pack(fill = "both",side = "top")
baseFrame = tk.Frame(root, bg = "white", height = 100) # 버튼 및 리스트박스 구현, 배치를 위한 프레임
baseFrame.pack(fill = "both", expand = True)
timeStr = tk.StringVar() # 시간 정보를 저장할 변수
t1 = threading.Thread(target=alarm, args = timeStr.get()) # 알람의 병렬처리를 위한 스레드 생성
label = tk.Label(frame, text = "시간 입력(Day HH:MM):")
label.grid(row = 0, column = 0, padx = 2 , pady = 2)
entry = tk.Entry(frame, textvariable = timeStr) # 시간 정보가 입력되어 시각적으로 확인이 가능하도록 구현
entry.grid(row = 0, column = 1)
btn = tk.Button(frame , text = "입력", command = lambda : t1.start()) # 클릭 시 알람 실행
btn.grid(row = 0, column = 2)
#########################시간 선택#############################
dlistBox = tk.Listbox(baseFrame, width = 3 , height = 5)
for i in range(31):
dlistBox.insert(i,i+1)
hlistBox = tk.Listbox(baseFrame, width = 3, height = 5)
for i in range(24):
if i < 10:
hlistBox.insert(i,"0"+str(i)) # strftime의 1~10은 01~09로 시간을 반환하므로 0을 추가해줘야 매칭이 가능
else:
hlistBox.insert(i,i)
mlistBox = tk.Listbox(baseFrame, width = 5, height = 5)
for i in range(60):
if i < 10:
mlistBox.insert(i, "0" + str(i))
else:
mlistBox.insert(i, i)
dlistBox.grid(row = 1 , column = 0)
hlistBox.grid(row = 1 , column = 1)
mlistBox.grid(row = 1 , column = 2)
dlistLable = tk.Label(baseFrame,text = "Day")
dlistLable.grid(row = 0 , column = 0)
hlistLable = tk.Label(baseFrame,text = "Hour")
hlistLable.grid(row = 0 , column = 1)
mlistLable = tk.Label(baseFrame,text = "Minute")
mlistLable.grid(row = 0 , column = 2)
btn = tk.Button(baseFrame, text = "확인", command =
lambda x = dlistBox.curselection(): update_day(x), width = 3)
btn.grid(row = 2, column =0,padx = 1,pady =1)
btn = tk.Button(baseFrame, text = "확인", command =
lambda x = dlistBox.curselection(): update_hour(x), width = 3)
btn.grid(row = 2, column = 1,padx = 1,pady =1)
btn = tk.Button(baseFrame, text = "확인", command =
lambda x = dlistBox.curselection(): update_minute(x), width = 4)
btn.grid(row = 2, column = 2, padx = 1, pady = 1)
###############################################################################
btn = tk.Button(baseFrame, text = "알람 종료", command = message)
btn.grid(row = 2, column = 3, padx = 1, pady= 1)
clearbtn = tk.Button(baseFrame, text = "시간 수정", command = clear_time)
clearbtn.grid(row = 0, column = 4)
if __name__ == "__main__":
root.mainloop()
전체 코드는 위와 같다. 이를 구현하는데 많은 시간과 정보들이 필요했다.
중간중간 등장하는 알 수 없는 에러를 잡아내는 과정도 많은 도움이 되었다.
아직 기본적인 뼈대만 구성되었다고 할 수 있지만 다른 요소들을 추가하는 것을 염두에 두고 있다.
대표적으로 원하는 음원 파일을 재생시키는 기능이 있을 것이다.
다른 기능들로는 유튜브 링크를 입력하면 시간에 맞춰 재생시키는 기능을 구현해 보려고 한다.
위 기능을 구현하기 위해서 beatiful soup 같은 다양한 웹 관련 모듈들의 기능들을 찾아보는 중이다.