From 55449414699aee3a8737d890e8d83f9dd8bc5080 Mon Sep 17 00:00:00 2001 From: prog1764 Date: Tue, 4 Aug 2026 23:54:26 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA?= =?UTF-8?q?=D0=B0=20=C2=AB=D0=91=D0=B5=D0=B7=D0=BE=D0=BF=D0=B0=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8C=C2=BB=20=E2=80=94=20=D0=B2=D0=BB=D0=BE?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=BF=D0=BE=D0=B4=D0=B2?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA=D0=B8=20(=D0=BD=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Вместо тесной двухколоночной раскладки — ряд подвкладок: 1) Основные данные — модель нарушителя (категория, уровень Н1-Н4, виды) + кнопки «⚡ Автозаполнить» и «🤖 Проверить безопасность через ИИ» 2) Угрозы — панель действий (⬇️ ФСТЭК / ⚙️ программа / 🤖 ИИ / ⚖️ сравнить) + большой список угроз на всю ширину 3) Инциденты и критерии — инциденты, показатели критериев, кнопка «📋 ПП № 127», обоснование 4) Орг. и тех. меры — оба списка с нормальной высотой Все виджеты и маппинги сохранены (is_*_listbox/combo на месте) --- dokogen/ui.py | 133 +++++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 56 deletions(-) diff --git a/dokogen/ui.py b/dokogen/ui.py index bc909fb..f266f96 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -1003,41 +1003,27 @@ class DokoGenApp: sw_frame.columnconfigure(0, weight=1) # ===== Вкладка «Безопасность» ===== + # ===== Вкладка «Безопасность» — с вложенными подвкладками ===== sec_tab = ttk.Frame(nb2) nb2.add(sec_tab, text="Безопасность") sec_frame = ttk.Frame(sec_tab, padding=6) sec_frame.pack(fill=tk.BOTH, expand=True) - # --- Панель действий (сверху, всегда видна) --- - sec_actions = ttk.Frame(sec_frame) - 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) + nb_sec = ttk.Notebook(sec_frame) + nb_sec.pack(fill=tk.BOTH, expand=True) - # --- Две колонки: слева нарушитель+угрозы, справа остальное --- - sec_cols = ttk.Frame(sec_frame) - sec_cols.pack(fill=tk.BOTH, expand=True) - 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) + # --- Подвкладка 1: Основные данные (нарушитель + автозаполнение) --- + sec_main = ttk.Frame(nb_sec, padding=8) + nb_sec.add(sec_main, text="Основные данные") - # ===== Левая колонка ===== - ttk.Label(sec_left, text="Категория нарушителя:").pack(anchor=tk.W) - self.is_attacker_category_combo = ttk.Combobox(sec_left, state='readonly') - self.is_attacker_category_combo.pack(fill=tk.X, pady=(2, 2)) + ttk.Label(sec_main, text="Модель нарушителя (Методика ФСТЭК от 05.02.2021):", + font=('Arial', 10, 'bold')).pack(anchor=tk.W, pady=(0, 4)) + + 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: import json as _json _ac = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_categories.json') @@ -1047,9 +1033,11 @@ class DokoGenApp: except Exception: pass - ttk.Label(sec_left, text="Уровень возможностей нарушителя:").pack(anchor=tk.W) - self.is_attacker_level_combo = ttk.Combobox(sec_left, state='readonly') - self.is_attacker_level_combo.pack(fill=tk.X, pady=(2, 2)) + row2 = ttk.Frame(sec_main) + row2.pack(fill=tk.X, pady=3) + 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: import json as _json _al = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_levels.json') @@ -1060,10 +1048,10 @@ class DokoGenApp: except Exception: pass - ttk.Label(sec_left, text="Виды нарушителей:").pack(anchor=tk.W) - sec_at_row = ttk.Frame(sec_left) - sec_at_row.pack(fill=tk.X, pady=(2, 4)) - self.is_attacker_types_listbox = tk.Listbox(sec_at_row, height=3, exportselection=False) + ttk.Label(sec_main, text="Виды нарушителей:").pack(anchor=tk.W, pady=(6, 2)) + sec_at_row = ttk.Frame(sec_main) + sec_at_row.pack(fill=tk.BOTH, expand=True) + 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) sec_at_btn = ttk.Frame(sec_at_row) sec_at_btn.pack(side=tk.RIGHT, padx=(4, 0)) @@ -1072,19 +1060,48 @@ class DokoGenApp: 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) - ttk.Label(sec_left, text="Основные угрозы БИ:").pack(anchor=tk.W) - sec_thr_row = ttk.Frame(sec_left) - sec_thr_row.pack(fill=tk.BOTH, expand=True, pady=(2, 4)) - self.is_threats_listbox = tk.Listbox(sec_thr_row, height=7, exportselection=False) + sec_main_btns = ttk.Frame(sec_main) + sec_main_btns.pack(fill=tk.X, pady=(8, 0)) + ttk.Button(sec_main_btns, text="⚡ Автозаполнить (нарушитель, угрозы, меры)", + 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) - sec_thr_btn = ttk.Frame(sec_thr_row) - sec_thr_btn.pack(side=tk.RIGHT, padx=(4, 0)) - ttk.Button(sec_thr_btn, text="- Удалить", + thr_btn = ttk.Frame(thr_row) + thr_btn.pack(side=tk.RIGHT, padx=(4, 0)) + ttk.Button(thr_btn, text="- Удалить", 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) - # ===== Правая колонка ===== + # --- Подвкладка 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): """Ряд: label + listbox + кнопки (+/-/✏️/📚).""" ttk.Label(parent, text=label).pack(anchor=tk.W) @@ -1096,7 +1113,6 @@ class DokoGenApp: btns = ttk.Frame(row) btns.pack(side=tk.RIGHT, padx=(4, 0)) if allow_add or not dict_name: - # ручное добавление: если разрешено или нет словаря ttk.Button(btns, text="+", width=2, command=lambda l=lb: self._add_sw_item(l)).pack(pady=1) ttk.Button(btns, text="-", width=2, @@ -1107,18 +1123,23 @@ class DokoGenApp: ttk.Button(btns, text="📚", width=3, command=lambda l=lb, d=dict_name: self._select_sw_from_dict(l, d)).pack(pady=1) - _sec_row(sec_right, "Типы компьютерных инцидентов:", - 'is_incidents_listbox', 'incidents.json', height=2) - _sec_row(sec_right, "Показатели критериев значимости:", - 'is_criteria_values_listbox', 'criteria_indicators.json', height=2) - ttk.Button(sec_right, text="📋 Заполнить по таблице ПП № 127", + _sec_row(sec_inc, "Типы компьютерных инцидентов:", + 'is_incidents_listbox', 'incidents.json', height=3) + _sec_row(sec_inc, "Показатели критериев значимости:", + 'is_criteria_values_listbox', 'criteria_indicators.json', height=3) + ttk.Button(sec_inc, text="📋 Заполнить по таблице ПП № 127", command=self._open_criteria_127_dialog).pack(anchor=tk.W, pady=(0, 4)) - _sec_row(sec_right, "Обоснование значений показателей:", - 'is_criteria_justification_listbox', None, height=2) - _sec_row(sec_right, "Организационные меры:", - 'is_org_measures_listbox', 'org_measures.json', height=2, allow_add=True) - _sec_row(sec_right, "Технические меры:", - 'is_tech_measures_listbox', 'tech_measures.json', height=2, allow_add=True) + _sec_row(sec_inc, "Обоснование значений показателей:", + 'is_criteria_justification_listbox', None, height=3) + + # --- Подвкладка 4: Организационные и технические меры --- + sec_meas = ttk.Frame(nb_sec, padding=8) + 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)