Nordine Lotfi
Nordine Lotfi

Reputation: 533

Draw on top of see-through hole in window

So I recently found out about the concept of "see through hole" on windows.

Managed to do it in PyQT5 using following code:

import sys
from PyQt5.QtCore import Qt, QRect, QPoint
from PyQt5.QtGui import QPainter, QBrush, QColor, QRegion
from PyQt5.QtWidgets import QApplication, QWidget

class SeeThroughHoleWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.dragging = False
        self.drag_start_position = QPoint()

    def initUI(self):
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setGeometry(100, 100, 800, 600)
        self.setWindowTitle('See-Through Hole Window')
        self.hole_rect = QRect(300, 200, 200, 200)
        self.updateMask()

    def updateMask(self):
        region = QRegion(self.rect())
        region -= QRegion(self.hole_rect)
        self.setMask(region)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(QBrush(QColor(255, 255, 255, 255)))
        painter.setPen(Qt.NoPen)
        painter.drawRect(self.rect())

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragging = True
            self.drag_start_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if self.dragging:
            self.move(event.globalPos() - self.drag_start_position)
            event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragging = False
            event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = SeeThroughHoleWindow()
    ex.show()
    sys.exit(app.exec_())

But then I noticed it's not as obvious to me if it's possible to draw on top of the see-through hole? I know it's possible in Tkinter for example, but I did not find any way to do that with PyQT5.

My Goal would be for example to draw bounding boxes on top, but not with the mouse (since the mouse events pass through the see-through hole) for computer vision related projects.

Here is an example of what I tried to do using EasyOCR and mss:

import sys
import cv2
import numpy as np
import mss
import easyocr
from PyQt5.QtCore import Qt, QRect, QPoint, QTimer
from PyQt5.QtGui import QPainter, QBrush, QColor, QRegion, QPen
from PyQt5.QtWidgets import QApplication, QWidget


class SeeThroughHoleWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.dragging = False
        self.drag_start_position = QPoint()
        self.reader = easyocr.Reader(['en'])
        self.bounding_boxes = []

        # Set up a timer to periodically capture and process the screen
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.capture_and_process)
        self.timer.start(1000)  # Adjust the interval as needed

    def initUI(self):
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setGeometry(100, 100, 800, 600)
        self.setWindowTitle('See-Through Hole Window')
        self.hole_rect = QRect(300, 200, 200, 200)
        self.updateMask()

    def updateMask(self):
        region = QRegion(self.rect())
        region -= QRegion(self.hole_rect)
        self.setMask(region)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(QBrush(QColor(255, 255, 255, 255)))
        painter.setPen(Qt.NoPen)
        painter.drawRect(self.rect())

        # Draw bounding boxes over the see-through hole
        painter.setPen(QPen(Qt.red, 2))
        for box in self.bounding_boxes:
            x1, y1, x2, y2 = box
            rect = QRect(x1 + self.hole_rect.left(), y1 + self.hole_rect.top(),
                         x2 - x1, y2 - y1)
            painter.drawRect(rect)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragging = True
            self.drag_start_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if self.dragging:
            self.move(event.globalPos() - self.drag_start_position)
            event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragging = False
            event.accept()

    def capture_and_process(self):
        with mss.mss() as sct:
            monitor = {
                "top": self.y() + self.hole_rect.top(),
                "left": self.x() + self.hole_rect.left(),
                "width": self.hole_rect.width(),
                "height": self.hole_rect.height()
            }
            img = sct.grab(monitor)
            img_rgb = np.array(img)

            # Convert the image from BGRA to RGB
            img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGRA2RGB)

            # Read text from the image
            self.bounding_boxes = []
            results = self.reader.readtext(img_rgb, detail=1)
            for (bbox, text, prob) in results:
                (top_left, top_right, bottom_right, bottom_left) = bbox
                x1, y1 = int(top_left[0]), int(top_left[1])
                x2, y2 = int(bottom_right[0]), int(bottom_right[1])
                self.bounding_boxes.append((x1, y1, x2, y2))

            self.update()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = SeeThroughHoleWindow()
    ex.show()
    sys.exit(app.exec_())

But only part of the edges of some bounding boxes are shown on the white part of the window.

Upvotes: 0

Views: 46

Answers (0)

Related Questions