Python tkinter实现日期选择器
如何利用Python的tkinter模块实现日期选择器,根据我在网上的搜索情况,这一块一直是一个盲点。虽然也有接近的答案,并没有真正实用的,我经过几天的探索,终于摸索出一套可用的,分享给大家。
首先,定义一个类,叫Calendar,这个是搬运来的。
#-*-coding:utf-8-*- importcalendar importtkinterastk importtkinter.fontastkFont fromtkinterimportttk datetime=calendar.datetime.datetime timedelta=calendar.datetime.timedelta classCalendar: def__init__(s,point=None): s.master=tk.Toplevel() s.master.withdraw() s.master.attributes('-topmost',True) fwday=calendar.SUNDAY year=datetime.now().year month=datetime.now().month locale=None sel_bg='#ecffc4' sel_fg='#05640e' s._date=datetime(year,month,1)#每月第一日 s._selection=None#设置为未选中日期 s.G_Frame=ttk.Frame(s.master) s._cal=s.__get_calendar(locale,fwday) s.__setup_styles()#创建自定义样式 s.__place_widgets()#pack/grid小部件 s.__config_calendar()#调整日历列和安装标记 #配置画布和正确的绑定,以选择日期。 s.__setup_selection(sel_bg,sel_fg) #存储项ID,用于稍后插入。 s._items=[s._calendar.insert('','end',values='')for_inrange(6)] #在当前空日历中插入日期 s._update() s.G_Frame.pack(expand=1,fill='both') s.master.overrideredirect(1) s.master.update_idletasks() width,height=s.master.winfo_reqwidth(),s.master.winfo_reqheight() s.height=height ifpoint: x,y=point[0],point[1] else: x,y=(s.master.winfo_screenwidth()-width)/2,(s.master.winfo_screenheight()-height)/2 s.master.geometry('%dx%d+%d+%d'%(width,height,x,y))#窗口位置居中 s.master.after(300,s._main_judge) s.master.deiconify() s.master.focus_set() s.master.wait_window()#这里应该使用wait_window挂起窗口,如果使用mainloop,可能会导致主程序很多错误 def__get_calendar(s,locale,fwday): iflocaleisNone: returncalendar.TextCalendar(fwday) else: returncalendar.LocaleTextCalendar(fwday,locale) def__setitem__(s,item,value): ifitemin('year','month'): raiseAttributeError("attribute'%s'isnotwriteable"%item) elifitem=='selectbackground': s._canvas['background']=value elifitem=='selectforeground': s._canvas.itemconfigure(s._canvas.text,item=value) else: s.G_Frame.__setitem__(s,item,value) def__getitem__(s,item): ifitemin('year','month'): returngetattr(s._date,item) elifitem=='selectbackground': returns._canvas['background'] elifitem=='selectforeground': returns._canvas.itemcget(s._canvas.text,'fill') else: r=ttk.tclobjs_to_py({item:ttk.Frame.__getitem__(s,item)}) returnr[item] def__setup_styles(s): #自定义TTK风格 style=ttk.Style(s.master) arrow_layout=lambdadir:( [('Button.focus',{'children':[('Button.%sarrow'%dir,None)]})] ) style.layout('L.TButton',arrow_layout('left')) style.layout('R.TButton',arrow_layout('right')) def__place_widgets(s): #标头框架及其小部件 Input_judgment_num=s.master.register(s.Input_judgment)#需要将函数包装一下,必要的 hframe=ttk.Frame(s.G_Frame) gframe=ttk.Frame(s.G_Frame) bframe=ttk.Frame(s.G_Frame) hframe.pack(in_=s.G_Frame,side='top',pady=5,anchor='center') gframe.pack(in_=s.G_Frame,fill=tk.X,pady=5) bframe.pack(in_=s.G_Frame,side='bottom',pady=5) lbtn=ttk.Button(hframe,style='L.TButton',command=s._prev_month) lbtn.grid(in_=hframe,column=0,row=0,padx=12) rbtn=ttk.Button(hframe,style='R.TButton',command=s._next_month) rbtn.grid(in_=hframe,column=5,row=0,padx=12) s.CB_year=ttk.Combobox(hframe,width=5,values=[str(year)foryearinrange(datetime.now().year,datetime.now().year-11,-1)],validate='key',validatecommand=(Input_judgment_num,'%P')) s.CB_year.current(0) s.CB_year.grid(in_=hframe,column=1,row=0) s.CB_year.bind('',lambdaevent:s._update(event,True)) s.CB_year.bind("< >",s._update) tk.Label(hframe,text='年',justify='left').grid(in_=hframe,column=2,row=0,padx=(0,5)) s.CB_month=ttk.Combobox(hframe,width=3,values=['%02d'%monthformonthinrange(1,13)],state='readonly') s.CB_month.current(datetime.now().month-1) s.CB_month.grid(in_=hframe,column=3,row=0) s.CB_month.bind("< >",s._update) tk.Label(hframe,text='月',justify='left').grid(in_=hframe,column=4,row=0) #日历部件 s._calendar=ttk.Treeview(gframe,show='',selectmode='none',height=7) s._calendar.pack(expand=1,fill='both',side='bottom',padx=5) ttk.Button(bframe,text="确定",width=6,command=lambda:s._exit(True)).grid(row=0,column=0,sticky='ns',padx=20) ttk.Button(bframe,text="取消",width=6,command=s._exit).grid(row=0,column=1,sticky='ne',padx=20) tk.Frame(s.G_Frame,bg='#565656').place(x=0,y=0,relx=0,rely=0,relwidth=1,relheigh=2/200) tk.Frame(s.G_Frame,bg='#565656').place(x=0,y=0,relx=0,rely=198/200,relwidth=1,relheigh=2/200) tk.Frame(s.G_Frame,bg='#565656').place(x=0,y=0,relx=0,rely=0,relwidth=2/200,relheigh=1) tk.Frame(s.G_Frame,bg='#565656').place(x=0,y=0,relx=198/200,rely=0,relwidth=2/200,relheigh=1) def__config_calendar(s): #cols=s._cal.formatweekheader(3).split() cols=['日','一','二','三','四','五','六'] s._calendar['columns']=cols s._calendar.tag_configure('header',background='grey90') s._calendar.insert('','end',values=cols,tag='header') #调整其列宽 font=tkFont.Font() maxwidth=max(font.measure(col)forcolincols) forcolincols: s._calendar.column(col,width=maxwidth,minwidth=maxwidth, anchor='center') def__setup_selection(s,sel_bg,sel_fg): def__canvas_forget(evt): canvas.place_forget() s._selection=None s._font=tkFont.Font() s._canvas=canvas=tk.Canvas(s._calendar,background=sel_bg,borderwidth=0,highlightthickness=0) canvas.text=canvas.create_text(0,0,fill=sel_fg,anchor='w') canvas.bind(' ',__canvas_forget) s._calendar.bind(' ',__canvas_forget) s._calendar.bind(' ',s._pressed) def_build_calendar(s): year,month=s._date.year,s._date.month header=s._cal.formatmonthname(year,month,0) #更新日历显示的日期 cal=s._cal.monthdayscalendar(year,month) forindx,iteminenumerate(s._items): week=cal[indx]ifindx 9999:return s._canvas.place_forget() s._date=datetime(year,month,1) s._build_calendar()#重建日历 ifyear==datetime.now().yearandmonth==datetime.now().month: day=datetime.now().day for_item,day_listinenumerate(s._cal.monthdayscalendar(year,month)): ifdayinday_list: item='I00'+str(_item+2) column='#'+str(day_list.index(day)+1) s.master.after(100,lambda:s._pressed(item=item,column=column,widget=s._calendar)) def_exit(s,confirm=False): ifnotconfirm:s._selection=None s.master.destroy() def_main_judge(s): """判断窗口是否在最顶层""" try: ifs.master.focus_displayof()==Noneor'toplevel'notinstr(s.master.focus_displayof()):s._exit() else:s.master.after(10,s._main_judge) except: s.master.after(10,s._main_judge) defselection(s): """返回表示当前选定日期的日期时间。""" ifnots._selection:returnNone year,month=s._date.year,s._date.month returnstr(datetime(year,month,int(s._selection[0])))[:10] defInput_judgment(s,content): """输入判断""" ifcontent.isdigit()orcontent=="": returnTrue else: returnFalse
如何使用这个类呢?直接调用即可,什么参数都不用。如图
直接调用这个类,就出现了一个选择器
其实你也可以用参数,比如Calendar(100,100),这个参数是调整选择器的坐标位置的,问题是没啥用,没有参数选择器就出现在了屏幕的正中央,凑合用吧。
显然,仅仅这样是不足以实用的,于是我又封装了一个datepicker类,需要调用Calendar类
classdatepicker: def__init__(s,window,axes):#窗口对象坐标 s.window=window s.frame=tk.Frame(s.window,padx=5) s.frame.grid(row=axes[0],column=axes[1]) s.start_date=tk.StringVar()#开始日期 s.end_date=tk.StringVar()#结束日期 s.bt1=tk.Button(s.frame,text='开始',command=lambda:s.getdate('start'))#开始按钮 s.bt1.grid(row=0,column=0) s.ent1=tk.Entry(s.frame,textvariable=s.start_date)#开始输入框 s.ent1.grid(row=0,column=1) s.bt2=tk.Button(s.frame,text='结束',command=lambda:s.getdate('end')) s.bt2.grid(row=0,column=2) s.ent2=tk.Entry(s.frame,textvariable=s.end_date) s.ent2.grid(row=0,column=3) defgetdate(s,type):#获取选择的日期 fordatein[Calendar().selection()]: ifdate: if(type=='start'):#如果是开始按钮,就赋值给开始日期 s.start_date.set(date) elif(type=='end'): s.end_date.set(date) #执行 if__name__=='__main__': window=tk.Tk() window.wm_attributes('-topmost',True)#窗口置顶 tk.Label(window,text='日期段一:').grid(row=0,column=0) obj=datepicker(window,(0,1))#初始化类为对象 startstamp1=obj.start_date.get()#获取开始时期 endstamp1=obj.end_date.get() tk.Label(window,text='日期段二:').grid(row=1,column=0) obj=datepicker(window,(1,1)) startstamp2=obj.start_date.get() endstamp2=obj.end_date.get() window.mainloop()
执行效果如图:
目的是搞成一个日期段的效果。所以datepicker类里面包括了一个开始按钮,开始输入框,结束按钮,结束输入框。并把这四个
组件放在了一个frame里面。所以使用的时候,先建立一个window,然后把window以及frame的位置坐标传入datepicker类即可。比如datepicker(window,(1,1))
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。