This is my code so far:
master = Tk()
Label(master, text="Input:").grid(row=0)
e1 = Entry(master, width = 100)
e1.grid(row=0, column=1)
Button(master, text='Q', command=q_pressed).grid(row=3, column=2,
sticky=W, padx = 4, pady=4)
Button(master, text='C', command =c_input).grid(row=3, column=1,
sticky=W, padx = 0, pady=4)
Button(master, text='Confirm', command=parse_input).grid(row=3, column=0,
sticky=W, padx = 4, pady=4)
I am trying to make it so that "Q" button is next to the "C" button (like how "C" is next to the "Confirm" button) but it instead places it where the Entry widget ends.
I understand this is a grid management issue. How do I set up a layout so row = 0, has text and entry widget and row=3 has its own independent columns (0,1,2) for the buttons?
Your problem is that the Entry e1 is very long and contains 100 characters, but it spans only one column. You can use columnspan to define the number of used columns for e1 in the grid command. Following example with columnspan = 30 works for me. Decide yourself, what would be a reasonable value for columnspan.
from Tkinter import *
master = Tk()
Label(master, text="Input:").grid(row=0)
e1 = Entry(master, width = 100)
e1.grid(row=0, column=1, columnspan=30)
Button(master, text='Q').grid(row=3, column=2)
Button(master, text='C').grid(row=3, column=1)
Button(master, text='Confirm').grid(row=3, column=0)
mainloop()
There is possibly more than one way to perform the layout you want, I choose to present a clean one. Your Entry widget occupies a large horizontal space in the second column of the first row, thus placing widgets below it in the next row is going to cause you trouble. Since you know you will have 3 buttons below it, you could make this Entry span two columns (columnspan=2 while gridding it) and play with that while gridding the next row. But I prefer a different approach, which also produces a better layout overall. What you want is to grid a Frame in the second row that takes two columns (since in the row above it you have two widgets). Then you grid your buttons on this new Frame.
This turns your code into:
import Tkinter
master = Tkinter.Tk()
Tkinter.Label(master, text="Input:").grid(row=0, column=0)
Tkinter.Entry(master, width = 100).grid(row=0, column=1)
frame = Tkinter.Frame()
frame.grid(row=1, column=0, columnspan=2, sticky='w')
Tkinter.Button(frame, text='Confirm').grid(row=0, column=0, sticky='w')
Tkinter.Button(frame, text='C').grid(row=0, column=1, sticky='w')
Tkinter.Button(frame, text='Q').grid(row=0, column=2, sticky='w')
master.mainloop()
enter code here{ master = Tk()
Label(master, text="Input:" ).grid(row=0)
e1 = Entry(master, width=100)
e1.grid(row=0, column=1, columnspan=10)
Button(master, text='Q', width=5, command=q_pressed).grid(row=3, column=1, padx=0, ipadx=5, pady=4, sticky=E)
Button(master, text='C', width=5, command =c_input).grid(row=3, column=1, padx=0, ipadx=5, pady=4, sticky=W)
Button(master, text='Confirm', width=10, command=parse_input).grid(row=3, column=0, padx=0, pady=4, sticky=W)
mainloop()}
Since you wanted 100 char for entry string I based my cell/column width on 10 for simple math division. Set my columnspan to 10. I got rid of padx since it pertains to border width and used ipadx which pertains to inner text width. Made entry set to 10 columns wide of 10 char. Used "input" text as 1 column. This will allow me to use 10 char or one column for text "confirm" and I can break column 1 in half by making button "c" & "q" 5 char wide using width=5. Then by using justfy put "c" West and "q" East.
There are many ways this is just easiest in my mind.
Related
I've been trying to align the entries and buttons on this password manager I built for a while now but haven't been able to find a solution that works.
I tried changing the width, columnspan, and coordinates but it doesn't seem to work.
I want the password entry to be aligned just like the other two but with a lower width so that the generate button does not go over. I also want the add button to be aligned equally with the row and column.
window = Tk()
window.title("Password Manager")
window.config(padx=50, pady=50)
canvas = Canvas(width=200, height=200)
my_pass_img = PhotoImage(file="logo.png")
canvas.create_image(100, 100, image=my_pass_img)
canvas.grid(column=1, row=0)
web_label = Label(text="Website:", fg="black")
web_label.grid(row=1, column=0)
user_label = Label(text="Email/Username:", fg="black")
user_label.grid(row=2, column=0)
pass_label = Label(text="Password:", fg="black")
pass_label.grid(row=3, column=0)
web_entry = Entry(width=35)
web_entry.grid(row=1, column=1, columnspan=2)
web_entry.focus()
user_entry = Entry(width=35)
user_entry.grid(row=2, column=1, columnspan=2)
user_entry.insert(0, "-#gmail.com")
pass_entry = Entry(width=30)
pass_entry.grid(row=3, column=1)
generate_button = Button(text="Generate Password", fg="black", command=generate_password)
generate_button.grid(row=3, column=2)
add_button = Button(width=36, text="Add", fg="black", command=save)
add_button.grid(row=4, column=1, columnspan=2)
window.mainloop()
Grid
When using grid to setup your widgets, the entire window is divided into individual cells based on the number of columns and rows you've specified. Although you can control the individual sizes of widgets, the overall size it can take will be limited by your choice of column- and rowwidth, as well as column- and rowspan.
Sticky
You can use the sticky attribute in grid to set the side of the column you want your widgets to 'stick' to.
The stickiness is set by choosing one of the 8 directions on a compass:
N (north): stick to top of cell
S (south): bottom
E (east): right
W (west): left
NW, NE, SW, SE: top-left, top-right, bottom-left, bottom-right corners respectively.
There are also two bonus stickiness options:
NS: stretch your widget from top to bottom of the sell, but keep it centered horizontally
EW: stretch from left to right of the cell, but centered vertically.
Padding
To increase legibility, you can add padding above and below, and to the sides, of your widgets using the padx and pady attributes (measured in pixels).
So if you set stickyness to "W" with a horizontal padding padx=5, the widget position is offset from the cell boundary by 5 pixels.
Combining it in your example
To align the entries, set their sticky attribute to "W", or tk.W, to algin on left side of cell.
If you want to align labels on the right set sticky="E".
You can reduce the columnspan too of the entries, if you don't need them to be extra large. The resulting changes to grid are thus:
canvas.grid(column=1, row=0)
# Label widgets: set sticky to 'East'
web_label.grid(row=1, column=0, sticky='E')
user_label.grid(row=2, column=0, sticky='E')
pass_label.grid(row=3, column=0, sticky='E')
# Entry widgets
web_entry.grid(row=1, column=1, columnspan=1, sticky='WE')
user_entry.grid(row=2, column=1, columnspan=1, sticky=tk.EW) # Note flag is available as tkinter attribute
# Password entry: align on left-hand side
pass_entry.grid(row=3, column=1, sticky='W')
# Align button to right side of 3rd column
generate_button.grid(row=3, column=2,sticky='E')
# Can either align in middle column...
add_button.grid(row=4, column=1, columnspan=1, sticky="WE")
#... or in middle of page
add_button.grid(row=4, column=0, columnspan=3, sticky="WE")
Bonus
Apply padding (or sticky) to all widgets in a frame or window:
for child in window.winfo_children():
child.grid_configure(padx=3, pady=3)
# child.grid_configure(sticky='W')
You can use sticky option to put the widget in a grid cell at "n" (north), "s" (south), "e" (east) and "w" (west). If not specify, it is by default is "" which will put the widget at the center of the cell.
For your case, add sticky="w" to all the labels and entries, and sticky="e" to the "Add" button:
from tkinter import *
def generate_password():
pass
def save():
pass
window = Tk()
window.title("Password Manager")
window.config(padx=50, pady=50)
canvas = Canvas(window, width=200, height=200)
my_pass_img = PhotoImage(file="logo.png")
canvas.create_image(100, 100, image=my_pass_img)
canvas.grid(column=1, row=0)
web_label = Label(window, text="Website:", fg="black")
web_label.grid(row=1, column=0, sticky="w")
user_label = Label(window, text="Email/Username:", fg="black")
user_label.grid(row=2, column=0, sticky="w")
pass_label = Label(window, text="Password:", fg="black")
pass_label.grid(row=3, column=0, sticky="w")
web_entry = Entry(window, width=35)
web_entry.grid(row=1, column=1, columnspan=2, sticky="w")
web_entry.focus()
user_entry = Entry(window, width=35)
user_entry.grid(row=2, column=1, columnspan=2, sticky="w")
user_entry.insert(0, "-#gmail.com")
pass_entry = Entry(window, width=30)
pass_entry.grid(row=3, column=1, sticky="w")
generate_button = Button(window, text="Generate Password", fg="black", command=generate_password)
generate_button.grid(row=3, column=2)
add_button = Button(window, width=36, text="Add", fg="black", command=save)
add_button.grid(row=4, column=1, columnspan=2, sticky="e")
window.mainloop()
Result:
You can play around with different values or combinations of them of the sticky option to see the different effects.
Note also that it is better to specify the parent of those widgets.
I tried to follow the lesson on databases in this youtube video from freecodecamp. However, when I wrote my code and executed it, an entry box and a label are stuck on top of my button to submit a record. I don't know how to fix this issue.
Using rowconfigure didn't help either and the label is still stuck on top of the submit button.
Also, for every label and button that I add, even if the button's row is smaller than the label's row, the label is displayed before the button in the GUI.
label and entry box on top of button
I have removed all the database elements from my code as they are not relevant to the problem I am facing. Here is my code:
from tkinter import *
window = Tk()
window.geometry("400x400")
# Create function to Delete a record
def delete():
pass
# create submit function
def submit():
pass
# create query function
def query():
pass
f_name = Entry(window, width=30)
f_name.grid(row=0, column=1, pady=(10, 0))
l_name = Entry(window, width=30)
l_name.grid(row=1, column=1)
address = Entry(window, width=30)
address.grid(row=2, column=1)
city = Entry(window, width=30)
city.grid(row=3, column=1)
province = Entry(window, width=30)
province.grid(row=4, column=1)
p_code = Entry(window, width=30)
p_code.grid(row=5, column=1)
# Create text box label
f_name_label = Label(window, text="First Name")
f_name_label.grid(row=0, column=0, pady=(10, 0))
l_name_label = Label(window, text="Last Name")
l_name_label.grid(row=1, column=0)
address_label = Label(window, text="Street")
address_label.grid(row=2, column=0)
city_label = Label(window, text="City")
city_label.grid(row=3, column=0)
province_label = Label(window, text="Province")
province_label.grid(row=4, column=0)
p_code_label = Label(window, text="Postal Code")
p_code_label.grid(row=5, column=0)
# Create submit button
submit_btn = Button(window, text="Add record to database", command=submit)
submit_btn.grid(rows=6, column=0, columnspan=2, pady=10, padx=10, ipadx=100)
# create a query button
query_btn = Button(window, text="Show Records", command=query)
query_btn.grid(rows=7, column=0, columnspan=2, pady=10, padx=10, ipadx=125)
delete_box = Label(window, text="ID Number")
delete_box.grid(row=8, column=0)
delete_box = Entry(window, width=30)
delete_box.grid(row=8, column=1)
# create a delete button
delete_btn = Button(window, text="Delete Record", command=delete)
delete_btn.grid(rows=9, column=0, columnspan=2, pady=10, padx=10, ipadx=125)
window.mainloop()
What mistake have I made?
Edit: Thanks for the responses. It turns out that it was just a typo (rows instead of row) that caused the problem. But now I am curious to know how did my previous code give that particular output? Shouldn't it have thrown an error saying that rows is an invalid keyword argument? Moreover, when the row parameter was not even given for submit_btn, how was it correctly placed below "Postal Code"? Why exactly was there an overlap of the label, entry and button?
The reason for your output is really interesting and involves some minute details in tkinter.
Key Observations:
After experimenting with your code, I made the following observations:
rows is equivalent to rowspan and columns is equivalent to columnspan
When row is not given in the .grid statement, it is taken to be <index-of-last-occupied-row> + 1. On the other hand, the default value of column is fixed at 0.
Explanation for the Undesired Output:
Let's first figure out which is the last occupied row after p_code_label is added.
p_code_label = Label(window, text="Postal Code")
p_code_label.grid(row=5, column=0)
p_code_label is present in row[5] and it is the last occupied row.
Next, the buttons are displayed using grid as explained below:
# Create submit button
submit_btn = Button(window, text="Add record to database", command=submit)
submit_btn.grid(rows=6, column=0, columnspan=2, pady=10, padx=10, ipadx=100)
In the above grid statement, row has not been given. Therefore, submit_btn is added to row[5+1=6] and spans 6 rows (due to rows=6).
Now, the last occupied row = 6 + (6-1) = 11.
# create a query button
query_btn = Button(window, text="Show Records", command=query)
query_btn.grid(rows=7, column=0, columnspan=2, pady=10, padx=10, ipadx=125)
As row is once again not given above, query_btn is added to row[11+1=12] and spans 7 rows.
Now, the last occupied row = 12 + (7-1) = 18.
Note that even though submit_btn and query_btn span multiple rows, they appear to be spanning only 1 row because none of the rows have been assigned any weights.
delete_box = Label(window, text="ID Number")
delete_box.grid(row=8, column=0)
delete_box = Entry(window, width=30)
delete_box.grid(row=8, column=1)
Unlike the previous two widgets, the above two widgets have been given a row. Thus, they occupy row[8] and are displayed on top of the submit_btn. The button is almost completely covered vertically even though the above two widgets are present in only one of the rows spanned by submit_btn. This is because the rows do not have any weights.
If all the rows spanned by submit_btn are assigned weights and the button is gridded using sticky=NSEW, the output would be as given below:
(The vertical centre of 6 rows would be in between two rows. That's the reason why Add record to database is displayed between row[8] and row[9].)
By now, the reason for the overlap of the label, entry and button should be clear.
For the sake of completion, I will also explain how is delete_btn added.
# create a delete button
delete_btn = Button(window, text="Delete Record", command=delete)
delete_btn.grid(rows=9, column=0, columnspan=2, pady=10, padx=10, ipadx=125)
The above widget is added to row[18+1=19] and spans 9 rows.
Thus, the last occupied row = 19 + (9 - 1) = 27.
I did not make any minor changes except to rearrange in order(Label, Entry and Button) for row index (6,7,8,9).
Also, it's bad for putting Entry widget any where before mainloop().
Always start after row=5. Then you can put Entry widget there.
I rearrange code to make readability widgets order..
Code:
delete_box = Label(window, text="ID Number")
delete_box.grid(row=6, column=0)
delete_box = Entry(window, width=30)
delete_box.grid(row=6, column=1)
# Create submit button
submit_btn = Button(window, text="Add record to database", command=submit)
submit_btn.grid(row=7, column=0, columnspan=2, pady=10, padx=10, ipadx=100)
# create a query button
query_btn = Button(window, text="Show Records", command=query)
query_btn.grid(row=8, column=0, columnspan=2, pady=10, padx=10, ipadx=125)
# create a delete button
delete_btn = Button(window, text="Delete Record", command=delete)
delete_btn.grid(row=9, column=0, columnspan=2, pady=10, padx=10, ipadx=125)
window.mainloop()
Screenshot:
I am running into a curious problem. I have a small grid (8 rows, 5 columns). Root with a couple of frames. Most run fine. Four fields of the the top row (1 in my case, I do not use 0) have a text and must have the same color continuously. I could not realise this in root itself so I made a frame (text=""), with labels. That gives the desired look apart from the fact that all the texts are concatenated at the left and no longer above the gridfields below.
The code is realy very simple but I expect it will be requested anyway so here is the relevant part:
# Top frame
top_frame=LabelFrame(root, text= "", bg=top_line_color)
top_frame.grid(row =1, column=0, sticky=W+E, columnspan=5)
# Fixed labels:
Label(top_frame, text="Anten", font=fnt, bg=top_line_color).grid(row=1,
column=0)
Label(top_frame, text="Antenna", font=fnt, bg=top_line_color).grid(row=1,
column=1)
Label(top_frame, text="Corr.", font=fnt, width=4,
bg=top_line_color).grid(row=1, column=3)
Label(top_frame, text="Move", font=fnt, width=4,
bg=top_line_color).grid(row=1, sticky=W, column=4)
Label(root, text="Azimuth:", justify=RIGHT, font=fnt).grid(row=2, sticky=W)
Label(root, text="Elevation:", justify=RIGHT, font=fnt).grid(row=3,
sticky=W)
Label(root, text="Location:",justify=RIGHT, font=fnt).grid(row=4, sticky=W)
Label(root, text=acemedat.myloc, font=fnt).grid(row=4, column=1)
Label(root, text="Dat/Tim:", justify=RIGHT, font=fnt).grid(row=4, column=2)
Label(root, text="Moon distance (km):", font=fnt).grid(row=5, columnspan=2,
sticky=W)
The labels are followed by data fields (in root as well). They comply with the grid without problem.
What can I do to to align the frame fields with the root fields?
Many thanks in advance,
Harke
If you want everything to line up in a grid, the easiest solution is to put them all in the same frame. If you use the sticky attribute for the fixed labels, you can have a solid, unbroken color. Depending on your system, you might need to change the labels to have a borderwidth and/or highlightthickness of zero. If you want the labels to be left-aligned, you can do that with the anchor attribute of the labels.
For example:
from tkinter import *
root = Tk()
fnt = ("Helvetica", "12")
top_line_color = "bisque"
# Fixed labels:
Label(root, text="Anten", font=fnt, bg=top_line_color,
anchor="w").grid(row=0, column=0, sticky="nsew")
Label(root, text="Antenna", font=fnt, bg=top_line_color,
anchor="w").grid(row=0, column=1, sticky="nsew")
Label(root, text="Corr.", font=fnt, width=4, bg=top_line_color,
anchor="w").grid(row=0, column=2, sticky="nsew")
Label(root, text="Move", font=fnt, width=4, bg=top_line_color,
anchor="w").grid(row=0, column=3, sticky="nsew")
Label(root, text="Azimuth:", justify=RIGHT, font=fnt).grid(row=2, sticky=W)
Label(root, text="Elevation:", justify=RIGHT, font=fnt).grid(row=3, sticky=W)
Label(root, text="Location:",justify=RIGHT, font=fnt).grid(row=4, sticky=W)
Label(root, text="acemedat.myloc", font=fnt).grid(row=4, column=1)
Label(root, text="Dat/Tim:", justify=RIGHT, font=fnt).grid(row=4, column=2)
Label(root, text="Moon distance (km):", font=fnt).grid(row=5, columnspan=2, sticky=W)
root.mainloop()
import tkinter as tk
root = tk.Tk()
buttonOK = tk.Button(root, text='B1')
MCC = tk.Button(root, text='B2')
TID = tk.Button(root, text='B3')
CURRENCY = tk.Button(root, text='B4')
COUNTRY = tk.Button(root, text='B5')
RESPONSE = tk.Button(root, text='B6')
B1.grid(row=3, column=0, sticky=tk.E+tk.W)
B2.grid(row=3, column=1, sticky=tk.E+tk.W)
B3.grid(row=3, column=2, sticky=tk.E+tk.W)
B4.grid(row=4, column=0, sticky=tk.E+tk.W)
B5.grid(row=4, column=1, sticky=tk.E+tk.W)
B6.grid(row=4, column=2, sticky=tk.E+tk.W)
label1 = tk.Entry(root, bd =8)
label1.grid(row=2, column=0, rowspan=1, columnspan=3, sticky=tk.E+tk.W)
label=tk.Text(root,background="yellow")
label.insert(index=0.0, chars="Enter values below\nand click search.\n")
label.grid(row=0, column=0,rowspan=1, columnspan=3, sticky=tk.E+tk.W)
root.mainloop()
I am trying to build a GUI in Python using Tkinter but the space for the inserted text label as "Enter values below\nand click search.\n" occupies about 6 blank rows. Please help me remove it. My current result using the code above is the left one, I want to have the right one image.
When you create the text widget, specify the number of lines you want it to display, for example:
label=tk.Text(root,background="yellow", height=3)
Failing to specify means it will default to 24, hence why it is so large in your program.
Ignoring the grid() mistake in your code.
You could correct the sizing issue by providing a weight and starting geometry size.
UPDATE:
If you provide weights to the proper rows and columns give your Text widget a default height of say 3 and tell the grid() on your Text widget to sticky="nsew" you can have your program start out the size you want and be able to resize evenly if you want to.
Take a look at the below code:
import tkinter as tk
root = tk.Tk()
# we want all 3 columns to resize evenly for the buttons so we provide
# a weight of 1 to each. We also want the first row where the text box is
# to resize so there is not unwanted behavior when resizing, so we set its weight to 1.
# keep in mind a weight of zero (default) will tell tkinter to not resize that row or column.
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
root.columnconfigure(2, weight=1)
root.rowconfigure(0, weight=1)
buttonOK = tk.Button(root, text='B1')
MCC = tk.Button(root, text='B2')
TID = tk.Button(root, text='B3')
CURRENCY = tk.Button(root, text='B4')
COUNTRY = tk.Button(root, text='B5')
RESPONSE = tk.Button(root, text='B6')
# corrected the variables being assigned a grid location
buttonOK.grid(row=3, column=0, sticky=tk.E+tk.W)
MCC.grid(row=3, column=1, sticky=tk.E+tk.W)
TID.grid(row=3, column=2, sticky=tk.E+tk.W)
CURRENCY.grid(row=4, column=0, sticky=tk.E+tk.W)
COUNTRY.grid(row=4, column=1, sticky=tk.E+tk.W)
RESPONSE.grid(row=4, column=2, sticky=tk.E+tk.W)
label1 = tk.Entry(root, bd =8)
label1.grid(row=2, column=0, rowspan=1, columnspan=3, sticky=tk.E+tk.W)
# added a height of 3 to the Text widget. to reduce its starting height
label=tk.Text(root,background="yellow", height=3)
label.insert(index=0.0, chars="Enter values below\nand click search.\n")
# added stick="nsew" so the text box will resize with the available space in the window.
label.grid(row=0, column=0,rowspan=1, columnspan=3, sticky="nsew")
root.mainloop()
Picture a 4x4 grid in a tkinter window. I want to expand the cell at row 2, column 2 but not everything else on row 2 or column 2. Im designing a text window with selectable options on the left side in rows 1-15. Making row 2 with weight 1 and column 2 with weight 1 allows my Text widget to expand but so does everything else in row 2 and column 2. Any way around this?
from tkinter import *
root = Tk()
lbl1 = Label(root, text="label1")
lbl1.grid(row=0, column=1)
lbl2 = Label(root, text="label2")
lbl2.grid(row=1, column=0)
lbl3 = Label(root, text="label3")
lbl3.grid(row=3, column=0)
lbl4 = Label(root, text="label4")
lbl4.grid(row=5, column=0)
txt = Text(root, state='disabled', bg='#E8E8E8')
txt.grid(row=1, column=1, padx=10, pady=10, sticky="NSEW", columnspan=2, rowspan=2)
root.rowconfigure(2, weight=1)
root.columnconfigure(2, weight=1)
root.mainloop()
Example 2:
from tkinter import *
root = Tk()
frame1 = Frame(root)
frame1.grid(row=0, column=1)
frame2 = Frame(root)
frame2.grid(row=1, column=0)
frame3 = Frame(root)
frame3.grid(row=1, column=1, rowspan=2, columnspan=2)
lbl1 = Label(frame1, text="label1")
lbl2 = Label(frame2, text="label2")
lbl3 = Label(frame2, text="label3")
lbl4 = Label(frame2, text="label4")
lbl1.grid(row=0, column=1, sticky=N)
lbl2.grid(row=3, column=0, sticky=N)
lbl3.grid(row=5, column=0, sticky=N)
lbl4.grid(row=7, column=0, sticky=N)
txt = Text(frame3, state='disabled', bg='#E8E8E8')
txt.grid(row=0, column=0, padx=10, pady=10, sticky="NSEW", columnspan=2, rowspan=2)
root.rowconfigure(2, weight=1)
root.columnconfigure(2, weight=1)
frame3.rowconfigure(0, weight=1)
frame3.columnconfigure(0, weight=1)
root.mainloop()
Example 2 has everything in the position I want it in but the Text widget does not expand. Is it possible to set a frame to expand when using grid?
Your question asks about a 4x4 grid, but your example shows only two columns. That makes it hard to understand what you want. In the comments you say you simply want the text area of the example to grow and shrink and all the labels together, so that's what I'll address.
The simplest solution is to have an extra row and column to the right and below the text area. Have the text widget span into those areas, and give those areas a weight of 1. That means that, as the window changes size, any extra space is allocated to areas not occupied by buttons.
pro tip: I find layout problems much easier to visualize and solve when all of the layout code is together.
It would look something like this:
lbl1.grid(row=0, column=1)
lbl2.grid(row=1, column=0)
lbl3.grid(row=2, column=0)
lbl4.grid(row=3, column=0)
txt.grid(row=1, column=1, padx=10, pady=10, sticky="NSEW", columnspan=2, rowspan=4)
root.rowconfigure(4, weight=1)
root.columnconfigure(2, weight=1)
I think your layout problems might be better solved by using pack instead of grid for part of the layout. For example, you might start with three areas: a toolbar, a side panel, and then main area with the text widget:
toolbar = Frame(root, ...)
side = Frame(root, ...)
main = Frame(root, ...)
toolbar.pack(side="top", fill="x")
side.pack(side="left", fill="y")
main.pack(side="right", fill="both", expand=True)
With that you now have three relatively independent areas. You can use pack or grid in each of these frames independently, making it much easier to keep track of rows and columns.
One way around it would be to make your grid twice as large, setting the things you want to be expandable to span two columns/rows.
I.e. you use exclusively odd numbered rows/columns for griding things ([1,1][1,3],[3,1][3,3]...) and you set even-numbered rows/columns to have weight. Anything you want to expand in one or both directions gets a columnspan or rowspan of 2, pushing it into a row/column which may expand as needed.
With the information everyone has provided I was able to come up with a solution. I left the Text widget on the main window instead of in a frame and put my labels/tools in frames. Basically using the fact that a frame will not expand to lock down the labels. Now when the window is expanded only the widget grows.
from tkinter import *
root = Tk()
frame1 = Frame(root)
frame1.grid(row=0, column=1)
frame2 = Frame(root)
frame2.grid(row=1, column=0)
lbl1 = Label(frame1, text="label1")
lbl2 = Label(frame2, text="label2")
lbl3 = Label(frame2, text="label3")
lbl4 = Label(frame2, text="label4")
lbl1.grid(row=0, column=1, sticky=N)
lbl2.grid(row=3, column=0, sticky=N)
lbl3.grid(row=5, column=0, sticky=N)
lbl4.grid(row=7, column=0, sticky=N)
txt = Text(root, state='disabled', bg='#E8E8E8')
txt.grid(row=1, column=1, padx=10, pady=10, sticky="NSEW", columnspan=2, rowspan=2)
root.rowconfigure(2, weight=1)
root.columnconfigure(2, weight=1)
root.mainloop()
Thanks for all the help.