tkinter present the values from a list to entry widgets - python

Using tkinter in the gui I'm building an array made of entry boxes. In some columns of the array I'm entering values which i read them and build a list for every column. There are also columns without values which are readonly in which I want to export results from lists. I can't find how to make the program to show the results.
Keep in mind that i have two defs one for building the widgets and one for the calculations.
In order to build one empty-results column I make this:
self.akt_b =[]
for i in range(12):
akt_b =StringVar()
self.akt_b =Entry(self, textvariable =akt_b, width =10,
state='readonly', readonlybackground ="white")
self.akt_b.grid(row =7+i, column =3)
which makes visually what I want.
Then I calculate a list_b of 12 float numbers which I want to show in self.akt_b
I'm trying:
for i in range(12):
self.akt_b[i+1].delete(0, END)
self.akt_b[(i+1)].set("round(list_b[(i+1)], 2)")
which doesn't work
I've tried also .insert not again, I've tried akt_b nothing again
what can I do?

after Kevin's answer I found out the solution, I was missing the .append argument.
Just in case anyone is interested here is the solution:
self.akt_b, self.entries =[], []
for i in range(12):
akt_b =StringVar()
entries =Entry(self, textvariable =akt_b, width =10,
state='readonly', readonlybackground ="white")
entries.grid(row =7+i, column =3)
self.akt_b.append(akt_b)
so now
for i in range(12):
self.akt_b[i].set(round(list_b[i], 2))
works perfect.
Sometimes brain just seems to stop...

Related

how to set a list of entries text variables to another dynamic list

Is there anyway I can plug my list of integers into my list of entry boxes? The list of integers is constantly changing..
This would not be a problem if the list of integers and list of entry boxes had the same number of data points, however I can't determine that initially because I want user input to determine this entry list length in future code. I've tried using Insert to solve this problem, to no avail, given that I couldn't use the index of entry to configure its text option.
from tkinter import *
def entry_list_extender():
entrylist.extend(number)
gx=10
number=0
root=Tk()
frame=Frame(root)
frame.pack()
entry=[]
entrylist=[1,2,3,4]
var = []
entrybox=Entry(frame,bg='blue',textvariable=number)
entrybox.pack()
button=Button(frame,bg='red',command=entry_list_extender)
button.pack()
for i in range(gx):
entry.append(Entry(frame, textvariable=entrylist[i]))
entry[-1].pack()
root.mainloop()
A solution or path I could take to get the results I want would be appreciated.
Edit: my original question was quite ambiguous. This should make it more clear
UPDATE:
I am going to have to make an assumption here to make this work.
I am assuming that gx is the user defined variable you want to use down the road.
If that is the case then you need to change you your code a bit to re-create the entry fields when you press the button and also use the value of gx to decide on how many entry fields you should use.
Let me know if this is closer to what you are trying to do as it is still not very clear what your goal is.
from tkinter import *
root=Tk()
gx=10
number=0
entry=[]
entrylist=[1, 2, 3, 4]
var = []
def entry_list_extender():
global frame, entrylist, entry
entry = []
entrylist = []
for i in range(gx):
entrylist.append(i)
frame.destroy()
create_entry_fields()
entrybox=Entry(root, bg='blue', textvariable = number)
entrybox.pack()
button=Button(root, bg='red', command = entry_list_extender)
button.pack()
def create_entry_fields():
global frame, entrylist, entry
frame = Frame(root)
frame.pack()
print (len(entrylist))
for i in range(len(entrylist)):
entry.append(Entry(frame, textvariable = i))
entry[-1].pack()
create_entry_fields()
root.mainloop()

Programatically add and remove tkinter python labels causes IndexError: list index out of range

