【実現したいこと(Must要件)】
①画面の左右どちらかの半分のみにカレンダーを描画すること
②各日付をセルごとで区分けし、その境界線を描画すること
③日曜・祝日は赤色、土曜日は水色、平日は黒色の文字とすること
④現在の日付はセル内を塗りつぶすことで表現すること
⑤祝日は一か月毎に、最新の情報となるようにすること。(専用ライブラリの使用)
⑥カレンダーは壁外の上に重ねて描画すること
⑦月名はカレンダー上部に記載すること
⑧カレンダーは全画面表示にすること
⑨本カレンダーは1日1回更新されるようにすること
この要件を満たすように、コードを記述すると下記のようになります。
from PIL import Image, ImageDraw, ImageFont import datetime import jpholiday import calendar import pygame from datetime import datetime, timedelta import time def create_calendar_image(year, month, day): # 祝日を取得する関数 def get_holidays(year, month): first_day = datetime(year, month, 1) last_day = (first_day + timedelta(days=32)).replace(day=1) - timedelta(days=1) holidays = [] for day in range(1, last_day.day + 1): date = datetime(year, month, day) if jpholiday.is_holiday(date): holidays.append(day) return holidays # フォントの読み込み font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" # DejaVu Sansのフォントファイルパス font_size_month = 40 font_size_days = 20 font_month = ImageFont.truetype(font_path, font_size_month) font_days = ImageFont.truetype(font_path, font_size_days) # 画像サイズ image_width = 800 image_height = 480 background_color = (255, 255, 255) image = Image.new("RGB", (image_width, image_height), background_color) draw = ImageDraw.Draw(image) # 月に応じた背景画像の選択 background_images = ["1.png", "2.png", "3.png", "4.png", "5.png", "6.png"] bg_image_file = background_images[(month - 1) % len(background_images)] # 背景画像の描画 bg_image = Image.open(bg_image_file) bg_image = bg_image.resize((image_width, image_height), Image.Resampling.LANCZOS) image.paste(bg_image, (0, 0)) # カレンダーの設定 cal = calendar.Calendar(firstweekday=6) # 日曜日から始まる month_days = cal.monthdayscalendar(year, month) holidays = get_holidays(year, month) # セルのサイズ(正方形) if bg_image_file == "4.png": cell_size = min((image_width * 0.8 // 2 - 10) // 7, (image_height - 100) // len(month_days)) # 月名のために高さを減らす else: cell_size = min((image_width // 2 - 10) // 7, (image_height - 100) // len(month_days)) # 月名のために高さを減らす cell_width = cell_size cell_height = cell_size # カレンダーの位置設定 if bg_image_file == "1.png": calendar_x_offset = image_width // 2 + 10 calendar_y_offset = (image_height - (cell_height * len(month_days))) // 2 + 50 # 月名のためにオフセットを増やす elif bg_image_file in ["2.png", "3.png", "6.png"]: calendar_x_offset = 10 calendar_y_offset = (image_height - (cell_height * len(month_days))) // 2 + 50 # 月名のためにオフセットを増やす elif bg_image_file == "4.png": calendar_x_offset = image_width - (cell_width * 7) - 10 calendar_y_offset = 80 # 月名のためにオフセットを増やす elif bg_image_file == "5.png": calendar_x_offset = 10 calendar_y_offset = 80 # 月名のためにオフセットを増やす # 月名の描画 month_name = now.strftime("%B") month_name_pos = (calendar_x_offset, calendar_y_offset - 50) # カレンダーの上に配置 draw.text(month_name_pos, month_name, font=font_month, fill="black") # カレンダーの日付の描画 for week_index, week in enumerate(month_days): for day_index, day in enumerate(week): if day != 0: # 0はその月に含まれない日付 day_str = str(day) x = calendar_x_offset + day_index * cell_width + cell_width // 2 y = calendar_y_offset + week_index * cell_height + cell_height // 2 # 今日の日付のセルを薄緑で塗りつぶす if now.day == day: draw.rectangle( [x - cell_width // 2, y - cell_height // 2, x + cell_width // 2, y + cell_height // 2], fill="lightgreen" ) # 祝日の色を赤色に設定 if day in holidays: fill_color = "red" elif day_index == 0: # 日曜日 fill_color = "red" elif day_index == 6: # 土曜日 fill_color = "blue" else: fill_color = "black" # 日付の描画 draw.text((x, y), day_str, font=font_days, fill=fill_color, anchor="mm") # グリッド線の描画 draw.rectangle( [x - cell_width // 2, y - cell_height // 2, x + cell_width // 2, y + cell_height // 2], outline="black" ) # 画像の保存 image.save("calendar.png") # Pygameを使って画像を全画面表示 pygame.init() screen = pygame.display.set_mode((800, 480), pygame.FULLSCREEN) # メインループ running = True while running: now = datetime.now() create_calendar_image(now.year, now.month, now.day) calendar_image = pygame.image.load("calendar.png") for event in pygame.event.get(): if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): running = False screen.blit(calendar_image, (0, 0)) pygame.display.flip() time.sleep(60) # 86400秒(1日)待機 pygame.quit()