如何创建一组tkinter小部件(for循环)并分别引用它们中的每一个?

问题描述 投票:0回答:1

我是老师。使用Tkinter和Openpyxl我需要编写一个图形用户界面,我可以点击“.xlsx”文件中的学生列表。我希望将每个学生的姓名“链接”到一个检查按钮,并在他/她的检查按钮处于活动状态时显示他/她的标记(保存在“.xlsx”文件中)。如何创建一组我稍后可以单独引用的小部件/检查按钮?在下面的代码中,每个checkbutton都具有相同的名称!

# -*- 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()

Simplified Worksheet

Tkinter GUI

python tkinter widget openpyxl
1个回答
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()

请注意,这只会添加,您需要其他逻辑来删除您取消选择的学生,但我通常会如何解决问题!

edit

您需要查看post 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()函数中引用的行。

© www.soinside.com 2019 - 2024. All rights reserved.