How to create a transparent QWidget on top of a sibling QVideoWidget?

2.3k views Asked by At

I want to add some controls and information on top of a fullscreen QVideoWidget. So I create a QMainWindow ui and then promote the central widget to QVideoWidget as: enter image description here

<?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>

And ControlBoards ui: enter image description here

<?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>

Then in videoscreen.cpp, I also add the ControlBoard as sibling of 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);
}

When I play a video, the result: enter image description here

How to make the ControlBoard be transparent so that we can see the video behind?
I tried some code in ControlBoard constructor:

//    setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
//    setAttribute(Qt::WA_NoSystemBackground);
//    setAttribute(Qt::WA_TranslucentBackground);
//    setAttribute(Qt::WA_PaintOnScreen);
//    setAttribute(Qt::WA_TransparentForMouseEvents);
//    setAutoFillBackground(false);

But this just show the parent's background, not the sibling video. Does Qt5 support this? Thanks!

UPDATE:
I also tried on some other like this (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(&central);

   QMediaPlayer player;
   player.setMedia(QUrl::fromLocalFile("dolbycanyon.mov"));
   player.setVideoOutput(&central);

   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();
}

But the result is still opaque, not transparent:
enter image description here

1

There are 1 answers

0
JustWe On BEST ANSWER

Demo of transparent background overlay on the video:

#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();
}