PythonでカレンダーをGUI表示させるデスクトップアプリを作成してみました。
その制作記録です。
GUIは外部パッケージのPySimpleGUI、その他に、標準ライブラリのcalendar、datetimeを使用しました。
PySimpleGUIの使い方↓
完成したコードは70行ほどで、「まとめ」にあります。
使用した環境
Windows10
Python 3.10.0
PySimpleGUI 4.56.0
制作の概要
そもそもPySimpleGUIにはcalendarボタンが備わっていますが、それはカレンダーから選択された日付を受け取るものでしょう。
カレンダーボタンの使用



上掲のカレンダーは小さくて、カレンダーを表示させる専用のものではありません。
なので、練習もかねて自分で作ってみることにしました。
おおまかな手順としては、レイアウトの作成をし、メインループ部分を作っていきます。
実際に出来上がったものは、次のような画像です。

レイアウトの作成
各モジュールのインポートと必要な変数を定義します。
import calendar
import datetime
import PySimpleGUI as sg
sg.theme('LightBlue6')
today = datetime.date.today()
cal_date = today
「cal_date」という変数に、カレンダー表示の基準となる月日のデータを保存します。
次に、レイアウトを作成する機能を「create_layout」関数にまとめました。
def create_layout(cal_date):
weekday = ['SUN','MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']
cal = calendar.Calendar(firstweekday=6)
days = cal.monthdatescalendar(cal_date.year, cal_date.month)
layout = [[sg.Text(cal_date.year, font=(None, 13, 'bold'))],
[sg.Push(), sg.Button('<<'), sg.Button('<'), sg.Text(cal_date.month, font=(None, 30)), sg.Button('>'), sg.Button('>>'), sg.Push()]]
inner = []
for week in weekday:
inner.append(sg.Text(week, size=(4,1), text_color='white', background_color='green', justification='center'))
layout.append(inner.copy())
def date_judgement(i, day):
if day == today:
return sg.Text(day.day, size=(4,1), justification='right', text_color='white', background_color='gray')
elif i == 0 and day.month == cal_date.month:
return sg.Text(day.day, size=(4,1), justification='right', text_color='red')
elif i == 6 and day.month == cal_date.month:
return sg.Text(day.day, size=(4,1), justification='right', text_color='blue')
elif day.month == cal_date.month:
return sg.Text(day.day, size=(4,1), justification='right')
elif i == 0:
return sg.Text(day.day, size=(4,1), justification='right', text_color='#ff9999')
elif i == 6:
return sg.Text(day.day, size=(4,1), justification='right', text_color='#9999ff')
else:
return sg.Text(day.day, size=(4,1), justification='right', text_color='#cccccc')
for row in days:
inner = []
for i, day in enumerate(row):
sg_text = date_judgement(i, day)
inner.append(sg_text)
layout.append(inner.copy())
return layout
cal = calendar.Calendar(firstweekday=6) days = cal.monthdatescalendar(cal_date.year, cal_date.month)
カレンダークラスのオブジェクトを作成し、カレンダーの週初めの曜日を日曜日に指定しています。
「days」に入るのはdetatimedate型の週のリストです。
最初の「layout」に代入しているのは次の部分です。

続いてのfor文では、曜日の部分を「inner」に追加していき、最終的に「layout」の末尾に「inner」を追加するものです。
アプリの次の部分に当たります。

最後のfor文で追加しているのが、日付に当たります。

