如何使用单个tkinter函数比较一堆这样的成对条目对两个条目的输入?

时间:2019-06-02 08:43:39

标签: python tkinter tkinter-entry

我想验证两个tkinter条目。一个称为最小值,另一个称为最大值。当然,我要确保最小值不超过最大值。还有第三个条目,称为增量,必须小于最大值。我正在尝试验证一组15个此类条目。

我尝试使用for循环并跟踪每个条目的textvariable。但是在for循环中,我只能验证单个输入框。另外,当我跳过对名为txtCab的特定条目的验证时,它会引发以下异常:如果我对所有小部件都进行了验证,则它确实可以工作,但有时会失败。

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\beejb\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1705, in __call__
    return self.func(*args)
  File "C:\PROSAIL_5B_Fortran\PROSAIL_5B_FORTRAN\PROSAIL.py", line 191, in min_max
    minVar = eval("self.txtVar_"+ str(wid)+ "_min.get()")
  File "<string>", line 1, in <module>
NameError: name 'self' is not defined

我使用的验证功能是:

def min_max(*args):
            alltextFields = ["N","Cab","Car","Cw","Cm","Cbrown", "rsoil0","LIDFa","LIDFb","TypeLIDF","LAI","hspot","tts","tto","psi" ]
            for wid in alltextFields:
                if eval("self." + wid + "_variable.get()"):
                    minVar = eval("self.txtVar_"+ str(wid)+ "_min.get()")
                    maxVar = eval("self.txtVar_"+ str(wid) + "_max.get()")
                    rangeVar = eval("self.txtVar_"+ str(wid) + "_range.get()")
##
##            print((minVar))
##            print((maxVar))
##            print((rangeVar))

            if len(minVar) > 0 and len(maxVar):
                if (minVar) > (maxVar):
                    messagebox.showinfo("Input Error", "Minimum should not be greater than maximum")

            if len(rangeVar) > 0 and len(maxVar) > 0:
                if (rangeVar) > (maxVar) :
                    messagebox.showinfo("Input Error", "Increment cannot exceed maximum limit")

##            print(self.txtVar_Cab_min.get()); print(self.txtVar_Cab_max.get());
##            print(self.txtVar_N_min.get()); print(self.txtVar_N_max.get());
            if len(self.txtVar_Cab_min.get()) > 0 and len(self.txtVar_Cab_max.get()) > 0 and len(self.txtVar_Cab_range.get()) > 0:
                if (self.txtVar_Cab_min.get()) > (self.txtVar_Cab_max.get()):
                    messagebox.showinfo("Input Data Error", "Minimum should not be greater than maximum!!")
                if (self.txtVar_Cab_range.get()) > (self.txtVar_Cab_max.get()):
                    messagebox.showinfo("Error", "Increment cannot exceed maximum!!")

我尝试过的另一个验证功能是:

    def validateMRM(self,value, text,W):
        vMin,vMax,vRange;
        entry = self.controller.nametowidget(W)
        print(entry)
        if entry == self.txt_N_min:
            print(entry.get())
            print(self.txtVar_N_max.get())
            print(self.txtVar_N_range.get())
        alltextFields = ["txt_N","txt_Cab","txt_Car","txt_Cab","txt_Cw","txt_Cw","txt_Cm","txt_Cbrown","txt_Cm", "txt_rsoil0",
                                    "txt_LIDFa","txt_LIDFb","txt_TypeLIDF","txt_LAI","txt_hspot","txt_hspot","txt_tts","txt_tto","txt_psi"
                                ]
        for wid in alltextFields:
            typeOfVar = wid.split("_")

            if entry == eval("self.txt_" + str(typeOfVar[1])+ "_min"):
                vMin = eval("self.txtVar_" + str(typeOfVar[1])+ "_min.get()")
                print(eval("self.txtVar_" + str(typeOfVar[1])+ "_min.get()"))
                vMax = eval("self.txtVar_" + str(typeOfVar[1])+ "_max.get()")
                print(eval("self.txtVar_" + str(typeOfVar[1])+ "_max.get()"))
                vRange = eval("self.txtVar_" + str(typeOfVar[1])+ "_range.get()")
                print(eval("self.txtVar_" + str(typeOfVar[1])+ "_range.get()"))

        print(vMin); print(vMax); print(vRange)

        if len(vMin) > 0 and len(vMax) > 0 and len(vRange) > 0:
            if (vMin) > (vMax):
                messagebox.showinfo("Error", "Minimum cannot be greater than maximum")
            if (vRange) > (vMax) :
                messagebox.showinfo("Error", "Increment cannot exceed the maximum limit")        
        print(len(entry.get()))
        if len(entry.get())>2:

