You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 05:24:08