我得到了一个 AttributeError - Python GUI Programming with Tkinter 作者:Alan D Moore



大家好,

我是Python和编码的新手。我还在阅读、熟悉和实践书中的代码。我想请您帮助我如何调试这个错误。请帮助我,让我明白我错在哪里。

我真的抄了书里的代码。这本书是Alan D. Moore的《Python GUI Programming with Tkinter》。但我得到了attributeerror。我从他的github下载了他的源代码并尝试了它(它有效!),但我的没有。

这是我的re-type代码

from datetime import datetime
import os
import csv
import tkinter as tk
from tkinter import ttk

# Start coding here
# A widget containing a label and input together
class LabelInput(tk.Frame):
def __init__(self, parent, label='', input_class=ttk.Entry,
input_var=None, input_args=None, label_args=None,
**kwargs):
super().__init__(parent, **kwargs)
input_args = input_args or {}
label_args = label_args or {}
self.variable = input_var
if input_class in (ttk.Checkbutton, ttk.Button, ttk.Radiobutton):
input_args["text"] = label
input_args["variable"] = input_var
else:
self.label = ttk.Label(self, text=label, **label_args)
self.label.grid(row=0, column=0, sticky=(tk.W + tk.E))
input_args["textvariable"] = input_var
self.input = input_class(self, **input_args)
self.input.grid(row=1, column=0, sticky=(tk.W + tk.E))
self.columnconfigure(0, weight=1)
def grid(self, sticky=(tk.E + tk.W), **kwargs):
super().grid(sticky=sticky, **kwargs)
def get(self):
try:
if self.variable:
return self.variable.get()
elif type(self.input) == tk.Text:
return self.input.get('1.0', tk.END)
else:
return self.input.get()
except (TypeError, tk.TclError):
# happens when numeric fields are empty.
return ''
def set(self, value, *args, **kwargs):
if type(self.variable) == tk.BooleanVar:
self.variable.set(bool(value))
elif self.variable:
self.variable.set(value, *args, **kwargs)
elif type(self.input) in (ttk.Checkbutton, ttk.Radiobutton):
if value:
self.input.select()
else:
self.input.deselect()
elif type(self.input) == tk.Text:
self.input.delete('1.0', tk.END)
self.input.insert(*'1.0', value)
else: # input must be an Entry-type widget with no variable
self.input.delete(0, tk.END)
self.input.insert(0, value)
class DataRecordForm(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.inputs = {}
recordinfo = tk.LabelFrame(self, text="Record Information")
self.inputs['Date'] = LabelInput(recordinfo, "Date", 
input_var=tk.StringVar())
self.inputs['Date'].grid(row=0, column=0)
self.inputs['Time'] = LabelInput(recordinfo, "Time", 
input_class=ttk.Combobox, input_var=tk.StringVar(),
input_args={"values": ["8:00", "12:00", "16:00", "20:00"]})
self.inputs['Time'].grid(row=0, column=1)
self.inputs['Technician'] = LabelInput(recordinfo, "Technician", 
input_var=tk.StringVar())
self.inputs['Technician'].grid(row=0, column=2)
# Line 2
self.inputs['Lab'] = LabelInput(recordinfo, "Lab", 
input_class=ttk.Combobox, input_var=tk.StringVar(),
input_args={"values": ["A", "B", "C", "D", "E"]})
self.inputs['Lab'].grid(row=1, column=0)
self.inputs['Plot'] = LabelInput(recordinfo, "Plot",
input_class=ttk.Combobox, input_var=tk.IntVar(),
input_args={"values": list(range(1,21))})
self.inputs['Plot'].grid(row=1, column=1)
self.inputs['Seed Sample'] = LabelInput(recordinfo, "Seed Sample",
input_var=tk.StringVar())
self.inputs['Seed Sample'].grid(row=1, column=2)
recordinfo.grid(row=0, column=0, sticky=(tk.W + tk.E))
# Environment Data
environmentinfo = tk.LabelFrame(self, text="Environment Data")
self.inputs['Humidity'] = LabelInput(environmentinfo, "Humidity (g/m³)",
input_class=ttk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0.5, "to": 52.0, "increment": .01})
self.inputs['Humidity'].grid(row=0, column=0)       
self.inputs['Light'] = LabelInput(environmentinfo, "Light (klx)",
input_class=ttk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 100, "increment": .01})
self.inputs['Light'].grid(row=0, column=1)
self.inputs['Temperature'] = LabelInput(environmentinfo, "Temperature(°C))",
input_class=ttk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 4, "to": 40, "increment": .01})
self.inputs['Temperature'].grid(row=0, column=2)
self.inputs['Equipment Fault'] = LabelInput(environmentinfo, "Equipment Fault",
input_class=ttk.Checkbutton, input_var=tk.BooleanVar())
self.inputs['Equipment Fault'].grid(row=1, column=0, columnspan=3)
environmentinfo.grid(row=1, column=0, sticky=(tk.W + tk.E))
# Plant Data
plantinfo = tk.LabelFrame(self, text="Plant Data")
self.inputs['Plants'] = LabelInput(plantinfo, "Plants",
input_class=tk.Spinbox, input_var=tk.IntVar(),
input_args={"from_": 0, "to": 20})
self.inputs['Plants'].grid(row=0, column=0)
self.inputs['Blossoms'] = LabelInput(plantinfo, "Blossoms",
input_class=tk.Spinbox, input_var=tk.IntVar(),
input_args={"from_": 0, "to": 1000})
self.inputs['Blossoms'].grid(row=0, column=1)
self.inputs['Fruit'] = LabelInput(plantinfo, "Fruit",
input_class=tk.Spinbox, input_var=tk.IntVar(),
input_args={"from_": 0, "to": 1000})
self.inputs['Fruit'].grid(row=0, column=2)      
self.inputs['Min Height'] = LabelInput(plantinfo, "Min Height(cm)",
input_class=tk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 1000, "increment": .01})
self.inputs['Min Height'].grid(row=1, column=0)     
self.inputs['Max Height'] = LabelInput(plantinfo, "Max Height(cm)",
input_class=tk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 1000, "increment": .01})
self.inputs['Max Height'].grid(row=1, column=0) 
self.inputs['Median Height'] = LabelInput(plantinfo, "Median Height(cm)",
input_class=tk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 1000, "increment": .01})
self.inputs['Median Height'].grid(row=1, column=0)  
plantinfo.grid(row=2, column=0, sticky=(tk.W + tk.E))
# Notes Section
self.inputs['Notes'] = LabelInput(self, "Notes", input_class=tk.Text,
input_args={"width": 50, "height": 10})
self.inputs['Notes'].grid(sticky=tk.W, row=3, column=0)
self.reset()
def get(self):
data = {}
for key, widget in self.inputs.items():
data[key] = widget.get()
return data
def reset(self):
for widget in self.inputs.values():
widget.set('')
class Application(tk.Tk):
"""Application root window"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title("ABC Data Entry Application")
self.resizable(0,0)
ttk.Label(self, text="ABC Data Entry Application", 
font=("TkDefaultFont", 16)).grid(row=0)
self.recordform = DataRecordForm(self)
self.recordform.grid(row=1, padx=10)
self.savebutton = ttk.Button(self, text="Save", command=self.on_save)
self.savebutton.grid(sticky=tk.E, row=2, padx=10)
# Status Bar
self.status = tk.StringVar()
self.statusbar = ttk.Label(self, textvar=self.status)
self.statusbar.grid(sticky=(tk.W + tk.E), row=3, padx=10)
self.records_saved = 0

def on_save(self):
datestring = datetime.today().strftime("%Y-%m-%d")
filename = "abc_data_record_{}.csv".format(datestring)
newfile = not os.path.exists(filename)
data = self.recordform.get()
with open(filename, 'a') as fh:
csvwriter = csv.DictWriter(fn, fieldnames=data.keys())
if newfile:
csvwriter.writeheader()
csvwriter.writerow(data)
self.records_saved += 1
self.status.set("{} records saved this session".format(self.records_saved))
self.recordform.reset()
if __name__ == "__main__":
app = Application()
app.mainloop()

应该可以:

from datetime import datetime
import os
import csv
import tkinter as tk
from tkinter import ttk

# Start coding here
# A widget containing a label and input together
class LabelInput(tk.Frame):
def __init__(self, parent, label='', input_class=ttk.Entry,
input_var=None, input_args=None, label_args=None,
**kwargs):
super().__init__(parent, **kwargs)
input_args = input_args or {}
label_args = label_args or {}
self.variable = input_var
if input_class in (ttk.Checkbutton, ttk.Button, ttk.Radiobutton):
input_args["text"] = label
input_args["variable"] = input_var
else:
self.label = ttk.Label(self, text=label, **label_args)
self.label.grid(row=0, column=0, sticky=(tk.W + tk.E))
input_args["textvariable"] = input_var
self.input = input_class(self, **input_args)
self.input.grid(row=1, column=0, sticky=(tk.W + tk.E))
self.columnconfigure(0, weight=1)
def grid(self, sticky=(tk.E + tk.W), **kwargs):
super().grid(sticky=sticky, **kwargs)
def get(self):
try:
if self.variable:
return self.variable.get()
elif type(self.input) == tk.Text:
return self.input.get('1.0', tk.END)
else:
return self.input.get()
except (TypeError, tk.TclError):
# happens when numeric fields are empty.
return ''
def set(self, value, *args, **kwargs):
if type(self.variable) == tk.BooleanVar:
self.variable.set(bool(value))
elif self.variable:
self.variable.set(value, *args, **kwargs)
elif type(self.input) in (ttk.Checkbutton, ttk.Radiobutton):
if value:
self.input.select()
else:
self.input.deselect()
elif type(self.input) == tk.Text:
self.input.delete('1.0', tk.END)
self.input.insert(*'1.0', value)
else: # input must be an Entry-type widget with no variable
self.input.delete(0, tk.END)
self.input.insert(0, value)
class DataRecordForm(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.inputs = {}
recordinfo = tk.LabelFrame(self, text="Record Information")
self.inputs['Date'] = LabelInput(recordinfo, "Date", 
input_var=tk.StringVar())
self.inputs['Date'].grid(row=0, column=0)
self.inputs['Time'] = LabelInput(recordinfo, "Time", 
input_class=ttk.Combobox, input_var=tk.StringVar(),
input_args={"values": ["8:00", "12:00", "16:00", "20:00"]})
self.inputs['Time'].grid(row=0, column=1)
self.inputs['Technician'] = LabelInput(recordinfo, "Technician", 
input_var=tk.StringVar())
self.inputs['Technician'].grid(row=0, column=2)
# Line 2
self.inputs['Lab'] = LabelInput(recordinfo, "Lab", 
input_class=ttk.Combobox, input_var=tk.StringVar(),
input_args={"values": ["A", "B", "C", "D", "E"]})
self.inputs['Lab'].grid(row=1, column=0)
self.inputs['Plot'] = LabelInput(recordinfo, "Plot",
input_class=ttk.Combobox, input_var=tk.IntVar(),
input_args={"values": list(range(1,21))})
self.inputs['Plot'].grid(row=1, column=1)
self.inputs['Seed Sample'] = LabelInput(recordinfo, "Seed Sample",
input_var=tk.StringVar())
self.inputs['Seed Sample'].grid(row=1, column=2)
recordinfo.grid(row=0, column=0, sticky=(tk.W + tk.E))
# Environment Data
environmentinfo = tk.LabelFrame(self, text="Environment Data")
self.inputs['Humidity'] = LabelInput(environmentinfo, "Humidity (g/m³)",
input_class=ttk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0.5, "to": 52.0, "increment": .01})
self.inputs['Humidity'].grid(row=0, column=0)       
self.inputs['Light'] = LabelInput(environmentinfo, "Light (klx)",
input_class=ttk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 100, "increment": .01})
self.inputs['Light'].grid(row=0, column=1)
self.inputs['Temperature'] = LabelInput(environmentinfo, "Temperature(°C))",
input_class=ttk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 4, "to": 40, "increment": .01})
self.inputs['Temperature'].grid(row=0, column=2)
self.inputs['Equipment Fault'] = LabelInput(environmentinfo, "Equipment Fault",
input_class=ttk.Checkbutton, input_var=tk.BooleanVar())
self.inputs['Equipment Fault'].grid(row=1, column=0, columnspan=3)
environmentinfo.grid(row=1, column=0, sticky=(tk.W + tk.E))
# Plant Data
plantinfo = tk.LabelFrame(self, text="Plant Data")
self.inputs['Plants'] = LabelInput(plantinfo, "Plants",
input_class=tk.Spinbox, input_var=tk.IntVar(),
input_args={"from_": 0, "to": 20})
self.inputs['Plants'].grid(row=0, column=0)
self.inputs['Blossoms'] = LabelInput(plantinfo, "Blossoms",
input_class=tk.Spinbox, input_var=tk.IntVar(),
input_args={"from_": 0, "to": 1000})
self.inputs['Blossoms'].grid(row=0, column=1)
self.inputs['Fruit'] = LabelInput(plantinfo, "Fruit",
input_class=tk.Spinbox, input_var=tk.IntVar(),
input_args={"from_": 0, "to": 1000})
self.inputs['Fruit'].grid(row=0, column=2)      
self.inputs['Min Height'] = LabelInput(plantinfo, "Min Height(cm)",
input_class=tk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 1000, "increment": .01})
self.inputs['Min Height'].grid(row=1, column=0)     
self.inputs['Max Height'] = LabelInput(plantinfo, "Max Height(cm)",
input_class=tk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 1000, "increment": .01})
self.inputs['Max Height'].grid(row=1, column=0) 
self.inputs['Median Height'] = LabelInput(plantinfo, "Median Height(cm)",
input_class=tk.Spinbox, input_var=tk.DoubleVar(),
input_args={"from_": 0, "to": 1000, "increment": .01})
self.inputs['Median Height'].grid(row=1, column=0)  
plantinfo.grid(row=2, column=0, sticky=(tk.W + tk.E))
# Notes Section
self.inputs['Notes'] = LabelInput(self, "Notes", input_class=tk.Text,
input_args={"width": 50, "height": 10})
self.inputs['Notes'].grid(sticky=tk.W, row=3, column=0)
self.reset()
def get(self):
data = {}
for key, widget in self.inputs.items():
data[key] = widget.get()
return data
def reset(self):
for widget in self.inputs.values():
widget.set('')
class Application(tk.Tk):
"""Application root window"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title("ABC Data Entry Application")
self.resizable(0,0)
ttk.Label(self, text="ABC Data Entry Application", 
font=("TkDefaultFont", 16)).grid(row=0)
self.recordform = DataRecordForm(self)
self.recordform.grid(row=1, padx=10)
self.savebutton = ttk.Button(self, text="Save", command=self.on_save)
self.savebutton.grid(sticky=tk.E, row=2, padx=10)
# Status Bar
self.status = tk.StringVar()
self.statusbar = ttk.Label(self, textvar=self.status)
self.statusbar.grid(sticky=(tk.W + tk.E), row=3, padx=10)
self.records_saved = 0

def on_save(self):
datestring = datetime.today().strftime("%Y-%m-%d")
filename = "abc_data_record_{}.csv".format(datestring)
newfile = not os.path.exists(filename)
data = self.recordform.get()
with open(filename, 'a') as fh:
csvwriter = csv.DictWriter(fn, fieldnames=data.keys())
if newfile:
csvwriter.writeheader()
csvwriter.writerow(data)
self.records_saved += 1
self.status.set("{} records saved this session".format(self.records_saved))
self.recordform.reset()
if __name__ == "__main__":
app = Application()
app.mainloop()

您的getreset方法缩进太多。

最新更新