Окно регистрации перекрывает видео

Рейтинг: -4Ответов: 0Опубликовано: 10.08.2025
import customtkinter as ctk
from PIL import Image, ImageTk
import cv2
import numpy as np
import logging # Added for robust error handling

# Configure logging for better error visibility
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')

class VideoBackgroundApp(ctk.CTk):
    def __init__(self):
        super().__init__()

        # Window settings
        self.geometry("500x515")
        self.title("Авторизация с видеофоном")
        self.resizable(False, False)

        # Create main frame - still packed to fill the root window.
        # This frame acts as the common parent for both the video canvas and the UI content frame.
        self.main_frame = ctk.CTkFrame(self, fg_color="#121212")
        self.main_frame.pack(fill="both", expand=True)

        # Create canvas for video background.
        # Crucially, use 'place' to position it to cover the entire main_frame.
        self.canvas = ctk.CTkCanvas(self.main_frame, width=500, height=515, highlightthickness=0, bg="#121212")
        # Place the canvas to fill 100% of its parent's width and height, starting from top-left.
        self.canvas.place(x=0, y=0, relwidth=1, relheight=1)

        # Container for UI elements.
        # Also use 'place' to position it, allowing it to overlap the canvas.
        self.content_frame = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        # Maintain original centering relative to its parent.
        self.content_frame.place(relx=0.5, rely=0.5, anchor="center")

        # Explicitly lift content_frame to be on top of the canvas.
        # This is the crucial step for resolving the overlay bug and ensuring UI visibility.
        self.content_frame.lift() # [4, 5, 6, 7]

        # Load video
        self.video_path = "background.mp4"
        self.setup_video()

        # Create interface
        self.create_interface()

    def setup_video(self):
        """Initializes video capture and starts the video frame update loop."""
        try:
            self.cap = cv2.VideoCapture(self.video_path)
            if not self.cap.isOpened():
                # Raise an explicit error if video source cannot be opened
                raise IOError(f"Cannot open video source: {self.video_path}")
            self.update_video_frame() # Start the frame update loop
        except Exception as e:
            # Log the error for debugging purposes
            logging.error(f"Error loading video: {e}")
            # Provide user feedback: display an error message directly on the main frame
            ctk.CTkLabel(self.main_frame, text=f"Ошибка загрузки видео: {e}\nВидеофон недоступен.",
                         text_color="red", font=("Arial", 16, "bold")).place(relx=0.5, rely=0.5, anchor="center")

    def update_video_frame(self):
        """
        Reads a frame from the video, processes it, updates the canvas,
        and ensures the content frame remains on top.
        """
        # Check if video capture object is valid and open before proceeding
        if not hasattr(self, 'cap') or not self.cap.isOpened():
            logging.warning("Video capture not open or failed to initialize. Stopping update loop.")
            return

        try:
            ret, frame = self.cap.read()
            if not ret:
                # If frame not read, assume end of video and loop back to start
                self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
                ret, frame = self.cap.read()
                if not ret: # If still no frame after reset, a persistent issue exists
                    logging.error("Failed to read frame even after video loop reset. Stopping video updates.")
                    # Release capture to prevent further errors
                    self.cap.release()
                    return

            # Dynamically get current canvas dimensions for accurate resizing
            # This ensures the video always fits the canvas area, even if the window is resized (if resizable were true)
            canvas_width = self.canvas.winfo_width()
            canvas_height = self.canvas.winfo_height()

            # Fallback for initial rendering where winfo_width/height might be 0
            if canvas_width == 0 or canvas_height == 0:
                canvas_width = 500 # Use initial window size as a safe default
                canvas_height = 515
                logging.debug("Canvas dimensions not yet available, using fallback size.")

            # Resize frame to canvas dimensions and convert color format
            frame = cv2.resize(frame, (canvas_width, canvas_height))
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.photo = ImageTk.PhotoImage(image=Image.fromarray(frame))

            # Clear previous image on canvas and draw the new frame
            self.canvas.delete("all")
            self.canvas.create_image(0, 0, image=self.photo, anchor="nw")

            # Re-lift the content frame after canvas update.
            # This ensures consistent layering, especially after rapid canvas redraws,
            # acting as a defensive measure against potential Z-order regressions. [4, 5]
            self.content_frame.lift()

            # Schedule the next frame update to maintain desired FPS (approx. 30 FPS)
            self.after(33, self.update_video_frame)
        except Exception as e:
            # Log any exceptions during frame processing
            logging.error(f"Error updating video frame: {e}. Stopping video updates.")
            # Ensure video capture is released on error to free resources
            if hasattr(self, 'cap') and self.cap.isOpened():
                self.cap.release()

    def create_interface(self):
        """Constructs the interactive user interface elements."""
        # Logo section
        logo_image = self.load_image("pix.jpg", (150, 150))
        logo_frame = ctk.CTkFrame(self.content_frame, fg_color="transparent")
        logo_frame.pack(pady=(14, 20))

        if logo_image:
            ctk.CTkLabel(logo_frame, image=logo_image, text="").pack()
        else:
            # Fallback text if logo image cannot be loaded
            ctk.CTkLabel(logo_frame, text="PIX", font=("Arial", 24, "bold"), text_color="white").pack()

        # Input fields for login credentials
        self.create_input_field("Логин или email", "👤", False)
        self.create_input_field("Пароль", "🔒", True)

        # Login button
        ctk.CTkButton(
            self.content_frame,
            text="Войти",
            width=150,
            height=42,
            corner_radius=20,
            font=("Arial", 14, "bold"),
            fg_color="#333333",
            hover_color="#444444",
            text_color="white"
        ).pack(pady=20)

    def create_input_field(self, placeholder, icon, is_password):
        """Helper method to create a standardized input field with an icon."""
        frame = ctk.CTkFrame(
            self.content_frame,
            fg_color="#1a1a1a",  # Opaque dark background for input fields
            corner_radius=14,
            width=380,
            height=46
        )
        frame.pack(pady=15)

        # Icon for the input field
        ctk.CTkLabel(
            frame,
            text=icon,
            font=("Arial", 16),
            width=30,
            text_color="white"
        ).pack(side="left", padx=(15, 10))

        # Entry widget for user input
        ctk.CTkEntry(
            frame,
            width=300,
            height=38,
            border_width=0,
            fg_color="#1a1a1a",
            placeholder_text=placeholder,
            placeholder_text_color="#b3b3b3",
            text_color="white",
            font=("Arial", 14),
            show="*" if is_password else None # Hide text for password fields
        ).pack(side="left", fill="x", expand=True, padx=(0, 15))

    def load_image(self, path, size):
        """Loads an image for use in CustomTkinter, with error handling."""
        try:
            return ctk.CTkImage(Image.open(path), size=size)
        except Exception as e:
            # Log a warning if image loading fails, but allow the application to continue
            logging.warning(f"Could not load image '{path}': {e}")
            return None

    def __del__(self):
        """Ensures proper release of video capture resources when the object is destroyed."""
        if hasattr(self, 'cap') and self.cap.isOpened():
            self.cap.release()
            logging.info("Video capture released during object destruction.")


if __name__ == "__main__":
    ctk.set_appearance_mode("dark") # Set the overall appearance mode for CustomTkinter
    app = VideoBackgroundApp()
    app.mainloop() # Start the CustomTkinter event loop

Ответы

Ответов пока нет.