Expanding label widget to fill cell width using grid - python-3.x

trying to get the label to fill the cell entirely using tkinter in Python. I have tried this code, and essentially IMO I am not able to get 'EW' to work. Please help!
from tkinter import *
root = Tk()
root.configure(background='#696969')
root.title('Title')
root.geometry('310x510')
root.resizable(False, False)
f = Frame(root, width=300, height=500, bg='silver', bd=0, cursor='arrow', highlightbackground='silver', highlightcolor='silver', highlightthickness=0, relief=FLAT)
f.grid(row = 0, column = 0, padx = 5, pady = 5, ipadx = 0, ipady = 0, rowspan = 1, columnspan = 1, sticky = 'NSEW')
f.grid_propagate(0)
l = Label(f, text = 'Algorithms', justify = CENTER, bd = 0, anchor = CENTER, bg = '#313233', fg = 'white', wraplength = 0, font = 'Helvetica 9 bold')
l.grid(row = 0, column = 0, padx = 0, pady = 0, ipadx = 0, ipady = 0, rowspan = 1, columnspan = 1, sticky = 'EW')
l.grid_propagate(0)
root.mainloop()

Actually, the Label does fill the cell completely. The problem is that the column is only as wide as its contents, which is the width of the Label. You could make the Label, and therefore the column, wider by using width=... on the Label, but it would be difficult to make it exactly as wide as you want because the width of a Label is measured in character units.
What you really want is the column to expand to the width of the Frame. You can do this by giving the column a weight with columnconfigure. Unused width is distributed to columns with a non-zero weight, relative to the weigth they have (a column with weight=2 will get twice the space of a column with weight=1). Because you only have one column, you can basically use any non-zero number to distribute all unused space to this column. Since we're talking about the grid inside the Frame, you should call columnconfigure on the Frame widget:
f.columnconfigure(0, weight=1)

Related

Tkinter How to make listbox appear over a frame

