feat: вкладка «ПО» — списки с кнопками (как «Средства защиты»)

3 списка: ПО и аппаратные средства, Операционные системы, Прикладные программы.
У каждого кнопки «+ Добавить», «- Удалить», «✏️ Редактировать».
Данные хранятся построчно (\n), импорт и генератор сохраняют переносы.
This commit is contained in:
2026-08-04 16:31:25 +04:00
parent 300dfed5b3
commit 28cd764795
+60 -19
View File
@@ -906,25 +906,34 @@ class DokoGenApp:
net_frame.columnconfigure(1, weight=1) net_frame.columnconfigure(1, weight=1)
# ===== Вкладка «ПО» ===== # ===== Вкладка «ПО» (списки, как «Средства защиты») =====
sw_tab = ttk.Frame(nb2) sw_tab = ttk.Frame(nb2)
nb2.add(sw_tab, text="ПО") nb2.add(sw_tab, text="ПО")
sw_frame = ttk.Frame(sw_tab, padding=8) sw_frame = ttk.Frame(sw_tab, padding=8)
sw_frame.pack(fill=tk.BOTH, expand=True) sw_frame.pack(fill=tk.BOTH, expand=True)
sw_fields = [ sw_fields = [
("ПО и аппаратные средства (модели, производители, количество):", "is_hw_sw_entry", 5), ("ПО и аппаратные средства (модели, производители, количество):", "is_hw_sw_listbox"),
("Операционные системы:", "is_os_sw_entry", 3), ("Операционные системы:", "is_os_sw_listbox"),
("Прикладные программы (MS Office, OpenOffice и пр.):", "is_app_sw_entry", 4), ("Прикладные программы (MS Office, OpenOffice и пр.):", "is_app_sw_listbox"),
] ]
for i, (label, attr, height) in enumerate(sw_fields): for i, (label, attr) in enumerate(sw_fields):
ttk.Label(sw_frame, text=label).grid(row=i * 2, column=0, sticky=tk.W, pady=(4, 0)) row = i * 2
txt = tk.Text(sw_frame, width=70, height=height, wrap=tk.WORD) ttk.Label(sw_frame, text=label).grid(row=row, column=0, sticky=tk.W, pady=(4, 0))
txt.grid(row=i * 2 + 1, column=0, columnspan=2, padx=8, sticky=tk.EW, pady=1) box = tk.Listbox(sw_frame, height=4, exportselection=False)
sb = ttk.Scrollbar(sw_frame, orient=tk.VERTICAL, command=txt.yview) box.grid(row=row + 1, column=0, sticky=tk.EW, padx=(0, 4), pady=1)
sb.grid(row=i * 2 + 1, column=2, sticky=tk.NS) sb = ttk.Scrollbar(sw_frame, orient=tk.VERTICAL, command=box.yview)
txt.configure(yscrollcommand=sb.set) sb.grid(row=row + 1, column=1, sticky=tk.NS)
setattr(self, attr, txt) box.configure(yscrollcommand=sb.set)
btn = ttk.Frame(sw_frame)
btn.grid(row=row + 1, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(btn, text="+ Добавить",
command=lambda a=attr: self._add_sw_item(getattr(self, a))).pack(fill=tk.X, pady=1)
ttk.Button(btn, text="- Удалить",
command=lambda a=attr: self._del_sw_item(getattr(self, a))).pack(fill=tk.X, pady=1)
ttk.Button(btn, text="✏️ Редактировать",
command=lambda a=attr: self._edit_sw_item(getattr(self, a))).pack(fill=tk.X, pady=1)
setattr(self, attr, box)
sw_frame.columnconfigure(0, weight=1) sw_frame.columnconfigure(0, weight=1)
# ===== Вкладка «Безопасность (236)» ===== # ===== Вкладка «Безопасность (236)» =====
@@ -1211,9 +1220,9 @@ class DokoGenApp:
('is_net_operator_entry', 'net_operator'), ('is_net_operator_entry', 'net_operator'),
('is_net_purpose_combo', 'net_purpose'), ('is_net_purpose_combo', 'net_purpose'),
('is_net_method_combo', 'net_method'), ('is_net_method_combo', 'net_method'),
('is_hw_sw_entry', 'hw_sw'), ('is_hw_sw_listbox', 'hw_sw'),
('is_os_sw_entry', 'os_sw'), ('is_os_sw_listbox', 'os_sw'),
('is_app_sw_entry', 'app_sw'), ('is_app_sw_listbox', 'app_sw'),
('is_attacker_category_entry', 'attacker_category'), ('is_attacker_category_entry', 'attacker_category'),
('is_threats_entry', 'threats'), ('is_threats_entry', 'threats'),
('is_incidents_entry', 'incidents'), ('is_incidents_entry', 'incidents'),
@@ -1224,7 +1233,9 @@ class DokoGenApp:
]: ]:
w = getattr(self, attr, None) w = getattr(self, attr, None)
if w is not None: if w is not None:
if isinstance(w, tk.Text): if isinstance(w, tk.Listbox):
setattr(isys, model_field, '\n'.join(w.get(0, tk.END)))
elif isinstance(w, tk.Text):
setattr(isys, model_field, w.get('1.0', 'end-1c').strip()) setattr(isys, model_field, w.get('1.0', 'end-1c').strip())
else: else:
setattr(isys, model_field, w.get().strip()) setattr(isys, model_field, w.get().strip())
@@ -1443,9 +1454,9 @@ class DokoGenApp:
('is_net_operator_entry', 'net_operator'), ('is_net_operator_entry', 'net_operator'),
('is_net_purpose_combo', 'net_purpose'), ('is_net_purpose_combo', 'net_purpose'),
('is_net_method_combo', 'net_method'), ('is_net_method_combo', 'net_method'),
('is_hw_sw_entry', 'hw_sw'), ('is_hw_sw_listbox', 'hw_sw'),
('is_os_sw_entry', 'os_sw'), ('is_os_sw_listbox', 'os_sw'),
('is_app_sw_entry', 'app_sw'), ('is_app_sw_listbox', 'app_sw'),
('is_attacker_category_entry', 'attacker_category'), ('is_attacker_category_entry', 'attacker_category'),
('is_threats_entry', 'threats'), ('is_threats_entry', 'threats'),
('is_incidents_entry', 'incidents'), ('is_incidents_entry', 'incidents'),
@@ -1459,6 +1470,11 @@ class DokoGenApp:
val = getattr(isys, model_field, '') or '' val = getattr(isys, model_field, '') or ''
if isinstance(w, ttk.Combobox): if isinstance(w, ttk.Combobox):
w.set(val) w.set(val)
elif isinstance(w, tk.Listbox):
w.delete(0, tk.END)
for line in str(val).split('\n'):
if line.strip():
w.insert(tk.END, line.strip())
elif isinstance(w, tk.Text): elif isinstance(w, tk.Text):
w.delete('1.0', tk.END) w.delete('1.0', tk.END)
w.insert('1.0', val) w.insert('1.0', val)
@@ -1545,6 +1561,31 @@ class DokoGenApp:
if item: if item:
self.defense_tools_listbox.insert(tk.END, item) self.defense_tools_listbox.insert(tk.END, item)
def _add_sw_item(self, listbox):
"""Добавить строку в список ПО."""
item = simpledialog.askstring("Добавить", "Введите позицию:")
if item:
listbox.insert(tk.END, item.strip())
def _del_sw_item(self, listbox):
"""Удалить выбранную строку из списка ПО."""
sel = listbox.curselection()
if sel:
listbox.delete(sel[0])
def _edit_sw_item(self, listbox):
"""Редактировать выбранную строку списка ПО."""
sel = listbox.curselection()
if not sel:
messagebox.showinfo("Редактировать", "Сначала выберите позицию в списке")
return
current = listbox.get(sel[0])
item = simpledialog.askstring("Редактировать", "Измените позицию:",
initialvalue=current)
if item is not None:
listbox.delete(sel[0])
listbox.insert(sel[0], item.strip())
def _add_user_to_is(self): def _add_user_to_is(self):
user = simpledialog.askstring("Добавить пользователя", "Введите ФИО или должность:") user = simpledialog.askstring("Добавить пользователя", "Введите ФИО или должность:")
if user: if user: