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
这是脚本 运行:
如果您能告诉我我做错了什么或指出可能会给我答案的资源,我将不胜感激。
记住我是一个菜鸟,但我也很乐意提出任何改进我的代码的建议。
感谢您的帮助!!
您的问题是 local
和 global
变量 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()
提前感谢您的帮助。
我正在创建一个 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
这是脚本 运行:
如果您能告诉我我做错了什么或指出可能会给我答案的资源,我将不胜感激。
记住我是一个菜鸟,但我也很乐意提出任何改进我的代码的建议。
感谢您的帮助!!
您的问题是 local
和 global
变量 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()