-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathregion_selector_widget.py
More file actions
127 lines (98 loc) · 5.32 KB
/
region_selector_widget.py
File metadata and controls
127 lines (98 loc) · 5.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import sys
from PySide6.QtWidgets import QWidget, QApplication, QRubberBand
from PySide6.QtCore import Qt, QRect, QPoint, Signal
from PySide6.QtGui import QScreen, QKeyEvent
class RegionSelectorWidget(QWidget):
region_selected = Signal(QRect) # Emits the selected rectangle in screen coordinates
selection_cancelled = Signal()
def __init__(self, screen: QScreen):
super().__init__()
self.screen_geometry = screen.geometry()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
# Make the widget transparent to see the screen underneath
self.setAttribute(Qt.WA_TranslucentBackground)
# Ensure mouse tracking is enabled even without a button press for cursor changes
self.setMouseTracking(True)
self.setGeometry(self.screen_geometry) # Cover the entire specified screen
self.rubber_band = None
self.origin = QPoint()
self.is_selecting = False
# A semi-transparent overlay to dim the screen slightly
self.overlay_color = Qt.black
self.overlay_opacity = 0.3 # 30% opacity
def paintEvent(self, event):
super().paintEvent(event)
painter = QApplication.instance().getPainter(self) # Get painter from QWidget
if painter:
painter.setOpacity(self.overlay_opacity)
painter.fillRect(self.rect(), self.overlay_color)
painter.end()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.is_selecting = True
self.origin = event.globalPosition().toPoint() # Use global position
if not self.rubber_band:
self.rubber_band = QRubberBand(QRubberBand.Rectangle, self)
# Adjust origin to be relative to this widget's geometry if it's fullscreen
# Since we are using globalPosition for origin and later for current_pos,
# the rect formed by them will be in global screen coordinates.
self.rubber_band.setGeometry(QRect(self.origin, QPoint())) # Start with zero size
self.rubber_band.show()
def mouseMoveEvent(self, event):
if self.is_selecting:
current_pos_global = event.globalPosition().toPoint()
selection_rect = QRect(self.origin, current_pos_global).normalized()
# The rubber_band's geometry is relative to its parent (this widget).
# So, map the global selection_rect to this widget's coordinates.
mapped_origin = self.mapFromGlobal(selection_rect.topLeft())
mapped_bottom_right = self.mapFromGlobal(selection_rect.bottomRight())
widget_relative_rect = QRect(mapped_origin, mapped_bottom_right).normalized()
self.rubber_band.setGeometry(widget_relative_rect)
# Change cursor to crosshair when over the selection widget
self.setCursor(Qt.CrossCursor)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.is_selecting:
self.is_selecting = False
current_pos_global = event.globalPosition().toPoint()
final_rect_global = QRect(self.origin, current_pos_global).normalized()
if self.rubber_band:
self.rubber_band.hide()
if final_rect_global.width() > 5 and final_rect_global.height() > 5: # Minimum selection size
print(f"[RegionSelector] Selected global rect: {final_rect_global}")
self.region_selected.emit(final_rect_global)
else:
self.selection_cancelled.emit() # Or just close without emitting if too small
self.close() # Close the selector widget
def keyPressEvent(self, event: QKeyEvent):
if event.key() == Qt.Key_Escape:
print("[RegionSelector] Selection cancelled by Escape key.")
if self.rubber_band:
self.rubber_band.hide()
self.is_selecting = False
self.selection_cancelled.emit()
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
primary_screen = QApplication.primaryScreen()
if not primary_screen:
print("Error: No primary screen found.")
sys.exit(-1)
selector = RegionSelectorWidget(primary_screen)
def on_region_selected(rect):
print(f"Signal 'region_selected' caught with QRect: {rect}")
# In a real app, capture this part of the screen.
# For test, just show a small window there.
feedback_widget = QLabel(f"Selected:\n{rect.x()},{rect.y()}\n{rect.width()}x{rect.height()}")
feedback_widget.setGeometry(rect) # This won't work if selector is closed.
feedback_widget.setStyleSheet("background-color: rgba(0,255,0,0.5); color: black; border: 1px solid black;")
# feedback_widget.show() # This would need to be managed differently.
# For test, just print. The main app will handle the rect.
QTimer.singleShot(100, app.quit) # Quit after selection for test
def on_selection_cancelled():
print("Signal 'selection_cancelled' caught.")
QTimer.singleShot(100, app.quit)
selector.region_selected.connect(on_region_selected)
selector.selection_cancelled.connect(on_selection_cancelled)
print("Showing RegionSelectorWidget. Drag to select a region. Press Esc to cancel.")
selector.show()
sys.exit(app.exec())