[Python] tkinter로 간단하게 달력 만들기
카톡 하다가 이상한 달력 만들어보라는 이야기 나와서
그냥 평범한 달력 만들어봅니다.
근데 뤼튼씨가 나 블로그 하는 거에 꽂혀있어서
체험단 일정관리용으로 쓰라고 만들어줌.
나 진짜 소스코드 한줄도 안 쓰고 복붙으로만 프로그램 짜봄.
1. 프로젝트 소개
- 일정 관리가 필요한 사람들을 위한 심플한 달력
- tkinter로 만든 GUI 프로그램
- 클릭으로 일정 추가, 우클릭으로 삭제 가능
2. 필요한 모듈
import tkinter as tk
import calendar
import json
- tkinter : GUI 구현용 기본 모듈
- calendar : 달력 데이터 생성
- json : 일정 데이터 저장/불러오기
3. 클래스 구조
class Calendar:
def __init__(self, root):
self.schedules = {}
try:
with open('schedules.json', 'r') as f:
self.schedules = json.load(f)
except FileNotFoundError:
pass
- 달력 클래스 생성하고 schedules 초기화
- json 파일 있으면 저장된 일정 불러오기
- 없으면 빈 딕셔너리로 시작
4. 달력 표시 함수
def show_calendar(self):
days = ['일','월','화','수','목','금','토']
for i, day in enumerate(days):
label = tk.Label(self.frame, text=day)
label.grid(row=1, column=i)
cal = calendar.monthcalendar(self.year, self.month)
for i, week in enumerate(cal):
for j, day in enumerate(week):
if day != 0:
key = f"{self.year}-{self.month}-{day}"
text = str(day)
bg = "white"
if hasattr(self, 'schedules') and key in self.schedules:
schedule = self.schedules[key]
text = f"{day}\n{schedule['text']}"
bg = schedule['color']
btn = tk.Button(self.frame, text=text, bg=bg,
command=lambda d=day: self.add_schedule(d))
btn.bind('<Button-3>', lambda e, d=day: self.delete_schedule(d))
btn.grid(row=i+2, column=j)
- 달력 프레임 만들고
- 요일 먼저 표시
- 그 다음에 날짜 표시하는 구조
5. 일정 추가 팝업창
def add_schedule(self, date):
popup = tk.Toplevel()
popup.title("일정 추가")
tk.Label(popup, text="일정:").grid(row=0)
entry = tk.Entry(popup)
entry.grid(row=0, column=1)
colors = [("체험단", "red"), ("포스팅", "blue")]
var = tk.StringVar()
for i, (text, color) in enumerate(colors):
tk.Radiobutton(popup, text=text, value=color, variable=var).grid(row=i+1)
def save_and_close():
self.save_schedule(date, entry.get(), var.get())
popup.destroy()
save_btn = tk.Button(popup, text="저장", command=save_and_close)
save_btn.grid(row=3, columnspan=2)
- 날짜 클릭하면 팝업창 열리고
- 일정 입력하는 entry 창이랑
- 저장 버튼 누르면 save_and_close 실행되면서 저장 & 창 닫힘
- 아니 저장버튼 누르고 안 닫혀서 내가 기능 넣어달라고 추가요청함^^
6. 일정 저장 기능
def save_schedule(self, date, text, color):
if not hasattr(self, 'schedules'):
self.schedules = {}
key = f"{self.year}-{self.month}-{date}"
self.schedules[key] = {'text': text, 'color': color}
with open('schedules.json', 'w') as f:
json.dump(self.schedules, f)
self.update_calendar()
- 날짜와 일정을 딕셔너리에 저장
- json 파일로 저장해서 다음에도 볼 수 있게
- 저장 후 달력 화면 업데이트
7. 달력 업데이트
def update_calendar(self):
self.label.config(text=f"{self.year}년 {self.month}월")
self.show_calendar()
- 상단에 년월 표시 업데이트하고
- 달력 다시 그리기
8. 일정 삭제
def delete_schedule(self, date):
key = f"{self.year}-{self.month}-{date}"
if key in self.schedules:
del self.schedules[key]
with open('schedules.json', 'w') as f:
json.dump(self.schedules, f)
self.update_calendar()
- 우클릭하면 일정 삭제
- json 파일에서도 삭제
- 화면 새로고침
- 근데 삭제 후 UI가 좀 이상합니다.
- 뤼튼에게 말은 했으나 고치기에는 졸립니다.
- 나중에 언젠간 다시 해봅시다.
9. 실행 코드
root = tk.Tk()
app = Calendar(root)
root.mainloop()
- root 윈도우 생성하고
- Calendar 클래스 실행
- mainloop로 창 유지
10. 전체 소스 코드
import tkinter as tk
from datetime import datetime
import calendar
import json
class Calendar:
def __init__(self, root):
self.schedules = {}
try:
with open('schedules.json', 'r') as f:
self.schedules = json.load(f)
except FileNotFoundError:
pass
self.root = root
self.root.title("달력")
now = datetime.now()
self.year = now.year
self.month = now.month
self.frame = tk.Frame(root)
self.frame.grid(row=0, column=0)
self.label = tk.Label(self.frame, text=f"{self.year}년 {self.month}월")
self.label.grid(row=0, column=0, columnspan=7)
self.prev_button = tk.Button(self.frame, text="<", command=self.prev_month)
self.prev_button.grid(row=0, column=0)
self.next_button = tk.Button(self.frame, text=">", command=self.next_month)
self.next_button.grid(row=0, column=6)
self.show_calendar()
def show_calendar(self):
days = ['일','월','화','수','목','금','토']
for i, day in enumerate(days):
label = tk.Label(self.frame, text=day)
label.grid(row=1, column=i)
cal = calendar.monthcalendar(self.year, self.month)
for i, week in enumerate(cal):
for j, day in enumerate(week):
if day != 0:
key = f"{self.year}-{self.month}-{day}"
text = str(day)
bg = "white"
if hasattr(self, 'schedules') and key in self.schedules:
schedule = self.schedules[key]
text = f"{day}\n{schedule['text']}"
bg = schedule['color']
btn = tk.Button(self.frame, text=text, bg=bg,
command=lambda d=day: self.add_schedule(d))
btn.bind('<Button-3>', lambda e, d=day: self.delete_schedule(d))
btn.grid(row=i+2, column=j)
def prev_month(self):
if self.month > 1:
self.month -= 1
else:
self.year -= 1
self.month = 12
self.update_calendar()
def next_month(self):
if self.month < 12:
self.month += 1
else:
self.year += 1
self.month = 1
self.update_calendar()
def update_calendar(self):
self.label.config(text=f"{self.year}년 {self.month}월")
self.show_calendar()
def add_schedule(self, date):
popup = tk.Toplevel()
popup.title("일정 추가")
tk.Label(popup, text="일정:").grid(row=0)
entry = tk.Entry(popup)
entry.grid(row=0, column=1)
colors = [("체험단", "red"), ("포스팅", "blue")]
var = tk.StringVar()
for i, (text, color) in enumerate(colors):
tk.Radiobutton(popup, text=text, value=color, variable=var).grid(row=i+1)
def save_and_close():
self.save_schedule(date, entry.get(), var.get())
popup.destroy()
save_btn = tk.Button(popup, text="저장", command=save_and_close)
save_btn.grid(row=3, columnspan=2)
def save_to_file(self):
with open('schedules.json', 'w') as f:
json.dump(self.schedules, f)
def save_schedule(self, date, text, color):
if not hasattr(self, 'schedules'):
self.schedules = {}
key = f"{self.year}-{self.month}-{date}"
self.schedules[key] = {'text': text, 'color': color}
with open('schedules.json', 'w') as f:
json.dump(self.schedules, f)
self.update_calendar()
def delete_schedule(self, date):
key = f"{self.year}-{self.month}-{date}"
if key in self.schedules:
del self.schedules[key]
with open('schedules.json', 'w') as f:
json.dump(self.schedules, f)
self.update_calendar()
root = tk.Tk()
app = Calendar(root)
root.mainloop()
텍스트로 만든 프로그램 완성.
뤼튼, 나만의 AI라서 폰으로 왔다 갔다 복붙 힘들었는데
ChatGPT랑 PC로 했으면 더 편했을지도 모름.
거기다가 한 번에 일목요연하게 더 잘 짜주더라.
얘는 모바일이라 찔끔찔끔씩 감질맛 나게 짜줌.
아무튼 얼른 이거까지 했으니 진짜 기초부터
다시 공부하러 가야겠음.
하다가 중간에 막히면 혼자 해결을 못 한당.