Меню Tkinter для выбора даты

Я создаю раскрывающееся меню, которое будет использоваться для выбора даты начала. Он имеет 3 каскада с названиями год, месяц, день. Содержимое каскада дней генерируется таким образом, чтобы доступные дни соответствовали выбранному году и месяцу. Возможно/вероятно, что пользователь собирается изменить дату несколько раз в течение одного сеанса.

Моя проблема: когда пользователь впервые выбирает год/месяц, генерируются команды дней. После любой комбинации нового года/месяца следующий код просто добавляет команды в каскад. Так что дневной каскад содержит дни двух месяцев.

Я пытался заставить код удалить старую запись меню в строке меню и воссоздать ее на основе новых данных. Я хотел бы знать, как сделать такие изменения в уже существующей, работающей строке меню? Я искал документацию tkinter, но не мог понять, как ее реализовать.

import calendar as cal
import Tkinter as tk
import datetime
import os.path
window = tk.Tk()



# Menu variables:
year = tk.IntVar()
month = tk.IntVar()
day = tk.IntVar()
hour = tk.IntVar()
minute = tk.IntVar()
dur_hour = tk.IntVar()
dur_minute = tk.IntVar()
duration = tk.StringVar()
start = tk.StringVar()

#            list initializations    

list_of_years = []
list_of_months = []
list_of_hours = []
list_of_days = []
list_of_minutes = []


def year_seter(value):
    year.set(value)
    all_for_day()

def all_for_day(): #checks if the data needed to determine number of days in the month is present
    list_of_days = []
    y = year.get()
    m = month.get()
    lenght_of_month = cal.monthrange(y,m)
    lenght_of_month2 = lenght_of_month[1]
    if m != 0 and y != 0:
        make_daylist(lenght_of_month2)
        make_daymenu()

def month_seter(value):
    month.set(value)
    all_for_day()

def day_seter(value):
    day.set(value)

def time_parameters():
    the_date = datetime.datetime(1,1,1,0,0,0,0)
    the_date = the_date.now()
    end_year = the_date.year  
    make_yearlist(1995, end_year)
    make_monthlist()
    make_hourlist()
    make_minutelist()

def make_yearlist(the_year, end_year):
    while the_year <= end_year:
        list_of_years.append(the_year)
        the_year += 1

def make_monthlist():
    for i in range(12):
        list_of_months.append(i + 1)

def make_daylist(num_days):
    for i in range(num_days):
        list_of_days.append(i + 1)

def make_hourlist():
    for i in range(24):
        list_of_hours.append(i)

def make_minutelist():
    for i in range(60):
        list_of_minutes.append(i)

def make_daymenu():
    for the_day in list_of_days:
        daymenu.add_command(label=the_day, command=lambda : day_seter(the_day))
    window.config(menu=menubar)



# The following constructs the menu
time_parameters()
menubar = tk.Menu(window)

yearmenu = tk.Menu(menubar)
for the_year in list_of_years:
    yearmenu.add_command(label=str(the_year), command=lambda the_year=the_year: year_seter(the_year))
menubar.add_cascade(label = 'Year', menu=yearmenu)
window.config(menu=menubar)

monthmenu = tk.Menu(menubar)
for the_month in list_of_months:
    monthmenu.add_command(label=the_month, command=lambda the_month=the_month: month_seter(the_month))
menubar.add_cascade(label = 'Month', menu=monthmenu)
window.config(menu=menubar)  

daymenu = tk.Menu(menubar)
menubar.add_cascade(label = 'Day', menu=daymenu)
window.config(menu=menubar)

window.mainloop()

person Sasha    schedule 25.09.2011    source источник


Ответы (2)


Вы можете удалить все записи в daymenu перед добавлением новых:

def make_daymenu():
    daymenu.delete(0,31) 
    for the_day in list_of_days:
        daymenu.add_command(label=the_day, command=lambda : day_setter(the_day))
    window.config(menu=menubar)
