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

Tkinter应用问题:从加载的DataFrame更新Listbox时变量未定义

Tkinter多页面GUI应用报错:name 'x' is not defined

问题描述

开发Tkinter多页面GUI应用,功能为选择CSV文件并加载为pandas DataFrame,在第二页Listbox中显示DataFrame的“Category”列唯一值作为可选项。运行时出现name 'x' is not defined错误,错误出现在Listbox加载数据的循环代码处。尝试绑定“Next”按钮触发列表加载逻辑,但问题未解决。

CSV数据示例

import pandas as pd
data = [['TNUAX', '1290 Diversified Bond A','Intermediate Core-Plus Bond'],
        ['ABSZX', 'AB Discovery Value Z','Small Value'],
        ['AUIAX', 'AB Equity Income A','Large Value']]
df = pd.DataFrame(data, columns=['Ticker', 'Fund Name','Category'])
df

应用代码

import tkinter as tk
from tkinter import *
import tkinter.ttk as ttk 
import pandas as pd


class MyApp(Tk):
    def __init__(self):
        Tk.__init__(self)
        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        self.frames = {}
        for F in (PageOne, PageTwo):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky='NSEW')
        self.show_frame(PageOne)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class PageOne(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.make_widget()

    def make_widget(self):
        #Sections of Page/Canvas
        self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
        self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
        self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
        
        #Fonts and sizes
        mainf = 'Arial'
        headsz = 20
        bodysz = 12
        
        def databrowser():
            root = tk.Tk()
            root.withdraw()
            data_f_path = filedialog.askopenfilename()
            df = pd.read_csv(data_f_path)
            x = df.Category.unique().tolist()
            
        #Score Sort Header
        lbl=tk.Label(self.cvs2, text="Score Sort", background= 'white',fg='#529e3f', font=(mainf, headsz))
        lbl.place(relx=0.75, rely=0.01, relwidth=0.5, anchor='ne')
        #Select Data File (.csv) label
        datainlabel=Label(self.cvs2, text="Select Data File (.csv)", bg='white', fg='Black', font=(mainf,bodysz))
        datainlabel.place(relx=0.55, rely=0.55, relwidth=0.31, anchor='ne')
        #Select Data Input File Button
        InSbtn = Button(self.cvs2, text='Browse', font=(mainf, bodysz),command=databrowser)    #command=selected_item
        InSbtn.place(relx=0.75, rely=0.55, relwidth=0.15, anchor='ne')
        
        
        def func2():
            print('Load List Function Here?')
        
        
        #Next Page Button
        btnChange = Button(self.cvs3, text="Next", font=(mainf,bodysz),command=lambda: [self.controller.show_frame(PageTwo),func2()],
                           bg="white") #'#529e3f' #acdcea
        btnChange.place(relx=.775, rely=.15, width="80", height="40")
        
        def change_page(self):
            pass
        
         #Pack Sections 
        self.cvs.pack()
        self.cvs2.pack()
        self.cvs3.pack()
        
class PageTwo(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller  
        ttk.Frame.__init__(self, parent)
        self.make_widget()

    def make_widget(self):
        #Fonts and sizes
        mainf = 'Arial'
        headsz = 20
        bodysz = 12
        
        #Sections of Page
        self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
        self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
        self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
        self.cvslist = Canvas(self, width="500", height="300", background="#acdcea", bd=-2)
        
        #Category Selection List
        yscrollbar = Scrollbar(self.cvslist)
        yscrollbar.pack(side = RIGHT, fill = Y)
        #Create window label
        label = Label(self.cvslist,text = "Select Assset Categories :  ",font = (mainf, 13),bg='#acdcea', padx = 5, pady = 5)
        label.pack()
        # Create a listbox
        listbox = Listbox(self.cvslist, width=40, height=15, selectmode=MULTIPLE, font=(mainf,11),yscrollcommand = yscrollbar.set)
        #Expanse Option
        listbox.pack(padx = 10, pady = 10,expand = YES, fill = "both")

#Part that Loads Assets categories from csv in list 
        for each_item in range(len(x)):
            listbox.insert(END, x[each_item])
            listbox.itemconfig(each_item, bg = "white")

        #Back Page Button
        backbutt = tk.Button(self.cvs3, text='Back',font=('Arial',12), bg='white',command=lambda: self.controller.show_frame(PageOne))
        backbutt.place(relx=0.225, rely=0.15, anchor='ne', width="80", height="40")
       
        #Pack Sections 
        self.cvs.pack()
        self.cvs2.pack()
        self.cvslist.place(relx=0.75, rely=0.2, relwidth=0.5, anchor='ne')
        self.cvs3.pack()
            
if __name__ == '__main__':
    app = MyApp()
    app.title('Score Sort')
    app.mainloop()

错误信息

name 'x' is not defined

已尝试的解决方法

绑定“Next”按钮触发列表加载逻辑,定义了func2()但未实现具体加载代码,期望点击“Next”时加载Listbox数据,但问题仍未解决。同时希望获取可提供手把手帮助的平台建议。


解决方案

错误原因

  1. 变量作用域隔离:x是PageOne中databrowser函数的局部变量,PageTwo无法直接访问;且PageTwo初始化时就执行加载循环,此时用户还未选择CSV文件,x根本不存在。
  2. 页面初始化时机错误:PageTwo的make_widget方法在应用启动时就执行,此时无CSV数据,循环自然找不到x。
  3. 页面间数据共享缺失:两个页面没有统一的数据存储容器,导致PageOne加载的数据无法传递到PageTwo。

修复步骤

1. 在主应用类中添加共享数据属性

在MyApp类中定义存储DataFrame和分类列表的属性,作为页面间数据传递的容器:

class MyApp(Tk):
    def __init__(self):
        Tk.__init__(self)
        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        self.frames = {}
        # 添加共享数据存储
        self.df = None
        self.categories = []
        for F in (PageOne, PageTwo):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky='NSEW')
        self.show_frame(PageOne)

2. 修改PageOne的databrowser函数

将加载的CSV数据存入主应用的共享属性,同时避免新建Tk实例导致冲突:

def databrowser():
    data_f_path = filedialog.askopenfilename(filetypes=[("CSV Files", "*.csv")])
    if data_f_path:  # 处理用户取消选择的情况
        self.controller.df = pd.read_csv(data_f_path)
        self.controller.categories = self.controller.df.Category.unique().tolist()

注意:需要在代码开头添加from tkinter import filedialog导入文件对话框模块。

3. 修改PageTwo,添加数据加载方法

将Listbox设为实例属性,新增load_categories方法用于动态加载数据:

class PageTwo(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller  
        ttk.Frame.__init__(self, parent)
        self.make_widget()

    def make_widget(self):
        # ... 其他代码不变 ...
        # 将listbox改为实例属性
        self.listbox = Listbox(self.cvslist, width=40, height=15, selectmode=MULTIPLE, font=(mainf,11),yscrollcommand = yscrollbar.set)
        self.listbox.pack(padx = 10, pady = 10,expand = YES, fill = "both")
        # 移除原有的循环加载代码
        # ... 其他代码不变 ...

    def load_categories(self):
        # 清空现有列表
        self.listbox.delete(0, END)
        # 加载主应用中的分类数据
        for idx, category in enumerate(self.controller.categories):
            self.listbox.insert(END, category)
            self.listbox.itemconfig(idx, bg = "white")

4. 修改PageOne的func2函数

在点击“Next”按钮时,调用PageTwo的load_categories方法加载数据:

def func2():
    page_two = self.controller.frames[PageTwo]
    page_two.load_categories()

修复后完整代码

import tkinter as tk
from tkinter import *
from tkinter import filedialog
import tkinter.ttk as ttk 
import pandas as pd


class MyApp(Tk):
    def __init__(self):
        Tk.__init__(self)
        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        self.frames = {}
        # 共享数据存储
        self.df = None
        self.categories = []
        for F in (PageOne, PageTwo):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky='NSEW')
        self.show_frame(PageOne)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class PageOne(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.make_widget()

    def make_widget(self):
        #Sections of Page/Canvas
        self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
        self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
        self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
        
        #Fonts and sizes
        mainf = 'Arial'
        headsz = 20
        bodysz = 12
        
        def databrowser():
            data_f_path = filedialog.askopenfilename(filetypes=[("CSV Files", "*.csv")])
            if data_f_path:
                self.controller.df = pd.read_csv(data_f_path)
                self.controller.categories = self.controller.df.Category.unique().tolist()
            
        #Score Sort Header
        lbl=tk.Label(self.cvs2, text="Score Sort", background= 'white',fg='#529e3f', font=(mainf, headsz))
        lbl.place(relx=0.75, rely=0.01, relwidth=0.5, anchor='ne')
        #Select Data File (.csv) label
        datainlabel=Label(self.cvs2, text="Select Data File (.csv)", bg='white', fg='Black', font=(mainf,bodysz))
        datainlabel.place(relx=0.55, rely=0.55, relwidth=0.31, anchor='ne')
        #Select Data Input File Button
        InSbtn = Button(self.cvs2, text='Browse', font=(mainf, bodysz),command=databrowser)
        InSbtn.place(relx=0.75, rely=0.55, relwidth=0.15, anchor='ne')
        
        
        def func2():
            page_two = self.controller.frames[PageTwo]
            page_two.load_categories()
        
        
        #Next Page Button
        btnChange = Button(self.cvs3, text="Next", font=(mainf,bodysz),command=lambda: [self.controller.show_frame(PageTwo),func2()],
                           bg="white")
        btnChange.place(relx=.775, rely=.15, width="80", height="40")
        
        #Pack Sections 
        self.cvs.pack()
        self.cvs2.pack()
        self.cvs3.pack()
        
class PageTwo(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller  
        ttk.Frame.__init__(self, parent)
        self.make_widget()

    def make_widget(self):
        #Fonts and sizes
        mainf = 'Arial'
        headsz = 20
        bodysz = 12
        
        #Sections of Page
        self.cvs = Canvas(self, width="500", height="60", background='#f0f0f0', bd=-2) #-2 to eliminate border
        self.cvs2 = Canvas(self, width="500", height="500", background="white", bd=-2)
        self.cvs3 = Canvas(self, width="500", height="60", background="#f0f0f0", bd=-2)
        self.cvslist = Canvas(self, width="500", height="300", background="#acdcea", bd=-2)
        
        #Category Selection List
        yscrollbar = Scrollbar(self.cvslist)
        yscrollbar.pack(side = RIGHT, fill = Y)
        #Create window label
        label = Label(self.cvslist,text = "Select Assset Categories :  ",font = (mainf, 13),bg='#acdcea', padx = 5, pady = 5)
        label.pack()
        # Create a listbox
        self.listbox = Listbox(self.cvslist, width="40", height="15", selectmode=MULTIPLE, font=(mainf,11),yscrollcommand = yscrollbar.set)
        #Expanse Option
        self.listbox.pack(padx = 10, pady = 10,expand = YES, fill = "both")

        #Back Page Button
        backbutt = tk.Button(self.cvs3, text='Back',font=('Arial',12), bg='white',command=lambda: self.controller.show_frame(PageOne))
        backbutt.place(relx=0.225, rely=0.15, anchor='ne', width="80", height="40")
       
        #Pack Sections 
        self.cvs.pack()
        self.cvs2.pack()
        self.cvslist.place(relx=0.75, rely=0.2, relwidth=0.5, anchor='ne')
        self.cvs3.pack()

    def load_categories(self):
        self.listbox.delete(0, END)
        for idx, category in enumerate(self.controller.categories):
            self.listbox.insert(END, category)
            self.listbox.itemconfig(idx, bg = "white")
            
if __name__ == '__main__':
    app = MyApp()
    app.title('Score Sort')
    app.mainloop()

手把手帮助平台建议

  • 本地调试:使用PyCharm、VS Code的断点调试功能,逐步跟踪变量作用域和数据传递流程,直观定位问题。
  • 社区交流:参与国内Python开发者交流群、技术论坛的实时讨论,直接和其他开发者互动排查问题。

内容的提问来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 23:51:24