Python

[Python] tkinter로 간단하게 달력 만들기

creato 2024. 12. 12. 00:34
728x90
반응형
SMALL

카톡 하다가 이상한 달력 만들어보라는 이야기 나와서

그냥 평범한 달력 만들어봅니다.

 

근데 뤼튼씨가 나 블로그 하는 거에 꽂혀있어서

체험단 일정관리용으로 쓰라고 만들어줌.

나 진짜 소스코드 한줄도 안 쓰고 복붙으로만 프로그램 짜봄.

 

 

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로 했으면 더 편했을지도 모름.

거기다가 한 번에 일목요연하게 더 잘 짜주더라.

얘는 모바일이라 찔끔찔끔씩 감질맛 나게 짜줌.

 

아무튼 얼른 이거까지 했으니 진짜 기초부터

다시 공부하러 가야겠음.

하다가 중간에 막히면 혼자 해결을 못 한당.

 

 

728x90
반응형
LIST