Posts: 15
Threads: 5
Joined: May 2023
May-20-2023, 05:22 PM
(This post was last modified: May-22-2023, 01:09 PM by Edward_.)
This May be really simple, but I'm not finding enough details to help as I'm just learning TKinter.
I have a simple grid sourced by a Python list, and I'm trying to center it regardless of window size, and add single push button at bottom of the window.
What I have tested caused the full window to grey out.
my code:
import tkinter as tk
from tkinter import *
import tkinter.font as font
window = tk.Tk()
window.title("Feed Bins")
window.geometry("800x400")
aList = ["Bin 1", "Bin 2", "Bin 3", "Bin 4", "Bin 5","Bin 6", "Bin 7", "Bin 8", "Bin 9", "Bin 10", "Bin 11",
"Bin 12", "Bin 13", "Bin 14", "Bin 15"]
onList = ["Bin 3", "Bin 12", "Bin 14"] # For testing, usually empty
myFont2 = font.Font(size=10)
label= [0] * 15
for x in range(5): # Number of Rows
for y in range(3): #Number of Colums
frame = tk.Frame(master=window,relief=tk.RAISED,borderwidth=4)
frame.grid(row=x, column=y, padx=5, pady=5) # line 13
label[x+(y*5)] = tk.Label(master=frame, text=aList[x+(y*5)],font=myFont2, height=3, width=20)
label[x+(y*5)].pack(fill="x")
def check_alerts():
global aList,onList
for x in range(0,len(aList)):
if aList[x] in onList:
label[x].config(bg = "red")
else:
label[x].config(bg = "lightgrey")
window.after(1000,check_alerts)
check_alerts()
window.mainloop()
Any assistance greatly appreciated.
Posts: 6,552
Threads: 19
Joined: Feb 2020
To center justify a label you set "justify = tk.CENTER". The label will have to be the same width as the buttons, so you should set sticky=
"NEWS".
Posts: 1,058
Threads: 111
Joined: Sep 2019
Your code altered a little. Expands with the window
import tkinter as tk
aList = ["Bin 1", "Bin 2", "Bin 3", "Bin 4", "Bin 5","Bin 6", "Bin 7", "Bin 8", "Bin 9", "Bin 10", "Bin 11",
"Bin 12", "Bin 13", "Bin 14", "Bin 15"]
root = tk.Tk()
root['padx'] = 5
root['pady'] = 5
root.title('Feed Bins')
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame = tk.Frame(root)
frame['highlightbackground'] = 'black'
frame['highlightcolor'] = 'black'
frame['highlightthickness'] = 1
frame.grid(column=0, row=0, sticky='news', padx=5, pady=5)
for x in range(5):
frame.grid_rowconfigure(x, weight=3, uniform='rows')
for y in range(3):
frame.grid_columnconfigure(y, weight=3, uniform='cols')
label = tk.Label(frame, text=aList[x+(y*5)], relief='raised', font=(None, 16, 'bold'))
label['bg'] = 'red' if aList[x+(y*5)] in ['Bin 3', 'Bin 12', 'Bin 14'] else 'gray86'
label.grid(column=y, row=x, sticky='news', padx=5, pady=5)
btn = tk.Button(root, text='Button', font=(None, 14, 'bold'))
btn.grid(column=0, row=1)
root.mainloop()
Posts: 6,552
Threads: 19
Joined: Feb 2020
Sorry, I answered the wrong question again.
To place a frame in the middle of the window, use "frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER)".
import tkinter as tk
import random
class FramedLabel(tk.Frame):
"""A label with a frame. Looks suspiciously like a button."""
def __init__(
self,
parent,
text="",
relief=tk.RAISED,
borderwidth=4,
font=(None, 10),
width=10,
height=3,
**kwargs):
super().__init__(
parent, relief=relief, borderwidth=borderwidth, **kwargs
)
self.label = tk.Label(
self, text=text, font=font, width=width, height=height
)
self.label.pack()
def set_color(self, color):
self["bg"] = color
self.label["bg"] = color
class MyWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Feed Bins")
self.geometry("800x400")
frame = tk.Frame(self)
frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
self.labels = [
FramedLabel(frame, text=f"Bin {x}") for x in range(1, 16)
]
for x, label in enumerate(self.labels):
label.grid(row=x // 5, column=x % 5, padx=5, pady=5)
label.set_color("lightgrey")
button = tk.Button(frame, text="Update", command=self.check_alerts)
button.grid(row=4, column=0, columnspan=5, sticky="news")
def check_alerts(self):
for label in self.labels:
label.set_color(random.choice(("red", "lightgrey")))
MyWindow().mainloop() However, I prefer to pack the frame and set the borders to expand the window to the desired size.
import tkinter as tk
import random
class FramedLabel(tk.Frame):
"""A label with a frame. Looks suspiciously like a button."""
def __init__(
self,
parent,
text="",
relief=tk.RAISED,
borderwidth=4,
font=(None, 10),
width=10,
height=3,
**kwargs):
super().__init__(
parent, relief=relief, borderwidth=borderwidth, **kwargs
)
self.label = tk.Label(
self, text=text, font=font, width=width, height=height
)
self.label.pack()
def set_color(self, color):
self["bg"] = color
self.label["bg"] = color
class MyWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Feed Bins")
frame = tk.Frame(self)
frame.pack(padx=100, pady=100)
self.labels = [
FramedLabel(frame, text=f"Bin {x}") for x in range(1, 16)
]
for x, label in enumerate(self.labels):
label.grid(row=x // 5, column=x % 5, padx=5, pady=5)
label.set_color("lightgrey")
button = tk.Button(frame, text="Update", command=self.check_alerts)
button.grid(row=4, column=0, columnspan=5, sticky="news")
def check_alerts(self):
for label in self.labels:
label.set_color(random.choice(("red", "lightgrey")))
MyWindow().mainloop() Either way, the real trick is to create a frame that holds your labels and buttons. Pack (or grid) the labels and buttons in the frame. Place or pack the frame in the window
Posts: 15
Threads: 5
Joined: May 2023
May-22-2023, 03:57 PM
(This post was last modified: May-22-2023, 03:57 PM by Edward_.)
Thanks to all, you all are a part of my TKinter crash-course.
Menator01,
In 'Your code altered a little' code, the onList[] in this project will be getting additions and removals every few minutes possibly, so I made this minor change:
label['bg'] = 'red' if aList[x+(y*5)] in onList else 'gray86' and re-added the checkAlerts() function.
Looks like my checkAlerts() function is necessary to update the label colors when the onList[] values come and go.
Any thoughts why the checkAlerts() function below might cause this error within TKInter?
File "/usr/lib/python3.9/tkinter/__init__.py", line 1652, in cget
return self.tk.call(self._w, 'cget', '-' + key)
TypeError: can only concatenate str (not "int") to str
Doesn't throw this error in my original code.
Posts: 6,552
Threads: 19
Joined: Feb 2020
May-22-2023, 04:11 PM
(This post was last modified: May-22-2023, 04:11 PM by deanhystad.)
Post the full error trace and the code that raises the error.
There is no reason to use global in this function
def check_alerts():
global aList,onList # Only affects assignment. Function does not assign values to aList or onList
for x in range(0,len(aList)):
if aList[x] in onList:
label[x].config(bg = "red")
else:
label[x].config(bg = "lightgrey")
window.after(1000,check_alerts)
Posts: 15
Threads: 5
Joined: May 2023
(May-21-2023, 01:56 AM)deanhystad Wrote: To center justify a label you set "justify = tk.CENTER". The label will have to be the same width as the buttons, so you should set sticky=
"NEWS". using:
frame = tk.Frame(master=window,relief=tk.RAISED,justify=tk.CENTER,borderwidth=4) Gives:
self.tk.call(
_tkinter.TclError: unknown option "-justify"
Posts: 6,552
Threads: 19
Joined: Feb 2020
From my quote (that you included in your last post).
Quote:To center justify a label you set "justify = tk.CENTER".
"justify" works with labels, but later I realized you weren't asking how to center a label and I wrote this post:
https://python-forum.io/thread-40023-pos...#pid169617
Which shows how you can center a frame using the place() command.
Posts: 1,058
Threads: 111
Joined: Sep 2019
I have one more example. Doesn't use the button to update. uses after. Sometimes pulls the same bin multiple times in the comparison list though.
So instead of three red labels, will get only one or two.
import tkinter as tk
from random import choices
aList = ["Bin 1", "Bin 2", "Bin 3", "Bin 4", "Bin 5","Bin 6", "Bin 7", "Bin 8", "Bin 9", "Bin 10", "Bin 11",
"Bin 12", "Bin 13", "Bin 14", "Bin 15"]
def checker(root, labels):
bins = choices(aList, k=3)
print(bins)
for label in labels:
label['bg'] = 'red' if label['text'] in bins else 'gray86'
root.after(1000, lambda: checker(root, labels))
root = tk.Tk()
root['padx'] = 5
root['pady'] = 5
root.title('Feed Bins')
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame = tk.Frame(root)
frame['highlightbackground'] = 'black'
frame['highlightcolor'] = 'black'
frame['highlightthickness'] = 1
frame.grid(column=0, row=0, sticky='news', padx=5, pady=5)
_labels = []
i = 0
for x in range(5):
frame.grid_rowconfigure(x, weight=3, uniform='rows')
for y in range(3):
frame.grid_columnconfigure(y, weight=3, uniform='cols')
_labels.append(tk.Label(frame, text=aList[x+(y*5)], relief='raised', font=(None, 16, 'bold')))
_labels[i]['bg'] = 'gray86'
_labels[i].grid(column=y, row=x, sticky='news', padx=5, pady=5)
i+=1
btn = tk.Button(root, text='Button', font=(None, 14, 'bold'))
btn.grid(column=0, row=1, pady=8)
root.after(1000, lambda: checker(root, _labels))
root.mainloop()
Posts: 15
Threads: 5
Joined: May 2023
So I now have a strange error in the centering grid when I use the needed check_alerts() function from my original code.
Or should I make a separate post?
The error is:
File "/usr/lib/python3.9/tkinter/__init__.py", line 1652, in cget
return self.tk.call(self._w, 'cget', '-' + key)
TypeError: can only concatenate str (not "int") to str The full code I'm testing is below. Not sure why the check_alerts() function causes this after centering changes.
import tkinter as tk
aList = ["Bin 1", "Bin 2", "Bin 3", "Bin 4", "Bin 5","Bin 6", "Bin 7", "Bin 8", "Bin 9", "Bin 10", "Bin 11",
"Bin 12", "Bin 13", "Bin 14", "Bin 15"]
onList = ["Bin 3", "Bin 14"] #For test, usually empty
root = tk.Tk()
root['padx'] = 5
root['pady'] = 5
root.title('Feed Bins')
root.geometry("1200x900")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame = tk.Frame(root)
frame['highlightbackground'] = 'black'
frame['highlightcolor'] = 'black'
frame['highlightthickness'] = 1
frame.grid(column=0, row=0, sticky='nsew', padx=5, pady=5)
for x in range(5):
frame.grid_rowconfigure(x, weight=3, uniform='rows')
for y in range(3):
frame.grid_columnconfigure(y, weight=3, uniform='cols')
label = tk.Label(frame, text=aList[x+(y*5)], relief='raised', font=(None, 16, 'bold'))
label['bg'] = 'red' if aList[x+(y*5)] in onList else 'gray86'
label.grid(column=y, row=x, sticky='nsew', padx=5, pady=5)
#check_alerts()
btn = tk.Button(root, text='Add item to onList', font=(None, 14, 'bold'), command = lambda: [onList.append("Bin 9"), print("Button Press")])
btn.grid(column=0, row=1)
def check_alerts():
global aList,onList
for x in range(0,len(aList)):
if aList[x] in onList:
label[x].config(bg = "red")
else:
label[x].config(bg = "lightgrey")
root.after(1000,check_alerts)
check_alerts()
root.mainloop()
|