Просмотр исходного кода

Загрузить файлы ''

u23-27tsukanovich 1 месяц назад
Родитель
Сommit
ef7b976cb5
3 измененных файлов с 1323 добавлено и 0 удалено
  1. 1168 0
      PROECT.PY
  2. BIN
      PROECT.txt
  3. 155 0
      тз.md

+ 1168 - 0
PROECT.PY

@@ -0,0 +1,1168 @@
+import tkinter as tk
+from tkinter import messagebox
+import random
+import os
+from pathlib import Path
+from typing import Dict, List, Optional, Tuple
+
+# Попытка импорта PIL для работы с изображениями
+try:
+    from PIL import Image
+    PIL_AVAILABLE = True
+except ImportError:
+    PIL_AVAILABLE = False
+    print("PIL (Pillow) не установлен. Установите: pip install Pillow")
+
+# ==================== КОНСТАНТЫ ====================
+WINDOW_SIZE = "1000x700"
+IMAGES_FOLDER = "quiz_images"
+SUPPORTED_FORMATS = ('.jpg', '.jpeg', '.png', '.bmp', '.ico', '.tiff')
+DEFAULT_COLORS = [
+    '#f0f8ff', '#f5f5dc', '#e6e6fa', '#ffe4e1', '#f0fff0',
+    '#fff0f5', '#e0ffff', '#faf0e6', '#fffacd', '#f8f8ff'
+]
+
+# ==================== СПИСОК ВОПРОСОВ ====================
+questions = [
+    {
+        'id': 1,
+        'question': 'Что понимается под "персональными данными" согласно законодательству?',
+        'options': [
+            'Только паспортные данные и фотографии',
+            'Любая информация, прямо или косвенно относящаяся к конкретному физическому лицу',
+            'Только контактные данные человека',
+            'Информация о юридических лицах'
+        ],
+        'correct': 1,
+        'explanation': 'Персональные данные - любые сведения, которые относятся, непосредственно либо косвенно, к конкретному физическому лицу.',
+        'image': 'q1.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 2,
+        'question': 'Кто такой "оператор" персональных данных?',
+        'options': [
+            'Человек, чьи данные обрабатываются',
+            'Государственный орган, контролирующий защиту данных',
+            'Лицо или орган, осуществляющий обработку ПД и определяющий цели обработки',
+            'Сотрудник, работающий с базами данных'
+        ],
+        'correct': 2,
+        'explanation': 'Оператор - лицо или орган, осуществляющий обработку персональных данных и определяющий цели обработки, состав данных и действия с ними.',
+        'image': 'q2.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 3,
+        'question': 'Что включает в себя система защиты персональных данных?',
+        'options': [
+            'Только антивирусное программное обеспечение',
+            'Только организационные меры',
+            'Организационные и технические меры, определенные с учетом актуальных угроз',
+            'Только физическую охрану помещений'
+        ],
+        'correct': 2,
+        'explanation': 'Система защиты включает организационные и технические меры, определенные с учетом актуальных угроз безопасности.',
+        'image': 'q3.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 5,
+        'question': 'Сколько уровней защищенности персональных данных устанавливается при их обработке в информационных системах?',
+        'options': [
+            '2 уровня',
+            '3 уровня',
+            '4 уровня',
+            '5 уровней'
+        ],
+        'correct': 2,
+        'explanation': 'При обработке персональных данных в информационных системах устанавливаются 4 уровня защищенности.',
+        'image': 'q5.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 6,
+        'question': 'Какие данные относятся к специальным категориям персональных данных?',
+        'options': [
+            'ФИО и адрес проживания',
+            'Информация о расовой принадлежности, политических взглядах, здоровье',
+            'Номер телефона и email',
+            'Место работы и должность'
+        ],
+        'correct': 1,
+        'explanation': 'К специальным категориям относятся данные о расовой принадлежности, политических взглядах, религиозных убеждениях, здоровье.',
+        'image': 'q6.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 7,
+        'question': 'Что относится к биометрическим персональным данным?',
+        'options': [
+            'ФИО и дата рождения',
+            'Физиологические особенности для идентификации: отпечатки пальцев, ДНК, группа крови',
+            'Номер паспорта',
+            'Адрес проживания'
+        ],
+        'correct': 1,
+        'explanation': 'Биометрические данные - физиологические особенности, используемые для идентификации личности.',
+        'image': 'q7.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 8,
+        'question': 'Как часто должен проводиться контроль за выполнением требований защиты ПД?',
+        'options': [
+            'Ежемесячно',
+            'Ежегодно',
+            'Не реже 1 раза в 3 года',
+            'Один раз в 5 лет'
+        ],
+        'correct': 2,
+        'explanation': 'Контроль проводится не реже 1 раза в 3 года в сроки, определяемые оператором.',
+        'image': 'q8.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 9,
+        'question': 'Какой федеральный закон регулирует защиту персональных данных в России?',
+        'options': [
+            'ФЗ "Об информации"',
+            'Федеральный закон №152-ФЗ "О персональных данных"',
+            'ФЗ "О связи"',
+            'ФЗ "О безопасности"'
+        ],
+        'correct': 1,
+        'explanation': 'В России требования к обработке и защите персональных данных определяет Федеральный закон №152-ФЗ "О персональных данных".',
+        'image': 'q9.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 10,
+        'question': 'Что такое GDPR?',
+        'options': [
+            'Российский закон о данных',
+            'Американский стандарт безопасности',
+            'Европейский регламент по защите данных',
+            'Международный стандарт шифрования'
+        ],
+        'correct': 2,
+        'explanation': 'GDPR - Общий регламент по защите данных, служит правилом для защиты личных данных граждан ЕС.',
+        'image': 'q10.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 11,
+        'question': 'Какие требования предъявляются к сбору персональных данных?',
+        'options': [
+            'Сбор только с согласия субъекта и ограничение только необходимыми данными',
+            'Сбор любой доступной информации',
+            'Сбор без согласия, если это удобно',
+            'Только сбор через официальные запросы'
+        ],
+        'correct': 0,
+        'explanation': 'Персональные данные могут собираться только при согласии субъекта и ограничиваться только необходимыми данными.',
+        'image': 'q11.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 12,
+        'question': 'Что означает требование "целостность и конфиденциальность" ПД?',
+        'options': [
+            'Данные должны быть красиво оформлены',
+            'Защита от несанкционированного доступа, потери и уничтожения',
+            'Данные должны быть доступны всем',
+            'Данные должны храниться вечно'
+        ],
+        'correct': 1,
+        'explanation': 'Данные должны защищаться от несанкционированного доступа, потери, повреждения или уничтожения.',
+        'image': 'q12.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 13,
+        'question': 'Что означает "ограничение сроков хранения" персональных данных?',
+        'options': [
+            'Хранить данные бесконечно',
+            'Хранить только необходимое для целей обработки время',
+            'Удалять сразу после получения',
+            'Хранить минимум 10 лет'
+        ],
+        'correct': 1,
+        'explanation': 'Хранить персональные сведения можно только столько, сколько необходимо для достижения целей их обработки.',
+        'image': 'q13.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 14,
+        'question': 'В каких сферах применяются персональные данные?',
+        'options': [
+            'Только в медицине',
+            'Только в банковской сфере',
+            'Практически во всех сферах: коммерция, финансы, медицина, образование, госорганы',
+            'Только в интернете'
+        ],
+        'correct': 2,
+        'explanation': 'Персональные данные используются практически в любой сфере современного цифрового мира.',
+        'image': 'q14.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 15,
+        'question': 'Какие данные относятся к "общим" согласно 152-ФЗ?',
+        'options': [
+            'ФИО, место регистрации, образование, контактная информация',
+            'Данные о здоровье',
+            'Отпечатки пальцев',
+            'Политические взгляды'
+        ],
+        'correct': 0,
+        'explanation': 'Общие данные: ФИО, место регистрации, сведения об образовании, месте работы, контактная информация.',
+        'image': 'q15.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 16,
+        'question': 'Что должно быть предусмотрено в договоре между оператором и уполномоченным лицом?',
+        'options': [
+            'Стоимость услуг',
+            'Обязанность обеспечить безопасность ПД при обработке',
+            'Срок действия договора',
+            'Реквизиты сторон'
+        ],
+        'correct': 1,
+        'explanation': 'Договор должен предусматривать обязанность уполномоченного лица обеспечить безопасность ПД при их обработке.',
+        'image': 'q16.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 17,
+        'question': 'Кто обеспечивает безопасность персональных данных в информационной системе?',
+        'options': [
+            'ФСБ России',
+            'Оператор системы или уполномоченное им лицо',
+            'Роскомнадзор',
+            'Сам субъект данных'
+        ],
+        'correct': 1,
+        'explanation': 'Безопасность обеспечивает оператор системы или лицо, осуществляющее обработку по поручению оператора.',
+        'image': 'q17.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 18,
+        'question': 'Какие угрозы относятся к 1-му типу?',
+        'options': [
+            'Только внешние атаки',
+            'Угрозы, связанные с недокументированными возможностями в системном ПО',
+            'Только физические угрозы',
+            'Угрозы от сотрудников'
+        ],
+        'correct': 1,
+        'explanation': 'Угрозы 1-го типа связаны с наличием недокументированных возможностей в системном программном обеспечении.',
+        'image': 'q18.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 19,
+        'question': 'Какие угрозы относятся ко 2-му типу?',
+        'options': [
+            'Угрозы от хакеров',
+            'Угрозы, связанные с недокументированными возможностями в прикладном ПО',
+            'Угрозы пожаров и наводнений',
+            'Угрозы отключения электричества'
+        ],
+        'correct': 1,
+        'explanation': 'Угрозы 2-го типа связаны с наличием недокументированных возможностей в прикладном программном обеспечении.',
+        'image': 'q19.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 20,
+        'question': 'Какие угрозы относятся к 3-му типу?',
+        'options': [
+            'Угрозы из интернета',
+            'Угрозы, не связанные с недокументированными возможностями в системном и прикладном ПО',
+            'Только физические угрозы',
+            'Угрозы от вирусов'
+        ],
+        'correct': 1,
+        'explanation': 'Угрозы 3-го типа - угрозы, не связанные с наличием недокументированных возможностей в ПО.',
+        'image': 'q20.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 21,
+        'question': 'Когда устанавливается необходимость 1-го уровня защищенности?',
+        'options': [
+            'Всегда для всех систем',
+            'При угрозах 1 типа и обработке специальных или биометрических данных',
+            'Только для государственных систем',
+            'При обработке менее 100 записей'
+        ],
+        'correct': 1,
+        'explanation': '1-й уровень необходим при угрозах 1 типа и обработке специальных категорий или биометрических данных.',
+        'image': 'q21.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 22,
+        'question': 'Какие требования предъявляются для 4-го уровня защищенности?',
+        'options': [
+            'Создание специального отдела',
+            'Организация безопасности помещений, сохранность носителей, утверждение списка допущенных лиц',
+            'Шифрование всех данных',
+            'Ежедневный аудит'
+        ],
+        'correct': 1,
+        'explanation': '4-й уровень требует безопасности помещений, сохранности носителей и утверждения списка допущенных лиц.',
+        'image': 'q22.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 23,
+        'question': 'Что дополнительно требуется для 3-го уровня защищенности?',
+        'options': [
+            'Создание отдела защиты данных',
+            'Назначение ответственного за безопасность ПД в ИС',
+            'Автоматическая регистрация изменений',
+            'Шифрование каналов связи'
+        ],
+        'correct': 1,
+        'explanation': 'Для 3-го уровня необходимо назначение должностного лица, ответственного за безопасность ПД в ИС.',
+        'image': 'q23.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 24,
+        'question': 'Что дополнительно требуется для 2-го уровня защищенности?',
+        'options': [
+            'Видеонаблюдение',
+            'Ограничение доступа к электронному журналу сообщений',
+            'Биометрическая идентификация',
+            'Еженедельные проверки'
+        ],
+        'correct': 1,
+        'explanation': 'Для 2-го уровня доступ к электронному журналу должен быть только для лиц, которым это необходимо.',
+        'image': 'q24.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 25,
+        'question': 'Что дополнительно требуется для 1-го уровня защищенности?',
+        'options': [
+            'Автоматическая регистрация изменений полномочий и создание структурного подразделения',
+            'Ежечасный мониторинг',
+            'Вооруженная охрана',
+            'Спутниковое наблюдение'
+        ],
+        'correct': 0,
+        'explanation': '1-й уровень требует автоматической регистрации изменений полномочий и создания структурного подразделения.',
+        'image': 'q25.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 26,
+        'question': 'Что должно содержать уведомление в Роскомнадзор о начале обработки ПД?',
+        'options': [
+            'Только название организации',
+            'Информацию о целях обработки, категориях данных и мерах защиты',
+            'Только контактные данные',
+            'Только ФИО директора'
+        ],
+        'correct': 1,
+        'explanation': 'Уведомление должно содержать информацию о целях, категориях данных и мерах защиты.',
+        'image': 'q26.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 27,
+        'question': 'Что такое трансграничная передача данных?',
+        'options': [
+            'Передача внутри организации',
+            'Передача данных за пределы государства',
+            'Передача по локальной сети',
+            'Передача на флешке'
+        ],
+        'correct': 1,
+        'explanation': 'Трансграничная передача - это передача персональных данных на территорию иностранного государства.',
+        'image': 'q27.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 28,
+        'question': 'Кто должен давать согласие на обработку данных детей?',
+        'options': [
+            'Сам ребенок',
+            'Учитель',
+            'Законные представители (родители)',
+            'Директор школы'
+        ],
+        'correct': 2,
+        'explanation': 'Для обработки данных детей требуется согласие законных представителей.',
+        'image': 'q28.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 29,
+        'question': 'Что должно быть сделано при утечке персональных данных?',
+        'options': [
+            'Ничего, это не обязательно',
+            'Уведомление владельцев данных и контролирующих органов',
+            'Только смена паролей',
+            'Увольнение сотрудников'
+        ],
+        'correct': 1,
+        'explanation': 'В случае утечки владельцы данных должны быть своевременно уведомлены.',
+        'image': 'q29.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 30,
+        'question': 'Что такое обезличивание персональных данных?',
+        'options': [
+            'Удаление фотографий',
+            'Действия, после которых нельзя определить принадлежность данных конкретному лицу',
+            'Шифрование данных',
+            'Смена имен'
+        ],
+        'correct': 1,
+        'explanation': 'Обезличивание делает невозможным идентификацию человека без использования дополнительной информации.',
+        'image': 'q30.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 31,
+        'question': 'Кто осуществляет выбор средств защиты информации для системы защиты ПД?',
+        'options': [
+            'Роскомнадзор',
+            'Оператор в соответствии с нормативными актами ФСБ и ФСТЭК',
+            'Субъект данных',
+            'Поставщик оборудования'
+        ],
+        'correct': 1,
+        'explanation': 'Выбор средств защиты осуществляется оператором в соответствии с актами ФСБ и ФСТЭК.',
+        'image': 'q31.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 32,
+        'question': 'Какие организации могут привлекаться для контроля защиты ПД?',
+        'options': [
+            'Любые организации',
+            'Юридические лица и ИП с лицензией на техзащиту информации',
+            'Только государственные органы',
+            'Только ФСБ'
+        ],
+        'correct': 1,
+        'explanation': 'Могут привлекаться организации и ИП, имеющие лицензию на техзащиту конфиденциальной информации.',
+        'image': 'q32.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 33,
+        'question': 'Что относится к организационным мерам защиты ПД?',
+        'options': [
+            'Установка файерволов',
+            'Назначение ответственных лиц, разработка документов, инструктаж сотрудников',
+            'Шифрование дисков',
+            'Антивирусная защита'
+        ],
+        'correct': 1,
+        'explanation': 'Организационные меры включают назначение ответственных, разработку документов и обучение сотрудников.',
+        'image': 'q33.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 34,
+        'question': 'Что относится к техническим мерам защиты ПД?',
+        'options': [
+            'Проведение инструктажей',
+            'Антивирусы, межсетевые экраны, системы шифрования',
+            'Разработка политик безопасности',
+            'Подписание согласий'
+        ],
+        'correct': 1,
+        'explanation': 'Технические меры включают программно-аппаратные средства защиты информации.',
+        'image': 'q34.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 35,
+        'question': 'Какие данные являются общедоступными?',
+        'options': [
+            'Все данные в интернете',
+            'Данные, доступ к которым предоставлен самим субъектом',
+            'Паспортные данные',
+            'Медицинские записи'
+        ],
+        'correct': 1,
+        'explanation': 'Общедоступные данные - это данные, доступ к которым предоставлен самим субъектом.',
+        'image': 'q35.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 36,
+        'question': 'Что такое инцидент с персональными данными?',
+        'options': [
+            'Плановое обновление ПО',
+            'Нарушение безопасности, приводящее к утечке или компрометации данных',
+            'Обычная проверка системы',
+            'Резервное копирование'
+        ],
+        'correct': 1,
+        'explanation': 'Инцидент - это любое событие, которое привело к нарушению защиты данных.',
+        'image': 'q36.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 37,
+        'question': 'Какие требования предъявляются к точности и актуальности данных?',
+        'options': [
+            'Данные можно не обновлять',
+            'Важно обеспечивать правильность и своевременность данных',
+            'Точность не важна',
+            'Достаточно собрать данные один раз'
+        ],
+        'correct': 1,
+        'explanation': 'Важно обеспечивать правильность и своевременность данных.',
+        'image': 'q37.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 38,
+        'question': 'Что означает требование "законность, справедливость и прозрачность"?',
+        'options': [
+            'Данные должны быть доступны всем',
+            'Обработка должна вестись легально, честно и ясно для граждан',
+            'Данные должны публиковаться',
+            'Обработка может быть скрытой'
+        ],
+        'correct': 1,
+        'explanation': 'Обработка должна вестись легально, честно и ясно для граждан, чьи данные собираются.',
+        'image': 'q38.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 39,
+        'question': 'Что такое электронный журнал безопасности?',
+        'options': [
+            'Список сотрудников',
+            'Средство регистрации событий доступа и изменений в системе',
+            'Журнал учета посетителей',
+            'База данных клиентов'
+        ],
+        'correct': 1,
+        'explanation': 'Электронный журнал регистрирует события доступа и изменения в системе.',
+        'image': 'q39.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 40,
+        'question': 'К какому типу данных относится данные о заработной плате?',
+        'options': [
+            'Не являются персональными данными',
+            'Являются персональными данными и не подлежат разглашению без согласия',
+            'Могут публиковаться свободно',
+            'Относятся к общедоступным'
+        ],
+        'correct': 1,
+        'explanation': 'Сведения о заработной плате - это персональные данные и не подлежат разглашению без согласия.',
+        'image': 'q40.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 41,
+        'question': 'Что должен сделать оператор перед началом обработки ПД?',
+        'options': [
+            'Ничего',
+            'Подать уведомление в Роскомнадзор',
+            'Купить оборудование',
+            'Нанять сотрудников'
+        ],
+        'correct': 1,
+        'explanation': 'Необходимо подать уведомление о начале обработки ПД в Роскомнадзор для регистрации в качестве оператора.',
+        'image': 'q41.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 42,
+        'question': 'Что такое модель угроз для ИСПД?',
+        'options': [
+            'Описание возможных злоумышленников',
+            'Документ, описывающий актуальные угрозы безопасности для конкретной системы',
+            'Программа для взлома',
+            'Схема сети'
+        ],
+        'correct': 1,
+        'explanation': 'Модель угроз - документ, описывающий актуальные угрозы для конкретной информационной системы.',
+        'image': 'q42.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 43,
+        'question': 'Какие существуют категории персональных данных по 152-ФЗ?',
+        'options': [
+            'Только общие и специальные',
+            'Общие, особые, биометрические и иные данные',
+            'Только биометрические',
+            'Только контактные'
+        ],
+        'correct': 1,
+        'explanation': 'Закон выделяет общие, особые, биометрические и иные категории персональных данных.',
+        'image': 'q43.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 44,
+        'question': 'Что должно содержать согласие на обработку ПД?',
+        'options': [
+            'Только подпись',
+            'Конкретные цели обработки, перечень данных, срок действия',
+            'Только ФИО',
+            'Только дату'
+        ],
+        'correct': 1,
+        'explanation': 'Согласие должно быть конкретным, содержать цели обработки, перечень данных и срок действия.',
+        'image': 'q44.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 45,
+        'question': 'Кто такой уполномоченный по защите данных?',
+        'options': [
+            'Сотрудник полиции',
+            'Лицо, ответственное за контроль соблюдения законодательства о данных',
+            'Программист',
+            'Начальник отдела кадров'
+        ],
+        'correct': 1,
+        'explanation': 'Это лицо, ответственное за контроль соблюдения законодательства о персональных данных.',
+        'image': 'q45.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 46,
+        'question': 'Что такое техническое задание на создание системы защиты?',
+        'options': [
+            'Рекламный буклет',
+            'Документ с требованиями к создаваемой системе защиты ПД',
+            'Инструкция пользователя',
+            'Договор с поставщиком'
+        ],
+        'correct': 1,
+        'explanation': 'ТЗ - документ с требованиями по формированию требуемой системы защиты.',
+        'image': 'q46.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 47,
+        'question': 'Какие данные относятся к "иным" согласно 152-ФЗ?',
+        'options': [
+            'Данные о здоровье',
+            'Все, что не попадает в общие, особые и биометрические категории',
+            'Только фотографии',
+            'Только отпечатки пальцев'
+        ],
+        'correct': 1,
+        'explanation': 'Иные данные - всё, что не попадает в предыдущие категории.',
+        'image': 'q47.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 48,
+        'question': 'Что такое ФСТЭК России?',
+        'options': [
+            'Производитель компьютеров',
+            'Федеральная служба по техническому и экспортному контролю',
+            'Провайдер интернета',
+            'Страховая компания'
+        ],
+        'correct': 1,
+        'explanation': 'ФСТЭК - федеральная служба по техническому и экспортному контролю, регулирующая вопросы защиты информации.',
+        'image': 'q48.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 49,
+        'question': 'Какие требования предъявляются к помещениям, где обрабатываются ПД?',
+        'options': [
+            'Наличие кондиционера',
+            'Режим, препятствующий неконтролируемому проникновению посторонних',
+            'Наличие коврового покрытия',
+            'Яркое освещение'
+        ],
+        'correct': 1,
+        'explanation': 'Необходима организация режима, препятствующего неконтролируемому проникновению посторонних.',
+        'image': 'q49.jpg',
+        'bg_color': None
+    },
+    {
+        'id': 50,
+        'question': 'Какова основная цель выполнения требований по защите персональных данных?',
+        'options': [
+            'Выполнение формальностей',
+            'Минимизация вреда от возможной реализации угроз безопасности',
+            'Экономия средств',
+            'Повышение престижа компании'
+        ],
+        'correct': 1,
+        'explanation': 'Основная цель - минимизация вреда, возникающего из-за возможной реализации угроз безопасности.',
+        'image': 'q50.jpg',
+        'bg_color': None
+    }
+]  # Остальные вопросы аналогичны, для краткости сократил до 10
+
+class ImageManager:
+    """Класс для управления изображениями"""
+    
+    def __init__(self, images_folder: str):
+        self.images_folder = Path(images_folder)
+        self.cache = {}  # Кэш для изображений
+        self.pil_available = PIL_AVAILABLE
+        
+    def find_image(self, image_name: str) -> Optional[Path]:
+        """Поиск изображения по имени с разными расширениями"""
+        if not image_name:
+            return None
+            
+        # Проверка кэша
+        if image_name in self.cache:
+            return self.cache[image_name]
+        
+        # Поиск файла
+        image_path = self.images_folder / image_name
+        if image_path.exists():
+            self.cache[image_name] = image_path
+            return image_path
+        
+        # Поиск с разными расширениями
+        name_without_ext = image_path.stem
+        for ext in SUPPORTED_FORMATS:
+            test_path = self.images_folder / f"{name_without_ext}{ext}"
+            if test_path.exists():
+                self.cache[image_name] = test_path
+                return test_path
+        
+        return None
+    
+    def load_image(self, image_path: Path, bg_color: str = '#ffffff', size: Tuple[int, int] = (350, 350)):
+        """Загрузка и обработка изображения"""
+        if not self.pil_available:
+            if image_path.suffix.lower() == '.jpg':
+                return tk.PhotoImage(file=str(image_path))
+            raise Exception("Для работы с изображениями требуется Pillow")
+        
+        img = Image.open(image_path)
+        
+        # Обработка анимированных GIF
+        if getattr(img, 'is_animated', False):
+            img.seek(0)
+        
+        # Обработка прозрачности
+        if img.mode == 'RGBA':
+            bg_rgb = tuple(int(bg_color[i:i+2], 16) for i in (1, 3, 5))
+            background = Image.new('RGB', img.size, bg_rgb)
+            background.paste(img, mask=img.split()[3])
+            img = background
+        elif img.mode != 'RGB':
+            img = img.convert('RGB')
+        
+        # Изменение размера
+        img.thumbnail(size, Image.Resampling.LANCZOS)
+        return ImageTk.PhotoImage(img)
+    
+    def get_dominant_color(self, image_path: Path) -> Optional[str]:
+        """Получение доминирующего цвета изображения"""
+        if not self.pil_available:
+            return None
+        
+        try:
+            img = Image.open(image_path)
+            
+            if getattr(img, 'is_animated', False):
+                img.seek(0)
+            
+            # Конвертация в RGB
+            if img.mode == 'RGBA':
+                background = Image.new('RGB', img.size, (255, 255, 255))
+                background.paste(img, mask=img.split()[3])
+                img = background
+            elif img.mode != 'RGB':
+                img = img.convert('RGB')
+            
+            # Уменьшение для ускорения
+            img.thumbnail((100, 100))
+            pixels = img.getdata()
+            
+            # Вычисление среднего цвета
+            r_sum = g_sum = b_sum = 0
+            for pixel in pixels:
+                r_sum += pixel[0]
+                g_sum += pixel[1]
+                b_sum += pixel[2]
+            
+            count = len(pixels)
+            if count == 0:
+                return None
+            
+            return f'#{r_sum//count:02x}{g_sum//count:02x}{b_sum//count:02x}'
+        except Exception as e:
+            print(f"Ошибка определения цвета: {e}")
+            return None
+
+
+class QuizApp:
+    """Основной класс приложения"""
+    
+    def __init__(self, master):
+        self.master = master
+        self.master.title("Викторина: Защита персональных данных")
+        self.master.geometry(WINDOW_SIZE)
+        self.master.resizable(False, False)
+        
+        # Инициализация
+        self.questions = questions.copy()
+        self.current_question = 0
+        self.correct_answers = 0
+        self.total_questions = len(self.questions)
+        
+        # Менеджеры
+        self.image_manager = ImageManager(IMAGES_FOLDER)
+        
+        # Переменные состояния
+        self.var = None
+        self.option_mapping = {}
+        self.current_image = None
+        
+        # Привязка клавиш
+        self._bind_keys()
+        
+        # Запуск
+        self.show_start_screen()
+    
+    def _bind_keys(self):
+        """Привязка клавиатурных комбинаций"""
+        self.master.bind('<Key>', self.on_key_press)
+        self.master.bind('<Return>', lambda e: self.check_answer())
+        self.master.bind('<Escape>', lambda e: self.quit_app())
+    
+    def on_key_press(self, event):
+        """Обработка нажатий клавиш"""
+        if event.char in '1234' and self.var is not None:
+            self.var.set(int(event.char) - 1)
+    
+    def quit_app(self):
+        """Выход из приложения"""
+        if messagebox.askyesno("Выход", "Вы действительно хотите выйти?"):
+            self.master.quit()
+    
+    def clear_window(self):
+        """Очистка окна"""
+        for widget in self.master.winfo_children():
+            widget.destroy()
+    
+    def shuffle_questions(self):
+        """Перемешивание вопросов"""
+        random.shuffle(self.questions)
+    
+    def get_text_color(self, bg_hex: str) -> str:
+        """Определение цвета текста на основе фона"""
+        if not bg_hex:
+            return 'black'
+        try:
+            r = int(bg_hex[1:3], 16)
+            g = int(bg_hex[3:5], 16)
+            b = int(bg_hex[5:7], 16)
+            brightness = 0.299 * r + 0.587 * g + 0.114 * b
+            return 'black' if brightness > 128 else 'white'
+        except:
+            return 'black'
+    
+    def show_start_screen(self):
+        """Экран приветствия"""
+        self.clear_window()
+        self.master.config(bg='lightblue')
+        
+        # Заголовок
+        title = tk.Label(self.master, text="Викторина по защите персональных данных",
+                        font=("Arial", 20, "bold"), bg='lightblue', fg='navy')
+        title.pack(pady=40)
+        
+        # Инструкция
+        instr_text = (
+            f"Добро пожаловать!\n\n"
+            f"Эта викторина проверит ваши знания об основах защиты персональных данных.\n"
+            f"Правила:\n"
+            f"• Всего вопросов: {self.total_questions}\n"
+            f"• Выберите один правильный вариант.\n"
+            f"• Управление с клавиатуры: клавиши 1-4 для выбора ответа\n"
+            f"• Нажмите Enter для проверки ответа\n"
+            f"• Esc для выхода\n"
+            f"• После ответа вы увидите пояснение.\n\n"
+            f"Удачи!"
+        )
+        
+        instr_label = tk.Label(self.master, text=instr_text, font=("Arial", 12),
+                              bg='lightblue', justify='left')
+        instr_label.pack(pady=20)
+        
+        # Кнопка старта
+        start_btn = tk.Button(self.master, text="Начать викторину", font=("Arial", 14),
+                             command=self.start_quiz, bg='green', fg='white',
+                             padx=20, pady=10, cursor='hand2')
+        start_btn.pack(pady=30)
+    
+    def start_quiz(self):
+        """Начало викторины"""
+        self.shuffle_questions()
+        self.current_question = 0
+        self.correct_answers = 0
+        self.show_question()
+    
+    def get_background_color(self, q: Dict, index: int) -> str:
+        """Получение цвета фона для вопроса"""
+        if q.get('bg_color'):
+            return q['bg_color']
+        
+        if PIL_AVAILABLE and q.get('image'):
+            image_path = self.image_manager.find_image(q['image'])
+            if image_path:
+                color = self.image_manager.get_dominant_color(image_path)
+                if color:
+                    return color
+        
+        return DEFAULT_COLORS[index % len(DEFAULT_COLORS)]
+    
+    def create_option_buttons(self, parent, options: List, bg_color: str, text_color: str):
+        """Создание кнопок вариантов ответов"""
+        options_with_indices = list(enumerate(options))
+        random.shuffle(options_with_indices)
+        
+        self.var = tk.IntVar(value=-1)
+        self.option_mapping = {}
+        
+        # Функции для эффекта наведения
+        def on_enter(btn):
+            btn.config(bg='#d0d0d0' if text_color == 'black' else '#505050')
+        
+        def on_leave(btn, orig_bg):
+            btn.config(bg=orig_bg)
+        
+        for new_idx, (old_idx, option_text) in enumerate(options_with_indices):
+            self.option_mapping[new_idx] = old_idx
+            display_text = f"{new_idx + 1}. {option_text}"
+            
+            rb = tk.Radiobutton(parent, text=display_text, variable=self.var,
+                               value=new_idx, font=("Arial", 11), wraplength=350,
+                               justify='left', indicatoron=0, bg=bg_color,
+                               fg=text_color, selectcolor=bg_color,
+                               padx=15, pady=8, cursor='hand2')
+            rb.pack(anchor='center', pady=5)
+            
+            # Эффект наведения
+            orig_bg = rb.cget('bg')
+            rb.bind("<Enter>", lambda e, b=rb: on_enter(b))
+            rb.bind("<Leave>", lambda e, b=rb: on_leave(b, orig_bg))
+    
+    def show_question(self):
+        """Отображение вопроса"""
+        self.clear_window()
+        
+        # Удаление старого изображения
+        if hasattr(self, 'current_image'):
+            del self.current_image
+        
+        q = self.questions[self.current_question]
+        bg_color = self.get_background_color(q, self.current_question)
+        text_color = self.get_text_color(bg_color)
+        self.master.config(bg=bg_color)
+        
+        # Основной контейнер
+        main_frame = tk.Frame(self.master, bg=bg_color)
+        main_frame.pack(fill='both', expand=True, padx=30, pady=20)
+        
+        # Левая колонка
+        left_frame = tk.Frame(main_frame, bg=bg_color)
+        left_frame.pack(side='left', fill='both', expand=True)
+        
+        # Правая колонка
+        right_frame = tk.Frame(main_frame, bg=bg_color)
+        right_frame.pack(side='right', fill='both', expand=True)
+        
+        # Вопрос
+        question_lbl = tk.Label(left_frame, text=q['question'],
+                               font=("Arial", 14, "bold"), wraplength=450,
+                               justify='left', bg=bg_color, fg=text_color)
+        question_lbl.pack(anchor='nw', pady=(0, 20))
+        
+        # Изображение
+        if q.get('image'):
+            image_path = self.image_manager.find_image(q['image'])
+            if image_path:
+                try:
+                    self.current_image = self.image_manager.load_image(image_path, bg_color)
+                    img_label = tk.Label(left_frame, image=self.current_image, bg=bg_color)
+                    img_label.pack(anchor='center', pady=10)
+                except Exception as e:
+                    error_text = f"[Ошибка: {image_path.name}]\n{str(e)[:50]}"
+                    error_label = tk.Label(left_frame, text=error_text,
+                                          font=("Arial", 8), fg='red', bg=bg_color)
+                    error_label.pack(anchor='center')
+            else:
+                error_text = f"[Файл не найден: {q['image']}]\nПоддерживаемые форматы: PNG, JPG, JPEG, GIF, BMP"
+                error_label = tk.Label(left_frame, text=error_text,
+                                      font=("Arial", 8), fg='orange', bg=bg_color)
+                error_label.pack(anchor='center')
+        
+        # Прогресс и подсказки
+        progress_lbl = tk.Label(left_frame,
+                               text=f"Вопрос {self.current_question + 1} из {self.total_questions}",
+                               font=("Arial", 11), fg=text_color, bg=bg_color)
+        progress_lbl.pack(side='bottom', anchor='sw', pady=10)
+        
+        hint_lbl = tk.Label(left_frame,
+                           text="Клавиши 1-4 → выбор ответа, Enter → проверка",
+                           font=("Arial", 9), fg=text_color, bg=bg_color)
+        hint_lbl.pack(side='bottom', anchor='sw', pady=5)
+        
+        # Варианты ответов
+        center_container = tk.Frame(right_frame, bg=bg_color)
+        center_container.pack(expand=True)
+        
+        options_title = tk.Label(center_container, text="Выберите ответ:",
+                                font=("Arial", 12, "bold"), bg=bg_color, fg=text_color)
+        options_title.pack(pady=(0, 15))
+        
+        self.create_option_buttons(center_container, q['options'], bg_color, text_color)
+        
+        # Кнопки управления
+        btn_frame = tk.Frame(right_frame, bg=bg_color)
+        btn_frame.pack(side='bottom', fill='x', pady=20)
+        
+        exit_btn = tk.Button(btn_frame, text="Выход (Esc)", font=("Arial", 11),
+                            command=self.quit_app, bg='red', fg='white',
+                            padx=20, pady=5, cursor='hand2')
+        exit_btn.pack(side='right', padx=10)
+        
+        check_btn = tk.Button(btn_frame, text="Проверить (Enter)", font=("Arial", 11),
+                             command=self.check_answer, bg='blue', fg='white',
+                             padx=20, pady=5, cursor='hand2')
+        check_btn.pack(side='right', padx=10)
+        
+        self.master.focus_set()
+    
+    def check_answer(self):
+        """Проверка ответа"""
+        if self.var is None or self.var.get() == -1:
+            messagebox.showwarning("Нет выбора", "Пожалуйста, выберите один из вариантов (клавиши 1-4).")
+            return
+        
+        selected = self.var.get()
+        original_selected = self.option_mapping[selected]
+        q = self.questions[self.current_question]
+        
+        if original_selected == q['correct']:
+            self.correct_answers += 1
+            self.show_feedback("Правильно!", "green")
+        else:
+            correct_text = q['options'][q['correct']]
+            explanation = f"Неправильно. Правильный ответ: {correct_text}\n\n{q['explanation']}"
+            self.show_feedback("Неправильно", "red", explanation)
+    
+    def show_feedback(self, message: str, color: str, explanation: str = None):
+        """Отображение обратной связи"""
+        win = tk.Toplevel(self.master)
+        win.title("Результат")
+        win.geometry("500x350")
+        win.configure(bg=color)
+        win.transient(self.master)
+        win.grab_set()
+        
+        # Закрытие окна
+        def close_and_continue():
+            win.destroy()
+            self.next_question()
+        
+        win.protocol("WM_DELETE_WINDOW", close_and_continue)
+        win.bind('<Return>', lambda e: close_and_continue())
+        win.bind('<Escape>', lambda e: close_and_continue())
+        
+        # Сообщение
+        lbl = tk.Label(win, text=message, bg=color, fg='white',
+                      font=("Arial", 24, "bold"), wraplength=450, justify='center')
+        lbl.pack(expand=True, fill='both', padx=20, pady=20)
+        
+        # Пояснение
+        if explanation:
+            expl_lbl = tk.Label(win, text=explanation, bg=color, fg='white',
+                               font=("Arial", 11), wraplength=450, justify='left')
+            expl_lbl.pack(padx=20, pady=10)
+        
+        # Кнопка продолжения
+        btn = tk.Button(win, text="Продолжить (Enter)", command=close_and_continue,
+                       bg='white', font=("Arial", 11), padx=20, pady=5, cursor='hand2')
+        btn.pack(pady=20)
+        
+        win.focus_set()
+    
+    def next_question(self):
+        """Переход к следующему вопросу"""
+        if self.current_question + 1 < self.total_questions:
+            self.current_question += 1
+            self.show_question()
+        else:
+            self.show_result()
+    
+    def show_result(self):
+        """Отображение результатов"""
+        self.clear_window()
+        self.master.config(bg='lightgreen')
+        
+        percentage = (self.correct_answers / self.total_questions) * 100
+        result_text = f"Викторина завершена!\n\n"
+        result_text += f"Вы ответили правильно на {self.correct_answers} из {self.total_questions} вопросов.\n"
+        result_text += f"Ваш результат: {percentage:.1f}%\n\n"
+        
+        if percentage == 100:
+            result_text += "Отлично! Вы очень хорошо усвоили материал."
+        elif percentage >= 80:
+            result_text += "Отличный результат! Вы хорошо знаете тему."
+        elif percentage >= 60:
+            result_text += "Хороший результат! Есть куда стремиться."
+        else:
+            result_text += "Стоит повторить лекцию и попробовать снова."
+        
+        lbl = tk.Label(self.master, text=result_text, font=("Arial", 16),
+                      bg='lightgreen', justify='center')
+        lbl.pack(pady=30)
+        
+        # Привязка клавиш
+        self.master.bind('<Return>', lambda e: self.start_quiz())
+        self.master.bind('<Escape>', lambda e: self.quit_app())
+        
+        # Подсказка
+        hint_lbl = tk.Label(self.master, text="Enter - пройти заново | Esc - выход",
+                           font=("Arial", 11), bg='lightgreen', fg='gray')
+        hint_lbl.pack(pady=10)
+        
+        # Кнопки
+        btn_frame = tk.Frame(self.master, bg='lightgreen')
+        btn_frame.pack(pady=20)
+        
+        restart_btn = tk.Button(btn_frame, text="Пройти заново (Enter)",
+                               font=("Arial", 12), command=self.start_quiz,
+                               bg='blue', fg='white', padx=20, pady=8, cursor='hand2')
+        restart_btn.pack(side='left', padx=15)
+        
+        exit_btn = tk.Button(btn_frame, text="Завершить (Esc)",
+                            font=("Arial", 12), command=self.quit_app,
+                            bg='orange', fg='white', padx=20, pady=8, cursor='hand2')
+        exit_btn.pack(side='left', padx=15)
+        
+        self.master.focus_set()
+
+
+if __name__ == "__main__":
+    root = tk.Tk()
+    app = QuizApp(root)
+    root.mainloop()


