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]ifindx9999: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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。