0

Explanation: On Clicking submit button, the entries should appear in tree.

I have restricted the entries fields so they can take only two values (using trace method).

Problem 1: I have a total of 8 entries, which means the code has 8 StringVar, 8 entry, 8 label, 1 button and a total of 17 grids. Please help me to reduce the code.

Problem 2: I need entries in one list which I can use globally.

from tkinter import Tk, Frame, Button, Label, Entry, ttk, StringVar, Scrollbar
import datetime

# Main Window
class WINDOW(Tk):
    def __init__(self, master):
        Tk.__init__(self, master)
        self.master = master

        frame1 = Frame1(self)
        frame1.grid(row=0,column=0)
        list_of_entries=[]

class Frame1(Frame):
    def __init__(self, master):
        Frame.__init__(self, master,height=master.winfo_screenheight(),
                       width=master.winfo_screenwidth())
        self.master = master


        one_var = StringVar()
        two_var = StringVar()
        three_var = StringVar()
        four_var = StringVar()
        five_var = StringVar()

        # Restrict entry field for 2 values only (using trace)
        one_var.trace("w", lambda name, index, mode, one_var=one_var: callback())
        two_var.trace("w", lambda name, index, mode, two_var=one_var: callback())
        three_var.trace("w", lambda name, index, mode, three_var=one_var: callback())
        four_var.trace("w", lambda name, index, mode, four_var=one_var: callback())
        five_var.trace("w", lambda name, index, mode, five_var=one_var: callback())

        def callback(*args):
            one_var.set(one_var.get()[:2])
            two_var.set(two_var.get()[:2])
            three_var.set(three_var.get()[:2])
            four_var.set(four_var.get()[:2])
            five_var.set(five_var.get()[:2])


        # Request frame labels

        DATA0_lbl = Label(self, text='DATA0', font=('calibre', 10, 'bold'))
        DATA1_lbl = Label(self, text='DATA1', font=('calibre', 10, 'bold'))
        DATA2_lbl = Label(self, text='DATA2', font=('calibre', 10, 'bold'))
        DATA3_lbl = Label(self, text='DATA3', font=('calibre', 10, 'bold'))
        DATA4_lbl = Label(self, text='DATA4', font=('calibre', 10, 'bold'))

        # Request frame label grid
        DATA0_lbl.grid(row=0, column=0)
        DATA1_lbl.grid(row=0, column=1)
        DATA2_lbl.grid(row=0, column=2)
        DATA3_lbl.grid(row=0, column=3)
        DATA4_lbl.grid(row=0, column=4)

        # Request frame entry fields
        DATA0_entry = Entry(self,textvariable=one_var, width=10, font=('calibre', 10, 'normal'))
        DATA1_entry = Entry(self,textvariable=two_var,  width=10, font=('calibre', 10, 'normal'))
        DATA2_entry = Entry(self, textvariable=three_var, width=10, font=('calibre', 10, 'normal'))
        DATA3_entry = Entry(self,textvariable=four_var,  width=10, font=('calibre', 10, 'normal'))
        DATA4_entry = Entry(self, textvariable=five_var, width=10, font=('calibre', 10, 'normal'))

        # Request frame entry field grid
        DATA0_entry.grid(row=1, column=0)
        DATA1_entry.grid(row=1, column=1)
        DATA2_entry.grid(row=1, column=2)
        DATA3_entry.grid(row=1, column=3)
        DATA4_entry.grid(row=1, column=4)



        # Log data sheet
        NewTree = ttk.Treeview(self, height=23, columns=("DATA0","DATA1", "DATA2", "DATA3","DATA4"))

        NewTree.column("#0", width=180)
        NewTree.column("#1", width=150)
        NewTree.column("#2", width=150)
        NewTree.column("#3", width=150)
        NewTree.column("#4", width=150)
        NewTree.column("#5", width=150)

        NewTree.heading("#0",text='TIME')
        NewTree.heading("#1",text='DATA0')
        NewTree.heading("#2",text='DATA1')
        NewTree.heading("#3",text='DATA2')
        NewTree.heading("#4",text="DATA3")
        NewTree.heading("#5", text="DATA4")

        NewTree.grid(row=5, columnspan=4)

        def insert_data():
            NewTree.insert('', 'end', text=datetime.datetime.now(),
                                 values=(DATA0_entry.get(),
                                         DATA1_entry.get(),
                                         DATA2_entry.get(),
                                         DATA3_entry.get(),
                                         DATA4_entry.get()))
            one_var.set("")
            two_var.set("")
            three_var.set("")
            four_var.set("")
            five_var.set("")

        submit_button = Button(self, text="SUBMIT", command=insert_data)
        submit_button.grid(row=3, column=4)


