Search code examples
python-3.xclasstkintersubclass

Issue with Entry subclass in tkinter/python3.9 and duplication of user input


first post but long time listener...

I'm new to tkinter and am running into some issues. Main one that has me scratching my head is that when I enter a value into one of the 4 input boxes then they duplicate the value in the other 4 boxes. Buttons are working OK, and I need to clean things up but not sure if this is a valid approach:

Also the frames are wrong and I plan to add comments

I removed some extraneous lines but got the same results. Added self to the text variable in the Entry object.

import tkinter as tk
from tkinter import  *
from tkinter import ttk

root = Tk()
s = ttk.Style()
s.configure('Danger.TFrame', background='red', borderwidth=5, relief='sunken')
frm = ttk.Frame(root, padding=200, style='Danger.TFrame').grid()

#Fix needed for inputs duplicating.

input_frame = ttk.Frame(frm,padding=10).grid(row=1)

output_frame = ttk.Frame(frm,padding=10).grid(row=16,column=0)

class single_frame(tk.Frame): #intent is to have repeatable frame with name, abreviation, and unit
    def __init__(self, parent, name, abreviation, unit,row_adjust) -> None:
        self.n = name
        self.u = unit
        self.a = abreviation
        
        ttk.Frame(parent, width = 50, height = 50,style='Danger.TFrame').grid()
        ttk.Label(parent,text=self.n).grid(column=0, row=row_adjust)
        ttk.Label(parent,text=self.a).grid(column=1,row=row_adjust)
        ttk.Label(parent,text=self.u).grid(column=3,row=row_adjust)
        pass


class fxinput(single_frame):  #setting up a class to handle input boxes
    def __init__(self,parent,name,abreviation,unit,row_adjust) -> None:
        super().__init__(parent,name,abreviation,unit,row_adjust)
        self.v = float()
        self.e = ttk.Entry(parent, textvariable=self.v)
        #self.e.insert(tk.END,"1")
        self.e.grid(column=2,row=row_adjust)
        pass

class fxoutput(single_frame):  #setting up a class to handle input boxes
    def __init__(self,parent,name,abreviation,unit,out,row_adjust) -> None:
        super().__init__(parent,name,abreviation,unit,row_adjust)
        self.out = out
        self.l = ttk.Label(parent, text=self.out)
        self.l.grid(column=2,row=row_adjust)
        pass
    def update(self,value):
        self.l["text"]=value

inV = fxinput(input_frame,'Ideal Gas','v','L*atm/mol*K',1)
inn = fxinput(input_frame,'Ideal Gas','n','L*atm/mol*K',2)
inT = fxinput(input_frame,'Ideal Gas','T','L*atm/mol*K',3)
inR = fxinput(input_frame,'Ideal Gas','R','L*atm/mol*K',4)
out1 = fxoutput(output_frame,'Pressure','P','atm',1, 5)



ttk.Button(frm, text="Quit!", command=root.destroy).grid(column=10,row=0)

def calculate():
    try:

        v = float(inV.e.get())
        n = float(inn.e.get())
        R = float(inR.e.get())
        T = float(inT.e.get())

        print(v)
        p = n*R*T/v
        out1.update(p)
        print(out1.out)
    except:
        print("error")
        return

calc_button = ttk.Button(frm, text="Calculate", default="active", command=calculate)
calc_button.grid(column=20,row =20)
#root.bind('<Return>', lambda e: calc_button.invoke())
root.mainloop()



Solution

  • Main one that has me scratching my head is that when I enter a value into one of the 4 input boxes then they duplicate the value in the other 4 boxes.

    That instantly tells me that all four share the same textvariable.

    When you assign a value to the textvariable attribute it needs to be one of the tkinter variable objects (StringVar, IntVar, etc). The string value of the objects name is associated with a variable internal to the embedded tcl interpreter. The name of each instance will be unique, giving you unique variables in the tcl interpreter.

    However, in your case you're passing in the value of float(). float() returns 0.0, so all of your widgets are going to be associated with a variable with the internal name 0.0.

    The simple fix is to use a proper tkinter variable rather than a float. For example, DoubleVar:

    class fxinput(single_frame):  #setting up a class to handle input boxes
        def __init__(self,parent,name,abreviation,unit,row_adjust) -> None:
            ...
            self.v = tk.DoubleVar()
            self.e = ttk.Entry(parent, textvariable=self.v)
            ...
    

    In some commented out code it appears you intend to initialize the value to 1. You can do that when creating the variable:

    self.v = tk.DoubleVar(value=1.0)