Я пытаюсь создать простой пользовательский виджет календаря в python, используя tkinter. Я хочу создать это как выпадающий при выборе. Я создал базу всего кода. Мне было интересно, есть ли способ создать свой собственный календарь в виде выпадающего меню, похожего на меню параметров. Я хочу, чтобы он выпал из списка при выборе даты и автоматически закрывался при обновлении даты.
Примечание: я знаю, что будет проще использовать что-то вроде tkcalendar, но мне не нравится этот интерфейс, и я предпочел бы создать свой собственный.
Редактировать: я сейчас использую grid забудь скрыть / показать календарь. Единственная проблема - он перемещает все виджеты внизу. Я хочу, чтобы он как бы накладывался на следующие виджеты.
CODE:
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
. Есть ли способ выполнить sh без использования .pack
?