Класс QOpenGLWidget
Класс QOpenGLWidget — это виджет для рендеринга графики OpenGL. Подробнее...
| Заголовок: | #include <QOpenGLWidget> |
| qmake: | QT += widgets |
| С версии: | Qt 5.4 |
| Наследуется от: | QWidget |
Типы
| Перечисление | UpdateBehavior { NoPartialUpdate, PartialUpdate } |
Открытые функции
| QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = ...) | |
| virtual | ~QOpenGLWidget() |
| QOpenGLContext * | context() const |
| GLuint | defaultFramebufferObject() const |
| void | doneCurrent() |
| QSurfaceFormat | format() const |
| QImage | grabFramebuffer() |
| bool | isValid() const |
| void | makeCurrent() |
| void | setFormat(const QSurfaceFormat &format) |
| void | setTextureFormat(GLenum texFormat) |
| void | setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior) |
| GLenum | textureFormat() const |
| QOpenGLWidget::UpdateBehavior | updateBehavior() const |
- 214 открытых функций унаследованных от QWidget
- 31 открытых функций унаследованных от QObject
- 14 открытых функций унаследованных от QPaintDevice
Сигналы
| void | aboutToCompose() |
| void | aboutToResize() |
| void | frameSwapped() |
| void | resized() |
Статические открытые члены
| const QMetaObject | staticMetaObject |
- 5 статических открытых членов унаследованных от QWidget
- 9 статических открытых членов унаследованных от QObject
Защищенные функции
| virtual void | initializeGL() |
| virtual void | paintGL() |
| virtual void | resizeGL(int w, int h) |
Переопределенные защищенные функции
| virtual void | paintEvent(QPaintEvent *e) override |
| virtual void | resizeEvent(QResizeEvent *e) override |
- 35 защищенных функций унаследованных от QWidget
- 9 защищенных функций унаследованных от QObject
- 1 защищенная функция унаследованная от QPaintDevice
Дополнительные унаследованные члены
- 59 свойств унаследованных от QWidget
- 1 свойство унаследованное от QObject
- 19 публичных слотов унаследованных от QWidget
- 1 публичный слот унаследованный от QObject
- 1 защищенный слот унаследованный от QWidget
Подробное описание
Класс QOpenGLWidget — это виджет для рендеринга графики OpenGL.
QOpenGLWidget предоставляет функциональность для отображения графики OpenGL, интегрированной в приложение Qt. Он очень прост в использовании: сделайте свой класс наследником от него и используйте подкласс как любой другой QWidget, за исключением того, что у вас есть выбор между использованием QPainter и стандартными командами рендеринга OpenGL.
QOpenGLWidget предоставляет три удобные виртуальные функции, которые вы можете переопределить в своем подклассе для выполнения типичных задач OpenGL:
- paintGL() — рендерит сцену OpenGL. Вызывается всякий раз, когда виджету требуется обновление.
- resizeGL() — настраивает область просмотра OpenGL, проекцию и т. д. Вызывается всякий раз, когда виджет был изменен по размеру (а также при его первом отображении, так как все только что созданные виджеты автоматически получают событие изменения размера).
- initializeGL() — настраивает ресурсы и состояние OpenGL. Вызывается один раз перед первым вызовом resizeGL() или paintGL().
Если вам нужно инициировать перерисовку из мест, отличных от paintGL() (типичным примером является использование таймеров для анимации сцены), вы должны вызвать функцию update() виджета для планирования обновления.
Контекст OpenGL вашего виджета становится текущим, когда вызывается paintGL(), resizeGL() или initializeGL(). Если вам нужно вызвать стандартные функции API OpenGL из других мест (например, в конструкторе вашего виджета или в ваших собственных функциях рисования), вы должны сначала вызвать makeCurrent().
Все рендеринг происходит в объекте буфера OpenGL. makeCurrent() гарантирует, что он связан в контексте. Имейте это в виду при создании и привязке дополнительных объектов буфера в коде рендеринга в paintGL(). Никогда не перепривязывайте буфер с идентификатором 0. Вместо этого вызовите defaultFramebufferObject() для получения ID, который должен быть привязан.
QOpenGLWidget позволяет использовать различные версии и профили OpenGL, когда платформа поддерживает это. Просто установите необходимый формат с помощью setFormat(). Однако имейте в виду, что наличие нескольких экземпляров QOpenGLWidget в одном окне требует, чтобы все они использовали один и тот же формат или, по крайней мере, форматы, которые не делают контексты несовместимыми. Чтобы решить эту проблему, предпочтительнее использовать QSurfaceFormat::setDefaultFormat() вместо setFormat().
Примечание: Вызов QSurfaceFormat::setDefaultFormat() перед созданием экземпляра QApplication обязателен на некоторых платформах (например, macOS) при запросе контекста OpenGL с профилем ядра. Это гарантирует, что совместное использование ресурсов между контекстами остается функциональным, так как все внутренние контексты создаются с правильной версией и профилем.
Методы рендеринга
Как описано выше, подклассы QOpenGLWidget для рендеринга чисто 3D контента следующим образом:
- Переопределите функции initializeGL() и resizeGL() для настройки состояния OpenGL и задания перспективной проекции.
- Переопределите функцию paintGL() для отрисовки 3D сцены, используя только функции OpenGL.
Также можно рисовать 2D графику на подклассе QOpenGLWidget с помощью QPainter:
- В функции paintGL() вместо вызова функций OpenGL создайте объект QPainter для использования с виджетом.
- Отрисовывайте примитивы, используя члены QPainter.
- Прямые вызовы функций OpenGL всё ещё возможны. Однако, вы должны убедиться, что они заключены в вызов beginNativePainting() и endNativePainting() художника.
При отрисовке только с помощью QPainter, также можно выполнить отрисовку, как и для обычных виджетов: переопределив paintEvent().
- Переопределите функцию paintEvent().
- Создайте объект QPainter, нацеленный на виджет. Передайте виджет в конструктор или в функцию QPainter::begin().
- Отрисовывайте примитивы, используя члены QPainter.
- Отрисовка завершается, когда экземпляр QPainter уничтожается. В качестве альтернативы, вызовите QPainter::end() явно.
Вызовы функций OpenGL, заголовки и QOpenGLFunctions
При вызове функций OpenGL настоятельно рекомендуется избегать прямого вызова функций. Вместо этого предпочтительнее использовать QOpenGLFunctions (при создании переносимых приложений) или версионные варианты (например, QOpenGLFunctions_3_2_Core и аналогичные, при нацеливании на современные, только настольные OpenGL). Таким образом, приложение будет работать правильно во всех конфигурациях построения Qt, включая те, которые выполняют динамическую загрузку реализации OpenGL, что означает, что приложения не связываются напрямую с реализацией GL, и поэтому прямые вызовы невозможны.
В функции paintGL() текущий контекст всегда доступен с помощью вызова QOpenGLContext::currentContext(). Из этого контекста уже инициализированный, готовый к использованию экземпляр QOpenGLFunctions извлекается с помощью вызова QOpenGLContext::functions(). В качестве альтернативы префиксу каждому вызову GL можно унаследовать от QOpenGLFunctions и вызвать QOpenGLFunctions::initializeOpenGLFunctions() в initializeGL().
Что касается заголовков OpenGL, имейте в виду, что в большинстве случаев нет необходимости в прямом включении таких заголовков, как GL.h. Заголовки Qt, относящиеся к OpenGL, включат qopengl.h, который, в свою очередь, включит соответствующий заголовок для системы. Это может быть заголовок OpenGL ES 3.x или 2.0, самая последняя доступная версия, или системный gl.h. Кроме того, копия заголовков расширений (называемая glext.h на некоторых системах) предоставляется в Qt как для OpenGL, так и для OpenGL ES. Они будут включены автоматически на платформах, где это возможно. Это означает, что константы и типы функций-указателей из расширений ARB, EXT, OES автоматически доступны.
Примеры кода
Для начала, самый простой подкласс QOpenGLWidget может выглядеть следующим образом:
class MyGLWidget : public QOpenGLWidget
{
public:
MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }
protected:
void initializeGL() override
{
// Set up the rendering context, load shaders and other resources, etc.:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
...
}
void resizeGL(int w, int h) override
{
// Update projection matrix and other size related settings:
m_projection.setToIdentity();
m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
...
}
void paintGL() override
{
// Draw the scene:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClear(GL_COLOR_BUFFER_BIT);
...
}
}; В качестве альтернативы, префикс каждого вызова OpenGL можно избежать, унаследовав от QOpenGLFunctions:
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
...
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(...);
...
}
...
}; Чтобы получить контекст, совместимый с заданной версией или профилем OpenGL, или чтобы запросить буферы глубины и трафарета, вызовите setFormat():
QOpenGLWidget *widget = new QOpenGLWidget(parent); QSurfaceFormat format; format.setDepthBufferSize(24); format.setStencilBufferSize(8); format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); widget->setFormat(format); // must be called before the widget or its parent window gets shown
С контекстами OpenGL 3.0+, когда переносимость не важна, версионные варианты QOpenGLFunctions обеспечивают легкий доступ ко всем современным функциям OpenGL, доступным в данной версии:
...
void paintGL() override
{
QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
...
f->glDrawArraysInstanced(...);
...
}
... Как описано выше, проще и надёжнее установить запрашиваемый формат глобально, чтобы он применялся ко всем окнам и контекстам в течение всего срока службы приложения. Ниже приведен пример этого:
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
MyWidget widget;
widget.show();
return app.exec();
} Связь с QGLWidget
Модуль QtOpenGL (классы с префиксом QGL) предоставляет виджет под названием QGLWidget. QOpenGLWidget предназначен для того, чтобы стать современным заменителем. Поэтому, особенно в новых приложениях, рекомендуется использовать QOpenGLWidget.
Хотя API очень похож, существует важное различие между ними: QOpenGLWidget всегда рендерит вне экрана, используя буферы кадров. QGLWidget, с другой стороны, использует родное окно и поверхность. Последнее вызывает проблемы при использовании в сложных пользовательских интерфейсах, так как в зависимости от платформы такие родные дочерние виджеты могут иметь различные ограничения, например, в отношении порядка стеков. QOpenGLWidget избегает этого, не создавая отдельного родного окна.
Благодаря поддержке буфера кадров, поведение QOpenGLWidget очень похоже на QOpenGLWindow с поведением обновления, установленным на PartialUpdateBlit или PartialUpdateBlend. Это означает, что содержимое сохраняется между вызовами paintGL(), что позволяет выполнять пошаговую отрисовку. С QGLWidget (и естественно, QOpenGLWindow с поведением обновления по умолчанию) это обычно не так, потому что обмен буферами оставляет задний буфер с неопределённым содержимым.
Примечание: Большинству приложений не требуется пошаговая отрисовка, поскольку они будут отрисовывать всё в представлении при каждом вызове отрисовки. В этом случае важно вызвать glClear() как можно раньше в paintGL(). Это помогает мобильным графическим процессорам, использующим архитектуру на основе тайлов, распознать, что буфер тайла не нужно перезагружать содержимым предыдущего кадра буфера кадров. Пропуск вызова clear может привести к существенному падению производительности на таких системах.
Примечание: Избегайте вызова winId() на QOpenGLWidget. Эта функция запускает создание родного окна, что приводит к снижению производительности и потенциально к проблемам с отрисовкой.
Отличия от QGLWidget
Помимо основного концептуального различия в использовании буфера кадров, существуют некоторые меньшие внутренние различия между QOpenGLWidget и более старым QGLWidget:
- Состояние OpenGL при вызове paintGL(). QOpenGLWidget настраивает область просмотра с помощью glViewport(). Она не выполняет очистку.
- Очистка при запуске отрисовки с помощью QPainter. В отличие от обычных виджетов, QGLWidget по умолчанию имел значение
trueдля autoFillBackground. Затем она выполняла очистку до фонового цвета палитры каждый раз, когда использовалась QPainter::begin(). QOpenGLWidget не следует этому: autoFillBackground по умолчанию имеет значение false, как и для любого другого виджета. Исключением является использование в качестве области просмотра для других виджетов, таких как QGraphicsView. В этом случае autoFillBackground будет автоматически установлен в значение true для обеспечения совместимости с областями просмотра на основе QGLWidget.
Многообразное выбор
Для включения многообразного выбора установите количество запрошенных выборок в QSurfaceFormat, который передаётся в setFormat(). На системах, которые не поддерживают это, запрос может быть проигнорирован.
Поддержка многообразного выбора требует поддержки многообразных буферов рендеринга и обмена данными между буферами кадров. На реализациях OpenGL ES 2.0, скорее всего, этого не будет. Это означает, что многообразный выбор не будет доступен. С современными версиями OpenGL и OpenGL ES 3.0 и выше, это обычно больше не проблема.
Потоки
Выполнение отрисовки вне экрана в потоках обработки задач, например, для создания текстур, которые затем используются в графическом интерфейсе/главном потоке в paintGL(), поддерживается путём экспонирования QOpenGLContext виджета, чтобы дополнительные контексты, совмещённые с ним, могли быть созданы в каждом потоке.
Отрисовка непосредственно в буфер кадров QOpenGLWidget вне потока графического интерфейса/главного потока возможна путём переопределения paintEvent() на ничего не делающее. Аффинность потока контекста нужно изменить с помощью QObject::moveToThread(). После этого makeCurrent() и doneCurrent() могут использоваться в потоке обработки задач. Будьте внимательны, чтобы вернуть контекст обратно в поток графического интерфейса/главный поток.
В отличие от QGLWidget, запуск обмена буферами только для QOpenGLWidget невозможен, так как нет реальной, отображаемой родной поверхности для него. Вместо этого, управление композицией и обменом буферами в потоке графического интерфейса должно выполняться стеком виджетов. Когда поток обработки задач закончил обновление буфера кадров, вызовите update() в потоке графического интерфейса/главном потоке, чтобы запланировать композицию.
Необходимо проявлять особую осторожность, чтобы избегать использования буфера кадров, когда поток графического интерфейса/главный поток выполняет композицию. Сигналы aboutToCompose() и frameSwapped() будут испущены при запуске и завершении композиции. Они испускаются в потоке графического интерфейса/главном потоке. Это означает, что с помощью прямого подключения aboutToCompose() может заблокировать поток графического интерфейса/главный поток до завершения отрисовки потоком обработки задач. После этого поток обработки задач не должен выполнять дальнейшую отрисовку до испускания сигнала frameSwapped(). Если это неприемлемо, поток обработки задач должен реализовать механизм двойного буферизации. Это подразумевает отрисовку с использованием альтернативной области рендеринга, полностью контролируемой потоком, например, дополнительного буфера кадров, и передачу в буфер кадров QOpenGLWidget в подходящее время.
Совместное использование контекста
Когда несколько QOpenGLWidgets добавляются как дочерние элементы к одному виджету верхнего уровня, их контексты будут совместно использоваться. Это не относится к экземплярам QOpenGLWidget, которые принадлежат различным окнам.
Это означает, что все QOpenGLWidgets в одном окне могут получать доступ к общим ресурсам друг друга, таким как текстуры, и нет необходимости в дополнительном глобальном контексте совместного использования, как это было в случае с QGLWidget.
Для настройки совместного использования между экземплярами QOpenGLWidget, принадлежащими разным окнам, установите атрибут приложения Qt::AA_ShareOpenGLContexts до создания экземпляра QApplication. Это вызовет совместное использование между всеми экземплярами QOpenGLWidget без дополнительных шагов.
Также возможно создание дополнительных экземпляров QOpenGLContext, которые будут совместно использовать ресурсы, такие как текстуры, с контекстом QOpenGLWidget. Просто передайте указатель, возвращённый из context(), в QOpenGLContext::setShareContext() перед вызовом QOpenGLContext::create(). Результирующий контекст также может быть использован в другом потоке, что позволяет выполнять генерацию текстур и асинхронную загрузку текстур в потоках.
Обратите внимание, что QOpenGLWidget ожидает стандартной реализации совместного использования ресурсов в отношении базовых драйверов графики. Например, некоторые драйверы, особенно для мобильных и встроенных устройств, имеют проблемы с настройкой совместного использования между существующим контекстом и контекстами, созданными позже. Другие драйверы могут вести себя непредсказуемо при попытке использовать общие ресурсы между различными потоками.
Инициализация и очистка ресурсов
Контекст OpenGL, связанный с QOpenGLWidget, гарантированно будет текущим при вызове initializeGL() и paintGL(). Не пытайтесь создавать ресурсы OpenGL до вызова initializeGL(). Например, попытка компиляции шейдеров, инициализации объектов буфера вершин или загрузки данных текстур в конструкторе подкласса завершится ошибкой. Эти операции необходимо отложить до initializeGL(). Некоторые вспомогательные классы Qt для OpenGL, такие как QOpenGLBuffer или QOpenGLVertexArrayObject, имеют соответствующее поведение отложенной инициализации: их можно создать без контекста, но вся инициализация откладывается до вызова create() или подобного. Это означает, что их можно использовать как обычные (не указательные) переменные-члены в подклассе QOpenGLWidget, но функцию create() или аналогичную можно вызывать только из initializeGL(). Однако следует учитывать, что не все классы разработаны таким образом. В случае сомнений сделайте переменную-член указателем и создавайте/удаляйте экземпляр динамически в initializeGL() и деструкторе, соответственно.
Освобождение ресурсов также требует, чтобы контекст был текущим. Поэтому ожидается, что деструкторы, выполняющие такую очистку, вызовут makeCurrent() перед уничтожением ресурсов OpenGL или оболочек. Избегайте отложенного удаления с помощью deleteLater() или механизма наследования от QObject. Нет гарантии, что правильный контекст будет текущим в момент фактического уничтожения экземпляра.
Типичный подкласс, следовательно, часто будет выглядеть следующим образом в отношении инициализации и уничтожения ресурсов:
class MyGLWidget : public QOpenGLWidget
{
...
private:
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLShaderProgram *m_program;
QOpenGLShader *m_shader;
QOpenGLTexture *m_texture;
};
MyGLWidget::MyGLWidget()
: m_program(0), m_shader(0), m_texture(0)
{
// No OpenGL resource initialization is done here.
}
MyGLWidget::~MyGLWidget()
{
// Make sure the context is current and then explicitly
// destroy all underlying OpenGL resources.
makeCurrent();
delete m_texture;
delete m_shader;
delete m_program;
m_vbo.destroy();
m_vao.destroy();
doneCurrent();
}
void MyGLWidget::initializeGL()
{
m_vao.create();
if (m_vao.isCreated())
m_vao.bind();
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(...);
m_texture = new QOpenGLTexture(QImage(...));
m_shader = new QOpenGLShader(...);
m_program = new QOpenGLShaderProgram(...);
...
} Это, естественно, не единственное возможное решение. Альтернативой является использование сигнала aboutToBeDestroyed() объекта QOpenGLContext. Подключив слот, используя прямое подключение, к этому сигналу, можно выполнить очистку всякий раз, когда будет освобожден базовый обработчик нативного контекста или весь экземпляр QOpenGLContext. Следующий фрагмент принципиально эквивалентен предыдущему:
void MyGLWidget::initializeGL()
{
// context() and QOpenGLContext::currentContext() are equivalent when called from initializeGL or paintGL.
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}
void MyGLWidget::cleanup()
{
makeCurrent();
delete m_texture;
m_texture = 0;
...
doneCurrent();
} Примечание: Для виджетов, которые несколько раз меняют связанное верхнее окно в течение своего жизненного цикла, необходим комбинированный подход. Всякий раз, когда виджет или его родитель изменяет родителя таким образом, что верхнее окно становится другим, связанный с виджетом контекст уничтожается, и создаётся новый. После этого вызывается initializeGL(), где все ресурсы OpenGL должны быть повторно инициализированы. В связи с этим единственный способ выполнить надлежащую очистку — подключиться к сигналу aboutToBeDestroyed() контекста. Обратите внимание, что контекст, о котором идет речь, может не быть текущим, когда сигнал генерируется. Поэтому рекомендуется вызывать makeCurrent() в подключенном слоте. Кроме того, те же шаги по очистке должны выполняться из деструктора производного класса, так как слот, подключенный к сигналу, не будет вызван при уничтожении виджета.
Примечание: При установке Qt::AA_ShareOpenGLContexts контекст виджета никогда не меняется, даже при изменении родительского окна, потому что текстура, связанная с виджетом, гарантированно доступна также из контекста нового верхнего окна.
Надлежащая очистка особенно важна из-за совместного использования контекстов. Несмотря на то, что контекст, связанный с каждым QOpenGLWidget, уничтожается вместе с QOpenGLWidget, общие ресурсы в этом контексте, такие как текстуры, останутся действительными до уничтожения верхнего окна, в котором существовал QOpenGLWidget. Кроме того, такие параметры, как Qt::AA_ShareOpenGLContexts и некоторые модули Qt, могут вызвать более широкий охват совместного использования контекстов, что может привести к сохранению рассматриваемых ресурсов в течение всего жизненного цикла приложения. Поэтому самый безопасный и надёжный способ — всегда выполнять явную очистку всех ресурсов и оболочек ресурсов, используемых в QOpenGLWidget.
Ограничения
Размещение других виджетов под QOpenGLWidget и установка для него прозрачности не даст ожидаемых результатов: виджеты, расположенные под ним, не будут видны. Это связано с тем, что QOpenGLWidget на практике отрисовывается до всех остальных обычных, не-OpenGL виджетов, и поэтому решения типа «прозрачности» невозможны. Другие типы макетов, например, размещение виджетов поверх QOpenGLWidget, будут работать как ожидается.
В случае крайней необходимости это ограничение можно преодолеть, установив атрибут Qt::WA_AlwaysStackOnTop для QOpenGLWidget. Однако следует понимать, что это нарушает порядок отображения, например, это не позволит разместить другие виджеты поверх QOpenGLWidget, поэтому использовать его следует только в ситуациях, когда требуется полупрозрачный QOpenGLWidget с виджетами, расположенными под ним, видимыми.
Обратите внимание, что это не относится к случаям, когда других виджетов под ним нет, и цель состоит в создании полупрозрачного окна. В этом случае традиционный подход — установить Qt::WA_TranslucentBackground для окна верхнего уровня. Обратите внимание, что если области прозрачности нужны только в QOpenGLWidget, то Qt::WA_NoSystemBackground потребуется вернуть к false после включения Qt::WA_TranslucentBackground. Кроме того, в зависимости от системы, может потребоваться запрос канала альфа для контекста QOpenGLWidget с помощью setFormat().
QOpenGLWidget поддерживает несколько режимов обновления, так же как и QOpenGLWindow. В режиме сохранения содержимое, отрисованное в предыдущем вызове paintGL(), доступно в следующем, что позволяет выполнять инкрементную отрисовку. В режиме без сохранения содержимое теряется, и ожидается, что реализации paintGL() перерисуют всё содержимое.
До Qt 5.5 поведение QOpenGLWidget по умолчанию сохраняло содержимое отрисовки между вызовами paintGL(). С Qt 5.5 поведение по умолчанию — без сохранения, так как это обеспечивает лучшую производительность, и большинству приложений не нужен предыдущий контент. Это также соответствует семантике OpenGL-ориентированного QWindow и совпадает с поведением по умолчанию QOpenGLWindow, так как цветовые и вспомогательные буферы делаются недопустимыми для каждого кадра. Для восстановления поведения сохранения вызовите setUpdateBehavior() со значением PartialUpdate.
Альтернативы
Добавление QOpenGLWidget в окно включает OpenGL-композитинг для всего окна. В некоторых особых случаях это может быть нежелательно, и требуется старое поведение в стиле QGLWidget с отдельным нативным дочерним окном. Приложения для настольных компьютеров, понимающие ограничения этого подхода (например, при перекрытии, прозрачности, прокрутке и областях MDI), могут использовать QOpenGLWindow с QWidget::createWindowContainer(). Это современная альтернатива QGLWidget и быстрее, чем QOpenGLWidget, из-за отсутствия дополнительного шага композитинга. Настоятельно рекомендуется ограничивать использование этого подхода случаями, когда нет другого выбора. Обратите внимание, что этот вариант не подходит для большинства встроенных и мобильных платформ, и известно, что он имеет проблемы на определённых настольных платформах (например, macOS) тоже. Стабильное кроссплатформенное решение — всегда QOpenGLWidget.
OpenGL — товарный знак Silicon Graphics, Inc. в Соединенных Штатах и других странах.
См. также QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts и UpdateBehavior.
Документация типов членов
перечисление QOpenGLWidget::UpdateBehavior
Это перечисление описывает семантику обновления QOpenGLWidget.
| Константа | Значение | Описание |
|---|---|---|
QOpenGLWidget::NoPartialUpdate |
0 |
QOpenGLWidget удалит содержимое буфера цвета и вспомогательных буферов после отрисовки QOpenGLWidget на экране. Это поведение аналогично вызову QOpenGLContext::swapBuffers с аргументом по умолчанию QWindow с включенным OpenGL. NoPartialUpdate может иметь некоторые преимущества в производительности на определённых аппаратных архитектурах, характерных для мобильных и встраиваемых систем, когда в качестве целевого объекта рендеринга используется объект framebuffer. Объект framebuffer инвалидируется между кадрами с помощью glDiscardFramebufferEXT, если он поддерживается, или glClear. Для получения дополнительной информации см. документацию по EXT_discard_framebuffer: https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt |
QOpenGLWidget::PartialUpdate |
1 |
Буфер цвета и вспомогательные буферы объекта framebuffer не инвалидируются между кадрами. |
Этот перечисление был добавлен или изменён в Qt 5.5.
См. также updateBehavior() и setUpdateBehavior().
Документация функций-членов
QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = ...)
Создаёт виджет, являющийся дочерним по отношению к parent, со флагами виджета, установленными в f.
[virtual] QOpenGLWidget::~QOpenGLWidget()
Уничтожает экземпляр QOpenGLWidget, освобождая его ресурсы.
Контекст QOpenGLWidget делается текущим в деструкторе, что позволяет безопасно уничтожить любой дочерний объект, которому может потребоваться освободить ресурсы OpenGL, принадлежащие контексту, предоставленному этим виджетом.
Предупреждение: если у вас есть объекты, оборачивающие ресурсы OpenGL (такие как QOpenGLBuffer, QOpenGLShaderProgram и т. д.) в качестве членов подкласса OpenGLWidget, вам может потребоваться добавить вызов makeCurrent() также в деструкторе этого подкласса. Из-за правил уничтожения объектов C++, эти объекты будут уничтожены до вызова этой функции (но после того, как будет выполнен деструктор подкласса), поэтому установка контекста OpenGL текущим в этой функции происходит слишком поздно для их безопасного удаления.
См. также makeCurrent.
[signal] void QOpenGLWidget::aboutToCompose()
Этот сигнал подаётся, когда верхнеуровневое окно виджета собирается начать композицию текстур его дочерних виджетов QOpenGLWidget и других виджетов.
[signal] void QOpenGLWidget::aboutToResize()
Этот сигнал подаётся, когда размер виджета изменяется, и, следовательно, объект framebuffer будет пересоздан.
QOpenGLContext *QOpenGLWidget::context() const
Возвращает QOpenGLContext, используемый этим виджетом, или 0 если он ещё не инициализирован.
Примечание: контекст и объект framebuffer, используемый виджетом, изменяются при изменении родителя виджета с помощью setParent().
См. также QOpenGLContext::setShareContext() и defaultFramebufferObject().
GLuint QOpenGLWidget::defaultFramebufferObject() const
Возвращает дескриптор объекта framebuffer или 0 если он ещё не инициализирован.
Примечание: объект framebuffer принадлежит контексту, возвращаемому context(), и может быть недоступен из других контекстов.
Примечание: контекст и объект framebuffer, используемые виджетом, изменяются при изменении родителя виджета с помощью setParent(). Кроме того, объект framebuffer изменяется при каждом изменении размера.
См. также context().
void QOpenGLWidget::doneCurrent()
Освобождает контекст.
В большинстве случаев вызывать эту функцию не нужно, так как виджет сам позаботится о связывании и освобождении контекста при вызове paintGL().
QSurfaceFormat QOpenGLWidget::format() const
Возвращает формат контекста и поверхности, используемый этим виджетом и его верхнеуровневым окном.
После создания, изменения размера и отображения виджета и его верхнего уровня эта функция вернёт фактический формат контекста. Он может отличаться от запрошенного формата, если платформа не смогла удовлетворить запрос. Также возможно получить более крупные размеры буфера цвета, чем запрошенные.
Когда окно виджета и связанные ресурсы OpenGL ещё не инициализированы, возвращаемое значение представляет собой формат, установленный с помощью setFormat().
См. также setFormat() и context().
[signal] void QOpenGLWidget::frameSwapped()
Этот сигнал подаётся после того, как верхнеуровневое окно виджета завершит композицию и вернётся из потенциально блокирующего вызова QOpenGLContext::swapBuffers().
QImage QOpenGLWidget::grabFramebuffer()
Отображает и возвращает 32-битное RGB изображение framebuffer.
Примечание: это потенциально дорогостоящая операция, так как она опирается на glReadPixels() для считывания пикселей. Это может быть медленным и может остановить конвейер GPU.
[virtual protected] void QOpenGLWidget::initializeGL()
Эта виртуальная функция вызывается один раз перед первым вызовом paintGL() или resizeGL(). Переопределите её в подклассе.
Эта функция должна настраивать необходимые ресурсы и состояние OpenGL.
Нет необходимости вызывать makeCurrent(), так как это уже сделано при вызове этой функции. Однако следует учесть, что framebuffer ещё недоступен на этом этапе, поэтому избегайте выполнения команд отрисовки отсюда. Отложите такие вызовы до paintGL вместо этого.
См. также paintGL() и resizeGL().
bool QOpenGLWidget::isValid() const
Возвращает true, если виджет и ресурсы OpenGL, такие как контекст, были успешно инициализированы. Обратите внимание, что возвращаемое значение всегда ложно до тех пор, пока виджет не будет показан.
void QOpenGLWidget::makeCurrent()
Подготавливает к отрисовке содержимого OpenGL для этого виджета, делая соответствующий контекст текущим и связывая объект framebuffer в этом контексте.
В большинстве случаев вызывать эту функцию не нужно, так как она вызывается автоматически перед вызовом paintGL().
См. также context(), paintGL() и doneCurrent().
[override virtual protected] void QOpenGLWidget::paintEvent(QPaintEvent *e)
Переопределена из QWidget::paintEvent().
Обрабатывает события отрисовки.
Вызов QWidget::update() приведёт к отправке события отрисовки e и, следовательно, вызову этой функции. (Примечание: это асинхронно и произойдёт в какой-то момент после возвращения из update()). Эта функция затем, после некоторой подготовки, вызовет виртуальную paintGL() для обновления содержимого framebuffer QOpenGLWidget. После этого верхнеуровневое окно виджета объединит текстуру framebuffer с остальной частью окна.
[virtual protected] void QOpenGLWidget::paintGL()
Эта виртуальная функция вызывается всякий раз, когда виджет необходимо перерисовать. Переопределите её в подклассе.
Вызывать makeCurrent() не нужно, так как это уже сделано при вызове этой функции.
Перед вызовом этой функции контекст и framebuffer связываются, а viewport настраивается с помощью вызова glViewport(). Никакое другое состояние не устанавливается, и никакое очищение или отрисовка не выполняется фреймворком.
См. также initializeGL() и resizeGL().
[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)
Переопределена из QWidget::resizeEvent().
Обрабатывает события изменения размера, передаваемые в параметре события e. Вызывает виртуальную функцию resizeGL().
Примечание: Избегайте переопределения этой функции в производных классах. Если это невозможно, убедитесь, что реализация QOpenGLWidget также вызывается. В противном случае базовый объект framebuffer и связанные ресурсы не будут правильно изменены по размеру, что приведёт к некорректной отрисовке.
[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)
Эта виртуальная функция вызывается всякий раз, когда размер виджета изменяется. Переопределите её в подклассе. Новый размер передаётся в w и h.
Нет необходимости вызывать makeCurrent(), так как это уже выполнено при вызове этой функции. Кроме того, framebuffer также связан.
См. также initializeGL() и paintGL().
[signal] void QOpenGLWidget::resized()
Этот сигнал испускается сразу после того, как объект буфера кадров был пересоздан из-за изменения размера виджета.
void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
Устанавливает запрошенный формат поверхности format.
Если формат не задан явно через эту функцию, используется формат, возвращаемый QSurfaceFormat::defaultFormat(). Это означает, что при наличии нескольких виджетов OpenGL отдельные вызовы этой функции могут быть заменены одним вызовом QSurfaceFormat::setDefaultFormat() перед созданием первого виджета.
Примечание: Запрос буфера альфа-канала через эту функцию не даст желаемого результата, если цель — сделать видимыми другие виджеты под ним. Вместо этого используйте Qt::WA_AlwaysStackOnTop для включения полупрозрачных экземпляров QOpenGLWidget с видимыми другими виджетами под ними. Однако имейте в виду, что это нарушает порядок стекирования, поэтому больше не будет возможности разместить другие виджеты поверх QOpenGLWidget.
См. также format(), Qt::WA_AlwaysStackOnTop и QSurfaceFormat::setDefaultFormat().
void QOpenGLWidget::setTextureFormat(GLenum texFormat)
Устанавливает пользовательский внутренний формат текстуры texFormat.
При работе с буферами кадров sRGB необходимо указать формат, например, GL_SRGB8_ALPHA8. Это можно сделать, вызвав эту функцию.
Примечание: Эта функция не имеет эффекта, если вызвана после того, как виджет уже был показан и выполнил инициализацию.
Примечание: Обычно эта функция должна использоваться в сочетании с вызовом QSurfaceFormat::setDefaultFormat(), который устанавливает цветовое пространство на QSurfaceFormat::sRGBColorSpace.
Функция была добавлена в Qt 5.10.
См. также textureFormat().
void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)
Устанавливает поведение обновления этого виджета на updateBehavior.
Функция была добавлена в Qt 5.5.
См. также updateBehavior().
GLenum QOpenGLWidget::textureFormat() const
Возвращает активный внутренний формат текстуры, если виджет уже инициализирован, запрошенный формат, если он был задан, но виджет еще не был показан, или 0, если setTextureFormat() не вызывался и виджет еще не был показан.
Функция была добавлена в Qt 5.10.
См. также setTextureFormat().
QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
Возвращает поведение обновления виджета.
Функция была добавлена в Qt 5.5.
См. также setUpdateBehavior().
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.12/qopenglwidget.html