root = WINDOW(None)
root.geometry(f'{root.winfo_screenwidth()}x{root.winfo_screenheight()}')
root.title("ADD DATA")


root.mainloop()
costaparas
  • 5,047
  • 11
  • 16
  • 26

2 Answers2

0

You can use a loop to create the entries and labels. By using the validation feature of the entry widgets rather than the trace, you can throw away the trace statements and the instances of StringVar completely.

All combined, this will end up reducing the code by about 50 lines or so.

For a detailed explanation of entry validation, see Interactively validating Entry widget content in tkinter

Here's a complete example:

from tkinter import Tk, Frame, Button, Label, Entry, ttk, StringVar, Scrollbar
import datetime

# Main Window
class WINDOW(Tk):
    def __init__(self, master):
        Tk.__init__(self, master)
        self.master = master

        frame1 = Frame1(self)
        frame1.grid(row=0,column=0)
        list_of_entries=[]

class Frame1(Frame):
    def __init__(self, master):
        Frame.__init__(self, master,height=master.winfo_screenheight(),
                       width=master.winfo_screenwidth())
        self.master = master

        vcmd = (self.register(self.validate), '%P')
        self.entries = []
        for i in range(5):
            label = Label(self, text=f"DATA{i}", font=('calibre', 10, 'bold'))
            entry = Entry(self, validatecommand=vcmd, validate='key', width=10, font=('calibre', 10, 'normal'))
            label.grid(row=0, column=i)
            entry.grid(row=1, column=i)
            self.entries.append(entry)

        # Log data sheet
        NewTree = ttk.Treeview(self, height=23, columns=("DATA0","DATA1", "DATA2", "DATA3","DATA4"))

        NewTree.column("#0", width=180)
        NewTree.column("#1", width=150)
        NewTree.column("#2", width=150)
        NewTree.column("#3", width=150)
        NewTree.column("#4", width=150)
        NewTree.column("#5", width=150)

        NewTree.heading("#0",text='TIME')
        NewTree.heading("#1",text='DATA0')
        NewTree.heading("#2",text='DATA1')
        NewTree.heading("#3",text='DATA2')
        NewTree.heading("#4",text="DATA3")
        NewTree.heading("#5", text="DATA4")

        NewTree.grid(row=5, columnspan=4)

        def insert_data():
            NewTree.insert('', 'end', text=datetime.datetime.now(),
                                 values=(self.entries[0].get(),
                                         self.entries[1].get(),
                                         self.entries[2].get(),
                                         self.entries[3].get(),
                                         self.entries[4].get()))
            for i in range(5):
                self.entries[i].delete(0, 'end')

        submit_button = Button(self, text="SUBMIT", command=insert_data)
        submit_button.grid(row=3, column=4)

    def validate(self, new_value):
        return len(new_value) <= 2


root = WINDOW(None)
root.geometry(f'{root.winfo_screenwidth()}x{root.winfo_screenheight()}')
root.title("ADD DATA")


