Why are my widgets not positioning on given row and column? - python

I am trying to place a label and a button widgets at row=0 and different columns, inside a nested frame (i.e result frame). But inside result frame, label and button widgets appears in the center. Can you me to place widgets on intended positions.
import tkinter as tk
window = tk.Tk()
window.title("ASA Downloader")
window.geometry("400x400")
frame1 = tk.LabelFrame(window, padx=5, pady=5, height="200")
#Labels
weight_label = tk.Label(frame1, text= "Weight")
height_label = tk.Label(frame1, text= "Height")
weight_label.grid(row=0,column=0)
height_label.grid(row=1,column=0)
result_frame = tk.LabelFrame(frame1, text="result frame")
result_frame.grid(row=2, column=0)
result_lable= tk.Label(result_frame, text=" result is none")
result_lable.grid(row=0, column=0)
button = tk.Button(result_frame, text="My button inside nested frame")
button.grid(row=1, column=0)
frame1.place(relx=.5, rely=.5, anchor="center")
window.mainloop()

By default, widgets are centered by grid unless you tell it to do otherwise. You aren't telling it to do otherwise, so that is what is happening.
The width of the column is being set by the widest thing in the column. In this case the width of column 0 is the width of the inner frame (which itself is controlled by the width of the button). Since the column is widget than the labels, the labels are centered.
If you are wanting the width and height labels to be on the left, the simplest solution is to set the sticky option to "w" (for west).
weight_label.grid(row=0,column=0, sticky="w")
height_label.grid(row=1,column=0, sticky="w")
Another option would be to set sticky to "ew" (east-west) so that the widget expands to fill the column. You can then set the "anchor" attribute of each label to "w" so that the text is left-aligned. The choice of which solution to use depends a bit on whether you plan to have other widgets in your window.
weight_label = tk.Label(frame1, text= "Weight", anchor="w")
height_label = tk.Label(frame1, text= "Height", anchor="w")
weight_label.grid(row=0,column=0, sticky="ew")
height_label.grid(row=1,column=0, sticky="ew")

Related

Aligning entries/buttons with tkinter

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.

How do I align something to the bottom left in Tkinter using either .grid() or .pack()?

I am currently trying to make a game in Tkinter which uses multiple different windows.
However, as I am trying to create the layout of a secondary window, I can't seem to get my Return to Menu button underneath the list box, and aligned to the left. I have tried it using .pack() and .grid() methods, but they don't make sense to me.
I've tried using .pack():
header = Frame(wn).pack()
title = Label(header, text='Single-Player',font=('Arial bold',20),bg=bgcolor).pack(anchor='center')
footer = Frame(wn).pack(side=BOTTOM)
return_to_menu = Button(footer, text='Return to Main Menu',font=('Arial',16),bg=bgcolor,command=lambda: menu()).pack(side=BOTTOM,padx=20,pady=250)
# body frame (left side)
bodyL = Frame(wn).pack(side=LEFT)
#output box
output = Listbox(bodyL, width=50, font=("Arial", 20)).pack(side=LEFT,padx=15)`
And I've tried using .grid():
header = Frame(wn).grid(sticky=N)
title = Label(header, text='Single-Player',font=('Arial bold',20),bg=bgcolor).grid(sticky=N+E+W,row=0,column=0)
footer = Frame(wn).grid(sticky=S)
return_to_menu = Button(footer, text='Return to Main Menu',font=('Arial',16),bg=bgcolor,command=lambda: menu()).grid(sticky=S,padx=20,row=0,column=0)
# body frame (left side)
bodyL = Frame(wn).grid(sticky=W)
#output box
output = Listbox(bodyL, width=50, font=("Arial", 20)).grid(sticky=W,padx=15, )`
However using .grid() doesn't align my title to the center of the screen anymore.
Is there a way to center it more efficiently - I didn't like using padx=450 to get it centered.
What happens with the button and the list box, is the button appears to the left of the list box, instead of on the bottom. I have attempted several other methods, such as incrementing row numbers, but my script doesn't appear to do what I anticipated.
Here is an example of how you can set up the weight of specific columns and row to get widgets to stick to a specific location on the screen.
With the use of grid() we need to use columnconfigure() and rowconfigure() to make this work.
These 2 methods are used to define at what ratio the column or row will expand in relation to the columns or rows around it as the container grows or shrinks.
See below example and let me know if you have any questions:
import tkinter as tk
root = tk.Tk()
for i in range(3):
root.columnconfigure(i, weight=1)
root.rowconfigure(1, weight=1)
tk.Label(root, text='Top left').grid(row=0, column=0, sticky='w')
tk.Label(root, text='Top center').grid(row=0, column=1)
tk.Label(root, text='Top right').grid(row=0, column=2, sticky='e')
tk.Label(root, text='center').grid(row=1, column=1)
tk.Label(root, text='Bottom left').grid(row=2, column=0, sticky='w')
tk.Label(root, text='Bottom center').grid(row=2, column=1)
tk.Label(root, text='Bottom right').grid(row=2, column=2, sticky='e')
root.mainloop()
Example:
Here is another example but this time I have a title label outside of a frame so that we can make it easier to manage the title being centered and then working with all the other content of the frame is separate from the title label.
import tkinter as tk
root = tk.Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(1, weight=1)
frame = tk.Frame(root)
frame.grid(row=1, column=0, sticky='nsew')
for i in range(3):
frame.columnconfigure(i, weight=1)
frame.rowconfigure(1, weight=1)
tk.Label(root, text='Title centered').grid(row=0, column=0)
tk.Label(frame, text='Top left').grid(row=0, column=0, sticky='w')
tk.Label(frame, text='Top center').grid(row=0, column=1)
tk.Label(frame, text='Top right').grid(row=0, column=2, sticky='e')
tk.Label(frame, text='Center').grid(row=1, column=1)
tk.Label(frame, text='Bottom left').grid(row=2, column=0, sticky='w')
tk.Label(frame, text='Bottom center').grid(row=2, column=1)
tk.Label(frame, text='Bottom right').grid(row=2, column=2, sticky='e')
tk.Label(root, text='Footer centered').grid(row=2, column=0)
root.mainloop()
Example:
To address your problem in the comments you cannot use grid() or any other geometry manager for that matter on the same line you create your container. This will always cause the variable for that frame to return None as the geometry managers return None when called.
See this image as to what happens when you use grid() on the same line you create your container.
Now if you delete the grid() part on the row that your container is created and then write it on the next line as the commented out section of the above images shows it will work as expected. See below image of proper use of grid() for containers.
To address your 2nd question in the comments you can add this line to provide a button at the bottom left.
tk.Button(root, text='Bottom left button').grid(row=3, column=0, sticky='w')
Example:

