Класс QOpenGLWidget
Класс QOpenGLWidget — это виджет для рендеринга графики OpenGL. Подробнее...
| Заголовок: | #include <QOpenGLWidget> |
| qmake: | QT += widgets |
| С тех пор: | Qt 5.4 |
| Наследует: | QWidget |
Этот класс был представлен в Qt 5.4.
Открытые типы
| Перечисление | UpdateBehavior { NoPartialUpdate, PartialUpdate } |
Открытые функции
| QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()) | |
| виртуальный | ~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 |
Сигналы
| void | aboutToCompose() |
| void | aboutToResize() |
| void | frameSwapped() |
| void | resized() |
Защищённые функции
| виртуальный void | initializeGL() |
| виртуальный void | paintGL() |
| виртуальный void | resizeGL(int w, int h) |
Переопределённые защищённые функции
| виртуальный bool | event(QEvent *e) override |
| виртуальный int | metric(QPaintDevice::PaintDeviceMetric metric) const override |
| виртуальный QPaintEngine * | paintEngine() const override |
| виртуальный void | paintEvent(QPaintEvent *e) override |
| виртуальный QPaintDevice * | redirected(QPoint *p) const override |
| виртуальный void | resizeEvent(QResizeEvent *e) override |
Подробное описание
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(). Никогда не пересвязывайте буфер кадра с ID 0. Вместо этого вызовите defaultFramebufferObject() для получения ID, который следует связать.
QOpenGLWidget позволяет использовать разные версии и профили OpenGL, если платформа это поддерживает. Просто установите требуемый формат через setFormat(). Однако имейте в виду, что наличие нескольких экземпляров QOpenGLWidget в одном окне требует, чтобы все они использовали один и тот же формат или, по крайней мере, форматы, которые не делают контексты несовместимыми. Чтобы решить эту проблему, предпочтительнее использовать QSurfaceFormat::setDefaultFormat() вместо setFormat().
Примечание: Вызов QSurfaceFormat::setDefaultFormat() перед созданием экземпляра QApplication обязателен на некоторых платформах (например, macOS) при запросе контекста профиля OpenGL core. Это необходимо для обеспечения функциональности совместного использования ресурсов между контекстами, так как все внутренние контексты создаются с правильной версией и профилем.
Техники рисования
Как описано выше, подклассы QOpenGLWidget для рендеринга чисто 3D-содержимого:
- Переопределите функции initializeGL() и resizeGL(), чтобы настроить состояние OpenGL и предоставить трансформацию перспективы.
- Переопределите paintGL(), чтобы нарисовать 3D-сцену, вызывая только функции OpenGL.
Также можно рисовать 2D-графику на подклассе QOpenGLWidget с помощью QPainter:
- В paintGL(), вместо выдачи команд OpenGL, создайте объект QPainter для использования на виджете.
- Рисуйте примитивы с помощью функций члена QPainter.
- Команды OpenGL всё ещё можно выдавать. Однако вы должны убедиться, что они заключены в вызов beginNativePainting() и endNativePainting() художника.
При выполнении рисования только с помощью QPainter, также можно выполнить рисование так, как это делается для обычных виджетов: переопределив paintEvent().
END_OF_DOCUMENT_MARKER- Переопределите функцию 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. Кроме того, в Qt предоставляется копия заголовков расширений (называемых glext.h на некоторых системах) как для 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 предназначен для замены QGLWidget. Поэтому в новых приложениях рекомендуется использовать QOpenGLWidget.
Хотя API очень похож, между ними есть важное различие: QOpenGLWidget всегда рендерит вне экрана, используя объекты фреймбуфера. QGLWidget, с другой стороны, использует родное окно и поверхность. Последнее вызывает проблемы при использовании в сложных пользовательских интерфейсах, так как в зависимости от платформы такие родные дочерние виджеты могут иметь различные ограничения, например, относительно порядка стеков. QOpenGLWidget избегает этого, не создавая отдельного родного окна.
Благодаря тому, что QOpenGLWidget поддерживается объектом фреймбуфера, его поведение очень похоже на QOpenGLWindow с поведением обновления, установленным в PartialUpdateBlit или PartialUpdateBlend. Это означает, что содержимое сохраняется между вызовами paintGL(), что позволяет выполнять инкрементную отрисовку. С QGLWidget (и, естественно, QOpenGLWindow с поведением обновления по умолчанию) это обычно не так, потому что обмен буферами оставляет задний буфер с неопределенным содержимым.
Примечание: Большинству приложений не нужна инкрементная отрисовка, так как они отрисуют все содержимое в окне в каждом вызове отрисовки. В этом случае важно вызвать glClear() как можно раньше в paintGL(). Это помогает мобильным графическим процессорам, использующим архитектуру на основе тайлов, распознать, что буфер тайла не нужно перезагружать с предыдущим содержимым буфера фреймбуфера. Пропуск вызова clear может привести к существенному падению производительности на таких системах.
Примечание: Избегайте вызова winId() на QOpenGLWidget. Эта функция запускает создание родного окна, что приводит к снижению производительности и, возможно, артефактам рендеринга.
Отличия от QGLWidget
Помимо основного концептуального различия, заключающегося в том, что QOpenGLWidget поддерживается объектом фреймбуфера, существуют и другие, более мелкие внутренние различия между 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 вне основного потока (GUI/главный поток) возможна путем переопределения paintEvent() для выполнения пустой операции. Аффинность потока контекста необходимо изменить с помощью QObject::moveToThread(). После этого makeCurrent() и doneCurrent() могут быть использованы в рабочем потоке. После этого необходимо вернуть контекст в GUI/главный поток.
В отличие от QGLWidget, запуск обмена буферами только для QOpenGLWidget невозможен, так как для него нет реальной, видимой на экране родной поверхности. Вместо этого, управление композицией и обменом буферами в потоке GUI/главного потока выполняется стеком виджетов. Когда поток завершает обновление фреймбуфера, вызовите update() в GUI/главном потоке, чтобы запланировать композицию.
Необходимо проявлять особую осторожность, чтобы избежать использования фреймбуфера, когда GUI/главный поток выполняет композицию. Сигналы aboutToCompose() и frameSwapped() будут испускаться при запуске и завершении композиции. Они испускаются в GUI/главном потоке. Это означает, что, используя непосредственную связь, aboutToCompose() может заблокировать GUI/главный поток до тех пор, пока рабочий поток не завершит рендеринг. После этого рабочий поток не должен выполнять дальнейшей отрисовки до испускания сигнала 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 после включения 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 с аргументом по умолчанию, enabled QWindow с включенным OpenGL. NoPartialUpdate может иметь некоторые преимущества производительности на определенных архитектурах оборудования, распространённых в мобильных и встроенных системах, когда в качестве целевого объекта отрисовки используется фреймбуфер. Фреймбуфер недействителен между кадрами с glDiscardFramebufferEXT, если поддерживается, или с glClear. Дополнительную информацию см. в документации EXT_discard_framebuffer: https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt |
QOpenGLWidget::PartialUpdate |
1 |
Цветовой буфер и вспомогательные буферы объекта фреймбуфера не делают недействительными между кадрами. |
Это перечисление было добавлено или изменено в Qt 5.5.
См. также updateBehavior() и setUpdateBehavior().
Документация по функциям-членам
QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
Конструирует виджет, который является дочерним элементом parent, с флагами виджета, установленными в f.
[signal] void QOpenGLWidget::aboutToCompose()
Этот сигнал испускается, когда верхнее окно виджета собирается начать композицию текстур его дочерних элементов QOpenGLWidget и других виджетов.
[signal] void QOpenGLWidget::aboutToResize()
Этот сигнал испускается, когда размер виджета изменяется, и поэтому фреймбуфер будет пересоздан.
[signal] void QOpenGLWidget::frameSwapped()
Этот сигнал испускается после того, как верхнее окно виджета завершило композицию и вернулось из потенциально блокирующего вызова QOpenGLContext::swapBuffers().
[signal] void QOpenGLWidget::resized()
Этот сигнал испускается сразу после того, как объект фреймбуфера был пересоздан из-за изменения размера виджета.
[virtual] QOpenGLWidget::~QOpenGLWidget()
Уничтожает экземпляр QOpenGLWidget, освобождая его ресурсы.
Контекст QOpenGLWidget делается текущим в деструкторе, что позволяет безопасно уничтожить любой дочерний объект, который может потребовать освободить ресурсы OpenGL, принадлежащие контексту, предоставляемому этим виджетом.
Предупреждение: если у вас есть объекты, оборачивающие ресурсы OpenGL (например, QOpenGLBuffer, QOpenGLShaderProgram и т.д.) в качестве членов подкласса OpenGLWidget, вам может потребоваться добавить вызов makeCurrent() и в деструкторе этого подкласса. Из-за правил уничтожения объектов C++, эти объекты будут уничтожены до вызова этой функции (но после того, как выполнится деструктор подкласса), поэтому установка текущего контекста OpenGL в этой функции происходит слишком поздно для их безопасного удаления.
См. также makeCurrent.
QOpenGLContext *QOpenGLWidget::context() const
Возвращает QOpenGLContext, используемый этим виджетом, или 0 если он ещё не инициализирован.
Примечание: Контекст и объект фреймбуфера, используемые виджетом, изменяются при изменении родителя виджета с помощью setParent().
См. также QOpenGLContext::setShareContext() и defaultFramebufferObject().
GLuint QOpenGLWidget::defaultFramebufferObject() const
Возвращает дескриптор объекта фреймбуфера или 0 если он ещё не инициализирован.
Примечание: Объект фреймбуфера принадлежит контексту, возвращённому функцией context(), и может быть недоступен из других контекстов.
Примечание: Контекст и объект фреймбуфера, используемые виджетом, изменяются при изменении родителя виджета с помощью setParent(). Кроме того, объект фреймбуфера изменяется при каждом изменении размера.
См. также context().
void QOpenGLWidget::doneCurrent()
Освобождает контекст.
В большинстве случаев вызывать эту функцию не нужно, поскольку виджет позаботится о том, чтобы контекст был привязан и освобождён должным образом при вызове paintGL().
[override virtual protected] bool QOpenGLWidget::event(QEvent *e)
Переопределяет: QWidget::event(QEvent *event).
QSurfaceFormat QOpenGLWidget::format() const
Возвращает формат контекста и поверхности, используемый этим виджетом и его верхним окном.
После создания виджета и его верхнего окна, изменения размера и показа, эта функция вернёт фактический формат контекста. Это может отличаться от запрошенного формата, если платформа не смогла удовлетворить запрос. Также возможно получить размеры буфера цвета больше, чем запрошенные.
Когда окно виджета и связанные ресурсы OpenGL ещё не инициализированы, возвращаемое значение — это формат, установленный с помощью setFormat().
См. также setFormat() и context().
QImage QOpenGLWidget::grabFramebuffer()
Отображает и возвращает 32-битное RGB изображение фреймбуфера.
Примечание: это потенциально дорогостоящая операция, поскольку она полагается на glReadPixels() для считывания пикселей. Это может быть медленным и может приостановить конвейер GPU.
[virtual protected] void QOpenGLWidget::initializeGL()
Эта виртуальная функция вызывается один раз перед первым вызовом paintGL() или resizeGL(). Переопределите её в подклассе.
Эта функция должна настроить любые необходимые ресурсы и состояние OpenGL.
Нет необходимости вызывать makeCurrent(), так как это уже сделано при вызове этой функции. Однако обратите внимание, что фреймбуфер ещё недоступен на этом этапе, поэтому избегайте вызовов отрисовки отсюда. Отложите такие вызовы до paintGL() вместо этого.
См. также paintGL() и resizeGL().
bool QOpenGLWidget::isValid() const
Возвращает true, если виджет и ресурсы OpenGL, такие как контекст, были успешно инициализированы. Обратите внимание, что возвращаемое значение всегда false, пока виджет не отображается.
void QOpenGLWidget::makeCurrent()
Подготавливает отрисовку контента OpenGL для этого виджета, делая соответствующий контекст текущим и связывая объект фреймбуфера в этом контексте.
В большинстве случаев вызывать эту функцию не нужно, так как она вызывается автоматически перед вызовом paintGL().
См. также context(), paintGL() и doneCurrent().
[override virtual protected] int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
Переопределяет: QWidget::metric(QPaintDevice::PaintDeviceMetric m) const.
[override virtual protected] QPaintEngine *QOpenGLWidget::paintEngine() const
Переопределяет: QWidget::paintEngine() const.
[override virtual protected] void QOpenGLWidget::paintEvent(QPaintEvent *e)
Переопределяет: QWidget::paintEvent(QPaintEvent *event).
Обрабатывает события рисования.
Вызов QWidget::update() приведёт к отправке события рисования e, и, следовательно, вызовет эту функцию. (Примечание: это асинхронно и произойдёт в какой-то момент после возвращения из update()). Затем эта функция, после некоторой подготовки, вызовет виртуальную функцию paintGL() для обновления содержимого фреймбуфера QOpenGLWidget. Затем верхнее окно виджета выполнит композицию текстуры фреймбуфера с остальной частью окна.
[virtual protected] void QOpenGLWidget::paintGL()
Эта виртуальная функция вызывается всякий раз, когда требуется перерисовать виджет. Переопределите её в подклассе.
Нет необходимости вызывать makeCurrent(), так как это уже сделано при вызове этой функции.
Перед вызовом этой функции контекст и фреймбуфер привязываются, а область просмотра настраивается с помощью вызова glViewport(). Никакое другое состояние не устанавливается, и никакой очисткой или отрисовкой не выполняется фреймворком.
См. также initializeGL() и resizeGL().
[override virtual protected] QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)
Переопределяет: QWidget::resizeEvent(QResizeEvent *event).
Обрабатывает события изменения размера, передаваемые в параметре события e. Вызывает виртуальную функцию resizeGL().
Примечание: Избегайте переопределения этой функции в производных классах. Если это невозможно, убедитесь, что реализация QOpenGLWidget также вызывается. В противном случае базовый объект фреймбуфера и связанные ресурсы не будут правильно изменены размером, что приведёт к неправильной отрисовке.
[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)
Эта виртуальная функция вызывается всякий раз, когда виджет изменяет размер. Переопределите её в подклассе. Новый размер передаётся в w и h.
Нет необходимости вызывать makeCurrent(), так как это уже сделано при вызове этой функции. Кроме того, фреймбуфер также привязан.
См. также initializeGL() и paintGL().
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
Возвращает активный внутренний формат текстуры, если виджет уже инициализирован, запрошенный формат, если он был установлен, но виджет ещё не отображается, или nullptr, если 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.15/qopenglwidget.html