Implemented Grabber Handles for easier usage.
This commit is contained in:
parent
7991aa751f
commit
415f1842cd
@ -32,7 +32,7 @@ pytesseract.pytesseract.tesseract_cmd = r"C:\\Program Files\\Tesseract-OCR\\tess
|
|||||||
|
|
||||||
DEFAULT_WIDTH = 180
|
DEFAULT_WIDTH = 180
|
||||||
DEFAULT_HEIGHT = 130
|
DEFAULT_HEIGHT = 130
|
||||||
HANDLE_SIZE = 8
|
HANDLE_SIZE = 5
|
||||||
LABEL_HEIGHT = 20
|
LABEL_HEIGHT = 20
|
||||||
|
|
||||||
collector_mutex = QMutex()
|
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}")
|
print(f"[ERROR] Failed to capture OCR region: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def draw_identification_boxes(region_id, positions, color=(0, 0, 255), thickness=2):
|
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.
|
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.previous_positions = [] # This prevents redundant redraws
|
||||||
self.num_slices = 1 # Ensures slice count is initialized
|
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()
|
self.show()
|
||||||
|
|
||||||
def paintEvent(self, event):
|
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
|
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)
|
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):
|
def set_draw_positions(self, positions, color, thickness):
|
||||||
"""
|
"""
|
||||||
@ -292,57 +300,99 @@ class OCRRegionWidget(QWidget):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def _resize_handles(self):
|
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()
|
w, h = self.width(), self.height()
|
||||||
return [
|
handles = [
|
||||||
QRect(0, 0, HANDLE_SIZE, HANDLE_SIZE), # Top-left
|
QRect(0, 0, HANDLE_SIZE, HANDLE_SIZE), # Top-left
|
||||||
QRect(w - HANDLE_SIZE, h - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE) # Bottom-right
|
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):
|
def mousePressEvent(self, event):
|
||||||
if event.button() == Qt.LeftButton:
|
if event.button() == Qt.LeftButton:
|
||||||
|
# Check if any handle (including the new top-center) is clicked
|
||||||
for i, handle in enumerate(self._resize_handles()):
|
for i, handle in enumerate(self._resize_handles()):
|
||||||
if handle.contains(event.pos()):
|
if handle.contains(event.pos()):
|
||||||
self.selected_handle = i
|
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
|
return
|
||||||
|
# If no handle is clicked, allow dragging by clicking anywhere in the widget
|
||||||
self.drag_offset = event.pos()
|
self.drag_offset = event.pos()
|
||||||
|
|
||||||
def mouseMoveEvent(self, event):
|
def mouseMoveEvent(self, event):
|
||||||
if self.selected_handle is not None:
|
if self.selected_handle is not None:
|
||||||
w, h = self.width(), self.height()
|
if self.selected_handle == 4:
|
||||||
if self.selected_handle == 0: # Top-left
|
# --- Top-center handle dragging ---
|
||||||
new_w = w + (self.x() - event.globalX())
|
new_x = event.globalX() - self.drag_offset.x()
|
||||||
new_h = h + (self.y() - event.globalY())
|
new_y = event.globalY() - self.drag_offset.y()
|
||||||
new_x = event.globalX()
|
self.move(new_x, new_y)
|
||||||
new_y = event.globalY()
|
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:
|
if new_w < 20:
|
||||||
new_w = 20
|
new_w = 20
|
||||||
if new_h < 20:
|
if new_h < 20:
|
||||||
new_h = 20
|
new_h = 20
|
||||||
|
|
||||||
self.setGeometry(new_x, new_y, new_w, new_h)
|
self.setGeometry(new_x, new_y, new_w, new_h)
|
||||||
elif self.selected_handle == 1: # Bottom-right
|
collector_mutex.lock()
|
||||||
new_w = event.globalX() - self.x()
|
if self.region_id in regions:
|
||||||
new_h = event.globalY() - self.y()
|
regions[self.region_id]["bbox"] = [self.x(), self.y(), self.width(), self.height()]
|
||||||
if new_w < 20:
|
collector_mutex.unlock()
|
||||||
new_w = 20
|
self.update()
|
||||||
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()
|
|
||||||
elif self.drag_offset:
|
elif self.drag_offset:
|
||||||
|
# --- General widget dragging (if no handle was clicked) ---
|
||||||
new_x = event.globalX() - self.drag_offset.x()
|
new_x = event.globalX() - self.drag_offset.x()
|
||||||
new_y = event.globalY() - self.drag_offset.y()
|
new_y = event.globalY() - self.drag_offset.y()
|
||||||
self.move(new_x, new_y)
|
self.move(new_x, new_y)
|
||||||
|
|
||||||
collector_mutex.lock()
|
collector_mutex.lock()
|
||||||
if self.region_id in regions:
|
if self.region_id in regions:
|
||||||
regions[self.region_id]["bbox"] = [new_x, new_y, self.width(), self.height()]
|
regions[self.region_id]["bbox"] = [new_x, new_y, self.width(), self.height()]
|
||||||
collector_mutex.unlock()
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user