「row」に入るのは日曜日スタートの一週間分の日付なので、enumerate(row)で代入される「i」は曜日番号、「day」はdatetime.date型になります。
# 2022年4月分のdaysの初週のデータ [datetime.date(2022, 3, 27), datetime.date(2022, 3, 28), datetime.date(2022, 3, 29), datetime.date(2022, 3, 30), datetime.date(2022, 3, 31), datetime.date(2022, 4, 1), datetime.date(2022, 4, 2)]
その際、「date_judgement」関数で、日付の判定を行いました。
引数の曜日番号と日付を使い、その日が今日なのか、カレンダーに表示させる当月の日曜かそれとも土曜日か、当月の平日か、当月ではない日曜・土曜・平日かを判定し、表示させる色を変えて、一行ごとに「inner」に追加するようにしています。
これも「layout」の末尾に追加し、最終的には「layout」を返します。
メインループ
def main():
cal_date = today
layout = create_layout(cal_date)
window = sg.Window('Simple Calendar', layout)
while True:
event, _ = window.read()
if event == sg.WIN_CLOSED:
break
elif event == '<':
cal_date = datetime.date(cal_date.year, cal_date.month, 1) - datetime.timedelta(days=1)
elif event == '>':
cal_date = datetime.date(cal_date.year, cal_date.month, 28) + datetime.timedelta(days=4)
elif event == '<<':
cal_date = datetime.date(cal_date.year - 1, cal_date.month, 1)
elif event == '>>':
cal_date = datetime.date(cal_date.year + 1, cal_date.month, 1)
window.close()
window = sg.Window('Simple Calendar', create_layout(cal_date))
window.close()
if __name__ == '__main__':
main()
冒頭で定義していた「cal_date」をmain関数内部に移動しました。
メインループの方では、ボタンによる処理を変えています。
'<'ボタンの場合、基準となる「cal_date」の年、月を使い、日付を1日にし、timedeltaで1日分引くことで、「cal_date」を前月のものに変更します。
'>'ボタンが押されると、「cal_date」のデータを使い、datetime.date型の日付を28日にし、timedelataで4日分足して、基準を翌月のものに変更します。
'<<'と'>>'ボタンはcal_dateの年から1を足すか、引くか、なので問題ないでしょう。
上記のボタンが押されると、windowが閉じ、更新された「cal_date」を使用して新しいwindowを作ります。
まとめ
これまでのコードをひとつにまとめます。
import calendar
import datetime
import PySimpleGUI as sg
sg.theme('LightBlue6')
today = datetime.date.today()
def create_layout(cal_date):
weekday = ['SUN','MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']
cal = calendar.Calendar(firstweekday=6)
days = cal.monthdatescalendar(cal_date.year, cal_date.month)
layout = [[sg.Text(cal_date.year, font=(None, 13, 'bold'))],
[sg.Push(), sg.Button('<<'), sg.Button('<'), sg.Text(cal_date.month, font=(None, 30)), sg.Button('>'), sg.Button('>>'), sg.Push()]]
inner = []
for week in weekday:
inner.append(sg.Text(week, size=(4,1), text_color='white', background_color='green', justification='center'))
layout.append(inner.copy())
def date_judgement(i, day):
if day == today:
return sg.Text(day.day, size=(4,1), justification='right', text_color='white', background_color='gray')
elif i == 0 and day.month == cal_date.month:
return sg.Text(day.day, size=(4,1), justification='right', text_color='red')
elif i == 6 and day.month == cal_date.month:
return sg.Text(day.day, size=(4,1), justification='right', text_color='blue')
elif day.month == cal_date.month:
return sg.Text(day.day, size=(4,1), justification='right')
elif i == 0:
return sg.Text(day.day, size=(4,1), justification='right', text_color='#ff9999')
elif i == 6:
return sg.Text(day.day, size=(4,1), justification='right', text_color='#9999ff')
else:
return sg.Text(day.day, size=(4,1), justification='right', text_color='#cccccc')
for row in days:
inner = []
for i, day in enumerate(row):
sg_text = date_judgement(i, day)
inner.append(sg_text)
layout.append(inner.copy())
return layout
def main():
cal_date = today
layout = create_layout(cal_date)
window = sg.Window('Simple Calendar', layout)
while True:
event, _ = window.read()
if event == sg.WIN_CLOSED:
break
elif event == '<':
cal_date = datetime.date(cal_date.year, cal_date.month, 1) - datetime.timedelta(days=1)
elif event == '>':
cal_date = datetime.date(cal_date.year, cal_date.month, 28) + datetime.timedelta(days=4)
elif event == '<<':
cal_date = datetime.date(cal_date.year - 1, cal_date.month, 1)
elif event == '>>':
cal_date = datetime.date(cal_date.year + 1, cal_date.month, 1)
window.close()
window = sg.Window('Simple Calendar', create_layout(cal_date))
window.close()
if __name__ == '__main__':
main()
あまり利用していませんが、GitHubでも公開しました。
このページが少しでもお役に立てたのなら幸いです。