Загрузка данных


# Dozor/modules/utils/window_behavior.py
import tkinter as tk


class LevelOneBehavior:
    """
    Поведение Главного окна (Уровень 1).
    Применяется к объекту класса tk.Tk
    """
    def setup_level_one(self):
        # 1. Окно неизменно в размерах
        self.resizable(False, False)
        
        # 5. Раскрываться не должно (уже выполнено resizable False)
        
        # 2. Открытие строго по центру экрана
        self.update_idletasks() # Обновляем данные о размере окна перед расчетом
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        size = tuple(int(_) for _ in self.geometry().split('+')[0].split('x'))
        x = (screen_width - size[0]) // 2
        y = (screen_height - size[1]) // 2
        self.geometry(f"{size[0]}x{size[1]}+{x}+{y}")

        # 3. Возможность передвижения пользователем
        # Эмуляция захвата за любое место окна (или привяжите к конкретному виджету шапки)
        self.bind("<ButtonPress-1>", self.save_offset)
        self.bind("<B1-Motion>", self.drag_window)

        # 4. Сворачивание (-) и закрытие (X) работают из коробки.
        # Кнопка "Свернуть" работает всегда, скрыть её нельзя без сторонних библиотек.
        # Но можно заблокировать действие разворачивания двойным кликом по шапке
        self.bind("<Double-Button-1>", lambda e: None) 

        # Хранилище для приклеенных окон 2 уровня
        self._child_windows = []
        self._drag_start_x = 0
        self._drag_start_y = 0

    def save_offset(self, event):
        self._drag_start_x = event.x_root - self.winfo_x()
        self._drag_start_y = event.y_root - self.winfo_y()

    def drag_window(self, event):
        x = event.x_root - self._drag_start_x
        y = event.y_root - self._drag_start_y
        self.geometry(f"+{x}+{y}")
        
        # 7. Тащим за собой окна 2 уровня
        for child_data in self._child_windows:
            win, offset_x, offset_y = child_data
            win.geometry(f"+{x + offset_x}+{y + offset_y}")

    def register_child_window(self, window, offset_x=0, offset_y=0):
        """Регистрирует дочернее окно 2 уровня."""
        self._child_windows.append((window, offset_x, offset_y))


class LevelTwoBehavior:
    """
    Поведение окон второго уровня.
    Применяется к объекту класса tk.Toplevel
    """
    def setup_level_two(self, parent_main_window):
        self.parent_main = parent_main_window
        
        # 1. Может увеличиваться, но не меньше интерфейса
        width, height = int(self.winfo_reqwidth()), int(self.winfo_reqheight())
        self.minsize(width, height)
        self.resizable(True, True) # Разрешаем менять размер вправо и вниз
        
        # 2. Центрирование относительно главного окна
        p_x = self.parent_main.winfo_x()
        p_y = self.parent_main.winfo_y()
        p_w = self.parent_main.winfo_width()
        p_h = self.parent_main.winfo_height()
        
        x = p_x + (p_w - width) // 2
        y = p_y + (p_h - height) // 2
        self.geometry(f"+{x}+{y}")

        # Сохраняем смещение для синхронного движения (требование №3)
        self._offset_x = x - p_x
        self._offset_y = y - p_y
        
        # Регистрация у родителя
        self.parent_main.register_child_window(self, self._offset_x, self._offset_y)

        # 4. Блокировка сворачивания через "-" (кнопка останется, но ничего не сделает)
        # Полностью убрать стандартные кнопки Min/Max в чистом Tkinter сложно, 
        # это требует вызова WinAPI через ctypes. Оставляем заглушку на событие.
        self.protocol("WM_MINIMIZE_WINDOW", self.do_nothing)
        
        # 5. Закрытие и разворачивание
        # Протокол закрытия (крестик)
        self.protocol("WM_DELETE_WINDOW", self.destroy)
        
        # Свой функционал для разворачивания (квадратик), если нужна анимация,
        # иначе оставляем стандартное поведение системы.
        
        # Перетаскивание (приклеено к родителю)
        self.bind("<ButtonPress-1>", self.save_offset)
        self.bind("<B1-Motion>", self.drag_with_parent)

    def do_nothing(self, event=None):
        pass # Заглушка для блокировки сворачивания

    def save_offset(self, event):
        self._win_click_x = event.x
        self._win_click_y = event.y

    def drag_with_parent(self, event):
        dx = event.x_root - self._win_click_x
        dy = event.y_root - self._win_click_y
        
        new_parent_x = dx - self._offset_x
        new_parent_y = dy - self._offset_y
        
        # Двигаем родителя
        self.parent_main.geometry(f"+{new_parent_x}+{new_parent_y}")
        # Двигаем себя (геометрия обновится также через регистрацию в родителе при его движении)
        self.geometry(f"+{dx}+{dy}")


class LevelThreeBehavior:
    """
    Поведение окон третьего уровня.
    Применяется к объекту класса tk.Toplevel
    """
    def setup_level_three(self):
        # 1. Минимальный размер
        width, height = int(self.winfo_reqwidth()), int(self.winfo_reqheight())
        self.minsize(width, height)
        self.resizable(True, True)
        
        # 2. Левый верхний угол независимо от других
        self.geometry("+0+0")

        # 4. Блокировка сворачивания
        self.protocol("WM_MINIMIZE_WINDOW", self.do_nothing)
        
        # 5. Закрытие
        self.protocol("WM_DELETE_WINDOW", self.destroy)
        
        # 3. Свободное передвижение
        self.bind("<ButtonPress-1>", self.save_offset)
        self.bind("<B1-Motion>", self.drag_free)

    def do_nothing(self, event=None):
        pass

    def save_offset(self, event):
        self._win_click_x = event.x
        self._win_click_y = event.y

    def drag_free(self, event):
        dx = event.x_root - self._win_click_x
        dy = event.y_root - self._win_click_y
        self.geometry(f"+{dx}+{dy}")