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

Tkinter嵌套窗口变量未定义:NameError问题求助

问题描述

使用Python 3.11.3搭配Spyder开发基于Tkinter的机器控制GUI(类似简易日志系统)。在主窗口(Ventana principal)中点击「No aceptable」按钮(boton_flujo_2)会弹出Toplevel窗口「Ventana 1」,需求是勾选该窗口内的「No necesario 2」时禁用窗口内的按钮。此逻辑在主窗口中可正常运行,但在嵌套窗口中触发错误:

NameError: name 'mensual_check_top1' is not defined

尝试设置全局变量也未解决问题。

原始代码
import tkinter as tk
from tkinter import ttk 

# 根据条件禁用控件
def disable_widgets():
    # 若Valve Status的"0 (cero)"被勾选,禁用下拉菜单
    if cero_check.get() == 1:
        dropdown.config(state='disabled')
    else:
        dropdown.config(state='readonly')

    # 若"5lpm"被勾选,禁用流量输入框
    if flow_check.instate(['selected']):
        flow_entry.config(state='disabled')
    else:
        flow_entry.config(state='normal')
        
    # 若CONTROL MENSUAL的"no necesario"被勾选,禁用对应按钮
    if control_mensual_check.instate(['selected']):
        boton_flujo_1.config(state='disabled')
        boton_flujo_2.config(state='disabled')
    else:
        boton_flujo_1.config(state='normal')
        boton_flujo_2.config(state='normal')
         
    # 若Ventana 1的"No necesario 2"被勾选,禁用对应按钮
    if mensual_check_top1.instate(['selected']):
        boton_flujo_3.config(state='disabled')
        boton_flujo_4.config(state='disabled')
    else:
        boton_flujo_3.config(state='normal')
        boton_flujo_4.config(state='normal')
        
# 限制Status输入仅为数字
def validate_numeric_input(new_value):
    return new_value.isnumeric()

# 创建Ventana 1窗口
def realizar_calibracion():
    ventana1 = tk.Toplevel(root)
    ventana1.geometry("300x100")
    ventana1.title("Ventana 1")

    verif_flujo_mensual = ttk.Label(ventana1, text="Verificacion de flujo")
    verif_flujo_mensual.grid(row=0, column=1)
    
    mensual_check_top1 = ttk.Checkbutton(ventana1, text="No necesario 2", command=disable_widgets)
    mensual_check_top1.grid(row=2, column=0)

    boton_flujo_3 = ttk.Button(ventana1, text="Aceptable")
    boton_flujo_3.grid(row=2, column=1)

    boton_flujo_4 = ttk.Button(ventana1, text="No aceptable")
    boton_flujo_4.grid(row=2, column=2)
    

# 主窗口
root = tk.Tk()
root.title("Ventana Principal") 
root.geometry("800x600")

# OPERADOR区域
operador_label = ttk.Label(root, text="Operador")
operador_label.grid(row=0, column=0)
operador_entry = ttk.Entry(root)
operador_entry.grid(row=0, column=1)

# STATUS区域(仅允许数字输入)
status_label = ttk.Label(root, text="Status")
status_label.grid(row=1, column=0)
status_entry = ttk.Entry(root, validate="key")
status_entry['validatecommand'] = (status_entry.register(validate_numeric_input), '%P')
status_entry.grid(row=1, column=1)

# VALVE STATUS区域
valve_status_label = ttk.Label(root, text="Valve Status")
valve_status_label.grid(row=2, column=0)

# 勾选"0 (cero)"时禁用下拉菜单
cero_check = tk.IntVar()
cero_checkbox = ttk.Checkbutton(root, text="0 (cero)", variable=cero_check, command=disable_widgets)
cero_checkbox.grid(row=2, column=1)

# Valve Status非零选项下拉菜单
options_var = tk.StringVar()
options = [" Otro valor ","01011 : Derivación", "01100 : Calentamiento/Aire limpio", "00000 : Medida", "00010 : Calibración medidor de flujo"]
dropdown = ttk.OptionMenu(root, options_var, *options)
dropdown.grid(row=2, column=2)

# APARIENCIA DEL FILTRO区域
apariencia_filtro_label = ttk.Label(root, text="Apariencia Filtro")
apariencia_filtro_label.grid(row=3, column=0)

apariencia_filtro_options = ["APARIENCIA","Normal", "Achucharrado", "Marron"]
apariencia_filtro_var = tk.StringVar(value="")
apariencia_filtro_dropdown = ttk.OptionMenu(root, apariencia_filtro_var, *apariencia_filtro_options)
apariencia_filtro_dropdown.grid(row=3, column=1)

# FLUJO区域
flow_label = ttk.Label(root, text="Flujo")
flow_label.grid(row=4, column=0)
# 勾选"5lpm"时禁用输入框
flow_check = ttk.Checkbutton(root, text="5lpm", command=disable_widgets)
flow_check.grid(row=4, column=1)
# 流量输入框
flow_entry = ttk.Entry(root)
flow_entry.grid(row=4, column=2)

