How to move images using grid? - python

I'm using this code to display images + text to the screen:
root = Tk()
for i in range(5):
img = some_function_that_returns_ImageTk_PhotoImage()
ls.append(img)
panel = Label(root, image = img)
txt = Label(root, text = str(1+i))
panel.grid(row = 1, column = i)
txt.grid(row = 1, column = i)
root.mainloop()
and I get these 5 images + text lines displayed, but what I want is to have some space between each image.
So, I tried changing the column value to 2*i but that didn't
help. If I changed only the column value in panel, it lined up
one image and then one text and so on, until 5 < 2*i and then it
went back to lining the images one after the other - not what I
expected
Then, I tried changing the column value to 2*i for both label & txt. Didn't work either. It actually gave the same results as just using i. What am I missing here?

you will need to use padding when setting the grid:
panel.grid(row = 1, column = i,padding=(5,5,5,5))
you use it as a tuple containing the amount of pixel padding you want for your widget in each direction, i usually set them the same as above, but you can play around and see what works

Related

Placing Images in loop in Tkinter

I'm trying to place a set of images in a horizontal row in Tkinter. I do this by looping over a list, and loading the corresponding images from the working directory.
I place the images according to the index multiplied with a certain amount of spacing. However, when i actually place the images, they all get placed on top of each other in the last spot, instead of spaced out. The count value works fine, and when i print(spacing*(count+1)) it outputs the correct values but when placing they all get bunched up in the last place.
Does this have something to do with the Label() class?
for count, mood in enumerate(mood_options):
mood_img = Image.open(f"img/{mood}.png")
mood_img_copy = mood_img.resize(img_size, Image.ANTIALIAS)
mood_img_resized = ImageTk.PhotoImage(mood_img_copy)
mood_img_label = Label(root, image=mood_img_resized).place(relx=spacing*(count+1), rely=0.35)
print(spacing * (count + 1))
EDIT: I have used this exact method for placing buttons, see below:
for count, mood in enumerate(mood_options):
mood_btn = Button(root, text=mood.capitalize(), command=lambda mood=mood: register_mood(mood), width=7) \
.place(relx=(count + 1) * spacing, rely=0.5)
This works flawlessly, which makes me wonder why it wouldn't work with images, instead of buttons.
If the same logic works for buttons, then the problem might be the image is garbage collected, in this case you cannot store the image as an attribute of some class to hold reference, because over each iteration each image will be overwritten, holding refence to the last image only. Same happens with global as well. So the way to go here would be appending to a list. Try something like:
lst = []
for count, mood in enumerate(mood_options):
mood_img = Image.open(f"img/{mood}.png")
mood_img_copy = mood_img.resize(img_size, Image.ANTIALIAS)
mood_img_resized = ImageTk.PhotoImage(mood_img_copy)
lst.append(mood_img_resized)
Label(root, image=mood_img_resized).place(relx=spacing*(count+1), rely=0.35)
print(spacing * (count + 1))
Though it would be much better to place stuff using grid to place it in a grid like manner. If your worried about responsiveness of the window then look at weight:
from tkinter import *
root = Tk()
mood_options = ['a', 'b', 'c']
for count, mood in enumerate(mood_options):
Label(root, text=mood).grid(row=0,column=count,padx=50)
root.grid_columnconfigure(count,weight=1)
root.grid_rowconfigure(0,weight=1)
root.mainloop()
Is your spacing the actual amount of space in pixels?
If so, leave away the relx and only use x. relx has to be a float between 0.0 and 1.0.

Tkinter List of Variable Sizes