Tkinter: Placing the Labels one after another in Canvas relatively.

I would like to arrange the Labels in the canvas one after another. But placement is not coming out as desired.
Below is the function that inserts the labels in the canvas. But the ones in the for loop overlaps. Reason - some labels are larger in size than the other. Hence, I assume that largest size be 80 and do the placements respectively. I would like to change this type of approach. Rather I want the labels to be placed relatively one after the other.
def calculate(*args):
try:
ttk.Label(canvas, text="Result:").place(x=20, y=20)
ttk.Label(canvas, text="Topic:").place(x=20, y=80)
ttk.Label(canvas, textvariable=topic).place(x=200, y=80)
ttk.Label(canvas, text="Environment:").place(x=20, y=120)
ttk.Label(canvas, textvariable=environment).place(x=200, y=120)
ttk.Label(canvas, text="Event Results:").place(x=20, y=160)
inputValue=TextArea.get("1.0","end-1c")
len_max=0
result={}
for s in inputValue.splitlines():
data = MainInstance.searchWithPayload(s)
result[s]=data
if len(s+data) > len_max:
len_max = len(s+data)
i = 190
for key in result.keys():
print(key)
print(result[key])
ttk.Label(canvas, text=key+"\n\n"+result[key], wraplength=800).place(x=20,y = i)
i = i + 80
except ValueError:
pass
Below is the code that integrates the canvas widget. And the calculate button calls the calculate function.
ttk.Button(page2, text="Exit",command=page1.quit).grid(column=2, row=8)
ttk.Button(page2, text="Calculate", command=calculate).grid(column=3, row=8)
canvas = Canvas(root, width=900, height=universal_height)
canvas.grid(column=1, row=0)
root.mainloop()
Actually, there are two parts of the question:
How can I relatively place the label to stop the overlapping?
I tried adding the scroll to the canvas. But the application does not respond and does not pop up.
Code for adding the scrollbar:
canvas=Canvas(root,bg='#FFFFFF',width=300,height=300,scrollregion=
(0,0,500,500))
hbar=Scrollbar(root,orient=HORIZONTAL)
hbar.pack(side=BOTTOM,fill=X)
hbar.config(command=canvas.xview)
vbar=Scrollbar(root,orient=VERTICAL)
vbar.pack(side=RIGHT,fill=Y)
vbar.config(command=canvas.yview)
canvas.config(width=300,height=300)
canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
canvas.pack(side=LEFT,expand=True,fill=BOTH)
This is the output that I am getting, wherein the first two labels are getting overlapped.
If you want to scroll the widgets that you put inside the canvas, you need to use canvas.create_window(x, y, window=label) instead of label.place(...).
I suggest you to create a frame, grid all you labels inside it so that you won't have overlapping issues and put the frame inside the canvas using create_window to be able to scroll it:
import tkinter as tk
from tkinter import ttk
def on_resize(event):
"""Resize canvas scrollregion when the canvas is resized."""
canvas.configure(scrollregion=canvas.bbox('all'))
root = tk.Tk()
root.geometry('100x100')
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
canvas = tk.Canvas(root)
frame = ttk.Frame(canvas)
# create and grid the labels
for i in range(3):
for j in range(3):
ttk.Label(frame, text="Label %i-%i" % (i, j)).grid(row=i, column=j, padx=10, pady=10)
# put the frame in the canvas
canvas.create_window(0, 0, anchor='nw', window=frame)
# add the scrollbars
vbar = ttk.Scrollbar(root, orient='vertical', command=canvas.yview)
hbar = ttk.Scrollbar(root, orient='horizontal', command=canvas.xview)
canvas.configure(xscrollcommand=hbar.set,
yscrollcommand=vbar.set,
scrollregion=canvas.bbox('all'))
canvas.grid(row=0, column=0, sticky='eswn')
vbar.grid(row=0, column=1, sticky='ns')
hbar.grid(row=1, column=0, sticky='ew')
canvas.bind('<Configure>', on_resize)
root.mainloop()

