Загрузка данных
# ---*--- Dozor/gui/main_interface.py ---*---
import tkinter as tk
from PIL import Image, ImageTk
import os
from tkinter import ttk
from modules.administrator.admin_user_inter import AdminUserInter
from modules.utils.window_behavior import LevelOneBehavior
from modules.administrator.admin_db_inter import AdminDBInter
def open_window(title):
"""
Универсальная заглушка для всех окон, кроме "Пользователей".
Теперь она просто выводит сообщение в консоль,
так как у неё нет доступа к главному окну напрямую.
"""
print(f"[DEBUG] Вызван универсальный open_window для: {title}")
# Создаем временное независимое окно, чтобы программа не вылетала
temp_win = tk.Toplevel()
temp_win.title(title)
temp_win.geometry("300x150")
ttk.Label(temp_win, text=f"Интерфейс '{title}'", font=("Arial", 14)).pack(pady=20)
ttk.Button(temp_win, text="Закрыть", command=temp_win.destroy).pack(pady=10)
class MainInterface(tk.Tk):
def __init__(self):
super().__init__()
# --- Настройка поведения Уровня 1 (Главное окно) ---
behavior = LevelOneBehavior()
behavior.setup_level_one(self)
self.title("ДОЗОР -- Heri-Hodie-Cras")
self.geometry("458x360")
self.resizable(False, False)
bg_color = "#EEEEEE"
self.configure(bg=bg_color)
main_frame = tk.Frame(self, bg=bg_color)
main_frame.pack(padx=1, pady=1, fill='both', expand=True)
button_width = 20
button_height = 2
button_font = ("Arial", 12, "bold")
for i in range(7):
main_frame.grid_rowconfigure(i, weight=0)
main_frame.grid_columnconfigure(0, minsize=210, weight=0)
main_frame.grid_columnconfigure(1, minsize=235, weight=0)
self.active_popup = None
left_texts = [
["Администратор", [("Пользователи", "users"), ("Базы данных", "db")]],
["Менеджер базы", [("Менеджер группы", "group"), ("Менеджер показателей", "metrics")]],
["Эксперты", [("Создание файлов", "create_files"), ("Агрегирование файлов", "aggregate")]],
["Проект", [("Создать", "new"), ("Редактировать", "edit"), ("В архив", "archive")]],
["Прогнозирование", []]
]
# --- ЛЕВАЯ ПАНЕЛЬ ---
for row_index, item in enumerate(left_texts):
btn_text = item[0]
btn = tk.Button(
main_frame,
text=btn_text,
font=button_font,
width=button_width,
height=button_height,
anchor="w",
command=lambda r=row_index, name=btn_text: self.show_dropdown(name, r)
)
btn.grid(row=row_index, column=0, padx=5, pady=5, sticky="ew")
# --- ПРАВАЯ ПАНЕЛЬ ---
right_box = tk.Frame(main_frame, bg=bg_color, width=235, height=300)
right_box.grid(row=0, column=1, rowspan=6, pady=5, sticky="nsew", padx=(0, 5))
right_box.grid_propagate(False)
right_box.columnconfigure(0, weight=1)
picture_frame = tk.Frame(right_box, bg="#d9ead3", highlightbackground="gray", highlightthickness=1, width=225, height=160)
picture_frame.place(relx=0.5, y=5, anchor="n")
picture_frame.grid_propagate(False)
current_dir = os.path.dirname(os.path.abspath(__file__))
image_path = os.path.join(current_dir, "images", "Сова1.png")
try:
original_img = Image.open(image_path)
target_w, target_h = 215, 130
img_ratio = original_img.width / original_img.height
box_ratio = target_w / target_h
if img_ratio > box_ratio:
new_w, new_h = target_w, int(target_w / img_ratio)
else:
new_w, new_h = int(target_h * img_ratio), target_h
resized_img = original_img.resize((new_w, new_h), resample=Image.LANCZOS)
self.tk_image = ImageTk.PhotoImage(resized_img)
label = tk.Label(picture_frame, image=self.tk_image, bg="#d9ead3", borderwidth=0)
label.image = self.tk_image
label.place(relx=0.5, rely=0.5, anchor='center', width=new_w, height=new_h)
except Exception as e:
print(f"[FATAL IMAGE ERROR]: {e}")
canvas = tk.Canvas(picture_frame, width=225, height=160, bg="red", highlightthickness=0)
canvas.create_text(112, 80, text="IMG\nFAIL", fill="white")
canvas.place(x=0, y=0, relwidth=1, relheight=1)
spacer = tk.Frame(right_box, bg=bg_color, height=1)
spacer.grid(row=0, column=0, pady=(170, 10), sticky="ew")
# --- ФИКС: ДОБАВЛЕН НЕВИДИМЫЙ РАЗДЕЛИТЕЛЬ ---
# Этот Frame заберет на себя те самые 2-3 пикселя сдвига,
# создав жесткий отступ сверху для кнопок.
upper_padding = tk.Frame(right_box, bg=bg_color, height=3)
upper_padding.grid(row=1, column=0)
btn6 = tk.Button(right_box, text="Руководство", font=button_font, width=button_width, height=button_height)
btn6.grid(row=2, column=0, ipadx=10, pady=(0, 5), sticky="ew")
btn7 = tk.Button(right_box, text="О программе", font=button_font, width=button_width, height=button_height)
btn7.grid(row=3, column=0, ipadx=10, pady=(5, 15), sticky="ew")
# --- ПОДВАЛ ---
footer_frame = tk.Frame(main_frame, bg="#0078D7")
footer_frame.grid(row=6, column=0, columnspan=2, sticky="ew", pady=(10, 0))
footer_label = tk.Label(
footer_frame,
text="Copyright © Минск, НИИ, 2027 год",
fg="#FF8C00",
bg="#0078D7",
font=("Arial", 12, "bold")
)
footer_label.pack(expand=True, ipady=4)
# Закрытие при клике мимо
self.bind("<Button-1>", self.close_popup_global)
def show_dropdown(self, menu_name, base_row):
"""Показывает всплывающее окно под кнопкой."""
if self.active_popup is not None:
self.active_popup.destroy()
data = None
lookup = {
"Администратор": [("Пользователи", "users"), ("Базы данных", "db")],
"Менеджер базы": [("Менеджер группы", "group"), ("Менеджер показателей", "metrics")],
"Эксперты": [("Создание файлов", "create_files"), ("Агрегирование файлов", "aggregate")],
"Проект": [("Создать", "new"), ("Редактировать", "edit"), ("В архив", "archive")]
}
data = lookup.get(menu_name, [])
if not data:
return
# Координаты относительно экрана
x = self.winfo_rootx() + 5
y = self.winfo_rooty() + (base_row * 35) + 50
popup = tk.Toplevel(self)
popup.overrideredirect(True)
popup.geometry(f"+{x}+{y}")
popup_frame = tk.Frame(popup, bg="#F9F9F9", bd=1, relief="solid")
popup_frame.pack()
def make_command(action_title, target_popup):
def execute():
target_popup.destroy()
if action_title == "Пользователи":
self.open_admin_users()
elif action_title == "Базы данных":
self.open_admin_dbs()
else:
open_window(action_title)
return execute
for sub_text, _ in data:
def make_command(action_title, target_popup=None):
"""Создаем команду для кнопки"""
def execute():
if target_popup:
target_popup.destroy()
if action_title == "Пользователи":
self.open_admin_users()
elif action_title == "Базы данных":
self.open_admin_dbs()
else:
open_window(action_title)
return execute
btn_command = make_command(sub_text, popup) # Передаем popup вторым аргументом
sub_btn = tk.Button(
popup_frame,
text=sub_text,
font=("Arial", 11),
width=20,
height=1,
anchor="w",
bd=0,
relief="flat",
cursor="hand2",
command=btn_command
)
sub_btn.pack(fill="x", ipadx=5, ipady=3)
sep = tk.Frame(popup_frame, bg="#E0E0E0", height=1)
sep.pack(fill="x")
def open_admin_users(self):
from modules.utils.app_state import disable_app, enable_app
root = self.winfo_toplevel()
if not root or not hasattr(root, 'winfo_exists') or not root.winfo_exists():
return
disable_app(root)
users_win = AdminUserInter(self) # Создание окна
self.wait_window(users_win) # Ожидание закрытия
enable_app(root)
def open_admin_dbs(self):
from modules.utils.app_state import disable_app, enable_app
root = self.winfo_toplevel()
if not root or not hasattr(root, 'winfo_exists') or not root.winfo_exists():
return
disable_app(root)
db_win = AdminDBInter(self)
self.wait_window(db_win)
enable_app(root)
def on_submenu_select(self, title, popup_widget):
"""Этот метод больше не нужен, логика перенесена прямо в Button"""
pass
def close_popup_global(self, event):
"""Закрытие меню, если кликнули по пустому месту."""
if self.active_popup is not None:
widget = event.widget
while widget:
if widget == self.active_popup:
return
widget = widget.master
self.active_popup.destroy()
self.active_popup = None
def _update_child_positions(self, new_x, new_y):
"""Обновляет координаты всех дочерних окон второго уровня"""
if hasattr(self, '_child_windows'):
for child_data in self._child_windows:
win, offset_x, offset_y = child_data
try:
win.geometry(f"+{new_x + offset_x}+{new_y + offset_y}")
except Exception:
pass # Окно уже могло быть закрыто
def register_child_window(self, window, offset_x=0, offset_y=0):
"""Регистрирует дочернее окно 2 уровня."""
if not hasattr(self, '_child_windows'):
self._child_windows = []
# Проверяем, не зарегистрирован ли уже этот объект
for item in self._child_windows:
if item[0] == window:
return
self._child_windows.append((window, offset_x, offset_y))
if __name__ == "__main__":
app = MainInterface()
app.mainloop()