Tkcalendar:将日历下拉列表与 DateEntry 右对齐
Tkcalendar: Right align calendar dropdown with the DateEntry
默认情况下,日历下拉菜单和 DateEntry 小部件是左对齐的。在一种情况下,日历会超出屏幕,如图所示。我们能否以某种方式将日历下拉菜单与相应的 DateEntry 小部件右对齐。
可以通过重写 DateEntry
的 drop_down()
方法来 right-align 下拉菜单。下拉菜单是一个 Toplevel,它位于屏幕上
self._top_cal.geometry('+%i+%i' % (x, y))
其中 (x, y) 是下拉菜单的 top-left 角。因此,对于 left-aligned 下拉列表
x = self.winfo_rootx() # the left side of the entry
现在要获得 right-aligned 下拉列表,我们需要将 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()
编辑:您还可以检测drop-down是否会超出屏幕并自动调整drop-down位置以避免:
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 仅检测到一个大矩形屏幕,因此在使用多个显示器时此解决方案将无法正常工作。
默认情况下,日历下拉菜单和 DateEntry 小部件是左对齐的。在一种情况下,日历会超出屏幕,如图所示。我们能否以某种方式将日历下拉菜单与相应的 DateEntry 小部件右对齐。
可以通过重写 DateEntry
的 drop_down()
方法来 right-align 下拉菜单。下拉菜单是一个 Toplevel,它位于屏幕上
self._top_cal.geometry('+%i+%i' % (x, y))
其中 (x, y) 是下拉菜单的 top-left 角。因此,对于 left-aligned 下拉列表
x = self.winfo_rootx() # the left side of the entry
现在要获得 right-aligned 下拉列表,我们需要将 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()
编辑:您还可以检测drop-down是否会超出屏幕并自动调整drop-down位置以避免:
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 仅检测到一个大矩形屏幕,因此在使用多个显示器时此解决方案将无法正常工作。