import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import numpy as np
import os
from datetime import datetime, timedelta
# 日本語フォントの設定 (Windows向け)
# ご自身の環境に合うフォントパスを指定してください。
# 例: 'C:/Windows/Fonts/YuGothM.ttc' または 'C:/Windows/Fonts/msgothic.ttc'
font_path = 'C:/Windows/Fonts/msgothic.ttc' # または YuGothM.ttc, YuGothR.ttc など
try:
if not os.path.exists(font_path):
raise FileNotFoundError(f"指定されたフォントファイルが見つかりません: {font_path}\n"
"ご自身のWindowsにインストールされている日本語フォントのパスに変更してください。")
fm.fontManager.addfont(font_path)
# フォントファイルを指定したら、そのフォントのファミリー名を指定します
# 例: 'msgothic.ttc' なら 'MS Gothic', 'yugothic.ttc' なら 'Yu Gothic'
plt.rcParams['font.family'] = 'MS Gothic' # 使用するフォントファミリー名を設定
except FileNotFoundError as e:
print(e)
print("フォント設定なしで続行します。日本語が豆腐になる可能性があります。")
plt.rcParams['font.family'] = 'sans-serif'
except Exception as e:
print(f"フォント設定中にエラーが発生しました: {e}")
plt.rcParams['font.family'] = 'sans-serif'
# データの定義
# 時間配分をtimedeltaで計算
# 時刻は24時間表記で指定
# TODO:一旦、固定値のままやるけど、ここは後で引数(input)や外出しにする
schedule = [
{"label": "睡眠", "start": "00:00", "end": "07:00"},
{"label": "朝食・準備", "start": "07:00", "end": "07:50"},
{"label": "通勤", "start": "07:50", "end": "08:40"},
{"label": "業務", "start": "08:40", "end": "11:30"}, # 業務1
{"label": "昼休", "start": "11:30", "end": "12:30"},
{"label": "業務", "start": "12:30", "end": "17:40"}, # 業務2
{"label": "通勤", "start": "17:40", "end": "19:00"},
{"label": "夕食・家事", "start": "19:00", "end": "20:30"},
{"label": "自由", "start": "20:30", "end": "23:00"},
{"label": "無", "start": "23:00", "end": "24:00"} # 24:00 は翌日の00:00として扱う
]
sizes = []
labels = []
# TODO:値のチェックを入れる
for item in schedule:
start_time_str = item["start"]
end_time_str = item["end"]
# "24:00" を "00:00" として扱い、日をまたぐフラグを設定
is_next_day = False
if end_time_str == "24:00":
end_time_str_parsed = "00:00"
is_next_day = True
else:
end_time_str_parsed = end_time_str
# 日付は仮で設定し、時間のみを考慮
start_dt = datetime.strptime(start_time_str, "%H:%M")
end_dt = datetime.strptime(end_time_str_parsed, "%H:%M")
# 終了時刻が開始時刻より前の場合、または "24:00" で翌日扱いの場合、翌日として扱う
if end_dt < start_dt or is_next_day:
time_diff_hours = (end_dt + timedelta(days=1) - start_dt).total_seconds() / 3600
else:
time_diff_hours = (end_dt - start_dt).total_seconds() / 3600
sizes.append(time_diff_hours)
labels.append(item["label"])
# 各セグメントの色 (元のschedule_chart.jpgの配色に近づける)
# TODO:一旦、固定値のままやるけど、ここは後で引数(input)や外出しにする
colors = [
'#3B609E', # 睡眠 (濃い青)
'#6A9E4D', # 朝食・準備 (緑)
'#4D99B1', # 通勤1 (水色)
'#DC5D5C', # 業務1 (赤)
'#F2DC73', # 昼休 (黄)
'#E77A3D', # 業務2 (オレンジ)
'#4D99B1', # 通勤2 (水色)
'#7D4D9E', # 夕食・家事 (紫)
'#EBD748', # 自由 (明るい黄)
'#DD4D4D' # 無 (赤)
]
# グラフの開始角度 (0時が上、時計回り)
# Matplotlibのpieチャートはデフォルトで90度(12時の位置)から反時計回りに描画される
# 0時が上になるように90度から時計回りに描画
start_angle = 90
# 円グラフの作成
fig, ax = plt.subplots(figsize=(10, 10)) # グラフのサイズを設定
# 円グラフ (ドーナツ型ではないのでwidthを削除)
wedges, _ = ax.pie(sizes, colors=colors, startangle=start_angle,
counterclock=False) # 時計回り
# ラベルの配置調整
# 各ウェッジの中心角度を計算し、ラベルを配置
# TODO:一旦、固定値のままやるけど、ここは後で引数(input)や外出しにする⇒これに対応させる
for i, (wedge, label) in enumerate(zip(wedges, labels)):
angle = (wedge.theta2 + wedge.theta1) / 2 # ウェッジの中心角度
# ラベルのオフセットを調整する係数
# 短いセグメントや端にあるラベルは少し外側に、長いセグメントのラベルは内側に
# 元の画像に合わせて調整
text_radius_factor = 0.75 # デフォルトの距離 (円の中心からの相対距離)
text_color = 'black' # デフォルトの文字色
if label == '睡眠':
text_radius_factor = 0.65 # 中央よりやや内側
text_color = 'white'
elif label == '朝食・準備':
text_radius_factor = 0.75
text_color = 'white'
elif label == '通勤':
text_radius_factor = 0.75
# 最初の通勤(index 2)は水色背景なので白文字、2番目の通勤(index 6)も水色なので白文字
text_color = 'white' if i in [2, 6] else 'black'
elif label == '業務':
text_radius_factor = 0.65
text_color = 'white'
elif label == '昼休':
text_radius_factor = 0.75
text_color = 'black'
elif label == '夕食・家事':
text_radius_factor = 0.65
text_color = 'white'
elif label == '自由':
text_radius_factor = 0.75
text_color = 'black'
elif label == '無':
text_radius_factor = 0.75
text_color = 'white' # 無も赤背景なので白文字
else:
text_radius_factor = 0.75
text_color = 'black'
# 座標計算
# 円グラフの中心からの距離を調整(radiusはwedgeの半径)
x = wedge.r * text_radius_factor * np.cos(np.deg2rad(angle))
y = wedge.r * text_radius_factor * np.sin(np.deg2rad(angle))
ax.text(x, y, label, ha='center', va='center', fontsize=12, color=text_color,
bbox=dict(boxstyle="round,pad=0.3", fc="none", ec="none", alpha=0.0)) # 背景ボックスを透明に
# 各時間の目盛りの追加 (円グラフの外周に配置)
circle_radius = 1.0 # 円グラフの半径 (Matplotlibのpieはデフォルトで1.0)
for hour in range(24):
# 0時が上 (90度) から時計回り
angle_rad = np.deg2rad(90 - (hour / 24) * 360)
# 目盛り線の外側
x_tick_outer = circle_radius * np.cos(angle_rad)
y_tick_outer = circle_radius * np.sin(angle_rad)
# 目盛り線の内側 (中心からの距離)
x_tick_inner = (circle_radius - 0.03) * np.cos(angle_rad) # 少しだけ内側から
y_tick_inner = (circle_radius - 0.03) * np.sin(angle_rad)
# 短い目盛り線を引く
ax.plot([x_tick_inner, x_tick_outer], [y_tick_inner, y_tick_outer], color='gray', linestyle='-', linewidth=0.7)
# 時間の数字 (目盛り線の少し外側)
text_radius_hour = circle_radius + 0.1 # 数字を配置する半径
text_x = text_radius_hour * np.cos(angle_rad)
text_y = text_radius_hour * np.sin(angle_rad)
ax.text(text_x, text_y, f'{hour}時', ha='center', va='center', fontsize=10)
ax.set_aspect('equal') # 円を正円に保つ
ax.set_title('1日の時間配分', fontsize=16, pad=20) # グラフのタイトルと余白
# グラフをPNG画像として保存
output_filename = 'daily_schedule_chart_final_v3.png'
fig.savefig(output_filename, bbox_inches='tight', dpi=300)
print(f"グラフが '{output_filename}' として保存されました。")
plt.show() # グラフを表示