Sorry for the vague title but I didn't know how to explain myself better. Basically what I try to do in tkinter here is adding and removing labels. The label value gets updated so that I always have an increment of 1 even though I deleted a label in the beginning. If I generate labels and delete them from the bottom up I have no problems but it I delete one from the middle and then try to clean my list I get an error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1536, in __call__
return self.func(*args)
File "/Users/XXXX/Helper/development/dynamicListLabels.py", line 21, in <lambda>
labelList[index].append(ttk.Button(root, text="Remove", command=lambda: removeLabel(labelList[index][0], index)))
IndexError: list index out of range
My python code looks like this:
#!/usr/bin/python
from Tkinter import *
import ttk
def removeLabel(labelToRemove, bla):
labelList[labelToRemove.get()][1].destroy()
labelList[labelToRemove.get()][2].destroy()
del labelList[labelToRemove.get()]
for label in labelList:
index = labelList.index(label)
label[0].set(index)
def addNewLabel():
labelList.append([IntVar()])
index = len(labelList) - 1
labelList[index][0].set(index)
labelList[index].append(ttk.Label(root, textvariable=labelList[index][0]))
labelList[index].append(ttk.Button(root, text="Remove", command=lambda: removeLabel(labelList[index][0], index)))
labelList[index][1].grid(column=0)
labelList[index][2].grid(column=1, row=labelList[index][1].grid_info()['row'])
root = Tk()
labelList = []
ttk.Button(root, text="add label", command=addNewLabel).grid(column=1, row=0)
root.mainloop()
And my GUI looks like this:
Thanks for your help!
Design
The main problem comes when dealing with different indexes. Trying to manipulate them carefully leads to complicated operations resulting in a long and inefficient code. To remedy to this problem, we simply get rid of them and take advantage of the label class variable Tkinter.IntVar() you already are using. This gives us full control of the labels and associated widgets.
An other efficient decision to take that prevents from getting lot of headache is to attach each (label, button) couple widgets to a unique Tkinter.Frame() instance. This offers the advantage of deleting the frame using destroy() method leading automatically to the destruction of the widgets it contains. In the same time, this keeps the look of your GUI and makes your it scalable as it offers you the possibility to add more widgets.
Designing addNewLabel()
There is nothing new here compared to your original code except, as I said in 2. each (label, button) couple will be drawn into a single and unique Tkinter.Frame() instance. Of course, the list frames must be declared global in this method.
Designing removeLabel()
From 1. the only argument we need to pass to removeLabel() is the Tkinter variable (var in the code below) inherent to the label we want to get rid of.
We need then to loop over list of frames (frames in the code below) using winfo_children() to seek for the label which has the text variable we are looking for.
Note that because I draw the label before the button inside individual frames, winfo_children() returns as first widget list element the label
winfo_children():
Returns a list containing the path names of all the children of window. Top-level windows are returned as children of their logical
parents. The list is in stacking order, with the lowest window first,
except for Top-level windows which are not returned in stacking order.
Use the wm stackorder command to query the stacking order of Top-level
windows.
This is why it is correct to write : if frame.winfo_children()[0].var == var and destroy the frame that contains the label which satisfies this condition.
Solution
Here is the program. I commented the lines which I think deserve to be commented:
'''
Created on Jun 25, 2016
#author: billal begueradj
'''
from Tkinter import *
import ttk
def removeLabel(var):
global frames
z = -1
# Loop over the list of rames
for frame in frames:
z = z + 1
# Check the text variable of the label of this frame
if frame.winfo_children()[0].var == var:
# Destroy the related frame
frame.destroy()
# Update the size of the list of frames
frames = frames[:z] + frames[z+1:]
# Do not forget to always rest this flag back to -1
z = -1
# Update the labels' numbers
r = 0
for frame in frames:
frame.winfo_children()[0].var.set(r)
r = r + 1
def addNewLabel():
global frames, i
var = IntVar()
frame = Frame(root)
i = i + 1
frame.grid(row=i, column=0)
var.set(len(frames))
l = ttk.Label(frame, textvariable=var)
l.grid(row=0, column=0)
l.var = var
b = ttk.Button(frame, text="Remove", command=lambda: removeLabel(var))
b.grid(row=0, column=1)
frames.append(frame)
if __name__ == '__main__':
root = Tk()
frames = []
i = 1
ttk.Button(root, text="add label", command=addNewLabel).grid(column=0, row=0)
root.mainloop()
Demo
Let us create 6 labels:
Now let us delete the label number 3. You can see that the numbering of the labels is automatically updated:
Now let us add a new label. You can see the newly added label has a number which is consecutive to the last existing label number in the list:
Note that the length of the list is updated all the time as you wanted.

How can i display a 3x3 grid in Python tkinter?

For a program i intend to develop, its essential for me to develop a 3x3 grid which will display words from a set which has been converted to a list. To achieve this I'm aware of a method which will supposedly create a 3x3 grid using a list with linebreaks already within the developed list. However, my program functions at a more diverse level and instead of having a pre-defined text file / list, my set/list is defined by the users input and utalises a file dialog which allows the user to select their own 9 words for the 3x3 grid. For this reason i dont think its possible to apply the line breaks. Is there another way i could display a 3x3 grid which displays each of these 9 words. Would be extremely grateful of any advice given. As for code... if anyone feels that any particular section of code would help them solve this problem, do not hesitate to leave a comment. Thank you very much!
Creating a 3x3 grid is no more difficult than looping over items and creating widgets.
This creates a 3x3 grid of labels, storing references to the widgets in a dictionary:
table = tk.Frame(root)
for row in range(3):
for col in range(3):
label = tk.Label(table, text="")
label.grid(row=row, column=col, sticky="nsew", padx=1, pady=1)
table[(row, col)] = label
You can then modify any of the cells of the table using the configure method of the label widget. For example, this sets the middle cell to "Hello":
table[(1,1)].configure(text="Hello")
oyoy, i see dis,
table = tk.Frame(root)
for row in range(3):
for col in range(3):
label = tk.Label(table, text="")
label.grid(row=row, column=col, sticky="nsew", padx=1, pady=1)
table[(row, col)] = label

