How to fill cells in tkinter table automatically Python - python

I tried to incorporate this piece into my script and add the calculation into column 7, however, it appears to be failing to return correct multiple of inputs, any idea why? Here is the code:
def autofill(self, event):
row = int(event.widget.grid_info()['row'])
auto_list = self.in_list(self.LookUpList, self._entry[row, 0].get())
if auto_list is not None:
self._entry[row,1].delete(0, 'end')
self._entry[row,1].insert(0, auto_list[1])
self._entry[row,2].delete(0, 'end')
self._entry[row,2].insert(0, auto_list[2])
self._entry[row,4].delete(0, 'end')
self._entry[row,4].insert(0, auto_list[3])
self._entry[row,6].delete(0,'end')
if self._entry[row,3].get() != '':
a = self._entry[row,3].get()
else: a = 0
b = int(self._entry[row,4].get())
c = int(a * b)
self._entry[row,6].insert(0, c)
I have just found the error, had to convert one variable into int, then it worked:
self._entry[row,6].delete(0,'end')
if self._entry[row,3].get() != '':
a = int(self._entry[row,3].get())
else: a = 0
b = int(self._entry[row,4].get())
c = int(a * b)
self._entry[row,6].insert(0, c)

Here is a fix that does what you're asking for. The main thing to note is that, I added an autofill method, and a binding to the Return key that calls the autofill method. You need to hit the Return/Enter key after typing for the cells to be populated, you can change this to any other event that you prefer.
There are few other changes just to make the code work in its current state. I did not make any change regarding how efficient/elegant your implementation is, I leave that to you.
from tkinter import *
class SimpleTableInput(Frame):
def __init__(self, parent, rows, columns):
Frame.__init__(self, parent)
self._entry = {}
self.rows = rows
self.columns = columns
# register a command to use for validation
vcmd = (self.register(self._validate), "%P")
# create the table of widgets
for row in range(self.rows):
for column in range(self.columns):
index = (row, column)
if column == 3:
e = Entry(self, validate="key", validatecommand=vcmd)
else:
e = Entry(self)
e.grid(row=row, column=column, stick="nsew")
self._entry[index] = e
# adjust column weights so they all expand equally
for column in range(self.columns):
self.grid_columnconfigure(column, weight=1)
## Lookup table:
self.LookUpList=[
['a','Black skirt','PP','2000'],
['b','Pink T-shirt','PP','1000'],
['c','Yellow skirt','Marela','1500'],
['d','White trousers','PP','2000']]
## Bind the Return/Enter key to populate the entries
for row in range(self.rows):
self._entry[row, 0].bind("<Return>", self.autofill)
def in_list(self, list_of_lists, item):
if not list_of_lists:
return None
if item in list_of_lists[0]:
return list_of_lists[0]
return self.in_list(list_of_lists[1:], item)
## The method that will be called to populate the entries
def autofill(self, event):
row = int(event.widget.grid_info()['row'])
auto_list = self.in_list(self.LookUpList, self._entry[row, 0].get())
self._entry[row,1].delete(0, 'end')
self._entry[row,1].insert(0, auto_list[1])
def get(self):
'''Return a list of lists, containing the data in the table'''
result = []
for row in range(self.rows):
current_row = []
for column in range(self.columns):
index = (row, column)
current_row.append(self._entry[index].get())
result.append(current_row)
return result
def _validate(self, P):
if P.strip() == "":
return True
try:
f = float(P)
except ValueError:
self.bell()
return False
return True
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
names = ["Cislo produktu",
"Popis produktu",
"Znacka",
"Mnozstvi",
"Jednotkova cena",
"Prodejna",
"Celkova cena"]
frame = Frame(self)
frame.pack(side="top", fill="both")
for i, title in enumerate(names):
l = Label(frame, text=title)
l.grid(row=0, column=i)
frame.grid_columnconfigure(i, weight=1)
self.EmptySpace = Label(self)
self.table = SimpleTableInput(self, 30, 7)
self.table.pack(side="top", fill="both")
self.EmptySpace.pack(side="top",fill="both")
## frame1 = Frame(self)
## frame1.pack(side="left",fill="both")
## self.SubButton = Button(self, text="Ulozit a zavrit", command=self.on_ulozit)
## self.StornoButton = Button(self, text="Stornovat nakup", command=self.on_storno)
## self.SubButton.pack(side="left", fill="both", expand=True)
## self.StornoButton.pack(side="left", fill="both", expand=True)
def on_ulozit(self):
data = self.table.get()
data1 = [list(filter(None, lst)) for lst in data]
data2 = list(filter(None, data1))
for item in data2:
item.append(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
## look up property
with open('C:\\Users\\chroustovskyj\\Desktop\\Dev_py\\App\\Data_Storage\\Data_Storage.csv', 'a', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(data2)
root.destroy()
def on_storno(self):
print("This is storno.")
if __name__ == '__main__':
root = Tk()
root.wm_title("Formular")
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
Example(root).pack(side="top", fill="both", expand=False)
root.mainloop()

Related

How to have another argument when we alread have *arg in the function?

I have a code to add rows of entries using a spinbox. I have the update function to update the changes in the number of rows using the spinbox instantly. Inside this function in the line: for j in range(3): number 3 shows the number of columns of the created entries. So, when we change the number of rows, we have 3 columns.
import tkinter as tk
class Data:
def __init__(self):
self.n_para = tk.IntVar()
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.minsize(700, 700)
container = tk.Frame(self)
container.pack()
self.data = Data()
self.frames = {}
for F in (PageOne, ):
frame = F(container, self.data)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
def show_frame(self, c):
frame = self.frames[c]
frame.tkraise()
class PageOne(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
frame1 = tk.Frame(self, width=200)
frame1.grid(row=0, column=0)
self.frame2 = tk.Frame(self)
self.frame2.grid(row=1, column=0)
frame3 = tk.Frame(self)
frame3.grid(row=2, column=0)
label1 = tk.Label(frame1, text="Numeric parameters")
label1.grid(row=0, column=0, pady=10)
my_spinbox = tk.Spinbox(frame1, from_=2, to=10, textvariable=data.n_para)
my_spinbox.grid(row=0, column=1, columnspan=3)
self.row_list = []
for i in range(2):
entry_list1 = []
for j in range(3):
entryX = tk.Entry(self.frame2)
entryX.grid(row=i + 1, column=j)
entry_list1.append(entryX) # Add entry to list
self.row_list.append(entry_list1) # Add entry list to row
self.data.n_para.trace('w', self.update1) # Trace changes in n_para
# This function is for updating the number of rows
def update1(self, *args):
try:
para = int(self.data.n_para.get())
except ValueError:
return # Return without changes if ValueError occurs
rows = len(self.row_list)
diff = para - rows # Compare old number of rows with entry value
if diff == 0:
return # Return without changes
elif diff > 0: # Add rows of entries and remember them
for row in range(rows + 1, rows + diff + 1):
entry_list = [] # Local list for entries on this row
for col in range(1):
e = tk.Entry(self.frame2)
e.grid(row=row, column=col)
entry_list.append(e) # Add entry to list
self.row_list.append(entry_list) # Add entry list to row
elif diff < 0: # Remove rows of entries and forget them
for row in range(rows - 1, rows - 1 + diff, -1):
for widget in self.row_list[row]:
widget.grid_forget()
widget.destroy()
del self.row_list[-1]
app = SampleApp()
app.mainloop()
I wanted to add another argument to update1 function to get the number of columns. but, I think I have a problem with the concept of *args, and how to add another argument to the function.
when I modify the code in the below lines, the code does not work.
def update1(self, *args, n_col):
and
for j in range(n_col):
I think this is a fairly simple solution. (i'm kind of guessing a little to what error your having and how your calling the method) if this is not helpful maybe update the question with the errors.
either reverse the order in which you declare *args and n_col or when calling use a named variable.
def my_sum(*args, ncol):
result = 0
# Iterating over the Python args tuple
for x in args:
result += x
return f"{ncol} + {result}"
print(my_sum(1, 2, 3, ncol=5))
def my_sum(ncol, *args):
result = 0
# Iterating over the Python args tuple
for x in args:
result += x
return f"{ncol} + {result}"
print(my_sum(5, 1, 2, 3))
5 + 6
5 + 6
The *args argument must be the last in your method input.
Try this:
def update(self, n_col, *args):

How to deselect a radiobutton when it is created after to be selected and deleted with Tkinter?

I did a tkinter window where an user has to select some items in a listbox which displays two radiobuttons. If the user selects one radiobutton and then deselects the item in the listbox, radiobuttons are deleted. The problem is that if user selects the same item as previously, the radiobutton is already selected. I would like they are empty when they are created again.
Thanks in advance
from tkinter import *
import tkinter as tk
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.dictLabel = dict()
self.createWidgets()
def createWidgets(self):
self.ListNumber = ['one', 'two', 'three', 'four']
self.labelListNumber = tk.Label(self, text=' Select a Number : ')
self.labelListNumber.place(x=40, y=30)
self.frame = Frame(self)
self.frame.place(x=200, y=30)
self.list = Listbox(self.frame, exportselection=False,activestyle = tk.NONE, height=5, selectmode="multiple")
self.list.pack(side='left', fill='y')
for each_item in range(len(self.ListNumber)):
self.list.insert(END, self.ListNumber[each_item])
self.scrollbar = Scrollbar(self.frame, orient="vertical", command=self.list.yview)
self.scrollbar.pack(side='right', fill='y')
self.list.config(yscrollcommand=self.scrollbar.set)
self.dictRadioButtonValue = dict()
self.list.bind('<<ListboxSelect>>',self.createRadioButton)
def createRadioButton(self, evt):
index = self.list.curselection() # grab the index
c = 1
if len(index) == 0 or len(self.dictLabel) != 0:
for e in self.dictLabel:
self.dictLabel[e][0].place_forget()
self.dictLabel[e][1].place_forget()
self.dictLabel[e][2].place_forget()
del self.dictLabel
self.dictLabel = dict()
for i in index:
item = self.list.get(i)
if not item in self.dictRadioButtonValue:
if len(self.dictRadioButtonValue) > len(index):
if not item in self.dictLabel[item]:
del self.dictRadioButtonValue[item]
else :
radioButtonValue = tk.IntVar()
radioButtonValue.set(' ')
self.dictRadioButtonValue[item] = radioButtonValue
L = tk.Label(self, text=f"Number selected is {item}")
radiobtn5 = tk.Radiobutton(self, text="Yes", variable = self.dictRadioButtonValue[item], value = 5)
radiobtn7 = tk.Radiobutton(self, text="No", variable = self.dictRadioButtonValue[item], value = 6)
L.place(x=350, y=10+(c * 20))
radiobtn5.place(x=500, y=10 + (c * 20))
radiobtn7.place(x=550, y=10 + (c * 20))
self.dictLabel[item] = L, radiobtn5, radiobtn7
c = c+1
if __name__ == "__main__":
app = Application()
app.geometry("700x250")
app.mainloop()
It is because the deselected item is not removed from self.dictRadioButtonValue.
Create a copy of self.dictRadioButtonValue and then remove the items that are used from the copy. At the end, remove remaining items in copy from self.dictRadioButtonValue:
def createRadioButton(self, evt):
index = self.list.curselection() # grab the index
c = 1
if len(index) == 0 or len(self.dictLabel) != 0:
for e in self.dictLabel:
self.dictLabel[e][0].place_forget()
self.dictLabel[e][1].place_forget()
self.dictLabel[e][2].place_forget()
del self.dictLabel
self.dictLabel = dict()
copy = self.dictRadioButtonValue.copy()
for i in index:
item = self.list.get(i)
if not item in self.dictRadioButtonValue:
# new item selected
radioButtonValue = tk.IntVar(value=' ')
self.dictRadioButtonValue[item] = radioButtonValue
else:
# remove current item from copy
del copy[item]
L = tk.Label(self, text=f"Number selected is {item}")
radiobtn5 = tk.Radiobutton(self, text="Yes", variable = self.dictRadioButtonValue[item], value = 5)
radiobtn7 = tk.Radiobutton(self, text="No", variable = self.dictRadioButtonValue[item], value = 6)
L.place(x=350, y=10+(c * 20))
radiobtn5.place(x=500, y=10 + (c * 20))
radiobtn7.place(x=550, y=10 + (c * 20))
self.dictLabel[item] = L, radiobtn5, radiobtn7
c = c+1
# remove remaining items in copy from self.dictRadioButtonValue
for item in copy:
del self.dictRadioButtonValue[item]

Adding headers to existing table created in tkinter python

I recently tried to create a table in tkinter. I luckily managed to find a pieces of code that works fine for me, however, I would need to add headers to this table. Do you have any suggestions how to incorporate it into the code I found here on forums?:
import tkinter as tk
class SimpleTableInput(tk.Frame):
def __init__(self, parent, rows, columns):
tk.Frame.__init__(self, parent)
self._entry = {}
self.rows = rows
self.columns = columns
# register a command to use for validation
vcmd = (self.register(self._validate), "%P")
# create the table of widgets
for row in range(self.rows):
for column in range(self.columns):
index = (row, column)
e = tk.Entry(self, validate="key", validatecommand=vcmd)
e.grid(row=row, column=column, stick="nsew")
self._entry[index] = e
# adjust column weights so they all expand equally
for column in range(self.columns):
self.grid_columnconfigure(column, weight=1)
# designate a final, empty row to fill up any extra space
self.grid_rowconfigure(rows, weight=1)
def get(self):
'''Return a list of lists, containing the data in the table'''
result = []
for row in range(self.rows):
current_row = []
for column in range(self.columns):
index = (row, column)
current_row.append(self._entry[index].get())
result.append(current_row)
return result
def _validate(self, P):
'''Perform input validation.
Allow only an empty value, or a value that can be converted to a float
'''
if P.strip() == "":
return True
try:
f = float(P)
except ValueError:
self.bell()
return False
return True
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.table = SimpleTableInput(self, 3, 4)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.table.pack(side="top", fill="both", expand=True)
self.submit.pack(side="bottom")
def on_submit(self):
print(self.table.get())
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
I have tried the following but it clearly is not what I am after:
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
names = ["header1", "header2", "header3"]
self.label = tk.Label(self, text=names)
self.table = SimpleTableInput(self, 3, 4)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.label.pack(side="top")
self.table.pack(side="top", fill="both", expand=True)
self.submit.pack(side="bottom")
As stated below, I would like to have headers that dynamically appears above the table. Any help is appreciated. Thanks
Adding a label before the table will add the table header.
Update this portion of the code:
self.label = tk.Label(self, text="Table Header")
self.table = SimpleTableInput(self, 3, 4)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.label.pack(side="top")
self.table.pack(fill="both", expand=True)
self.submit.pack(side="bottom")
I have not repeated the given code again to let you think about it. Here is a reference label in TKinter you might find handy.

How to obtain all available Entry values

I have created a pop up that would ask for entry, and the amount of entries would depend on the information given.
self.e = Entry(self.top, bd = 5)
self.e.grid(column = 1, row = 0)
row = 2
for d in extra:
self.e2 = Entry(self.top, bd = 5)
self.e2.grid(column = 1, row = row)
row = row + 1
def ok(self):
new = self.e.get().strip()
Function ok would be called by a button and then it would return the values. How do I return a list of values from an unknown amount of entries?
Python 2.7
Normally, you would put the entries in a list:
from Tkinter import *
class App(object):
def __init__(self, top):
self.top = top
self.ok_button = Button(self.top, text='OK', command=self.ok)
self.make_entries()
def make_entries(self):
self.entries = []
for d in extra:
e2 = Entry(self.top, bd = 5)
e2.grid(column = 1, row = row)
self.entries.append(e2)
row += 1
def ok(self):
values = [e.get().strip() for e in self.entries]
root = Tk()
app = App(root)
root.mainloop()

Making a grid of Entry boxes in Tkinter in a loop

I want to make a grid of entry boxes that I can edit and save to a text file somewhere else, but every time I run my code, If I call the variable "e", I can only edit the last box that was made.
from Tkinter import *
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
self.TXTlist = open('txtlist.txt', 'r+')
self.row = self.TXTlist.readline()
self.row = self.row.rstrip('\n')
self.row = self.row.replace('characters = ', "") #should end up being "6"
self.columns = self.TXTlist.readline()
self.columns = self.columns.rstrip('\n')
self.columns = self.columns.replace('columns = ', "") #should end up being "9"
i = 0
x = 0
for i in range (int(self.row)):
for x in range (int(self.columns)):
sroot = str('row' + str(i) + 'column' + str(x))
e = Entry(self, width=15)
e.grid(row = i, column = x, padx = 5, pady = 5, sticky = W)
e.delete(0, END)
e.insert(0, (sroot))
x = x + 1
x = 0
i = i + 1
root = Tk()
root.title("Longevity")
root.geometry("450x250")
app = Application(root)
root.mainloop()
I would store the entries in some sort of data structure to have easy access to them later. a list of lists would work nicely for this:
self.entries = []
for i in range (int(self.row)):
self.entries.append([])
for x in range (int(self.columns)):
...
e = Entry(self, width=15)
self.entries[-1].append(e)
...
Now you have a reference to the entry box:
self.entries[row_idx][col_idx]
And you can modify it however you want.

Categories