tkinter button destroys frame and removes index from list - python

I'm relatively new to tkinter and I'm trying to create a program to show some stock values. I have the user input the stock symbol which appends to a list of stocks and displays some information about the stock in a tkinter frame. Within the frame I have a "Remove Stock" button that I want to simultaneously remove the stock from the list of stocks and destroy that frame. Here is my relevant code:
current_stocks = []
stock_frames = []
def update(): #Update the Currently Displayed Frames
print(stock_frames)
for stock in current_stocks:
list_index = current_stocks.index(stock)
stock_frame = LabelFrame(root, text=stock, padx=5, pady=5)
stock_frame.grid(row=list_index+1,column=0,columnspan=3)
if stock_frame not in stock_frames:
stock_frames.append(stock_frame)
stock_info_lbl = Label(stock_frame, text=f'{stock} Current Price')
stock_info_lbl.grid(row=0,column=0)
graph_stock_btn = Button(stock_frame, text="Graph", command=graph_stock)
graph_stock_btn.grid(row=0,column=1)
remove_stock_btn = Button(stock_frame, text="Remove", command=lambda list_index=list_index: remove_stock(list_index))
remove_stock_btn.grid(row=0,column=2)
def remove_stock(i): #Remove Stock From List and Destroy the Frame
current_stocks.pop(i)
stock_frames[i].destroy()
stock_frames.pop(i)
update()
I pass the list index parameter to remove stock because as far as I can see the index position of current_stocks and stock_frames should correspond to the same stock. I tried using grid_forget() instead of destroy() but that doesn't change anything. I should note that as long as I only have one stock displayed I can remove it perfectly fine, however if I add more than one stock things start breaking. This makes me believe the error has something to do with this:
if stock_frame not in stock_frames:
stock_frames.append(stock_frame)
Any help would be greatly appreciated, thanks!

your intuition is correct about where your problematic code lies, it is in if stock_frame not in stock_frames. This will always return false as this is object comparison and two LabelFrame are not equivalent even if they have the same text.
To get around this you would want to compare only the name in the first list (current_stocks) and take it for granted that stock_frames has an associated entry which you can delete.
the more thorough solution would be to use a more associative datastructure such as a dict from stock_name --> tkinter_frame
That would look something like this:
stocks = {
"GE": None, # A default case
"SYM": tkinter.LabelFrame(...), # A populated value
...
}
and your remove method would operate on that dictionary instead:
def remove_stock(stock_name): #Remove Stock From List and Destroy the Frame
stock_frame = stocks[stock_name]
# stock_frame could be None
if stock_frame:
stock_frames[i].destroy()
del stocks[stock_name]

Related

Tkinter Checkbutton created from a SQLite database using a For Loop always returns false

sort of a beginner at python programming but ive run into a roadblock and need a little assistance. i have a data base of two items, which are basically just a name for a crypto, and a id that needs to be passed later. i'm trying to use the for loop to create checkbuttons that take the id as the onvalue so that i can get them from the variable and pass them later. I'm trying to create these checkbuttons inside of a labelframe that's attached to a different window, but I'm calling Tk and running mainloop and everything is set up properly, its just one little issue ill explain down below
here's whats contained in the coinlist db database and fetchedcoins variable:
[('Bitcoin', 'bitcoin', 1), ('Ethereum', 'ethereum', 2)]
so when i iterate through the database for the first item, it sets the id variable to the on value properly, but when it hits the second item, it replaces the previous coinid data in the checkbutton var variable with the coinid of the new coin, and as a result when i activate the pasteids function and get the checkbutton variable to use in a label, it only returns the onvalue for the checkbutton of the last item of the list, since that was the database item that was last loaded into the variable. even if the last item is deselected and the other is selected, the other items on value returns false. im wondering if theres a way for me to create different variables for each item of the loop, so that the state variable var doesnt only reflect the state of the last button that was created. any tips or workarounds help are greatly appreciated
def selectcoins():
selectframe = LabelFrame(avreadermain, text='Select Coins')
selectframe.grid(row=1,column=0,padx=20,pady=15,sticky=W+E)
conn = sqlite3.connect('coinlist.db')
cursor = conn.cursor()
cursor.execute('SELECT *, oid FROM coins')
fetchedcoins = cursor.fetchall()
for coins in fetchedcoins:
coinid = coins[1]
coinname = coins[0]
var = StringVar()
c = Checkbutton(selectframe, text=coinname, variable=var,onvalue=coinid,offvalue='false')
c.deselect()
c.pack()
def pastedids():
coinids = Label(selectframe,text=var.get())
coinids.pack()
showidbtn = Button(selectframe,text='show IDs',command=pastedids)
showidbtn.pack()

How to a convert a listbox item's name to a label in Python

For the program that I am required to write up, I need the name of a Listbox item to also be the text in the label. This is the code that I thought would work, but it only configurated the label to be the item's number in the Listbox, not the name:
def openAccount(self):
selectedItem = ltboxSrcUser.curselection()
for item in selectedItem:
rootMainPage.withdraw()
rootOthAccPage.deiconify()
lblOtherUserName.config(text = ltboxSrcUser.curselection())
Can anybody help me out?
The documentation for the listbox shows that curselection returns the index of the selected items, not the values. To get the values you must pass the index to the get method.
def openAccount(self):
selectedItems = ltboxSrcUser.curselection()
if selectedItems:
rootMainPage.withdraw()
rootOthAccPage.deiconify()
index = selectedItems[0]
item = ltboxSrcUser.get(index)
lblOtherUserName.config(text=item)

