Класс QOpenGLWidget
Класс QOpenGLWidget — это виджет для отрисовки графики OpenGL. Подробнее...
| Заголовок: | #include <QOpenGLWidget> |
| qmake: | QT += widgets |
| С момента: | Qt 5.4 |
| Наследует: | QWidget |
Типы public
| перечисление | UpdateBehavior { NoPartialUpdate, PartialUpdate } |
Функции public
| QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = ...) | |
| виртуальный | ~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 функций public, унаследованных от QWidget
- 34 функции public, унаследованных от QObject
- 14 функции public, унаследованных от QPaintDevice
Сигналы
| void | aboutToCompose() |
| void | aboutToResize() |
| void | frameSwapped() |
| void | resized() |
Защищенные функции
| виртуальный void | initializeGL() |
| виртуальный void | paintGL() |
| виртуальный void | resizeGL(int w, int h) |
Переопределенные защищенные функции
| виртуальный void | paintEvent(QPaintEvent *e) override |
| виртуальный void | resizeEvent(QResizeEvent *e) override |
- 35 защищенных функций, унаследованных от QWidget
- 9 защищенных функций, унаследованных от QObject
- 1 защищенная функция, унаследованная от QPaintDevice
Дополнительные унаследованные члены
- 59 свойств, унаследованных от QWidget
- 1 свойство, унаследованное от QObject
- 19 public слотов, унаследованных от QWidget
- 1 public слот, унаследованный от QObject
- 1 public переменная, унаследованная от QObject
- 5 статических public членов, унаследованных от QWidget
- 10 статических public членов, унаследованных от QObject
- 1 защищенный слот, унаследованный от QWidget
- 2 защищенных переменных, унаследованных от QObject
- 1 защищенный тип, унаследованный от QPaintDevice
Подробное описание
Класс 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(). Никогда не перепривязывайте фреймбуфер с 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() объекта painter.
При выполнении отрисовки только с помощью 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 предназначен для замены QGLWidget. Поэтому, особенно в новых приложениях, рекомендуется использовать QOpenGLWidget.
Хотя API очень похож, есть важное различие между ними: QOpenGLWidget всегда рендерит вне экрана, используя объекты фреймбуфера. QGLWidget, с другой стороны, использует нативное окно и поверхность. Это вызывает проблемы при использовании в сложных пользовательских интерфейсах, так как в зависимости от платформы такие нативные дочерние виджеты могут иметь различные ограничения, например, в отношении порядка стеков. QOpenGLWidget избегает этого, не создавая отдельного нативного окна.
Благодаря тому, что он основан на объекте фреймбуфера, поведение QOpenGLWidget очень похоже на QOpenGLWindow с поведением обновления, установленным в PartialUpdateBlit или PartialUpdateBlend. Это означает, что содержимое сохраняется между вызовами paintGL(), так что возможна пошаговая отрисовка. В QGLWidget (и, естественно, в QOpenGLWindow с поведением обновления по умолчанию) это обычно не так, потому что обмен буферами оставляет задний буфер с неопределенным содержимым.
Примечание: Большинству приложений не нужна пошаговая отрисовка, потому что они будут отрисовывать всё содержимое в представлении в каждом вызове paint. В этом случае важно вызвать glClear() как можно раньше в paintGL(). Это помогает мобильным GPU, которые используют архитектуру на основе плиток, распознать, что буфер плитки не нужно перезагружать с предыдущим содержимым фреймбуфера. Пропуск вызова 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 и выше это обычно уже не проблема.
Потоки
Поддерживается выполнение отрисовки вне экрана в потоках задач, например, для создания текстур, которые затем используются в GUI/главном потоке в 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.
END_OF_DOCUMENT_MARKERДля настройки совместного использования между экземплярами 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 не приведут к ожидаемому результату: виджеты под ним не будут видны. Это происходит потому, что 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. 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 = ...)
Создаёт виджет, являющийся дочерним элементом 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()
Этот сигнал генерируется, когда размер виджета изменяется, и поэтому объект буфера кадра собирается пересоздать.
QOpenGLContext *QOpenGLWidget::context() const
Возвращает используемый этим виджетом QOpenGLContext или 0, если он ещё не инициализирован.
Примечание: Контекст и объект буфера кадра, используемые виджетом, изменяются при изменении родителя виджета с помощью setParent().
См. также QOpenGLContext::setShareContext() и defaultFramebufferObject().
GLuint QOpenGLWidget::defaultFramebufferObject() const
Возвращает дескриптор объекта буфера кадра или 0, если он ещё не инициализирован.
Примечание: Объект буфера кадра принадлежит контексту, возвращаемому context(), и может быть недоступен из других контекстов.
Примечание: Контекст и объект буфера кадра, используемые виджетом, изменяются при изменении родителя виджета с помощью setParent(). Кроме того, объект буфера кадра изменяется при каждом изменении размера.
См. также context().
void QOpenGLWidget::doneCurrent()
Освобождает контекст.
В большинстве случаев вызывать эту функцию не нужно, так как виджет позаботится о том, чтобы контекст был привязан и освобожден должным образом при вызове paintGL().
QSurfaceFormat QOpenGLWidget::format() const
Возвращает используемый этим виджетом и его верхнеуровневым окном контекст и формат поверхности.
После создания, изменения размера и отображения виджета и его верхнеуровневого окна эта функция вернёт фактический формат контекста. Он может отличаться от запрошенного формата, если платформа не смогла выполнить запрос. Также возможно получить размеры буфера цвета больше, чем запрошенные.
Когда окно виджета и связанные с ним ресурсы OpenGL ещё не инициализированы, возвращаемое значение — это формат, который был задан с помощью setFormat().
См. также setFormat() и context().
[signal] void QOpenGLWidget::frameSwapped()
Этот сигнал генерируется после того, как верхнеуровневое окно виджета завершило композицию и вернулось из потенциально блокирующего вызова QOpenGLContext::swapBuffers().
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] void QOpenGLWidget::paintEvent(QPaintEvent *e)
Переопределено из QWidget::paintEvent().
Обрабатывает события рисования.
Вызов QWidget::update() приведёт к отправке события рисования e, и, следовательно, к вызову этой функции. (Примечание: это асинхронно и произойдёт в какой-то момент после возвращения из update()). Затем эта функция после некоторой подготовки вызовет виртуальную paintGL(), чтобы обновить содержимое буфера кадра QOpenGLWidget. Затем верхнеуровневое окно виджета комбинирует текстуру буфера кадра с остальной частью окна.
[virtual protected] void QOpenGLWidget::paintGL()
Эта виртуальная функция вызывается всякий раз, когда виджет нужно перерисовать. Переопределите её в подклассе.
Вызывать makeCurrent() не нужно, так как это уже сделано при вызове этой функции.
Перед вызовом этой функции контекст и буфер кадра привязываются, а область просмотра настраивается с помощью вызова glViewport(). Никакое другое состояние не устанавливается, и никакое очищение или рисование не выполняется фреймворком.
См. также initializeGL() и resizeGL().
[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)
Переопределено из QWidget::resizeEvent().
Обрабатывает события изменения размера, переданные в параметре события e. Вызывает виртуальную функцию resizeGL().
Примечание: Избегайте переопределения этой функции в производных классах. Если это невозможно, убедитесь, что реализация QOpenGLWidget тоже вызывается. В противном случае подлежащий буфер кадра и связанные с ним ресурсы не будут правильно изменены по размеру, что приведёт к некорректной отрисовке.
[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)
Эта виртуальная функция вызывается всякий раз, когда виджет был изменен по размеру. Переопределите её в подклассе. Новый размер передаётся в w и h.
Вызывать makeCurrent() не нужно, так как это уже сделано при вызове этой функции. Кроме того, буфер кадра также привязан.
См. также 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/archives/qt-5.11/qopenglwidget.html