0

在下面的代码中,我设法部分验证了输入到 self.e2 条目小部件中的数据,不幸的是,如果条目小部件为空并且按下了提交按钮,则会生成 ValueError”ValueError: invalid literal for int() with base 10: '' " 我想让程序识别 e2 条目小部件为空,捕获 ValueError 并将焦点返回到条目小部件。

我试图使用 is_valid_int 和 invalid_int 方法来做到这一点,但这不起作用。

from tkinter import *
from tkinter import ttk
from tkinter.scrolledtext import *

class DailyOrderGUI:
    def __init__(self, parent):
        #Data entring frame
        self.frame = Frame(parent, bg = "grey")
        self.frame.grid(row=0)
        self.label1 = Label(self.frame, text = "Mrs CackleBerry's Egg Ordering System", wraplength = 200, bg="grey", font=("Comic Sans MS", "14", "bold"))
        self.label1.grid(row = 0, columnspan = 3, padx = 5, pady = 5)
        self.superegg_img = PhotoImage(file = "images/superegg.gif")
        self.label5 = Label(self.frame,  bg="grey", image = self.superegg_img)
        self.label5.grid(row = 0, column= 3, padx = 5, pady = 5)
        self.v = StringVar()
        self.v.set("Monday")
        self.label2 = Label(self.frame, text = "Which day are you ordering for?", bg="grey", font=("Arial", "12", "bold"))
        self.label2.grid(row = 1, columnspan = 4, sticky = W)
        self.rb1 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Monday", text = "Monday")
        self.rb1.grid(row = 2, column = 0, sticky = W)
        self.rb2 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Tuesday", text = "Tuesday")
        self.rb2.grid(row = 2, column = 1, sticky = W)
        self.rb3 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Wednesday", text = "Wednesday")
        self.rb3.grid(row = 2, column = 2, sticky = W)
        self.rb4 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Thursday", text = "Thursday")
        self.rb4.grid(row = 2, column = 3, sticky = W)
        self.rb5 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Friday", text = "Friday")
        self.rb5.grid(row = 3, column = 0, sticky = W)
        self.rb6 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Saturday", text = "Saturday")
        self.rb6.grid(row = 3, column = 1, sticky = W)
        self.rb7 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Sunday", text = "Sunday")
        self.rb7.grid(row = 3, column = 2, sticky = W)
        self.label3 = Label(self.frame, text = "Customer's Name:?(Press \"Orders Complete\" to finish)", bg="grey", font=("Arial", "12", "bold"))
        self.label3.grid(row = 4, columnspan = 4,padx = 5, sticky = W)
        self.e1 = Entry(self.frame, width = 30)
        self.e1.grid(row = 5, columnspan = 4, sticky = W, padx = 5, pady=3)
        self.e1.focus()
        self.label4 = Label(self.frame, text = "How many eggs being ordered:?", bg="grey", font=("Arial", "12", "bold"))
        self.label4.grid(row = 6, columnspan = 4,padx = 5,sticky = W)

        integer = self.create_integer_widget()

        self.btn1 = Button(self.frame, text = "Submit")
        self.btn1.grid(row = 8, padx = 5, pady = 3, sticky = E+W)
        self.btn1.bind("<Button-1>", self.get_orders)
        self.btn2 = Button(self.frame, text = "Orders Complete", command = self.show_summary_result)
        self.btn2.grid(row = 8, column = 3, padx = 5, pady = 3, sticky = E+W)

        #Summary Frame
        self.summ_frame = Frame(parent, bg = "grey")
        self.summ_frame.grid(row=0)
        self.summ_label1 = Label(self.summ_frame, text = "Mrs CackleBerry's Egg Ordering System", bg="grey", font=("Comic Sans MS", "14", "bold"))
        self.summ_label1.grid(row = 0, columnspan = 4, padx = 5, pady = 5)
        self.scrolled_display = ScrolledText(self.summ_frame, width = 50, height = 10, bg="thistle", font=("Times New Roman", "12"))
        self.scrolled_display.grid(row = 1, columnspan = 2, padx = 5, pady = 20, sticky = W)
        self.data_entry_btn = Button(self.summ_frame, text = "Back to Data Entry", command = self.show_data_entry_frame)
        self.data_entry_btn.grid(row = 2, column = 0, sticky = SE, padx = 5, pady = 20)

        self.egg_orders=[]

        self.show_data_entry_frame()

    def create_integer_widget(self):
        self.e2 = ttk.Entry(self.frame, width = 10, validate='key')
        self.e2['validatecommand'] = (self.frame.register(self.is_valid_int), '%P')
        self.e2['invalidcommand'] = (self.frame.register(self.invalid_int), '%W')
        self.e2.grid(row = 7, columnspan = 4, sticky = W, padx = 5, pady=3)
        self.e2.bind("<Return>", self.get_orders)
        return self.e2

    def is_valid_int(self, txt):
        # txt - value in %P
        if not txt:         # do not accept empty string
            return False

        try:
            int(txt)
            return True     # accept integer

        except ValueError:  # not an integer
            return False

    def invalid_int(self, widgetName):
        # called automatically when the
        # validation command returns 'False'

        # get entry widget

        widget = self.frame.nametowidget(widgetName)

        # clear entry
        widget.delete(0, END)

        # return focus to integer entry
        widget.focus_set()
        widget.bell()

    def show_data_entry_frame(self):
        self.summ_frame.grid_remove()
        self.frame.grid()
        root.update_idletasks()

    def show_summary_result(self):
        self.frame.grid_remove()
        self.summ_frame.grid()
        root.update_idletasks()
        self.scrolled_display.delete('1.0', END)
        if len(self.egg_orders) == 0:
            self.scrolled_display.insert(END, "No Orders")
        else:
            total = 0
            self.scrolled_display.insert(END, "Orders for " + self.v.get() + "\n")
            for i in range(len(self.egg_orders)):
                total += self.egg_orders[i].num_eggs
                self.scrolled_display.insert(END, str(self.egg_orders[i]) + "\n")
            self.scrolled_display.insert(END, "" + "\n")
            self.scrolled_display.insert(END, "Summary for " + self.v.get() + "\n")
            self.scrolled_display.insert(END, "" + "\n")
            self.scrolled_display.insert(END, "Total eggs: " + str(total) + "\n")
            self.scrolled_display.insert(END, "Dozens required: " + str(self.get_dozens(total)) + "\n")
            average = 0
            if len(self.egg_orders) > 0:
                average = total / len(self.egg_orders)
            self.scrolled_display.insert(END, "Average number of eggs per customer: {0:.1f}".format(average) + "\n")

    def get_orders(self, event):
        """
        Collects order information - name, number of eggs in a loop
        """
        self.name = self.e1.get()
        self.no_eggs = self.e2.get()
        self.e1.delete(0, END)
        self.e2.delete(0, END)
        self.e1.focus()
        self.egg_orders.append(EggOrder(self.name, self.no_eggs))

    def get_dozens (self, total):
        """
        returns whole number of dozens required to meet required number of eggs
        """
        num_dozens = total//12
        if total%12 != 0:
            num_dozens += 1
        return num_dozens