making an F1 application where users can type in a driver from the current grid [supported via autofill/suggestions], and then API fetches relevant stats
The issue I'm having is that I want the listbox to appear over another frame (called left_frame). However, what this does is that the listbox goes under left_frame , and not over it. Any thoughts of how to make it appear over the frame?
some approaches that I tried:
used .lift(), but .lift() only seems to work when it's relative to objects in the same frame
since I currently use .grid and placed the listbox in the root frame, I initially tried to put it in top_frame, but it then expands the top_frame. Since I have positioned all frames using .place(), I don't think I can use propagate(False) (I tried it , didn't work)
Here's a picture of the issues
Here's my code so far:
#import necessary modules
from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
from tkinter import messagebox
import requests
import json
#set up main window components
root = Tk()
root.title("F1 Desktop Application")
root.geometry("500x600")
root.configure(bg="white")
#since same directory, can just use filename
root.iconbitmap("formula1_logo.ico")
#images
#creating canvas for image to go into
#canvas_f1_logo = Canvas(top_frame, width = 5, height = 5)
#canvas.grid(row = 0, column = 1)
#acquire image
#formula1_logo = ImageTk.PhotoImage(Image.open("formula1_logo.png"))
#resize image
#resized_formula1_logo = formula1_logo.resize((3,3), Image.ANTIALIAS)
#new_formula1_logo = ImageTK.PhotoImage(resized_formula1_logo)
#canvas.create_image(5,5, anchor = NW, image = new_formula1_logo)
#functions
#generate 2022 drivers-list [can scale to drivers-list by changing the]
drivers_list_request = requests.get("http://ergast.com/api/f1/2022/drivers.json")
#initialize empty-list
drivers_list = []
drivers_list_object = json.loads(drivers_list_request.content)
for elements in drivers_list_object["MRData"]["DriverTable"]["Drivers"]:
drivers_list.append(elements["givenName"] + " " + elements["familyName"])
#set up frames [main frames, can put frames within frames if needed]
#frame for search bar + magnifying glass
top_frame = LabelFrame(root, padx = 80, pady = 15)
top_frame.place(relx = 0.5, rely = 0.2, anchor = CENTER)
header_label = Label(top_frame, text = "F1 2022 Drivers App", pady = 20, font = ("Arial bold",14))
header_label.grid(row = 0, column = 0, pady = 2 )
search_button = Button(top_frame, text = "search", padx = 2, pady = 2)
search_button.grid(row = 1, column = 1)
# Update the Entry widget with the selected item in list
def check(e):
v= entry.get()
if v=='':
hide_button(menu)
else:
data=[]
for item in drivers_list:
if v.lower() in item.lower():
data.append(item)
update(data)
show_button(menu)
def update(data):
# Clear the Combobox
menu.delete(0, END)
# Add values to the combobox
for value in data:
menu.insert(END,value)
def fillout(event):
try:
entry.delete(0,END)
entry.insert(0,menu.get(menu.curselection()))
#handle a complete deletion of entry-box via cursor double tap
except:
pass
def hide_button(widget):
widget.grid_remove()
def show_button(widget):
widget.grid()
# Create an Entry widget
entry= Entry(top_frame)
entry.grid(row = 1, column = 0)
entry.bind('<KeyRelease>',check)
# Create a Listbox widget to display the list of items
menu= Listbox(root)
menu.grid(row = 2, column = 0, padx = 165, pady = 165)
menu.bind("<<ListboxSelect>>",fillout)
menu.lift()
# Add values to our combobox
hide_button(menu)
left_frame = LabelFrame(root, padx = 30, pady = 30)
left_frame.place(relx = 0.24, rely = 0.5, anchor = CENTER)
bottom_left_frame = LabelFrame(root, padx = 30, pady = 30)
bottom_left_frame.place(relx = 0.24, rely = 0.82, anchor = CENTER)
bottom_right_frame = LabelFrame(root, padx = 30, pady = 30)
bottom_right_frame.place(relx = 0.6, rely = 0.82)
basic_info = Label(left_frame, text = "Basic Info ", font = ("Arial bold",14))
basic_info.grid(row = 0, column = 0, pady = 3)
full_name = Label(left_frame, text = "Full Name : ")
full_name.grid(row = 1, column = 0, pady = 2)
driver_code = Label(left_frame, text = "Driver Code : ")
driver_code.grid(row = 2, column = 0, pady = 2)
nationality = Label(left_frame, text = "Nationality : ")
nationality.grid(row = 3, column = 0, pady = 2)
F1_career = Label(bottom_left_frame, text = "F1 Career ", font = ("Arial bold",14))
F1_career.grid(row = 0, column = 0, pady = 3)
wins = Label(bottom_left_frame, text = "Wins :")
wins.grid(row = 1, column = 0, pady = 2)
poles = Label(bottom_left_frame, text = "Poles :")
poles.grid(row = 2, column = 0, pady = 2)
drivers_championships = Label(bottom_left_frame, text = "Championships :")
drivers_championships.grid(row = 3, column = 0 , pady = 2)
F1_22_stats = Label(bottom_right_frame, text = "F1 22 Stats", font = ("Arial bold",14))
F1_22_stats.grid(row = 0, column = 0, pady = 3)
root.mainloop()
would appreciate the help!

Print from Tk Listbox on selection