这是所有条目的创建方式:

 self.lbl_N = tk.Label(self,text="Structure Coefficient(N)",anchor="w",width=40,bg='white'); self.lbl_N.grid(row=3,column=4,padx=4,pady=4);
        self.N_variable = tk.BooleanVar()
        self.chk_N = tk.Checkbutton(self,variable=self.N_variable, command=lambda:self.show_hide()); self.chk_N.grid(row=3,column=6,padx=4,pady=4);
        self.txt_N = tk.Entry(self,width=10,validate = 'key', validatecommand = vcmd); self.txt_N.grid(row=3,column=7,padx=4,pady=4);

        self.txtVar_N_min = tk.StringVar(); self.txtVar_N_max = tk.StringVar(); self.txtVar_N_range = tk.StringVar();
        self.txtVar_N_min.trace("w", min_max); self.txtVar_N_max.trace("w", min_max); self.txtVar_N_range.trace("w", min_max);

        self.txt_N_min = tk.Entry(self,width=5,validate = 'key',textvariable=self.txtVar_N_min, validatecommand = vcmd_min_max);
        self.txt_N_max = tk.Entry(self,width=5,validate = 'key', textvariable=self.txtVar_N_max,validatecommand = vcmd_min_max);
        self.txt_N_range = tk.Entry(self,width=5,validate = 'key', textvariable=self.txtVar_N_range,validatecommand = vcmd_min_max); 

一共有14个这样的条目,我需要对其进行验证。

但是这些都没有给出我想要的实际输出。它有时会工作,而有时会失败。 我不确定为什么会这样,我花了很多时间进行验证。

2 个答案:

答案 0 :(得分:0)

我不确定这是否能回答您的问题,但这应该为您指明正确的方向。

我对您的代码不太了解。我制作了一个15行x 4列的网格。 第4列是一则消息,指示其旁边的3个字段为“确定”,否则显示为问题。验证在每个按键的整个网格上运行。如果太慢,可以使用验证按钮启动验证。

import tkinter as tk
from tkinter import ttk

def rec(): return {'lo': 0, 'hi': 0, 'step': 0, 'ok': '' }

root = tk.Tk()
root.title('SO Question')

def entry(id, ent_dict, var_dict, v=0):
    """ Add an Entry Widget to the root, with associated StringVar."""
    var_dict[id] = tk.StringVar()
    var_dict[id].set(str(v))
    ent_dict[id] = ttk.Entry(root, textvariable= var_dict[id], width = 10 )
    return ent_dict[id]

def do_validate(lo, hi, step):
    """ Return OK if lo, hi and step are consistent else an error string. """
    if lo < hi and step < hi: return 'OK'
    txt = ''
    if lo >= hi: 
        txt = 'lo >= hi. ' 
    if step >= hi:
        txt += 'step >= hi.'
    return txt 

def conv(txt):
    """ Convert text to float.  Return 0.0 if not valid float e.g "" or 'a' """
    try: 
        return float(txt)
    except ValueError:
        return 0.0 

def oklabel(ent_dict, var_dict):
    """ Add an OK Label to a row. """
    lo = conv(var_dict['lo'].get())
    hi = conv(var_dict['hi'].get())
    step = conv(var_dict['step'].get())
    var_dict['ok'] = tk.StringVar()
    var_dict['ok'].set(do_validate(lo, hi, step))
    ent_dict['ok'] = ttk.Label(root, textvariable = var_dict['ok'], width = -17)
    return ent_dict['ok']  # Return the Label object for gridding. 

def do_check(*args):
    """ Loop through the rows setting the validation string in each one. """ 
    for var_dict in stringvars:
        lo = conv(var_dict['lo'].get())
        hi = conv(var_dict['hi'].get())
        step = conv(var_dict['step'].get())
        var_dict['ok'].set(do_validate(lo, hi, step))

# Add column labels
ttk.Label(root, text='Minimums').grid(row=0, column=0)
ttk.Label(root, text =' Maximums').grid(row=0, column=1)
ttk.Label(root, text='Increment').grid(row=0, column=2)
ttk.Label(root, text='Valid').grid(row=0, column=3)

