How to control label positions and spacing in Tkinter grid - python

Now I want to make the food price at the same row therefore I change that to row 2 as well but then the output is this
the food goes back to row 0, why does it do that? I am trying to get something like this:
foodprice=['3','2','1.50']
drinks = ['Water','Hot water','Medium water']
drinksprice = ['1','2','3']
myFrame3 = Frame(root, bg = '')
myFrame3.pack()
myFrame3.columnconfigure(0, weight=1)
myFrame3.columnconfigure(1, weight=2)
for x in range (len(food)):
foodop = Label(myFrame3, font=("Impact", "15"), text = food[x])
foodop.grid(row = 2+x, column = 4) # <--
for x in range (len(foodprice)):
fprice = Label(myFrame3, font=("Impact", "15"), text = foodprice[x])
fprice.grid(row = 2+x, column = 8) # <--
for x in range (len(drinks)):
drinksop = Label(myFrame3, font=("Impact", "15"), text = drinks[x])
drinksop.grid(row = 4+(len(food))+x, column = 4) # <--
for x in range (len(drinksprice)):
drinksp = Label(myFrame3, font=("Impact", "15"), text = drinksprice[x])
drinksp.grid(row = 4+(len(food))+x, column = 8) # <--

Creating white space around text does not work like in spreadsheet programs.
Tkinter collapses grid rows which are completely empty if there are no weights set by rowconfigure() to prevent this. The same is true for columns.
A layout like this:
import tkinter as tk
root = tk.Tk()
def place(col, row, text='Label', color='white'):
label = tk.Label(root, text=text, bg=color)
label.grid(column=col, row=row,
ipadx=10, ipady=10, padx=1, pady=1, sticky="NSEW")
def make_layout(cols, rows):
for c in cols:
place(c, 0, c, 'lightgrey')
for r in rows:
place(0, r, r, 'lightgrey')
for r in rows:
for c in cols:
place(c, r, 'Label')
make_layout((1,2),(1,2))
root.mainloop()
... would look exactly like this:
without configuring weights in columns 8 to 29 and rows 5 to 49.
(To get this, replace make_layout((1,2),(1,2)) by make_layout((7,30),(4,50)) in code!)
And even if you added weights, Labels are still higher than weighted empty rows, and thus the layout would maybe not look as clean as you might imagine.
Tkinter's Grid Geometry Manager is a good tutorial I read the other day that goes into this detail. It also introduces the optional columnspan and rowspan parameters of the grid() function.
Last but not least, you should have a look into How to justify text in label in Tkinter

Related

Tkinter: retrieve Radiobutton column and row value

Here How to retrieve the row and column information of a button and use this to alter its settings in python I've found a nice code that I would like to alter and use.
Here's the original:
from tkinter import *
root = Tk()
def showGrid():
row = btn.grid_info()['row'] # Row of the button
column = btn.grid_info()['column'] # grid_info will return dictionary with all grid elements (row, column, ipadx, ipday, sticky, rowspan and columnspan)
print("Grid position of 'btn': {} {}".format(row, column))
btn = Button(root, text = 'Click me!', command = showGrid)
btn.grid(row = 0, column = 0)
root.mainloop()
If the buttons are 2, how the def would recognize which button I've clicked?
I've tried to change the button into a Radiobutton (which is fine for me), set a value, set a variable, ecc, tried to used .get() and so on but I am unable to clearly identify the single radiobutton.
Any suggestion?
Best
Using lambda might help.
from tkinter import *
root = Tk()
def showGrid(widget):
row = widget.grid_info()['row'] # Row of the button
column = widget.grid_info()['column'] # grid_info will return dictionary with all grid elements (row, column, ipadx, ipday, sticky, rowspan and columnspan)
print("Grid position of 'btn': {} {}".format(row, column))
btn = Button(root, text = 'Click me!', command = lambda: showGrid(btn))
btn.grid(row = 0, column = 0)
btn1 = Button(root, text = 'Click me!', command = lambda: showGrid(btn1))
btn1.grid(row = 1, column = 0)
root.mainloop()

how to format 2 check buttons in same row and column python

I have created two check buttons and I want them both in row 5 and column 1. But I want the 'YES' check button to be formatted on the left side and the 'NO' check button to be on the right.
I have used sticky but it will not work. I also tried changing the width of the check buttons but this did not work either.
Do you have any suggestions?
Thank you!
self.yes_checkbtn = Checkbutton(self.entry_frame, width = 20, variable = checkbutton1,
anchor = W, text = "YES")
self.no_checkbtn = Checkbutton(self.entry_frame, width = 20, variable = checkbutton2,
anchor = E, text = "NO")
self.yes_checkbtn.grid(row = 5, column = 1, sticky = W)
self.no_checkbtn.grid(row = 5, column = 1, sticky = E)
Outcome:
I dont have enough points yet to show the image without a link - sorry
Put both check buttons under another new frame, then you can grid this new frame to column 1 of row 5, also change option width of Checkbutton to a small number, like 5.
Example code
from tkinter import *
root = Tk()
entry_frame = Frame(root)
entry_frame.grid(row=0, column=0)
for i in range(4):
for j in range(2):
label = Label(entry_frame, text=f'Row {i} Column {j}')
label.grid(row=i, column=j)
frame = Frame(entry_frame)
frame.grid(row=4, column=1)
checkbutton1 = BooleanVar()
checkbutton2 = BooleanVar()
yes_checkbtn = Checkbutton(frame, width = 5, variable = checkbutton1,
anchor = W, text = "YES", bg='green')
no_checkbtn = Checkbutton(frame, width = 5, variable = checkbutton2,
anchor = E, text = "NO", bg='blue')
yes_checkbtn.grid(row = 0, column = 1, sticky = W)
no_checkbtn.grid(row = 0, column = 2, sticky = W)
root.mainloop()