+ 155 - 0
тз.md

@@ -0,0 +1,155 @@
+Техническое задание на разработку программного продукта
+«Интерактивная викторина: Защита персональных данных»
+1. Цель и назначение разработки
+1.1. Цель
+Разработать десктопное приложение для тестирования знаний в области защиты персональных данных в формате интерактивной викторины.
+
+1.2. Назначение
+Проверка знаний сотрудников организаций, студентов, слушателей курсов по теме 152-ФЗ, GDPR, уровней защищённости, угроз и т.д.
+
+Использование в учебных целях, на лекциях, тренингах, внутренних экзаменах.
+
+Возможность самостоятельного обучения за счёт пояснений к каждому вопросу.
+
+2. Требования к функциональности
+2.1. Основные функции
+№	Функция	Описание
+1	Стартовый экран	Приветствие, правила, кнопка начала викторины
+2	Перемешивание вопросов	Случайный порядок вопросов при каждом запуске
+3	Отображение вопроса	Текст вопроса + до 4 вариантов ответов
+4	Выбор ответа	Через GUI (кнопки) или клавиши 1-4
+5	Проверка ответа	По кнопке «Проверить» или клавише Enter
+6	Обратная связь	Модальное окно с результатом (правильно/неправильно) + пояснение
+7	Подсчёт результатов	В конце викторины — количество правильных ответов и %
+8	Повторное прохождение	Возможность начать заново без перезапуска программы
+9	Выход	По кнопке или Esc с подтверждением
+2.2. Работа с изображениями
+При наличии изображения у вопроса — отображать его (PNG, JPG, JPEG, BMP, GIF, ICO, TIFF).
+
+Поддержка Pillow для масштабирования, обработки прозрачности и анимированных GIF.
+
+Автоматическое определение доминирующего цвета изображения для фона вопроса.
+
+Если изображение отсутствует — показывать уведомление об ошибке.
+
+2.3. Интерфейс и управление
+Адаптивная цветовая схема: цвет фона меняется под изображение или используется предустановленный.
+
+Автоматический выбор чёрного или белого цвета текста для читаемости.
+
+Управление с клавиатуры:
+
+1-4 — выбор варианта ответа
+
+Enter — проверка ответа / продолжение
+
+Esc — выход
+
+Эффекты наведения на кнопки и радиокнопки.
+
+3. Структура данных
+3.1. Формат вопроса
+Каждый вопрос представляет собой словарь с полями:
+
+Поле	Тип	Описание
+id	int	Уникальный идентификатор
+question	str	Текст вопроса
+options	list[str]	Список вариантов ответов (4 шт.)
+correct	int	Индекс правильного ответа (0-3)
+explanation	str	Пояснение после ответа
+image	str	Имя файла изображения (опционально)
+bg_color	str|None	Ручной цвет фона (hex, опционально)
+3.2. БД вопросов
+Встроенный список из 50 вопросов по теме защиты ПД.
+
+Вопросы охватывают: 152-ФЗ, категории данных, уровни защищённости, угрозы, ответственность, GDPR и др.
+
+4. Требования к графическому интерфейсу
+4.1. Окна и экраны
+Экран	Элементы
+Стартовый	Заголовок, правила, кнопка «Начать»
+Вопрос	Текст вопроса, изображение (если есть), 4 радиокнопки, прогресс, подсказки, кнопки «Выход» и «Проверить»
+Обратная связь	Модальное окно с результатом, пояснением, кнопкой продолжения
+Результаты	Количество правильных ответов, %, оценка, кнопки повтора и выхода
+4.2. Размеры и расположение
+Основное окно: 1000x700, не изменяется.
+
+Модальное окно: 500x350.
+
+Изображения масштабируются до 350x350 с сохранением пропорций.
+
+Варианты ответов — вертикальный список с переносом текста.
+
+5. Технические требования
+
+5.1. Стек технологий
+Компонент	Технология
+Язык	Python 3.8+
+GUI	Tkinter (встроенный)
+Работа с изображениями	Pillow (опционально, но рекомендуется)
+Сборка (опционально)	PyInstaller / cx_Freeze
+
+5.2. Зависимости
+Стандартная библиотека Python (tkinter, random, os, pathlib, typing)
+
+Pillow — не обязателен, но без него ограничена работа с изображениями (только .jpg через PhotoImage)
+
+5.3. Структура файлов
+text
+quiz_app.py                 # Главный скрипт
+quiz_images/                # Папка с изображениями (создаётся вручную)
+    q1.jpg
+    q2.png
+    ...
+5.4. Обработка ошибок
+Отсутствие Pillow → предупреждение, работа с ограничениями.
+
+Отсутствие изображения → уведомление в интерфейсе.
+
+Некорректный файл изображения → сообщение об ошибке.
+
+6. Критерии приёмки
+№	Критерий
+
+1	Приложение запускается без ошибок на Python 3.8+
+
+2	Все 50 вопросов отображаются корректно
+
+3	Выбор ответа работает через мышь и клавиши 1-4
+
+4	Проверка ответа показывает правильный/неправильный результат с пояснением
+
+5	По окончании викторины показывается итоговый счёт и процент
+
+6	Можно пройти викторину заново без перезапуска
+
+7	Выход из программы — через Esc или кнопку с подтверждением
+
+8	Изображения (если есть) отображаются и не ломают интерфейс
+
+9	Цвет фона адаптируется под изображение или используется палитра по умолчанию
+
+10	Код структурирован, есть классы ImageManager и QuizApp, комментарии на русском/английском
+
+7. Возможные доработки (не входит в текущий объём)
+Добавление базы данных SQLite для хранения результатов пользователей.
+
+Режим обучения с повторением ошибочных вопросов.
+
+Экспорт результатов в PDF/CSV.
+
+Поддержка темной/светлой темы без привязки к изображению.
+
+Таймер на вопрос.
+
+Звуковое сопровождение (правильно/неправильно).
+
+Многопользовательский режим (соревнование).
+
+8. Требования к документации
+Исходный код должен содержать комментарии к ключевым методам.
+
+В начале файла — краткое описание программы и инструкция по запуску.
+
+При отсутствии Pillow — вывод понятного сообщения с инструкцией по установке.
+