Tkinter frame using create_window option equivalent to expand - python

I am doing this small project. I have a frame (initially expanded to eat up space not taken by other frames) that I added a scrollbar because this frame contains a set of widgets that replicates once a button is hit.
My problem now is that once I already added the scrollbar and make it worked, I can no longer resize the frame made from create_window option. The widgets inside it have already used the options fill and expand but still would not expand. I even specified the widths and heights in the containing widgets but still wont work.
Here are the relevant codes:
from tkinter import *
from tkinter import ttk
class Database(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
parent.state('zoomed')
self.parent = parent
#Main Frame
MainFrame=Frame(self, bg="white")
MainFrame.pack(expand = True, side = TOP, fill="both")
#Level 2 Frame
SubmainFrame=Frame(MainFrame)
SubmainFrame.pack(side=LEFT, fill = BOTH, expand = True)
#Level 3 Frame
DataFrame=Frame(SubmainFrame)
DataFrame.pack(side=RIGHT, expand = True, fill = BOTH)
frames = []
def New():
global widgetNames
global frameNames
DataFrameAF_=LabelFrame(frame,bg = "sky blue", text="Sub Info")
DataFrameAF_.pack(side = TOP, padx = 10, pady = 10, fill = BOTH, expand = True)
DataFrameAc = Frame(DataFrameAF_)
DataFrameAc.pack(side = LEFT, padx = 5)
DataFrameAc1 = Frame(DataFrameAc)
DataFrameAc1.pack(side = LEFT)
DataFrameAc2 = Frame(DataFrameAc)
DataFrameAc2.pack(side = LEFT)
#first column
self.lbl_ac2=Label(DataFrameAc1, text = "A1 ")
self.lbl_ac2.grid(row=1,column=0,sticky=W)
self.txt_ac2=ttk.Entry(DataFrameAc1, width=20)
self.txt_ac2.grid(row=1,column=1)
self.lbl_ac3=Label(DataFrameAc1, text = "A2 ")
self.lbl_ac3.grid(row=2,column=0,sticky=W)
self.txt_ac3=ttk.Entry(DataFrameAc1, width=20)
self.txt_ac3.grid(row=2,column=1)
#second column
self.lbl_ac2_=Label(DataFrameAc2, text = "B1 ")
self.lbl_ac2_.grid(row=1,column=0,sticky=W)
self.txt_ac2_=ttk.Entry(DataFrameAc2, width=20)
self.txt_ac2_.grid(row=1,column=1)
self.lbl_ac3_=Label(DataFrameAc2, text = "B2 ")
self.lbl_ac3_.grid(row=2,column=0,sticky=W)
self.txt_ac3_=ttk.Entry(DataFrameAc2, width=20)
self.txt_ac3_.grid(row=2,column=1)
DataFrameAF=LabelFrame(DataFrame, text="Information")
DataFrameAF.pack(side = TOP, pady = 10)
canvas = Canvas(DataFrameAF, borderwidth=0, background="#ffffff")
frame = Frame(canvas, background="#ffffff")
vsb = Scrollbar(DataFrameAF, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill=BOTH, expand=True)
canvas.create_window((20,20), window=frame, anchor="nw")
def reset_scrollregion(event):
canvas.configure(scrollregion=canvas.bbox("all"))
frame.bind("<Configure>", reset_scrollregion)
First_Info = New()
createWidgetButton = Button(frame, text="New Entry", command=New)
createWidgetButton.pack(side=BOTTOM, fill="x")
root = Tk()
database_window = Database(root)
database_window.pack(side="top", fill="both", expand=True)
database_window.update()
root.mainloop()

Related

Sizing the Canvas widget with other widgets on the GUI

I'm creating a GUI that has a spreadsheet-like interface which is wrapped with entry method to allow the person that uses the program to enter it as if it is on a spreadsheet. This allows me much greater control over all the data and manipulate it for data analysis later. However, I can't restrict the size of the canvas to allow the scrolling bar to take effect. I.E. if I change the number of rows, the canvas will resize to that (along with column(s) changes too). I have other widgets within the GUI that isn't shown in the code but I'm just focusing on trying to force a size on the Canvas widget itself.
Is there a way for me to force the Canvas to stay within "width and height" size without having the rows and columns controlling the size of the Canvas?
import tkinter as tk
from tkinter import *
from textwrap import fill
from datetime import date
#Instantiate the GUI
class StartApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
t1 = ExcelTable(self)
t1.grid(row = 0, columnspan=2, sticky = "N")
t2 = ProductWidget()
t2.grid(row=1, column=0,sticky="SW")
t3 = TotalTrucks()
t3.grid(row=1, column=1, sticky="nsew")
#Instantiate the layout for the excel-like table entry for the top
part of the GUI
class ExcelTable(tk.Frame):
def __init__(self, parent, width=500, height=500):
rows=20
columns=10
#label for each column in the table colHeaders = ["#", "Driver", "Tare", "Ticket #", "Location", "Product", "Gross", "Net", "Tons", "Date"]
# use black background so it "peeks through" to
# form grid lines
#tk.Frame.__init__(self, parent, background="black", width=150, height=200)
#attempt to create a scrollbar within a canvas
canvas = tk.Canvas(parent)
scrollbar = tk.Scrollbar(parent, orient="vertical", command=canvas.yview)
#contain the Frame within the canvas
tk.Frame.__init__(self, canvas)
#creates the widgets to behave like excel-like table for data entry
self._widgets = []
for row in range(rows):
current_row = []
for column in range(columns):
if row == 0: #create the headers for the spreadsheet widget, using the colHeaders array
if column == 0:
label = tk.Label(self, text = colHeaders[column], borderwidth=0, width = 4)
else:
label = tk.Label(self, text = colHeaders[column], borderwidth=0, width=10)
else:
if column == 0:
label = tk.Label(self, text = (row))
else:
label = tk.Entry(self, text="")
label.bind
label.grid(row=row, column=column, sticky="nsew", padx=1, pady=1)
current_row.append(label)
self._widgets.append(current_row)
for column in range(columns):
self.grid_columnconfigure(column, weight=1)
canvas.create_window(0,0,anchor='nw', window=self)
canvas.update_idletasks()
canvas.configure(scrollregion=parent.bbox('all'))
canvas.grid(row=0,column=0)
scrollbar.grid(row=0, column=1, sticky="ns")
def set(self, row, column, value):
widget = self._widgets[row][column]
widget.configure(text=value)
def key(event):
print ("key: {}".format(event.char))
#obtain and store values that are entered in the ExcelTable
def find_in_grid(frame, row, column):
for children in frame.children.values():
info = children.grid_info()
#note that rows and column numbers are stored as string
if info['row'] == str(row) and info['column'] == str(column):
return children
return None
class ProductWidget(tk.Frame):
def __init__(self):
tk.Frame.__init__(self, background="white")
self._widgets = []
label1 = tk.Label(self, text="Product")
label1.grid(row=0,column=0, sticky="nsew")
label2 = tk.Label(self, text="Total Tons")
label2.grid(row=0, column=1, sticky="nsew")
class TotalTrucks(tk.Frame):
def __init__(self):
tk.Frame.__init__(self, background="white" )
self._widgets = []
label1 = tk.Label(self, text="Total Trucks")
label1.grid(row=0, rowspan=2, column=0, sticky="nsew")
label2 = tk.Label(self, text="Today: ")
label2.grid(row=1, column=0, stick="nsew")
label3 = tk.Label(self, text="Last Week: ")
label3.grid(row=2, column=0, sticky="nsew")
label4 = tk.Label(self, text="Overall")
label4.grid(row=3, column=0, sticky="nsew")
if __name__ == "__main__":
currYear = date.today().year
startGUI = StartApp()
startGUI.title("Truck Log " + str(currYear))
startGUI.mainloop()
Since ExcelTable is the internal frame inside canvas and is already put inside canvas using canvas.create_window(...), so you should not call t1.grid(...) inside StartApp.__init__().
Also you set the scrollregion of canvas wrong in the following line:
canvas.configure(scrollregion=parent.bbox('all'))
It should be:
canvas.configure(scrollregion=canvas.bbox('all'))
Finally you forgot to configure yscrollcommand option of canvas.
Below is the changes required:
...
class StartApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
t1 = ExcelTable(self)
### should not call t1.grid(...) since t1 has already put in canvas using canvas.create_window(...)
#t1.grid(row = 0, columnspan=2, sticky = "N")
t2 = ProductWidget()
t2.grid(row=1, column=0,sticky="SW")
t3 = TotalTrucks()
t3.grid(row=1, column=1, sticky="nsew")
...
class ExcelTable(tk.Frame):
def __init__(self, parent, width=500, height=500):
...
#canvas.configure(scrollregion=parent.bbox('all'))
canvas.configure(scrollregion=canvas.bbox('all'), width=self.winfo_width(),
yscrollcommand=scrollbar.set)
...

Why doesn't the column width fill the frame using `weight` in tkinter?

I have run in to a diffuctly where despite using the sticky and weight options from the grid and columnconfigure methods respectively, I am unable to make the column expand the entire width of my frame.
I have read through this post which explains things well, however I haven't had any luck. Below is a simplified example of my code, where the desired result can be achieved by uncommenting the lines using the pack method, which I'd like to replicate using the grid method as well.
import tkinter as tk
class myGUI(tk.Frame):
class ScrollableFrame(tk.Frame):
def __init__(self, parent):
# Create scrollbar
self.frame = tk.Frame(parent)
self.canvas = tk.Canvas(self.frame)
self.yscrollbar = tk.Scrollbar(self.canvas, orient='vertical', command=self.canvas.yview)
tk.Frame.__init__(self, self.canvas)
self.canvas.create_window((0, 0), window=self, anchor='nw')
# Configure scrollbar
self.canvas.configure(yscrollcommand=self.yscrollbar.set)
self.canvas.bind('<Configure>', lambda e: self.canvas.configure(scrollregion=self.canvas.bbox('all')))
# Pack scrollbar
self.canvas.pack(side='left', fill='both', expand=True)
self.yscrollbar.pack(side='right', fill='y')
# Configure placement
self.pack = self.frame.pack
self.place = self.frame.place
self.grid = self.frame.grid
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.wrap = tk.LabelFrame(parent)
self.myFrame = self.ScrollableFrame(parent)
for i in range(50):
tk.Button(self.myFrame, text='My Button - '+str(i)).pack()
# self.myFrame.pack(fill='both', expand='yes')
# self.wrap.pack(fill='both', expand='yes')
self.myFrame.grid(row=0, column=0, sticky = 'news')
self.wrap.grid(row=1, column=0, sticky = 'news')
parent.columnconfigure(0, weight=1)
parent.columnconfigure(1, weight=1)
parent.rowconfigure(0, weight=1)
parent.rowconfigure(1, weight=1)
if __name__ == "__main__":
root = tk.Tk()
root.geometry('500x500')
root.title('Scrollbar Test')
myGUI(root)
root.mainloop()

How to expand buttons and labels to fill the x axis in python tkinter?

This is a part of code from my school project.
from tkinter import *
from tkinter.font import Font
class student_window():
def __init__(self, master):
self.student_win = master
#window = Toplevel(self.master)
self.student_win.geometry("1280x720")
self.header1Font = Font(family='Helvetica', size=20)
self.optionFont = Font(family='Sans Serrif', size=20)
self.student_win.focus()
self.show_window()
def show_window(self):
print("ookk")
self.student_win.title("Student Window")
self.option_frame = Frame(self.student_win, width=200, height=720)
lbl_header = Label(self.option_frame,text="EXAMINATION", font=self.header1Font, fg='white', bg='#172D44').grid(row=0,column=0, sticky=NSEW)
lbl_welcome = Label(self.option_frame, text="Welcome,", fg='#E9F1F7', bg='#2A3F54').grid(row=1,column=0)
lbl_username = Label(self.option_frame, text="Username", fg='white', bg='#2A3F54').grid(row=2,column=0)
lbl_header2 = Label(self.option_frame, text="STUDENT CORNER", fg='white', bg='#2A3F54').grid(row=3, column=0)
self.btn_tests = Button(self.option_frame, text="Attempt Exam", fg='#E9F1F7', bg='#35495D', relief=FLAT)
self.btn_tests.grid(row=4,column=0, sticky=NSEW)
self.btn_attempts = Button(self.option_frame, text="Attempts", fg='#E9F1F7', bg='#2A3F54', relief=FLAT)
self.btn_attempts.grid(row=5, column=0, sticky=NSEW)
self.btn_result = Button(self.option_frame, text="Result", fg='#E9F1F7', bg='#2A3F54', relief=FLAT)
self.btn_result.grid(row=6, column=0, sticky=NSEW)
self.btn_goBack = Button(self.option_frame, text="Go Back", fg='#E9F1F7', bg='#2A3F54', relief=FLAT)
self.btn_goBack.grid(row=7, column=0, sticky=NSEW)
self.option_frame.configure(bg='#2A3F54')
self.option_frame.grid(row=0, column=0)
self.option_frame.grid_propagate(0)
self.main_frame = Frame(self.student_win, width=880, height=720)
self.main_result_frame = Frame(self.main_frame)
self.main_result_frame.grid(row=0,column=0)
self.attempts_frame = Frame(self.main_frame)
self.attempts_frame.grid(row=0, column=0)
self.test_frame = Frame(self.main_frame)
lbl_test = Label(self.test_frame, text="In test frame").pack()
self.test_frame.grid(row=0,column=0)
self.main_frame.grid(row=0,column=1)
self.main_frame.grid_propagate(0)
self.info_frame = Frame(self.student_win, width=200, height=720)
self.btn_username = Button(self.info_frame, text="Username", relief=FLAT)
self.btn_username.grid(row=0,column=0)
self.userInfo_frame = Frame(self.info_frame)
self.info_frame.grid(row=0, column=2)
self.info_frame.grid_propagate(0)
root = Tk()
student_window(root)
root.mainloop()
And it looks something like this.
The Student Panel for my project
The whole window is divided into three frames and want to expand each label and button of the left frame(self.option_frame) to fill it horizontally. I tried doing sticky=EW and sticky=NSEW but still some space is left. How do I fix that?
You need to call self.option_frame.columnconfigure(0, weight=1) to make column 0 to use all the available horizontal space.
I was just trying some things and what I have found to be working is to make the label width bigger than than the frame then anchoring the text to the left.

Button changes frame size in Tkinter

I'm new to Tkinter, and have a problem with my frames when adding widgets. In this example, I add a button which makes my frame wider when I place the button inside it with .grid().
How can I make the frame "fixed"? I want the blue frame in the code below to keep the same width when I add the button.
Thanks in advance.
Regards,
Laphroaig
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
master.title("Yatzy - The Game")
master.geometry("800x600+0+0")
master.iconbitmap(r'dice.ico')
master.state('zoomed')
# Create grid index for the window
for r in range(20):
self.master.rowconfigure(r, weight=1)
for c in range(20):
self.master.columnconfigure(c, weight=1)
# Place Frame 1
Frame1 = Frame(master, bg="blue")
Frame1.grid(row = 0, column = 0, rowspan = 20, columnspan = 3, sticky=W+E+N+S)
# Place Frame 2
Frame2 = Frame(master, bg="green")
Frame2.grid(row=0, column=3, rowspan=20, columnspan=17, sticky = W+E+N+S)
# Place Frame 3
Frame3 = Frame(master, bg="red")
Frame3.grid(row=5, column=8, rowspan=10, columnspan=7, sticky = W+E+N+S)
# Place button 1
btn_1 = Button(master, text="hello123")
btn_1.grid(row=0, column=0)
root = Tk()
app = Window(master=root)
app.mainloop()
You can stop content from affecting the size of a Frame with grid_propagate(False). See example below.
Other things; You inherit from Frame but never put anything inside self, instead you put everything inside self.master ie. root. I changed to put everything in self and then pack self within root.
I also removed the icon as I don't have your icon file.
from tkinter import *
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
master.title("Yatzy - The Game")
master.geometry("800x600+0+0")
master.state('zoomed')
self.pack(expand=True, fill='both')
# Create grid index for the window
for r in range(20):
self.rowconfigure(r, weight=1)
for c in range(20):
self.columnconfigure(c, weight=1)
# Place Frame 1
Frame1 = Frame(self, bg="blue")
Frame1.grid(row = 0, column = 0, rowspan = 20, columnspan = 3, sticky=W+E+N+S)
Frame1.grid_propagate(False) # Stop grid() from resizing container
# Place Frame 2
Frame2 = Frame(self, bg="green")
Frame2.grid(row=0, column=3, rowspan=20, columnspan=17, sticky = W+E+N+S)
# Place Frame 3
Frame3 = Frame(self, bg="red")
Frame3.grid(row=5, column=8, rowspan=10, columnspan=7, sticky = W+E+N+S)
# Place button 1
btn_1 = Button(Frame1, text="hello123")
btn_1.grid(row=0, column=0)
root = Tk()
app = Window(master=root)
app.mainloop()

Python tkinter frame parent widget line up columns

I'm placing a scrollbar in a frame, and the frame in a widget. The frame has a label above it. The label above has three columns. The frame with the scrollbar has three columns. I can't get the three columns in the frame and above the frame to line up.
I saw and used the following two questions to get this far, but got stuck.
Adding a scrollbar to a group of widgets in Tkinter
Tkinter - Add scrollbar for each LabelFrame
Any help on lining up the columns would be greatly appreciated. Thanks.
Here's a picture of the widget without the lined up columns:
Columns don't line up
Here's a MWE:
import tkinter as tk
class Selections(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.FifthLabelLeft = tk.Label(self,
text="""Riding""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 0, pady=5)
self.FifthLabelCenter = tk.Label(self,
text="""Winning Candidate""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 1, pady=5)
self.FifthLabelRight = tk.Label(self,
text="""Percent of Vote""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 2, pady=5)
mybox = tk.LabelFrame(self, padx=5, pady=4)
mybox.grid(row=5, column=0, columnspan=3)
canvas = tk.Canvas(mybox, borderwidth=5, background="#70ff33")
frame = tk.Frame(canvas, background="#33f4ff")
vsb = tk.Scrollbar(mybox, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, heigh=50)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw", tags="frame")
# be sure that we call OnFrameConfigure on the right canvas
frame.bind("<Configure>", lambda event: self.OnFrameConfigure(canvas))
self.fillWindow(frame)
self.QuitButton = tk.Button(self,
text="QUIT",
command=root.destroy,
padx=25, pady=0)
self.QuitButton.grid(column = 0, columnspan=3)
def fillWindow(self, frame):
PartyWinnersList = [['Some list of places', "Somebody's Name", 0.37448599960838064],
['A shorter list', 'Three Long Names Here', 0.52167817821240514],
['A much longer, longer entry', 'Short Name', 0.41945832387008858]]
placement = 2
for i in PartyWinnersList:
ShowYear = tk.Label(frame,
text="""%s """ % i[0]
)
ShowYear.grid(row=placement, column = 0, sticky=tk.S)
ShowSystem = tk.Label(frame,
text="""%s """ % i[1]
)
ShowSystem.grid(row=placement, column = 1, sticky=tk.N)
PercentVotes = i[2]*100
ShowVotes = tk.Label(frame,
text="""%3.1f""" % PercentVotes
)
ShowVotes.grid(row=placement, column = 2, sticky=tk.N)
placement += 1
def OnFrameConfigure(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
main = Selections(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
The two defined functions are in the class, but I couldn't get them to line up.
I usually don't solve the problem this way, but you can force alignment by specifying label widths. The problem will come when you dynamically resize the window -- everything is static. But this will solve the specified problem -- alignment.
import tkinter as tk
class Selections(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.FifthLabelLeft = tk.Label(self,
text="""Riding""",
justify = tk.CENTER,
width=25,padx = 10)
self.FifthLabelLeft.grid(row=4, column = 0, pady=5)
self.FifthLabelCenter = tk.Label(self,
text="""Winning Candidate""",
justify = tk.CENTER,
width=25,
padx = 10).grid(row=4, column = 1, pady=5)
self.FifthLabelRight = tk.Label(self,
text="""Percent of Vote""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 2, pady=5)
mybox = tk.LabelFrame(self, padx=5, pady=4)
mybox.grid(row=5, column=0, columnspan=3)
canvas = tk.Canvas(mybox, borderwidth=5, background="#70ff33")
frame = tk.Frame(canvas, background="#33f4ff")
# frame.grid_columnconfigure((0,1,2), weight=3)
vsb = tk.Scrollbar(mybox, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, heigh=50)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw", tags="frame")
# be sure that we call OnFrameConfigure on the right canvas
frame.bind("<Configure>", lambda event: self.OnFrameConfigure(canvas))
self.fillWindow(frame)
self.QuitButton = tk.Button(self,
text="QUIT",
command=root.destroy,
padx=25, pady=0)
self.QuitButton.grid(column = 0, columnspan=3)
def fillWindow(self, frame):
PartyWinnersList = [['Some list of places', "Somebody's Name", 0.37448599960838064],
['A shorter list', 'Three Long Names Here', 0.52167817821240514],
['A much longer, longer entry', 'Short Name', 0.41945832387008858]]
placement = 2
for i in PartyWinnersList:
ShowYear = tk.Label(frame, text="""%s """ % i[0], width=25)
ShowYear.grid(row=placement, column = 0, sticky=tk.S)
ShowSystem = tk.Label(frame,
text="""%s """ % i[1],
width=25
)
ShowSystem.grid(row=placement, column = 1, sticky=tk.N)
PercentVotes = i[2]*100
ShowVotes = tk.Label(frame,
text="""%3.1f""" % PercentVotes,
width=15
)
ShowVotes.grid(row=placement, column = 2, sticky=tk.N)
placement += 1
def OnFrameConfigure(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
main = Selections(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()

Categories