Python tkinter main window improper size when .grid() widgets

I have a game board which is rows x columns list.
Min size is 2x2 and max 10x10, with unequal rows:columns being okay (e.g. 2x3, 4x9).
Main window object has no predetermines geometry size setting, and widgets (buttons) are being .grid() in it for each list element in a was that creates a 2D map.
Ideally, given the method used this would lead to a nice, edge=to-edge map inside the main window.
Unfortunately, testing has shown that while this is true for maps with columns count > 3, when columns <= 3 then the window seems to default to a certain X-size, where this ugly free space is present at the right of the window.
This is not the case for Y-axis, which is defined by rows.
Note that buttons placed are fixed 32x32 px (determined by image inside).
def createMap (): #creates rows x columns 2D list - a map
global rowsEntryVar, columnsEntryVar, mapList
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, bd=0, bg=redMagenta, activebackground=redMagenta))
mapList.append(tempList)
and then:
def drawMap ():
global mapList
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].grid(row=row, column=column)
Image:
Image showing the problem
Please go easy on me, I'm quite new to programming. :)
This appears to be a platform-specific limitation. I can't duplicate the problem on my Mac, but I can on a windows VM. Apparently, Windows won't allow the width of the window to be smaller than the space required for the buttons and icon on the titlebar.
My advice is to give the rows and columns a positive weight so that they will grow to fit the window, and then use the sticky option to cause the buttons to fill the space given to them.
when columns <= 3 then the window seems to default to a certain X-size,
Tkinter defaults to the size of the widgets so you must be setting the geometry for "root" somewhere. The following works fine on my Slackware box (and using a function as a function eliminates the globals). If you are just starting, then it is good to form good habits, like conforming to the Python Style Guide https://www.python.org/dev/peps/pep-0008/ (variables and functions are all lower case with underlines).
from Tkinter import *
def create_map (rowsEntryVar, columnsEntryVar): #creates rows x columns 2D list - a map
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, text="%s-%s" % (row, column),
bd=0, bg="magenta2", activebackground=r"magenta3"))
mapList.append(tempList)
return mapList
def draw_map(mapList):
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].grid(row=row, column=column)
root = Tk()
map_list=create_map(4, 3)
draw_map(map_list)
root.mainloop()

Positioning widget buttons and Entry boxes better in Python

Ok, so I'm using the Tkinter module for Python, and I want to have 5 Entry boxes, each of which will be a variable which combines into a single variable, and is used as the serial key for an automatic installer for Office (among other software, for an install script for a medium-sized office).
None of that is really relevant, except to give you an idea of the content.
I am attempting to put all of the Entry boxes in a small space, preferably one or two columns (I am currently using 4-6 columns, I can check).
Is this possible with Tkinter?
Here's my code so far for this part of the script:
Label(app, text="Office Serial Key").grid(row=3, column=0)
entries = ["e1", "e2", "e3", "e4", "e5"]
colnum = 1
for item in entries:
item = Entry(app, width=10)
item.grid(row=3, column=colnum)
colnum = colnum + 1
I want to look more professional/like a Microsoft install.
You can do a lot to get the layout exactly the way you want it. I think that the way to accomplish something like this is to put your Label+Entrys into another Frame, then when you grid that Frame, use the columnspan keyword to set how wide it should be with respect to the other widgets in "app".
( http://effbot.org/tkinterbook/grid.htm )
here's a silly example:
import Tkinter as tk
root=tk.Tk()
for i in range(7):
tk.Label(root,text='%d'%i,width=12).grid(row=0,column=i)
myframe=tk.Frame(root)
tk.Label(myframe, text="Office Serial Key").grid(row=0, column=0)
entries = ["e1", "e2", "e3", "e4", "e5"]
for column,item in enumerate(entries,start=1):
item = tk.Entry(myframe, width=10)
item.grid(row=0, column=column)
myframe.grid(row=1,column=0,columnspan=3)
root.mainloop()

Categories