QPaintEngine *QWidget::paintEngine 的实现:
/*!
\fn QPaintEngine *QWidget::paintEngine() const
Returns the widget's paint engine.
Note that this function should not be called explicitly by the
user, since it's meant for reimplementation purposes only. The
function is called by Qt internally, and the default
implementation may not always return a valid pointer.
*/
QPaintEngine *QWidget::paintEngine() const
{
qWarning("QWidget::paintEngine: Should no longer be called");
#ifdef Q_OS_WIN
// We set this bit which is checked in setAttribute for
// Qt::WA_PaintOnScreen. We do this to allow these two scenarios:
//
// 1. Users accidentally set Qt::WA_PaintOnScreen on X and port to
// Windows which would mean suddenly their widgets stop working.
//
// 2. Users set paint on screen and subclass paintEngine() to
// return 0, in which case we have a "hole" in the backingstore
// allowing use of GDI or DirectX directly.
//
// 1 is WRONG, but to minimize silent failures, we have set this
// bit to ignore the setAttribute call. 2. needs to be
// supported because its our only means of embedding native
// graphics stuff.
const_cast<QWidgetPrivate *>(d_func())->noPaintOnScreen = 1;
#endif
return nullptr; //##### @@@
}
如果想不走qt 的渲染逻辑,那么需要做:
创建一个继承QWidget的类,重写paintEvent,resizeEvent
但是仅仅这样还不够,画面会一直闪烁,需要重写以下函数
virtual QPaintEngine paintEngine() const { return NULL; }*
并设置窗口属性
this->setAttribute(Qt::WA_PaintOnScreen, true);
this->setAttribute(Qt::WA_NativeWindow, true);
1.系统级别 QApplication::setAttribute(Qt::AA_NativeWindows)
(1)设置Qt::AA_NativeWindows
所有控件均视为window,可通过QGuiApplication::allWindows()获取所有控件,然后获取窗口id和大小,并且,观察每个子widget大小(通过widget->geometry()获取),可发现均集中在屏幕左上角,并且在显示上,屏幕左上角均是矩形空白,并且随着缩放,矩形空白也在变化,widget大小也在变化,即整个widget是多个子widget的合并,每个widget在底层均有一个窗口系统的句柄和内存空间,对刷新会有一定影响。
对于依托父widget建立的widget,也有自己的窗口句柄,并且依然依附于父widget。
对于没有依托父widget建立的widget,有自己的窗口句柄,并且作为独立窗口,默认显示在屏幕左上角。
(2)不设置 Qt::AA_NativeWindows
只有toplevelWidget有本地窗口系统句柄,屏幕没有矩形空白,按照QT文档所述,非native_window由QT自己绘制。
对于依托父widget建立的widget,没有自己的窗口句柄,依附于父widget。
对于没有依托父widget建立的widget,有自己的窗口句柄,并且作为独立窗口,默认显示在屏幕左上角。
2.widget级别 setAttribute(Qt::WA_NativeWindow)
这个widget以及其祖先都会设置成native_window,并且父widget的所有子widget也都会变(enforceNativeChildren()函数),所以发现allWindows变多了,如果不需要所有祖先都变,需要加 setAttribute(Qt::WA_DontCreateNativeAncestors),如果不需要所有兄弟都变,需要加系统级参数QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
3.widget的winId()函数和setAttribute(Qt::WA_NativeWindow)类似,并且返回widget的句柄id
?
如下面的qt-display中,设置了
setAttribute(Qt::WA_DontCreateNativeAncestors);
setAttribute(Qt::WA_NativeWindow);
让自己的窗口成为native 同时禁止父窗口成为native窗口
同时设置了Qt::WA_PaintOnScreen属性:Qt Namespace | Qt Core 6.6.1
指示小部件想要直接绘制到屏幕上。具有此属性集的小部件不参与组合管理,
即它们不能是半透明的或通过半透明重叠小部件发光。注意:此标志仅在 X11
上受支持,并且它禁用双缓冲。在 Qt for Embedded Linux 上,该标志
仅在顶级小部件上设置时才起作用,并且它依赖于活动屏幕驱动程序的支持。
该标志由小部件的作者设置或清除。要在 Qt 的绘画系统之外进行渲染,
例如,如果您需要本机绘画基元,则需要重新实现QWidget::paintEngine()
返回 0 并设置此标志。
?? ?setAttribute(Qt::WA_NoSystemBackground);
?? ?setAttribute(Qt::WA_OpaquePaintEvent);
https://github.com/alibaba/tblive/blob/master/obs/qt-display.cpp
#pragma once
#include <QWidget>
#include <obs.hpp>
class OBSQTDisplay : public QWidget {
Q_OBJECT
OBSDisplay display;
void CreateDisplay();
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
signals:
void DisplayCreated(OBSQTDisplay *window);
void DisplayResized();
public:
OBSQTDisplay(QWidget *parent = 0, Qt::WindowFlags flags = 0);
virtual QPaintEngine *paintEngine() const override;
inline obs_display_t *GetDisplay() const {return display;}
};
#include "qt-display.hpp"
#include "qt-wrappers.hpp"
#include "display-helpers.hpp"
#include <QWindow>
#include <QScreen>
#include <QResizeEvent>
#include <QShowEvent>
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_StaticContents);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_DontCreateNativeAncestors);
setAttribute(Qt::WA_NativeWindow);
auto windowVisible = [this] (bool visible)
{
if (!visible)
return;
if (!display) {
CreateDisplay();
} else {
QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), size.height());
}
};
auto sizeChanged = [this] (QScreen*)
{
CreateDisplay();
QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), size.height());
};
connect(windowHandle(), &QWindow::visibleChanged, windowVisible);
connect(windowHandle(), &QWindow::screenChanged, sizeChanged);
}
void OBSQTDisplay::CreateDisplay()
{
if (display || !windowHandle()->isExposed())
return;
QSize size = GetPixelSize(this);
gs_init_data info = {};
info.cx = size.width();
info.cy = size.height();
info.format = GS_RGBA;
info.zsformat = GS_ZS_NONE;
//将qt的创建句柄
QTToGSWindow(winId(), info.window);
display = obs_display_create(&info);
emit DisplayCreated(this);
}
void OBSQTDisplay::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
CreateDisplay();
if (isVisible() && display) {
QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), size.height());
}
emit DisplayResized();
}
void OBSQTDisplay::paintEvent(QPaintEvent *event)
{
CreateDisplay();
QWidget::paintEvent(event);
}
QPaintEngine *OBSQTDisplay::paintEngine() const
{
return nullptr;
}
如obs中使用opengl在qwidget上绘制如上,使用如下
OBS源码使用学习(二)之预览框显示_obsqtdisplay-CSDN博客
OBS中的预览框:
【obs-studio开源项目从入门到放弃】预览窗口中source的UI操作绘制处理 - 代码先锋网
其他:
从零开始开发3D游戏引擎 - 在QtWidget中进行Direct3D11渲染,并捕捉Windows消息_qt5窗口显示d3d11-CSDN博客
OBS:
?主界面:OBSBasic::OBSBasic
预览界面:OBSBasicPreview