I'm trying to make a menu of buttons using Tkinter. I have a randomly generated list of files with their attributes as tuples (Name, Size, Link), and need to arrange them in a grid. I can't set each button to the grid since the number of buttons changes. Here is how I tried to make it:
def selected_button(file):
print(f"File: {file[0]}, {file[1]}, Link: {file[2]}")
for file in files:
fileButton = Button(root, text= file[0],command= lambda: selected_button(file).grid()`
Problems:
The variable "file" that gets passed on at the button click is always the last generated button's.
The buttons arrange themselves as a long list in one column - I need a nice square/rectangle.
If I left out any info, comment and I'll answer promptly.
Problem #1
To get the lambda to store the current value in the iteration it needs to be called out in the lambda function such as command= lambda f = file: selected_button(f).
Problem #2
The method I would usually use for making a grid of buttons is pick a width you want, possibly 3 wide, then increment the column until it reaches that point. Once that width is reached, reset the column and increment the row.
import tkinter as tk
# Testing
files = []
for x in range(12):
files.append((f"Test{x}", f"stuff{x}", f"other{x}"))
# /Testing
def selected_button(file):
print(f"File: {file[0]}, {file[1]}, Link: {file[2]}")
root = tk.Tk()
r, c = (0,0) # Set the row and column to 0.
c_limit = 3 # Set a limit for how wide the buttons should go.
for file in files:
tk.Button(root,
text= file[0],
width = 20,
# This will store what is currently in file to f
# then assign it to the button command.
command= lambda f=file: selected_button(f)
).grid(row = r, column = c)
c += 1 # increment the column by 1
if c == c_limit:
c = 0 # reset the column to 0
r += 1 # increment the row by 1
root.mainloop()

Position / lot size based on buy entry and stop gap

I'm trying to code in Python my lot size based on a positive order signal.
Below is the expected output which was done in excel.
The logic is:
Lot_size = IF (Order_Signal=1, then Prior_Period_Portfolio_Value * Risk_pct / Stop_gap,
elif(Active=0, 0, prior period lot size)) risk_pct=0.02
I'm having a having a hard time to reproduce this in excel, specifically the last component where it refers to the prior period lot size.
It depends on how you store these data in Python. But for simplicity I'll assume each variable is in its own list.
order_signal = [0,0,...-1,0]
stop_gap = [0.44,1.13,...1.94,9.06]
prior_period_portfolio_value = [10000,10000,...9900,9807.5]
active = [0,0,...1,0]
lot_size = [0] * len(order_signal)
risk_pct = 0.02
for i = 1:len(order_signal):
if order_signal[i] == 1:
lot_size[i] = prior_period_portfolio_value[i] * risk_pct / stop_gap[i]
elif active[i] == 0:
lot_size[i] = 0
else:
# just need to be sure this doesn't happen on the first iteration
lost_size[i] = lot_size[i-1]
What I learn it from basic app developing courses (PhP) I was use itand on game dev fro sorting data in tables. So the trick here will be to create first the columns and then to add rows as childrens.
On tkinter I will do it like that:
1 - I will create a frame extended vertical as parent
frame = Frame()
frame.pack(expand=True, fill="y", side="left")
2 - I will add entrys extended horizontal as childrens
entry = Entry(frame)
entry.pack(expand=True, fill="x", side="top")
In this case all childrens will have same width.
If you want to use labels just for showing data for every child I will make a frame (same like entry example) and inside of that I will add label (same like entry but with side="right") and all text will have same width, and will be aligned on right.

Changing the text of "mass-produced" labels

Hy All,
I'm creating a layout for a database, and made a big canvas which are the lines, spawning smaller canvas inside them (as cells) to contain labels for the data. It looks nice, but the problem is, that due to this "mass-creation" of canvas and label widgets, none of them stays uniqly addressable, they are all named after the same variable when created in a for loop. Any idea how to tag/address them during the creation so I can edit them later?
for f in range(15)
z = z+1
f = Label(someFrame, width = 45 if z < 4 else 12, text = f, borderwidth=2, relief="groove", bg = "#E5E5E5" if Color == True else "#B2B2B2" )
f.pack(side = LEFT)
It may look a bit messy, but you have a picture at least how the widgets are being created and what is my issue.
You can store your widgets in a dictionary. Something like this:
widget_dict = {}
for idx in range(10):
widget_dict['abc' + str(idx)] = label(root, ...)
Then you can access each widget through its dictionary key:
widget_dict[abc2].config(text='Banana')
Before your for loop create a list. Then inside of the for loop just append every label to the list. Or you can use a dictionary to store them, depending on how you want to deal with that.

Resizing tkinter listbox to width of largest item, using .grid

I have two listboxes that are side by side. They use the lstBox.grid() method to order them in the window. I then have a dropdown option that changes the items displayed in one of the listboxes. The ultimate aim here is to be able to "add" items from one box to the other, and remove ones from the other. My issue is that I don't know how to expand the list box width to be that of the largest item it contains. I have a method that is used for handling when the dropdown is changed, and determining its value to change the items as appropriate, but te listbox doesn't change width.
I am aware that by using lstBox.pack(fill=X,expand=YES), the listbox will resize, but i'm using the .grid() geometry.
Any suggestions?
You can list all your items to find the biggest (if there aren't too much it should be fine). For instance with strings you count their length with 'len(item)'.
Then, when you create your listbox (not when you grid it) your set its width with 'width = "The size you want" ', if the size you put in there is well defined with regard to the length of the biggest item, you shouldn't have any problem.(I think I remember, the listbox's width's unity is given by the size of the text in it, but it needs to be checked)
I don't know grid that much, so that I don't know if there is any faster option to do it.
It should look something like this:
len_max = 0
list_items = ["item2", "item2", "item3+a few characters for the size"]
for m in list_items:
if len(m) > len_max:
len_max = len(m)
import tkinter
master = Tk()
my_listbox1 = Listbox(master, width = len_max)
my_listbox1.grid(row = 0, column = 0)
my_listbox2 = Listbox(master, width = len_max)
my_listbox2.grid(row = 0, column = 1)
my_listbox1.insert(END, list_items[0])
my_listbox2.insert(END, list_items[1])
my_listbox2.insert(END, list_items[2])
master.mainloop()

Categories

Resources