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

