今日も見に来てくださって、ありがとうございます。石川さんです。
自分でもしつこいなぁ、とは思いますが、Entryで日付を入力するためのウィジェットをつくる、パート3です。前回つくったのは、ちょっと納得できなかったのですよねぇ。ま、今回も納得したか、というと、微妙なのですけど、まあ、現状としてはこんなものでしょう。
実行イメージ
出来上がりイメージはこんな感じになります。
DateEntryをつくりました
スクリプト
スクリプトは以下のようになりました。日付を入力するだけなのに、ずいぶんとかかりました。ひょっとしたら車輪の再発明をしたのかも知れませんねぇ。
import tkinter.messagebox as mb
from datetime import datetime
class DateEntry(tk.Frame):
def __init__(self,master=None,frame_look={},**look):
args = dict(relief=tk.SUNKEN,bg="white")
tk.Frame.__init__(self, master, **args)
yearvc = (self.register(self.year_check), "%V", "%d", "%i", "%S", "%P")
monthvc = (self.register(self.month_check), "%V", "%d", "%i", "%S", "%P")
dayvc = (self.register(self.day_check), "%V", "%d", "%i", "%S", "%P")
self.year = tk.Entry(self,width=4,relief=tk.FLAT,validate="all",validatecommand=yearvc)
self.sep1 = tk.Label(self,text="/",relief=tk.FLAT,bg="white")
self.month = tk.Entry(self,width=2,relief=tk.FLAT,validate="all",validatecommand=monthvc)
self.sep2 = tk.Label(self,text="/",relief=tk.FLAT,bg="white")
self.day = tk.Entry(self,width=2,relief=tk.FLAT,validate="all",validatecommand=dayvc)
self.year.pack(side=tk.LEFT)
self.sep1.pack(side=tk.LEFT)
self.month.pack(side=tk.LEFT)
self.sep2.pack(side=tk.LEFT)
self.day.pack(side=tk.LEFT)
self.year.bind("<KeyPress>",lambda e:self.key('year', e))
self.month.bind("<KeyPress>",lambda e:self.key('month', e))
self.day.bind("<KeyPress>",lambda e:self.key('day', e))
def setPrev(self, prev=None):
def setNext(self, next=None,):
def year_check(self, event, command, index, char, proposed):
if command == '0': # Delete command
if event == 'key' and len(proposed) == 4:
def month_check(self, event, command, index, char, proposed):
if command == '0': # Delete command
if int(proposed) < 0 or 12 < int(proposed):
def day_check(self, event, command, index, char, proposed):
if command == '0': # Delete command
if int(proposed) < 0 or 31 < int(proposed):
if event == 'key' and len(proposed) == 2:
if event.keysym == 'Left':
if self.year.index(tk.INSERT) == 0:
if self.month.index(tk.INSERT) == 0:
if self.day.index(tk.INSERT) == 0:
elif event.keysym == 'Right':
if len(self.year.get()) == self.year.index(tk.INSERT):
if len(self.month.get()) == self.month.index(tk.INSERT):
if len(self.day.get()) == self.day.index(tk.INSERT):
d = datetime(year=int(self.year.get()),
month=int(self.month.get()),
self.year.delete(0,tk.END)
self.month.delete(0,tk.END)
self.day.delete(0,tk.END)
self.title("DateEntry test")
self.label = tk.Label(text="日付を入力してください(YYYY/MM/DD):")
self.label.grid(row=0,column=0)
self.date_entry = DateEntry()
self.clear_button = tk.Button(text="Clear", command=self.clear)
self.close_button = tk.Button(text="Close", command=self.destroy)
self.show = tk.Button(text="show", command=self.show)
self.date_entry.setPrev(self.show)
self.date_entry.setNext(self.clear_button)
self.date_entry.grid(row=0,column=1)
self.clear_button.grid(row=2,column=0)
self.close_button.grid(row=2,column=1)
self.show.grid(row=2,column=2)
d = self.date_entry.get()
message = "入力されたのは、「"+d.strftime("%Y/%m/%d")+"」です。"
message = "正しい日付がセットされていません。"
mb.showinfo(title="info",message=message)
if __name__ == "__main__":
import tkinter as tk
import tkinter.messagebox as mb
from datetime import datetime
class DateEntry(tk.Frame):
def __init__(self,master=None,frame_look={},**look):
args = dict(relief=tk.SUNKEN,bg="white")
args.update(frame_look)
tk.Frame.__init__(self, master, **args)
yearvc = (self.register(self.year_check), "%V", "%d", "%i", "%S", "%P")
monthvc = (self.register(self.month_check), "%V", "%d", "%i", "%S", "%P")
dayvc = (self.register(self.day_check), "%V", "%d", "%i", "%S", "%P")
self.year = tk.Entry(self,width=4,relief=tk.FLAT,validate="all",validatecommand=yearvc)
self.sep1 = tk.Label(self,text="/",relief=tk.FLAT,bg="white")
self.month = tk.Entry(self,width=2,relief=tk.FLAT,validate="all",validatecommand=monthvc)
self.sep2 = tk.Label(self,text="/",relief=tk.FLAT,bg="white")
self.day = tk.Entry(self,width=2,relief=tk.FLAT,validate="all",validatecommand=dayvc)
self.year.pack(side=tk.LEFT)
self.sep1.pack(side=tk.LEFT)
self.month.pack(side=tk.LEFT)
self.sep2.pack(side=tk.LEFT)
self.day.pack(side=tk.LEFT)
self.year.bind("<KeyPress>",lambda e:self.key('year', e))
self.month.bind("<KeyPress>",lambda e:self.key('month', e))
self.day.bind("<KeyPress>",lambda e:self.key('day', e))
def setPrev(self, prev=None):
self.prev = prev
def setNext(self, next=None,):
self.next = next
def year_check(self, event, command, index, char, proposed):
if event == 'key':
if command == '0': # Delete command
return True
else:
if not char.isdigit():
return False
if event == 'key' and len(proposed) == 4:
self.month.focus()
return True
def month_check(self, event, command, index, char, proposed):
if event == 'key':
if command == '0': # Delete command
return True
else:
if not char.isdigit():
return False
if int(proposed) < 0 or 12 < int(proposed):
return False
if len(proposed) == 2:
self.day.focus()
return True
def day_check(self, event, command, index, char, proposed):
if event == 'key':
if command == '0': # Delete command
return True
else:
if not char.isdigit():
return False
if int(proposed) < 0 or 31 < int(proposed):
return False
if event == 'key' and len(proposed) == 2:
if self.next:
self.next.focus()
return True
def key(self, w, event):
if event.keysym == 'Left':
if w == 'year':
if self.prev:
if self.year.index(tk.INSERT) == 0:
self.prev.focus()
elif w == 'month':
if self.month.index(tk.INSERT) == 0:
self.year.focus()
elif w == 'day':
if self.day.index(tk.INSERT) == 0:
self.month.focus()
elif event.keysym == 'Right':
if w == 'year':
if len(self.year.get()) == self.year.index(tk.INSERT):
self.month.focus()
elif w == 'month':
if len(self.month.get()) == self.month.index(tk.INSERT):
self.day.focus()
elif w == 'day':
if self.next:
if len(self.day.get()) == self.day.index(tk.INSERT):
self.next.focus()
def get(self):
try:
d = datetime(year=int(self.year.get()),
month=int(self.month.get()),
day=int(self.day.get()))
except:
return None
return d
def clear(self):
self.year.delete(0,tk.END)
self.month.delete(0,tk.END)
self.day.delete(0,tk.END)
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("DateEntry test")
self.label = tk.Label(text="日付を入力してください(YYYY/MM/DD):")
self.label.grid(row=0,column=0)
self.date_entry = DateEntry()
self.clear_button = tk.Button(text="Clear", command=self.clear)
self.close_button = tk.Button(text="Close", command=self.destroy)
self.show = tk.Button(text="show", command=self.show)
self.date_entry.setPrev(self.show)
self.date_entry.setNext(self.clear_button)
self.date_entry.grid(row=0,column=1)
self.clear_button.grid(row=2,column=0)
self.close_button.grid(row=2,column=1)
self.show.grid(row=2,column=2)
def clear(self):
self.date_entry.clear()
def show(self):
d = self.date_entry.get()
if d:
message = "入力されたのは、「"+d.strftime("%Y/%m/%d")+"」です。"
else:
message = "正しい日付がセットされていません。"
mb.showinfo(title="info",message=message)
if __name__ == "__main__":
app = App()
app.mainloop()
import tkinter as tk
import tkinter.messagebox as mb
from datetime import datetime
class DateEntry(tk.Frame):
def __init__(self,master=None,frame_look={},**look):
args = dict(relief=tk.SUNKEN,bg="white")
args.update(frame_look)
tk.Frame.__init__(self, master, **args)
yearvc = (self.register(self.year_check), "%V", "%d", "%i", "%S", "%P")
monthvc = (self.register(self.month_check), "%V", "%d", "%i", "%S", "%P")
dayvc = (self.register(self.day_check), "%V", "%d", "%i", "%S", "%P")
self.year = tk.Entry(self,width=4,relief=tk.FLAT,validate="all",validatecommand=yearvc)
self.sep1 = tk.Label(self,text="/",relief=tk.FLAT,bg="white")
self.month = tk.Entry(self,width=2,relief=tk.FLAT,validate="all",validatecommand=monthvc)
self.sep2 = tk.Label(self,text="/",relief=tk.FLAT,bg="white")
self.day = tk.Entry(self,width=2,relief=tk.FLAT,validate="all",validatecommand=dayvc)
self.year.pack(side=tk.LEFT)
self.sep1.pack(side=tk.LEFT)
self.month.pack(side=tk.LEFT)
self.sep2.pack(side=tk.LEFT)
self.day.pack(side=tk.LEFT)
self.year.bind("<KeyPress>",lambda e:self.key('year', e))
self.month.bind("<KeyPress>",lambda e:self.key('month', e))
self.day.bind("<KeyPress>",lambda e:self.key('day', e))
def setPrev(self, prev=None):
self.prev = prev
def setNext(self, next=None,):
self.next = next
def year_check(self, event, command, index, char, proposed):
if event == 'key':
if command == '0': # Delete command
return True
else:
if not char.isdigit():
return False
if event == 'key' and len(proposed) == 4:
self.month.focus()
return True
def month_check(self, event, command, index, char, proposed):
if event == 'key':
if command == '0': # Delete command
return True
else:
if not char.isdigit():
return False
if int(proposed) < 0 or 12 < int(proposed):
return False
if len(proposed) == 2:
self.day.focus()
return True
def day_check(self, event, command, index, char, proposed):
if event == 'key':
if command == '0': # Delete command
return True
else:
if not char.isdigit():
return False
if int(proposed) < 0 or 31 < int(proposed):
return False
if event == 'key' and len(proposed) == 2:
if self.next:
self.next.focus()
return True
def key(self, w, event):
if event.keysym == 'Left':
if w == 'year':
if self.prev:
if self.year.index(tk.INSERT) == 0:
self.prev.focus()
elif w == 'month':
if self.month.index(tk.INSERT) == 0:
self.year.focus()
elif w == 'day':
if self.day.index(tk.INSERT) == 0:
self.month.focus()
elif event.keysym == 'Right':
if w == 'year':
if len(self.year.get()) == self.year.index(tk.INSERT):
self.month.focus()
elif w == 'month':
if len(self.month.get()) == self.month.index(tk.INSERT):
self.day.focus()
elif w == 'day':
if self.next:
if len(self.day.get()) == self.day.index(tk.INSERT):
self.next.focus()
def get(self):
try:
d = datetime(year=int(self.year.get()),
month=int(self.month.get()),
day=int(self.day.get()))
except:
return None
return d
def clear(self):
self.year.delete(0,tk.END)
self.month.delete(0,tk.END)
self.day.delete(0,tk.END)
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("DateEntry test")
self.label = tk.Label(text="日付を入力してください(YYYY/MM/DD):")
self.label.grid(row=0,column=0)
self.date_entry = DateEntry()
self.clear_button = tk.Button(text="Clear", command=self.clear)
self.close_button = tk.Button(text="Close", command=self.destroy)
self.show = tk.Button(text="show", command=self.show)
self.date_entry.setPrev(self.show)
self.date_entry.setNext(self.clear_button)
self.date_entry.grid(row=0,column=1)
self.clear_button.grid(row=2,column=0)
self.close_button.grid(row=2,column=1)
self.show.grid(row=2,column=2)
def clear(self):
self.date_entry.clear()
def show(self):
d = self.date_entry.get()
if d:
message = "入力されたのは、「"+d.strftime("%Y/%m/%d")+"」です。"
else:
message = "正しい日付がセットされていません。"
mb.showinfo(title="info",message=message)
if __name__ == "__main__":
app = App()
app.mainloop()
解説
日付を入力するのに、途中のハイフンやスラッシュを入力するのはイケてないよねぇ、と思ったので、なんとかならないかとちょっと調べてみました。ここにありました。みんな同じようなこと考えるのですね。EntryとLabelを組み合わせたFrameをつくることで実現してありましたので、これを参考にしてつくったのが上記のスクリプトです。
年、月、日をそれぞれEntryでつくります。それぞれvalidatecommand
を設定しています。入力は数字のみ、年は4桁入力されたとき、月と日は2桁入力されたとき、それぞれ次の項目に移動するようにしています。月は1~12、日は1~31のみ入力できるようにしました。
あと、左右のキーで年月日を移動できるようにしてみました。ポイントは現在のカーソル位置が途中の時は項目間の移動ではなくて、項目内の移動にしなくてはならない、というところでしょうか。現在のカーソル位置は、tk.Entry.index(tk.INSERT)
で取得できました。
まとめ
日付項目を実装するだけなのに、けっこうなコーディング量になってしまいました。これって、誰かがもう作っているんじゃないかなぁ、と、思いながら作りましたが、楽しかったです♪