So I recently found out about the concept of “see through hole” on windows.
Managed to do it in PyQT5 using following code:
from PyQt5.QtCore import Qt, QRect, QPoint
from PyQt5.QtGui import QPainter, QBrush, QColor, QRegion
from PyQt5.QtWidgets import QApplication, QWidget
class SeeThroughHoleWindow(QWidget):
self.drag_start_position = QPoint()
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)
region = QRegion(self.rect())
region -= QRegion(self.hole_rect)
def paintEvent(self, event):
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor(255, 255, 255, 255)))
painter.drawRect(self.rect())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.globalPos() - self.frameGeometry().topLeft()
def mouseMoveEvent(self, event):
self.move(event.globalPos() - self.drag_start_position)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SeeThroughHoleWindow()
<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_())
</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:
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):
self.drag_start_position = QPoint()
self.reader = easyocr.Reader(['en'])
# 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
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)
region = QRegion(self.rect())
region -= QRegion(self.hole_rect)
def paintEvent(self, event):
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor(255, 255, 255, 255)))
painter.drawRect(self.rect())
# Draw bounding boxes over the see-through hole
painter.setPen(QPen(Qt.red, 2))
for box in self.bounding_boxes:
rect = QRect(x1 + self.hole_rect.left(), y1 + self.hole_rect.top(),
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.globalPos() - self.frameGeometry().topLeft()
def mouseMoveEvent(self, event):
self.move(event.globalPos() - self.drag_start_position)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
def capture_and_process(self):
"top": self.y() + self.hole_rect.top(),
"left": self.x() + self.hole_rect.left(),
"width": self.hole_rect.width(),
"height": self.hole_rect.height()
# Convert the image from BGRA to RGB
img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGRA2RGB)
# Read text from the image
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))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SeeThroughHoleWindow()
<code>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_())
</code>
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.