如何在同级 QVideoWidget 之上创建透明 QWidget?
How to create a transparent QWidget on top of a sibling QVideoWidget?
我想在全屏上添加一些控件和信息 QVideoWidget
。因此,我创建了一个 QMainWindow
ui,然后将中央小部件提升为 QVideoWidget
,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VideoScreen</class>
<widget class="QMainWindow" name="VideoScreen">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QVideoWidget" name="videoScreen">
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>QVideoWidget</class>
<extends>QWidget</extends>
<header>QVideoWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
和ControlBoards
ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ControlBoard</class>
<widget class="QWidget" name="ControlBoard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1044</width>
<height>520</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QLabel" name="title">
<property name="geometry">
<rect>
<x>210</x>
<y>90</y>
<width>59</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">
</string>
</property>
<property name="text">
<string>Title</string>
</property>
</widget>
<widget class="QPushButton" name="play">
<property name="geometry">
<rect>
<x>320</x>
<y>120</y>
<width>101</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>play</string>
</property>
</widget>
<widget class="QPushButton" name="stop">
<property name="geometry">
<rect>
<x>210</x>
<y>120</y>
<width>101</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>stop</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
然后在 videoscreen.cpp
中,我还将 ControlBoard
添加为 QVideoWidget
的同级:
VideoScreen::VideoScreen(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::VideoScreen), player(new QMediaPlayer)
{
ui->setupUi(this);
player->setVideoOutput(ui->videoScreen);
ui->videoScreen->lower();
ControlBoard *cb = new ControlBoard(this);
}
当我播放视频时,结果:
如何把ControlBoard
转成parent以便我们能看到后面的视频?
我在 ControlBoard
构造函数中尝试了一些代码:
// setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
// setAttribute(Qt::WA_NoSystemBackground);
// setAttribute(Qt::WA_TranslucentBackground);
// setAttribute(Qt::WA_PaintOnScreen);
// setAttribute(Qt::WA_TransparentForMouseEvents);
// setAutoFillBackground(false);
但这只是显示 parent 的背景,而不是兄弟视频。 Qt5 支持这个吗?谢谢!
更新:
我也试过其他一些这样的(base on):
class OverlayWidget : public QWidget
{
void newParent() {
if (!parent()) return;
parent()->installEventFilter(this);
raise();
}
public:
explicit OverlayWidget(QWidget * parent = {}) : QWidget{parent} {
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
newParent();
}
protected:
//! Catches resize and child events from the parent widget
bool eventFilter(QObject * obj, QEvent * ev) override {
if (obj == parent()) {
if (ev->type() == QEvent::Resize)
{
// resize(static_cast<QResizeEvent*>(ev)->size());
}
else if (ev->type() == QEvent::ChildAdded)
raise();
}
return QWidget::eventFilter(obj, ev);
}
//! Tracks parent widget changes
bool event(QEvent* ev) override {
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent()) parent()->removeEventFilter(this);
}
else if (ev->type() == QEvent::ParentChange)
newParent();
return QWidget::event(ev);
}
};
class LoadingOverlay : public OverlayWidget
{
public:
LoadingOverlay(QWidget * parent = {}) : OverlayWidget{parent} {
setAttribute(Qt::WA_TranslucentBackground);
}
protected:
void paintEvent(QPaintEvent *) override {
QPainter p(this);
// p.fillRect(rect(), {10, 10, 10, 100});
p.setPen({200, 200, 255});
p.setFont({"arial,helvetica", 48});
p.drawText(rect(), "Loading...", Qt::AlignHCenter | Qt::AlignVCenter);
}
};
int main(int argc, char * argv[])
{
QApplication a(argc, argv);
QMainWindow window;
QVideoWidget central(&window);
central.setMinimumSize(600, 700);
window.setCentralWidget(¢ral);
QMediaPlayer player;
player.setMedia(QUrl::fromLocalFile("dolbycanyon.mov"));
player.setVideoOutput(¢ral);
LoadingOverlay overlay(&window);
overlay.resize(400, 300);
overlay.setWindowOpacity(.4);
overlay.setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
overlay.setAttribute(Qt::WA_PaintOnScreen);
overlay.setAutoFillBackground(false);
// QTimer::singleShot(5000, &overlay, SLOT(hide()));
// QTimer::singleShot(7000, &player, SLOT(play()));
player.play();
window.show();
return a.exec();
}
但是结果还是不透明,没有反式parent:
视频透明背景叠加演示:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsVideoItem>
#include <QMediaPlayer>
#include <QFileDialog>
#include <QPushButton>
#include <QLabel>
#include <QGraphicsProxyWidget>
const QSizeF VideoItemSize(500, 500);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMediaPlayer player;
QGraphicsView v;
QGraphicsScene scene;
QGraphicsVideoItem video;
v.setScene(&scene);
video.setSize(VideoItemSize);
scene.setSceneRect(QRectF(QPointF(0, 0), VideoItemSize)); // VideoItem full fill the scene
scene.addItem(&video);
player.setVideoOutput(&video);
player.setMedia(QMediaContent(QFileDialog::getOpenFileUrl()));
// Recommend using QGraphicsItems for overlay component
QGraphicsTextItem text("Loading...",&video);
text.setPos(100, 150);
// If you need a button...
QPushButton button("ButtonTest");
QGraphicsProxyWidget* proxyButton = scene.addWidget(&button);
proxyButton->setPos(100, 200);
// Instead of QGraphicsItems, if you really need a QWidget...
QLabel label("LabelTest");
label.setAttribute(Qt::WA_TranslucentBackground); // You can delete this line to see different
QGraphicsProxyWidget* proxyLabel = scene.addWidget(&label);
proxyLabel->setPos(100, 250);
v.show();
player.play();
return a.exec();
}
我想在全屏上添加一些控件和信息 QVideoWidget
。因此,我创建了一个 QMainWindow
ui,然后将中央小部件提升为 QVideoWidget
,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VideoScreen</class>
<widget class="QMainWindow" name="VideoScreen">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QVideoWidget" name="videoScreen">
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>QVideoWidget</class>
<extends>QWidget</extends>
<header>QVideoWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
和ControlBoards
ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ControlBoard</class>
<widget class="QWidget" name="ControlBoard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1044</width>
<height>520</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QLabel" name="title">
<property name="geometry">
<rect>
<x>210</x>
<y>90</y>
<width>59</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">
</string>
</property>
<property name="text">
<string>Title</string>
</property>
</widget>
<widget class="QPushButton" name="play">
<property name="geometry">
<rect>
<x>320</x>
<y>120</y>
<width>101</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>play</string>
</property>
</widget>
<widget class="QPushButton" name="stop">
<property name="geometry">
<rect>
<x>210</x>
<y>120</y>
<width>101</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>stop</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
然后在 videoscreen.cpp
中,我还将 ControlBoard
添加为 QVideoWidget
的同级:
VideoScreen::VideoScreen(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::VideoScreen), player(new QMediaPlayer)
{
ui->setupUi(this);
player->setVideoOutput(ui->videoScreen);
ui->videoScreen->lower();
ControlBoard *cb = new ControlBoard(this);
}
当我播放视频时,结果:
如何把ControlBoard
转成parent以便我们能看到后面的视频?
我在 ControlBoard
构造函数中尝试了一些代码:
// setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
// setAttribute(Qt::WA_NoSystemBackground);
// setAttribute(Qt::WA_TranslucentBackground);
// setAttribute(Qt::WA_PaintOnScreen);
// setAttribute(Qt::WA_TransparentForMouseEvents);
// setAutoFillBackground(false);
但这只是显示 parent 的背景,而不是兄弟视频。 Qt5 支持这个吗?谢谢!
更新:
我也试过其他一些这样的(base on):
class OverlayWidget : public QWidget
{
void newParent() {
if (!parent()) return;
parent()->installEventFilter(this);
raise();
}
public:
explicit OverlayWidget(QWidget * parent = {}) : QWidget{parent} {
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
newParent();
}
protected:
//! Catches resize and child events from the parent widget
bool eventFilter(QObject * obj, QEvent * ev) override {
if (obj == parent()) {
if (ev->type() == QEvent::Resize)
{
// resize(static_cast<QResizeEvent*>(ev)->size());
}
else if (ev->type() == QEvent::ChildAdded)
raise();
}
return QWidget::eventFilter(obj, ev);
}
//! Tracks parent widget changes
bool event(QEvent* ev) override {
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent()) parent()->removeEventFilter(this);
}
else if (ev->type() == QEvent::ParentChange)
newParent();
return QWidget::event(ev);
}
};
class LoadingOverlay : public OverlayWidget
{
public:
LoadingOverlay(QWidget * parent = {}) : OverlayWidget{parent} {
setAttribute(Qt::WA_TranslucentBackground);
}
protected:
void paintEvent(QPaintEvent *) override {
QPainter p(this);
// p.fillRect(rect(), {10, 10, 10, 100});
p.setPen({200, 200, 255});
p.setFont({"arial,helvetica", 48});
p.drawText(rect(), "Loading...", Qt::AlignHCenter | Qt::AlignVCenter);
}
};
int main(int argc, char * argv[])
{
QApplication a(argc, argv);
QMainWindow window;
QVideoWidget central(&window);
central.setMinimumSize(600, 700);
window.setCentralWidget(¢ral);
QMediaPlayer player;
player.setMedia(QUrl::fromLocalFile("dolbycanyon.mov"));
player.setVideoOutput(¢ral);
LoadingOverlay overlay(&window);
overlay.resize(400, 300);
overlay.setWindowOpacity(.4);
overlay.setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
overlay.setAttribute(Qt::WA_PaintOnScreen);
overlay.setAutoFillBackground(false);
// QTimer::singleShot(5000, &overlay, SLOT(hide()));
// QTimer::singleShot(7000, &player, SLOT(play()));
player.play();
window.show();
return a.exec();
}
但是结果还是不透明,没有反式parent:
视频透明背景叠加演示:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsVideoItem>
#include <QMediaPlayer>
#include <QFileDialog>
#include <QPushButton>
#include <QLabel>
#include <QGraphicsProxyWidget>
const QSizeF VideoItemSize(500, 500);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMediaPlayer player;
QGraphicsView v;
QGraphicsScene scene;
QGraphicsVideoItem video;
v.setScene(&scene);
video.setSize(VideoItemSize);
scene.setSceneRect(QRectF(QPointF(0, 0), VideoItemSize)); // VideoItem full fill the scene
scene.addItem(&video);
player.setVideoOutput(&video);
player.setMedia(QMediaContent(QFileDialog::getOpenFileUrl()));
// Recommend using QGraphicsItems for overlay component
QGraphicsTextItem text("Loading...",&video);
text.setPos(100, 150);
// If you need a button...
QPushButton button("ButtonTest");
QGraphicsProxyWidget* proxyButton = scene.addWidget(&button);
proxyButton->setPos(100, 200);
// Instead of QGraphicsItems, if you really need a QWidget...
QLabel label("LabelTest");
label.setAttribute(Qt::WA_TranslucentBackground); // You can delete this line to see different
QGraphicsProxyWidget* proxyLabel = scene.addWidget(&label);
proxyLabel->setPos(100, 250);
v.show();
player.play();
return a.exec();
}