Untitled

 avatar
unknown
plain_text
16 days ago
9.5 kB
3
Indexable
class ManualRotationScreen(ctk.CTkToplevel):
    def __init__(self, parent, dark_mode: bool, language='EN', settings_menu=None):
        super().__init__(parent)
        self.language = getattr(parent, 'LANGUAGE', 'EN')
        self.settings_menu = settings_menu
        self.parent = parent
        self._destroyed = False
        self._closing = False
        
        # Store references to prevent garbage collection
        self._stored_widgets = []
        
        # Use CircularProgress's class-level turntable reference
        self.turntable = CircularProgress._turntable
        self.bin_config = BinConfiguration()
        
        # Make it fullscreen and disable all close operations
        self.overrideredirect(True)
        self.geometry(f"{self.winfo_screenwidth()}x{self.winfo_screenheight()}+0+0")
        self.protocol("WM_DELETE_WINDOW", lambda: None)
        self.attributes('-topmost', True)

        # Create full-screen transparent event blocker
        self.event_blocker = ctk.CTkFrame(
            self,
            fg_color="transparent",
            corner_radius=0
        )
        self.event_blocker.place(relx=0, rely=0, relwidth=1, relheight=1)
        self._stored_widgets.append(self.event_blocker)
        
        # Set colors to match main screen
        self.dark_mode = dark_mode
        self.bg_color = '#1c1c1e' if dark_mode else '#f5f5f7'
        self.configure(fg_color=self.bg_color)

        # Create content frame on top of event blocker
        self.content_frame = ctk.CTkFrame(
            self.event_blocker,
            fg_color=self.bg_color,
            corner_radius=0
        )
        self.content_frame.place(relx=0, rely=0, relwidth=1, relheight=1)
        self._stored_widgets.append(self.content_frame)

        # Create title label
        self.title_label = ctk.CTkLabel(
            self.content_frame,
            text=TRANSLATIONS[self.language].get('manual_rotation', 'Manual Rotation'),
            font=("Dongle", 40, "bold"),
            text_color='white' if dark_mode else 'black'
        )
        self.title_label.place(relx=0.5, rely=0.18, anchor='center')
        self._stored_widgets.append(self.title_label)
        
        # Create circles frame with transparent background
        self.circles_frame = ctk.CTkFrame(self.content_frame, fg_color="transparent")
        self.circles_frame.place(relx=0.5, rely=0.5, anchor='center')
        self._stored_widgets.append(self.circles_frame)
        
        # Add padding frame with transparent background
        self.padding_frame = ctk.CTkFrame(self.circles_frame, fg_color="transparent")
        self.padding_frame.pack(padx=50)
        self._stored_widgets.append(self.padding_frame)
        
        # Create list to track circle widgets
        self.circle_widgets = []

        # Create Grey Circles for each bin
        for bin_config in self.bin_config.bins:
            container = ctk.CTkFrame(self.padding_frame, fg_color="transparent")
            container.pack(side='left', padx=15)
            self._stored_widgets.append(container)

            progress = RotationCircleButton(
                container,
                bin_id=bin_config['id'],
                size=220,
                language=language,
                dark_mode=dark_mode,
                command=lambda bid=bin_config['id']: self._rotate_to_position(bid)
            )
            progress.pack()
            progress.set_dark_mode(dark_mode)
            self.circle_widgets.append(progress)
            self._stored_widgets.append(progress)

        # Add close button
        self.close_button = ctk.CTkLabel(
            self.content_frame,
            text="×",  
            width=40,
            height=40,
            text_color='white' if dark_mode else 'black',
            font=("Arial", 32, "bold"),
            cursor="hand2"
        )
        self.close_button.place(relx=0.995, rely=0.01, anchor="ne")
        self.close_button.bind("<Button-1>", self.handle_close)
        self.close_button.bind("<Enter>", lambda e: self.close_button.configure(text_color="#ff3b30"))
        self.close_button.bind("<Leave>", lambda e: self.close_button.configure(
            text_color='white' if dark_mode else 'black'
        ))
        self._stored_widgets.append(self.close_button)

        # Bind click events to the event blocker instead of the window
        self.event_blocker.bind('<Button-1>', self.check_click_location)
        
        # Bind destruction event
        self.bind("<Destroy>", self.on_destroy)

    def handle_close(self, event=None):
        """Handle closing the screen safely"""
        if self._closing or self._destroyed:
            return
            
        self._closing = True
        
        try:
            # Disable all circle widgets first
            for circle in self.circle_widgets:
                circle.configure(state="disabled")
            
            # Close settings menu if open
            if self.settings_menu and self.settings_menu.is_open:
                self.settings_menu.close_menu()
            
            # Apply temporary cooldown to all instances
            for instance in CircularProgress._instances:
                if not instance._destroyed:
                    instance.can_press = False
                    instance.is_pressed = False
                    instance.press_animation_active = False
            
            # Schedule re-enabling after delay
            self.after(1500, self._re_enable_instances)
            
            # Schedule actual destruction
            self.after(100, self.destroy)
            
        except Exception as e:
            print(f"Error during close: {e}")
            self.destroy()

    def _re_enable_instances(self):
        """Re-enable CircularProgress instances"""
        try:
            for instance in CircularProgress._instances:
                if not instance._destroyed:
                    instance.can_press = True
        except Exception as e:
            print(f"Error re-enabling instances: {e}")

    def check_click_location(self, event):
        """Safely check click location and handle closing"""
        if self._closing or self._destroyed:
            return
            
        # Get the click location relative to the window
        click_x = event.x_root - self.winfo_rootx()
        click_y = event.y_root - self.winfo_rooty()
        
        # Check if click is inside any circle
        for circle in self.circle_widgets:
            circle_x = circle.winfo_x() + circle.winfo_width() / 2
            circle_y = circle.winfo_y() + circle.winfo_height() / 2
            radius = circle.size / 2
            
            distance = ((click_x - circle_x) ** 2 + (click_y - circle_y) ** 2) ** 0.5
            
            if distance <= radius:
                return  # Click was inside a circle
        
        # If we get here, click was outside circles
        self.handle_close()

    def on_destroy(self, event):
        """Handle destruction of the window"""
        if event.widget is self and not self._destroyed:
            self._destroyed = True
            
            # Clear stored widget references
            self._stored_widgets.clear()
            
            # Ensure cleanup happens
            self._re_enable_instances()

    def _rotate_to_position(self, bin_id):
        """Rotate to the specified bin position"""
        if self._closing or self._destroyed:
            return
            
        print(f"Starting rotation to {bin_id}")
        
        if not self.turntable or not self.turntable.is_initialized:
            print("No turntable controller available or not initialized")
            MessageDialog(
                self.winfo_toplevel(),
                TRANSLATIONS[self.language].get('please_wait', 'Please wait'),
                self.dark_mode
            )
            return
            
        # Create loading screen
        loading_screen = LoadingScreen(
            self.winfo_toplevel(),
            message=TRANSLATIONS[self.language].get('moving_bin', 'Moving to position...'),
            dark_mode=self.dark_mode,
            language=self.language
        )
        
        def perform_rotation():
            try:
                if self._destroyed:
                    return
                    
                # Calculate target position
                target_position = self.turntable.positions[bin_id]
                
                # Calculate and execute movement
                steps, direction = self.turntable._calculate_steps(target_position)
                self.turntable._move_motor(steps, direction)
                self.turntable.current_position = target_position
                
                # Close loading screen after movement completes
                if not self._destroyed:
                    self.after(500, loading_screen.close)
                
            except Exception as e:
                print(f"Error during rotation: {e}")
                if not self._destroyed:
                    self.after(0, loading_screen.close)
        
        # Run rotation in separate thread
        thread = threading.Thread(target=perform_rotation, daemon=True)
        thread.start()
Leave a Comment