How to make each radiobutton unique from the others in tkinter

I have a window in Tkinter that looks like this:
When i click on a button in the first row, it stays. However, when i click on a button in the second row, it unselects the one i chose above.
I want it to be able to only select one option per row. Is there something I'm missing? When it's done, I want to be able to iterate over the rows and get the value of the boxes, but I'm not sure how to do that either.
The code for that section is:
for i in studentList:
Label(left,text=i[0][::]+' ' + i[1][::],fg='black',bg='#dbdbdb',font=('Arial',11,'bold')).grid(row=counter,column=0,pady=13,sticky='news')
P = Radiobutton(right,text='Present',bg='#56ab32',fg='black',value='P'+str(counter),indicatoron = 0,font=('Arial',12,'bold'))
P.grid(row=counter,column=0,pady=10,padx=20,sticky='news')
L = Radiobutton(right,text='Leave',bg='#e6a800',fg='white',indicatoron = 0,value='L'+str(counter),font=('Arial',12,'bold'))
L.grid(row=counter,column=1,pady=10,padx=20,sticky='news')
Radiobutton(right,text='Absent',bg='#bd2900',fg='white',indicatoron = 0,value='A'+str(counter),font=('Arial',12,'bold')).grid(row=counter,column=2,pady=10,padx=20,sticky='news')
counter+=1
Radiobuttons work by assigning two or more radiobuttons the same instance of one of tkinter's special variable objects -- usuallyStringVar or IntVar. This sharing of a variable is what makes a group of radiobuttons work as a set, since the variable can only hold a single value.
Because you aren't assigning a variable, tkinter is using a default variable which is the same for every button. Thus, all buttons are acting as a single set.
To make your code work, each row needs to use it's own instance of StringVar. It would look something like this:
vars = []
for i in studentList:
var = StringVar()
vars.append(var)
...
Radiobutton(right, variable=var, ...)
Radiobutton(right, variable=var, ...)
Radiobutton(right, variable=var, ...)
...
With the above, you can get the choice of each row by getting the value of the variable for that row. For example, the first row would be vars[0].get(), the second row would be vars[1].get() and so on.

Tkinter - getting an array of entry values

I have 20 entries in my Tkinter GUI created using for-loop (there might be more of them in the future and I really don't want to have 50 lines of code just for deifining the entries). I need to collect entries values to create a numpy array out of them. As a shot in the dark I have tried this:
master = Tk()
R=StringVar()
namR = []
for ii in range(0,20):
namR.append(Entry(master), textvariable=R[ii])
namR[ii].grid(row=2+ii, column=3)
which obviously does not work (StringVar instance has no attribute '__getitem__'), but I think the goal is clear.
Any suggestions to make this work, please?
You should include your textvariable within the Entry() call, not after it (append(Entry(master, textvariable=xyz)) rather than append(Entry(master), textvariable=xyz)). append() won't know what to do with the second argument. Next, you can create a list for the StringVar objects and refer to them with something like Entry(master, stringvariable=svars[ii]). However, this is only necessary if you want to do things like variable tracing. If you just want to retrieve the text in an entry object, you can do it with my_entry.get().
master = Tk()
namR = []
for ii in range(0,20):
namR.append(Entry(master))
namR[ii].grid(row=2+ii, column=3)
[e.get() for e in namR] will then be a list of all the entry contents.

Retrieve text from entry widget Tkinter

So I know how to retrieve the text from a single entry widget using the get function but now I am trying to create multiple entry widgets at the same time using a for loop. How can I go back and retrieve the text from anyone of the entry widgets once the user types in them?
rows=11
for line in range(10):
rows=rows+1
widget = Tk.Entry(self.frame)
widget.grid(row=rows, column=5)
The problem is that all of your widget objects are being assigned to a reference, and with each next iteration of the loop are being unreferenced. A way to fix this is to create a list and add these widgets to the list:
entries = []
for line in range(10):
rows = rows + 1
widget = Tk.Entry(self.name)
widget.grid(row = rows, column = 5)
entries.append(widget) # Add this line to your code
Now, to access a specific entrybox, you just find it in the array. So, for example, the second box will be found at entries[1] (because it is 0-based).
The fundamental problem you're having is that you don't have any sort of data structure. Are you familiar with the list type?
rows=11
entries = [Tk.Entry(self.frame) for item in range(10)]
for item in entries:
rows=rows+1
item.grid(row=row, column=5)
This creates a list of Entry widgets, then goes through that list and grids each one into a row (starting with 12). You can access list items by index, e.g. entries[0] for the first Entry.
widgets = []
for i in range(11, 23):
widgets.append(Tk.Entry(self.frame))
widget[i-11].grid(row = i, column = 5)
To answer your question directly, you need the get() method, which is available to the Entry class. To get the value of your widget object, the code would look something like this:
myValue = widget.get()
Please note that, as others have mentioned, your "for" loop does not actually create 10 Entry objects. Since you keep reassigning your new Entry objects to the variable "widget", the old Entry object you created gets de-referenced. Instead of assigning each new Entry object to the variable "widget", append them to a list instead.

Categories

Resources