All was going well as seen in the 1st pic below. all the cells are the same perfect size. its great.
But then comes the implementation of the textbox. and all hell breaks loose. as seen in the 2nd picture it completely disrupts my grid layout. i dont want the textbox adjusting cell sizes, i want it to go where i tell it to go like all the other widgets do. Ive spent hours on this and no avail!
import tkinter as tk
from tkinter import ttk, scrolledtext
root = tk.Tk()
root.state('zoomed')
root.configure(background='#8585ad')
for i in range(0,20):
for x in range(0,20):
root.columnconfigure(i, weight=1)
root.rowconfigure(x, weight=1)
for i in range(0, 20): # 0-19(20 is excluded) so this will loop 10x
for x in range(0, 20):
tk.Label(root, text=f"C-{i}, R-{x}", bg="green", fg="white").grid(column=i, row=x, sticky="NSEW", padx=1, pady=1)
main_frame = tk.Label(root, text="MAIN FRAME", bg="blue", fg="white", anchor="n").grid(column=1, row=1, columnspan=18, rowspan=18, sticky="NSEW")
frame1 = tk.Label(root, text="FRAME 1", bg="red", fg="white", anchor="n").grid(column=2, row=2, columnspan=3, rowspan=16, sticky="NSEW")
frame2 = tk.Label(root, text="FRAME 2", bg="green", fg="white", anchor="n").grid(column=6, row=2, columnspan=6, rowspan=16, sticky="NSEW")
frame3 = tk.Label(root, text=" FRAME 3", bg="red", fg="white", anchor="n").grid(column=13, row=2, columnspan=5, rowspan=16, sticky="NSEW")
for i in range(2, 5): # start at 2 and end after the 3rd loop.
for x in range(3, 18): # to loop 15x and for index to start at 3 so i then put (3,18), 18-3 = 15
tk.Label(root, text=f"Button-{(x-2)}", bg="white", fg="black").grid(column=i, row=x, sticky="EW", padx=5, pady=5)
frame1_header = tk.Label(root, text="User Panel", bg="black", fg="white").grid(column=2, row=2, columnspan=3, sticky="SEW", padx=5, pady=5)
frame2_header = tk.Label(root, text="Editor", bg="black", fg="white").grid(column=6, row=2, columnspan=6, sticky="SEW", padx=5, pady=5)
frame3_header = tk.Label(root, text="Info Panel", bg="black", fg="white").grid(column=13, row=2, columnspan=5, sticky="SEW", padx=5, pady=5)
frame2_text_area = tk.Label(root, text="Text Box", bg="black", fg="white", anchor="center").grid(column=6, row=3, columnspan=4, rowspan=15, sticky="NSEW", padx=5, pady=5)
frame2_list_box = tk.Label(root, text="List Box", bg="grey", fg="white", anchor="center").grid(column=10, row=3, columnspan=2, rowspan=15, sticky="NSEW", padx=5, pady=5)
frame3_tab_panel = ttk.Notebook(root)
frame3_tab_panel.grid(column=13, row=3, columnspan=5, rowspan=15, sticky="NSEW", padx=5, pady=5)
tab1 = ttk.Frame(root)
tab2 = ttk.Frame(root)
tab3 = ttk.Frame(root)
frame3_tab_panel.add(tab1, text ='Generic Editor')
frame3_tab_panel.add(tab2, text ='Text Compare')
frame3_tab_panel.add(tab3, text ='Script Ref')
# width and height does indeed adjust the texbox size but the textbox still isnt properly sticking to the grid that i set.
frame3_tab_panel_tab1 = tk.Text(root, relief="ridge", bd=2, undo=True, wrap="none", background='#1E1E1E', insertbackground='white')#, width=40, height=10)
frame3_tab_panel_tab1.grid(column=13, row=4, columnspan=5, rowspan=14, padx=5, pady=5)
frame3_tab_panel_tab1.config(font=('Consolas bold',10), fg="white")
frame3_tab_panel_tab1.focus()
root.mainloop()
"""
text_area = scrolledtext.ScrolledText(tab1, wrap = tk.WORD, width=40, height=10, font=("Times New Roman", 15))
text_area.grid(column = 0, pady = 10, padx = 10)
text_area.focus()
"""
without textbox. as you can see its all perfectly even.
FYI: this is just a template im working on so i can better understand tk's positioning.
textbox ruining grid by not adjusting itself accordingly and fitting to the grid i set.
There is a lot of wrong doing in your code and you really should take a good tutorial for tkinter and you may wish to have a brief overview of tkinters geometry management.
The biggest issue is whats causes your code to work differently as you expect it, you always define the root as the master. Every widget, except for the root window, has a master and is set by the ONLY positional argument every widget requiers. Note that if None is given, the root window is set by default. This is, because tkinter is built hirachically and at the top of this hirachy stands the root window (the instance of tk.Tk()).
A master should be a container and this means either the root window, a Toplevel or a Frame. Masters can have so called children, which can be every other widget plus frames that are handled as children. The relationship between a master and a frame are various, but for the scope of this question we will just look at the geometry.
Every widget has a geometry and can be received by the universal widget method .winfo_geometry() that will give you a geometry string 'widthxheight±x_offset±y_offset' (e.g. '120x50-0+20'). The geometry string is the basement for every calculations to order your widgets, which you can affect by choosing a geometry manager and different optional keywords. With those information an output will be created and displayed on your screen.
Suggestion:
import tkinter as tk
from tkinter import ttk, scrolledtext
def populate_frame_1():
frame_1_label = tk.Label(frame_1,text='User Panel',
background = 'black',
foreground = 'white')
frame_1_label.grid(column=0,row=0,sticky='ew',columnspan=3)
frame_1.columnconfigure(0,weight=1)
frame_1.columnconfigure(1,weight=1)
frame_1.columnconfigure(2,weight=1)
for i in range(0, 3):
for x in range(1, 16):
l = tk.Button(frame_1, text=f"Button-{(x-2)}",
bg="white", fg="black")
l.grid(column=i, row=x, sticky="EW", padx=5, pady=5)
def populate_frame_2():
frame_2_label = tk.Label(frame_2,text='Editor',
background = 'black',
foreground = 'white')
textbox = tk.Text(frame_2,width=35)
listbox = tk.Listbox(frame_2,bg='yellow')
frame_2_label.grid(column=0,row=0,sticky='ew',columnspan=6)
textbox.grid(column=0,row=1,sticky='ns',columnspan=4)
listbox.grid(column=4,row=1,sticky='ns',columnspan=2)
frame_2.rowconfigure(1,weight=2)
def populate_frame_3():
frame_3_label = tk.Label(frame_3,text='Info Panel',
background = 'black',
foreground = 'white')
frame_3_label.grid(column=0,row=0,sticky='ew',columnspan=5)
control_panel = ttk.Notebook(frame_3)
tab1 = ttk.Frame(control_panel)
tab2 = ttk.Frame(control_panel)
tab3 = ttk.Frame(control_panel)
control_panel.add(tab1, text ='Generic Editor')
control_panel.add(tab2, text ='Text Compare')
control_panel.add(tab3, text ='Script Ref')
control_panel.grid(column=0,row=1,sticky='nswe')
frame3_tab_panel_tab1 = tk.Text(tab1, relief="ridge", bd=2, undo=True,
wrap="none", background='#1E1E1E',
insertbackground='white',width=40, height=10)
frame3_tab_panel_tab1.pack(fill=tk.BOTH,expand=True)
frame3_tab_panel_tab1.config(font=('Consolas bold',10), fg="white")
frame3_tab_panel_tab1.focus()
frame_3.rowconfigure(1,weight=2)
frame_3.columnconfigure(0,weight=2)
XOFFSET = 75
YOFFSET = 50
root = tk.Tk()
root.state('zoomed')
root.configure(background='#8585ad')
main_frame = tk.Frame(root,background='blue')
frame_1 = tk.Frame(main_frame,background='red')
frame_2 = tk.Frame(main_frame,background='green')
frame_3 = tk.Frame(main_frame,background='red')
main_frame.pack(fill=tk.BOTH,expand=True,
padx=XOFFSET,pady=YOFFSET)
frame_1.pack(side=tk.LEFT,fill=tk.BOTH,padx=XOFFSET,pady=YOFFSET,expand=True)
frame_2.pack(side=tk.LEFT,fill=tk.Y,pady=YOFFSET,expand=True)
frame_3.pack(side=tk.LEFT,fill=tk.BOTH,padx=XOFFSET,pady=YOFFSET,expand=True)
populate_frame_1()
populate_frame_2()
populate_frame_3()
root.mainloop()
Change
frame3_tab_panel_tab1.grid(
column=13, row=4, columnspan=5, rowspan=14, padx=5, pady=5
)
to
frame3_tab_panel_tab1.grid(
column=13, row=4, columnspan=5, rowspan=14, padx=5, pady=5,
sticky="NSEW"
)
I managed to solve it by replacing the Text() widget with the scrolledtext.ScrolledText() widget. Its strange. No grid was required and if i remove height and width then it messes it up. Why does height and width have such an impact? why does it even exist when you have things like column and row configure along with sticky. Tkinter is quite confusing sometimes with its logic. But anyways, got there in the end.
Here's the code in case anyone encounters a similar issue.
import tkinter as tk
from tkinter import ttk, scrolledtext
root = tk.Tk()
root.state('zoomed')
root.configure(background='#8585ad')
for i in range(0,20):
for x in range(0,20):
root.columnconfigure(i, weight=1)
root.rowconfigure(x, weight=1)
for i in range(0, 20): # 0-19(20 is excluded) so this will loop 10x
for x in range(0, 20):
tk.Label(root, text=f"C-{i}, R-{x}", bg="green", fg="white").grid(column=i, row=x, sticky="NSEW", padx=1, pady=1)
main_frame = tk.Label(root, text="MAIN FRAME", bg="blue", fg="white", anchor="n").grid(column=1, row=1, columnspan=18, rowspan=18, sticky="NSEW")
frame1 = tk.Label(root, text="FRAME 1", bg="red", fg="white", anchor="n").grid(column=2, row=2, columnspan=3, rowspan=16, sticky="NSEW")
frame2 = tk.Label(root, text="FRAME 2", bg="green", fg="white", anchor="n").grid(column=6, row=2, columnspan=6, rowspan=16, sticky="NSEW")
frame3 = tk.Label(root, text=" FRAME 3", bg="red", fg="white", anchor="n").grid(column=13, row=2, columnspan=5, rowspan=16, sticky="NSEW")
for i in range(2, 5): # start at 2 and end after the 3rd loop.
for x in range(3, 18): # to loop 15x and for index to start at 3 so i then put (3,18), 18-3 = 15
tk.Label(root, text=f"Button-{(x-2)}", bg="white", fg="black").grid(column=i, row=x, sticky="EW", padx=5, pady=5)
frame1_header = tk.Label(root, text="User Panel", bg="black", fg="white").grid(column=2, row=2, columnspan=3, sticky="SEW", padx=5, pady=5)
frame2_header = tk.Label(root, text="Editor", bg="black", fg="white").grid(column=6, row=2, columnspan=6, sticky="SEW", padx=5, pady=5)
frame3_header = tk.Label(root, text="Info Panel", bg="black", fg="white").grid(column=13, row=2, columnspan=5, sticky="SEW", padx=5, pady=5)
frame2_text_area = tk.Label(root, text="Text Box", bg="black", fg="white", anchor="center").grid(column=6, row=3, columnspan=4, rowspan=15, sticky="NSEW", padx=5, pady=5)
frame2_list_box = tk.Label(root, text="List Box", bg="grey", fg="white", anchor="center").grid(column=10, row=3, columnspan=2, rowspan=15, sticky="NSEW", padx=5, pady=5)
frame3_tab_panel = ttk.Notebook(root)
frame3_tab_panel.grid(column=13, row=3, columnspan=5, rowspan=15, sticky="NSEW", padx=5, pady=5)
frame3_tab_panel_tab1 = scrolledtext.ScrolledText(root, bd=2, undo=True, wrap="none", width=40, height=10, font=("Times New Roman", 15), background='#1E1E1E', insertbackground='white')
frame3_tab_panel_tab1.config(font=('Consolas bold',10), fg="white")
frame3_tab_panel_tab1.focus()
tab2 = ttk.Frame(root)
tab3 = ttk.Frame(root)
frame3_tab_panel.add(frame3_tab_panel_tab1, text ='Generic Editor')
frame3_tab_panel.add(tab2, text ='Text Compare')
frame3_tab_panel.add(tab3, text ='Script Ref')
root.mainloop()
I'm aiming to make a login program but the only part that confuses me is how to make the frames.I need 3 different frames but I neither know how to make a frame other the then like this:
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
and I can only make labels and widgets using that single mainframe. As far as making another one, it is beyond me. I need to know exactly place widets inside of each frame and even after creating frames I don't know how to place stuff on the grid. Would I go for the overall grid, or does something change after making the grid. I'm using the following layout for making the frame. Basically i'm hoping for a crash course in frames. Any information i've gathered doesn't make sense to me, even after I tried to put it into code.
I've got the coding part down just not the frame part.
#Import tkinter to make gui
from tkinter import *
from tkinter import ttk
import codecs
def login(*args
):
file = open("rot13.txt", "r")
lines = file.readlines()
uname = user.get()
pword = pw.get()
for i in lines:
x = i.split()
if codecs.encode(uname,'rot13') == x[0] and codecs.encode(pword,'rot13') == x[1]:
result.set("Successful")
break;
else:
result.set("Access Denied")
root = Tk()
root.title("Login")
#Configures column and row settings and sets padding
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
user = StringVar()
pw = StringVar()
result = StringVar()
user_entry = ttk.Entry(mainframe, width=20, textvariable=user)
user_entry.grid(column=2, row=1, sticky=(W, E))
pw_entry = ttk.Entry(mainframe, width=20, textvariable=pw)
pw_entry.grid(column=2, row=2, sticky=(W, E))
ttk.Label(mainframe, text="Username ").grid(column=1, row=1, sticky=W)
ttk.Label(mainframe, text="Password ").grid(column=1, row=2, sticky=W)
ttk.Label(mainframe, text="").grid(column=1, row=3, sticky=W)
ttk.Label(mainframe, text="Result").grid(column=1, row=4, sticky=W)
ttk.Label(mainframe, text="").grid(column=1, row=5, sticky=W)
ttk.Button(mainframe, text="Login", command=login).grid(column=3, row=6, sticky=W)
#Makes a spot to put in result
ttk.Label(mainframe, textvariable=result).grid(column=2, row=4, sticky=(W, E))
#Opens up with item selected and allows you to enter username without having to click it
user_entry.focus()
#Runs calculate if click enter
root.bind('<Return>', login)
root.mainloop()
I believe the key point that you are missing is that subframes of mainframe use mainframe as the parent and that widgets within subframes use the subframe as parent. Furthermore, you can then place the subframe within the mainframe and the subframe widgets within the subframe. You do not have to pass parents to .grid because each widget knows its parent. A simplified example:
from tkinter import *
root = Tk()
mainframe = Frame(root)
login = Frame(mainframe)
label = Label(login, text='label')
entry = Entry(login)
display = Frame(mainframe)
result = Label(display, text='display result')
mainframe.grid() # within root
login.grid(row=0, column=0) # within mainframe
label.grid(row=0, column=0) # within login
entry.grid(row=0, column=1) # within login
display.grid() # within mainfram
result.grid(row=2, column=0) # within display
from tkinter import *
def show_entry_fields():
e2.insert(10,(e1.get()))
master = Tk()
master.minsize(width=900, height=20)
Label(master, text="Paste").grid(row=0)
Label(master, text="Output").grid(row=1)
e1 = Entry(master)
e2 = Entry(master)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
Button(master, text='Quit', command=master.quit).grid(row=3, column=0, sticky=W, pady=4)
Button(master, text='Convert', command=show_entry_fields).grid(row=3, column=1, sticky=W, pady=4)
mainloop( )
I just started learning Python and tkinter. I want to make the two Entry boxes as wide as the entire window. But even as I make the window larger, the Entry fields are confined to the first column of the "grid" and do not expand. How can I make the entry fields wider?
From http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/grid-config.html
w.columnconfigure(N, option=value, ...)
weight To make a column or row stretchable, use this option and supply a value that gives the relative weight of this column or row when distributing the extra space. For example, if a widget w contains a grid layout, these lines will distribute three-fourths of the extra space to the first column and one-fourth to the second column:
w.columnconfigure(0, weight=3)
w.columnconfigure(1, weight=1)
If this option is not used, the column or row will not stretch.
So in your case
master.columnconfigure(1, weight=1)
along with updating sticky attribute of e1 (and e2), as pointed out by #BryanOakley:
e1.grid(row=0, column=1, sticky=W+E)
should do the trick.
I have a similar example you can try this:
from Tkinter import *
root = Tk()
row1 = Frame(root)
row1.grid(row=0, column=0, sticky=N+S+E+W)
row2 = Frame(root)
row2.grid(row=1, column=0, sticky=N+S+E+W)
Label1 = Label(row1,text = "First")
Label2 = Label(row2,text = "Second")
entry1 = Entry(row1)
entry2= Entry(row2)
row1.pack(fill=X)
Label1.pack(side = LEFT)
entry1.pack(side = LEFT,fill=X,expand = True)
row2.pack(fill=X)
Label2.pack(side = LEFT)
entry2.pack(side = LEFT,fill=X,expand = True)
root.mainloop()
I came up with the following code more as a reference to help me remember how to build GUI apps with TkInter. It runs great except when a click Button1 or any other widget whose command option is set to self.hello. As you can see in the code bellow, the hello function is like a place holder. While the button click works fine while running the script through IDLE, it simply causes the application to exit if you start the program by double-clicking the actual file test.pyw. My question is, why?
#Some guy somewhere
from tkinter import *
class Application:
def hello(self):
msg = messagebox.showinfo('Message Title','Message Body')
def __init__(self, parent):
parent.resizable(0,0)
parent.minsize(800, 400)
parent.title('Top Level')
# Global Padding pady and padx
pad_x = 0
pad_y = 0
# CASCADE MENU
# create a parent menu.
self.parentmenu1 = Menu(parent, tearoff=0)
#self.menubar1.add_command(label='Menu1', command=self.hello)
#create a child menu for parent menu.
self.parentmenu1_child1 = Menu(parent, tearoff=0)
self.parentmenu1_child1.add_command(label='Item1', command=self.hello)
self.parentmenu1_child1.add_command(label='Item2', command=self.hello)
self.parentmenu1_child1.add_command(label='Item3', command=self.hello)
#add child menu to parent menu.
self.parentmenu1.add_cascade(label='Menu1', menu=self.parentmenu1_child1)
#self.menubar1.add_separator()
# SINGLE MENU
# create a parent menu.
self.parentmenu1.add_command(label='Menu2', command=self.hello)
# SINGLE MENU
# create a parent menu.
self.parentmenu1.add_command(label='Menu3', command=self.hello)
# display the parent menu.
parent.config(menu=self.parentmenu1)
# Create controls
#create label
self.label1 = Label(parent, text='Label1')
#create textbox
self.textbox1 = Entry(parent)
#create button
self.button1 = Button(parent, text='Button1', command=self.hello)
#string variable to hold checkbox1 values.
self.str_checkbox1 = StringVar()
#create checkbox
self.checkbox1 = Checkbutton(parent, text='Checkbox1', variable=self.str_checkbox1, onvalue='on1', offvalue='off1')
#deselect checkbox1
self.checkbox1.deselect()
#string variable to hold checkbox2 values.
self.str_checkbox2 = StringVar()
#create checkbox
self.checkbox2 = Checkbutton(parent, text='Checkbox2', variable=self.str_checkbox2, onvalue='on2', offvalue='off2')
#deselect checkbox2
self.checkbox2.deselect()
#???? ..what sets the groupbox apart from others. primary key???!!
self.str_radiobutton1 = StringVar()
#command= parameter missing.
self.radiobutton1 = Radiobutton(parent, text='Radio 1', variable=self.str_radiobutton1, value='a')
self.radiobutton2 = Radiobutton(parent, text='Radio 2', variable=self.str_radiobutton1, value='b')
self.radiobutton1.select()
#create a list of options.
optionList = ('Option1', 'Option2', 'Option3')
#string variable to hold optionlist values.
self.str_optionmenu1 = StringVar()
#associate string variable with optionlist
self.str_optionmenu1.set(optionList[0])
#create optionmenu
self.optionmenu1 = OptionMenu(parent, self.str_optionmenu1, *optionList)
#create a frame
self.frame1 = Frame(parent)
#create a text.
self.textarea1 = Text(self.frame1, width=20, height=10)
#align text left and fill frame with it.
self.textarea1.pack(side=LEFT, fill=Y)
#create a scrollbar.
self.scrollbar1 = Scrollbar(self.frame1)
#align scrollbar right and fill frame with it.
self.scrollbar1.pack(side=RIGHT, fill=Y)
#what is going to be scrolled?
self.scrollbar1.config(command=self.textarea1.yview)
#set textarea scrollbar.
self.textarea1.config(yscrollcommand=self.scrollbar1.set)
#align frame left and fill.
self.frame1.pack(side=LEFT, fill=Y)
#create a frame
self.frame2 = Frame(parent)
#create a text.
self.listbox1 = Listbox(self.frame2, width=20, height=10, activestyle='none', selectmode=SINGLE)
#create a list of items.
optionList = ('Item1', 'Item2', 'Item3', 'Item4', 'Item5', 'Item6', 'Item7', 'Item8', 'Item9', 'Item10', 'Item11')
#add items from list to listbox
for item in optionList:
self.listbox1.insert(END, item)
#align text left and fill frame with it.
self.listbox1.pack(side=LEFT, fill=Y)
#create a scrollbar.
self.scrollbar2 = Scrollbar(self.frame2)
#align scrollbar right and fill frame with it.
self.scrollbar2.pack(side=RIGHT, fill=Y)
#what is going to be scrolled?
self.scrollbar2.config(command=self.listbox1.yview)
#set textarea scrollbar.
self.listbox1.config(yscrollcommand=self.scrollbar2.set)
#align frame left and fill.
self.frame2.pack(side=LEFT, fill=Y)
# Place controls inside of grid
self.label1.grid(row=0, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.textbox1.grid(row=0, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.button1.grid(row=1, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.checkbox1.grid(row=1, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.checkbox2.grid(row=1, column=2, padx=pad_x, pady=pad_y, sticky=W)
self.optionmenu1.grid(row=2, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.frame1.grid(row=2, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.radiobutton1.grid(row=3, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.radiobutton2.grid(row=3, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.frame2.grid(row=4, column=0, padx=pad_x, pady=pad_y, sticky=W)
if __name__ == '__main__':
parent = Tk()
app = Application(parent)
parent.mainloop()
Alright. Apparently tkMessageBox has been renamed to messagebox in Python 3.x. Also this
module is not available in tkinter so even though a developer might use:
from tkinter import *
..he/she would still need to:
from tkinter import messagebox