Tkcalendar: Right align calendar dropdown with the DateEntry Tkcalendar: Right align calendar dropdown with the DateEntry tkinter tkinter

Tkcalendar: Right align calendar dropdown with the DateEntry


It is possible to right-align the dropdown by rewriting the drop_down() method of the DateEntry. The dropdown is a Toplevel which is positioned on the screen with

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

where (x, y) is the top-left corner of the dropdown. So, for a left-aligned dropdown

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

Now to get a right-aligned dropdown, we need to change x into

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

namely (position of the right side of the entry) - (width of the dropdown).

Full code:

from tkcalendar import DateEntryimport tkinter as tkclass 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()    

EDIT: you can also detect if the drop-down will be out of screen and automatically adjust the drop-down position to avoid that:

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)

Note that this solution will not work properly when using several monitors since tkinter detects only one big rectangular screen.