# CINTA REEMPLAZADA区域
replacement_tape_label = ttk.Label(root, text="Cinta reemplazada?")
replacement_tape_label.grid(row=5, column=0)

# 替换胶带勾选框
replacement_tape_check = ttk.Checkbutton(root, text="Si")
replacement_tape_check.grid(row=5, column=1)


# 分割线
division_label = ttk.Label(root, text="------------------------------")
division_label.grid(row=7, column=1)


# CHECKLIST GENERAL标题
general_label = ttk.Label(root, text="CHECKLIST GENERAL")
general_label.grid(row=8, column=1)


# CHECKLIST GENERAL区域
big_checkbox_var = tk.IntVar()
big_checkbox = ttk.Checkbutton(root, width=0)
big_checkbox.grid(row=9, column=1, rowspan=4)

# 检查当前时间与仪器时间
hora_label = ttk.Label(root, text="Chequeo hora actual + instrumento")
hora_label.grid(row=9, column=0)

# 检查管道和软管
mangueras_label = ttk.Label(root, text="Inspeccion caños y mangueras")
mangueras_label.grid(row=10, column=0)

# 检查内部水阱
aguainterna_label = ttk.Label(root, text="Inspeccion trampa de agua interna")
aguainterna_label.grid(row=11, column=0)

# 检查外部水阱
aguaexterna_label = ttk.Label(root, text="Inspeccion trampa de agua externa")
aguaexterna_label.grid(row=12, column=0)

# 分割线
division_label = ttk.Label(root, text="------------------------------")
division_label.grid(row=13, column=1)

# CONTROL MENSUAL标题
general_label = ttk.Label(root, text="CONTROL MENSUAL")
general_label.grid(row=14, column=1) 

# 流量验证标签
verif_flujo = ttk.Label(root, text="Verificacion de flujo")
verif_flujo.grid(row=15, column=0)

control_mensual_check = ttk.Checkbutton(root, text="No necesario", command=disable_widgets)
control_mensual_check.grid(row=15, column=1)

boton_flujo_1 = ttk.Button(root, text="Aceptable")
boton_flujo_1.grid(row=15, column=2)

boton_flujo_2 = ttk.Button(root, text="No aceptable",command=realizar_calibracion)
boton_flujo_2.grid(row=15, column=3)


root.mainloop()
解决方案

问题根源

  1. mensual_check_top1、boton_flujo_3、boton_flujo_4是realizar_calibracion函数的局部变量,disable_widgets无法直接访问。
  2. 主窗口启动时子窗口尚未创建,disable_widgets中直接访问子窗口控件会触发变量未定义错误。

修复方案(全局变量+存在性判断)

修改realizar_calibracion函数,将子窗口控件声明为全局变量;同时在disable_widgets中先判断变量是否存在,再执行逻辑:

import tkinter as tk
from tkinter import ttk 

# 全局变量声明,用于子窗口控件
mensual_check_top1 = None
boton_flujo_3 = None
boton_flujo_4 = None

# 根据条件禁用控件
def disable_widgets():
    # 若Valve Status的"0 (cero)"被勾选,禁用下拉菜单
    if cero_check.get() == 1:
        dropdown.config(state='disabled')
    else:
        dropdown.config(state='readonly')

    # 若"5lpm"被勾选,禁用流量输入框
    if flow_check.instate(['selected']):
        flow_entry.config(state='disabled')
    else:
        flow_entry.config(state='normal')
        
    # 若CONTROL MENSUAL的"no necesario"被勾选,禁用对应按钮
    if control_mensual_check.instate(['selected']):
        boton_flujo_1.config(state='disabled')
        boton_flujo_2.config(state='disabled')
    else:
        boton_flujo_1.config(state='normal')
        boton_flujo_2.config(state='normal')
         
    # 先判断子窗口控件是否存在,再执行禁用逻辑
    if mensual_check_top1 is not None and boton_flujo_3 is not None and boton_flujo_4 is not None:
        if mensual_check_top1.instate(['selected']):
            boton_flujo_3.config(state='disabled')
            boton_flujo_4.config(state='disabled')
        else:
            boton_flujo_3.config(state='normal')
            boton_flujo_4.config(state='normal')
        
# 限制Status输入仅为数字
def validate_numeric_input(new_value):
    return new_value.isnumeric()

# 创建Ventana 1窗口
def realizar_calibracion():
    global mensual_check_top1, boton_flujo_3, boton_flujo_4
    
    ventana1 = tk.Toplevel(root)
    ventana1.geometry("300x100")
    ventana1.title("Ventana 1")

    verif_flujo_mensual = ttk.Label(ventana1, text="Verificacion de flujo")
    verif_flujo_mensual.grid(row=0, column=1)
    
    mensual_check_top1 = ttk.Checkbutton(ventana1, text="No necesario 2", command=disable_widgets)
    mensual_check_top1.grid(row=2, column=0)

    boton_flujo_3 = ttk.Button(ventana1, text="Aceptable")
    boton_flujo_3.grid(row=2, column=1)

    boton_flujo_4 = ttk.Button(ventana1, text="No aceptable")
    boton_flujo_4.grid(row=2, column=2)
    