I am able to select items from my list but once I click the item I am not able to print the contents back to the terminal. I am also trying to figure out how to close the prompt once the user makes a selection. Any help would be greatly appreciated.
window.title('UTC Time Selection')
# for scrolling vertically
yscrollbar = Scrollbar(window)
yscrollbar.pack(side = RIGHT, fill = Y)
label = Label(window,
text = "Select the UTC Time for Scanning to Start : ",
font = ("Times New Roman", 12),
padx = 10, pady = 10)
label.pack()
list = Listbox(window, selectmode = "single",
yscrollcommand = yscrollbar.set)
# Widget expands horizontally and
# vertically by assigning both to
# fill option
list.pack(padx = 10, pady = 10,
expand = YES, fill = "both")
x =["0000", "0100", "0200", "0300", "0400",
"0500", "0600", "0700", "0800", "0900",
"1000", "1100", "1200", "1300", "1400",
"1500", "1600", "1700", "1800", "1900",
"2000", "2100", "2200", "2300"]
for each_item in range(len(x)):
list.insert(END, x[each_item])
list.itemconfig(each_item, bg = "white")
# Attach listbox to vertical scrollbar
yscrollbar.config(command = list.yview)
Button(window, text="Save & Exit", command=window.destroy).pack()
window.mainloop()
the_response = list.get(list.curselection())
print (the_response)

How to separate 2 label far apart with tkinter Python?

I would like to separate 2 elements far apart in Tkinter.
I have tried using column such that label_1 is column = 0, row = 0 and label 2 is column 19 and label 3 is column 20 but this still results in them being side by side in the middle. I have set my frame with pack(side =TOP).
I also tried using pack on my label such that label 2 & 3 are right and label 1 is left but still ended up with an unexpected result.
Hence is there a way to separate the 2 elements far apart?
Example
First I use a frame using pack() to display.
self.frameTop.pack(fill=BOTH, expand=NO)
Apparently the condition expand and fill played an important role in display the expected result.
The bottom shows the code for the layout of each element and its anchoring
# Monty Logo
self.icon = Label(self.frameTop, image = self.iconImage, font=('Zilla Slab', 16, 'bold'), borderwidth = 0, highlightthickness = 0, bg="#FFFFFF")
self.icon.pack(padx = 8, pady = 8, side = LEFT, anchor=NW)
# Use a canvas line to deine the cutting
self.labelSeperator = Separator(self.window, orient="horizontal")#Label(self.frameTop, bg="#000000", height= 2, width = int(ws)).pack(side= BOTTOM)
self.labelTitle_time = Label(self.frameTop, font=('Zilla Slab', 16), anchor = "w", borderwidth = 0, highlightthickness = 0, bg="#FFFFFF")
self.labelTitle_time.pack(padx=8,side = RIGHT, anchor=CENTER)
self.labelTitle_day = Label(self.frameTop, font=('Zilla Slab', 16, 'bold'), borderwidth = 0, highlightthickness = 0,bg="#FFFFFF")
self.labelTitle_day.pack(side = RIGHT, anchor=CENTER)

I have created an Entry Widget and placed it in the grid but it doesn't show

I'm creating a front end for a customer access and on one of my pages, the entry widget that I want to place next to the label doesn't show, I don't get any errors.
I've placed it on a grid and moved it around, I have made sure nothing else is in it's part of the grid
from tkinter import *
from tkinter import ttk
newCustomerPage = Tk()
newCustomerPage.geometry('400x172') #define the size of the window
newCustomerPage.overrideredirect(True) #removes the bar at the top of the window
windowWidth = newCustomerPage.winfo_reqwidth() #Place the window in the middle of the page
windowHeight = newCustomerPage.winfo_reqheight()
positionRight = int(newCustomerPage.winfo_screenwidth()/2.3 - windowWidth/2)
positionDown = int(newCustomerPage.winfo_screenheight()/3 - windowHeight/2)
newCustomerPage.geometry("+{}+{}".format(positionRight, positionDown))
print("New Customer Page set")
customerPageLabel = Label(newCustomerPage, text = "New Customer", font = ("Calibri", 25), padx = 110)#.pack(side = TOP, pady = 1)
customerPageLabel.grid(row = 0, column = 0)
newCustNameLabel = Label(newCustomerPage, text = 'Given Name:', font = ("Calibri", 12))#.pack(side = TOP, pady = 1)
newCustNameLabel.grid(row = 1, column = 0)
newCustNameEntry = Entry(newCustomerPage, width = 10)
newCustNameEntry.grid(row = 1, column = 1)
close = ttk.Button(newCustomerPage, text = 'Close', width = 20, command = newCustomerPage.destroy)#.pack(side = TOP, pady = 8)
close.grid(row = 2, column = 0)
The page opens and displays "New Customer" then directly below that in the middle of the page displays "Given Name:" then directly below that displays the "close" button which functions fine, I want the entry box to display to the right of 'newCustNameLabel' but it doesn't
From the docs:
padx: The amount specifies how much horizontal external padding to leave on each side of the slave(s), in screen units.
So by setting padx=100 in your grid column #0, you applied padding for 100px on both sides, which pushes the Entry widget off screen.
Instead of using padx, what you should do is to set the columnspan of customerPageLabel so it can span over more than 1 column:
customerPageLabel = Label(newCustomerPage, text = "New Customer", font = ("Calibri", 25))
customerPageLabel.grid(row = 0, column = 0, columnspan=2)
And if you want the columns to occupy all the remaining space, set a weight to the columns:
newCustomerPage.columnconfigure(0,weight=1)
newCustomerPage.columnconfigure(1,weight=1)

