Custom Tkinter Dropdown - PullRequest
       0

Custom Tkinter Dropdown

0 голосов
/ 25 января 2020

Я пытаюсь создать простой пользовательский виджет календаря в 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?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...