質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

7832閲覧

GUIカレンダーでクリックされた年月日を取得したい

3109

総合スコア80

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2020/01/12 02:35

編集2020/02/02 03:38

概要

下記サイトのPythonのtkinterで作成されたカレンダーを利用したいのですが、
クリックされた年月日の取得で躓いています。

python tkinter カレンダーの月めくり処理を実装する(カレンダー編③)
http://memopy.hatenadiary.jp/entry/2017/06/18/100406

memopy様、アップしていただきありがとうございます。
引用させていただきます。

実現したいこと

カレンダーの日付ボタンをクリックすると
クリックされたボタンのテキストを取得し、
トップに表示中の西暦と月を連結して
'20200112'のような8桁の年月日データを取り出したいと思っています。

(ゆくゆくはこのイベントで
選択された日の詳細データを取得する処理をさせる予定です。)

やってみたこと

クリックされたボタンのテキストを取得する関数を書き、
ボタンを作成するクラスとバインドさせることには成功したのですが、
トップに表示中の西暦と月の両テキストを取得するためには
どうしたらよいのかがわかりません。
イメージ説明
以下、私が新たに記述した関数です。

Python

1#左クリックされたカレンダーの年月日を取得する関数(作成中) 2def callback(event): 3 selected_date = '' 4 if event.widget['text'] not in ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']: 5 selected_date += '2020'#ここに表示中の年を代入したい 6 selected_date += '1'#ここに表示中の月を代入したい 7 selected_date += str(event.widget['text']) 8 print(selected_date)

スクリプト全文

以下、上記サイトから引用し手を加えたスクリプト全文です。

python

