首页 > 解决方案 > 如何使用单个 tkinter 函数比较两个条目的输入以获取一堆这样的条目对?

问题描述

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

我尝试使用 for 循环并跟踪每个条目的文本变量。但是在 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); 

有一组十四个这样的条目,我需要验证它们中的每一个。

但是这些都没有给出我想要的实际输出。它有时会起作用,有时会失败。我不确定为什么会发生这种情况,并且我花了很多时间进行此验证。

标签: pythontkintertkinter-entry

解决方案


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

我无法理解您的代码。我制作了一个 15 行 x 4 列的网格。第 4 列是一条消息,它旁边的 3 个字段是“OK”或者如果不是则表示问题。对于每个按键,验证在整个网格上运行。如果这太慢,验证按钮可以启动验证。

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 中的最后一个零,留下 10,即 gt 9?

这可以扩展为仅在所有行都正常时才激活“下一步”按钮。

编辑 1 - 回复评论

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

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()

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


推荐阅读