person unutbu    schedule 25.09.2011
comment
конечно, во всех месяцах есть как минимум 28 дней, так что вам нужно добавить или убрать что-нибудь сверх этого. - person Bryan Oakley; 25.09.2011
comment
@ByanOakley: я не совсем понимаю, как реализовать то, что вы предлагаете. Это не так просто, как использовать daymenu.delete(29,31), так как это будет дублировать дни 1..28. Чтобы ограничить цикл for добавлением только необходимых дней, потребуется выяснить, какие дни уже есть в меню. Как это сделать? - person unutbu; 25.09.2011
comment
Я тоже думал об этом решении, но я все еще изучаю основы, поэтому понятия не имею, как его реализовать. В любом случае спасибо вам обоим за ваш вклад. - person Sasha; 25.09.2011
comment
@unutbu: как только вы заполните меню в первый раз, в нем всегда будет не менее 28 пунктов. Требуется совсем немного математики, чтобы вычислить индекс меню на каждый день, и есть способы получить индекс последнего пункта. В вашем случае будет проще каждый раз заново создавать меню. - person Bryan Oakley; 27.09.2011

наткнулся на ваш код для выбора даты. Недавно пришлось написать простенький календарь для подобных целей. Я могу предложить вам этот вариант в качестве альтернативы. Я думаю, что этот вариант более удобен для выбора даты.

import calendar, datetime, Tkinter

class calendarTk(Tkinter.Frame): # class calendarTk
    """ Calendar, the current date is exposed today, or transferred to date"""
    def __init__(self,master=None,date=None,dateformat="%d/%m/%Y",command=lambda i:None):
        Tkinter.Frame.__init__(self, master)
        self.dt=datetime.datetime.now() if date is None else datetime.datetime.strptime(date, dateformat) 
        self.showmonth()
        self.command=command
        self.dateformat=dateformat
    def showmonth(self): # Show the calendar for a month
        sc = calendar.month(self.dt.year, self.dt.month).split('\n')
        for t,c in [('<<',0),('<',1),('>',5),('>>',6)]: # The buttons to the left to the right year and month
            Tkinter.Button(self,text=t,relief='flat',command=lambda i=t:self.callback(i)).grid(row=0,column=c)
        Tkinter.Label(self,text=sc[0]).grid(row=0,column=2,columnspan=3) # year and month
        for line,lineT in [(i,sc[i+1]) for i in range(1,len(sc)-1)]: # The calendar
            for col,colT in [(i,lineT[i*3:(i+1)*3-1]) for i in range(7)]: # For each element
                obj=Tkinter.Button if colT.strip().isdigit() else Tkinter.Label # If this number is a button, or Label
                args={'command':lambda i=colT:self.callback(i)} if obj==Tkinter.Button else {} # If this button, then fasten it to the command
                bg='green' if colT.strip()==str(self.dt.day) else 'SystemButtonFace' # If the date coincides with the day of date - make him a green background
                fg='red' if col>=5 else 'SystemButtonText' # For the past two days, the color red
                obj(self,text="%s"% colT,relief='flat',bg=bg,fg=fg,**args).grid(row=line, column=col, ipadx=2, sticky='nwse') # Draw Button or Label
    def callback(self,but): # Event on the button
        if but.strip().isdigit():  self.dt=self.dt.replace(day=int(but)) # If you clicked on a date - the date change
        elif but in ['<','>','<<','>>']:
            day=self.dt.day
            if but in['<','>']: self.dt=self.dt+datetime.timedelta(days=30 if but=='>' else -30) # Move a month in advance / rewind
            if but in['<<','>>']: self.dt=self.dt+datetime.timedelta(days=365 if but=='>>' else -365) #  Year forward / backward
            try: self.dt=self.dt.replace(day=day) # We are trying to put the date on which stood
            except: pass                          # It is not always possible
        self.showmonth() # Then always show calendar again
        if but.strip().isdigit(): self.command(self.dt.strftime(self.dateformat)) # If it was a date, then call the command

if __name__ == '__main__':
    def com(f): print f
    root = Tkinter.Tk()
    root.title("Monthly Calendar")
    c=calendarTk(root,date="21/11/2006",dateformat="%d/%m/%Y",command=com)
    c.pack()

    root.mainloop()
person Arty    schedule 01.10.2011