From 0b3aff4ae998626c46520c3b3bae2939724b44f2 Mon Sep 17 00:00:00 2001 From: prog1764 Date: Tue, 4 Aug 2026 17:30:13 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0=20?= =?UTF-8?q?=C2=AB=E2=9C=8F=EF=B8=8F=20=D0=A3=D1=82=D0=BE=D1=87=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C/=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB-=D0=B2=D0=BE=C2=BB=20=D0=B2=20=D0=BE=D0=BA=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=D0=B0=20=D0=B8=D0=B7=20?= =?UTF-8?q?=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - если в словаре есть позиции с «(уточнить …)» — в окне выбора появляется кнопка - отмечаешь галочкой, например «Сервер (уточнить модель)» — жмёшь кнопку, программа запрашивает модель/версию/конфигурацию и количество - в список добавляется полная строка: «Сервер Supermicro SSG-2027R -E1R24N, 2 шт.» - работает для всех словарей: ПО (hw_sw, os_sw, app_sw), СЗИ и т.д. --- dokogen/ui.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/dokogen/ui.py b/dokogen/ui.py index eedad0e..3b37991 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -1965,11 +1965,13 @@ class DokoGenApp: scrollbar.pack(side=tk.RIGHT, fill=tk.Y) vars = {} + items_map = {} for item in items: var = tk.BooleanVar(value=item in existing) cb = ttk.Checkbutton(check_frame, text=item, variable=var) cb.pack(anchor=tk.W, pady=1) vars[item] = var + items_map[item] = (var, cb) btn_frame = ttk.Frame(dialog, padding=10) btn_frame.pack(fill=tk.X) @@ -1989,12 +1991,87 @@ class DokoGenApp: for var in vars.values(): var.set(False) + has_refinable = any('уточн' in str(item).lower() for item in items) + + def _refine_selected(): + """Для отмеченных позиций с «(уточнить …)» запросить модель и количество.""" + for item, (var, cb) in list(items_map.items()): + if not var.get(): + continue + if 'уточн' not in item.lower(): + continue + new_text = self._refine_dict_item_dialog(item) + if new_text is None or new_text == item: + continue + # Обновляем текст чекбокса и ключ в словаре + cb.config(text=new_text) + vars.pop(item, None) + items_map.pop(item, None) + vars[new_text] = var + items_map[new_text] = (var, cb) + + if has_refinable: + ttk.Button(btn_frame, text="✏️ Уточнить модель/кол-во", + command=_refine_selected).pack(side=tk.LEFT, padx=2) ttk.Button(btn_frame, text="Выбрать всё", command=_select_all).pack(side=tk.LEFT, padx=2) ttk.Button(btn_frame, text="Снять всё", command=_clear_all).pack(side=tk.LEFT, padx=2) ttk.Button(btn_frame, text="✅ Применить", command=_apply).pack(side=tk.RIGHT, padx=2) dialog.wait_window() + def _refine_dict_item_dialog(self, base_item): + """Окно уточнения модели и количества для позиции из словаря. + + Возвращает полную строку вида «Сервер <модель>, шт.» или None. + """ + dialog = tk.Toplevel(self.root) + dialog.title("Уточнить модель и количество") + dialog.geometry("500x180") + dialog.transient(self.root) + dialog.grab_set() + + frame = ttk.Frame(dialog, padding=10) + frame.pack(fill=tk.BOTH, expand=True) + + ttk.Label(frame, text=f"Позиция: {base_item}", wraplength=470, + font=('Arial', 9, 'bold')).pack(anchor=tk.W, pady=(0, 8)) + + ttk.Label(frame, text="Модель / версия / конфигурация:").pack(anchor=tk.W) + model_entry = ttk.Entry(frame, width=60) + model_entry.pack(fill=tk.X, pady=(2, 6)) + model_entry.focus_set() + + ttk.Label(frame, text="Количество (необязательно):").pack(anchor=tk.W) + count_entry = ttk.Entry(frame, width=20) + count_entry.pack(anchor=tk.W, pady=(2, 6)) + + result = {} + + def _ok(): + model = model_entry.get().strip() + count = count_entry.get().strip() + text = base_item + if model: + text = f"{text} {model}" + if count: + text = f"{text}, {count} шт." + result['val'] = text + dialog.destroy() + + def _cancel(): + result['val'] = None + dialog.destroy() + + btn_frame = ttk.Frame(frame) + btn_frame.pack(fill=tk.X, pady=(6, 0)) + ttk.Button(btn_frame, text="✅ ОК", command=_ok).pack(side=tk.RIGHT, padx=2) + ttk.Button(btn_frame, text="Отмена", command=_cancel).pack(side=tk.RIGHT, padx=2) + + dialog.bind('', lambda e: _ok()) + dialog.bind('', lambda e: _cancel()) + dialog.wait_window() + return result.get('val') + def _select_from_defense_list(self): """Выбор СЗИ из JSON словаря.""" import json