Вспомните: в прошлом уроке вы собрали первое окно — надпись Label, поле Entry, кнопку Button с функцией в command, раскладку grid/pack и сообщение messagebox. А в главе 1 сортировщик «Загрузок» раскладывал файлы по папкам и хранил настройки в settings.json. Сейчас соединим одно с другим: у сортировщика появится окно, где папку выбирают мышкой, файлы видны списком, а режим «только показать» включается галочкой и запоминается до следующего запуска.
🎯 После урока сможете: связать поле и галочку с переменной StringVar/BooleanVar и читать её через get(), показать список файлов в Listbox и узнать, что выбрал человек, открыть стандартный диалог выбора папки filedialog.askdirectory, сохранить последнюю папку и положение галочки в JSON и восстановить их при запуске.

Переменные Tkinter: StringVar и BooleanVar
До сих пор текст из поля вы забирали методом entry.get(), а надпись меняли через label.config(text=...). Когда одно и то же значение показывают несколько виджетов или его надо сохранить в файл, удобнее завести переменную Tkinter — объект, который хранит значение и сам обновляет связанные с ним виджеты.
tk.StringVar()— хранит строку: путь к папке, текст состояния;tk.BooleanVar()— хранитTrueилиFalse: включена ли галочка;var.get()— прочитать значение,var.set(значение)— записать;value=при создании задаёт начальное значение:tk.StringVar(value="Загрузки").
Переменную создают после root = tk.Tk(): ей нужно уже существующее окно. Связь с виджетом задаётся параметром: у Entry и Label — textvariable=. Теперь set() меняет текст на экране, а то, что человек напечатал в поле, читается через get().
import tkinter as tk
root = tk.Tk()
folder = tk.StringVar(value="Загрузки")
entry = tk.Entry(root, textvariable=folder)
status = tk.Label(root, textvariable=folder)
entry.pack()
status.pack()
print(entry.get())
folder.set("Рабочий стол")
print(entry.get(), "|", status.cget("text"))
Вывод:
Загрузки
Рабочий стол | Рабочий стол
Одна строка folder.set(...) поменяла и поле, и надпись. Метод cget("text") читает текущий текст надписи — здесь он нужен, чтобы показать это в выводе.
В примерах урока нет
root.mainloop(): так их можно запустить и сразу увидеть вывод в терминале. В настоящей программеmainloop()— последняя строка, иначе окно не покажется. Иroot.destroy()в конце здесь можно не писать: в уроке 3.2 он аккуратно закрывал окно, но когда программа доходит до последней строки, окно закрывается вместе с ней.
Проверьте себя
Контрольный вопрос. Какой объект подходит, чтобы хранить состояние галочки «Только показать» — включена она или нет?
Подсказка: Галочка может быть только в двух состояниях. Какая переменная хранит True или False?
Онлайн-проверка ответа появится позже
Задание. Функцию reset() программа вызвала сама, без нажатия. Что выведет print? Введите текст вывода.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() folder = tk.StringVar(value="Загрузки") entry = tk.Entry(root, textvariable=folder) entry.pack() def reset(): folder.set("Видео") reset() print(entry.get())Подсказка: С какой переменной связано поле через textvariable и что с этой переменной сделала reset()?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Checkbutton: галочка «Только показать»
tk.Checkbutton — квадратик с подписью. Его состояние хранит переменная из параметра variable=: отмечен — в ней True, снят — False. Параметр command= работает как у кнопки: функция вызывается при каждом щелчке, после того как переменная уже поменялась.
import tkinter as tk
root = tk.Tk()
dry_run = tk.BooleanVar(value=True)
def on_toggle():
if dry_run.get():
print("Режим: только показать")
else:
print("Режим: переносить файлы")
check = tk.Checkbutton(root, text="Только показать, не переносить",
variable=dry_run, command=on_toggle)
check.pack()
check.invoke() # то же, что щелчок мышью
check.invoke()
Вывод:
Режим: переносить файлы
Режим: только показать
Метод invoke() делает то же, что щелчок мышью: переключает галочку и вызывает command. Им удобно проверять программу без окна. Галочка была включена, первый щелчок её снял, второй вернул.
В
variable=передают саму переменнуюdry_run, а неdry_run.get().get()вернёт простоTrue, и галочка ни с чем не будет связана. То же правило, что сcommand=on_toggleбез скобок: передаём объект, а не результат его вызова.
Проверьте себя
Задание. Что выведет программа? Введите значение, как его печатает Python.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() flag = tk.BooleanVar(value=False) check = tk.Checkbutton(root, text="Только показать", variable=flag) check.pack() check.invoke() check.invoke() check.invoke() print(flag.get())Подсказка: Каждый invoke() переключает галочку на противоположное состояние. С какого значения начали?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Галочка на экране ставится и снимается, но print(dry_run.get()) в обработчике всегда печатает одно и то же. Какая строка создаёт Checkbutton правильно?
Исходный код для этого задания:
dry_run = tk.BooleanVar(value=True)Подсказка: Какой параметр связывает галочку с переменной и что в него нужно передать — объект или его значение?
Онлайн-проверка ответа появится позже
Listbox: список файлов и выбор строки
tk.Listbox показывает строки столбиком, по одной на элемент. Основные методы:
lb.insert(tk.END, текст)— добавить строку в конец;tk.ENDозначает «после последней». Вместоtk.ENDможно дать номер:lb.insert(0, текст)вставит строку в начало;lb.delete(0, tk.END)— удалить всё от первой строки до последней (очистить список перед новым заполнением);lb.size()— сколько строк в списке;lb.get(i)— текст строки номерi(нумерация с нуля, как у списка Python);lb.curselection()— номера выделенных строк кортежем, например(2,); ничего не выделено — пустой кортеж().
Параметр height= при создании задаёт, сколько строк видно без прокрутки. Функцию заполнения сделаем отдельной: она получает список имён и сам Listbox параметрами, поэтому её можно проверить без окна.
import tkinter as tk
root = tk.Tk()
files_list = tk.Listbox(root, height=5)
files_list.pack()
def fill_list(listbox, names):
listbox.delete(0, tk.END)
for name in sorted(names):
listbox.insert(tk.END, name)
fill_list(files_list, ["отчёт.docx", "кот.jpg", "архив.zip"])
print(files_list.size(), files_list.get(0))
fill_list(files_list, ["договор.pdf"])
print(files_list.size(), files_list.get(0))
Вывод:
3 архив.zip
1 договор.pdf
Второй вызов не дописал строку к старым, а заменил список: первой строкой функции стоит очистка.
Что выбрал человек, узнают через curselection(). Щелчок мышью в примере заменяет метод selection_set(номер) — он выделяет строку из программы:
import tkinter as tk
root = tk.Tk()
files_list = tk.Listbox(root)
for name in ["договор.pdf", "кот.jpg", "архив.zip"]:
files_list.insert(tk.END, name)
def show_selected():
selected = files_list.curselection()
if not selected:
print("Ничего не выбрано")
return
print("Выбран:", files_list.get(selected[0]))
show_selected()
files_list.selection_set(1)
show_selected()
Вывод:
Ничего не выбрано
Выбран: кот.jpg

Проверьте себя
Задание. Сколько строк будет в списке после выполнения кода? Введите число.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() lb = tk.Listbox(root) for name in ["a.pdf", "b.jpg", "c.zip"]: lb.insert(tk.END, name) lb.delete(0, tk.END) for name in ["d.png", "e.txt"]: lb.insert(tk.END, name) lb.insert(0, "f.docx") print(lb.size())Подсказка: Что остаётся в списке после delete(0, tk.END)? Посчитайте только то, что добавлено после очистки.
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Что выведет программа? Введите имя файла.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() lb = tk.Listbox(root) lb.insert(tk.END, "отчёт.docx") lb.insert(tk.END, "обои.png") lb.insert(0, "договор.pdf") lb.insert(tk.END, "архив.zip") lb.selection_set(2) print(lb.get(lb.curselection()[0]))Подсказка: Выпишите строки списка по порядку после каждого insert, пронумеруйте с нуля и найдите выделенную.
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Допишите функцию fill_list целиком: она очищает список и добавляет в него только файлы, для которых в RULES есть правило; расширение сравнивайте без учёта регистра (Path(name).suffix.lower() из главы 1). Запустите программу и введите число, которое она выведет. Приложите код функции в поле комментария.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() files_list = tk.Listbox(root) names = ["отчёт.docx", "Кот.JPG", "readme", "фото.png", "setup.exe", "договор.pdf", "архив.tar.gz", "скан.PDF", "заметки.txt", "обои.jpg", "таблица.xlsx", "видео.mp4"] RULES = {".pdf": "Документы", ".docx": "Документы", ".jpg": "Картинки", ".png": "Картинки", ".zip": "Архивы"} def fill_list(listbox, names, rules): # допишите: очистить список и добавить только файлы, # у которых есть правило (регистр расширения не важен) ... fill_list(files_list, names, RULES) print(files_list.size())Подсказка: Расширение берите через Path(name).suffix и приводите к нижнему регистру. Какое расширение у «архив.tar.gz» по мнению suffix?
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
filedialog.askdirectory: выбор папки мышкой
Печатать путь к папке руками неудобно и легко ошибиться. В Tkinter есть стандартные окна выбора из модуля tkinter.filedialog, его подключают отдельно: from tkinter import filedialog.
filedialog.askdirectory(title="…")— окно выбора папки; возвращает путь строкой, например"C:/Users/ученик/Downloads"(разделитель — прямой слеш даже на Windows);filedialog.askopenfilename(title="…")— то же для одного файла;- параметр
initialdir=— в какой папке открыть окно (удобно подставить последнюю выбранную); - если человек нажал «Отмена» или закрыл окно, функция возвращает пустую строку
"". Это надо проверять, иначе программа начнёт читать «папку без имени».
Диалог ждёт человека, поэтому обработчик кнопки делим на две части: choose_folder только спрашивает путь, а вся работа с путём — в open_folder(path), которая получает путь параметром. Её можно вызвать из программы с любой папкой и проверить без щелчков.
import tkinter as tk
from tkinter import filedialog
from pathlib import Path
root = tk.Tk()
root.title("Сортировщик Загрузок")
folder = tk.StringVar(value="Папка не выбрана")
files_list = tk.Listbox(root, height=5)
def open_folder(path):
if not path: # «Отмена» — ничего не меняем
return
folder.set(path)
files_list.delete(0, tk.END)
for item in sorted(Path(path).iterdir()):
if item.is_file():
files_list.insert(tk.END, item.name)
def choose_folder():
path = filedialog.askdirectory(title="Какую папку разобрать?")
open_folder(path)
tk.Button(root, text="Выбрать папку", command=choose_folder).grid(row=0, column=0)
tk.Label(root, textvariable=folder).grid(row=0, column=1)
files_list.grid(row=1, column=0, columnspan=2)
open_folder("")
print(folder.get())
root.mainloop()
Если запустить без mainloop, строка open_folder("") имитирует «Отмену» и печатает Папка не выбрана: пустой путь функция отбросила. В окне choose_folder вызовется при нажатии кнопки, и в open_folder придёт путь из диалога.

Проверьте себя
Контрольный вопрос. Человек открыл окно выбора папки и нажал «Отмена». Что вернула filedialog.askdirectory()?
Подсказка: Посмотрите в списке функций модуля filedialog, что происходит при закрытии окна без выбора.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Программа открывает окно выбора папки сразу при запуске, а не по нажатию кнопки. Ошибка в одном аргументе Button. Как он должен выглядеть? Выберите вариант.
Исходный код для этого задания:
import tkinter as tk from tkinter import filedialog root = tk.Tk() def choose_folder(): path = filedialog.askdirectory() print(path) button = tk.Button(root, text="Выбрать папку", command=choose_folder()) button.pack() root.mainloop()Подсказка: Что Python делает со скобками после имени функции — передаёт функцию или сразу её вызывает?
Онлайн-проверка ответа появится позже
Задание. Функцию open_folder программа вызывает сама, как если бы из диалога пришли эти пути. Что выведет print? Введите текст вывода.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() status = tk.StringVar(value="Папка не выбрана") def open_folder(path): if not path: return status.set("Папка: " + path) open_folder("C:/Users/ученик/Pictures") open_folder("") open_folder("") print(status.get())Подсказка: Что делает функция с пустой строкой? Какой последний НЕпустой путь она получила?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Настройки в settings.json: запомнить и восстановить
Окно, которое при каждом запуске забывает папку и галочку, раздражает. Сохраним их тем же способом, что в главе 1: словарь → json.dump в файл, при запуске json.load обратно. В JSON кладём обычные значения из get() — строку и True/False; саму переменную Tkinter в JSON не записать.
При первом запуске файла ещё нет, а если его испортили руками — json.load не разберёт текст. На первый случай есть знакомое по главе 2 исключение FileNotFoundError. На второй — новое для нас json.JSONDecodeError: его выбрасывает json.load, когда текст файла — не правильный JSON (оборванная скобка, лишняя запятая). Оба ловим одним except с кортежем, как в главе 2. Тогда берём настройки по умолчанию. А чтобы не упасть, если в старом файле нет какого-то ключа, начинаем со словаря по умолчанию и обновляем его прочитанным: dict.update() перезаписывает совпавшие ключи и добавляет новые.
import json
from pathlib import Path
DEFAULTS = {"folder": "", "dry_run": True}
def load_settings(path):
settings = dict(DEFAULTS) # копия, DEFAULTS не портим
try:
with path.open(encoding="utf-8") as f:
settings.update(json.load(f))
except (FileNotFoundError, json.JSONDecodeError):
pass # берём значения по умолчанию
return settings
def save_settings(path, settings):
with path.open("w", encoding="utf-8") as f:
json.dump(settings, f, ensure_ascii=False, indent=2)
path = Path("settings.json")
print(load_settings(path))
save_settings(path, {"folder": "C:/Users/ученик/Downloads", "dry_run": False})
print(load_settings(path))
Вывод:
{'folder': '', 'dry_run': True}
{'folder': 'C:/Users/ученик/Downloads', 'dry_run': False}
Первый вызов не нашёл файла и вернул настройки по умолчанию, второй прочитал сохранённые. pass в except — «ничего не делать»: здесь это осознанно. В главе 2 мы договорились не глотать ошибки молча; исключение — когда ошибка ожидаемая и у нас уже есть запасной вариант. Так и тут: нет файла или он испорчен — значения по умолчанию уже лежат в settings, программа работает дальше.
Связываем с окном. При запуске: прочитали файл → folder.set(...), dry_run.set(...). Сохраняем в двух местах, где настройка меняется: после выбора папки и в command галочки.
def current_settings():
return {"folder": folder.get(), "dry_run": dry_run.get()}
def on_toggle():
save_settings(SETTINGS_PATH, current_settings())
settings = load_settings(SETTINGS_PATH)
folder.set(settings["folder"])
dry_run.set(settings["dry_run"])

