Add borealis_overlay.py
This commit is contained in:
commit
6625d29a0e
203
borealis_overlay.py
Normal file
203
borealis_overlay.py
Normal file
@ -0,0 +1,203 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QCheckBox, QPushButton
|
||||
from PyQt5.QtCore import Qt, QRect, QPoint
|
||||
from PyQt5.QtGui import QPainter, QPen, QColor, QFont
|
||||
from ctypes import windll
|
||||
|
||||
HANDLE_SIZE = 10 # Size of the resize handle squares
|
||||
LABEL_HEIGHT = 20 # Height of the label area above the rectangle
|
||||
|
||||
|
||||
class Region:
|
||||
def __init__(self, x, y, w, h, label="Region", color=QColor(0, 0, 255)):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.label = label
|
||||
self.color = color
|
||||
|
||||
def rect(self):
|
||||
return QRect(self.x, self.y, self.w, self.h)
|
||||
|
||||
def label_rect(self):
|
||||
"""The rectangle representing the label area."""
|
||||
return QRect(self.x, self.y - LABEL_HEIGHT, self.w, LABEL_HEIGHT)
|
||||
|
||||
def resize_handles(self):
|
||||
"""Calculate the positions of the resize handles."""
|
||||
return [
|
||||
QRect(self.x - HANDLE_SIZE // 2, self.y - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Top-left
|
||||
QRect(self.x + self.w - HANDLE_SIZE // 2, self.y - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Top-right
|
||||
QRect(self.x - HANDLE_SIZE // 2, self.y + self.h - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Bottom-left
|
||||
QRect(self.x + self.w - HANDLE_SIZE // 2, self.y + self.h - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Bottom-right
|
||||
]
|
||||
|
||||
|
||||
class OverlayCanvas(QWidget):
|
||||
"""
|
||||
A canvas to draw, resize, and interact with rectangular regions.
|
||||
"""
|
||||
def __init__(self, regions, parent=None):
|
||||
super().__init__(parent)
|
||||
self.regions = regions
|
||||
self.edit_mode = False
|
||||
self.selected_region = None
|
||||
self.selected_handle = None # Track which handle is being dragged
|
||||
self.drag_offset = QPoint()
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
for region in self.regions:
|
||||
# Draw the rectangle
|
||||
pen = QPen(region.color)
|
||||
pen.setWidth(3)
|
||||
painter.setPen(pen)
|
||||
painter.drawRect(region.x, region.y, region.w, region.h)
|
||||
|
||||
# Draw the label above the rectangle
|
||||
painter.setFont(QFont("Arial", 12, QFont.Bold))
|
||||
painter.setPen(region.color)
|
||||
painter.drawText(region.x + 5, region.y - 5, region.label)
|
||||
|
||||
# Draw resize handles if in edit mode
|
||||
if self.edit_mode:
|
||||
for handle in region.resize_handles():
|
||||
painter.fillRect(handle, region.color)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if not self.edit_mode:
|
||||
return # Ignore clicks if not in edit mode
|
||||
|
||||
if event.button() == Qt.LeftButton:
|
||||
for region in reversed(self.regions): # Check regions from topmost to bottommost
|
||||
# Check if a resize handle is clicked
|
||||
for i, handle in enumerate(region.resize_handles()):
|
||||
if handle.contains(event.pos()):
|
||||
self.selected_region = region
|
||||
self.selected_handle = i
|
||||
return
|
||||
|
||||
# Check if the label area is clicked (for dragging)
|
||||
if region.label_rect().contains(event.pos()):
|
||||
self.selected_region = region
|
||||
self.selected_handle = None # No resize handle, just dragging the rectangle
|
||||
self.drag_offset = event.pos() - QPoint(region.x, region.y)
|
||||
return
|
||||
|
||||
# Check if the main rectangle is clicked (fallback for dragging)
|
||||
if region.rect().contains(event.pos()):
|
||||
self.selected_region = region
|
||||
self.selected_handle = None
|
||||
self.drag_offset = event.pos() - QPoint(region.x, region.y)
|
||||
return
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if not self.edit_mode or self.selected_region is None:
|
||||
return
|
||||
|
||||
if self.selected_handle is None:
|
||||
# Dragging the entire rectangle
|
||||
self.selected_region.x = event.x() - self.drag_offset.x()
|
||||
self.selected_region.y = event.y() - self.drag_offset.y()
|
||||
else:
|
||||
# Resizing the rectangle
|
||||
if self.selected_handle == 0: # Top-left
|
||||
self.selected_region.w += self.selected_region.x - event.x()
|
||||
self.selected_region.h += self.selected_region.y - event.y()
|
||||
self.selected_region.x = event.x()
|
||||
self.selected_region.y = event.y()
|
||||
elif self.selected_handle == 1: # Top-right
|
||||
self.selected_region.w = event.x() - self.selected_region.x
|
||||
self.selected_region.h += self.selected_region.y - event.y()
|
||||
self.selected_region.y = event.y()
|
||||
elif self.selected_handle == 2: # Bottom-left
|
||||
self.selected_region.w += self.selected_region.x - event.x()
|
||||
self.selected_region.h = event.y() - self.selected_region.y
|
||||
self.selected_region.x = event.x()
|
||||
elif self.selected_handle == 3: # Bottom-right
|
||||
self.selected_region.w = event.x() - self.selected_region.x
|
||||
self.selected_region.h = event.y() - self.selected_region.y
|
||||
|
||||
# Prevent negative width/height
|
||||
self.selected_region.w = max(self.selected_region.w, 10)
|
||||
self.selected_region.h = max(self.selected_region.h, 10)
|
||||
|
||||
self.update() # Trigger a repaint
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if not self.edit_mode:
|
||||
return
|
||||
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.selected_region = None
|
||||
self.selected_handle = None # Deselect handle
|
||||
|
||||
|
||||
class BorealisOverlay(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Set window properties to cover the full screen
|
||||
screen_geometry = QApplication.primaryScreen().geometry()
|
||||
self.setGeometry(screen_geometry)
|
||||
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, True) # Transparent background
|
||||
|
||||
# Use Win32 API to keep the window on top
|
||||
hwnd = self.winId().__int__()
|
||||
windll.user32.SetWindowPos(hwnd, -1, 0, 0, 0, 0, 0x0001 | 0x0002)
|
||||
|
||||
# Create regions to draw and interact with
|
||||
self.regions = [
|
||||
Region(200, 200, 150, 50, label="Region 01"),
|
||||
Region(400, 300, 200, 80, label="Region 02"),
|
||||
]
|
||||
|
||||
# Create canvas and attach to window
|
||||
self.canvas = OverlayCanvas(self.regions, self)
|
||||
self.canvas.setGeometry(self.rect()) # Match the canvas size to the full window
|
||||
|
||||
# Add title, Edit Mode UI, and close button
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialize UI components."""
|
||||
# Title label
|
||||
self.title_label = QLabel("Borealis Overlay", self)
|
||||
self.title_label.setStyleSheet("QLabel { color: white; font-size: 18px; font-weight: bold; }")
|
||||
self.title_label.move(10, 5)
|
||||
|
||||
# Edit mode checkbox
|
||||
self.mode_toggle = QCheckBox("Edit Mode", self)
|
||||
self.mode_toggle.setStyleSheet("QCheckBox { color: white; }")
|
||||
self.mode_toggle.move(10, 30)
|
||||
self.mode_toggle.stateChanged.connect(self.toggle_edit_mode)
|
||||
|
||||
# Close button
|
||||
self.close_button = QPushButton("Close", self)
|
||||
self.close_button.setStyleSheet(
|
||||
"QPushButton { background-color: #40E0D0; color: white; font-weight: bold; border-radius: 5px; }"
|
||||
) # Turquoise color
|
||||
self.close_button.move(10, 60) # Place it below the Edit Mode checkbox
|
||||
self.close_button.clicked.connect(self.close)
|
||||
|
||||
def toggle_edit_mode(self, state):
|
||||
"""Enable or disable edit mode for dragging and resizing rectangles."""
|
||||
editing = (state == 2)
|
||||
self.canvas.edit_mode = editing # Pass the state to the canvas
|
||||
print(f"Borealis Overlay Edit Mode: {'ON' if editing else 'OFF'}") # Debugging output
|
||||
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
window = BorealisOverlay()
|
||||
window.setWindowTitle("Borealis Overlay") # Set application window title
|
||||
window.show() # Explicitly show the window
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user