class EggOrder:

    price_per_doz = 6.5
    def __init__(self, name, num_eggs):
        self.name = name
        self.num_eggs = int(num_eggs)


    def calc_price(self):
        self.price = EggOrder.price_per_doz/12 * self.num_eggs
        return self.price

    def __str__(self):
        return("{} ordered {} eggs. The price is ${:.2f}".format(self.name, self.num_eggs , self.calc_price()))



#main routine
if __name__== "__main__":
    root = Tk()
    root.title("Mrs Cackleberry's Egg Ordering Program")
    frames = DailyOrderGUI(root)
    root.mainloop()
4

1 回答 1

3

让我们追溯一下单击“提交”时发生的情况。

第一的:

self.btn1 = Button(self.frame, text = "Submit")
self.btn1.grid(row = 8, padx = 5, pady = 3, sticky = E+W)
self.btn1.bind("<Button-1>", self.get_orders)

所以,它调用self.get_orders. 该函数的最后一行是:

self.no_eggs = self.e2.get()
# ...
self.egg_orders.append(EggOrder(self.name, self.no_eggs))

EggOrder.__init__函数内部你有这个:

self.num_eggs = int(num_eggs)

这大概就是错误发生的地方。

(请注意,如果您发布了回溯而不仅仅是错误字符串,那么所有这些工作都是不必要的。)


所以,当self.e2.get()返回一个空字符串时,你最终会调用int(''),这会引发一个ValueError.

除非您想尝试提前检查这种可能性(这很少是一个好主意),否则您将需要try:/except ValueError:某处。问题是,在哪里?嗯,这取决于你想做什么。

如果你想创建一个空的EggOrder,你会在里面做EggOrder.__init__

try:
    self.num_eggs = int(num_eggs)
except ValueError:
    self.num_eggs = 0

另一方面,如果你不想创建一个EggOrder,你会在里面做self.get_orders

try:
    order = EggOrder(self.name, self.no_eggs)
except ValueError:
    # pop up an error message, log something, call self.invalid_int, whatever
else:
    self.egg_orders.append(order)

当然,您可能想在所有破坏性的东西(等)之前执行此操作。self.e1.delete(0, END)


此外,如果您想按invalid_int原样调用,则需要将self.e2小部件的名称传递给它。由于您没有给它一个,它将是动态且不可预测的,例如.1234567890,您必须向小部件询问,以便您可以通过该名称查看小部件。将核心功能分解出来会更简单,如下所示:

def invalid_int(self, widgetName):
    widget = self.frame.nametowidget(widgetName)
    self.handle_invalid_int(widget)
def handle_invalid_int(self, widget):
    widget.delete(0, END)
    # etc.

然后你就可以打电话了handle_invalid_int(self.e2)


同时,您尝试添加验证函数来检查有效的 int 输入。

is_valid_int应该可以正常工作。但是,这if not txt部分是完全没有必要的——<code>int(txt) 已经像处理非数字字符串一样处理空字符串。

而且您连接它的方式也应该有效。

但是,验证功能会在每次编辑 Entry 小部件时运行,而不是在您单击其他地方的其他随机小部件时运行。例如,如果您尝试在条目中输入字母,它应该会在您输入后立即将其删除。(请注意,如果您键入123a456,您最终会得到456, not 123456,这可能是也可能不是您想要的……)

所以,你做对了,但这不是你想做的。

于 2013-09-06T19:39:26.737 回答