Python 组合框的 Tkinter get() 值未更改

Python Tkinter get() value of combobox not changing

提前感谢您的帮助。

我正在创建一个 GUI,它的一部分让我感到困惑 - 我使用 Tkinter 创建了一个组合框,值来自 CSV 文件 - 然后我允许用户 select 其中一个名称和删除那个人,这会导致 CSV 文件中该用户的行被删除,下拉列表只用剩余的用户刷新 - 这部分工作正常 - 我的问题是当我去删除第二个用户时 - 我得到的价值组合框始终保留第一个用户的值。

我是一个 python 菜鸟,您可以从我的代码中快速确定,但我一直无法找到解决我做错的问题的方法,我相信这很简单。

这是我正在读取的 CSV 文件:

id,name,username,password,val1,val2
1,Billy,bsmith,GoodPassword,JZ4Z3ATP6,Test1
2,Amanda,asmith,GoodPassword1,NRROAZ6JP6,Test2
3,Sammy,ssmith,GoodPassword2,NRRZ3ATP6,Test3

我的 python 代码在这里:

from tkinter import *
from tkinter import ttk
import tkinter as tk
from tkinter import messagebox as msg
import os.path
from csv import reader, writer
import csv
import shutil

root = Tk()
root.title('Why am I an idiot')


labelFrame3 = ttk.LabelFrame(text = "Delete Button")
labelFrame3.grid(column = 0, row = 2, padx = 20, pady = 20)

def clicking(event):
    to_delete = delete_combo.get()
    print("CLICKING")
    print(to_delete)

def read_connections_file(conns_dict):
    conns = conns_dict
    with open('file.csv', 'r') as read_obj:
        csv_reader = reader(read_obj)
        header = next(csv_reader)
        # Check file as empty
        if header != None:
            # Iterate over each row after the header in the csv
            for row in csv_reader:
                # row variable is a list that represents a row in csv
                conns[row[0]] = {'name':row[1], 'username':row[2], 'password':row[3], 'val1':row[4], 'val2':row[5]}

def get_button_names():
    conns = {}
    button_names = []
    read_connections_file(conns)
    for i in conns:
        name_ = conns[i]["name"]
        button_names.append(name_)
    return button_names

buttons1 = get_button_names()
delete_combo = ttk.Combobox(labelFrame3, value=buttons1)
delete_combo.bind("<<ComboboxSelected>>", clicking)
delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)

def clicking(event):
    to_delete = delete_combo.get()
    print("CLICKING")
    print(to_delete)

def create_delete_dropdown():
    buttons2 = get_button_names()
    print(buttons2)
    delete_combo = ttk.Combobox(labelFrame3, value=buttons2)
    delete_combo.bind("<<ComboboxSelected>>", clicking)
    delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)
    print("FUNC")
    help = delete_combo.get()
    print(help)

def delete_saved_button():
    original = r'file.csv'
    target = r'file.bak'
    shutil.copyfile(original, target)
    to_delete = delete_combo.get()
    print(to_delete)

    answer = msg.askyesno("Delete Button", ("Are you sure you want to Delete " + to_delete + "?"))

    if answer == True:
        conns = []
        with open('file.csv', 'r') as read_obj:
            csv_reader = reader(read_obj)
            header = next(csv_reader)
            # Check file as empty
            if header != None:
                # Iterate over each row after the header in the csv
                for row in csv_reader:
                    # row variable is a list that represents a row in csv
                    conns.append({'id':row[0],'name':row[1], 'username':row[2], 'password':row[3], 'val1':row[4], 'val2':row[5]})

        new_list = None
        for item in conns:
            if item['name'] == to_delete:
                new_list = item
                break
        new_cons = conns.copy()
        new_cons.remove(new_list)

        i = 1
        for d in new_cons:
            d['id'] = i
            i += 1

        msg.showinfo("Button Deleted", (to_delete + " Button Deleted"))

        csv_file_name = "file.csv"
        with open(csv_file_name, 'w', newline='') as csvfile:
            writer = csv.DictWriter(csvfile, fieldnames=header)
            writer.writeheader()
            for data in new_cons:
                writer.writerow(data)

    create_delete_dropdown()

# LabelFrame 3 Content
delete_label = ttk.Label(labelFrame3, text = "Delete Who?: ")
delete_label.grid(row=0, column=0)

deleteButton = tk.Button(master=labelFrame3, text="Delete", command=delete_saved_button)
deleteButton.grid(row=1, column=0)

root.mainloop()

我在其中留下了一些打印语句,试图帮助我找出我做错了什么。

如果我先删除 Billy,他就会从 CSV 和下拉列表中删除,这很棒。

如果我然后 select Amanda 并尝试删除她的第二个我得到一个错误 - 我从这个序列的输出包括我的打印语句是:

CLICKING
Billy
Billy
['Amanda', 'Sammy']
FUNC

