Теперь, когда у нас есть файлик с несколькими записями попробуем вывести их сразу все на форму.
Для этого в tkinter встроена такая компонента как ListBox то бишь “коробочка под список”, выглядит он вот так:

Добавим ее на форму:
class UI():
def __init__(self, gui):
gui.geometry("400x150")
self.name = create_entry(10, 10, "Имя", 120)
self.last_name = create_entry(10, 40, "Фамилия", 120)
self.group = create_entry(10, 70, "Группа", 120)
self.button = Button(text="Сохранить в файл", command=self.save_to_file)
self.button.place(x=10, y=100)
# добавим список на форму
self.studentsList = Listbox(exportselection=False)
self.studentsList.place(x=220, y=10, height=130, width=170)
def save_to_file(self):
# ...
получится так:

Попробуем чего-нибудь добавить в этот список:
class UI():
def __init__(self, gui):
#...
self.studentsList = Listbox(exportselection=False)
self.studentsList.place(x=220, y=10, height=130, width=170)
# добавил три планеты
self.studentsList.insert(END, "Меркурий")
self.studentsList.insert(END, "Венера")
self.studentsList.insert(END, "Земля")
def save_to_file(self):
# ...

Попробуем теперь заполнить данными из файла:
class UI():
def __init__(self, gui):
self.studentsList = Listbox(exportselection=False)
self.studentsList.place(x=220, y=10, height=130, width=170)
"""
это убрали
self.studentsList.insert(END, "Меркурий")
self.studentsList.insert(END, "Венера")
self.studentsList.insert(END, "Земля")
"""
# открываем файл со студентами
with open("user.json", encoding="utf8") as f:
students = json.load(f)
# добавляем студентов в список
for s in students:
self.studentsList.insert(END, s)
# ...
смотрим что получится

ну в принципе вывелось, правда смотреть не удобно. Было бы лучше если выводил как-нибудь в виде имя фамилия.
class UI():
def __init__(self, gui):
# ...
with open("user.json", encoding="utf8") as f:
students = json.load(f)
for s in students:
# буду вместо s добавлять в список студента в виде "фамилия имя группа"
self.studentsList.insert(END, f"{s['last_name']} {s['name']} {s['group']}")
во, так уже лучше

Теперь попробуем сделать так, чтобы можно было кликнуть на студента в списке и подкорректировать запись.
Для этого есть возможность подцеплять так называемую реакцию на события к компоненте на форме.
В tkinter целая куча встроенных событий, например, мы можем добавить реакцию на клик мышкой. Делается это так:
class UI():
def __init__(self, gui):
# ...
self.studentsList = Listbox(exportselection=False)
self.studentsList.place(x=220, y=10, height=130, width=170)
# добавляем реакцию на клик мышки, чтобы вызывалась функция on_student_list_click
self.studentsList.bind("<Button>", self.on_student_list_click)
# ...
# собственно функция on_student_list_click
# обрати внимание на второй аргумент функции event, там информация о событии
def on_student_list_click(self, event):
print(event) # выведем информацию о событии
тестируем:

как мы видим он показывает координаты клика внутри списка. Еще есть там какой-то num, это номер кнопки мыши которую кликнули:
- Левая кнопка – 1
- Средняя – 2
- Правая – 3
можно, например, подкорректировать функцию:
def on_student_list_click(self, event):
if event.num == 1:
print(f"Вы кликнули левой кнопкой в точку ({event.x}, {event.y})")
elif event.num == 2:
print(f"Вы кликнули средней кнопкой в точку ({event.x}, {event.y})")
elif event.num == 3:
print(f"Вы кликнули правой кнопкой в точку ({event.x}, {event.y})")
и потыкать разными кнопками мыши:

а как например понять, что кликнули именно на конкретный элемент, тут есть два способа, первый это узнать по y координате порядковый номер строки в списке, а потом по номеру получить текст в этой строке
def on_student_list_click(self, event):
item_index = self.studentsList.nearest(event.y) # по координате event.y получаем номер строки
item_text = self.studentsList.get(item_index) # по номеру строки берем текст в строке
print(item_text) # выводим его в консольку
кстати если в консольку не нравится, можно показать информационное сообщение, делается это так:
def on_student_list_click(self, event):
item_index = self.studentsList.nearest(event.y) # по координате event.y получаем номер строки
item_text = self.studentsList.get(item_index) # по номеру строки берем текст в строке
messagebox.showinfo("Сообщение! =О", item_text) # выводим информационное сообщение, первый параметр -- это заголовок окна
больше видов можно подсмотреть тут https://docs.python.org/3/library/tkinter.messagebox.html
тестируем

прекрасно! =)
И альтернативный способ, если брать выбранный элемент по координате y выглядит странно. Можно вместо события клика мышью использовать специальное событие выбора строки.
Подключается это дело так:
class UI():
def __init__(self, gui):
#...
self.studentsList = Listbox(exportselection=False)
self.studentsList.place(x=220, y=10, height=130, width=170)
self.studentsList.bind('<<ListboxSelect>>', self.on_student_list_click)
# эту строчку убираем self.studentsList.bind("<Button>", self.on_student_list_click)
# ...
def on_student_list_click(self, event):
# тут вместо nearest используем метод curselection(),
# возвращает список выбранных строк (теоретически их может быть несколько),
# так как нам нужна только одна то поэтому пишем curselection()[0]
item_index = self.studentsList.curselection()[0]
# а дальше как обычно
item_text = self.studentsList.get(item_index)
messagebox.showinfo("", item_text)
работает так же

Ну пока мы играемся, а как сделать чтобы при клике на элемент в списке, заполнялась форма. И можно было бы обновить запись.
На самом деле сделать это не так просто, потому что то что выводится на форме к исходному списку уже никак не привязано, это по сути отдельный список.
И как быть?
А надо просто исходный список который из файла грузим тоже сохранять привязанным к self
Делаем это так:
class UI():
def __init__(self, gui):
# ...
self.studentsList = Listbox(exportselection=False)
self.studentsList.place(x=220, y=10, height=130, width=170)
self.studentsList.bind('<<ListboxSelect>>', self.on_student_list_click)
# как обычно открываем файл со студентами
with open("user.json", encoding="utf8") as f:
# и теперь привязываем список из файла к self.students
self.students = json.load(f)
# ну итерируем по списку self.students
for s in self.students:
self.studentsList.insert(END, f"{s['last_name']} {s['name']} {s['group']}")
def on_student_list_click(self, event):
# в реакции на клик берем как обычно выбранный номер строки с формы
item_index = self.studentsList.curselection()[0]
# а значение берем не из списка на форме, а из self.students
item = self.students[item_index]
# и заполняем значения на форме
self.name.delete(0, END)
self.name.insert(0, item['name'])
self.last_name.delete(0, END)
self.last_name.insert(0, item['last_name'])
self.group.delete(0, END)
self.group.insert(0, item['group'])
def save_to_file(self):
# ...
тыкаем:

в принципе можно теперь задание пилить