Python类对象变量访问失败:'has no attribute' 报错解决
解决Python类中变量访问报错:Control has no attribute setTemp
无法访问Control类中创建的变量,调用时始终报错‘Control has no attribute setTemp’。哪怕在__init__方法外声明变量也不行,不清楚Python类里怎么正确声明变量并在后续方法中使用。
以下是原代码:
from tkinter import * from tkinter import ttk from tkinter import messagebox from vivTimer import * import RPi.GPIO as GPO import threading import time import board import adafruit_dht dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False) class Control(object): setHumid = 95 timer = 5000 def __init__(self, master): setTemp = StringVar() setTemp.set('85') #set default max temp and humidity master.title('Vivarium Control') master.resizable(False, False) master.configure(background = '#e6e6e6') self.style = ttk.Style() self.style.configure('TFrame', background = '#e6e6e6') self.style.configure('TButton', background = '#e1d8b9') self.style.configure('TLabel', background = '#e6e6e6', font = ('Arial', 11)) self.style.configure('Header.TLabel', font = ('Arial', 18, 'bold')) self.frame_header = ttk.Frame(master) self.frame_header.pack() self.logo = PhotoImage(file = '115500-200.png') ttk.Label(self.frame_header, image = self.logo).grid(row = 0, column = 0, rowspan = 2) ttk.Label(self.frame_header, text = 'Vivarium Controller', style = 'Header.TLabel').grid(row = 0, column = 1) ttk.Label(self.frame_header, wraplength = 300, text = ("Temperature and Humidity control. " "Enter maximums for both humidity and temperature.")).grid(row = 1, column = 1, padx = 5, sticky = 'nw') self.frame_content = ttk.Frame(master) self.frame_content.pack() ttk.Label(self.frame_content, text = 'Maximum Temp:').grid(row = 0, column = 0, padx = 5, sticky = 'sw') ttk.Label(self.frame_content, text = 'Maximum Humidity:').grid(row = 0, column = 1, padx = 5, sticky = 'sw') self.setTempL = Label(self.frame_content, textvariable = setTemp).grid(row = 0, column = 3, padx = 5, sticky = 'sw') ttk.Label(self.frame_content, text = 'Current Humidity:' + str(self.setHumid)).grid(row = 0, column = 4, padx = 5, sticky = 'sw') self.entry_temp = ttk.Entry(self.frame_content, textvariable = setTemp, width = 10, font = ('Arial', 10)) self.entry_humid = ttk.Entry(self.frame_content, width = 10, font = ('Arial', 10)) self.entry_temp.grid(row = 1, column = 0, padx = 5) self.entry_humid.grid(row = 1, column = 1, padx = 5) ttk.Button(self.frame_content, text = 'Save', command = self.save).grid(row = 4, column = 0, padx = 5, pady = 5, sticky = 'e') def updateLabels(self): print('blah') def createTimer(root): print('timer 1 sec') readTemp = False while readTemp == False: try: # Print the values to the serial port temperature_c = dhtDevice.temperature temperature_f = temperature_c * (9 / 5) + 32 humidity = dhtDevice.humidity print( "Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format( temperature_f, temperature_c, humidity ) ) readTemp = True if(temperature_f > int(Control.setTemp.get())): print('fan on') except RuntimeError as error: # Errors happen fairly often, DHT's are hard to read, just keep going print(error.args[0]) time.sleep(2.0) continue except Exception as error: dhtDevice.exit() raise error time.sleep(2.0) print(Control.timer) root.after(Control.timer, Control.createTimer, root) #t2 = threading.Thread(target=VivTimer.startTimer(), args=(10,)) #t2.start() def save(self): tempT = self.entry_temp.get() self.setTemp = tempT print(self.setTemp) self.setHumid = self.entry_humid.get() Control.updateLabels(self) print('Temp: {}'.format(self.entry_temp.get())) print('Humidity: {}'.format(self.entry_humid.get())) self.clear() messagebox.showinfo(title = 'Vivarium Control', message = 'Conditions saved!') def clear(self): self.entry_temp.delete(0, 'end') self.entry_humid.delete(0, 'end') def main(): root = Tk() control = Control(root) root.after(1000, Control.createTimer, root) root.mainloop() if __name__ == "__main__": main()
问题根源与修复方案
1. 变量作用域混淆
__init__里的setTemp = StringVar()是局部变量,只能在__init__内部使用,后续方法无法访问;save方法里的self.setTemp = tempT是把它变成实例变量,但createTimer里错误地用Control.setTemp.get()访问类变量,而类本身并没有定义setTemp这个类属性,所以报错。
2. 修正变量声明
在__init__里把setTemp定义为实例变量,所有需要访问它的地方都用self.setTemp:
def __init__(self, master): self.setTemp = StringVar() # 改成实例变量 self.setTemp.set('85')
同时调整标签和输入框的绑定:
self.setTempL = Label(self.frame_content, textvariable = self.setTemp).grid(row = 0, column = 3, padx = 5, sticky = 'sw') self.entry_temp = ttk.Entry(self.frame_content, textvariable = self.setTemp, width = 10, font = ('Arial', 10))
3. 调整createTimer方法
把createTimer改成实例方法,接收self参数,访问实例变量而非类变量:
def createTimer(self): print('timer 1 sec') readTemp = False while readTemp == False: try: temperature_c = dhtDevice.temperature temperature_f = temperature_c * (9 / 5) + 32 humidity = dhtDevice.humidity print( "Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format( temperature_f, temperature_c, humidity ) ) readTemp = True # 访问实例的setTemp变量 if temperature_f > int(self.setTemp.get()): print('fan on') except RuntimeError as error: print(error.args[0]) time.sleep(2.0) continue except Exception as error: dhtDevice.exit() raise error time.sleep(2.0) print(self.timer) # 访问实例的timer属性(如果需要实例独立值)或者Control.timer(类共享值) # 用self.master访问主窗口,所以需要在__init__里保存master为实例属性 self.master.after(self.timer, self.createTimer)
同时在__init__里添加self.master = master,方便后续方法访问主窗口。
4. 修正main函数中的调用
把root.after(1000, Control.createTimer, root)改成:
root.after(1000, control.createTimer)
5. 其他小问题
save方法里的Control.updateLabels(self)改成self.updateLabels(),直接调用实例方法即可;另外save里不要直接给self.setTemp赋值,而是用self.setTemp.set(tempT)更新StringVar的值。
修正后的完整代码
from tkinter import * from tkinter import ttk from tkinter import messagebox from vivTimer import * import RPi.GPIO as GPO import threading import time import board import adafruit_dht dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False) class Control(object): setHumid = 95 timer = 5000 def __init__(self, master): self.master = master # 保存主窗口为实例属性 self.setTemp = StringVar() self.setTemp.set('85') #set default max temp and humidity master.title('Vivarium Control') master.resizable(False, False) master.configure(background = '#e6e6e6') self.style = ttk.Style() self.style.configure('TFrame', background = '#e6e6e6') self.style.configure('TButton', background = '#e1d8b9') self.style.configure('TLabel', background = '#e6e6e6', font = ('Arial', 11)) self.style.configure('Header.TLabel', font = ('Arial', 18, 'bold')) self.frame_header = ttk.Frame(master) self.frame_header.pack() self.logo = PhotoImage(file = '115500-200.png') ttk.Label(self.frame_header, image = self.logo).grid(row = 0, column = 0, rowspan = 2) ttk.Label(self.frame_header, text = 'Vivarium Controller', style = 'Header.TLabel').grid(row = 0, column = 1) ttk.Label(self.frame_header, wraplength = 300, text = ("Temperature and Humidity control. " "Enter maximums for both humidity and temperature.")).grid(row = 1, column = 1, padx = 5, sticky = 'nw') self.frame_content = ttk.Frame(master) self.frame_content.pack() ttk.Label(self.frame_content, text = 'Maximum Temp:').grid(row = 0, column = 0, padx = 5, sticky = 'sw') ttk.Label(self.frame_content, text = 'Maximum Humidity:').grid(row = 0, column = 1, padx = 5, sticky = 'sw') self.setTempL = Label(self.frame_content, textvariable = self.setTemp).grid(row = 0, column = 3, padx = 5, sticky = 'sw') # 把湿度标签改成实例属性,方便后续更新 self.setHumidLabel = ttk.Label(self.frame_content, text = 'Current Humidity:' + str(self.setHumid)) self.setHumidLabel.grid(row = 0, column = 4, padx = 5, sticky = 'sw') self.entry_temp = ttk.Entry(self.frame_content, textvariable = self.setTemp, width = 10, font = ('Arial', 10)) self.entry_humid = ttk.Entry(self.frame_content, width = 10, font = ('Arial', 10)) self.entry_temp.grid(row = 1, column = 0, padx = 5) self.entry_humid.grid(row = 1, column = 1, padx = 5) ttk.Button(self.frame_content, text = 'Save', command = self.save).grid(row = 4, column = 0, padx = 5, pady = 5, sticky = 'e') def updateLabels(self): # 更新湿度显示标签 self.setHumidLabel.config(text=f'Current Humidity:{self.setHumid}') print('labels updated') def createTimer(self): print('timer 1 sec') readTemp = False while readTemp == False: try: temperature_c = dhtDevice.temperature temperature_f = temperature_c * (9 / 5) + 32 humidity = dhtDevice.humidity print( "Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format( temperature_f, temperature_c, humidity ) ) readTemp = True if temperature_f > int(self.setTemp.get()): print('fan on') except RuntimeError as error: print(error.args[0]) time.sleep(2.0) continue except Exception as error: dhtDevice.exit() raise error time.sleep(2.0) print(self.timer) self.master.after(self.timer, self.createTimer) def save(self): tempT = self.entry_temp.get() self.setTemp.set(tempT) # 更新StringVar的值 print(self.setTemp.get()) self.setHumid = self.entry_humid.get() self.updateLabels() print('Temp: {}'.format(self.entry_temp.get())) print('Humidity: {}'.format(self.entry_humid.get())) self.clear() messagebox.showinfo(title = 'Vivarium Control', message = 'Conditions saved!') def clear(self): self.entry_temp.delete(0, 'end') self.entry_humid.delete(0, 'end') def main(): root = Tk() control = Control(root) root.after(1000, control.createTimer) root.mainloop() if __name__ == "__main__": main()
内容的提问来源于stack exchange,提问作者cawdeen
相关产品推荐
相关产品推荐

