Tkcalendar: выровнять раскрывающийся список календаря по правому краю с DateEntry - PullRequest
1 голос
/ 03 августа 2020

По умолчанию выпадающий список календаря и виджеты DateEntry выровнены по левому краю. В одном случае календарь выходит за пределы экрана, как показано в pi c. Можем ли мы как-нибудь выровнять раскрывающийся список календаря по правому краю относительно соответствующего виджета DateEntry.

введите описание изображения здесь

1 Ответ

2 голосов
/ 03 августа 2020

Можно выровнять раскрывающийся список по правому краю, переписав метод drop_down() из DateEntry. Раскрывающийся список представляет собой верхний уровень, который располагается на экране с помощью

self._top_cal.geometry('+%i+%i' % (x, y))

, где (x, y) - это верхний левый угол раскрывающегося списка. Итак, для выпадающего списка с выравниванием по левому краю

x = self.winfo_rootx()  # the left side of the entry 

Теперь, чтобы получить выпадающий список с выравниванием по правому краю, нам нужно изменить x на

x = self.winfo_rootx() + self.winfo_width() - self._top_cal.winfo_reqwidth()

, а именно (положение правой стороны запись) - (ширина раскрывающегося списка).

Полный код:

from tkcalendar import DateEntry
import tkinter as tk

class MyDateEntry(DateEntry):
    def __init__(self, master=None, align='left', **kw):
        DateEntry.__init__(self, master, **kw)
        self.align = align

    def drop_down(self):
        """Display or withdraw the drop-down calendar depending on its current state."""
        if self._calendar.winfo_ismapped():
            self._top_cal.withdraw()
        else:
            self._validate_date()
            date = self.parse_date(self.get())
            if self.align == 'left':  # usual DateEntry
                x = self.winfo_rootx()
            else:  # right aligned drop-down
                x = self.winfo_rootx() + self.winfo_width() - self._top_cal.winfo_reqwidth()
            y = self.winfo_rooty() + self.winfo_height()
            if self.winfo_toplevel().attributes('-topmost'):
                self._top_cal.attributes('-topmost', True)
            else:
                self._top_cal.attributes('-topmost', False)
            self._top_cal.geometry('+%i+%i' % (x, y))
            self._top_cal.deiconify()
            self._calendar.focus_set()
            self._calendar.selection_set(date)


root = tk.Tk()

tk.Label(root, text='left align').grid(row=0, column=0)
tk.Label(root, text='right align').grid(row=0, column=1)
MyDateEntry(root).grid(row=1, column=0)
MyDateEntry(root, align='right').grid(row=1, column=1)

root.mainloop()    

РЕДАКТИРОВАТЬ : вы также можете определить, будет ли раскрывающийся список вне экрана и автоматически отрегулируйте раскрывающееся положение, чтобы избежать этого:

def drop_down(self):
    """Display or withdraw the drop-down calendar depending on its current state."""
    if self._calendar.winfo_ismapped():
        self._top_cal.withdraw()
    else:
        self._validate_date()
        date = self.parse_date(self.get())
        h = self._top_cal.winfo_reqheight()
        w = self._top_cal.winfo_reqwidth()
        x_max = self.winfo_screenwidth()
        y_max = self.winfo_screenheight()
        # default: left-aligned drop-down below the entry
        x = self.winfo_rootx()
        y = self.winfo_rooty() + self.winfo_height()
        if x + w > x_max:  # the drop-down goes out of the screen
            # right-align the drop-down
            x += self.winfo_width() - w
        if y + h > y_max:  # the drop-down goes out of the screen
            # bottom-align the drop-down
            y -= self.winfo_height() + h
        if self.winfo_toplevel().attributes('-topmost'):
            self._top_cal.attributes('-topmost', True)
        else:
            self._top_cal.attributes('-topmost', False)
        self._top_cal.geometry('+%i+%i' % (x, y))
        self._top_cal.deiconify()
        self._calendar.focus_set()
        self._calendar.selection_set(date)

Обратите внимание, что это решение не будет работать должным образом при использовании нескольких мониторов, поскольку tkinter обнаруживает только один большой прямоугольный angular экран.

...