CLICKING
Billy
Billy
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\reeno\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
    return self.func(*args)
  File ".\fucked.py", line 90, in delete_saved_button
    new_cons.remove(new_list)
ValueError: list.remove(x): x not in list

这是脚本 运行:

如果您能告诉我我做错了什么或指出可能会给我答案的资源,我将不胜感激。

记住我是一个菜鸟,但我也很乐意提出任何改进我的代码的建议。

感谢您的帮助!!

您的问题是 localglobal 变量 delete_combo

当您在 create_delete_dropdown() 中创建新的 Combobox() 时,它会创建局部变量 delete_combo 并将新的组合框分配给该局部变量,但它不会更改全局变量 delete_combo 被其他函数使用 - 所以其他函数仍然使用旧的 Combobox()(隐藏在 grid() 中的新 Combobox() 后面)。

您必须在函数 create_delete_dropdown() 中使用 global delete_combo 来通知此函数您要分配给 external/global 变量,而不是本地变量

def create_delete_dropdown():
    global delete_combo # inform function that you will assign (=) to external/global variable

    buttons2 = get_button_names()
    print(buttons2)

    delete_combo = ttk.Combobox(labelFrame3, value=buttons2)
    delete_combo.bind("<<ComboboxSelected>>", clicking)
    delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)

    print("FUNC")
    help = delete_combo.get()
    print(help)

但是您可以更改现有 Combobox() 中的值,而不是创建新的 Combobox(),正如评论中提到的@TheFluffDragon9。

def create_delete_dropdown():

    buttons2 = get_button_names()
    print(buttons2)

    delete_combo['value'] = buttons2
    delete_combo.set('') 

    print("FUNC")
    help = delete_combo.get()
    print(help)

编辑: 具有其他更改的代码。

我使用DictReader获取行列表(字典)中的数据以保持行的原始顺序(旧的Python不必在字典中保持顺序)

import os.path
import csv
import shutil
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import ttk
from tkinter import messagebox as msg

# --- constants --- (UPPER_CASE_NAMES)

ORIGINAL = 'file.csv'
BACKUP   = 'file.bak'

# --- function --- (lower_case_names)

def read_data():
    global header # to keep in global variable

    with open(ORIGINAL) as csvfile:
        csv_reader = csv.DictReader(csvfile)
        header = csv_reader.fieldnames
        data = list(csv_reader)
        #print('header:', header)

    return data


def write_data(data, make_backup=True):
    if make_backup:
        shutil.copyfile(ORIGINAL, BACKUP)

    with open(ORIGINAL, 'w', newline='') as csvfile:
        csv_writer = csv.DictWriter(csvfile, fieldnames=header)
        csv_writer.writeheader()
        csv_writer.writerows(data)


def clicking(event):
    print("CLICKING:", delete_combo.get())
    print("CLICKING:", event.widget.get())


def get_column(column="name"):
    data = read_data()
    values = []

    for row in data:
        values.append(row[column])

    return values


def delete_saved_button():
    to_delete = delete_combo.get()
    print('to_delete:', to_delete)

    if not to_delete:
        msg.showinfo("Button Deleted", "You have to select name.")
        return

    answer = msg.askyesno("Delete Button", "Are you sure you want to Delete {}?".format(to_delete))

    if answer == True:
        # read old data
        data = read_data()

        # search element to delete
        number_to_delete = None
        for number, row in enumerate(data):
            if row['name'] == to_delete:
                number_to_delete = number
                break

        if number_to_delete is not None: # it has to compare with None because number can be 0 and bool(0) gives also False
            del data[number_to_delete]
        else:
            msg.showinfo("Button Deleted", "Name '{}' doesn't exist.".format(to_delete))
            return

        for number, row in enumerate(data, 1):
            row['id'] = number

        # write new data
        write_data(data)

        # update dropdown
        delete_combo["value"] = get_column("name")
        delete_combo.set("")

        msg.showinfo("Button Deleted", "{} Button Deleted".format(to_delete))


# --- main --- (lower_case_names)

root = tk.Tk()
root.title('Why am I an idiot')

label_frame = ttk.LabelFrame(text="Delete Button") # 
label_frame.grid(column=0, row=2, padx=20, pady=20) # PEP8: without spaces around =

delete_label = ttk.Label(label_frame, text="Delete Who?: ")
delete_label.grid(row=0, column=0)

names = get_column("name")

delete_combo = ttk.Combobox(label_frame, value=names)
delete_combo.bind("<<ComboboxSelected>>", clicking)
delete_combo.grid(row=0, column=1, columnspan=3, padx=10, pady=10)

delete_button = tk.Button(master=label_frame, text="Delete", command=delete_saved_button)
delete_button.grid(row=1, column=0)

root.mainloop()

PEP 8 -- Style Guide for Python Code