tkinter histogram prints downwards

I am trying to draw two histograms alongside one another using tkinter canvas. Everything sort of works ( looks extremely scruffy at the moment) but the histograms are drawn downwards. I have tried making the y0 value negative, but then nothing at all is drawn.
I am using two lists of numerical data, the first with 50 observations and the other with eleven observations, the scales are not the same, but it is the qualitative effect I want at the moment.
The offending code is as follows:
root = Tk()
canvas = Canvas(root, width=620, height=400, background = "salmon")
canvas.grid()
# draw x-axis lines
canvas.create_line(0,2, 500, 0, width = 2, fill = "firebrick")
canvas.create_line(505,2, 610, 0, width = 2, fill = "dark slate blue")
# draw histograms
for idx in range(len(main_counts[0])):
canvas.create_rectangle(idx*10, main_counts[0][idx], 10 +(idx*10), 0, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 40, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
for idx in range(len(star_counts[2])):
canvas.create_rectangle((505 + idx*10), star_counts[2][idx], (515 + (idx*10)), 0, fill = "gold", outline = "dark slate blue")
canvas.create_text(505 + idx*10 + 8, 120, text = idx + 1, font = ("Comic sans MS", 8) , fill = "dark slate blue")
root.mainloop()
I know that I am missing something quite simple and obvious to all of you, but I just can't see it or the way to make my y0 negative which will presumably solve the problem. I can also not see my x-axes, but that may be because they are occluded by the histogram bars.
Many thanks for your patience and help! Any other suggestions about formatting the graphs will be welcomed including suggestions of best font to use for small digit screen display
The system coordinates start in the upper-left corner so you should write something like:
main_counts =[[10, 20, 30]]
for idx in range(len(main_counts[0])):
canvas.create_rectangle(idx*10, 200 -main_counts[0][idx], 10 +(idx*10), 200, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 210, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
You can use enumerate for more readable code:
for idx, val in enumerate(main_counts[0]):
canvas.create_rectangle(idx*10, 200 -val, 10 +(idx*10), 200, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 210, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
In simple terms - Try to create the histogram from a point on the coordinate according to your value as your second argument in rectangle_crete() function, and then go upto the point where you want your base of the histogram should,which will me constant for all your histograms. Because Tkinter coordinates starts from (0,0) and goes from up to down.
An example code is here -
from Tkinter import *
from random import randint # for testing histogram
master = Tk()
w = Canvas(master, width=1000, height=500)
w.pack()
start_point = 70
padding_y = 450
width = 50
height = 450
list = []
for i in range(1,10):
list.append(randint(1,4))
for i in range(1,10):
end_point = start_point+width
w.create_rectangle(start_point, list[i-1]*100, end_point, height, fill="blue")
start_point += width+20
mainloop()

Resources