tkinter grid overlapping
Question:
I am building a calendar that allows the user to cycle through the months and years by pressing the buttons created of the previous month and next month. Essentially what I want the main window to do is update with the new month upon clicking PREV or NEXT month with the correct days, which it does, only issue is the day buttons that display the specific days of the month overlap when cycling through.
Below is the part where I am having issues:
def prevMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
lastMonth = self.month - 1
self.month = lastMonth
self.curr_month()
def nextMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
nextMonth = self.month + 1
self.month = nextMonth
self.curr_month()
When the program iterates between the months the grid does not refresh it just overlaps the days and months. I have tried EVERYTHING I found in my hours of research. “self.destroy()” merely creates a blank window. “self.grid.destroy()” returns and error that function has no attribute destroy. I have tried making the children of grid all global variables within self and I cant iterate through the months correctly so the set up is permanent but I feel like I am missing something simple as far as working with refreshing the grid and reprinting the based upon the updated month.
Can you please point me in the right direction or correct the error I am missing?
below is the entire program
from Tkinter import *
from calendar import *
import datetime
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
DateNow = datetime.datetime.now()
self.year = DateNow.year#declaring global variable year
self.month = DateNow.month#declaring global variable month
self.curr_month()
def curr_month(self):
try:#iterating the month and year backward if index is out of range
if self.month == 0:
self.month = 12
trueYear = int(self.year)
self.year = trueYear - 1
except:
pass
try:#iterating month and year forward if index is out of range
if self.month == 13:
self.month = 1
trueYear = int(self.year)
self.year = trueYear + 1
except:
pass
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
#create labels
self.label = Label(self, text=months[self.month - 1])#displaying month
self.label.grid(row=0, column = 1)
self.label = Label(self, text=self.year)#displaying year
self.label.grid(row=0, column = 6)
try:#displaying previous month
prevMonthBut = Button(self, text=months[self.month-2], command=self.prevMonth)
prevMonthBut.grid(row=0,column=0)
except:#USED ONLY IF PREVIOUS MONTH IS IN PREVIOUS YEAR
prevMonthBut = Button(self, text=months[11], command=self.prevMonth)
prevMonthBut.grid(row=0,column=0)
try:#displaying next month
nextMonthBut = Button(self, text=months[self.month], command=self.nextMonth)
nextMonthBut.grid(row=0,column=2)
except:#USED ONLY IF NEXT MONTH IS IN NEXT YEAR
nextMonthBut = Button(self, text=months[0], command=self.nextMonth)
nextMonthBut.grid(row=0,column=2)
for i in range(7):
self.label = Label(self, text=days[i])
self.label.grid(row = 1, column = i)
weekday, numDays = monthrange(self.year, self.month)
week = 2
for i in range(1, numDays + 1):
self.button = Button(self, text = str(i))
self.button.grid(row = week, column = weekday)
weekday += 1
if weekday > 6:
week += 1
weekday = 0
def prevMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
lastMonth = self.month - 1
self.month = lastMonth
self.curr_month()
def nextMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
nextMonth = self.month + 1
self.month = nextMonth
self.curr_month()
mainWindow = Tk()
obj = Application()
mainWindow.mainloop()here
Answers:
Tkinter is fairly efficient. And for the number of widgets that you have, it won’t impact performance much to create them all initially. Here is a sample that works about like what you were trying to do.
from calendar import *
import datetime
try:
from tkinter import * # Python 3.x
except:
from Tkinter import * # Python 2.x
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid(row=0, column=0, sticky='news')
DateNow = datetime.datetime.now()
month = int(DateNow.month)
year = int(DateNow.year)
self.createDaysOfWeekLabels()
month_name = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
# Create frames and button controls for previous, current and next month.
self.frameList = [] # List that contains the frame objects.
self.buttonList = [] # List that contains the button objects.
amonth = month - 1
for i in range(3):
if amonth < 0:
amonth = 11
year -= 1
elif amonth == 12:
amonth = 0
year += 1
mFrame = Frame(self)
self.createMonth(mFrame, amonth, year)
self.frameList.append(mFrame)
mButton = Button(self, text=month_name[amonth-1])
mButton['command'] = lambda f=mFrame, b=mButton: self.showMonth(f, b)
mButton.grid(row=0, column=i)
# Grid each frame
mFrame.grid(row=2, column=0, columnspan=7, sticky='news')
if (i == 1):
mButton['relief'] = 'flat'
else:
# Remove all but the ith frame. More efficient to remove than forget and configuration is remembered.
mFrame.grid_remove()
self.buttonList.append(mButton)
amonth += 1
# Create year widget at top left of top frame
label = Label(self, text=year)#displaying year
label.grid(row=0, column=6)
def createDaysOfWeekLabels(self):
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for i in range(7):
label = Label(self, text=days[i])
label.grid(row = 1, column = i)
def showMonth(self, mFrame, mButton):
# Display all buttons normally
for button in self.buttonList:
button['relief'] = 'raised'
# Set this month's button relief to flat
mButton['relief'] = 'flat'
# Hide all frames
for frame in self.frameList:
frame.grid_remove()
mFrame.grid()
def createMonth(self, mFrame, month, year):
weekday, numDays = monthrange(year, month)
week = 0
for i in range(1, numDays + 1):
button = Button(mFrame, text = str(i), width=3)
button.grid(row = week, column = weekday)
weekday += 1
if weekday > 6:
week += 1
weekday = 0
mainWindow = Tk()
obj = Application(mainWindow)
mainWindow.mainloop()
This is a modified version of the proposed answer that also includes the original desired intent of allowing the user to cycle through the months and will also increment the year.
from calendar import *
import datetime
try:
from tkinter import * # Python 3.x
except:
from Tkinter import * # Python 2.x
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid(row=0, column=0, sticky='news')
DateNow = datetime.datetime.now()
self.month = int(DateNow.month)
self.year = int(DateNow.year)
self.createDaysOfWeekLabels()
# Create frames and button controls for previous, current and next month.
self.frameList = [] # List that contains the frame objects.
self.buttonList = [] # List that contains the button objects.
self.split()
def split(self):
month_name = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
leftArrow = Button(self, text="<", command=self.prevMonth)
leftArrow.grid(row = 0, column = 0)
rightArrow = Button(self, text=">", command=self.nextMonth)
rightArrow.grid(row = 0, column = 1)
for i in range(3):
try:
print i, "this is i"
print self.month
mFrame = Frame(self)
self.createMonth(mFrame)
self.frameList.append(mFrame)
mButton = Button(self, text=month_name[self.month-1])
mButton['command'] = lambda f=mFrame, b=mButton: self.showMonth(f, b)
mButton.grid(row=1, column=i)
# Grid each frame
mFrame.grid(row=3, column=0, columnspan=7, sticky='news')
if (i == 1):
mButton['relief'] = 'flat'
else:
mButton.grid_remove()
# Remove all but the ith frame. More efficient to remove than forget and configuration is remembered.
mFrame.grid_remove()
self.buttonList.append(mButton)
except:
pass
# Create year widget at top right of top frame
label = Label(self, text=self.year)#displaying year
label.grid(row=0, column=6)
print "-------------------"
def prevMonth(self):
self.month -= 1
print self.month, "this is month in PREV"
if self.month <= 0:
self.month = 12
print self.month, "month inside forinif in PREVMONTH"
self.year -= 1
elif self.month >= 13:
self.month = 0
print self.month, "month inside forinelif in PREVMONTH"
self.year += 1
self.split()
def nextMonth(self):
self.month += 1
print self.month, "this is month in NEXT"
for frame in self.frameList:
frame.grid_remove()
if self.month <= -1:
self.month = 11
print self.month, "month inside forinif in NEXTMONTH"
self.year -= 1
elif self.month >= 13:
self.month = 1
print self.month, "month inside forinelif in NEXTMONTH"
self.year += 1
self.split()
def createDaysOfWeekLabels(self):
days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
for i in range(7):
label = Label(self, text=days[i], width = 3)
label.grid(row = 2, column = i)
def showMonth(self, mFrame, mButton):
# Display all buttons normally
for button in self.buttonList:
button['relief'] = 'raised'
# Set this month's button relief to flat
mButton['relief'] = 'flat'
# Hide all frames
for mframe in self.frameList:
mframe.grid_remove()
mFrame.grid()
def createMonth(self, mFrame):
weekday, numDays = monthrange(self.year, self.month)
week = 0
for i in range(1, numDays + 1):
button = Button(mFrame, text = str(i), width=3)
button.grid(row = week, column = weekday)
weekday += 1
if weekday > 6:
week += 1
weekday = 0
mainWindow = Tk()
obj = Application(mainWindow)
mainWindow.mainloop()
Use something like [yourWidget].grid(row=0, column=0, columnspan=4)
I am building a calendar that allows the user to cycle through the months and years by pressing the buttons created of the previous month and next month. Essentially what I want the main window to do is update with the new month upon clicking PREV or NEXT month with the correct days, which it does, only issue is the day buttons that display the specific days of the month overlap when cycling through.
Below is the part where I am having issues:
def prevMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
lastMonth = self.month - 1
self.month = lastMonth
self.curr_month()
def nextMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
nextMonth = self.month + 1
self.month = nextMonth
self.curr_month()
When the program iterates between the months the grid does not refresh it just overlaps the days and months. I have tried EVERYTHING I found in my hours of research. “self.destroy()” merely creates a blank window. “self.grid.destroy()” returns and error that function has no attribute destroy. I have tried making the children of grid all global variables within self and I cant iterate through the months correctly so the set up is permanent but I feel like I am missing something simple as far as working with refreshing the grid and reprinting the based upon the updated month.
Can you please point me in the right direction or correct the error I am missing?
below is the entire program
from Tkinter import *
from calendar import *
import datetime
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
DateNow = datetime.datetime.now()
self.year = DateNow.year#declaring global variable year
self.month = DateNow.month#declaring global variable month
self.curr_month()
def curr_month(self):
try:#iterating the month and year backward if index is out of range
if self.month == 0:
self.month = 12
trueYear = int(self.year)
self.year = trueYear - 1
except:
pass
try:#iterating month and year forward if index is out of range
if self.month == 13:
self.month = 1
trueYear = int(self.year)
self.year = trueYear + 1
except:
pass
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
#create labels
self.label = Label(self, text=months[self.month - 1])#displaying month
self.label.grid(row=0, column = 1)
self.label = Label(self, text=self.year)#displaying year
self.label.grid(row=0, column = 6)
try:#displaying previous month
prevMonthBut = Button(self, text=months[self.month-2], command=self.prevMonth)
prevMonthBut.grid(row=0,column=0)
except:#USED ONLY IF PREVIOUS MONTH IS IN PREVIOUS YEAR
prevMonthBut = Button(self, text=months[11], command=self.prevMonth)
prevMonthBut.grid(row=0,column=0)
try:#displaying next month
nextMonthBut = Button(self, text=months[self.month], command=self.nextMonth)
nextMonthBut.grid(row=0,column=2)
except:#USED ONLY IF NEXT MONTH IS IN NEXT YEAR
nextMonthBut = Button(self, text=months[0], command=self.nextMonth)
nextMonthBut.grid(row=0,column=2)
for i in range(7):
self.label = Label(self, text=days[i])
self.label.grid(row = 1, column = i)
weekday, numDays = monthrange(self.year, self.month)
week = 2
for i in range(1, numDays + 1):
self.button = Button(self, text = str(i))
self.button.grid(row = week, column = weekday)
weekday += 1
if weekday > 6:
week += 1
weekday = 0
def prevMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
lastMonth = self.month - 1
self.month = lastMonth
self.curr_month()
def nextMonth(self):
try:
self.grid_forget()
#SHOULD REFRESH THE WINDOW SO BUTTONS DONT OVERLAP
print "forgeting"
except:
print "passed the forgetting"
pass
nextMonth = self.month + 1
self.month = nextMonth
self.curr_month()
mainWindow = Tk()
obj = Application()
mainWindow.mainloop()here
Tkinter is fairly efficient. And for the number of widgets that you have, it won’t impact performance much to create them all initially. Here is a sample that works about like what you were trying to do.
from calendar import *
import datetime
try:
from tkinter import * # Python 3.x
except:
from Tkinter import * # Python 2.x
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid(row=0, column=0, sticky='news')
DateNow = datetime.datetime.now()
month = int(DateNow.month)
year = int(DateNow.year)
self.createDaysOfWeekLabels()
month_name = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
# Create frames and button controls for previous, current and next month.
self.frameList = [] # List that contains the frame objects.
self.buttonList = [] # List that contains the button objects.
amonth = month - 1
for i in range(3):
if amonth < 0:
amonth = 11
year -= 1
elif amonth == 12:
amonth = 0
year += 1
mFrame = Frame(self)
self.createMonth(mFrame, amonth, year)
self.frameList.append(mFrame)
mButton = Button(self, text=month_name[amonth-1])
mButton['command'] = lambda f=mFrame, b=mButton: self.showMonth(f, b)
mButton.grid(row=0, column=i)
# Grid each frame
mFrame.grid(row=2, column=0, columnspan=7, sticky='news')
if (i == 1):
mButton['relief'] = 'flat'
else:
# Remove all but the ith frame. More efficient to remove than forget and configuration is remembered.
mFrame.grid_remove()
self.buttonList.append(mButton)
amonth += 1
# Create year widget at top left of top frame
label = Label(self, text=year)#displaying year
label.grid(row=0, column=6)
def createDaysOfWeekLabels(self):
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for i in range(7):
label = Label(self, text=days[i])
label.grid(row = 1, column = i)
def showMonth(self, mFrame, mButton):
# Display all buttons normally
for button in self.buttonList:
button['relief'] = 'raised'
# Set this month's button relief to flat
mButton['relief'] = 'flat'
# Hide all frames
for frame in self.frameList:
frame.grid_remove()
mFrame.grid()
def createMonth(self, mFrame, month, year):
weekday, numDays = monthrange(year, month)
week = 0
for i in range(1, numDays + 1):
button = Button(mFrame, text = str(i), width=3)
button.grid(row = week, column = weekday)
weekday += 1
if weekday > 6:
week += 1
weekday = 0
mainWindow = Tk()
obj = Application(mainWindow)
mainWindow.mainloop()
This is a modified version of the proposed answer that also includes the original desired intent of allowing the user to cycle through the months and will also increment the year.
from calendar import *
import datetime
try:
from tkinter import * # Python 3.x
except:
from Tkinter import * # Python 2.x
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid(row=0, column=0, sticky='news')
DateNow = datetime.datetime.now()
self.month = int(DateNow.month)
self.year = int(DateNow.year)
self.createDaysOfWeekLabels()
# Create frames and button controls for previous, current and next month.
self.frameList = [] # List that contains the frame objects.
self.buttonList = [] # List that contains the button objects.
self.split()
def split(self):
month_name = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
leftArrow = Button(self, text="<", command=self.prevMonth)
leftArrow.grid(row = 0, column = 0)
rightArrow = Button(self, text=">", command=self.nextMonth)
rightArrow.grid(row = 0, column = 1)
for i in range(3):
try:
print i, "this is i"
print self.month
mFrame = Frame(self)
self.createMonth(mFrame)
self.frameList.append(mFrame)
mButton = Button(self, text=month_name[self.month-1])
mButton['command'] = lambda f=mFrame, b=mButton: self.showMonth(f, b)
mButton.grid(row=1, column=i)
# Grid each frame
mFrame.grid(row=3, column=0, columnspan=7, sticky='news')
if (i == 1):
mButton['relief'] = 'flat'
else:
mButton.grid_remove()
# Remove all but the ith frame. More efficient to remove than forget and configuration is remembered.
mFrame.grid_remove()
self.buttonList.append(mButton)
except:
pass
# Create year widget at top right of top frame
label = Label(self, text=self.year)#displaying year
label.grid(row=0, column=6)
print "-------------------"
def prevMonth(self):
self.month -= 1
print self.month, "this is month in PREV"
if self.month <= 0:
self.month = 12
print self.month, "month inside forinif in PREVMONTH"
self.year -= 1
elif self.month >= 13:
self.month = 0
print self.month, "month inside forinelif in PREVMONTH"
self.year += 1
self.split()
def nextMonth(self):
self.month += 1
print self.month, "this is month in NEXT"
for frame in self.frameList:
frame.grid_remove()
if self.month <= -1:
self.month = 11
print self.month, "month inside forinif in NEXTMONTH"
self.year -= 1
elif self.month >= 13:
self.month = 1
print self.month, "month inside forinelif in NEXTMONTH"
self.year += 1
self.split()
def createDaysOfWeekLabels(self):
days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
for i in range(7):
label = Label(self, text=days[i], width = 3)
label.grid(row = 2, column = i)
def showMonth(self, mFrame, mButton):
# Display all buttons normally
for button in self.buttonList:
button['relief'] = 'raised'
# Set this month's button relief to flat
mButton['relief'] = 'flat'
# Hide all frames
for mframe in self.frameList:
mframe.grid_remove()
mFrame.grid()
def createMonth(self, mFrame):
weekday, numDays = monthrange(self.year, self.month)
week = 0
for i in range(1, numDays + 1):
button = Button(mFrame, text = str(i), width=3)
button.grid(row = week, column = weekday)
weekday += 1
if weekday > 6:
week += 1
weekday = 0
mainWindow = Tk()
obj = Application(mainWindow)
mainWindow.mainloop()
Use something like [yourWidget].grid(row=0, column=0, columnspan=4)