1# -*- coding:utf-8 -*- 2 3import tkinter as tk 4 5# カレンダーを作成するフレームクラス 6class mycalendar(tk.Frame): 7 def __init__(self,master=None,cnf={},**kw): 8 "初期化メソッド" 9 import datetime 10 tk.Frame.__init__(self,master,cnf,**kw) 11 12 # 現在の日付を取得 13 now = datetime.datetime.now() 14 # 現在の年と月を属性に追加 15 self.year = now.year 16 self.month = now.month 17 18 # frame_top部分の作成 19 frame_top = tk.Frame(self) 20 frame_top.pack(pady=5) 21 self.previous_month = tk.Label(frame_top, text = "<", font = ("",14)) 22 self.previous_month.bind("<1>",self.change_month) 23 self.previous_month.pack(side = "left", padx = 10) 24 self.current_year = tk.Label(frame_top, text = self.year, font = ("",18)) 25 self.current_year.pack(side = "left") 26 self.current_month = tk.Label(frame_top, text = self.month, font = ("",18)) 27 self.current_month.pack(side = "left") 28 self.next_month = tk.Label(frame_top, text = ">", font = ("",14)) 29 self.next_month.bind("<1>",self.change_month) 30 self.next_month.pack(side = "left", padx = 10) 31 32 # frame_week部分の作成 33 frame_week = tk.Frame(self) 34 frame_week.pack() 35 button_mon = d_button(frame_week, text = "Mon") 36 button_mon.grid(column=0,row=0) 37 button_tue = d_button(frame_week, text = "Tue") 38 button_tue.grid(column=1,row=0) 39 button_wed = d_button(frame_week, text = "Wed") 40 button_wed.grid(column=2,row=0) 41 button_thu = d_button(frame_week, text = "Thu") 42 button_thu.grid(column=3,row=0) 43 button_fri = d_button(frame_week, text = "Fri") 44 button_fri.grid(column=4,row=0) 45 button_sta = d_button(frame_week, text = "Sat", fg = "blue") 46 button_sta.grid(column=5,row=0) 47 button_san = d_button(frame_week, text = "Sun", fg = "red")#'San'→'Sun'と修正した 48 button_san.grid(column=6,row=0) 49 50 # frame_calendar部分の作成 51 self.frame_calendar = tk.Frame(self) 52 self.frame_calendar.pack() 53 54 # 日付部分を作成するメソッドの呼び出し 55 self.create_calendar(self.year,self.month) 56 57 def create_calendar(self,year,month): 58 "指定した年(year),月(month)のカレンダーウィジェットを作成する" 59 60 # ボタンがある場合には削除する(初期化) 61 try: 62 for key,item in self.day.items(): 63 item.destroy() 64 except: 65 pass 66 67 # calendarモジュールのインスタンスを作成 68 import calendar 69 cal = calendar.Calendar() 70 # 指定した年月のカレンダーをリストで返す 71 days = cal.monthdayscalendar(year,month) 72 73 # 日付ボタンを格納する変数をdict型で作成 74 self.day = {} 75 # for文を用いて、日付ボタンを生成 76 for i in range(0,42): 77 c = i - (7 * int(i/7)) 78 r = int(i/7) 79 try: 80 # 日付が0でなかったら、ボタン作成 81 if days[r][c] != 0: 82 self.day[i] = d_button(self.frame_calendar,text = days[r][c]) 83 self.day[i].grid(column=c,row=r) 84 except: 85 """ 86 月によっては、i=41まで日付がないため、日付がないiのエラー回避が必要 87 """ 88 break 89 90 def change_month(self,event): 91 # 押されたラベルを判定し、月の計算 92 if event.widget["text"] == "<": 93 self.month -= 1 94 else: 95 self.month += 1 96 # 月が0、13になったときの処理 97 if self.month == 0: 98 self.year -= 1 99 self.month = 12 100 elif self.month == 13: 101 self.year +=1 102 self.month =1 103 # frame_topにある年と月のラベルを変更する 104 self.current_year["text"] = self.year 105 self.current_month["text"] = self.month 106 # 日付部分を作成するメソッドの呼び出し 107 self.create_calendar(self.year,self.month) 108 109# デフォルトのボタンクラス 110class d_button(tk.Button): 111 def __init__(self,master=None,cnf={},**kw): 112 tk.Button.__init__(self,master,cnf,**kw) 113 self.configure(font=("",14),height=2, width=4, relief="flat") 114 self.bind('<Button-1>', callback)#オリジナルに追加した 115 116#左クリックされたカレンダーの年月日を取得する関数(作成中) 117def callback(event): 118 selected_date = '' 119 if event.widget['text'] not in ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']: 120 selected_date += '2020'#ここに表示中の年を代入したい 121 selected_date += '1'#ここに表示中の月を代入したい 122 selected_date += str(event.widget['text']) 123 print(selected_date) 124 125# ルートフレームの定義 126root = tk.Tk() 127root.title("Calendar App") 128mycal = mycalendar(root) 129mycal.pack() 130root.mainloop()

補足

**current_year["text"]current_month["text"]**が
目的のデータを格納していると思うのですが、
私はクラスに関して初心者のためどう扱ったらよいのかわかりません。

さらに私が新規で書いた関数についても記述する場所はここでよいのか、
それともどちらかのクラスのメソッドとした方がよいのか、よく理解できていません。
その点もアドバイスをいただけると幸いです。

お知恵をお貸しください。よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

3109さん
私も丁度同じようなことで色々調べていたので大変参考になりました。ありがとうございます。

ところで、この方法だとコードを実行したときに今月の日付を取得するには、
一度月のボタンをクリックし月を変化させないと年と月を取得できないのではないでしょうか?
そこで、私は「カレンダーの年月日を取得するコールバック関数」の部分を以下のようにしてみました。

def callback(event): selected_date = '' if event.widget['text'] not in ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']: try: selected_date += YEAR except: selected_date += str(now.year) selected_date += '/' try: selected_date += convert_in2_2bytes(MONTH) except: selected_date += convert_in2_2bytes(str(now.month)) selected_date += '/' selected_date += convert_in2_2bytes(str(event.widget['text'])) print(selected_date)

