[Python] Is my tkcalendar clashing with ttkbootstrap?

Discussão em 'Python' iniciado por Stack, Agosto 31, 2025.

  1. Stack

    Stack Membro Participativo

    So when I run my code I get this error:

    Exception in Tkinter callback
    Traceback (most recent call last):
    File "C:\Users\sandw\AppData\Local\Programs\Python\Python313\Lib\tkinter\__init__.py", line 2074, in __call__
    return self.func(*args)
    ~~~~~~~~~^^^^^^^
    File "d:\BluCerulean\app.py", line 483, in calendar_window
    CW(self, self.settings.get("user"), master=cal_win)
    ~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "d:\BluCerulean\calendar_editor.py", line 42, in __init__
    self.cal = TKCal(left_frame, selectmode='day', date_pattern='yyyy-mm-dd', font=("Segoe UI", 14))
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "C:\Users\sandw\AppData\Local\Programs\Python\Python313\Lib\site-packages\tkcalendar\calendar_.py", line 224, in __init__
    ttk.Frame.__init__(self, master, class_=classname, cursor=curs, name=name)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "C:\Users\sandw\AppData\Local\Programs\Python\Python313\Lib\site-packages\ttkbootstrap\style.py", line 5209, in __init__
    self.configure(style=ttkstyle)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
    File "C:\Users\sandw\AppData\Local\Programs\Python\Python313\Lib\site-packages\tkcalendar\calendar_.py", line 1612, in configure
    self[item] = value
    ~~~~^^^^^^
    File "C:\Users\sandw\AppData\Local\Programs\Python\Python313\Lib\site-packages\tkcalendar\calendar_.py", line 517, in __setitem__
    if key not in self._properties:
    ^^^^^^^^^^^^^^^^
    AttributeError: 'Calendar' object has no attribute '_properties'


    Is it because my tkcalendar cannot put a calendar in a ttkbootstrap app?

    This is my calendar function of my app.py code (the code where I call the calendar file)



    # ---------------- CALENDAR ----------------
    def calendar_window(self):
    # Ensure root is visible
    if not self.root.winfo_viewable():
    self.root.deiconify()

    # Open the calendar in a Toplevel
    cal_win = Toplevel(self.root)
    cal_win.title("Blu Cerulean - Calendar")
    cal_win.state("zoomed")

    from calendar_editor import CalendarWindow as CW
    # Pass the App instance, user, and the Toplevel as parent
    CW(self, self.settings.get("user"), master=cal_win)



    if __name__ == "__main__":
    root = Tk()
    app = App(root)
    root.mainloop()


    and this is my calendar_editor.py code (the build of the calendar window)

    import os
    import json
    import tkinter as tk
    from tkinter import Toplevel, simpledialog, messagebox, Frame, Label, Button, Listbox, Text, END
    from tkcalendar import Calendar as TKCal
    from tkinter import ttk # plain ttk

    REMINDERS_FILENAME = "reminders.json"

    class CalendarWindow:
    def __init__(self, app, username, master=None):
    self.app = app
    self.username = username or "guest"
    self.app_folder = getattr(app, 'app_folder', os.getcwd())
    self.cal_folder = os.path.join(self.app_folder, 'calendar', self.username)
    os.makedirs(self.cal_folder, exist_ok=True)
    self.reminders_path = os.path.join(self.cal_folder, REMINDERS_FILENAME)

    self.reminders = self._load_reminders()

    # Use the passed parent if provided, else create new Toplevel
    self.win = master if master is not None else Toplevel(app.root)
    self.win.title("Blu Cerulean - Calendar")
    self.win.state("zoomed") # maximize


    # Main frames
    main_frame = Frame(self.win) # plain Frame, not ttkbootstrap
    main_frame.pack(fill="both", expand=True, padx=10, pady=10)

    left_frame = Frame(main_frame)
    left_frame.grid(row=0, column=0, sticky="nsew", padx=(0,10))

    right_frame = Frame(main_frame)
    right_frame.grid(row=0, column=1, sticky="nsew")

    main_frame.columnconfigure(0, weight=1)
    main_frame.columnconfigure(1, weight=1)
    main_frame.rowconfigure(0, weight=1)

    # Calendar (plain tkcalendar inside plain Frame)
    self.cal = TKCal(left_frame, selectmode='day', date_pattern='yyyy-mm-dd', font=("Segoe UI", 14))
    self.cal.pack(fill="both", expand=True)
    self.cal.bind("<<CalendarSelected>>", lambda e: self.refresh_list())

    # Listbox + description frame
    list_desc_frame = Frame(right_frame)
    list_desc_frame.pack(fill="both", expand=True)

    # Listbox
    list_frame = Frame(list_desc_frame)
    list_frame.pack(side="top", fill="both", expand=True)
    self.list_label = Label(list_frame, text="Reminders for selected day:")
    self.list_label.pack(anchor="w")
    self.listbox = Listbox(list_frame)
    self.listbox.pack(fill="both", expand=True)
    self.listbox.bind("<<ListboxSelect>>", lambda e: self.show_description())

    # Description box
    desc_frame = Frame(list_desc_frame)
    desc_frame.pack(side="top", fill="both", expand=True, pady=(10,0))
    desc_label = Label(desc_frame, text="Description:")
    desc_label.pack(anchor="w")
    self.desc_text = Text(desc_frame, height=6)
    self.desc_text.pack(fill="both", expand=True)

    # Buttons
    btn_frame = Frame(right_frame)
    btn_frame.pack(side="bottom", pady=10, fill="x")
    Button(btn_frame, text="Add Reminder", command=self.add_reminder).pack(side="left", padx=5)
    Button(btn_frame, text="Delete Selected", command=self.delete_reminder).pack(side="left", padx=5)
    Button(btn_frame, text="Refresh", command=self.refresh_list).pack(side="left", padx=5)
    Button(btn_frame, text="Add Description", command=self.add_description).pack(side="left", padx=5)
    Button(btn_frame, text="Edit Description", command=self.edit_description).pack(side="left", padx=5)
    Button(btn_frame, text="Remove Description", command=self.remove_description).pack(side="left", padx=5)

    self.refresh_list()

    # ---------------- Reminder management ----------------
    def _load_reminders(self):
    if os.path.exists(self.reminders_path):
    try:
    with open(self.reminders_path, 'r', encoding='utf-8') as f:
    return json.load(f)
    except Exception:
    return {}
    return {}

    def _save_reminders(self):
    try:
    os.makedirs(self.cal_folder, exist_ok=True)
    with open(self.reminders_path, 'w', encoding='utf-8') as f:
    json.dump(self.reminders, f, indent=2)
    except Exception as e:
    messagebox.showerror("Error", f"Failed to save reminders: {e}")

    def refresh_list(self):
    self.listbox.delete(0, END)
    self.desc_text.delete("1.0", END)
    date = self.cal.get_date()
    day_reminders = self.reminders.get(date, [])
    for rem in day_reminders:
    self.listbox.insert(END, f"{rem.get('title', '')}: {rem.get('description','')}")

    def add_reminder(self):
    date = self.cal.get_date()
    title = simpledialog.askstring("Add Reminder", f"Reminder title for {date}:")
    if title:
    description = self.desc_text.get("1.0", END).strip()
    self.reminders.setdefault(date, [])
    self.reminders[date].append({"title": title, "description": description})
    self._save_reminders()
    self.refresh_list()
    self.desc_text.delete("1.0", END)

    def delete_reminder(self):
    selection = self.listbox.curselection()
    if not selection: return
    date = self.cal.get_date()
    index = selection[0]
    try:
    self.reminders[date].pop(index)
    if not self.reminders[date]: del self.reminders[date]
    self._save_reminders()
    self.refresh_list()
    self.desc_text.delete("1.0", END)
    except Exception as e:
    messagebox.showerror("Error", f"Failed to delete reminder: {e}")

    # ---------------- Description management ----------------
    def show_description(self):
    selection = self.listbox.curselection()
    if not selection: return
    date = self.cal.get_date()
    index = selection[0]
    description = self.reminders[date][index].get("description", "")
    self.desc_text.delete("1.0", END)
    self.desc_text.insert("1.0", description)

    def add_description(self):
    selection = self.listbox.curselection()
    if not selection: messagebox.showinfo("Info", "Select a reminder first."); return
    date = self.cal.get_date()
    index = selection[0]
    desc = self.desc_text.get("1.0", END).strip()
    if not desc: messagebox.showinfo("Info", "Enter a description to add."); return
    if self.reminders[date][index].get("description","").strip():
    messagebox.showinfo("Info", "Description already exists. Use Edit Description instead."); return
    self.reminders[date][index]["description"] = desc
    self._save_reminders()
    self.refresh_list()
    self.listbox.selection_set(index)

    def edit_description(self):
    selection = self.listbox.curselection()
    if not selection: messagebox.showinfo("Info", "Select a reminder first."); return
    date = self.cal.get_date()
    index = selection[0]
    if not self.reminders[date][index].get("description","").strip():
    messagebox.showerror("Error", "Description not found."); return
    desc = self.desc_text.get("1.0", END).strip()
    if not desc: messagebox.showinfo("Info", "Enter a description to update."); return
    self.reminders[date][index]["description"] = desc
    self._save_reminders()
    self.refresh_list()
    self.listbox.selection_set(index)

    def remove_description(self):
    selection = self.listbox.curselection()
    if not selection: messagebox.showinfo("Info", "Select a reminder first."); return
    date = self.cal.get_date()
    index = selection[0]
    if not self.reminders[date][index].get("description","").strip():
    messagebox.showerror("Error", "Description not found."); return
    self.reminders[date][index]["description"] = ""
    self._save_reminders()
    self.refresh_list()
    self.listbox.selection_set(index)

    # ---------------- Test Run ----------------
    if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()
    class DummyApp:
    def __init__(self, root): self.root=root; self.app_folder=os.getcwd()
    app = DummyApp(root)
    CalendarWindow(app, "testuser")
    root.mainloop()


    So it would be great if I did get some help, I have tried putting my calendar window in a ttk frame, but that did not work either. I honestly do not know why this is happening, first time running into this error, asked my programming friends, they had no answers, and I even asked multiple AI, to no avail. I need this project done ASAP, so please guys.

    Continue reading...

Compartilhe esta Página