root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you so much . it works. but im not getting the values of entry fileds in list. because i need to check that the entry value is hex or not before inserting data into tree – namrata patted Feb 04 '21 at 06:06
  • is it possible to restrict entry filed that it can take only hex value, only binary using validate method?? – namrata patted Feb 04 '21 at 10:23
  • @namratapatted: yes, that is possible. You can add any checks you want in the validate function. – Bryan Oakley Feb 04 '21 at 15:06
  • what do u mean by checks. I didn't get that. – namrata patted Feb 05 '21 at 06:21
  • The `validate` function is performing a check, and returning True or False depending on the length of the characters. You can return True or False depending on any other check you want to perform. For example, you could check that the length is less than or equal to two and also that it contains only the characters 0-9 and a-f. – Bryan Oakley Feb 05 '21 at 14:47
0

To start with, you can reduce your code entirely by just using a for loop and you can discard the use of StringVar and instead use validation with tkinter instead, take a look here:

from tkinter import Tk, Frame, Button, Label, Entry, ttk, StringVar, Scrollbar
import datetime

# Main Window
class WINDOW(Tk):
    def __init__(self, master):
        Tk.__init__(self, master)
        self.master = master

        frame1 = Frame1(self)
        frame1.grid(row=0,column=0)
        list_of_entries=[]

class Frame1(Frame):
    def __init__(self, master):
        Frame.__init__(self, master,height=master.winfo_screenheight(),
                       width=master.winfo_screenwidth())
        self.master = master 
        vcmd = self.register(self.validate) # Register the validation function
        
        NewTree = ttk.Treeview(self, height=23, columns=("DATA0","DATA1", "DATA2", "DATA3","DATA4"))
        NewTree.grid(row=5, columnspan=4)
        
        NewTree.column("#0", width=180) # These column would stand out of the loop
        NewTree.heading("#0",text='TIME') # So manually inserting them

        MAX_WIDGETS = 5
        base_text = 'DATA' # Base text, so later you can add 1 to make it DATA1 and so on..
        lbls = [] # Empty list for label to append to later, not necessary if you dont need to change or reuse them later
        self.entries = []
        for i in range(MAX_WIDGETS):
            NewTree.column(f"#{str(i+1)}", width=150) # i+1 because you have an extra column already
            NewTree.heading(f"#{str(i+1)}",text=base_text+str(i))
            
            lbls.append(Label(self, text=base_text+str(i), font=('calibre', 10, 'bold')))
            lbls[i].grid(row=0, column=i)

            self.entries.append(Entry(self, width=10, font=('calibre', 10, 'normal'),validate='key',validatecommand=(vcmd,'%P')))
            self.entries[i].grid(row=1, column=i)

        submit_button = Button(self, text="SUBMIT", command=lambda: self.insert_data(NewTree))
        submit_button.grid(row=3, column=4)

    def insert_data(self,tree):
        data = [x.get() for x in self.entries]
        tree.insert('', 'end', text=datetime.datetime.now(),
                                values=data)
        [x.delete(0,'end') for x in self.entries] # Just to delete the items, list is otherwise of no use.

    def validate(self,inp): # The validation function
        return len(inp) <= 2

root = WINDOW(None)

root.geometry(f'{root.winfo_screenwidth()}x{root.winfo_screenheight()}')
root.title("ADD DATA")

root.mainloop()

I have shortened the code where ever possible, by using for. Sometimes its not possible to keep the stuff inside the range of loop, for example your extra TIME column, so then I have put it outside the loop. And since you are using classes take maximum advantage of it, instead of defining functions, define methods and so on. Your previous version was about 120 lines roughly, now its reduced to about 60.

You could also further use more LC(List Comprehension), but I think it might reduce the code readability as it is an important factor to keep in mind, while writing code.

You can get rid of appending labels to a list, if you don't plan to reuse them, it will save up 1 extra line ;)

To understand validation better, check this out: Interactively validating Entry widget content in tkinter

Delrius Euphoria
  • 14,910
  • 3
  • 15
  • 46
  • thank you so much for your time. but its not inserting the data. its helpful to reduce the code. specially tree part – namrata patted Feb 04 '21 at 06:08
  • @namratapatted What do you mean it's not inserting the data. Remember all the boxes have to be filled for the data to be inserted. If you wish to not use this, then remove the `if` inside of `insert_data` – Delrius Euphoria Feb 04 '21 at 14:22