Окно регистрации перекрывает видео
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
Источник: Stack Overflow на русском