今日も見に来てくださって、ありがとうございます。石川さんです。
自分でもしつこいなぁ、とは思いますが、Entryで日付を入力するためのウィジェットをつくる、パート3です。前回つくったのは、ちょっと納得できなかったのですよねぇ。ま、今回も納得したか、というと、微妙なのですけど、まあ、現状としてはこんなものでしょう。
実行イメージ
出来上がりイメージはこんな感じになります。
スクリプト
スクリプトは以下のようになりました。日付を入力するだけなのに、ずいぶんとかかりました。ひょっとしたら車輪の再発明をしたのかも知れませんねぇ。
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)
で取得できました。
まとめ
日付項目を実装するだけなのに、けっこうなコーディング量になってしまいました。これって、誰かがもう作っているんじゃないかなぁ、と、思いながら作りましたが、楽しかったです♪