Implemented Grabber Handles for easier usage.
This commit is contained in:
		| @@ -32,7 +32,7 @@ pytesseract.pytesseract.tesseract_cmd = r"C:\\Program Files\\Tesseract-OCR\\tess | ||||
|  | ||||
| DEFAULT_WIDTH = 180 | ||||
| DEFAULT_HEIGHT = 130 | ||||
| HANDLE_SIZE = 8 | ||||
| HANDLE_SIZE = 5 | ||||
| LABEL_HEIGHT = 20 | ||||
|  | ||||
| collector_mutex = QMutex() | ||||
| @@ -196,9 +196,6 @@ def find_word_positions(region_id, word, offset_x=0, offset_y=0, margin=5, ocr_e | ||||
|         print(f"[ERROR] Failed to capture OCR region: {e}") | ||||
|         return [] | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| def draw_identification_boxes(region_id, positions, color=(0, 0, 255), thickness=2): | ||||
|     """ | ||||
|     Draws non-interactive rectangles at specified positions within the given OCR region. | ||||
| @@ -236,6 +233,10 @@ class OCRRegionWidget(QWidget): | ||||
|         self.previous_positions = []  # This prevents redundant redraws | ||||
|         self.num_slices = 1  # Ensures slice count is initialized | ||||
|  | ||||
|         # --- Initialization for interactive handles --- | ||||
|         self.selected_handle = None  # Tracks which handle is being dragged/resized | ||||
|         self.drag_offset = None      # Tracks the offset for moving the widget | ||||
|  | ||||
|         self.show() | ||||
|  | ||||
|     def paintEvent(self, event): | ||||
| @@ -260,6 +261,13 @@ class OCRRegionWidget(QWidget): | ||||
|  | ||||
|             for i in range(1, self.num_slices):  # Do not draw the last one at the bottom | ||||
|                 painter.drawLine(0, i * strip_height, self.width(), i * strip_height) | ||||
|          | ||||
|         # --- Draw interactive handles (grabbers) with reduced opacity (15%) --- | ||||
|         # 15% opacity of 255 is approximately 38 | ||||
|         handle_color = QColor(0, 0, 0, 50)   | ||||
|         for handle in self._resize_handles(): | ||||
|             painter.fillRect(handle, handle_color) | ||||
|             painter.drawRect(handle)  # Optional: draw a border around the handle | ||||
|  | ||||
|     def set_draw_positions(self, positions, color, thickness): | ||||
|         """ | ||||
| @@ -292,57 +300,99 @@ class OCRRegionWidget(QWidget): | ||||
|         self.update() | ||||
|  | ||||
|     def _resize_handles(self): | ||||
|         """ | ||||
|         Returns a list of QRect objects representing the interactive handles: | ||||
|           - Index 0: Top-left (resize) | ||||
|           - Index 1: Top-right (resize) | ||||
|           - Index 2: Bottom-left (resize) | ||||
|           - Index 3: Bottom-right (resize) | ||||
|           - Index 4: Top-center (dragger) | ||||
|         """ | ||||
|         w, h = self.width(), self.height() | ||||
|         return [ | ||||
|             QRect(0, 0, HANDLE_SIZE, HANDLE_SIZE),  # Top-left | ||||
|             QRect(w - HANDLE_SIZE, h - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE)  # Bottom-right | ||||
|         handles = [ | ||||
|             QRect(0, 0, HANDLE_SIZE, HANDLE_SIZE),                           # Top-left | ||||
|             QRect(w - HANDLE_SIZE, 0, HANDLE_SIZE, HANDLE_SIZE),                # Top-right | ||||
|             QRect(0, h - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE),                # Bottom-left | ||||
|             QRect(w - HANDLE_SIZE, h - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE)     # Bottom-right | ||||
|         ] | ||||
|         # Top-center handle: centered along the top edge | ||||
|         top_center_x = (w - HANDLE_SIZE) // 2 | ||||
|         top_center = QRect(top_center_x, 0, HANDLE_SIZE, HANDLE_SIZE) | ||||
|         handles.append(top_center) | ||||
|         return handles | ||||
|  | ||||
|     def mousePressEvent(self, event): | ||||
|         if event.button() == Qt.LeftButton: | ||||
|             # Check if any handle (including the new top-center) is clicked | ||||
|             for i, handle in enumerate(self._resize_handles()): | ||||
|                 if handle.contains(event.pos()): | ||||
|                     self.selected_handle = i | ||||
|                     # For the top-center handle (index 4), initialize drag offset for moving | ||||
|                     if i == 4: | ||||
|                         self.drag_offset = event.pos() | ||||
|                     return | ||||
|  | ||||
|             # If no handle is clicked, allow dragging by clicking anywhere in the widget | ||||
|             self.drag_offset = event.pos() | ||||
|  | ||||
|     def mouseMoveEvent(self, event): | ||||
|         if self.selected_handle is not None: | ||||
|             w, h = self.width(), self.height() | ||||
|             if self.selected_handle == 0:  # Top-left | ||||
|                 new_w = w + (self.x() - event.globalX()) | ||||
|                 new_h = h + (self.y() - event.globalY()) | ||||
|                 new_x = event.globalX() | ||||
|                 new_y = event.globalY() | ||||
|             if self.selected_handle == 4: | ||||
|                 # --- Top-center handle dragging --- | ||||
|                 new_x = event.globalX() - self.drag_offset.x() | ||||
|                 new_y = event.globalY() - self.drag_offset.y() | ||||
|                 self.move(new_x, new_y) | ||||
|                 collector_mutex.lock() | ||||
|                 if self.region_id in regions: | ||||
|                     regions[self.region_id]["bbox"] = [new_x, new_y, self.width(), self.height()] | ||||
|                 collector_mutex.unlock() | ||||
|                 self.update() | ||||
|             else: | ||||
|                 # --- Resizing logic for corner handles --- | ||||
|                 if self.selected_handle == 0:  # Top-left | ||||
|                     new_w = self.width() + (self.x() - event.globalX()) | ||||
|                     new_h = self.height() + (self.y() - event.globalY()) | ||||
|                     new_x = event.globalX() | ||||
|                     new_y = event.globalY() | ||||
|                 elif self.selected_handle == 1:  # Top-right | ||||
|                     new_w = event.globalX() - self.x() | ||||
|                     new_h = self.height() + (self.y() - event.globalY()) | ||||
|                     new_x = self.x() | ||||
|                     new_y = event.globalY() | ||||
|                 elif self.selected_handle == 2:  # Bottom-left | ||||
|                     new_w = self.width() + (self.x() - event.globalX()) | ||||
|                     new_h = event.globalY() - self.y() | ||||
|                     new_x = event.globalX() | ||||
|                     new_y = self.y() | ||||
|                 elif self.selected_handle == 3:  # Bottom-right | ||||
|                     new_w = event.globalX() - self.x() | ||||
|                     new_h = event.globalY() - self.y() | ||||
|                     new_x = self.x() | ||||
|                     new_y = self.y() | ||||
|  | ||||
|                 if new_w < 20: | ||||
|                     new_w = 20 | ||||
|                 if new_h < 20: | ||||
|                     new_h = 20 | ||||
|  | ||||
|                 self.setGeometry(new_x, new_y, new_w, new_h) | ||||
|             elif self.selected_handle == 1:  # Bottom-right | ||||
|                 new_w = event.globalX() - self.x() | ||||
|                 new_h = event.globalY() - self.y() | ||||
|                 if new_w < 20: | ||||
|                     new_w = 20 | ||||
|                 if new_h < 20: | ||||
|                     new_h = 20 | ||||
|                 self.setGeometry(self.x(), self.y(), new_w, new_h) | ||||
|  | ||||
|             collector_mutex.lock() | ||||
|             if self.region_id in regions: | ||||
|                 regions[self.region_id]["bbox"] = [self.x(), self.y(), self.width(), self.height()] | ||||
|             collector_mutex.unlock() | ||||
|  | ||||
|             self.update() | ||||
|                 collector_mutex.lock() | ||||
|                 if self.region_id in regions: | ||||
|                     regions[self.region_id]["bbox"] = [self.x(), self.y(), self.width(), self.height()] | ||||
|                 collector_mutex.unlock() | ||||
|                 self.update() | ||||
|         elif self.drag_offset: | ||||
|             # --- General widget dragging (if no handle was clicked) --- | ||||
|             new_x = event.globalX() - self.drag_offset.x() | ||||
|             new_y = event.globalY() - self.drag_offset.y() | ||||
|             self.move(new_x, new_y) | ||||
|  | ||||
|             collector_mutex.lock() | ||||
|             if self.region_id in regions: | ||||
|                 regions[self.region_id]["bbox"] = [new_x, new_y, self.width(), self.height()] | ||||
|             collector_mutex.unlock() | ||||
|  | ||||
|  | ||||
|     def mouseReleaseEvent(self, event): | ||||
|         """ | ||||
|         Resets the drag/resize state once the mouse button is released. | ||||
|         """ | ||||
|         self.selected_handle = None | ||||
|         self.drag_offset = None | ||||
|   | ||||
		Reference in New Issue
	
	Block a user