feat: вкладка «Безопасность» — вложенные подвкладки (нормальное отображение)
Вместо тесной двухколоночной раскладки — ряд подвкладок: 1) Основные данные — модель нарушителя (категория, уровень Н1-Н4, виды) + кнопки «⚡ Автозаполнить» и «🤖 Проверить безопасность через ИИ» 2) Угрозы — панель действий (⬇️ ФСТЭК / ⚙️ программа / 🤖 ИИ / ⚖️ сравнить) + большой список угроз на всю ширину 3) Инциденты и критерии — инциденты, показатели критериев, кнопка «📋 ПП № 127», обоснование 4) Орг. и тех. меры — оба списка с нормальной высотой Все виджеты и маппинги сохранены (is_*_listbox/combo на месте)
This commit is contained in:
+77
-56
@@ -1003,41 +1003,27 @@ class DokoGenApp:
|
|||||||
sw_frame.columnconfigure(0, weight=1)
|
sw_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
# ===== Вкладка «Безопасность» =====
|
# ===== Вкладка «Безопасность» =====
|
||||||
|
# ===== Вкладка «Безопасность» — с вложенными подвкладками =====
|
||||||
sec_tab = ttk.Frame(nb2)
|
sec_tab = ttk.Frame(nb2)
|
||||||
nb2.add(sec_tab, text="Безопасность")
|
nb2.add(sec_tab, text="Безопасность")
|
||||||
sec_frame = ttk.Frame(sec_tab, padding=6)
|
sec_frame = ttk.Frame(sec_tab, padding=6)
|
||||||
sec_frame.pack(fill=tk.BOTH, expand=True)
|
sec_frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
# --- Панель действий (сверху, всегда видна) ---
|
nb_sec = ttk.Notebook(sec_frame)
|
||||||
sec_actions = ttk.Frame(sec_frame)
|
nb_sec.pack(fill=tk.BOTH, expand=True)
|
||||||
sec_actions.pack(fill=tk.X, pady=(0, 6))
|
|
||||||
ttk.Button(sec_actions, text="⚡ Автозаполнить",
|
|
||||||
command=self._auto_fill_security_click).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(sec_actions, text="⬇️ Загрузить угрозы ФСТЭК",
|
|
||||||
command=self._download_fstec_threats).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(sec_actions, text="⚙️ Подобрать угрозы (программа)",
|
|
||||||
command=self._suggest_threats_program_click).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(sec_actions, text="🤖 Подобрать угрозы (ИИ)",
|
|
||||||
command=self._suggest_threats_ai_click).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(sec_actions, text="⚖️ Сравнить",
|
|
||||||
command=self._compare_threats).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(sec_actions, text="🤖 Проверить безопасность через ИИ",
|
|
||||||
command=self._check_security_ai).pack(side=tk.LEFT, padx=2)
|
|
||||||
self._threats_date_label = ttk.Label(sec_actions, text="", foreground='gray')
|
|
||||||
self._threats_date_label.pack(side=tk.LEFT, padx=8)
|
|
||||||
|
|
||||||
# --- Две колонки: слева нарушитель+угрозы, справа остальное ---
|
# --- Подвкладка 1: Основные данные (нарушитель + автозаполнение) ---
|
||||||
sec_cols = ttk.Frame(sec_frame)
|
sec_main = ttk.Frame(nb_sec, padding=8)
|
||||||
sec_cols.pack(fill=tk.BOTH, expand=True)
|
nb_sec.add(sec_main, text="Основные данные")
|
||||||
sec_left = ttk.Frame(sec_cols)
|
|
||||||
sec_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 6))
|
|
||||||
sec_right = ttk.Frame(sec_cols)
|
|
||||||
sec_right.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
# ===== Левая колонка =====
|
ttk.Label(sec_main, text="Модель нарушителя (Методика ФСТЭК от 05.02.2021):",
|
||||||
ttk.Label(sec_left, text="Категория нарушителя:").pack(anchor=tk.W)
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W, pady=(0, 4))
|
||||||
self.is_attacker_category_combo = ttk.Combobox(sec_left, state='readonly')
|
|
||||||
self.is_attacker_category_combo.pack(fill=tk.X, pady=(2, 2))
|
row1 = ttk.Frame(sec_main)
|
||||||
|
row1.pack(fill=tk.X, pady=3)
|
||||||
|
ttk.Label(row1, text="Категория нарушителя:", width=26).pack(side=tk.LEFT)
|
||||||
|
self.is_attacker_category_combo = ttk.Combobox(row1, state='readonly', width=50)
|
||||||
|
self.is_attacker_category_combo.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
try:
|
try:
|
||||||
import json as _json
|
import json as _json
|
||||||
_ac = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_categories.json')
|
_ac = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_categories.json')
|
||||||
@@ -1047,9 +1033,11 @@ class DokoGenApp:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
ttk.Label(sec_left, text="Уровень возможностей нарушителя:").pack(anchor=tk.W)
|
row2 = ttk.Frame(sec_main)
|
||||||
self.is_attacker_level_combo = ttk.Combobox(sec_left, state='readonly')
|
row2.pack(fill=tk.X, pady=3)
|
||||||
self.is_attacker_level_combo.pack(fill=tk.X, pady=(2, 2))
|
ttk.Label(row2, text="Уровень возможностей нарушителя:", width=26).pack(side=tk.LEFT)
|
||||||
|
self.is_attacker_level_combo = ttk.Combobox(row2, state='readonly', width=50)
|
||||||
|
self.is_attacker_level_combo.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
try:
|
try:
|
||||||
import json as _json
|
import json as _json
|
||||||
_al = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_levels.json')
|
_al = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_levels.json')
|
||||||
@@ -1060,10 +1048,10 @@ class DokoGenApp:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
ttk.Label(sec_left, text="Виды нарушителей:").pack(anchor=tk.W)
|
ttk.Label(sec_main, text="Виды нарушителей:").pack(anchor=tk.W, pady=(6, 2))
|
||||||
sec_at_row = ttk.Frame(sec_left)
|
sec_at_row = ttk.Frame(sec_main)
|
||||||
sec_at_row.pack(fill=tk.X, pady=(2, 4))
|
sec_at_row.pack(fill=tk.BOTH, expand=True)
|
||||||
self.is_attacker_types_listbox = tk.Listbox(sec_at_row, height=3, exportselection=False)
|
self.is_attacker_types_listbox = tk.Listbox(sec_at_row, height=6, exportselection=False)
|
||||||
self.is_attacker_types_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.is_attacker_types_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
sec_at_btn = ttk.Frame(sec_at_row)
|
sec_at_btn = ttk.Frame(sec_at_row)
|
||||||
sec_at_btn.pack(side=tk.RIGHT, padx=(4, 0))
|
sec_at_btn.pack(side=tk.RIGHT, padx=(4, 0))
|
||||||
@@ -1072,19 +1060,48 @@ class DokoGenApp:
|
|||||||
ttk.Button(sec_at_btn, text="📚", width=3,
|
ttk.Button(sec_at_btn, text="📚", width=3,
|
||||||
command=lambda: self._select_sw_from_dict(self.is_attacker_types_listbox, 'attacker_types.json')).pack(pady=1)
|
command=lambda: self._select_sw_from_dict(self.is_attacker_types_listbox, 'attacker_types.json')).pack(pady=1)
|
||||||
|
|
||||||
ttk.Label(sec_left, text="Основные угрозы БИ:").pack(anchor=tk.W)
|
sec_main_btns = ttk.Frame(sec_main)
|
||||||
sec_thr_row = ttk.Frame(sec_left)
|
sec_main_btns.pack(fill=tk.X, pady=(8, 0))
|
||||||
sec_thr_row.pack(fill=tk.BOTH, expand=True, pady=(2, 4))
|
ttk.Button(sec_main_btns, text="⚡ Автозаполнить (нарушитель, угрозы, меры)",
|
||||||
self.is_threats_listbox = tk.Listbox(sec_thr_row, height=7, exportselection=False)
|
command=self._auto_fill_security_click).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(sec_main_btns, text="🤖 Проверить безопасность через ИИ",
|
||||||
|
command=self._check_security_ai).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Label(sec_main_btns,
|
||||||
|
text="Подбор по данным объекта — как рекомендация, оператор подтверждает.",
|
||||||
|
foreground='gray').pack(side=tk.LEFT, padx=8)
|
||||||
|
|
||||||
|
# --- Подвкладка 2: Угрозы ---
|
||||||
|
sec_threats = ttk.Frame(nb_sec, padding=8)
|
||||||
|
nb_sec.add(sec_threats, text="Угрозы")
|
||||||
|
|
||||||
|
thr_actions = ttk.Frame(sec_threats)
|
||||||
|
thr_actions.pack(fill=tk.X, pady=(0, 6))
|
||||||
|
ttk.Button(thr_actions, text="⬇️ Загрузить угрозы ФСТЭК",
|
||||||
|
command=self._download_fstec_threats).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(thr_actions, text="⚙️ Подобрать (программа)",
|
||||||
|
command=self._suggest_threats_program_click).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(thr_actions, text="🤖 Подобрать (ИИ)",
|
||||||
|
command=self._suggest_threats_ai_click).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(thr_actions, text="⚖️ Сравнить программа ↔ ИИ",
|
||||||
|
command=self._compare_threats).pack(side=tk.LEFT, padx=2)
|
||||||
|
self._threats_date_label = ttk.Label(thr_actions, text="", foreground='gray')
|
||||||
|
self._threats_date_label.pack(side=tk.LEFT, padx=8)
|
||||||
|
|
||||||
|
thr_row = ttk.Frame(sec_threats)
|
||||||
|
thr_row.pack(fill=tk.BOTH, expand=True)
|
||||||
|
self.is_threats_listbox = tk.Listbox(thr_row, height=10, exportselection=False)
|
||||||
self.is_threats_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.is_threats_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
sec_thr_btn = ttk.Frame(sec_thr_row)
|
thr_btn = ttk.Frame(thr_row)
|
||||||
sec_thr_btn.pack(side=tk.RIGHT, padx=(4, 0))
|
thr_btn.pack(side=tk.RIGHT, padx=(4, 0))
|
||||||
ttk.Button(sec_thr_btn, text="- Удалить",
|
ttk.Button(thr_btn, text="- Удалить",
|
||||||
command=lambda: self._del_sw_item(self.is_threats_listbox)).pack(fill=tk.X, pady=1)
|
command=lambda: self._del_sw_item(self.is_threats_listbox)).pack(fill=tk.X, pady=1)
|
||||||
ttk.Button(sec_thr_btn, text="📚 Из словаря",
|
ttk.Button(thr_btn, text="📚 Из словаря",
|
||||||
command=lambda: self._select_sw_from_dict(self.is_threats_listbox, 'threats_fstec.json')).pack(fill=tk.X, pady=1)
|
command=lambda: self._select_sw_from_dict(self.is_threats_listbox, 'threats_fstec.json')).pack(fill=tk.X, pady=1)
|
||||||
|
|
||||||
# ===== Правая колонка =====
|
# --- Подвкладка 3: Инциденты и критерии ---
|
||||||
|
sec_inc = ttk.Frame(nb_sec, padding=8)
|
||||||
|
nb_sec.add(sec_inc, text="Инциденты и критерии")
|
||||||
|
|
||||||
def _sec_row(parent, label, attr, dict_name=None, height=2, allow_add=False):
|
def _sec_row(parent, label, attr, dict_name=None, height=2, allow_add=False):
|
||||||
"""Ряд: label + listbox + кнопки (+/-/✏️/📚)."""
|
"""Ряд: label + listbox + кнопки (+/-/✏️/📚)."""
|
||||||
ttk.Label(parent, text=label).pack(anchor=tk.W)
|
ttk.Label(parent, text=label).pack(anchor=tk.W)
|
||||||
@@ -1096,7 +1113,6 @@ class DokoGenApp:
|
|||||||
btns = ttk.Frame(row)
|
btns = ttk.Frame(row)
|
||||||
btns.pack(side=tk.RIGHT, padx=(4, 0))
|
btns.pack(side=tk.RIGHT, padx=(4, 0))
|
||||||
if allow_add or not dict_name:
|
if allow_add or not dict_name:
|
||||||
# ручное добавление: если разрешено или нет словаря
|
|
||||||
ttk.Button(btns, text="+", width=2,
|
ttk.Button(btns, text="+", width=2,
|
||||||
command=lambda l=lb: self._add_sw_item(l)).pack(pady=1)
|
command=lambda l=lb: self._add_sw_item(l)).pack(pady=1)
|
||||||
ttk.Button(btns, text="-", width=2,
|
ttk.Button(btns, text="-", width=2,
|
||||||
@@ -1107,18 +1123,23 @@ class DokoGenApp:
|
|||||||
ttk.Button(btns, text="📚", width=3,
|
ttk.Button(btns, text="📚", width=3,
|
||||||
command=lambda l=lb, d=dict_name: self._select_sw_from_dict(l, d)).pack(pady=1)
|
command=lambda l=lb, d=dict_name: self._select_sw_from_dict(l, d)).pack(pady=1)
|
||||||
|
|
||||||
_sec_row(sec_right, "Типы компьютерных инцидентов:",
|
_sec_row(sec_inc, "Типы компьютерных инцидентов:",
|
||||||
'is_incidents_listbox', 'incidents.json', height=2)
|
'is_incidents_listbox', 'incidents.json', height=3)
|
||||||
_sec_row(sec_right, "Показатели критериев значимости:",
|
_sec_row(sec_inc, "Показатели критериев значимости:",
|
||||||
'is_criteria_values_listbox', 'criteria_indicators.json', height=2)
|
'is_criteria_values_listbox', 'criteria_indicators.json', height=3)
|
||||||
ttk.Button(sec_right, text="📋 Заполнить по таблице ПП № 127",
|
ttk.Button(sec_inc, text="📋 Заполнить по таблице ПП № 127",
|
||||||
command=self._open_criteria_127_dialog).pack(anchor=tk.W, pady=(0, 4))
|
command=self._open_criteria_127_dialog).pack(anchor=tk.W, pady=(0, 4))
|
||||||
_sec_row(sec_right, "Обоснование значений показателей:",
|
_sec_row(sec_inc, "Обоснование значений показателей:",
|
||||||
'is_criteria_justification_listbox', None, height=2)
|
'is_criteria_justification_listbox', None, height=3)
|
||||||
_sec_row(sec_right, "Организационные меры:",
|
|
||||||
'is_org_measures_listbox', 'org_measures.json', height=2, allow_add=True)
|
# --- Подвкладка 4: Организационные и технические меры ---
|
||||||
_sec_row(sec_right, "Технические меры:",
|
sec_meas = ttk.Frame(nb_sec, padding=8)
|
||||||
'is_tech_measures_listbox', 'tech_measures.json', height=2, allow_add=True)
|
nb_sec.add(sec_meas, text="Орг. и тех. меры")
|
||||||
|
|
||||||
|
_sec_row(sec_meas, "Организационные меры:",
|
||||||
|
'is_org_measures_listbox', 'org_measures.json', height=5, allow_add=True)
|
||||||
|
_sec_row(sec_meas, "Технические меры:",
|
||||||
|
'is_tech_measures_listbox', 'tech_measures.json', height=5, allow_add=True)
|
||||||
|
|
||||||
# ===== Вкладка «Средства защиты» =====
|
# ===== Вкладка «Средства защиты» =====
|
||||||
def_tab = ttk.Frame(nb2)
|
def_tab = ttk.Frame(nb2)
|
||||||
|
|||||||
Reference in New Issue
Block a user