# 主窗口
root = tk.Tk()
root.title("Ventana Principal") 
root.geometry("800x600")

# OPERADOR区域
operador_label = ttk.Label(root, text="Operador")
operador_label.grid(row=0, column=0)
operador_entry = ttk.Entry(root)
operador_entry.grid(row=0, column=1)

# STATUS区域(仅允许数字输入)
status_label = ttk.Label(root, text="Status")
status_label.grid(row=1, column=0)
status_entry = ttk.Entry(root, validate="key")
status_entry['validatecommand'] = (status_entry.register(validate_numeric_input), '%P')
status_entry.grid(row=1, column=1)

# VALVE STATUS区域
valve_status_label = ttk.Label(root, text="Valve Status")
valve_status_label.grid(row=2, column=0)

# 勾选"0 (cero)"时禁用下拉菜单
cero_check = tk.IntVar()
cero_checkbox = ttk.Checkbutton(root, text="0 (cero)", variable=cero_check, command=disable_widgets)
cero_checkbox.grid(row=2, column=1)

# Valve Status非零选项下拉菜单
options_var = tk.StringVar()
options = [" Otro valor ","01011 : Derivación", "01100 : Calentamiento/Aire limpio", "00000 : Medida", "00010 : Calibración medidor de flujo"]
dropdown = ttk.OptionMenu(root, options_var, *options)
dropdown.grid(row=2, column=2)

# APARIENCIA DEL FILTRO区域
apariencia_filtro_label = ttk.Label(root, text="Apariencia Filtro")
apariencia_filtro_label.grid(row=3, column=0)

apariencia_filtro_options = ["APARIENCIA","Normal", "Achucharrado", "Marron"]
apariencia_filtro_var = tk.StringVar(value="")
apariencia_filtro_dropdown = ttk.OptionMenu(root, apariencia_filtro_var, *apariencia_filtro_options)
apariencia_filtro_dropdown.grid(row=3, column=1)

# FLUJO区域
flow_label = ttk.Label(root, text="Flujo")
flow_label.grid(row=4, column=0)
# 勾选"5lpm"时禁用输入框
flow_check = ttk.Checkbutton(root, text="5lpm", command=disable_widgets)
flow_check.grid(row=4, column=1)
# 流量输入框
flow_entry = ttk.Entry(root)
flow_entry.grid(row=4, column=2)

# CINTA REEMPLAZADA区域
replacement_tape_label = ttk.Label(root, text="Cinta reemplazada?")
replacement_tape_label.grid(row=5, column=0)

# 替换胶带勾选框
replacement_tape_check = ttk.Checkbutton(root, text="Si")
replacement_tape_check.grid(row=5, column=1)


# 分割线
division_label = ttk.Label(root, text="------------------------------")
division_label.grid(row=7, column=1)


# CHECKLIST GENERAL标题
general_label = ttk.Label(root, text="CHECKLIST GENERAL")
general_label.grid(row=8, column=1)


# CHECKLIST GENERAL区域
big_checkbox_var = tk.IntVar()
big_checkbox = ttk.Checkbutton(root, width=0)
big_checkbox.grid(row=9, column=1, rowspan=4)

# 检查当前时间与仪器时间
hora_label = ttk.Label(root, text="Chequeo hora actual + instrumento")
hora_label.grid(row=9, column=0)

# 检查管道和软管
mangueras_label = ttk.Label(root, text="Inspeccion caños y mangueras")
mangueras_label.grid(row=10, column=0)

# 检查内部水阱
aguainterna_label = ttk.Label(root, text="Inspeccion trampa de agua interna")
aguainterna_label.grid(row=11, column=0)

# 检查外部水阱
aguaexterna_label = ttk.Label(root, text="Inspeccion trampa de agua externa")
aguaexterna_label.grid(row=12, column=0)

# 分割线
division_label = ttk.Label(root, text="------------------------------")
division_label.grid(row=13, column=1)

# CONTROL MENSUAL标题
general_label = ttk.Label(root, text="CONTROL MENSUAL")
general_label.grid(row=14, column=1) 

# 流量验证标签
verif_flujo = ttk.Label(root, text="Verificacion de flujo")
verif_flujo.grid(row=15, column=0)

control_mensual_check = ttk.Checkbutton(root, text="No necesario", command=disable_widgets)
control_mensual_check.grid(row=15, column=1)

boton_flujo_1 = ttk.Button(root, text="Aceptable")
boton_flujo_1.grid(row=15, column=2)

boton_flujo_2 = ttk.Button(root, text="No aceptable",command=realizar_calibracion)
boton_flujo_2.grid(row=15, column=3)


root.mainloop()

优化建议(面向对象写法)

如果GUI复杂度提升,建议改用面向对象方式组织代码,避免全局变量,将控件和逻辑封装在类中,更易维护:

import tkinter as tk
from tkinter import ttk 

class MachineControlGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Ventana Principal") 
        self.root.geometry("800x600")
        self.mensual_check_top1 = None
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 11:27:23