Grid resizes the line based on the largest widget - python

I'm having trouble resizing the ComboBox. Instead of being on the "Filter Order Status" side, she walked away. I realized that this happened because the size of the grid line was increased due to the size of the largest widget, which in this case are the tables.
How can I place the ComboBox next to "Filter Order Status" as it is in frame2, without changing the table size?
Here the sample code that shows the ComboBox being resized according to the size of the table
from tkinter import *
from tkinter import ttk
def make_table(row, column, window, text=0, headings=[], image=None, letter_pos=LEFT, borderwidth=1):
""" Returns a Frame with the created table """
data = [[0] * column for i in range(row)]
labels = [[0] * column for i in range(row)]
table = Frame(window)
while len(headings) < column:
headings.append(1)
for i in range(row):
# Insert "headings" values ​​in the first line
if i == 0:
labels[i] = [Label(table, text=headings[j], image=image, compound=letter_pos, borderwidth=borderwidth,
relief='groove', anchor=W, width=0) for j in range(column)]
else:
labels[i] = [Label(table, text=0, image=None, compound=LEFT, borderwidth=1,
relief='groove', anchor=W, width=0) for j in range(column)]
for j in range(column):
labels[i][j].grid(row=i, column=j, sticky='we')
return table
window = Tk()
window.grid_rowconfigure(1, weight=1)
window.grid_columnconfigure(0, weight=1)
# Create the Frames
frame1 = Frame(window, bg='yellow')
frame2 = Frame(window, bg='red')
# Position the Frames
frame1.grid(row=0, column=0, sticky='nesw')
frame2.grid(row=0, column=1, sticky='nesw')
""" Frame1 content """
# Create the "frame1" widgets
label1 = Label(frame1, text='frame1')
filter_order_status1 = Label(frame1, text='Filter Order Status')
comboBox_1 = ttk.Combobox(frame1, values=['Entregado', 'Não entregado'])
table1 = make_table(8, 4, frame1, headings=['', 'Customer', 'Order Status', 'Order Date'])
# Place the "frame1" widgets
label1.grid(row=0, column=0, sticky='w')
filter_order_status1.grid(row=1, column=0, sticky='w')
comboBox_1.grid(row=1, column=1, sticky='w')
table1.grid(row=2, column=0, sticky='w')
""" Frame2 content """
# Create the "frame2" widgets
label2 = Label(frame2, text='frame2')
filter_order_status2 = Label(frame2, text='Filter Order Status')
comboBox_2 = ttk.Combobox(frame2, values=['Entregado', 'Não entregado'])
table2 = make_table(8, 4, frame2, headings=[' ', 'Customer'])
# Place the "frame2" widgets
label2.grid(row=0, column=0, sticky='w')
filter_order_status2.grid(row=1, column=0, sticky='w')
comboBox_2.grid(row=1, column=1, sticky='w')
table2.grid(row=2, column=0, sticky='w')
window.mainloop()

The way I would do it is to create a frame specifically to hold just the label and the combobox. You can then easily use pack to align the label to the left and the combobox right next to it.

Related

How to shift rows up after deleting a row?

