Trying to make a weekly calendar view. When I add the scrollbar, it shifts the grid to the left.
This is the best code I came up with. Still not solving the problem.
import tkinter as tk
from tkinter import ttk
import calendar
from datetime import datetime
class CalendarApp:
def __init__(self, root):
self.root = root
self.root.title("Calendar")
self.root.state('zoomed') # Maximize window to fit the screen
self.current_year = datetime.now().year
self.current_month = datetime.now().month
self.today = datetime.now()
self.nav_frame = tk.Frame(self.root)
self.nav_frame.pack(fill='x', pady=10)
self.left_frame = tk.Frame(self.root, width=200, borderwidth=1, relief="solid")
self.left_frame.pack(side='left', fill='y', padx=5, pady=5)
self.right_frame = tk.Frame(self.root, borderwidth=1, relief="solid")
self.right_frame.pack(side='left', expand=True, fill='both', padx=5, pady=5)
self.create_navigation()
self.create_year_view()
self.show_week_view() # Show the weekly view in the right frame
def create_navigation(self):
prev_button = tk.Button(self.nav_frame, text="<", command=self.prev_year)
prev_button.pack(side='left', padx=10)
self.year_label = tk.Label(self.nav_frame, text=str(self.current_year), font=("Arial", 24))
self.year_label.pack(side='left', padx=10)
next_button = tk.Button(self.nav_frame, text=">", command=self.next_year)
next_button.pack(side='left', padx=10)
def create_year_view(self):
self.year_label.config(text=str(self.current_year))
self.clear_frame(self.left_frame)
canvas = tk.Canvas(self.left_frame, borderwidth=1, relief="solid") # Use canvas for proper width
scrollbar = ttk.Scrollbar(self.left_frame, orient="vertical", command=canvas.yview)
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set, width=200) # Fixed width for canvas
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
# Bind mouse wheel event for scrolling
canvas.bind_all("<MouseWheel>", lambda event: self.on_mouse_wheel(event, canvas))
canvas.bind_all("<Button-4>", lambda event: self.on_mouse_wheel(event, canvas))
canvas.bind_all("<Button-5>", lambda event: self.on_mouse_wheel(event, canvas))
calendar.setfirstweekday(calendar.MONDAY) # Set Monday as the first day of the week
for month in range(1, 13):
self.create_month_overview(scrollable_frame, month)
def create_month_overview(self, parent_frame, month):
month_frame = tk.Frame(parent_frame, borderwidth=1, relief="solid")
month_frame.pack(fill='x', padx=10, pady=10) # Reduced padding
month_button = tk.Button(month_frame, text=calendar.month_name[month], font=("Arial", 11, "bold"),
command=lambda m=month: self.show_month_view(m))
month_button.pack(fill='x')
days_frame = tk.Frame(month_frame)
days_frame.pack(fill='x')
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
for col, day in enumerate(days):
day_label = tk.Label(days_frame, text=day, font=("Arial", 10), borderwidth=1, relief="solid")
day_label.grid(row=0, column=col, sticky="nsew")
days_frame.grid_columnconfigure(0, weight=1)
days_frame.grid_columnconfigure(1, weight=1)
days_frame.grid_columnconfigure(2, weight=1)
days_frame.grid_columnconfigure(3, weight=1)
days_frame.grid_columnconfigure(4, weight=1)
days_frame.grid_columnconfigure(5, weight=1)
days_frame.grid_columnconfigure(6, weight=1)
month_days = calendar.monthcalendar(self.current_year, month)
for row, week in enumerate(month_days, start=1):
for col, day in enumerate(week):
if day == 0:
day_label = tk.Label(days_frame, text="", borderwidth=1, relief="solid")
else:
day_label = tk.Label(days_frame, text=str(day), borderwidth=1, relief="solid")
if self.current_year == self.today.year and month == self.today.month and day == self.today.day:
day_label.config(bg="lightblue")
day_label.bind("<Button-1>", lambda event, m=month, d=day: self.show_week_view(m, d))
day_label.grid(row=row, column=col, sticky="nsew", padx=0, pady=0) # Reduced padding
def on_mouse_wheel(self, event, canvas):
if event.num == 5 or event.delta == -120:
canvas.yview_scroll(1, "units")
elif event.num == 4 or event.delta == 120:
canvas.yview_scroll(-1, "units")
def show_week_view(self):
self.clear_frame(self.right_frame)
canvas = tk.Canvas(self.right_frame, borderwidth=1, relief="solid")
canvas.pack(side="left", fill="both", expand=True)
scrollbar = ttk.Scrollbar(self.right_frame, orient="vertical", command=canvas.yview)
scrollbar.pack(side="right", fill="y")
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
days = ["Time", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
times = [f"{h:02d}:{m:02d}" for h in range(5, 24) for m in (0, 15, 30, 45)]
# Create headers
for col, day in enumerate(days):
day_label = tk.Label(scrollable_frame, text=day, font=("Arial", 14, "bold"), borderwidth=1, relief="solid")
day_label.grid(row=0, column=col, sticky="nsew")
# Create time slots and gridlines
row_height = 3 # Increase row height by using the height attribute
for row, time in enumerate(times, start=1):
time_label = tk.Label(scrollable_frame, text=time, font=("Arial", 12), borderwidth=1, relief="solid", height=row_height)
time_label.grid(row=row, column=0, sticky="nsew")
for col in range(1, 8):
slot_label = tk.Label(scrollable_frame, text="", borderwidth=1, relief="solid", height=row_height)
slot_label.grid(row=row, column=col, sticky="nsew")
# Configure column and row weights for equal distribution
for i in range(8):
scrollable_frame.grid_columnconfigure(i, weight=1)
for i in range(len(times) + 1):
scrollable_frame.grid_rowconfigure(i, weight=1)
def show_month_view(self, month):
self.clear_frame(self.right_frame)
calendar.setfirstweekday(calendar.MONDAY) # Set Monday as the first day of the week
month_label = tk.Label(self.right_frame, text=f"{calendar.month_name[month]} {self.current_year}", font=("Arial", 24))
month_label.grid(row=0, column=0, columnspan=7, pady=5)
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
for col, day in enumerate(days):
day_label = tk.Label(self.right_frame, text=day, font=("Arial", 14, "bold"), borderwidth=1, relief="solid")
day_label.grid(row=1, column=col, sticky="nsew")
month_days = calendar.monthcalendar(self.current_year, month)
for row, week in enumerate(month_days, start=2):
for col, day in enumerate(week):
if day == 0:
day_label = tk.Label(self.right_frame, text="", borderwidth=1, relief="solid")
else:
day_label = tk.Label(self.right_frame, text=str(day), borderwidth=1, relief="solid")
if self.current_year == self.today.year and month == self.today.month and day == self.today.day:
day_label.config(bg="lightblue")
day_label.bind("<Button-1>", lambda event, m=month, d=day: self.show_week_view())
day_label.grid(row=row, column=col, sticky="nsew")
for i in range(7):
self.right_frame.grid_columnconfigure(i, weight=1)
for i in range(len(month_days) + 2):
self.right_frame.grid_rowconfigure(i, weight=1)
def clear_frame(self, frame):
for widget in frame.winfo_children():
widget.destroy()
def prev_year(self):
self.current_year -= 1
self.create_year_view()
def next_year(self):
self.current_year += 1
self.create_year_view()
if __name__ == "__main__":
root = tk.Tk()
app = CalendarApp(root)
root.mainloop()
I don’t know how to make the grid not shift over. ChatGTP was no use. It just gives me the same solution over and over. Anyone have any thoughts? I tried making the scroll bar function same on the right as the left.
New contributor
Tala Kaplinovsky is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.