Add borealis_overlay.py

This commit is contained in:
Nicole Rappe 2025-02-06 20:49:26 -07:00
commit 6625d29a0e

203
borealis_overlay.py Normal file
View 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()