12 May, 2012

QWebView с прозрачным фоном поверх Phonon::VideoWidget

Передо мной встала задача добиться размещения виджета QWebView поверх работающего Phonon::VideoWidget, таким образом, чтобы background был прозрачен и выводимый на QWebView текст отображался непосредственно поверх видеоряда.

Ниже я расскажу, почему это проблематично сделать, и какой workaround я нашел.

Проблема состоит в том что phonon-backend, например GStreamer рисует видеоряд в Linux напрямую через XRender, путем длительного гугления и всяческих тестов я убедился, что невозможно вывести прозрачный QWidget поверх видеоряда, в любом случае получается черная подложка. Т. е. изначально, я пробовал такой вариант кода:
  1. m_video = new Phonon::VideoWidget;
  2. .....
  3. QWebView *view = new QWebView(mVideo);
  4. view->setHtml("<div style=\"color:#FFFFFF;font-size:36px;\">Hello Qt!</div>");
  5. QPalette palette = view->page()->palette();
  6. palette.setBrush(QPalette::Base, Qt::transparent);
  7. view->setPalette(palette);
  8. view->setAttribute(Qt::WA_OpaquePaintEvent, false);
  9. m_video->show();
  10. m_video->resize(640, 480);
  11. view->setFixedSize(m_video->width(), m_video->height());
ЭТОТ ВАРИАНТ НЕ РАБОТАЕТ!
Тогда я решил попробовать поискать обходной путь и нашел его. Основная идея в том чтобы сделать оверлейный виджет отдельным окном с отключенным оформлением, причем такой, чтобы он был привязан к VideoWidget таким образом, чтобы мог перемещаться и менять размеры вслед за ним. 

Сначала наследуем новый класс от видео виджета и переопределяем event методы таким образом, чтобы автоматически изменять геометрию оверлейного виджета в зависимости от геометрии видео виджета:
  1. class OverlayedVideoWidget : public Phonon::VideoWidget
  2. {
  3. public:
  4.     OverlayedVideoWidget(QWidget *parent = 0) :
  5.         Phonon::VideoWidget(parent),
  6.         m_overlayWidget(0)
  7.     {
  8.     }
  9.     void setOverlayWidget(QWidget *widget)
  10.     {
  11.         m_overlayWidget = widget;
  12.     }
  13. protected:
  14.     void moveEvent(QMoveEvent *event)
  15.     {
  16.         if (m_overlayWidget)
  17.             m_overlayWidget->move(event->pos());
  18.     }
  19.     void resizeEvent(QResizeEvent *event)
  20.     {
  21.         if (m_overlayWidget)
  22.             m_overlayWidget->resize(event->size());
  23.     }
  24.     void hideEvent(QHideEvent *)
  25.     {
  26.         if (m_overlayWidget)
  27.             m_overlayWidget->hide();
  28.     }
  29.     void showEvent(QShowEvent *)
  30.     {
  31.         if (m_overlayWidget)
  32.             m_overlayWidget->show();
  33.     }
  34.     void closeEvent(QCloseEvent *)
  35.     {
  36.         if (m_overlayWidget)
  37.             m_overlayWidget->close();
  38.     }
  39. private:
  40.     QWidget *m_overlayWidget;
  41. };
Таким образом мы добиваемся того, чтобы оверлейный виджет всегда находился над виджетом с видеорядом и имел точно такие же размеры.

А теперь подключаем виджет:
  1. m_video = new OverlayedVideoWidget;
  2. QWebView *view = new QWebView();
  3. view->setHtml("<div style=\"color:#FFFFFF;font-size:36px;\">Hello Qt!</div>");
  4. QPalette palette = view->page()->palette();
  5. palette.setBrush(QPalette::Base, Qt::transparent);
  6. view->setPalette(palette);
  7. view->page()->setPalette(palette);
  8. view->setAttribute(Qt::WA_OpaquePaintEvent, false);
  9. view->setAttribute(Qt::WA_TranslucentBackground, true);
  10. view->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint);
  11. m_video->setOverlayWidget(view);
  12. m_video->show();
  13. m_video->resize(640, 480);
Вся соль идеи в 11 и 12 строках. Мы делаем оверлейный виджет окном специального типа, который не имеет заголовка, не отображается в панели задач и имеет прозрачный background.
Поскольку наше "окно" привязано к OverlayedVideoWidget оно все время будет как бы парить над ним, создавая эффект единого целого.

На этом все.


No comments:

Post a Comment