Tkinter issue with using wrap length on a Label widget organised within a grid

So I have 2 rows dedicated to a messaged label widget to display any successful/unsuccessful messages to the user while they're using the tkinter GUI.
Some of the messages are to long to fit within the column width, so I have used the wraplength feature for the Label widget to wrap the text to the next line.
The issue that I'm having, is that because of this feature, it shifts the placements of the widgets underneath this widget, by 1 row for every new row it wraps the text onto.
So I was wondering if there's any way to have the text wrap, without moving the lower widgets.
How the message Label looks within the GUI with height = 1:
How the message Label looks when it wrap's the text to a newline with height = 1:
How the message Label looks within the GUI with height = 2:
How the message Label looks when it wrap's the text to a newline with height = 1:
I would like for the message Label in the 2nd image link to display the way it does, but keeping the vertical layout of the widgets as seen in the 1st image link.
The following code is for defining the widgets:
Choice_list = Listbox(window, selectmode=SINGLE, width = 17, height = 9,
justify = CENTER)
image = PhotoImage(file = 'Dummy_Logo.gif')
Image_label = Label(window, image = image)
extract = Button(window, text = "Archive News from Webpage",
command = func1, width = 20)
archive = Button(window, text = "Extract News from Archive",
command = func2, width = 22)
display = Button(window, text = "Display the News",
command = func3, width = 14)
Message_Widget = Label(window, text = '', fg = 'black', justify = CENTER,
height = 2, wraplength = 300)
Log_Event = Checkbutton(window, text = 'Log Event', variable = logState,
command = logEvent)
The following code is the grid alignment for the widgets:
Image_label.grid(row = 1, column = 1)
Choice_list.grid(row = 1, column = 2, rowspan = 9, sticky = W)
Message_Widget.grid(row = 2, column = 1, rowspan = 2)
Log_Event.grid(row = 12, column = 2)
archive.grid(row = 13, column = 1, rowspan = 2, sticky = W)
extract.grid(row = 13, column = 1, rowspan = 2, sticky = E)
display.grid(row = 13, column = 2, rowspan = 2, sticky = W)
Give the label a height of 2 or 3. The problem is simply that it wants to be one character tall by default. Tkinter will allocate only one character worth of height when laying out all the widgets. When the text wraps, the label simply must become taller so that the text will fit.
By giving it a height of 2 or 3 to start out, that extra space is built-in to the GUI. When the text wraps, the label doesn't have to grow to accommodate the new text.
This might not be a proper method of solving this problem, however I have managed to get the outcome I was looking for by only adjusting the rowspan parameter in the Message_Widget to rowspan = 11.
Message_Widget.grid(row = 2, column = 1, rowspan = 11)
This produced the following results:
With a short text:
https://i.stack.imgur.com/XpGLq.png
With a long text:
https://i.stack.imgur.com/iXwlR.png
Have you considered using a text widget for the readout?
For example this below code will wrap the text if it is to long and it will not resize the widgets. This is just an example but should be enough to help you with you problem.
from tkinter import *
import random
root = Tk()
text1 = Text(root, height=1, width=40, wrap="word")
text1.grid(row=0, column=0, sticky="ew")
def update_lbl1():
x = ["Click and drag to see some much longer random text for updating label!!!","some short data"]
text1.delete("1.0", "end")
text1.insert("1.0", random.choice(x))
Button(root, text="Change label from list", command = update_lbl1).grid(row=1, column=0)
root.mainloop()

How to place widgets next to each other?

