python - How to display transparent images PyQt QWidget
问题描述
I'm using OpenCV for some image processing and want to create a transparent overlay on my screen using PyQt widgets. Below I have a basic example of sending a basic frame from opencv to PyQt through a signal/slot and displaying it on the window. The issue is I can't get the transparent background using this method, instead it is just a black background:
from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QVBoxLayout
from PyQt5.QtGui import QPixmap
import sys
import cv2
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread
import numpy as np
import os
class VideoThread(QThread):
change_pixmap_signal = pyqtSignal(np.ndarray)
def run(self):
img = np.zeros((500, 500, 4), dtype=np.uint8)
cv2.rectangle(img, (0, 0), (200, 200), (0, 0, 255), 2)
while True
self.change_pixmap_signal.emit(img)
class App(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Qt live label demo")
self.disply_width = 1920
self.display_height = 1080
# create the label that holds the image
self.image_label = QLabel(self)
self.image_label.resize(self.disply_width, self.display_height)
# create a text label
self.textLabel = QLabel('Webcam')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setStyleSheet("background-color:transparent;")
# create a vertical box layout and add the two labels
vbox = QVBoxLayout()
vbox.addWidget(self.image_label)
vbox.addWidget(self.textLabel)
# set the vbox layout as the widgets layout
self.setLayout(vbox)
# create the video capture thread
self.thread = VideoThread()
# connect its signal to the update_image slot
self.thread.change_pixmap_signal.connect(self.update_image)
# start the thread
self.thread.start()
@pyqtSlot(np.ndarray)
def update_image(self, cv_img):
"""Updates the image_label with a new opencv image"""
qt_img = self.convert_cv_qt(cv_img)
self.image_label.setPixmap(qt_img)
def convert_cv_qt(self, cv_img):
"""Convert from an opencv image to QPixmap"""
rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
convert_to_Qt_format = QtGui.QImage(
rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
p = convert_to_Qt_format.scaled(
self.disply_width, self.display_height, Qt.KeepAspectRatio)
return QPixmap.fromImage(p)
if __name__ == "__main__":
app = QApplication(sys.argv)
a = App()
a.show()
sys.exit(app.exec_())
If it isn't possible to send a frame with an alpha channel to PyQt, I was wondering if it's possible to just send the rectangle 4 point location and use the PyQt paint to draw a rectangle on screen? I think this would require a widget.update() but I'm not sure where to invoke that.
解决方案
If you are going to use transparencies then the colors and the image must be 4-channel, in your case you are passing a 3-channel color to the cv2.rectangle method and then converting you use a 3-channel format in QImage. On the other hand cv2.rectangle returns the drawn image, it does not modify the input array.
def run(self):
img = np.zeros((500, 500, 4), dtype=np.uint8)
output = cv2.rectangle(img, (0, 0), (200, 200), (0, 0, 255, 255), 2)
while True:
self.change_pixmap_signal.emit(output)
QThread.msleep(1)
def convert_cv_qt(self, cv_img):
h, w, ch = cv_img.shape
bytes_per_line = ch * w
convert_to_Qt_format = QtGui.QImage(
cv_img.data, w, h, bytes_per_line, QtGui.QImage.Format_RGBA8888
)
p = convert_to_Qt_format.scaled(
self.disply_width, self.display_height, Qt.KeepAspectRatio
)
return QPixmap.fromImage(p)
推荐阅读
- angular - 如何根据 Token 的值使用 *ngIf 渲染组件?
- bash - 如何在 shell 脚本中在当前目录之前打印目录?
- javascript - 如何在 Laravel mix Vue Js 上动态创建组件 Navbar
- mysql - 如何为 cli 和应用程序使用 DoctrineORMModule 不同的凭据?
- paypal - PayPal 支付体验配置文件创建
- node.js - AWS EC2 实例中的 puppeteer 集群脚本问题
- r - 如何使用相同的 R 代码运行多个 Excel 工作表
- php - 使用 Ajax 时如何访问当前 PHP 文件之外的 PHP 函数?
- c# - Xamarin 换屏时出现黑屏
- typescript - TypeScript:我们可以禁止推断混合类型吗?