I think the best approach would be to store each widget in a list. Something like:
list_of_widgets = []
for student in students:
square = Checkbutton(root, variable=value, text=student)
list_of_widgets.append(square)
square.pack()
Although I'm also pretty sure you'd need to assign a new variable to each, and you'd probably want to store that in a tuple within your list. Maybe something like this would be more appropriate:
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()
You could even include other information that makes it easier to associate a button with a particular student:
list_of_widgets.append((student, square, value)) # appending a tuple ()
You could then have a thread looping forever and if a change of state occurs update your program:
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()
Note this only adds and you would need other logic to remove students that you deselect, but I this is generally how I would approach the problem!
edit
You need to have a look at the post here because you are assigning a reference to a variable and not the variable itself, so it will always evaluate to the last value of the loop for all buttons. I like the answer lambda i=i: self.open_this(i)
and modifying your code to implement this would look more like:
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()
Of special note you need to use collections.OrderedDict()
to maintain the order of your list of names. This will not only make them appear in the correct order from your Excel sheet (Imagine trying to find a name in a list of 100 names that are randomly scrambled every time your program runs...That's where you were headed), but will also allow you to determine what row you need to reference within your get_marks()
function.