【发布时间】:2020-05-11 16:22:09
【问题描述】:
我正在尝试使用 tkinter 在 python 中创建一个简单的自定义日历小部件。我想在选择时将其创建为下拉列表。我已经创建了整个代码的基础。我想知道是否有一种方法可以将我的自定义日历创建为类似于选项菜单的下拉菜单。我希望它在我选择日期时下拉,并在我更新日期时自动关闭。
注意:我知道使用 tkcalendar 之类的东西会更容易,但我不喜欢那个界面,更愿意创建自己的界面。
编辑:我现在使用网格忘记隐藏/显示日历。唯一的问题是它将其下方的所有小部件向下移动。我希望它覆盖在下面的小部件上。
代码:
import tkinter as tk
import datetime
from calendar import monthrange
class CalendarDatePicker:
def __init__(self, frame, font=("Times New Roman", 12), bg="white", fg="black", bg_selected="light gray", fg_selected="black"):
self.__font = font
self.__bg = bg
self.__fg = fg
self.__bg_selected = bg_selected
self.__fg_selected = fg_selected
self.__frame = tk.Frame(frame, bg=self.__bg)
self.__selected_date = None
self.__top_frame = tk.Frame(self.__frame, bg=self.__bg)
self.__date_frame = tk.Frame(self.__frame, bg=self.__bg)
self.__dates = {}
today = datetime.datetime.today()
self.__dates["year"] = int(today.year)
self.__dates["month"] = today.strftime("%B")
self.__dates["date"] = today.day
self.__dates["selected"] = today
self.__update()
def __toggle(self, show=False):
if show:
self.__top_frame.grid(row=1, column=0, pady=5, padx=2)
self.__date_frame.grid(row=2, column=0, pady=5, padx=2)
else:
self.__top_frame.grid_remove()
self.__date_frame.grid_remove()
self.__frame.update()
def __shift_year(self, shift):
self.__dates["year"] = int(self.__dates["year"]) + shift
self.__CurYear.destroy()
self.__update()
def __shift_month(self, shift):
mn = int(datetime.datetime.strptime(self.__dates["month"], "%B").month) + shift
if mn == 0:
mn = 12
self.__dates["year"] = int(self.__dates["year"])-1
self.__CurYear.destroy()
elif mn == 13:
mn = 1
self.__dates["year"] = int(self.__dates["year"])+1
self.__CurYear.destroy()
self.__dates["month"] = datetime.datetime(2000, mn, 1).strftime("%B")
self.__CurMonth.destroy()
self.__update()
def __set_date(self, date):
if date.strip() != '':
self.__dates["date"] = date
self.__dates["selected"] = datetime.datetime(int(self.__dates["year"]),int(datetime.datetime.strptime(self.__dates["month"], "%B").month),int(self.__dates["date"]))
self.__toggle(False)
self.__update()
def get(self, date_format="%Y-%m-%d"):
return self.__dates["selected"].strftime(date_format)
def create(self):
return self.__frame
def __update(self):
if self.__selected_date is not None:
self.__selected_date.destroy()
self.__selected_date = tk.Label(self.__frame, width=17, bg=self.__bg, fg=self.__fg, font=("Times New Roman", 16, "bold"), text=self.get("%d %B, %Y"), relief='solid', bd=1)
self.__selected_date.bind('<Button-1>', lambda event, show=True: self.__toggle(show))
self.__selected_date.grid(row=0, column=0)
today = self.__dates["selected"]
self.__YearShiftPrev = tk.Label(self.__top_frame, fg=self.__fg, text="<", relief="solid", bd=1, bg=self.__bg)
self.__YearShiftPrev.bind('<Button-1>', lambda event, x=-1: self.__shift_year(x))
self.__YearShiftPrev.grid(row=0, column=1, padx=2)
self.__CurYear = tk.Label(self.__top_frame, fg=self.__fg, width=6, text=self.__dates["year"], bg=self.__bg)
self.__CurYear.grid(row=0, column=2, padx=2)
self.__YearShiftNext = tk.Label(self.__top_frame, fg=self.__fg, text=">", relief="solid", bd=1, bg=self.__bg)
self.__YearShiftNext.bind('<Button-1>', lambda event, x=1: self.__shift_year(x))
self.__YearShiftNext.grid(row=0, column=3, padx=2)
self.__MonthShiftPrev = tk.Label(self.__top_frame, fg=self.__fg, text="<", relief="solid", bd=1, bg=self.__bg)
self.__MonthShiftPrev.bind('<Button-1>', lambda event, x=-1: self.__shift_month(x))
self.__MonthShiftPrev.grid(row=0, column=5, padx=2)
self.__CurMonth = tk.Label(self.__top_frame, fg=self.__fg, width=10, text=self.__dates["month"], bg=self.__bg)
self.__CurMonth.grid(row=0, column=6, padx=2)
self.__MonthShiftNext = tk.Label(self.__top_frame, fg=self.__fg, text=">", relief="solid", bd=1, bg=self.__bg)
self.__MonthShiftNext.bind('<Button-1>', lambda event, x=1: self.__shift_month(x))
self.__MonthShiftNext.grid(row=0, column=7, padx=2)
day, total_days = monthrange(int(self.__dates["year"]), int(datetime.datetime.strptime(self.__dates["month"], "%B").month))
for ind, text in enumerate(["M", "T", "W", "T", "F", "S", "S"]):
lbl = tk.Label(self.__date_frame, fg=self.__fg, text=text, width=3, bg=self.__bg, relief="solid", bd=1)
lbl.grid(row=0, column=ind%7, pady=1, padx=1)
date = 1
cur_month = today.year == self.__dates["year"] and today.strftime("%B") == self.__dates["month"]
for i in range(42):
row, col = 1+i//7, i%7
if col == day and date <= total_days:
day = (day+1)%7
text = str(date)
date += 1
else:
text = ' '
bg, fg = (self.__bg_selected, self.__fg_selected) if (text==str(self.__dates["date"]) and cur_month) else (self.__bg, self.__fg)
lbl = tk.Label(self.__date_frame, width=3, text=text, bg=bg, fg=fg, relief="solid", bd=1)
lbl.bind("<Button-1>", lambda event, date=text: self.__set_date(date))
lbl.grid(row=row, column=col, pady=1, padx=1)
self.__frame.update()
window = tk.Tk()
calendar = CalendarDatePicker(window, bg="black", fg="white", bg_selected="white", fg_selected="black")
calendar.create().pack()
for i in range(10):
tk.Label(window, text=i).pack()
变化:
def __toggle(self, show=False):
if show:
self.__top_frame.grid(row=1, column=0, pady=5, padx=2)
self.__date_frame.grid(row=2, column=0, pady=5, padx=2)
else:
self.__top_frame.grid_remove()
self.__date_frame.grid_remove()
self.__frame.update()
self.__toggle(False) 在__set_date 函数的末尾被调用。
self.__toggle(True) id 在__update 函数下的第一个标签上调用。我已经绑定它来调用那里的函数。
我基本上所做的是添加了一种隐藏/显示日历部分的方法。但是现在当我切换它时,所有标签(1-9)都移到了下面。我希望它覆盖小部件。或者更准确地覆盖它们并在切换方法上发现它们。
我对此进行了更多调查,发现一个解决方案是使用.place 而不是.grid。但是在实际程序中,我想将它与grid 管理器一起使用。有什么方法可以在不使用.pack 的情况下做到这一点?
【问题讨论】:
-
“我想让它下拉”:你想下拉哪一部分? “当我选择日期时”:
bind('<Button-1>'到selected_date = tk.Label。 -
我希望当我点击日期时,它会显示整个日期选择器。当我再次选择日期时,它应该隐藏日期选择器并更新标签。
-
继续阅读
grid_forget() -
好的,我查过了。它非常有用。现在唯一的问题是当我使用它时,它将其下方的所有小部件移动到框架下方。我希望日历在下面的小部件上进行排序。我已经使用 grid_forget 更新了代码
-
“它移动了它下面的所有小部件”:验证了您的示例,它工作正常。 Edit 你的问题并展示你是怎么做的
.bind(...以及你在哪里打电话给toggle(False)
标签: python python-3.x tkinter