TkCalendar:尝试在屏幕上同时显示多个 DateEntries 会导致 TclError

TkCalendar: Trying to display multiple DateEntries on screen simultaneously leads to TclError

我目前正在做一个学校项目,该项目记录公司的员工信息,并根据员工可用性、工作角色等因素生成轮值表。

为了记录员工假期,我使用了模块 TkCalendar,它有自己的 Calendar 和 DateEntry 对象,可用于显示 GUI 日历信息。但是,我最近换了电脑,一段用来让用户添加假期的代码不再起作用了;看起来,当尝试创建第二个 DateEntry 对象时,TkCalendar 引发了一个错误,这似乎暗示我传递给第二个对象的选项无效。这很令人困惑,因为第一个 DateEntry 对象似乎生成得很好。以下是我遇到的问题的测试用例示例:

from tkinter import *
from tkcalendar import DateEntry

class TestApp:
    def __init__(self, root):
        self.root = root
        self.window = Frame(self.root)
        self.window.pack()

        self.label, self.calendar = [], []
        self.font = ('Impact Bold', 13, 'bold')

        labels = ['Select start date:', 'Select end date:']
        for i in range(2):
            self.label.append(Label(self.window, text=labels[i], font=self.font))
            self.label[-1].grid(row=i+1, column=0)

            self.calendar.append(DateEntry(self.window, font=self.font, locale='en_GB', width=15))
            self.calendar[-1].grid(row=i+1, column=3)


if __name__ == '__main__':
    root = Tk()
    app = TestApp(root)
    root.mainloop()

这会产生以下异常:

Traceback (most recent call last):
  File "C:\Users\xav\Documents\Python files\TkCalendar DateEntry test case.py", line 24, in <module>
    app = TestApp(root)
  File "C:\Users\xav\Documents\Python files\TkCalendar DateEntry test case.py", line 18, in __init__
    self.calendar.append(DateEntry(self.window, font=self.font, locale='en_GB', width=15))
  File "C:\Users\xav\AppData\Local\Programs\Python\Python38\lib\site-packages\tkcalendar\dateentry.py", line 105, in __init__
    self._setup_style()
  File "C:\Users\xav\AppData\Local\Programs\Python\Python38\lib\site-packages\tkcalendar\dateentry.py", line 160, in _setup_style
    self.style.map('DateEntry', **maps)
  File "C:\Users\xav\AppData\Local\Programs\Python\Python38\lib\tkinter\ttk.py", line 403, in map
    self.tk.call(self._name, "map", style, *_format_mapdict(kw)),
_tkinter.TclError: Invalid state name r

可以找到 TkCalendar 文档 here。提前感谢您的帮助!

此问题已在 tkcalendar https://github.com/j4321/tkcalendar/issues/61 and seems to come from a change in python https://bugs.python.org/issue38661 中报告。据我所知,它只发生在 Windows 中(我在 linux 中将 tkcalendar 与 python 3.8 一起使用没有问题)。问题是 self.style.map('TCombobox') 返回的样式映射不是有效的样式映射,而它曾经是并且应该根据 ttk.Style.map() 文档字符串。

以下是等待解决 python 问题时的临时修复。这个想法是覆盖触发错误的 DateEntry 方法,并手动向 DateEntry 提供正确的样式映射(请参见下面的代码)。

from tkcalendar import DateEntry as TkcDateEntry
import tkinter as tk

class DateEntry(TkcDateEntry):
    def _setup_style(self, event=None):
        # override problematic method to implement fix
        self.style.layout('DateEntry', self.style.layout('TCombobox'))
        self.update_idletasks()
        conf = self.style.configure('TCombobox')
        if conf:
            self.style.configure('DateEntry', **conf)
        # The issue comes from the line below:
        maps = self.style.map('TCombobox')
        if maps:
            try:
                self.style.map('DateEntry', **maps)
            except tk.TclError:
                # temporary fix to issue #61: manually insert correct map
                maps = {'focusfill': [('readonly', 'focus', 'SystemHighlight')],
                        'foreground': [('disabled', 'SystemGrayText'),
                                       ('readonly', 'focus', 'SystemHighlightText')],
                        'selectforeground': [('!focus', 'SystemWindowText')],
                        'selectbackground': [('!focus', 'SystemWindow')]}
                self.style.map('DateEntry', **maps)
        try:
            self.after_cancel(self._determine_downarrow_name_after_id)
        except ValueError:
            # nothing to cancel
            pass
        self._determine_downarrow_name_after_id = self.after(10, self._determine_downarrow_name)