I am building a simple GUI program to manage priorities. I am having trouble placing button widgets next to each other. It is somehow logical to me that if I want three buttons (Add, Remove, Edit) to place next to each other, I should use column = 0 for Add, column = 1 for Remove and column = 2 for Edit. Anyway, this is what I get:
Here is the createWidgets function:
def createWidgets(self):
listBox = Listbox(width = 30).grid(row=1, column = 0)
Button(self.root,
text = "Add",
command = self.addItem).grid(row = 2, column = 1, sticky = W)
Button(self.root,
text="Remove",
command = self.removeItem).grid(row = 2, column = 2, sticky = W)
Button(self.root,
text="Edit",
command = self.editItem).grid(row = 2, column = 3, sticky = W)
textBox = Text(height = 10, width = 30).grid(row = 3)
Use the columnspan option for:
textBox = Text(height = 10, width = 30).grid(row = 3, column=0, columnspan=3) # specify the column also
and
listBox = Listbox(width = 30).grid(row=1, column = 0, columnspan=3)
My advice is to break your UI down into regions, and manage each region independently. It's much easier than trying to cram everything into one big grid, especially when you have widgets with very different sizes.
Create three frames: one for the top, one for the group of buttons, and one for the bottom. You can then use pack to place them top-to-bottom:
top_frame.pack(side="top", fill="both", expand=True)
button_frame.pack(side="top", fill="x")
bottom_frame.pack(side="bottom", fill="both", expand=True)
(the use of expand depends on if you want that frame to expand when the window is made larger)
Once you've done that, you can tackle each region separately. For example, you can make the buttons children of the button frame, and use .pack(side='left') to get them to be left-aligned. The other widgets can also use pack if they are the only widgets in each of the other frames, or you can use grid.
You will find that just a few minutes organizing your UI before starting to code will make a big difference in how easy it is to create and maintain.
Example:
def createWidgets(self):
topFrame = tk.Frame(self)
buttonFrame = tk.Frame(self)
bottomFrame = tk.Frame(self)
topFrame.pack(side="top", fill="both", expand=True)
buttonFrame.pack(side="top", fill="x")
bottomFrame.pack(side="bottom", fill="both", expand=True)
listBox = tk.Listbox(topFrame, width=30)
listBox.pack(side="top", fill="both", expand=True)
tk.Button(buttonFrame, text="Add").pack(side="left")
tk.Button(buttonFrame, text="Remove").pack(side="left")
tk.Button(buttonFrame, text="Edit").pack(side="left")
textBox = tk.Text(bottomFrame, height=10, width=30)
textBox.pack(fill="both", expand=True)
When you do
textBox = Text(height = 10, width = 30).grid(row = 3)
tkinter automatically sets column = 0 and because textBox has a width of 30 the first column is stretched to a width of 30. It is possible to place textBox so that it occupies all columns by using the columnspan parameter:
textBox = Text(height = 10, width = 30).grid(row = 3, column = 0, columnspan = 3)
Because listBox also has a width of 30, you should also use columnspan here:
listBox = Listbox(width = 30).grid(row=1, column = 0, columnspan = 3)
A comprehensive guide for the grid method is here.

Positioning widgets on frames with Tkinter

So, I'm fairly new to Python, and Tkinter.
I'm in the process of creating a Battleship game. So far I've succesfully created a grid of buttons. I am now trying to create a sort of a menu to the right of this game pad. I'm trying to do this by having two different Frames in my window - one where my game pad is and one where I have the menu (which is below a Label).
Whatever I put inside cheat_button.grid() I can't seem to change the position of this button. It just stays in the top-left corner of the menu_frame Frame. I also want to add anothern button beneath this one, as well as a message box a bit further down, on the same frame. Any suggestions? I have linked the part of the code I find relevant.
Thanks.
col_tot = 10
row_tot = 10
self.grid_frame = tk.Frame(self)
self.grid_frame.grid(row=0, column=0, rowspan=row_tot, columnspan=col_tot, sticky = "WENS")
self.menu_frame = tk.Frame(self, bg="grey", bd=1, relief="ridge")
self.menu_frame.grid(row=1, column=col_tot, rowspan=row_tot-1, columnspan=4, sticky = "WENS")
button_no = 0
self.button_list = []
for x in range(row_tot):
self.button_list.append([""] * col_tot)
for i in range(row_tot):
for j in range(col_tot):
self.button_list[i][j] = (tk.Button(self.grid_frame, height = 3, width = 6, text = str(button_no + 1),
activebackground = "yellow", relief = "groove"))
self.button_list[i][j]["command"] = lambda i=i, j=j:self.update_colour(i, j)
self.button_list[i][j].grid(row = i, column = j, sticky = "NW")
button_no += 1
self.welcome = tk.Label(self, text = "Welcome to Battleship!",
fg = "red", bg = "grey", font = ("Helvetica", 12), relief="ridge")
self.welcome.grid(row = 0, column = col_tot, columnspan = 4, sticky = "WENS")
self.cheat_button = tk.Button(self.menu_frame, text = "Cheat")
self.cheat_button.grid()
self.cheat_button.config(bg = "grey")
Rows and columns have a size of zero if they are empty and haven't configured to have a minimum size. It's also important to know that each frame has it's own "grid", so even though the frame on the left has 10 rows and ten columns, the frame on the right starts out completely empty.
If you want the button centered in the frame, one solution is to leave an empty row above and below it, and configure those rows to be given any extra space. For example:
self.welcome.grid(row = 0, column = 0, sticky = "WENS")
# notice we've put nothing in rows 1 or 3
self.cheat_button.grid(row=2, column=0)
self.menu_frame.grid_rowconfigure(1, weight=1)
self.menu_frame.grid_rowconfigure(3, weight=1)

Categories