Source code for cgl.core.screen_grab

from PySide6 import QtWidgets, QtGui, QtCore
import os
from datetime import datetime


[docs] class ScreenCapture(QtWidgets.QDialog): """A dialog for capturing a selected area of the screen.""" def __init__(self, parent=None, path_object=None, output=None): super(ScreenCapture, self).__init__(parent) self.path_object = path_object self.click_position = None # Determine output path file_name = datetime.now().strftime("screen_grab_%Y-%m-%d_at_%H.%M.%S.png") self.output_path = self._determine_output_path(output, file_name) # Ensure output directory exists os.makedirs(os.path.dirname(self.output_path), exist_ok=True) # Initialize UI properties self.rectangle = QtCore.QRect() self._configure_window() self.set_screen_area() self._connect_screen_signals() def _determine_output_path(self, output, file_name): """Determines the output path for the screenshot.""" if output: return output if self.path_object: return self.path_object.get_preview_path(ext=".png") return os.path.expanduser(f"~/Desktop/{file_name}").replace("\\", "/") def _configure_window(self): """Configures the window properties.""" self.setWindowFlags( QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.Tool ) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.setCursor(QtCore.Qt.CrossCursor) self.setMouseTracking(True) def _connect_screen_signals(self): """Connects signals for screen changes to update geometry.""" screens = QtGui.QGuiApplication.screens() for screen in screens: screen.geometryChanged.connect(self.set_screen_area) primary_screen = QtGui.QGuiApplication.primaryScreen() if primary_screen: primary_screen.virtualGeometryChanged.connect(self.set_screen_area)
[docs] def set_screen_area(self): """Sets the screen area to the total desktop size.""" screens = QtGui.QGuiApplication.screens() total_desktop = QtCore.QRect() for screen in screens: total_desktop = total_desktop.united(screen.geometry()) self.setGeometry(total_desktop)
[docs] @classmethod def grab_window(cls): """Launches the ScreenCapture dialog.""" temp = ScreenCapture() temp.exec()
[docs] def get_rectangle(self): """Returns the selected rectangle area.""" return self.rectangle
[docs] def mousePressEvent(self, event): """Handles the mouse press event to start area selection.""" self.click_position = ( event.globalPosition().toPoint() ) # โœ… Fix deprecated method print(f"๐Ÿ–ฑ๏ธ Mouse pressed at: {self.click_position}")
[docs] def mouseReleaseEvent(self, event): """Handles the mouse release event to capture the selected area.""" self.rectangle = QtCore.QRect( self.click_position, event.globalPosition().toPoint() ).normalized() self.click_position = None if self.rectangle.width() == 0 or self.rectangle.height() == 0: print("โŒ Invalid selection! Capture area too small.") self.close() return print(f"๐Ÿ“ธ Selected region: {self.rectangle}") # ๐Ÿ›  Hide the overlay before capture self.hide() QtWidgets.QApplication.processEvents() # Ensure UI updates before capture # ๐Ÿ“ธ Capture the selected area pix = capture_area(self.rectangle, self.output_path) # ๐Ÿ›  Close the dialog after capturing self.close() return pix
[docs] def mouseMoveEvent(self, event): """Repaints while moving the mouse.""" self.repaint()
[docs] def paintEvent(self, event): """Custom paint event for drawing selection overlay.""" mouse_pos = self.mapFromGlobal(QtGui.QCursor.pos()) click_pos = ( self.mapFromGlobal(self.click_position) if self.click_position else None ) qp = QtGui.QPainter(self) qp.setBrush(QtGui.QColor(0, 0, 0, 150)) # Darken overlay qp.setPen(QtCore.Qt.NoPen) qp.drawRect(event.rect()) # Darken the whole screen if click_pos: self.rectangle = QtCore.QRect(click_pos, mouse_pos) qp.setCompositionMode(QtGui.QPainter.CompositionMode_Clear) qp.drawRect(self.rectangle) qp.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) # Draw cropping markers if click_pos: pen = QtGui.QPen(QtGui.QColor("white"), 3, QtCore.Qt.SolidLine) qp.setPen(pen) qp.drawLine( mouse_pos.x(), click_pos.y(), mouse_pos.x(), mouse_pos.y() ) # Left qp.drawLine( click_pos.x(), click_pos.y(), mouse_pos.x(), click_pos.y() ) # Top qp.drawLine( click_pos.x(), click_pos.y(), click_pos.x(), mouse_pos.y() ) # Right qp.drawLine( click_pos.x(), mouse_pos.y(), mouse_pos.x(), mouse_pos.y() ) # Bottom
[docs] def capture_area(rect, output_path): """ Captures the selected screen area, ensuring it works on multiple monitors. Args: rect (QRect): The selected area to capture. output_path (str): File path to save the captured image_plane. Returns: QPixmap: The captured screen area as a QPixmap. """ screens = QtGui.QGuiApplication.screens() # Identify the screen that contains the selection selected_screen = None for screen in screens: if screen.geometry().contains( rect.center() ): # Check if center of rect is in this screen selected_screen = screen break if not selected_screen: print("โŒ No screen detected for the selected area!") return None # Convert coordinates to screen-relative space screen_geometry = selected_screen.geometry() adjusted_rect = rect.translated( -screen_geometry.topLeft() ) # Adjust for screen offset # Ensure valid size if adjusted_rect.width() == 0 or adjusted_rect.height() == 0: print("โŒ Invalid capture area. Selection might be too small.") return None # Hide overlay before capture QtCore.QThread.msleep(100) QtWidgets.QApplication.processEvents() # Capture the adjusted area from the correct screen pixmap = selected_screen.grabWindow( 0, adjusted_rect.x(), adjusted_rect.y(), adjusted_rect.width(), adjusted_rect.height(), ) # Debugging Output print( f"๐Ÿ“ธ Capturing region: {rect}, Adjusted: {adjusted_rect}, from screen: {selected_screen.name()}, Output path: {output_path}" ) # Save the captured image_plane if not pixmap.isNull(): pixmap.save(output_path, "PNG") print(f"โœ… Screenshot saved at {output_path}") else: print("โŒ Failed to capture screenshot. Image is null.") return pixmap
[docs] def run(path_object=None, output="thumb", parent=None): """ Runs the screen capture tool. Args: path_object (object, optional): Path object. output (str, optional): Output file path. parent (QWidget, optional): Parent widget. Returns: str: The path to the saved screenshot. """ temp = ScreenCapture(parent=parent, path_object=path_object, output=output) temp.exec() print(f"๐Ÿ“ธ Screen capture saved to: {temp.output_path}") return temp.output_path
if __name__ == "__main__": app = QtWidgets.QApplication([]) ScreenCapture.grab_window()