我是老师。使用Tkinter和Openpyxl,我需要编写一个图形用户界面,在这里我可以单击“ .xlsx”文件中的学生列表。我想将每个学生的姓名“链接”到一个选中按钮的方格上,并在他/她的选中按钮处于活动状态时显示其标记(保存在“ .xlsx”文件中)。如何创建一组以后可以单独引用的小部件/检查按钮?在下面的代码中,每个复选按钮都具有相同的名称!!
# -*- coding: utf-8 -*-
from tkinter import *
import openpyxl
wb = openpyxl.load_workbook('marks.xlsx', data_only=True)
sheet = wb.active
names_and_rows = {}
for i in range(2, sheet.max_row + 1):
name = sheet.cell(row=i, column=1).value
names_and_rows[name] = i
root = Tk()
root.title("Student's marks")
students_names = Frame(root, bd=1, relief="solid")
students_names.pack(side="left")
student_marks = Frame(root, bd=1, relief="solid")
student_marks.pack(side="right")
message = Label(student_marks, text="You still haven't checked on any student's name")
message.pack()
def get_marks(v):
marks = ""
for i in range(2, sheet.max_column + 1):
information = str(sheet.cell(row=1, column=i).value) + ": " + str(sheet.cell(row=v, column=i).value) + "\n"
marks = marks + information
if (v.get() == 1):
message.config(text=marks)
else:
message.config(text="You still haven't checked on any student's name")
list_of_widgets = []
for k, v in names_and_rows.items():
square = Checkbutton(students_names, variable=v, onvalue=1, offvalue=0, text=k, command=lambda: get_marks(v))
list_of_widgets.append(square)
square.pack()
root.mainloop()
答案 0 :(得分:0)
我认为最好的方法是将每个小部件存储在列表中。像这样:
list_of_widgets = []
for student in students:
square = Checkbutton(root, variable=value, text=student)
list_of_widgets.append(square)
square.pack()
尽管我也很确定您需要为每个变量分配一个新变量,并且您可能希望将其存储在列表中的元组中。也许像这样更合适:
list_of_widgets = []
for student in students:
value = IntVar()
square = Checkbutton(root, variable=value, text=student)
list_of_widgets.append((square, value)) # appending a tuple ()
square.pack()
您甚至可以包括其他信息,以使将按钮与特定学生关联起来更加容易:
list_of_widgets.append((student, square, value)) # appending a tuple ()
然后您可能会导致线程永远循环,并且如果状态发生更改,请更新程序:
import threading
def check_for_changes():
global list_of_widgets
students_selected = []
while True: # loop forever
for item in list_of_widgets:
if item[2].get() != 0 and item[0] not in students_selected:
students_selected.append(item)
# or some other code
t = threading.Thread(target=check_for_changes)
t.start()
请注意,这只是添加,您将需要其他逻辑来删除您取消选择的学生,但是我通常这是解决问题的方法!
您需要看一下here帖子,因为您要分配对变量的引用,而不是变量本身,因此它将始终对所有按钮的循环求值。我喜欢答案lambda i=i: self.open_this(i)
,并修改您的代码以实现此目的更像:
from tkinter import *
import openpyxl
from collections import OrderedDict
wb = openpyxl.load_workbook('test.xlsx', data_only=True)
sheet = wb.active
names_and_rows = OrderedDict()
for i in range(2, sheet.max_row + 1):
name = sheet.cell(row=i, column=1).value
names_and_rows[name] = i
root = Tk()
root.title("Student's marks")
students_names = Frame(root, bd=1, relief="solid")
students_names.pack(side="left")
student_marks = Frame(root, bd=1, relief="solid")
student_marks.pack(side="right")
message = Label(student_marks, text="You still haven't checked on any student's name")
message.pack()
def get_marks(v):
button_status = list_of_widgets[v][1].get()
if button_status:
row = list_of_widgets[v][2] + 2
marks = ""
for col in range(2, sheet.max_column + 1):
information = str(sheet.cell(row=1, column=col).value) + ": " + str(sheet.cell(row=row, column=col).value) + "\n"
marks = marks + information
message.config(text=marks)
else:
message.config(text="You unselected a student")
list_of_widgets = []
i = 0
for k, v in names_and_rows.items():
new_variable = IntVar()
square = Checkbutton(students_names, variable=new_variable, onvalue=1, offvalue=0, text=k, command=lambda i=i: get_marks(i))
list_of_widgets.append((square, new_variable, i))
square.pack()
i += 1
root.mainloop()
需要特别注意的是,您需要使用collections.OrderedDict()
来保持姓名列表的顺序。这不仅会使它们在Excel工作表中以正确的顺序显示(想象一下,尝试在每次运行程序时都随机打乱的100个名字的列表中找到一个名字,这就是您的去向),而且还会使您可以确定在get_marks()
函数中需要引用的行。