Как создать набор виджетов tkinter (для цикла) и обращаться к каждому из них по отдельности?

Я преподаватель. Используя 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()

Упрощенный рабочий лист

Графический интерфейс Ткинтера

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, что лучшим подходом было бы хранить каждый виджет в списке. Что-то типа:

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

Обратите внимание, что это только добавляет, и вам потребуется другая логика для удаления студентов, которых вы отменяете, но я обычно подхожу к проблеме именно так!

редактировать

Вам нужно взглянуть на сообщение здесь, потому что вы назначаете ссылку на переменную, а не на саму переменную, поэтому она всегда будет оценивать последнее значение цикла для всех кнопок. Мне нравится ответ 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().

В вашем коде есть как минимум одна проблема: value = IntVar не делает то, что вы думаете. Кроме того, если list_of_widgets имеет два кортежа, item[2] выдаст ошибку.

Bryan Oakley 09.04.2019 01:48

Я просто копировал это из OP, но вы правы, теперь, когда вы указываете на это, это должно быть IntVar()

Reedinationer 09.04.2019 01:48

Другие вопросы по теме

Похожие вопросы

Как распаковать строку, полученную с сервера, с помощью gzip
Как правильно идентифицировать значения с плавающей запятой [0, 1], содержащие точку, в типе объекта DataFrame?
Разложение LUP (PLU) не удалось со случайной матрицей
Получение ошибки «pytesseract not in your path» в том же коде, который раньше работал нормально
Пытаюсь получить расстояние, используя долготу и широту, но продолжаю работать с ошибкой: объект «Серия» не имеет атрибута «радианы»
Найдите процент от общего количества категорий, отсортируйте от самой высокой до самой низкой, сохраните имена первых 80% и переименуйте все остальные в «другие»
Есть ли способ получить содержимое HTTP Live Streaming (HLS) с помощью Firefox/Chrome?
Как получить дату из объекта QCalendarWidget и установить ее как минимальную дату?
Добавить пользовательский генератор в класс spaCy
Разделить несколько (вложенных) определений регулярных выражений python