You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用坐标禁用tkCalendar单元格时触发AttributeError问题求助

解决tkCalendar禁用日期单元格时的AttributeError问题

问题描述

尝试通过坐标访问tkCalendar内部组件禁用指定日期单元格时触发AttributeError,目标是让指定日期变为灰色且无法被用户选中。

原始代码

from tkcalendar import Calendar
from datetime import datetime
import tkinter as tk
from tkinter import *

def dates_play(self,date_entry):
        #Creating the calendar display
        self.cal_display = Calendar(self, selectmode = "day",date_pattern="mm-dd-yyyy", disableddaybackground="red")

        #Placing the widget in a way that it fills up the cell W+E+N+S
        self.cal_display.grid(row=1, rowspan=8,column=1, columnspan=2, sticky=tk.W+E+N+S, padx=10, pady=10)
        
        #Converts the date into a date_time object
        date_entry =datetime.strptime(date_entry, "%m-%d-%Y")

        # Retruning the day of the week as a number(X)
        day_no=date_entry.weekday()

        #Returing the week of the month as a number(Y)
        month_no = date_entry.isocalendar()[1]- date_entry.replace(day=1).isocalendar()[1]

        #Disabling that date
        self.cal_display = self.cal_display._calendar[month_no][day_no].state(['disabled'])

错误信息

Traceback (most recent call last):
  File "c:\Users\Lenovo\OneDrive\Documents\Amm_Uni\BOE Leave Management App\BOE_Leave_Management.py", line 339, in <module>
    app = LeaveBookingApp()
  File "c:\Users\Lenovo\OneDrive\Documents\Amm_Uni\BOE Leave Management App\BOE_Leave_Management.py", line 29, in __init__
    self.create_widgets()
  File "c:\Users\Lenovo\OneDrive\Documents\Amm_Uni\BOE Leave Management App\BOE_Leave_Management.py", line 65, in create_widgets
    self.confirm_button = tk.Button(self, text="Confirm", bg="#a6db9c", width=21, height=2, font= 'Times 15', command = self.dates_play("08-25-2023"))
  File "c:\Users\Lenovo\OneDrive\Documents\Amm_Uni\BOE Leave Management App\BOE_Leave_Management.py", line 320, in dates_play
    self.cal_display = self.cal_display[day_no][month_no].state(['dsiabled'])
  File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python310\lib\site-packages\tkcalendar\calendar_.py", line 514, in __getitem__
    raise AttributeError("Calendar object has no attribute %s." % key)
AttributeError: Calendar object has no attribute 4.

问题分析

  1. 按钮command参数错误:直接调用self.dates_play("08-25-2023")会在创建按钮时立即执行函数,而非点击时触发,导致初始化阶段尝试访问未渲染的日历组件。
  2. 内部组件访问错误:
    • 颠倒了_calendar的索引顺序(应为[周行][星期列]),且周数计算逻辑不准确。
    • _calendar是tkCalendar的内部私有属性,版本更新可能导致结构变化,不建议直接依赖。
  3. 变量覆盖错误:self.cal_display = self.cal_display._calendar[...]将Calendar实例覆盖为组件状态字符串,破坏后续引用。
  4. 拼写错误:['dsiabled']应为['disabled']。

修复方案

方案1:使用tkCalendar官方推荐的disabled_dates参数(推荐)

无需访问内部组件,直接通过Calendar构造参数指定禁用日期,自动实现灰色不可选中效果:

from tkcalendar import Calendar
from datetime import datetime
import tkinter as tk
from tkinter import *

class LeaveBookingApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.create_widgets()

    def create_widgets(self):
        # 用lambda延迟函数执行,避免按钮创建时立即调用
        self.confirm_button = tk.Button(self, text="Confirm", bg="#a6db9c", width=21, height=2, font='Times 15',
                                        command=lambda: self.dates_play("08-25-2023"))
        self.confirm_button.grid(row=0, column=0, padx=10, pady=10)

    def dates_play(self, date_entry_str):
        # 转换字符串为date对象
        date_entry = datetime.strptime(date_entry_str, "%m-%d-%Y").date()
        
        # 创建日历,直接指定禁用日期与样式
        self.cal_display = Calendar(
            self,
            selectmode="day",
            date_pattern="mm-dd-yyyy",
            disableddaybackground="gray",  # 禁用日期背景色
            disableddayforeground="white", # 禁用日期文字色
            disabled_dates=[date_entry]    # 要禁用的日期列表
        )
        
        # 布局日历
        self.cal_display.grid(row=1, rowspan=8, column=1, columnspan=2, sticky=tk.W+E+N+S, padx=10, pady=10)

if __name__ == "__main__":
    app = LeaveBookingApp()
    app.mainloop()

方案2:修正内部组件访问逻辑(不推荐,依赖内部实现)

若必须通过内部组件操作,需修正索引逻辑、确保组件渲染完成,并避免覆盖Calendar实例:

from tkcalendar import Calendar
from datetime import datetime
import tkinter as tk
from tkinter import *

class LeaveBookingApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.create_widgets()

    def create_widgets(self):
        self.confirm_button = tk.Button(self, text="Confirm", bg="#a6db9c", width=21, height=2, font='Times 15',
                                        command=lambda: self.dates_play("08-25-2023"))
        self.confirm_button.grid(row=0, column=0, padx=10, pady=10)

    def dates_play(self, date_entry_str):
        self.cal_display = Calendar(self, selectmode="day", date_pattern="mm-dd-yyyy", disableddaybackground="gray")
        self.cal_display.grid(row=1, rowspan=8, column=1, columnspan=2, sticky=tk.W+E+N+S, padx=10, pady=10)
        
        date_entry = datetime.strptime(date_entry_str, "%m-%d-%Y")
        
        # 转换星期索引:tkCalendar默认周起始为周日(索引0),datetime.weekday()周一为0、周日为6
        day_no = date_entry.weekday() + 1 if date_entry.weekday() != 6 else 0
        
        # 计算日期所在的周行索引(从0开始)
        month_no = (date_entry.day - 1) // 7
        
        # 确保日历渲染完成后再访问内部组件
        self.after(100, lambda: self.disable_date(month_no, day_no))

    def disable_date(self, row, col):
        try:
            # 获取对应日期按钮并设置禁用状态
            date_btn = self.cal_display._calendar[row][col]
            date_btn.state(['disabled'])
        except IndexError:
            print("指定日期不在当前日历视图中")

if __name__ == "__main__":
    app = LeaveBookingApp()
    app.mainloop()

效果说明

  • 方案1中,指定日期会自动变为灰色且无法被选中,无需手动操作内部组件,兼容性和稳定性更强。
  • 方案2需处理日历渲染时机和索引转换,仅在特殊场景下使用。

内容的提问来源于stack exchange,提问作者amsi_25

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:02:33