Проверьте себя
Задание. Что выведет программа? Введите значение, как его печатает Python.
Исходный код для этого задания:
import json DEFAULTS = {"folder": "", "dry_run": True, "sort": "name"} text = '{"folder": "D:/Фото", "dry_run": false}' settings = dict(DEFAULTS) settings.update(json.loads(text)) print(settings["dry_run"])Подсказка: Какие ключи update() перезапишет значениями из текста? Во что json.loads превращает логические значения JSON?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Файла nastrojki.json в папке нет. Что выведет программа? Введите значение, как его печатает Python.
Исходный код для этого задания:
import json from pathlib import Path def load_settings(path): settings = {"folder": "Загрузки", "dry_run": True} try: with path.open(encoding="utf-8") as f: settings.update(json.load(f)) except (FileNotFoundError, json.JSONDecodeError): pass return settings print(load_settings(Path("nastrojki.json"))["folder"])Подсказка: Какая ошибка возникнет при открытии отсутствующего файла и что тогда вернёт функция?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Программа сохраняет настройки строкой json.dump({«dry_run»: dry_run}, f) и падает с TypeError. Как записать значение правильно?
Исходный код для этого задания:
dry_run = tk.BooleanVar(value=True)Подсказка: В JSON можно записать только обычные значения Python. Каким методом достают значение из переменной Tkinter?
Онлайн-проверка ответа появится позже
Собираем сортировщик: предпросмотр и перенос
Осталась кнопка «Разобрать». Логику раскладки (правила RULES из главы 1) держим в функции, которая получает папку и флаг параметрами и возвращает строки для списка. Окно только передаёт ей folder.get() и dry_run.get(). При включённой галочке функция ничего не переносит, а лишь показывает план — так человек видит, что произойдёт, до того как файлы поедут.
from pathlib import Path
import shutil
RULES = {
".pdf": "Документы", ".docx": "Документы",
".jpg": "Картинки", ".png": "Картинки",
".zip": "Архивы",
}
def plan_lines(names, rules):
lines = []
for name in sorted(names):
target = rules.get(Path(name).suffix.lower())
if target is not None:
lines.append(f"{name} -> {target}")
return lines
print(plan_lines(["кот.jpg", "readme", "договор.pdf", "фото.PNG"], RULES))
Вывод:
['договор.pdf -> Документы', 'кот.jpg -> Картинки', 'фото.PNG -> Картинки']
Полная программа окна. Она разбита комментариями на четыре части: настройки, логика раскладки, окно и запуск. Настройки и логику вы уже видели выше; новое здесь — show и on_sort. Функции sort_folder(path, dry_run_value) и plan_lines не знают про окно: их можно запустить и проверить отдельно. Обработчики кнопок только читают переменные и выводят результат.
import json
import shutil
import tkinter as tk
from tkinter import filedialog, messagebox
from pathlib import Path
# ---- настройки ----
RULES = {".pdf": "Документы", ".docx": "Документы", ".jpg": "Картинки", ".png": "Картинки", ".zip": "Архивы"}
SETTINGS_PATH = Path("settings.json")
DEFAULTS = {"folder": "", "dry_run": True}
def load_settings(path):
settings = dict(DEFAULTS)
try:
with path.open(encoding="utf-8") as f:
settings.update(json.load(f))
except (FileNotFoundError, json.JSONDecodeError):
pass
return settings
def save_settings(path, settings):
with path.open("w", encoding="utf-8") as f:
json.dump(settings, f, ensure_ascii=False, indent=2)
# ---- логика: план и перенос ----
def plan_lines(names, rules):
lines = []
for name in sorted(names):
target = rules.get(Path(name).suffix.lower())
if target is not None:
lines.append(f"{name} -> {target}")
return lines
def sort_folder(path, dry_run_value):
folder_path = Path(path)
names = [item.name for item in folder_path.iterdir() if item.is_file()]
lines = plan_lines(names, RULES)
if not dry_run_value:
for name in names:
target = RULES.get(Path(name).suffix.lower())
if target is not None:
(folder_path / target).mkdir(exist_ok=True)
shutil.move(folder_path / name, folder_path / target / name)
return lines
# ---- окно ----
root = tk.Tk()
root.title("Сортировщик Загрузок")
folder = tk.StringVar()
dry_run = tk.BooleanVar()
status = tk.StringVar(value="Выберите папку")
files_list = tk.Listbox(root, height=8, width=50)
def current_settings():
return {"folder": folder.get(), "dry_run": dry_run.get()}
def show(lines):
files_list.delete(0, tk.END)
for line in lines:
files_list.insert(tk.END, line)
status.set(f"Найдено файлов: {len(lines)}")
def open_folder(path):
if not path:
return
folder.set(path)
save_settings(SETTINGS_PATH, current_settings())
show(sort_folder(path, True)) # сразу показываем план, ничего не трогая
def choose_folder():
open_folder(filedialog.askdirectory(initialdir=folder.get() or None))
def on_toggle():
save_settings(SETTINGS_PATH, current_settings())
def on_sort():
if not folder.get():
messagebox.showwarning("Сортировщик", "Сначала выберите папку")
return
lines = sort_folder(folder.get(), dry_run.get())
show(lines)
if not dry_run.get():
messagebox.showinfo("Сортировщик", f"Перенесено файлов: {len(lines)}")
tk.Button(root, text="Выбрать папку", command=choose_folder).grid(row=0, column=0)
tk.Label(root, textvariable=folder).grid(row=0, column=1)
files_list.grid(row=1, column=0, columnspan=2)
tk.Checkbutton(root, text="Только показать, не переносить", variable=dry_run,
command=on_toggle).grid(row=2, column=0, columnspan=2)
tk.Button(root, text="Разобрать", command=on_sort).grid(row=3, column=0, columnspan=2)
tk.Label(root, textvariable=status).grid(row=4, column=0, columnspan=2)
# ---- запуск: восстановить настройки ----
settings = load_settings(SETTINGS_PATH)
dry_run.set(settings["dry_run"])
if settings["folder"] and Path(settings["folder"]).is_dir():
open_folder(settings["folder"])
root.mainloop()
Обратите внимание на open_folder: сразу после выбора папки вызывается sort_folder(path, True) — всегда только предпросмотр, чтобы человек увидел план. Переносит файлы лишь кнопка «Разобрать» при снятой галочке. initialdir=folder.get() or None открывает диалог в последней папке; если её нет — там, где решит система.
Создайте тестовую папку с пятью-шестью пустыми файлами разных типов, запустите программу, выберите эту папку и сравните список в окне с тем, что окажется в подпапках после «Разобрать» со снятой галочкой. Закройте и откройте программу снова — папка и галочка должны вернуться.
Проверьте себя
Задание. Что выведет программа? Введите число.
Исходный код для этого задания:
from pathlib import Path RULES = {".pdf": "Документы", ".docx": "Документы", ".jpg": "Картинки", ".png": "Картинки", ".zip": "Архивы"} def plan_lines(names, rules): lines = [] for name in sorted(names): target = rules.get(Path(name).suffix.lower()) if target is not None: lines.append(f"{name} -> {target}") return lines names = ["скан.PDF", "обои.jpeg", "кот.jpg", "backup.tar.gz", "презентация.pptx", "фото.Png", "notes", "проект.zip"] print(len(plan_lines(names, RULES)))Подсказка: Для каждого имени выпишите Path(name).suffix.lower() и проверьте, есть ли такой ключ в RULES. Внимательно с .jpeg и .tar.gz.
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Обработчик кнопки программа вызывает сама. Что окажется в строке состояния? Введите текст целиком.
Исходный код для этого задания:
import tkinter as tk root = tk.Tk() dry_run = tk.BooleanVar(value=True) status = tk.StringVar() check = tk.Checkbutton(root, variable=dry_run) def on_sort(): lines = ["кот.jpg -> Картинки", "договор.pdf -> Документы", "фото.png -> Картинки"] if dry_run.get(): status.set(f"План: {len(lines)} файла") else: status.set(f"Перенесено: {len(lines)}") check.invoke() on_sort() print(status.get())Подсказка: С каким значением создана dry_run и что с ней сделал invoke() до вызова on_sort()?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Сортировщик хранит последние папки списком recent, самая свежая — первой, не больше трёх. Что напечатает программа? Введите строку вывода целиком.
Исходный код для этого задания:
recent = ["Отчёты", "Фото", "Учёба"] for path in ["Фото", "Музыка", "Учёба"]: if path in recent: recent.remove(path) recent.insert(0, path) recent = recent[:3] print(", ".join(recent))Подсказка: Выписывайте recent после каждого шага цикла: remove убирает найденный путь, insert(0, …) ставит путь первым, срез [:3] отрезает всё после третьего элемента. Была ли «Учёба» в списке в тот момент, когда её выбрали?
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Проектная миссия «Предпросмотр сортировщика». Соберите окно: кнопка «Выбрать папку» (filedialog.askdirectory), Listbox, галочка «Только показать, не переносить» на BooleanVar, кнопка «Разобрать», строка состояния на StringVar. Правила — RULES урока (.pdf, .docx → Документы; .jpg, .png → Картинки; .zip → Архивы), регистр расширения не важен. Добавьте в окно второй флажок «Только картинки»: когда он включён, в Listbox попадают лишь строки «имя -> Картинки». Логику держите в функции, которая получает список имён и оба флага параметрами. Проверьте на своей папке. Ниже — содержимое папки «Загрузки» коллеги, 54 имени файла, по одному в строке. Сколько строк окажется в Listbox при включённых обоих флажках? Приложите файл программы .py; в поле ответа — число.
Принято, если:
1. число совпадает с эталоном;
2. приложен .py, где функция на этом списке имён выдаёт это число;Самопроверка перед сдачей (преподаватель смотрит на занятии):
3. флажки связаны с BooleanVar через variable=, в command передана функция без скобок;
4. при нажатии «Отмена» в диалоге программа не падает и не очищает список;
5. при включённом «Только показать» ни один файл не переносится.музыка_22.Png
резюме_62.jpeg
logo_65.exe
чек_62.pptx
backup_87.docx
backup_53.tar.gz
чек_64.PDF
logo_95.xlsx
резюме_41.Png
фото_8.exe
backup_28.docx
заметки_16.exe
обои_66.zip
setup_1.txt
чек_85.tar.gz
таблица_51.mp4
архив_88.jpg
кот_47.jpg
договор_20.docx
фото_41.pdf
кот_53.zip
заметки_26.png
презентация_59
заметки_76.pptx
backup_63.docx
отчёт_62.mp4
презентация_98.txt
фото_86.Png
отчёт_70.pptx
setup_90.jpeg
билет_94
кот_89.docx
logo_94.tar.gz
паспорт_36.xlsx
чек_53.zip
билет_80
презентация_3.mp4
setup_42.jpeg
фото_24.exe
logo_37.jpeg
заметки_55.xlsx
setup_83.txt
музыка_85.PDF
отчёт_30
договор_35.docx
музыка_16.png
счёт_26.docx
договор_88.pdf
музыка_56.txt
отчёт_20.xlsx
отчёт_12.JPG
паспорт_6.pdf
музыка_27.Png
фото_43.txt
Подсказка: Расширение берите через Path(name).suffix.lower(); проверьте, что делает suffix с .jpeg и .tar.gz.
✅ Готово, если: ответ раскрывает то, что просит критерий приёмки в задании выше.
Онлайн-проверка ответа появится позже
Задание. Проектная миссия «Недавние папки». Доработайте сортировщик: в settings.json, кроме folder и dry_run, хранится ключ recent — список из не более чем 5 последних выбранных папок, самая свежая первой. Функция remember(settings, path) получает путь параметром: пустой путь (отмена диалога) игнорирует; если путь уже есть в recent — убирает его оттуда и ставит в начало, считая это «повторным выбором»; если нет — ставит в начало и обрезает список до 5. После каждого выбора настройки сохраняются в JSON, а recent показывается в Listbox; двойного щелчка не нужно — достаточно кнопки «Открыть выбранную», которая берёт путь через curselection(). Ниже — журнал 50 выборов папки по порядку, по одному в строке; слово ОТМЕНА означает, что человек закрыл диалог и askdirectory вернул пустую строку. Начальный recent пуст. Сколько раз за журнал случился повторный выбор (путь уже был в recent в момент выбора)? Приложите файл программы .py; в поле ответа — число.
Принято, если:
1. число совпадает с эталоном;
2. приложен .py, где remember() на этом журнале выдаёт это число;Самопроверка перед сдачей (преподаватель смотрит на занятии):
3. после перезапуска программы recent, папка и галочка восстанавливаются из settings.json;
4. удалённый или испорченный settings.json не роняет программу — берутся значения по умолчанию;
5. «Открыть выбранную» без выделенной строки не падает.C:/Users/ученик/Pictures
E:/Флешка
D:/Работа/Отчёты
C:/Users/ученик/Downloads
C:/Users/ученик/Downloads
C:/Users/ученик/Downloads
E:/Флешка
D:/Работа/Отчёты
ОТМЕНА
C:/Users/ученик/Desktop
C:/Users/ученик/Desktop
D:/Работа/Договоры
D:/Фото/2024
C:/Users/ученик/Desktop
C:/Users/ученик/Pictures
C:/Users/ученик/Desktop
D:/Учёба
E:/Флешка
E:/Флешка
D:/Работа/Отчёты
D:/Фото/2025
C:/Users/ученик/Pictures
D:/Работа/Договоры
D:/Фото/2025
D:/Фото/2024
D:/Работа/Отчёты
C:/Users/ученик/Pictures
D:/Фото/2024
D:/Учёба
D:/Работа/Договоры
D:/Фото/2024
D:/Фото/2024
E:/Флешка
D:/Работа/Договоры
E:/Флешка
E:/Флешка
C:/Users/ученик/Desktop
D:/Фото/2025
C:/Users/ученик/Downloads
C:/Users/ученик/Downloads
C:/Users/ученик/Downloads
C:/Users/ученик/Pictures
D:/Фото/2024
C:/Users/ученик/Desktop
D:/Фото/2024
C:/Users/ученик/Desktop
D:/Работа/Отчёты
D:/Фото/2025
D:/Работа/Отчёты
ОТМЕНА
Подсказка: Храните recent обычным списком Python: проверка in, remove(), insert(0, …) и срез [:5]. Важно, был ли путь в recent именно в момент выбора — после обрезки до 5 старые пути выпадают.
✅ Готово, если: ответ раскрывает то, что просит критерий приёмки в задании выше.
Онлайн-проверка ответа появится позже
Что дальше
Теперь вы умеете связывать поля и галочки с переменными StringVar и BooleanVar, показывать и выбирать строки в Listbox, спрашивать папку через filedialog.askdirectory и не падать на «Отмене», а настройки окна сохранять в JSON и восстанавливать при запуске. Сортировщик стал программой, которой может пользоваться человек без терминала. Осталось отдать её коллеге без Python — в следующем уроке соберём из неё exe через PyInstaller.
← Назад · ↑ В начало урока · ⌂ К навигатору курса · Вперёд →
