Загрузка данных
# -*- coding: utf-8 -*-
# group_manager.py
import json
import logging
import os
import re
import tkinter as tk
from tkinter import messagebox, ttk
from modules.database_module import *
# Настройка логирования
logging.basicConfig(level=logging.INFO)
# Определение абсолютного пути к файлу конфигурации
CONFIG_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
# Загрузка и сохранение конфигурации
def load_config():
global config_data
if os.path.exists(CONFIG_FILE):
logging.info(f"Файл конфигурации найден: {CONFIG_FILE}. Начинаем загрузку.")
with open(CONFIG_FILE, "r") as file:
try:
config_data = json.load(file)
logging.info("Файл конфигурации успешно загружен.")
logging.info(f"Текущие данные: {config_data}")
except json.JSONDecodeError:
logging.error(f"Файл {CONFIG_FILE} поврежден или неправильный формат!")
config_data = {"amplifier": "", "groups": {}}
else:
logging.info(
f"Файл конфигурации не найден: {CONFIG_FILE}. Создание нового файла."
)
config_data = {"amplifier": "", "groups": {}}
save_config()
def save_config():
try:
with open(CONFIG_FILE, "w") as file:
json.dump(config_data, file)
logging.info("Файл конфигурации успешно сохранён.")
except Exception as e:
logging.error(f"Ошибка при сохранении конфигурации: {e}")
# Класс менеджера групп
class GroupManager(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Менеджер групп")
self.geometry("450x350")
# Сохраняем ссылку на главное окно
self.parent = parent
# Начало моего кода
# Централизация окна относительно родительского окна
self.update_idletasks() # Обязательно вызывайте update_idletasks для точности расчетов
parent_x = parent.winfo_x()
parent_y = parent.winfo_y()
parent_w = parent.winfo_width()
parent_h = parent.winfo_height()
# Рассчитываем позицию нового окна
w = self.winfo_width()
h = self.winfo_height()
x = parent_x + (parent_w - w) // 2
y = parent_y + (parent_h - h) // 2
# Устанавливаем позицию окна
self.geometry(f"+{x}+{y}")
# Загружаем конфигурационные данные
load_config()
# Верхняя панель навигации
nav_panel = tk.Frame(self)
nav_panel.pack(fill="x", pady=10)
# Название группы
tk.Label(nav_panel, text="Название группы:").grid(
row=0, column=0, sticky="w", padx=5
)
self.name_entry = tk.Entry(nav_panel, width=30)
self.name_entry.grid(row=0, column=1, sticky="w", padx=5)
# Код группы (уникальный идентификатор)
tk.Label(nav_panel, text="Код группы (ID):").grid(
row=1, column=0, sticky="w", padx=5
)
self.code_entry = tk.Entry(nav_panel, width=30)
self.code_entry.grid(row=1, column=1, sticky="w", padx=5)
# КОВ группы
tk.Label(nav_panel, text="КОВ группы:").grid(
row=2, column=0, sticky="w", padx=5
)
self.kov_entry = tk.Entry(nav_panel, width=15)
self.kov_entry.grid(row=2, column=1, sticky="w", padx=5)
# Постоянное поле для отображения коэффициента усиления
tk.Label(nav_panel, text="Постоянный коэффициент усиления:").grid(
row=3, column=0, sticky="w", padx=5
)
self.amplifier_display = tk.Entry(
nav_panel, state="normal", readonlybackground="white", fg="black", width=15
)
self.amplifier_display.grid(row=3, column=1, sticky="w", padx=5)
current_value = config_data.get("amplifier", "")
self.amplifier_display.delete(
0, tk.END
) # очистка поля перед установкой нового значения
self.amplifier_display.insert(0, current_value)
self.amplifier_display.config(
state="readonly"
) # переключаем поле в режим "только для чтения"
# Поле для временного ввода нового значения коэффициента
tk.Label(nav_panel, text="Новый коэффициент усиления:").grid(
row=4, column=0, sticky="w", padx=5
)
self.new_amplifier_entry = tk.Entry(nav_panel, width=15)
self.new_amplifier_entry.grid(row=4, column=1, sticky="w", padx=5)
# Кнопка для установки нового коэффициента
apply_amp_button = tk.Button(
nav_panel, text="Установить усиление", command=self.apply_new_amplifier
)
apply_amp_button.grid(row=5, column=0, columnspan=2, pady=10)
# Кнопка для добавления группы
add_group_button = tk.Button(
nav_panel, text="Добавить группу", command=self.add_group
)
add_group_button.grid(row=6, column=0, columnspan=2, pady=10)
# Дерево для отображения существующих групп
self.groups_tree = ttk.Treeview(self, columns=("code", "name"), show="headings")
self.groups_tree.column("code", width=150)
self.groups_tree.column("name", width=150)
self.groups_tree.heading("code", text="Код группы")
self.groups_tree.heading("name", text="Название")
self.groups_tree.pack(expand=True, fill="both")
# Привязываем события к дереву
self.groups_tree.bind("<Double-Button-1>", self.edit_group)
self.groups_tree.bind("<Button-3>", self.show_context_menu)
# Обновляем дерево с информацией о группах
self.update_groups_tree()
# Отдельно определяем методы класса
def notify_main_window(self):
# Уведомляем главное окно о том, что группа сменилась
self.parent.notify_group_change()
def select_group(self):
# Вызываем уведомление главного окна о смене группы
self.notify_main_window()
def update_groups_tree(self):
# Очищаем текущий список групп
self.groups_tree.delete(*self.groups_tree.get_children())
# Получаем список всех групп из базы данных
groups = list_groups()
# Заполняем таблицу заново
for group in groups:
self.groups_tree.insert("", "end", values=(group.code, group.name))
def apply_new_amplifier(self):
new_value = self.new_amplifier_entry.get().strip()
if new_value:
try:
float(new_value) # Проверяем, является ли значение числом
except ValueError:
messagebox.showwarning(
"Ошибка", "Коэффициент усиления должен быть числом."
)
return
# Обновляем постоянное поле
self.amplifier_display.config(state="normal")
self.amplifier_display.delete(0, tk.END)
self.amplifier_display.insert(0, new_value)
self.amplifier_display.config(state="readonly")
# Присваиваем значение глобальному состоянию
config_data["amplifier"] = new_value
save_config()
# Очищаем временное поле ввода
self.new_amplifier_entry.delete(0, tk.END)
else:
messagebox.showwarning(
"Ошибка", "Нужно ввести значение коэффициента усиления."
)
def add_group(self):
name = self.name_entry.get().strip()
code = self.code_entry.get().strip()
kov = self.kov_entry.get().strip()
# Проверка формата КОВ
if not re.match(r"^\d+(.\d+)?$", kov):
return messagebox.showwarning(
"Ошибка", "КОВ группы должен быть численным значением."
)
# Проверка уникальности кода группы
existing_codes = [grp.code for grp in list_groups()]
if code in existing_codes:
return messagebox.showwarning("Ошибка", "Такой код группы уже занят.")
if not all([name, code]):
return messagebox.showwarning("Ошибка", "Заполните название и код группы.")
# Добавляем группу в базу данных
add_group(name=name, code=code, kov=float(kov))
# Обновляем таблицу
self.update_groups_tree()
# Очищаем поля ввода
self.name_entry.delete(0, tk.END)
self.code_entry.delete(0, tk.END)
self.kov_entry.delete(0, tk.END)
def edit_group(self, event):
selected_item = self.groups_tree.selection()
if selected_item:
item_values = self.groups_tree.item(selected_item)["values"]
current_code = item_values[0] # получаем код группы
found_group = next(
(grp for grp in list_groups() if grp.code == current_code), None
)
if found_group:
# Начинаем создание окна редактирования группы
dialog = tk.Toplevel(self)
dialog.title("Редактировать группу")
dialog.geometry("300x200")
# ЦЕНТРИРУЕМ ОКНО ОТНОСИТЕЛЬНО РОДИТЕЛЯ
self.update_idletasks() # Важно для правильной оценки размеров
parent_x = self.winfo_x()
parent_y = self.winfo_y()
parent_w = self.winfo_width()
parent_h = self.winfo_height()
# Расчёт позиций окна
w = dialog.winfo_width()
h = dialog.winfo_height()
x = parent_x + (parent_w - w) // 2
y = parent_y + (parent_h - h) // 2
# Установка позиции окна
dialog.geometry(f"+{x}+{y}")
# Дальше остальная логика создания элементов окна...
# Название группы
tk.Label(dialog, text="Название группы:").pack(anchor="w")
name_field = tk.Entry(dialog)
name_field.insert(0, found_group.name)
name_field.pack(fill="x", pady=5)
# Код группы (уникальный идентификатор)
tk.Label(dialog, text="Код группы (ID):").pack(anchor="w")
code_field = tk.Entry(dialog)
code_field.insert(0, found_group.code)
code_field.pack(fill="x", pady=5)
# КОВ группы
tk.Label(dialog, text="КОВ группы:").pack(anchor="w")
kov_field = tk.Entry(dialog)
kov_field.insert(0, str(found_group.kov))
kov_field.pack(fill="x", pady=5)
# Кнопка для сохранения изменений
save_btn = tk.Button(
dialog,
text="Сохранить",
command=lambda: self.save_group_changes(
current_code,
name_field.get(),
code_field.get(),
kov_field.get(),
),
)
save_btn.pack(anchor="w", pady=5)
else:
messagebox.showwarning("Ошибка", "Не удалось найти указанную группу.")
else:
messagebox.showwarning("Ошибка", "Выберите группу для редактирования.")
def save_group_changes(self, group_code, new_name, new_code, new_kov):
# Проверяем оригинальную группу
original_group = next(
(grp for grp in list_groups() if grp.code == group_code), None
)
if original_group is None:
return messagebox.showwarning("Ошибка", "Группа не найдена.")
# Проверка уникальности KOV
other_groups = [grp for grp in list_groups() if grp.code != group_code]
existing_kovs = set(grp.kov for grp in other_groups)
# Проверка нового значения KOV
if new_kov.strip():
try:
new_kov_float = float(new_kov)
except ValueError:
return messagebox.showwarning(
"Ошибка", "Некорректное значение КОВ. Оно должно быть числом."
)
if new_kov_float in existing_kovs:
return messagebox.showwarning(
"Ошибка", "Такое значение КОВ уже занято другой группой."
)
else:
new_kov_float = None
# Если код группы изменился, обновляем показатели
old_code = original_group.code
if new_code != old_code:
# Получаем список показателей текущей группы
indicators = get_all_indicators_by_group(old_code)
updated_indicators = []
# Формируем новый код для каждого показателя
for ind in indicators:
# ЗАМЕНА _ НА -
parts = ind.code.split("-")
if len(parts) > 1:
new_indicator_code = f"{new_code}-{parts[-1]}"
else:
# Защита от ситуаций, когда в коде нет "-"
new_indicator_code = f"{new_code}-{ind.code}"
updated_indicators.append((ind.id, new_indicator_code))
# Обновляем коды показателей
for ind_id, new_code in updated_indicators:
print(f"Обновляю индикатор {ind_id}: новый код {new_code}")
edit_indicator(ind_id, new_code=new_code)
# Прямо обновляем группу в базе данных
session = Session()
group = session.query(Group).filter_by(code=group_code).first()
if group:
group.name = new_name
group.code = new_code
group.kov = new_kov_float
session.commit()
self.update_groups_tree()
messagebox.showinfo("Готово", "Группа успешно обновлена.")
else:
messagebox.showwarning("Ошибка", "Группа не найдена.")
def show_context_menu(self, event):
context_menu = tk.Menu(self, tearoff=0)
context_menu.add_command(label="Удалить группу", command=self.delete_group)
context_menu.post(event.x_root, event.y_root)
def delete_group(self):
selected_item = self.groups_tree.selection()
if not selected_item:
messagebox.showwarning("Ошибка", "Нет выбранной группы для удаления.")
return
item_values = self.groups_tree.item(selected_item)["values"]
group_code = item_values[0]
found_group = next(
(grp for grp in list_groups() if grp.code == group_code), None
)
if found_group:
confirmation = messagebox.askyesno(
"Подтверждение",
f"Вы уверены, что хотите удалить группу '{found_group.name}'?",
)
if confirmation:
with Session() as session:
group = session.query(Group).filter_by(code=group_code).first()
if group:
session.delete(group)
session.commit()
self.update_groups_tree()
messagebox.showinfo("Готово", "Группа успешно удалена.")
else:
messagebox.showwarning(
"Ошибка", "Группу не удалось найти в базе данных."
)
else:
messagebox.showwarning("Ошибка", "Не удалось найти указанную группу.")
# Основная точка входа
if __name__ == "__main__":
root = tk.Tk()
load_config() # Загружаем конфигурацию
manager = GroupManager(root)
root.mainloop()