I have the code where you can tick a checkbox and delete any row you want but I can't figure out how to get the rows beneath the row deleted to shift up and fill the space from the deleted row. Here is the code I'm running in jupyter. Please help!!
import tkinter as tk
from tkinter import messagebox
class GUI:
def __init__(self, root):
self.root = root
self.root.title("Editable Headings")
self.headings = ['Pad #', 'Location Name', 'Projected Level',
'# 130 bbl Loads', '24hr Rate', 'Rate Per Tank',
'# of Tanks', 'Notes', 'Water weight']
self.entries = []
self.vars = []
self.init_gui()
def init_gui(self):
for i, heading in enumerate(self.headings):
label = tk.Label(self.root, text=heading, font=("TkDefaultFont", 14))
label.grid(row=0, column=i, padx=10, pady=10, sticky="w")
entry = tk.Entry(self.root)
entry.grid(row=1, column=i, padx=10, pady=10, sticky="we")
self.entries.append(entry)
var = tk.IntVar()
checkbox = tk.Checkbutton(self.root, variable=var)
checkbox.grid(row=1, column=len(self.headings), padx=10, pady=10, sticky="w")
self.vars.append(var)
add_button = tk.Button(self.root, text="+", command=self.add_row)
add_button.grid(row=1, column=len(self.headings)+1, padx=25, pady=10)
delete_button = tk.Button(self.root, text="Delete", command=self.delete_row)
delete_button.grid(row=2, column=len(self.headings)+1, padx=25, pady=10)
def add_row(self):
row = len(self.entries) // len(self.headings) + 1
for i, heading in enumerate(self.headings):
entry = tk.Entry(self.root)
entry.grid(row=row, column=i, padx=10, pady=10, sticky="we")
self.entries.append(entry)
var = tk.IntVar()
checkbox = tk.Checkbutton(self.root, variable=var)
checkbox.grid(row=row, column=len(self.headings), padx=10, pady=10, sticky="w")
self.vars.append(var)
def delete_row(self):
if not self.entries:
return
if messagebox.askyesno("Delete", "Are you sure you want to delete the selected rows?"):
indices = [i for i, var in enumerate(self.vars) if var.get()]
for i in sorted(indices, reverse=True):
for j in range(len(self.headings)):
entry = self.entries.pop(i * len(self.headings))
entry.destroy()
var = self.vars.pop(i)
var.set(0)
if __name__ == "__main__":
root = tk.Tk()
app = GUI(root)
root.mainloop()
I would just like the rows beneath the row that is selected to be deleted to shift up after the selected is deleted.
For easier control, I would suggest to:
put those entry boxes and checkbuttons in a frame and those action buttons in another frame
use a single dictionary to store those widgets and tk.IntVar using row number as the key instead of separate lists
Then it is easy to delete the widgets in a row by just looking up them in the dictionary. Note that if all widgets in a row are deleted, the height of that row will be zero. So the widgets beneath will be shift up.
import tkinter as tk
from tkinter import messagebox
class GUI:
def __init__(self, root):
self.root = root
self.root.title("Editable Headings")
self.headings = ['Pad #', 'Location Name', 'Projected Level',
'# 130 bbl Loads', '24hr Rate', 'Rate Per Tank',
'# of Tanks', 'Notes', 'Water weight']
self.entries = {}
self.init_gui()
def init_gui(self):
# frame for the entries and checkbuttons
self.frame = tk.Frame(self.root)
self.frame.pack(side="left", fill="y")
# show the headings
for i, heading in enumerate(self.headings):
label = tk.Label(self.frame, text=heading, font=("TkDefaultFont", 14))
label.grid(row=0, column=i, padx=10, pady=10, sticky="w")
# add an initial row
self.add_row()
# frame for the buttons
button_frame = tk.Frame(self.root)
button_frame.pack(side="right", fill="y")
add_button = tk.Button(button_frame, text="+", command=self.add_row)
add_button.grid(row=1, column=len(self.headings)+1, padx=25, pady=10)
delete_button = tk.Button(button_frame, text="Delete", command=self.delete_row)
delete_button.grid(row=2, column=len(self.headings)+1, padx=25, pady=10)
def add_row(self):
# get the insertion row number
row = self.frame.grid_size()[1]
widgets = []
for i, heading in enumerate(self.headings):
entry = tk.Entry(self.frame)
entry.grid(row=row, column=i, padx=10, pady=10, sticky="we")
widgets.append(entry)
var = tk.IntVar()
checkbox = tk.Checkbutton(self.frame, variable=var)
checkbox.grid(row=row, column=len(self.headings), padx=10, pady=10, sticky="w")
widgets.append(checkbox)
# store the widgets in the new row and IntVar into the dictionary
self.entries[row] = (var, widgets)
def delete_row(self):
# any row selected
rows = [row for row, (var, _) in self.entries.items() if var.get()]
if rows and messagebox.askyesno("Delete", "Are you sure you want to delete the selected rows?"):
for row in rows:
_, widgets = self.entries.pop(row)
for w in widgets:
w.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = GUI(root)
root.mainloop()

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.

Why i can't fix the boxes alignment in Gridview on windows

there are some space between the result as you can see in the image that password entry and generate password aren't aligning together
*Result image
from tkinter import
window = Tk()
window.title("Password Manager")
window.config(padx=20, pady=20)
canvas = Canvas(width=200, height=200, highlightthickness=0)
password_image = PhotoImage(file="logo.png")
image = canvas.create_image(100, 100, image=password_image)
canvas.itemconfig(image)
canvas.grid(row=0, column=1)
website_label = Label(text="Website")
website_label.grid(row=1, column=0)
Email_username_label = Label(text="Email/Username")
Email_username_label.grid(row=2, column=0)
password_label = Label(text="Password")
password_label.grid(row=3, column=0)
website_input = Entry(width=35)
website_input.grid(row=1, column=1, columnspan=2)
Email_username_input = Entry(width=35)
Email_username_input.grid(row=2,column=1, columnspan=2)
password = Entry(width=21)
password.grid(row=3, column=1)
generate_button = Button(text="Generate Password")
generate_button.grid(row=3, column=2)
add_password_button = Button(text="Add", width=36)
add_password_button.grid(row=4, column=1, columnspan=2)
window.mainloop()
You can see that there is some space in password entry and generate password how to fix it
As noted in the comments, using the grid method allows you to just add sticky to align elements. In this case, I've removed all the width options in your entries and buttons with stick='EW' to expand East and West, or to the maximum left and right of the column the widget is in.
from tkinter import *
window = Tk()
window.title("Password Manager")
window.config(padx=20, pady=20)
# Canvas
canvas = Canvas(width=200, height=200, highlightthickness=0)
password_image = PhotoImage(file="logo.png")
image = canvas.create_image(100, 100, image=password_image)
canvas.itemconfig(image)
canvas.grid(row=0, column=1)
# Labels
website_label = Label(text="Website")
website_label.grid(row=1, column=0)
Email_username_label = Label(text="Email/Username")
Email_username_label.grid(row=2, column=0)
password_label = Label(text="Password")
password_label.grid(row=3, column=0)
# Entries
website_input = Entry()
website_input.grid(row=1, column=1, columnspan=2, sticky='EW') # sticky
Email_username_input = Entry()
Email_username_input.grid(row=2, column=1, columnspan=2, sticky='EW') # sticky
password = Entry()
password.grid(row=3, column=1, sticky='EW') # sticky
# Buttons
generate_button = Button(text="Generate Password")
generate_button.grid(row=3, column=2)
add_password_button = Button(text="Add")
add_password_button.grid(row=4, column=1, columnspan=2, sticky='EW') # sticky
window.mainloop()
You may also want to consider reading up on column and row weights using columnconfigure and rowconfigure if you want your GUI to resize dynamically.

How to change the frame to fill the X axis instead of being dependent on grid inside it

from tkinter import *
root = Tk()
root.geometry("400x400")
my_canvas = Canvas(root)
my_canvas["bg"] = '#808080'
my_canvas.place(relx=0, rely=0, relheight=1, relwidth=1)
This frame I want to change to fill the X axis instead of being dependent on grid inside it
frame = Frame(my_canvas, width=100, height=10)
frame.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
example = Label(frame, text="Hello World!", bg="#1d1d1d", fg="#ffffff")
example.grid(row=1, column=1, sticky="ew")
root.mainloop()

tkinter columconfigure and rowconfigure

I want to understand something with the grid layout in tkinter. Let's say that I want the column 1 in the row 1 to expand if there is extra space, but the column 1 in the row 2 to not expand how can I can do it?
widget.columnconfigure
give you control on all columns, it is not possible to specify the row.
You can't do precisely what you want. However, you can get the same visual effect by having the widget in row 1 span two columns where the widget in row 2 spans only one. You can then give weight to the second column, which will affect the widget on row 1 but not on row two.
Here's a simple example:
import tkinter as tk
root = tk.Tk()
l1 = tk.Frame(root, background="red", width=100, height=50)
l2 = tk.Frame(root, background="orange", width=100, height=50)
l3 = tk.Frame(root, background="green", width=100, height=50)
l4 = tk.Frame(root, background="blue", width=100, height=50)
root.columnconfigure(2, weight=1)
l1.grid(row=1, column=1, columnspan=2, sticky="ew")
l2.grid(row=1, column=3, sticky="ew")
l3.grid(row=2, column=1, sticky="ew")
l4.grid(row=2, column=3, sticky="ew")
root.mainloop()
When this code first starts up, it looks like this where the two columns are identical in size.
When the window is resized, you can see that the widget on row 1 expands but the widget in row 2 does not.
Use two canvases, one for each row. Then change the width and the columnspan of the widget you want to expand. Finally, user columnconfigure.
In the following example, I've created 2 Canvas,2 Frames, 2 buttons. One for each row.
import tkinter as tk
root = tk.Tk()
root.geometry("600x300")
root.resizable(False,False)
canvas_1 = tk.Canvas(master=root,width=600, height=15)
canvas_2 = tk.Canvas(master=root)
canvas_1.pack(expand=True, fill="both")
canvas_2.pack(expand=True, fill="both")
#
frame_1 = tk.Frame(master=root)
frame_1.pack(expand=True, fill="both")
# ------------------------------------------------------ All in row=0
button_in_column_0 = tk.Button(master=frame_1, text="I'm in Column_1").grid(row=0, column=1)
button_in_column_1 = tk.Button(master=frame_1, text="I'm in Column_2",width=80).grid(row=0, column=2,columnspan=2)
button_in_column_2 = tk.Button(master=frame_1, text="I'm in Column_3").grid(row=0, column=3)
# -----Let's say that you want the column 1 in the row 0 to expand if there is extra space
for x in range(2,3):
frame_1.columnconfigure(x, weight=1)
frame_1.rowconfigure(x, weight=1)
# LET CREATE THE SECOND LINE WITH LIST OF BUTTONS
frame_2 = tk.Frame(master=root)
frame_2.pack(expand=True, fill="both")
# ------------------------------------------------------------ All in row=0
button_1 = tk.Button(master=frame_2, text="I'm in Column_1").grid(row=0, column=1, sticky=tk.NSEW)
button_2 = tk.Button(master=frame_2, text="I'm in Column_2").grid(row=0, column=2, sticky=tk.NSEW)
button_3 = tk.Button(master=frame_2, text="I'm in Column_3").grid(row=0, column=3, sticky=tk.NSEW)
window_1 = canvas_1.create_window(0,1, anchor="nw", window=frame_1)
window_2 = canvas_2.create_window(0,1, anchor="nw", window=frame_2)
root.mainloop()
enter image description here
You may be looking for the columnspan option.
see http://effbot.org/tkinterbook/grid.htm

Categories