2013-09-06 8 views
0

В приведенном ниже коде мне удалось частично проверить данные, введенные в виджет ввода self.e2, к сожалению, если виджет ввода пуст и нажата кнопка «Отправить», ValueError сгенерирован «ValueError: недействительный литерал для int() с базой 10: ''" Я хотел бы, чтобы программа распознала, что виджет ввода e2 пуст, привяжите ValueError и верните фокус обратно в виджет ввода.Как уловить ValueError, сгенерированный из пустого виджета ввода в tkinter

Я попытался сделать это с использованием методов 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() 
+1

Я не вижу в нем 'try/except', что вы обычно использовали для обработки ошибки такого типа. Есть ли причина, по которой вы этого не используете? – kindall

+1

Кроме того, пожалуйста, не просто описывайте ошибку; опубликовать фактическую трассировку. Как написано, нам нужно прорыть целую кучу кода, чтобы угадать, где вы можете получить эту ошибку. – abarnert

+0

Кроме того, было бы намного легче помочь отладить ваш код, если вы предоставили нам [SSCCE] (http://sscce.org), усеченную версию, которая не имеет неактуального кода, внешние зависимости от файлов GIF , и т.д. – abarnert

ответ

2

Проследим, что происходит, когда вы нажимаете «Отправить».

Первое:

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) 

конечно, вы, вероятно, хотите, чтобы это сделать, прежде чем все разрушительный материал (и т. д.).


Кроме того, если вы хотите позвонить 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 совершенно не нужна - int(txt) уже обрабатывает пустую строку так же, как обрабатывает нечисловую строку.

И то, как вы его подключили, тоже должно работать.

Однако функция проверки правильности запускается при каждом редактировании виджета ввода, а не при нажатии другого случайного виджета в другом месте. Например, если вы пытаетесь ввести буквы в запись, они должны стереть их, как только вы сможете ввести их. (Обратите внимание, что если вы наберете 123a456 вы в конечном итоге с 456, не 123456, который может или не может быть то, что вы хотите ...)

Итак, вы сделали это правильно, но это не то, что вы хотели сделать ,