年と月が取得できないときには、今日の年月を取得することができました。
年月を分ける / は、この後私が使いやすい形式ししただけですが

投稿2020/03/04 09:08

Q_1986-kt

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

3109

2020/03/12 00:08

Q_1986-ktさん、回答ありがとうございます。 私もボタン押下時しか年月が取得できないことには気づいていましたが、 私のシステムでは年月が必要な時は必ずボタンを通過する設計になっていますので、 記述はしませんでした。'/'区切りについても同様の考えです。 実現したいことは人それぞれでしょうから、 これからここを訪れる迷い人の助けになるかもしれませんね。 ありがとうございます。
guest

0

自己解決

自己解決しました。
正確に言うと、目的実現のためにアプローチ法を変えました。

「GUIに表示されている年・月を取得する」ことを諦めて、
「年・月の表示が更新される度にグローバル変数を上書きし、
各日ボタン押下時にそれらの年・月・日を結合する」ことにしました。

以下スクリプト全文です。
私が付け足した部分及びブロックには
「追記」とコメントアウトしてあります。

Python

1# -*- coding:utf-8 -*- 2 3import tkinter as tk 4 5# カレンダーを作成するフレームクラス 6class mycalendar(tk.Frame): 7 def __init__(self,master=None,cnf={},**kw): 8 "初期化メソッド" 9 import datetime 10 tk.Frame.__init__(self,master,cnf,**kw) 11 12 # 現在の日付を取得 13 now = datetime.datetime.now() 14 # 現在の年と月を属性に追加 15 self.year = now.year 16 self.month = now.month 17 # 追記 https://teratail.com/questions/234639#reply-355304 18 global YEAR, MONTH 19 YEAR = str(self.year) 20 MONTH = str(self.month) 21 22 # frame_top部分の作成 23 frame_top = tk.Frame(self) 24 frame_top.pack(pady=5) 25 self.previous_month = tk.Label(frame_top, text = "<", font = ("",14)) 26 self.previous_month.bind("<1>",self.change_month) 27 self.previous_month.pack(side = "left", padx = 10) 28 self.current_year = tk.Label(frame_top, text = self.year, font = ("",18)) 29 self.current_year.pack(side = "left") 30 self.current_month = tk.Label(frame_top, text = self.month, font = ("",18)) 31 self.current_month.pack(side = "left") 32 self.next_month = tk.Label(frame_top, text = ">", font = ("",14)) 33 self.next_month.bind("<1>",self.change_month) 34 self.next_month.pack(side = "left", padx = 10) 35 36 # frame_week部分の作成 37 frame_week = tk.Frame(self) 38 frame_week.pack() 39 button_mon = d_button(frame_week, text = "Mon") 40 button_mon.grid(column=0,row=0) 41 button_tue = d_button(frame_week, text = "Tue") 42 button_tue.grid(column=1,row=0) 43 button_wed = d_button(frame_week, text = "Wed") 44 button_wed.grid(column=2,row=0) 45 button_thu = d_button(frame_week, text = "Thu") 46 button_thu.grid(column=3,row=0) 47 button_fri = d_button(frame_week, text = "Fri") 48 button_fri.grid(column=4,row=0) 49 button_sta = d_button(frame_week, text = "Sat", fg = "blue") 50 button_sta.grid(column=5,row=0) 51 button_san = d_button(frame_week, text = "Sun", fg = "red")#'San'→'Sun'と修正した 52 button_san.grid(column=6,row=0) 53 54 # frame_calendar部分の作成 55 self.frame_calendar = tk.Frame(self) 56 self.frame_calendar.pack() 57 58 # 日付部分を作成するメソッドの呼び出し 59 self.create_calendar(self.year,self.month) 60 61 def create_calendar(self,year,month): 62 "指定した年(year),月(month)のカレンダーウィジェットを作成する" 63 64 # ボタンがある場合には削除する(初期化) 65 try: 66 for key,item in self.day.items(): 67 item.destroy() 68 except: 69 pass 70 71 # calendarモジュールのインスタンスを作成 72 import calendar 73 cal = calendar.Calendar() 74 # 指定した年月のカレンダーをリストで返す 75 days = cal.monthdayscalendar(year,month) 76 77 # 日付ボタンを格納する変数をdict型で作成 78 self.day = {} 79 # for文を用いて、日付ボタンを生成 80 for i in range(0,42): 81 c = i - (7 * int(i/7)) 82 r = int(i/7) 83 try: 84 # 日付が0でなかったら、ボタン作成 85 if days[r][c] != 0: 86 self.day[i] = d_button(self.frame_calendar,text = days[r][c]) 87 self.day[i].grid(column=c,row=r) 88 except: 89 """ 90 月によっては、i=41まで日付がないため、日付がないiのエラー回避が必要 91 """ 92 break 93 94 def change_month(self,event): 95 # 押されたラベルを判定し、月の計算 96 if event.widget["text"] == "<": 97 self.month -= 1 98 else: 99 self.month += 1 100 # 月が0、13になったときの処理 101 if self.month == 0: 102 self.year -= 1 103 self.month = 12 104 elif self.month == 13: 105 self.year +=1 106 self.month =1 107 # frame_topにある年と月のラベルを変更する 108 self.current_year["text"] = self.year 109 self.current_month["text"] = self.month 110 111 # 追記 https://teratail.com/questions/234639#reply-355304 112 global YEAR, MONTH 113 YEAR = str(self.year) 114 MONTH = str(self.month) 115 116 # 日付部分を作成するメソッドの呼び出し 117 self.create_calendar(self.year,self.month) 118 119# デフォルトのボタンクラス 120class d_button(tk.Button): 121 def __init__(self,master=None,cnf={},**kw): 122 tk.Button.__init__(self,master,cnf,**kw) 123 self.configure(font=("",14),height=2, width=4, relief="flat") 124 self.bind('<Button-1>', callback)# 追記 https://teratail.com/questions/234639 125 126# カレンダーの年月日を取得するコールバック関数 127# 追記 https://teratail.com/questions/234639#reply-355304 128def callback(event): 129 selected_date = '' 130 if event.widget['text'] not in ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']: 131 selected_date += YEAR 132 selected_date += convert_in2_2bytes(MONTH) 133 selected_date += convert_in2_2bytes(str(event.widget['text'])) 134 print(selected_date) 135 136# 1桁の数字を2バイトに変換する関数 137# 追記 https://teratail.com/questions/234639#reply-355304 138def convert_in2_2bytes(str_number): 139 if len(str_number) == 1: 140 return '0' + str_number 141 else: 142 return str_number 143 144# ルートフレームの定義 145root = tk.Tk() 146root.title("Calendar App") 147mycal = mycalendar(root) 148mycal.pack() 149root.mainloop()

メインループ中にグローバル変数を宣言しなくても
動作するのを確認したので、明示はしませんでした。
もしも作法として誤りであれば御指摘ねがいます。

datetimeモジュールが吐き出す
年・月・日の桁数の検査は記述していません。
フル・フロンタル風に言うならば、
「もし、西暦がカンストしたとしたら、
それはもう、Pythonではなくなっているのではないかな。」
ということで。

もっとスマートで省メモリな書き方があれば、
ご教示ください。よろしくお願いします。

投稿2020/02/01 04:54

編集2020/03/11 23:42
3109

総合スコア80

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

selected_date += '2020'#ここに表示中の年を代入したい

selected_date += current_year["text"] #年のラベルの値を使う

にしては、どうでしょう?

投稿2020/01/12 03:25

coco_bauer

総合スコア6915

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

3109

2020/01/12 03:33

coco_bauerさん、回答ありがとうございます。 試してみたのですが下記のようにエラーとなります。 selected_date += current_year["text"] NameError: name 'current_year' is not defined やはり'current_year'がどこに属しているのかわからないので、 認識してくれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問