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

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()

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.

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.

Tkinter Label Widget weird behavior when changing color and text via List of Variables

I'm building a little Accountant tool so I stop spending money on useless stuff.
Since I am working with big Lists of Invoices I wanted to structure and color them by Categories, because I need to know where to cut Spending in general.
Since Listboxes don't support Multiline Text and no colors I wanted to use Buttons which dynamically change when scrolling.
I discovered that Buttons don't support fonts which makes working with long IBAN Strings very shitty.
Then I decided to use Label widgets which provide Coloring, Font and Multiline and created my own Widget.
This is the function which gets called when the user is scrolling and the Labels have to change Color and Text:
def Refresh (self):
for button in self.buttons:
try:
index = self.buttons.index(button) + self.ListIndex
if type(self.textList[index]) is list:
button.configure(text = self.textList[index][0] , bg = self.textList[index][1])
else:
button.config(text = self.textList[index])
except IndexError:
button.config(text = " ",bg = "#d9d9d9")
Variable Samples:
self.buttons = A list of Label references created when the Widget gets called length depends on how many buttons the user wants being displayed
self.ListIndex = Basically an Index offset, which gets in/decremented when the user is scrolling up or down
self.textList = list of config data for the Labels which elements maybe string or list
self.textList = [['foo', '#eb3434'],'bar']
index = 0
self.textList[index][0] = Invoice Details to be displayed
self.textList[index][1] = Invoice Group Color for easy recognition
now with only the text config:
button.configure(text = self.textList[index][0])
Expected Result:
Now this is very weird when I try to config the color with:
button.configure(text = self.textList[index][0] , bg = self.textList[index][1])
Not expected Result:
After Scrolling:
After a little bit of semantic debug I found out that the colors are in the correct position. Where text and color appears, to be random.

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.

Scale with variable resolution [duplicate]

Is it possible to have a slider (Scale widget in tkinter) where the possible values that are displayed when manipulating the slider are discrete values read from a list?
The values in my list are not in even steps and are situation dependent.
From all the examples I've seen, you can specify a minimum value, a maximum value and a step value (n values at a time), but my list might look like this:
list=['0', '2000', '6400', '9200', '12100', '15060', '15080']
Just as an example. To reiterate, I want it go from for instance list[0] to list[1] or list[6] to list[5] when pulling the slider.
If anyone has any other suggestion for easily being able to pick a value from hundreds of items in a list, I'm all ears. I tried the OptionMenu widget but it gets to extensive and hard get a view of.
Edit you could set the command of the slider to a callback, have that callback compare the current value to your list and then jump to the nearest by calling set() on the slider
so:
slider = Slider(parent, from_=0, to=100000, command=callback)
and:
def callback(event):
current = event.widget.get()
#compare value here and select nearest
event.widget.set(newvalue)
Edit:
to show a complete (but simple example)
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
valuelist = [0,10,30,60,100,150,210,270]
def valuecheck(value):
newvalue = min(valuelist, key=lambda x:abs(x-float(value)))
slider.set(newvalue)
root = tk.Tk()
slider = tk.Scale(root, from_=min(valuelist), to=max(valuelist), command=valuecheck, orient="horizontal")
slider.pack()
root.mainloop()
i've tested this in python 2.7.6 and 3.3.2, even when dragging the slider this jumps to the nearest value to where the mouse is currently as opposed to only jumping when you let go of the slider.

Categories

Resources