# Create containers for he Entries and Stringvars
entries =[]
stringvars = []

# Add 15 rows of Entries / Validation Labels to the UI. 
for row in range(1, 16):
    tempe=rec()
    tempv=rec()       
    entry('lo', tempe, tempv, 0).grid(row = row, column=0)
    entry('hi', tempe, tempv, 0).grid(row = row, column=1)
    entry('step', tempe, tempv, 0).grid(row = row, column=2)
    oklabel(tempe, tempv).grid(row = row, column = 3)
    entries.append(tempe)
    stringvars.append(tempv)

# Bind do_check to all Entry widgets.
root.bind_class('TEntry', '<KeyPress>', do_check, add='+')
root.bind_class('TEntry', '<BackSpace>', do_check, add='+')
root.bind_class('TEntry', '<Delete>', do_check, add='+')

root.mainloop()

过去,我一直在尝试通过不允许不一致的条目来验证多个字段。用户很难按照要求更正字段。他们必须以正确的顺序工作。例如lo = 100,hi = 9,step =1。UI是否应允许删除100中的最后一个零,而保留gt 9是10?

只有在所有行都正确的情况下,才能将其扩展为激活“下一步”按钮。

编辑1-对评论的回复

这具有创建和激活显示的每一行的功能。每行都有自己的变量和检查功能。它们是由三个Entry StringVars上的跟踪触发的,无需使用验证。

import tkinter as tk
from tkinter import ttk

def to_float(txt):
    """ Safely convert any string to a float.  Invalid strings return 0.0 """ 
    try:
        return float(txt)
    except ValueError: 
        return 0.0

def row_n( parent, n, init_show = 0 ):
    """ Create one row of the display. """
    # tk.Variables
    v_show = tk.IntVar()
    v_min = tk.StringVar()
    v_max = tk.StringVar()
    v_incr = tk.StringVar()
    v_message = tk.StringVar()

    # Initialise variables
    v_min.set('0')
    v_max.set('1')
    v_incr.set('1')  # Can the increment be zero?
    v_show.set(init_show)
    v_message.set("OK")

    def do_trace(*args):
        """ Runs every time any of the three Entries change value. 
            Sets the message to the appropriate text.
        """
        lo = to_float(v_min.get())
        hi = to_float(v_max.get())
        inc = to_float(v_incr.get())
        if lo < hi and inc <=hi:
            v_message.set('OK')
        else:
            txt = ''
            if lo >= hi: 
                txt += 'Min >= Max'
            if inc > hi:
                if len(txt): txt += ' & '
                txt += 'Incr > Max'
            v_message.set(txt)

    # Set trace callback for changes to the three StringVars
    v_min.trace('w', do_trace)
    v_max.trace('w', do_trace)
    v_incr.trace('w', do_trace)

    def activation(*args):
        """ Runs when the tickbox changes state """
        if v_show.get():
            e_min.grid(row = n, column = 1)
            e_max.grid(row = n, column = 2)
            e_inc.grid(row = n, column = 3)
            message.grid(row = n, column = 4)
        else:
            e_min.grid_remove()
            e_max.grid_remove()
            e_inc.grid_remove()
            message.grid_remove()

    tk.Checkbutton(parent, 
        text = 'Structure Coefficient {} :'.format(n), 
        variable = v_show, command = activation ).grid(row = n, column = 0)
    e_min = tk.Entry(parent, width=5, textvariable = v_min)
    e_max =tk.Entry(parent, width=5, textvariable = v_max)
    e_inc = tk.Entry(parent, width=5, textvariable = v_incr)
    message = tk.Label(parent, width=-15, textvariable = v_message)
    activation()

    return { 'Min': v_min, 'Max': v_max, 'Inc': v_incr }

def show_results():
    print('Min   Max  Inc')
    for row in rows:
        res = '{}   {}  {}'.format(row['Min'].get(), row['Max'].get(), row['Inc'].get())
        print( res )

root = tk.Tk()
root.title('SO Question')

ttk.Label(root, text='Minimums').grid(row=0, column=1)
ttk.Label(root, text =' Maximums').grid(row=0, column=2)
ttk.Label(root, text='Step', width = 5 ).grid(row=0, column=3)
ttk.Label(root, text='Valid', width = 15 ).grid(row=0, column=4)

rows = []
for r in range(1,16):
    rows.append(row_n(root, r, init_show=r%3 == 0 ))

