|
@@ -0,0 +1,333 @@
|
|
|
|
|
+import tkinter as tk
|
|
|
|
|
+from tkinter import ttk, messagebox
|
|
|
|
|
+import random
|
|
|
|
|
+import time
|
|
|
|
|
+try:
|
|
|
|
|
+ import winsound
|
|
|
|
|
+ SOUND_ENABLED = True
|
|
|
|
|
+except:
|
|
|
|
|
+ SOUND_ENABLED = False
|
|
|
|
|
+
|
|
|
|
|
+class SecurityGame:
|
|
|
|
|
+ def __init__(self, root):
|
|
|
|
|
+ self.root = root
|
|
|
|
|
+ self.root.title("Игра: Управление доступом и контроль печати")
|
|
|
|
|
+ self.root.geometry("900x700")
|
|
|
|
|
+ self.root.configure(bg="#2c3e50")
|
|
|
|
|
+
|
|
|
|
|
+ # Стили
|
|
|
|
|
+ self.style = ttk.Style()
|
|
|
|
|
+ self.style.theme_use("clam")
|
|
|
|
|
+ self.style.configure("TLabel", background="#2c3e50", foreground="white", font=("Arial", 11))
|
|
|
|
|
+ self.style.configure("TButton", font=("Arial", 11, "bold"), background="#3498db")
|
|
|
|
|
+ self.style.configure("TFrame", background="#2c3e50")
|
|
|
|
|
+
|
|
|
|
|
+ # Переменные
|
|
|
|
|
+ self.current_scene = 0
|
|
|
|
|
+ self.scenes = [self.scene1, self.scene2, self.scene3, self.scene4, self.scene5]
|
|
|
|
|
+ self.score = 0
|
|
|
|
|
+
|
|
|
|
|
+ # Заголовок и прогресс
|
|
|
|
|
+ self.header_frame = tk.Frame(root, bg="#1a252f", height=80)
|
|
|
|
|
+ self.header_frame.pack(fill=tk.X)
|
|
|
|
|
+ self.title_label = tk.Label(self.header_frame, text="Управление доступом и контроль печати", font=("Arial", 18, "bold"), fg="#ecf0f1", bg="#1a252f")
|
|
|
|
|
+ self.title_label.pack(pady=10)
|
|
|
|
|
+ self.progress = ttk.Progressbar(self.header_frame, length=400, mode='determinate')
|
|
|
|
|
+ self.progress.pack(pady=5)
|
|
|
|
|
+ self.score_label = tk.Label(self.header_frame, text="Очки: 0", font=("Arial", 12), fg="#f1c40f", bg="#1a252f")
|
|
|
|
|
+ self.score_label.pack()
|
|
|
|
|
+
|
|
|
|
|
+ # Основной фрейм для сцен
|
|
|
|
|
+ self.main_frame = tk.Frame(root, bg="#2c3e50")
|
|
|
|
|
+ self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
|
|
|
|
|
+
|
|
|
|
|
+ # Фрейм для подсказок
|
|
|
|
|
+ self.hint_frame = tk.Frame(root, bg="#34495e", height=50)
|
|
|
|
|
+ self.hint_frame.pack(fill=tk.X, side=tk.BOTTOM)
|
|
|
|
|
+ self.hint_label = tk.Label(self.hint_frame, text="Нажми 'Подсказка' для помощи", font=("Arial", 10), fg="#bdc3c7", bg="#34495e")
|
|
|
|
|
+ self.hint_label.pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ self.load_scene()
|
|
|
|
|
+
|
|
|
|
|
+ def play_sound(self, success=True):
|
|
|
|
|
+ if SOUND_ENABLED:
|
|
|
|
|
+ if success:
|
|
|
|
|
+ winsound.Beep(1000, 200)
|
|
|
|
|
+ else:
|
|
|
|
|
+ winsound.Beep(400, 500)
|
|
|
|
|
+
|
|
|
|
|
+ def update_progress(self):
|
|
|
|
|
+ value = (self.current_scene / len(self.scenes)) * 100
|
|
|
|
|
+ self.progress['value'] = value
|
|
|
|
|
+ self.score_label.config(text=f"Очки: {self.score}")
|
|
|
|
|
+
|
|
|
|
|
+ def show_hint(self, hint_text):
|
|
|
|
|
+ self.hint_label.config(text=f"💡 Подсказка: {hint_text}", fg="#f1c40f")
|
|
|
|
|
+ self.hint_frame.configure(bg="#2c3e50")
|
|
|
|
|
+ self.root.after(5000, lambda: self.hint_label.config(text="Нажми 'Подсказка' для помощи", fg="#bdc3c7", bg="#34495e"))
|
|
|
|
|
+
|
|
|
|
|
+ def clear_frame(self):
|
|
|
|
|
+ for widget in self.main_frame.winfo_children():
|
|
|
|
|
+ widget.destroy()
|
|
|
|
|
+
|
|
|
|
|
+ def animate_widget(self, widget, color_from, color_to, steps=10):
|
|
|
|
|
+ """Простая анимация смены цвета фона виджета"""
|
|
|
|
|
+ r1, g1, b1 = self.hex_to_rgb(color_from)
|
|
|
|
|
+ r2, g2, b2 = self.hex_to_rgb(color_to)
|
|
|
|
|
+ for i in range(steps+1):
|
|
|
|
|
+ r = r1 + (r2 - r1) * i // steps
|
|
|
|
|
+ g = g1 + (g2 - g1) * i // steps
|
|
|
|
|
+ b = b1 + (b2 - b1) * i // steps
|
|
|
|
|
+ color = f"#{r:02x}{g:02x}{b:02x}"
|
|
|
|
|
+ widget.configure(bg=color)
|
|
|
|
|
+ self.root.update()
|
|
|
|
|
+ time.sleep(0.02)
|
|
|
|
|
+
|
|
|
|
|
+ def hex_to_rgb(self, hex_color):
|
|
|
|
|
+ hex_color = hex_color.lstrip('#')
|
|
|
|
|
+ return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
|
|
|
|
|
+
|
|
|
|
|
+ def load_scene(self):
|
|
|
|
|
+ self.clear_frame()
|
|
|
|
|
+ self.update_progress()
|
|
|
|
|
+ self.scenes[self.current_scene]()
|
|
|
|
|
+
|
|
|
|
|
+ def next_scene(self):
|
|
|
|
|
+ self.current_scene += 1
|
|
|
|
|
+ self.score += 10
|
|
|
|
|
+ self.update_progress()
|
|
|
|
|
+ if self.current_scene < len(self.scenes):
|
|
|
|
|
+ self.load_scene()
|
|
|
|
|
+ else:
|
|
|
|
|
+ messagebox.showinfo("Поздравляем!", f"Вы успешно завершили игру!\nФинальный счёт: {self.score} очков.\nТеперь вы эксперт по безопасности!")
|
|
|
|
|
+ self.root.quit()
|
|
|
|
|
+
|
|
|
|
|
+ # СЦЕНА 1: Выбор модели доступа с анимацией
|
|
|
|
|
+ def scene1(self):
|
|
|
|
|
+ tk.Label(self.main_frame, text="СЦЕНА 1: Модели управления доступом", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10)
|
|
|
|
|
+ tk.Label(self.main_frame, text="Сопоставьте описание с правильной моделью (DAC, MAC, RBAC):", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ questions = [
|
|
|
|
|
+ ("Владелец ресурса сам назначает права (например, через ACL).", "DAC"),
|
|
|
|
|
+ ("Объектам и субъектам присваиваются метки конфиденциальности, решение принимает система.", "MAC"),
|
|
|
|
|
+ ("Права группируются по должностным ролям, пользователь получает доступ через роль.", "RBAC")
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ self.answers = {}
|
|
|
|
|
+ self.vars = []
|
|
|
|
|
+
|
|
|
|
|
+ for i, (desc, correct) in enumerate(questions):
|
|
|
|
|
+ frame_q = tk.Frame(self.main_frame, bg="#2c3e50")
|
|
|
|
|
+ frame_q.pack(fill=tk.X, padx=20, pady=5)
|
|
|
|
|
+ tk.Label(frame_q, text=desc, wraplength=600, anchor="w", justify=tk.LEFT, fg="white", bg="#2c3e50").pack(side=tk.LEFT, padx=5)
|
|
|
|
|
+ var = tk.StringVar(frame_q)
|
|
|
|
|
+ var.set("")
|
|
|
|
|
+ self.vars.append(var)
|
|
|
|
|
+ combo = ttk.Combobox(frame_q, textvariable=var, values=["DAC", "MAC", "RBAC"], state="readonly", width=10)
|
|
|
|
|
+ combo.pack(side=tk.RIGHT, padx=5)
|
|
|
|
|
+ self.answers[i] = correct
|
|
|
|
|
+
|
|
|
|
|
+ def check():
|
|
|
|
|
+ correct_count = 0
|
|
|
|
|
+ for i, var in enumerate(self.vars):
|
|
|
|
|
+ if var.get() == self.answers[i]:
|
|
|
|
|
+ correct_count += 1
|
|
|
|
|
+ if correct_count == 3:
|
|
|
|
|
+ self.play_sound(True)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8)
|
|
|
|
|
+ messagebox.showinfo("Верно", "Все модели определены правильно!")
|
|
|
|
|
+ self.next_scene()
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.play_sound(False)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8)
|
|
|
|
|
+ messagebox.showerror("Ошибка", f"Правильно: {correct_count} из 3. Попробуйте ещё раз.")
|
|
|
|
|
+
|
|
|
|
|
+ btn = tk.Button(self.main_frame, text="Проверить", command=check, bg="#3498db", fg="white", font=("Arial", 12, "bold"), relief=tk.RAISED, padx=20, pady=5)
|
|
|
|
|
+ btn.pack(pady=20)
|
|
|
|
|
+
|
|
|
|
|
+ hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("DAC — владелец решает, MAC — метки, RBAC — роли"), bg="#f39c12", fg="white")
|
|
|
|
|
+ hint_btn.pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ # СЦЕНА 2: Назначение прав (используем Canvas для красивого отображения)
|
|
|
|
|
+ def scene2(self):
|
|
|
|
|
+ tk.Label(self.main_frame, text="СЦЕНА 2: Принцип минимальных привилегий", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10)
|
|
|
|
|
+ tk.Label(self.main_frame, text="Для каждой роли отметьте разрешённые объекты (только необходимое):", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ roles = ["Бухгалтер", "Кадровик", "Сисадмин", "Стажёр"]
|
|
|
|
|
+ objects = ["Зарплата", "Личные дела", "Журналы сервера", "Общие инструкции"]
|
|
|
|
|
+ correct = [
|
|
|
|
|
+ [True, False, False, False],
|
|
|
|
|
+ [False, True, False, False],
|
|
|
|
|
+ [False, False, True, False],
|
|
|
|
|
+ [False, False, False, True]
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ self.check_vars = []
|
|
|
|
|
+ # Создаём таблицу с цветными ячейками
|
|
|
|
|
+ canvas = tk.Canvas(self.main_frame, bg="#2c3e50", highlightthickness=0)
|
|
|
|
|
+ canvas.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
+
|
|
|
|
|
+ # Заголовки
|
|
|
|
|
+ for j, obj in enumerate(objects):
|
|
|
|
|
+ canvas.create_text(200 + j*120, 50, text=obj, fill="white", font=("Arial", 10, "bold"))
|
|
|
|
|
+ for i, role in enumerate(roles):
|
|
|
|
|
+ canvas.create_text(80, 100 + i*60, text=role, fill="#f1c40f", font=("Arial", 11, "bold"), anchor="w")
|
|
|
|
|
+
|
|
|
|
|
+ self.check_boxes = []
|
|
|
|
|
+ for i in range(len(roles)):
|
|
|
|
|
+ row = []
|
|
|
|
|
+ for j in range(len(objects)):
|
|
|
|
|
+ var = tk.BooleanVar(value=False)
|
|
|
|
|
+ chk = tk.Checkbutton(self.main_frame, variable=var, bg="#2c3e50", selectcolor="#2c3e50", activebackground="#2c3e50")
|
|
|
|
|
+ # размещаем через create_window
|
|
|
|
|
+ window_id = canvas.create_window(200 + j*120, 100 + i*60, window=chk, anchor=tk.CENTER)
|
|
|
|
|
+ row.append(var)
|
|
|
|
|
+ self.check_boxes.append(row)
|
|
|
|
|
+
|
|
|
|
|
+ def check2():
|
|
|
|
|
+ ok = True
|
|
|
|
|
+ for i in range(len(roles)):
|
|
|
|
|
+ for j in range(len(objects)):
|
|
|
|
|
+ if self.check_boxes[i][j].get() != correct[i][j]:
|
|
|
|
|
+ ok = False
|
|
|
|
|
+ break
|
|
|
|
|
+ if not ok:
|
|
|
|
|
+ break
|
|
|
|
|
+ if ok:
|
|
|
|
|
+ self.play_sound(True)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8)
|
|
|
|
|
+ messagebox.showinfo("Верно", "Права назначены правильно!")
|
|
|
|
|
+ self.next_scene()
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.play_sound(False)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8)
|
|
|
|
|
+ messagebox.showerror("Ошибка", "Нарушен принцип минимальных привилегий. Стажёр не должен видеть зарплату, а бухгалтер — журналы.")
|
|
|
|
|
+
|
|
|
|
|
+ btn = tk.Button(self.main_frame, text="Проверить", command=check2, bg="#3498db", fg="white", font=("Arial", 12, "bold"))
|
|
|
|
|
+ btn.pack(pady=20)
|
|
|
|
|
+ hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Каждой роли — только её данные: бухгалтеру зарплата, кадровику личные дела и т.д."), bg="#f39c12", fg="white")
|
|
|
|
|
+ hint_btn.pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ # СЦЕНА 3: Настройка безопасной печати (с анимацией кнопок)
|
|
|
|
|
+ def scene3(self):
|
|
|
|
|
+ tk.Label(self.main_frame, text="СЦЕНА 3: Безопасная печать", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10)
|
|
|
|
|
+ tk.Label(self.main_frame, text="Выберите корректные параметры для печати конфиденциальных документов:", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ # Принтер
|
|
|
|
|
+ tk.Label(self.main_frame, text="Принтер:", fg="white", bg="#2c3e50").pack(anchor=tk.W)
|
|
|
|
|
+ self.printer_var = tk.StringVar(value="Общий")
|
|
|
|
|
+ printers = ["Общий принтер в холле", "Принтер в переговорной", "Принтер в сейфовой комнате"]
|
|
|
|
|
+ for p in printers:
|
|
|
|
|
+ tk.Radiobutton(self.main_frame, text=p, variable=self.printer_var, value=p, bg="#2c3e50", fg="white", selectcolor="#2c3e50").pack(anchor=tk.W)
|
|
|
|
|
+
|
|
|
|
|
+ # Время удаления
|
|
|
|
|
+ tk.Label(self.main_frame, text="Время автоудаления:", fg="white", bg="#2c3e50").pack(anchor=tk.W, pady=(10,0))
|
|
|
|
|
+ self.time_var = tk.IntVar(value=5)
|
|
|
|
|
+ times = [5, 15, 30]
|
|
|
|
|
+ for t in times:
|
|
|
|
|
+ tk.Radiobutton(self.main_frame, text=f"{t} мин", variable=self.time_var, value=t, bg="#2c3e50", fg="white", selectcolor="#2c3e50").pack(anchor=tk.W)
|
|
|
|
|
+
|
|
|
|
|
+ # Аутентификация
|
|
|
|
|
+ self.auth_var = tk.BooleanVar(value=True)
|
|
|
|
|
+ tk.Checkbutton(self.main_frame, text="Требовать аутентификацию у принтера", variable=self.auth_var, bg="#2c3e50", fg="white", selectcolor="#2c3e50").pack(anchor=tk.W)
|
|
|
|
|
+
|
|
|
|
|
+ def check3():
|
|
|
|
|
+ correct = (self.printer_var.get() == "Принтер в сейфовой комнате" and self.time_var.get() == 5 and self.auth_var.get() == True)
|
|
|
|
|
+ if correct:
|
|
|
|
|
+ self.play_sound(True)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8)
|
|
|
|
|
+ messagebox.showinfo("Верно", "Настройки безопасной печати корректны!")
|
|
|
|
|
+ self.next_scene()
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.play_sound(False)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8)
|
|
|
|
|
+ messagebox.showerror("Ошибка", "Нужно выбрать принтер в сейфовой комнате, время 5 минут и обязательную аутентификацию.")
|
|
|
|
|
+
|
|
|
|
|
+ btn = tk.Button(self.main_frame, text="Проверить", command=check3, bg="#3498db", fg="white", font=("Arial", 12, "bold"))
|
|
|
|
|
+ btn.pack(pady=20)
|
|
|
|
|
+ hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Конфиденциальная печать требует защищённого принтера, быстрого удаления и аутентификации."), bg="#f39c12", fg="white")
|
|
|
|
|
+ hint_btn.pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ # СЦЕНА 4: Маркировка и уничтожение (с анимированными кнопками)
|
|
|
|
|
+ def scene4(self):
|
|
|
|
|
+ tk.Label(self.main_frame, text="СЦЕНА 4: Маркировка и уничтожение", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10)
|
|
|
|
|
+ tk.Label(self.main_frame, text="Действия с конфиденциальными документами:", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ tk.Label(self.main_frame, text="1. Как маркировать конфиденциальный документ?", fg="white", bg="#2c3e50").pack(anchor=tk.W)
|
|
|
|
|
+ self.mark_var = tk.StringVar(value="")
|
|
|
|
|
+ marks = ["Только гриф 'Конфиденциально'", "Штамп с номером экземпляра", "Без маркировки"]
|
|
|
|
|
+ for m in marks:
|
|
|
|
|
+ tk.Radiobutton(self.main_frame, text=m, variable=self.mark_var, value=m, bg="#2c3e50", fg="white").pack(anchor=tk.W)
|
|
|
|
|
+
|
|
|
|
|
+ tk.Label(self.main_frame, text="2. Что сделать с бракованной распечаткой?", fg="white", bg="#2c3e50").pack(anchor=tk.W, pady=(10,0))
|
|
|
|
|
+ self.destroy_var = tk.StringVar(value="")
|
|
|
|
|
+ destroys = ["Выбросить в мусор", "Использовать как черновик", "Уничтожить в шредере 3-й степени", "Сжечь"]
|
|
|
|
|
+ for d in destroys:
|
|
|
|
|
+ tk.Radiobutton(self.main_frame, text=d, variable=self.destroy_var, value=d, bg="#2c3e50", fg="white").pack(anchor=tk.W)
|
|
|
|
|
+
|
|
|
|
|
+ def check4():
|
|
|
|
|
+ mark_ok = (self.mark_var.get() == "Штамп с номером экземпляра")
|
|
|
|
|
+ destroy_ok = (self.destroy_var.get() == "Уничтожить в шредере 3-й степени")
|
|
|
|
|
+ if mark_ok and destroy_ok:
|
|
|
|
|
+ self.play_sound(True)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8)
|
|
|
|
|
+ messagebox.showinfo("Верно", "Маркировка и уничтожение выполнены правильно!")
|
|
|
|
|
+ self.next_scene()
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.play_sound(False)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8)
|
|
|
|
|
+ messagebox.showerror("Ошибка", "Нужен штамп с номером экземпляра и уничтожение в шредере 3-й степени.")
|
|
|
|
|
+
|
|
|
|
|
+ btn = tk.Button(self.main_frame, text="Проверить", command=check4, bg="#3498db", fg="white", font=("Arial", 12, "bold"))
|
|
|
|
|
+ btn.pack(pady=20)
|
|
|
|
|
+ hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Каждый конфиденциальный документ должен иметь уникальный номер экземпляра, брак уничтожается в шредере с высокой степенью защиты."), bg="#f39c12", fg="white")
|
|
|
|
|
+ hint_btn.pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ # СЦЕНА 5: Расследование инцидента с таблицей и анимацией
|
|
|
|
|
+ def scene5(self):
|
|
|
|
|
+ tk.Label(self.main_frame, text="СЦЕНА 5: Расследование утечки", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10)
|
|
|
|
|
+ tk.Label(self.main_frame, text="Инцидент: Петров распечатал 'Отчет_КТ.pdf' и забыл на принтере в холле. Через 10 минут документ исчез.\nЖурнал печати:", font=("Arial", 11), fg="#bdc3c7", bg="#2c3e50").pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+ # Таблица с данными
|
|
|
|
|
+ columns = ("Время", "Пользователь", "Документ", "Принтер", "Результат")
|
|
|
|
|
+ tree = ttk.Treeview(self.main_frame, columns=columns, show="headings", height=6)
|
|
|
|
|
+ for col in columns:
|
|
|
|
|
+ tree.heading(col, text=col)
|
|
|
|
|
+ tree.column(col, width=140)
|
|
|
|
|
+ data = [
|
|
|
|
|
+ ("09:05", "Петров", "Отчет_КТ.pdf", "Холл", "Напечатан"),
|
|
|
|
|
+ ("09:07", "Иванов", "Отчет_КТ.pdf", "Холл", "Напечатан"),
|
|
|
|
|
+ ("09:10", "Сидорова", "Приказ_общий.docx", "Холл", "Напечатан"),
|
|
|
|
|
+ ("09:12", "Иванов", "Список.xlsx", "Холл", "Отменено"),
|
|
|
|
|
+ ("09:15", "Петров", "Счет.pdf", "Сейфовая", "Напечатан")
|
|
|
|
|
+ ]
|
|
|
|
|
+ for row in data:
|
|
|
|
|
+ tree.insert("", tk.END, values=row)
|
|
|
|
|
+ tree.pack(pady=10)
|
|
|
|
|
+
|
|
|
|
|
+ tk.Label(self.main_frame, text="Кто из сотрудников распечатал тот же конфиденциальный файл сразу после Петрова?", fg="white", bg="#2c3e50").pack()
|
|
|
|
|
+ self.suspect_var = tk.StringVar()
|
|
|
|
|
+ suspects = ["Петров", "Иванов", "Сидорова", "Неизвестный"]
|
|
|
|
|
+ for s in suspects:
|
|
|
|
|
+ tk.Radiobutton(self.main_frame, text=s, variable=self.suspect_var, value=s, bg="#2c3e50", fg="white").pack(anchor=tk.W)
|
|
|
|
|
+
|
|
|
|
|
+ def check5():
|
|
|
|
|
+ if self.suspect_var.get() == "Иванов":
|
|
|
|
|
+ self.play_sound(True)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8)
|
|
|
|
|
+ messagebox.showinfo("Верно", "Иванов распечатал тот же файл через 2 минуты, хотя его роль не позволяла печатать КТ. Нарушитель найден!")
|
|
|
|
|
+ self.next_scene()
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.play_sound(False)
|
|
|
|
|
+ self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8)
|
|
|
|
|
+ messagebox.showerror("Ошибка", "Неверно. Посмотрите внимательно на время и документ в журнале.")
|
|
|
|
|
+
|
|
|
|
|
+ btn = tk.Button(self.main_frame, text="Проверить", command=check5, bg="#3498db", fg="white", font=("Arial", 12, "bold"))
|
|
|
|
|
+ btn.pack(pady=20)
|
|
|
|
|
+ hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Обратите внимание на строку 09:07 — тот же документ, другой пользователь."), bg="#f39c12", fg="white")
|
|
|
|
|
+ hint_btn.pack(pady=5)
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ root = tk.Tk()
|
|
|
|
|
+ game = SecurityGame(root)
|
|
|
|
|
+ root.mainloop()
|