Tkinter button distance from window edges

Is there a way using Tkinter to have buttons so that they are always placed a certain number of pixels from the edge of the window, even when the window is resized? I've tried using anchors but that didn't seem to move the placement in the window that much.
You can anchor buttons or any other widget to the sides of a window by starting with a Frame, and configuring its rows and columns to have a weight of 1 in order for it to fill the parent window.
import Tkinter as tk
import ttk
root = tk.Tk()
frame = ttk.Frame(root)
frame.pack(fill=tk.BOTH, expand=True)
frame.columnconfigure(index=0, weight=1)
frame.columnconfigure(index=2, weight=1)
frame.rowconfigure(index=0, weight=1)
frame.rowconfigure(index=2, weight=1)
Then, for each button you want to use sticky to anchor it to the respective side, and use padx or pady to add some padding (in pixels) between the button and the window.
top_padding = 5
top = ttk.Button(frame, text="Top")
top.grid(row=0, column=1, sticky=tk.N, pady=(top_padding, 0))
left_padding = 5
left = ttk.Button(frame, text="Left")
left.grid(row=1, column=0, sticky=tk.W, padx=(left_padding, 0))
right_padding = 5
right = ttk.Button(frame, text="Right")
right.grid(row=1, column=2, sticky=tk.E, padx=(0, right_padding))
bottom_padding = 5
bottom = ttk.Button(frame, text="Bottom")
bottom.grid(row=2, column=1, sticky=tk.S, pady=(0, bottom_padding))
root.mainloop()
have you tried using the padx function?
it works like this:
button=Button(place,text="something something", padx=10)
it provides with extra horizontal padding between widgets, aditionally, you could use frames with padx and an anchor so the text is fixated to a position

Tkinter Button Alignment in Grid

I am attempting to fit two buttons on a grid within a frame, that takes up the entire row, no matter the size of the root frame. So essentially one button takes up half of the row, while the other takes the other half. Here's my code:
self.button_frame = tk.Frame(self)
self.button_frame.pack(fill=tk.X, side=tk.BOTTOM)
self.reset_button = tk.Button(self.button_frame, text='Reset')
self.run_button = tk.Button(self.button_frame, text='Run')
self.reset_button.grid(row=0, column=0)
self.run_button.grid(row=0, column=1)
Not really sure where to go from here. Any suggestions would be greatly appreciated. Thanks!
Use columnconfigure to set the weight of your columns. Then, when the window stretches, so will the columns. Give your buttons W and E sticky values, so that when the cells stretch, so do the buttons.
import Tkinter as tk
root = tk.Tk()
button_frame = tk.Frame(root)
button_frame.pack(fill=tk.X, side=tk.BOTTOM)
reset_button = tk.Button(button_frame, text='Reset')
run_button = tk.Button(button_frame, text='Run')
button_frame.columnconfigure(0, weight=1)
button_frame.columnconfigure(1, weight=1)
reset_button.grid(row=0, column=0, sticky=tk.W+tk.E)
run_button.grid(row=0, column=1, sticky=tk.W+tk.E)
root.mainloop()
Result:

Categories