tk.Button(root, command=show_results, text = '  Show Results  ').grid(column=1, pady = 5)

root.mainloop()

这是另一种方法。这有帮助吗?

答案 1 :(得分:0)

这是另一个建议。将标签和条目合并到第n行功能中。在激活功能中包括激活/禁用条目。通过所需的描述列表循环执行row_n函数。

import tkinter as tk

row_names = [ "Structure Coefficient(N)", "Chlorophyll Content(Cab) (µg.cm-2)", 
              "Carotenoid content(Car) (µg.cm-2)", "Brown pigment content(Cbrown)(arbitrary units)"]

def row_n(parent, desc, n, init_show = 0 ):
    """ Create one row of the display. """
    # tk.Variables
    v_show = tk.IntVar()
    v_min = tk.StringVar()
    v_max = tk.StringVar()
    v_incr = tk.StringVar()
    v_fixed = tk.StringVar() # New StringVar
    v_message = tk.StringVar()
    v_show.set(init_show)
    v_message.set("OK")

    def do_trace(*args):
        """ Runs every time any of the three Entries change value. 
            Sets the message to the appropriate text.
        """
        lo = to_float(v_min.get())
        hi = to_float(v_max.get())
        inc = to_float(v_incr.get())
        if lo < hi and inc <=hi:
            v_message.set('OK')
        else:
            txt = ''
            if lo >= hi: 
                txt += 'Min >= Max'
            if inc > hi:
                if len(txt): txt += ' & '
                txt += 'Incr > Max'
            v_message.set(txt)

    # Set trace callback for changes to the three StringVars
    v_min.trace('w', do_trace)
    v_max.trace('w', do_trace)
    v_incr.trace('w', do_trace)

    def activation(*args):
        """ Runs when the tickbox changes state """
        if v_show.get():
            e_min.grid(row = n, column = 8)
            e_max.grid(row = n, column = 9)
            e_inc.grid(row = n, column = 10)
            message.grid(row = n, column = 11)
            e_fixed.config(state = 'disabled') # Disable the base Entry

        else:
            e_min.grid_remove()
            e_max.grid_remove()
            e_inc.grid_remove()
            message.grid_remove()
            e_fixed.config(state = 'normal') # Enable the base Entry Widget

    tk.Label(parent, text = desc ).grid(row = r+1, column = 4 ) # Add the desc. Label
    e_fixed = tk.Entry(parent, textvariable = v_fixed) # Add the new Entry widget
    e_fixed.grid(row = r+1, column = 5)
    tk.Checkbutton(parent, 
        text = ' '.format(n), 
        variable = v_show, command = activation ).grid(row = n, column = 6)
    e_min = tk.Entry(parent, width=5, textvariable = v_min)
    e_min.config(font=('Candara', 15))
    e_max =tk.Entry(parent, width=5, textvariable = v_max)
    e_max.config(font=('Candara', 15))
    e_inc = tk.Entry(parent, width=5, textvariable = v_incr)
    e_inc.config(font=('Candara', 15))
    message = tk.Label(parent, width=-15, textvariable = v_message)
    message.config(font=('Candara', 15))
    activation()

    return { 'Min': v_min, 'Max': v_max, 'Inc': v_incr, 'Fixed': v_fixed }
    #  The 'Fixed' field added to the dictionary to return

def print_row(row):
    fmt = 'Min: {}, Max: {}, Inc: {}, Fixed: {}'
    print(fmt.format(
        row['Min'].get(), row['Max'].get(), row['Inc'].get(), row['Fixed'].get() 
    ))

def to_float(txt):

    """ Safely convert any string to a float.  Invalid strings return 0.0 """ 
    try:
        return float(txt)
    except ValueError: 
        return 0.0

# GUI Start
root = tk.Tk()
root.title('Validation wth Trace')

# Header Labels
tk.Label(root,text="Min").grid(row=0,column=8,padx=4,pady=4)
tk.Label(root,text="Max").grid(row=0,column=9,padx=4,pady=4)
tk.Label(root,text="Inc").grid(row=0,column=10,padx=4,pady=4)

# Body of rows
rows = []
for r, r_text in enumerate(row_names):
    rows.append(row_n( root, r_text, r+1))

root.mainloop()

print("Strings in the Entry fields")
for r, row in enumerate(rows):
    print('Row: ', r, 'Data:', end=' ')
    print_row(row)

HTH。在墨水问题中查看代码,您可能希望将row_n设为类。