QOpenGLWidget Class
La classe QOpenGLWidget est un widget permettant d'afficher des graphismes OpenGL. En savoir plus...
| En-tête : | #include <qopenglwidget.h> |
| CMake : | find_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets) target_link_libraries(mytarget PRIVATE Qt6::OpenGLWidgets) |
| qmake : | QT += openglwidgets |
| Hérite de : | QWidget |
Types publics
(since 6.5) enum | TargetBuffer { LeftBuffer, RightBuffer } |
| enum | UpdateBehavior { NoPartialUpdate, PartialUpdate } |
Fonctions publiques
| QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()) | |
| virtual | ~QOpenGLWidget() |
| QOpenGLContext * | context() const |
(since 6.5) QOpenGLWidget::TargetBuffer | currentTargetBuffer() const |
| GLuint | defaultFramebufferObject() const |
(since 6.5) GLuint | defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const |
| void | doneCurrent() |
| QSurfaceFormat | format() const |
| QImage | grabFramebuffer() |
(since 6.5) QImage | grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer) |
| bool | isValid() const |
| void | makeCurrent() |
(since 6.5) void | makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer) |
| void | setFormat(const QSurfaceFormat &format) |
| void | setTextureFormat(GLenum texFormat) |
| void | setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior) |
| GLenum | textureFormat() const |
| QOpenGLWidget::UpdateBehavior | updateBehavior() const |
Signaux
| void | aboutToCompose() |
| void | aboutToResize() |
| void | frameSwapped() |
| void | resized() |
Fonctions protégées
| virtual void | initializeGL() |
| virtual void | paintGL() |
| virtual void | resizeGL(int w, int h) |
Fonctions protégées réimplémentées
| virtual bool | event(QEvent *e) override |
| virtual int | metric(QPaintDevice::PaintDeviceMetric metric) const override |
| virtual QPaintEngine * | paintEngine() const override |
| virtual void | paintEvent(QPaintEvent *e) override |
| virtual QPaintDevice * | redirected(QPoint *p) const override |
| virtual void | resizeEvent(QResizeEvent *e) override |
Description détaillée
QOpenGLWidget fournit des fonctionnalités permettant d'afficher des graphiques OpenGL intégrés à une application Qt. Son utilisation est très simple : faites en sorte que votre classe en hérite et utilisez la sous-classe comme n'importe quelle autre classe de la classe « QWidget », à la différence près que vous avez le choix entre utiliser « QPainter » et les commandes de rendu OpenGL standard.
QOpenGLWidget fournit trois fonctions virtuelles pratiques que vous pouvez réimplémenter dans votre sous-classe pour effectuer les tâches OpenGL courantes :
- paintGL() - Effectue le rendu de la scène OpenGL. Est appelée chaque fois que le widget doit être mis à jour.
- resizeGL() - Configure la fenêtre d’affichage OpenGL, la projection, etc. Elle est appelée chaque fois que le widget a été redimensionné (ainsi que lors de sa première affichage, car tous les widgets nouvellement créés reçoivent automatiquement un événement de redimensionnement).
- initializeGL() - Configure les ressources et l’état OpenGL. Est appelée une seule fois avant le premier appel à ` resizeGL()` ou ` paintGL()`.
Si vous devez déclencher un rafraîchissement à partir d'autres endroits que paintGL() (un exemple typique est l'utilisation de timers pour animer des scènes), vous devez appeler la fonction update() du widget pour planifier une mise à jour.
Le contexte de rendu OpenGL de votre widget devient actif lorsque paintGL(), resizeGL() ou initializeGL() est appelé. Si vous devez appeler les fonctions standard de l’API OpenGL depuis d’autres endroits (par exemple dans le constructeur de votre widget ou dans vos propres fonctions de dessin), vous devez d’abord appeler makeCurrent().
Tout le rendu s'effectue dans un objet framebuffer OpenGL. La fonction makeCurrent() garantit qu'il est lié au contexte. Gardez cela à l'esprit lorsque vous créez et liez des objets framebuffer supplémentaires dans le code de rendu de la fonction paintGL(). Ne reliez jamais le framebuffer avec l'ID 0. Appelez plutôt la fonction defaultFramebufferObject() pour obtenir l'ID qui doit être lié.
QOpenGLWidget permet d’utiliser différentes versions et différents profils OpenGL lorsque la plate-forme le prend en charge. Il suffit de définir le format souhaité via setFormat(). Gardez toutefois à l’esprit que la présence de plusieurs instances de QOpenGLWidget dans une même fenêtre nécessite qu’elles utilisent toutes le même format, ou au moins des formats qui ne rendent pas les contextes non partageables. Pour contourner ce problème, privilégiez l’utilisation de QSurfaceFormat::setDefaultFormat() plutôt que setFormat().
Remarque : l’appel de QSurfaceFormat::setDefaultFormat() avant la construction de l’instance QApplication est obligatoire sur certaines plateformes (par exemple, macOS) lorsqu’un contexte OpenGL Core Profile est demandé. Cela permet de garantir que le partage des ressources entre les contextes reste fonctionnel, car tous les contextes internes sont créés en utilisant la version et le profil corrects.
Techniques de rendu
Comme décrit ci-dessus, sous-classez QOpenGLWidget pour afficher du contenu 3D pur de la manière suivante :
- Réimplémentez les fonctions ` initializeGL()` et ` resizeGL()` pour configurer l’état OpenGL et fournir une transformation de perspective.
- Réimplémentez la méthode paintGL() pour dessiner la scène 3D, en n'appelant que des fonctions OpenGL.
Il est également possible de dessiner des graphiques 2D sur une sous-classe de QOpenGLWidget à l'aide de QPainter:
- Dans paintGL(), au lieu d’émettre des commandes OpenGL, créez un objet QPainter destiné à être utilisé sur le widget.
- Dessinez des primitives à l’aide des fonctions membres de ` QPainter`.
- Il est toujours possible d'émettre des commandes OpenGL directes. Cependant, vous devez vous assurer que celles-ci sont encadrées par un appel aux fonctions beginNativePainting() et endNativePainting() du « painter ».
Lorsque vous effectuez un dessin en utilisant uniquement ` QPainter `, il est également possible d’effectuer le dessin comme pour les widgets ordinaires : en réimplémentant ` paintEvent()`.
- Réimplémentez la fonction paintEvent().
- Créez un objet ` QPainter ` en ciblant le widget. Passez le widget soit au constructeur, soit à la fonction ` QPainter::begin()`.
- Dessinez des primitives à l’aide des fonctions membres d’ QPainter.
- Une fois le dessin terminé, l’instance de ` QPainter ` est détruite. Vous pouvez également appeler explicitement la fonction ` QPainter::end()`.
Appels de fonctions OpenGL, en-têtes et QOpenGLFunctions
Lors de l’appel de fonctions OpenGL, il est fortement recommandé d’éviter d’appeler les fonctions directement. Préférez plutôt utiliser ` QOpenGLFunctions ` (lors de la création d’applications portables) ou les variantes versionnées (par exemple, ` QOpenGLFunctions_3_2_Core ` et similaires, lorsque vous ciblez une version moderne d’OpenGL réservée aux ordinateurs de bureau). De cette manière, l’application fonctionnera correctement dans toutes les configurations de compilation Qt, y compris celles qui effectuent un chargement dynamique de l’implémentation OpenGL, ce qui signifie que les applications ne se lient pas directement à une implémentation GL et que, par conséquent, les appels de fonction directs ne sont pas possibles.
Dans ` paintGL()` (), le contexte actuel est toujours accessible en appelant ` QOpenGLContext::currentContext()` (). À partir de ce contexte, une instance ` QOpenGLFunctions ` déjà initialisée et prête à l’emploi peut être récupérée en appelant ` QOpenGLContext::functions()` (). Une alternative au préfixage de chaque appel GL consiste à hériter de ` QOpenGLFunctions ` et à appeler ` QOpenGLFunctions::initializeOpenGLFunctions()` () dans ` initializeGL()` ().
En ce qui concerne les en-têtes OpenGL, notez que dans la plupart des cas, il ne sera pas nécessaire d’inclure directement des en-têtes tels que GL.h. Les en-têtes Qt liés à OpenGL incluront qopengl.h, qui inclura à son tour l’en-tête approprié pour le système. Il peut s’agir d’un en-tête OpenGL ES 3.x ou 2.0, de la version la plus récente disponible, ou d’un fichier gl.h fourni par le système. De plus, une copie des en-têtes d’extension (appelés glext.h sur certains systèmes) est fournie avec Qt, tant pour OpenGL que pour OpenGL ES. Ceux-ci seront inclus automatiquement sur les plateformes où cela est possible. Cela signifie que les constantes et les typedefs de pointeurs de fonction provenant des extensions ARB, EXT et OES sont automatiquement disponibles.
Exemples de code
Pour commencer, la sous-classe QOpenGLWidget la plus simple pourrait ressembler à ceci :
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);
...
}
};Il est également possible d’éviter d’avoir à préfixer chaque appel OpenGL en dérivant plutôt de la classe ` QOpenGLFunctions ` :
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
...
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(...);
...
}
...
};Pour obtenir un contexte compatible avec une version ou un profil OpenGL donné, ou pour demander des tampons de profondeur et de stencil, appelez 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 shownRemarque : il incombe à l’application de s’assurer que les tampons de profondeur et de stencil sont demandés via l’interface du système de fenêtrage sous-jacent. Si la taille du tampon de profondeur demandée n’est pas supérieure à zéro, rien ne garantit qu’un tampon de profondeur sera disponible ; par conséquent, les opérations OpenGL liées au test de profondeur risquent de ne pas fonctionner comme prévu. Les tailles couramment utilisées pour les tampons de profondeur et de stencil sont respectivement 24 et 8.
Avec les contextes OpenGL 3.0 et supérieurs, lorsque la portabilité n’est pas un critère important, les variantes versionnées de ` QOpenGLFunctions ` permettent d’accéder facilement à toutes les fonctions OpenGL modernes disponibles dans une version donnée :
...
void paintGL() override
{
QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
...
f->glDrawArraysInstanced(...);
...
}
...Comme décrit ci-dessus, il est plus simple et plus fiable de définir le format demandé de manière globale afin qu’il s’applique à toutes les fenêtres et à tous les contextes pendant toute la durée de vie de l’application. En voici un exemple :
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();
}Multisampling
Pour activer le multisampling, définissez le nombre d’échantillons requis sur l’ QSurfaceFormat transmis à la fonction ` setFormat()`. Sur les systèmes qui ne prennent pas en charge cette fonctionnalité, la requête peut être ignorée.
La prise en charge du multisampling nécessite la prise en charge des buffers de rendu multisamplés et des blits de framebuffer. Sur les implémentations OpenGL ES 2.0, il est probable que ces fonctionnalités ne soient pas présentes. Cela signifie que le multisampling ne sera pas disponible. Avec les versions modernes d’OpenGL et OpenGL ES 3.0 et versions ultérieures, cela ne pose généralement plus de problème.
Multithreading
Le rendu hors écran sur des threads de travail, par exemple pour générer des textures qui seront ensuite utilisées dans l’interface graphique ou le thread principal via ` paintGL()`, est pris en charge en exposant l’interface ` QOpenGLContext ` du widget, ce qui permet de créer des contextes supplémentaires partagés avec celui-ci sur chaque thread.
Il est possible de dessiner directement dans le framebuffer du QOpenGLWidget en dehors du thread de l’interface graphique ou du thread principal en réimplémentant la méthode ` paintEvent()` pour qu’elle ne fasse rien. L’affinité de thread du contexte doit être modifiée via ` QObject::moveToThread()`. Après cela, les méthodes ` makeCurrent()` et ` doneCurrent()` sont utilisables sur le thread de travail. Veillez à replacer ensuite le contexte sur le thread GUI/principal.
Il n’est pas possible de déclencher un échange de tampon uniquement pour le `QOpenGLWidget`, car il ne dispose pas de véritable surface native à l’écran. C’est à la pile de widgets qu’il revient de gérer la composition et les échanges de tampons sur le thread GUI. Lorsqu’un thread a terminé la mise à jour du tampon de trame, appelez ` update()` sur le thread GUI/principal pour planifier la composition.
Il faut veiller tout particulièrement à ne pas utiliser le tampon d’image lorsque le thread GUI/principal effectue la composition. Les signaux ` aboutToCompose()` et ` frameSwapped()` sont émis au début et à la fin de la composition. Ils sont émis sur le thread GUI/principal. Cela signifie qu’en utilisant une connexion directe, ` aboutToCompose()` peut bloquer le thread GUI/principal jusqu’à ce que le thread de travail ait terminé son rendu. Après cela, le thread de travail ne doit plus effectuer de rendu jusqu’à ce que le signal ` frameSwapped()` soit émis. Si cela n’est pas acceptable, le thread de travail doit mettre en œuvre un mécanisme de double tampon. Cela implique de dessiner à l’aide d’une cible de rendu alternative, entièrement contrôlée par le thread, par exemple un objet framebuffer supplémentaire, puis de transférer le contenu vers le framebuffer du `QOpenGLWidget` au moment opportun.
Partage de contexte
Lorsque plusieurs QOpenGLWidgets sont ajoutés en tant qu’enfants d’un même widget de niveau supérieur, leurs contextes sont partagés entre eux. Cela ne s’applique pas aux instances de QOpenGLWidget appartenant à des fenêtres différentes.
Cela signifie que tous les QOpenGLWidgets d’une même fenêtre peuvent accéder aux ressources partageables les uns des autres, comme les textures, et qu’il n’est pas nécessaire de disposer d’un contexte « global share » supplémentaire.
Pour configurer le partage entre des instances de `QOpenGLWidget` appartenant à des fenêtres différentes, définissez l’attribut d’application ` Qt::AA_ShareOpenGLContexts ` avant d’instancier ` QApplication`. Cela déclenchera le partage entre toutes les instances de `QOpenGLWidget` sans aucune autre étape.
Il est également possible de créer des instances supplémentaires de ` QOpenGLContext ` qui partagent des ressources, telles que des textures, avec le contexte de `QOpenGLWidget`. Il suffit de passer le pointeur renvoyé par ` context()` à ` QOpenGLContext::setShareContext()` avant d’appeler ` QOpenGLContext::create()`. Le contexte ainsi obtenu peut également être utilisé sur un autre thread, ce qui permet la génération de textures en multithread et le chargement asynchrone des textures.
Notez que QOpenGLWidget s’attend à une implémentation conforme aux normes en matière de partage des ressources de la part des pilotes graphiques sous-jacents. Par exemple, certains pilotes, notamment ceux destinés aux appareils mobiles et au matériel embarqué, rencontrent des problèmes lors de la configuration du partage entre un contexte existant et d’autres créés ultérieurement. D’autres pilotes peuvent se comporter de manière inattendue lorsqu’on tente d’utiliser des ressources partagées entre différents threads.
Initialisation et libération des ressources
Le contexte OpenGL associé à QOpenGLWidget est garanti d'être à jour chaque fois que les fonctions initializeGL() et paintGL() sont appelées. N'essayez pas de créer des ressources OpenGL avant l'appel de la fonction initializeGL(). Par exemple, toute tentative de compilation de shaders, d'initialisation d'objets de tampon de sommets ou de chargement de données de texture échouera si elle est effectuée dans le constructeur d'une sous-classe. Ces opérations doivent être reportées à l’appel de ` initializeGL()`. Certaines classes d’aide Qt OpenGL, telles que ` QOpenGLBuffer ` ou ` QOpenGLVertexArrayObject`, présentent un comportement différé similaire : elles peuvent être instanciées sans contexte, mais toute initialisation est reportée jusqu’à l’appel de ` create()` ou d’une fonction similaire. Cela signifie qu’elles peuvent être utilisées comme des variables membres normales (non pointeurs) dans une sous-classe de `QOpenGLWidget`, mais que la fonction ` create()` ou toute autre fonction similaire ne peut être appelée qu’à partir de ` initializeGL()`. Sachez toutefois que toutes les classes ne sont pas conçues de cette manière. En cas de doute, définissez la variable membre comme un pointeur et créez et détruisez l’instance de manière dynamique respectivement dans ` initializeGL()` et dans le destructeur.
La libération des ressources nécessite également que le contexte soit à jour. Par conséquent, les destructeurs chargés de ce nettoyage doivent appeler ` makeCurrent()` avant de procéder à la destruction des ressources OpenGL ou des wrappers. Évitez la suppression différée via ` deleteLater()` ou le mécanisme de parenté de ` QObject`. Il n'y a aucune garantie que le contexte correct soit à jour au moment où l'instance en question sera réellement détruite.
Une sous-classe typique ressemblera donc souvent à ce qui suit en matière d’initialisation et de destruction des ressources :
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(...);
...
}Cela fonctionne dans la plupart des cas, mais n’est pas tout à fait idéal en tant que solution générique. Lorsque le widget est réaffecté à un parent de sorte qu’il se retrouve dans une fenêtre de niveau supérieur entièrement différente, une mesure supplémentaire est nécessaire : en se connectant au signal ` aboutToBeDestroyed()` de ` QOpenGLContext`, le nettoyage peut être effectué chaque fois que le contexte OpenGL est sur le point d’être libéré.
Remarque : pour les widgets qui changent plusieurs fois de fenêtre de niveau supérieur associée au cours de leur cycle de vie, une approche de nettoyage combinée, comme illustrée dans l’extrait de code ci-dessous, est indispensable. Chaque fois que le widget ou l’un de ses parents est réaffecté à une nouvelle fenêtre de niveau supérieur, le contexte associé au widget est détruit et un nouveau est créé. Cette opération est ensuite suivie d’un appel à ` initializeGL()` où toutes les ressources OpenGL doivent être réinitialisées. De ce fait, la seule option pour effectuer un nettoyage correct consiste à se connecter au signal `aboutToBeDestroyed()` du contexte. Notez que le contexte en question peut ne pas être celui en cours lorsque le signal est émis. Il est donc recommandé d’appeler ` makeCurrent()` dans le slot connecté. De plus, les mêmes étapes de nettoyage doivent être effectuées depuis le destructeur de la classe dérivée, car le slot ou la fonction lambda connecté(e) au signal pourrait ne pas être appelé(e) lors de la destruction du widget.
MyGLWidget::~MyGLWidget()
{
cleanup();
}
void MyGLWidget::initializeGL()
{
...
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}
void MyGLWidget::cleanup()
{
makeCurrent();
delete m_texture;
m_texture = 0;
...
doneCurrent();
disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}Remarque : lorsque l’ Qt::AA_ShareOpenGLContexts est activé, le contexte du widget ne change jamais, même en cas de changement de parent, car la texture associée au widget restera accessible depuis le nouveau contexte de niveau supérieur. Par conséquent, il n’est pas obligatoire de traiter le signal aboutToBeDestroyed() du contexte lorsque ce drapeau est activé.
Un nettoyage adéquat est particulièrement important en raison du partage de contexte. Même si le contexte associé à chaque `QOpenGLWidget` est détruit en même temps que le `QOpenGLWidget`, les ressources partageables de ce contexte, telles que les textures, resteront valides jusqu’à ce que la fenêtre de niveau supérieur, dans laquelle résidait le `QOpenGLWidget`, soit détruite. De plus, des paramètres tels que ` Qt::AA_ShareOpenGLContexts ` et certains modules Qt peuvent élargir encore davantage la portée du partage des contextes, ce qui peut potentiellement entraîner le maintien en vie des ressources en question pendant toute la durée de vie de l’application. Par conséquent, la solution la plus sûre et la plus robuste consiste toujours à effectuer un nettoyage explicite de toutes les ressources et de tous les wrappers de ressources utilisés dans le `QOpenGLWidget`.
Limitations et autres considérations
Placer d’autres widgets en dessous et rendre le QOpenGLWidget transparent ne donnera pas les résultats escomptés : les widgets situés en dessous ne seront pas visibles. En effet, dans la pratique, le QOpenGLWidget est dessiné avant tous les autres widgets classiques (non OpenGL) ; les solutions de type « transparence » ne sont donc pas envisageables. D'autres types de mise en page, comme le fait de placer des widgets par-dessus le QOpenGLWidget, fonctionneront comme prévu.
En cas d’absolue nécessité, cette limitation peut être contournée en définissant l’attribut ` Qt::WA_AlwaysStackOnTop ` sur le `QOpenGLWidget`. Sachez toutefois que cela perturbe l’ordre d’empilement : par exemple, il ne sera plus possible de placer d’autres widgets au-dessus du QOpenGLWidget. Cette option ne doit donc être utilisée que dans les cas où un QOpenGLWidget semi-transparent laissant apparaître les widgets situés en dessous est requis.
Notez que cela ne s’applique pas lorsqu’il n’y a pas d’autres widgets en dessous et que l’objectif est d’obtenir une fenêtre semi-transparente. Dans ce cas, l’approche traditionnelle consistant à définir ` Qt::WA_TranslucentBackground ` sur la fenêtre de niveau supérieur est suffisante. Notez que si les zones transparentes ne sont souhaitées que dans le `QOpenGLWidget`, il faudra alors rétablir ` Qt::WA_NoSystemBackground ` sur ` false ` après avoir activé ` Qt::WA_TranslucentBackground`. De plus, il peut également être nécessaire de demander un canal alpha pour le contexte du `QOpenGLWidget` via ` setFormat()`, selon le système.
QOpenGLWidget prend en charge plusieurs comportements de mise à jour, tout comme QOpenGLWindow. En mode « preserved », le contenu rendu lors de l’appel précédent à paintGL() est disponible lors du suivant, ce qui permet un rendu incrémental. En mode « non preserved », le contenu est perdu et les implémentations de paintGL() doivent redessiner l’intégralité de la vue.
Avant Qt 5.5, le comportement par défaut de `QOpenGLWidget` consistait à conserver le contenu rendu entre les appels à ` paintGL()`. Depuis Qt 5.5, le comportement par défaut est le mode « non conservé », car cela offre de meilleures performances et la majorité des applications n’ont pas besoin du contenu précédent. Cela s’apparente également à la sémantique d’un « QWindow » basé sur OpenGL et correspond au comportement par défaut d’ QOpenGLWindow, dans la mesure où les tampons de couleur et auxiliaires sont invalidés à chaque image. Pour rétablir le comportement de conservation, appelez ` setUpdateBehavior()` avec ` PartialUpdate`.
Remarque : lors de l’ajout dynamique d’un QOpenGLWidget dans une hiérarchie de widgets, par exemple en associant un nouveau QOpenGLWidget à un widget dont le widget de niveau supérieur correspondant est déjà affiché à l’écran, la fenêtre native associée peut être implicitement détruite puis recréée si le QOpenGLWidget est le premier de ce type au sein de sa fenêtre. Cela s'explique par le fait que le type de fenêtre passe de ` RasterSurface ` à ` OpenGLSurface `, ce qui a des implications spécifiques à la plate-forme. Ce comportement est une nouveauté de Qt 6.4.
Une fois qu’un `QOpenGLWidget` est ajouté à une hiérarchie de widgets, le contenu de la fenêtre de niveau supérieur est mis à jour via un rendu basé sur OpenGL. Les widgets autres que le `QOpenGLWidget` continuent à dessiner leur contenu à l’aide d’un moteur de dessin logiciel, mais la composition finale s’effectue via l’API 3D.
Remarque : l’affichage d’un QOpenGLWidget nécessite un canal alpha dans le backing store de la fenêtre de niveau supérieur associée, en raison du fonctionnement de la composition avec d’autres contenus basés sur l’ QWidget. En l'absence de canal alpha, le contenu rendu par le QOpenGLWidget ne sera pas visible. Cela peut s'avérer particulièrement pertinent sous Linux/X11 dans les configurations d'affichage à distance (par exemple, avec Xvnc), lorsque l'on utilise une profondeur de couleur inférieure à 24. Par exemple, une profondeur de couleur de 16 correspond généralement à l’utilisation d’une image de mémoire de fond au format QImage::Format_RGB16 (RGB565), ce qui ne laisse aucune place pour un canal alpha. Par conséquent, si vous rencontrez des problèmes pour que le contenu d’un QOpenGLWidget s’intègre correctement avec les autres widgets de la fenêtre, assurez-vous que le serveur (tel que vncserver) est configuré avec une profondeur de 24 ou 32 bits au lieu de 16.
Alternatives
L’ajout d’un QOpenGLWidget dans une fenêtre active la composition basée sur OpenGL pour l’ensemble de la fenêtre. Dans certains cas particuliers, cela peut ne pas être idéal, et l’ancien comportement de type QGLWidget, avec une fenêtre enfant native distincte, peut être préférable. Les applications de bureau qui tiennent compte des limites de cette approche (par exemple en matière de chevauchements, de transparence, de vues défilantes et de zones MDI) peuvent utiliser ` QOpenGLWindow ` avec ` QWidget::createWindowContainer()`. Il s'agit d'une alternative moderne à `QGLWidget` et cette solution est plus rapide que `QOpenGLWidget` en raison de l'absence de l'étape de composition supplémentaire. Il est fortement recommandé de limiter l’utilisation de cette approche aux cas où il n’y a pas d’autre choix. Notez que cette option n’est pas adaptée à la plupart des plateformes embarquées et mobiles, et qu’elle présente également des problèmes sur certaines plateformes de bureau (par exemple macOS). La solution stable et multiplateforme reste toujours QOpenGLWidget.
Rendu stéréoscopique
À partir de la version 6.5, QOpenGLWidget prend en charge le rendu stéréoscopique. Pour l’activer, définissez globalement le drapeau QSurfaceFormat::StereoBuffers avant la création de la fenêtre, à l’aide de QSurfaceFormat::SetDefaultFormat().
Remarque : l’utilisation de ` setFormat()` ne fonctionnera pas nécessairement en raison de la manière dont ce drapeau est géré en interne.
Cela entraînera l’appel de ` paintGL()` deux fois par image, une fois pour chaque ` QOpenGLWidget::TargetBuffer`. Dans ` paintGL()`, appelez ` currentTargetBuffer()` pour déterminer sur lequel le dessin est en cours d’exécution.
Remarque : pour un contrôle accru sur les tampons de couleur gauche et droit, envisagez plutôt d’utiliser QOpenGLWindow + QWidget::createWindowContainer().
Remarque : ce type de rendu 3D impose certaines exigences matérielles ; par exemple, la carte graphique doit être configurée pour prendre en charge la stéréo.
OpenGL est une marque déposée de Silicon Graphics, Inc. aux États-Unis et dans d'autres pays.
Voir également QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts et UpdateBehavior.
Documentation sur les types de membres
[since 6.5] enum QOpenGLWidget::TargetBuffer
Spécifie le tampon à utiliser lorsque le rendu stéréoscopique est activé ; cette fonctionnalité s'active ou se désactive en définissant la variable ` QSurfaceFormat::StereoBuffers`.
Remarque : LeftBuffer est toujours la valeur par défaut et sert de valeur de repli lorsque le rendu stéréoscopique est désactivé ou n'est pas pris en charge par le pilote graphique.
| Constante | Valeur |
|---|---|
QOpenGLWidget::LeftBuffer | 0 |
QOpenGLWidget::RightBuffer | 1 |
Cette énumération a été introduite dans Qt 6.5.
enum QOpenGLWidget::UpdateBehavior
Cette énumération décrit la sémantique de mise à jour de QOpenGLWidget.
| Constante | Valeur | Description |
|---|---|---|
QOpenGLWidget::NoPartialUpdate | 0 | QOpenGLWidget effacera le contenu du tampon de couleur et des tampons auxiliaires une fois que l’ QOpenGLWidget aura été affiché à l’écran. Ce comportement est identique à celui obtenu en appelant ` QOpenGLContext::swapBuffers ` avec, en argument, un ` QWindow ` activé par défaut via OpenGL. NoPartialUpdate peut offrir certains avantages en termes de performances sur certaines architectures matérielles courantes dans le domaine mobile et embarqué lorsqu’un objet framebuffer est utilisé comme cible de rendu. L’objet framebuffer est invalidé entre les images à l’aide de glInvalidateFramebuffer (si pris en charge) ou, à défaut, de glDiscardFramebufferEXT (si pris en charge) ou d’un appel à glClear. |
QOpenGLWidget::PartialUpdate | 1 | Le tampon de couleurs et les tampons auxiliaires de l’objet framebuffer ne sont pas invalidés entre les images. |
Voir également ` updateBehavior()` et ` setUpdateBehavior()`.
Documentation des fonctions membres
[explicit] QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
Crée un widget qui est un enfant de ` parent`, avec les indicateurs de widget définis sur ` f`.
[virtual noexcept] QOpenGLWidget::~QOpenGLWidget()
Détruit l'instance de ` QOpenGLWidget `, libérant ainsi ses ressources.
Le contexte de l'QOpenGLWidget est rendu actif dans le destructeur, ce qui permet de détruire en toute sécurité tout objet enfant susceptible de devoir libérer des ressources OpenGL appartenant au contexte fourni par ce widget.
Avertissement : si vous avez des objets encapsulant des ressources OpenGL (tels que ` QOpenGLBuffer`, ` QOpenGLShaderProgram`, etc.) en tant que membres d’une sous-classe de `OpenGLWidget`, vous devrez peut-être également ajouter un appel à ` makeCurrent()` dans le destructeur de cette sous-classe. En raison des règles de destruction des objets en C++, ces objets seront détruits avant l'appel de cette fonction (mais après l'exécution du destructeur de la sous-classe) ; par conséquent, le fait de rendre le contexte OpenGL actif dans cette fonction intervient trop tard pour permettre leur destruction en toute sécurité.
Voir également makeCurrent.
[signal] void QOpenGLWidget::aboutToCompose()
Ce signal est émis lorsque la fenêtre de niveau supérieur du widget s'apprête à commencer à composer les textures de ses enfants de type « QOpenGLWidget » et des autres widgets.
[signal] void QOpenGLWidget::aboutToResize()
Ce signal est émis lorsque la taille du widget est modifiée et que, par conséquent, l'objet framebuffer va être recréé.
QOpenGLContext *QOpenGLWidget::context() const
Renvoie l'QOpenGLContext utilisé par ce widget ou 0 s'il n'est pas encore initialisé.
Remarque : le contexte et l’objet framebuffer utilisés par le widget changent lorsque celui-ci est replacé dans un nouveau contexte via setParent().
Voir également QOpenGLContext::setShareContext() et defaultFramebufferObject().
[since 6.5] QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const
Renvoie le tampon cible actuellement actif. Il s'agit par défaut du tampon de gauche ; le tampon de droite n'est utilisé que lorsque l'option « QSurfaceFormat::StereoBuffers » est activée. Lorsque le rendu stéréoscopique est activé, il est possible d'interroger la fonction ` paintGL()` pour savoir quel tampon est actuellement utilisé. La fonction ` paintGL()` sera appelée deux fois, une fois pour chaque cible.
Cette fonction a été introduite dans Qt 6.5.
Voir également paintGL().
GLuint QOpenGLWidget::defaultFramebufferObject() const
Renvoie le descripteur de l'objet framebuffer ou ` 0 ` s'il n'est pas encore initialisé.
Remarque : L'objet framebuffer appartient au contexte renvoyé par context() et peut ne pas être accessible depuis d'autres contextes.
Remarque : le contexte et l’objet framebuffer utilisés par le widget changent lorsque l’on modifie le parent du widget via setParent(). De plus, l’objet framebuffer change à chaque redimensionnement.
Voir aussi context().
[since 6.5] GLuint QOpenGLWidget::defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const
Renvoie le descripteur de l'objet framebuffer du tampon cible spécifié ou ` 0 ` si celui-ci n'est pas encore initialisé.
L'appel de cette surcharge n'a de sens que si la fonction ` QSurfaceFormat::StereoBuffers ` est activée et prise en charge par le matériel. Dans le cas contraire, cette méthode renvoie le tampon par défaut.
Remarque : l’objet framebuffer appartient au contexte renvoyé par ` context()` et peut ne pas être accessible depuis d’autres contextes. Le contexte et l’objet framebuffer utilisés par le widget changent lors du changement de parent du widget via ` setParent()`. De plus, l’objet framebuffer change à chaque redimensionnement.
Cette fonction a été introduite dans Qt 6.5.
Voir également ` context()`.
void QOpenGLWidget::doneCurrent()
Libère le contexte.
Dans la plupart des cas, il n'est pas nécessaire d'appeler cette fonction, car le widget veille à ce que le contexte soit correctement lié et libéré lors de l'appel de la fonction ` paintGL()`.
[override virtual protected] bool QOpenGLWidget::event(QEvent *e)
Réimplémente : QWidget::event(QEvent *event).
QSurfaceFormat QOpenGLWidget::format() const
Renvoie le contexte et le format de surface utilisés par ce widget et sa fenêtre de niveau supérieur.
Une fois que le widget et sa fenêtre de niveau supérieur ont été créés, redimensionnés et affichés, cette fonction renvoie le format réel du contexte. Celui-ci peut différer du format demandé si la plateforme n’a pas pu satisfaire la demande. Il est également possible d’obtenir des tailles de tampon de couleurs supérieures à celles demandées.
Lorsque la fenêtre du widget et les ressources OpenGL associées ne sont pas encore initialisées, la valeur renvoyée correspond au format défini via setFormat().
Voir également setFormat() et context().
[signal] void QOpenGLWidget::frameSwapped()
Ce signal est émis une fois que la fenêtre de niveau supérieur du widget a terminé son processus de composition et est revenue de son appel ` QOpenGLContext::swapBuffers()` (qui peut être bloquant).
QImage QOpenGLWidget::grabFramebuffer()
Rendu et retour d'une image RVB 32 bits du tampon d'image.
Remarque : il s'agit d'une opération potentiellement coûteuse, car elle s'appuie sur la fonction glReadPixels() pour relire les pixels. Cela peut être lent et bloquer le pipeline du GPU.
[since 6.5] QImage QOpenGLWidget::grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)
Rend et renvoie une image RVB 32 bits du tampon d'image du tampon cible spécifié. L'appel de cette surcharge n'a de sens que lorsque l'option « QSurfaceFormat::StereoBuffers » est activée. La capture du tampon d'image du tampon cible approprié renverra l'image par défaut si le rendu stéréoscopique est désactivé ou s'il n'est pas pris en charge par le matériel.
Remarque : il s'agit d'une opération potentiellement coûteuse en ressources, car elle s'appuie sur la fonction `glReadPixels()` pour lire les pixels. Cela peut être lent et bloquer le pipeline du GPU.
Cette fonction a été introduite dans Qt 6.5.
[virtual protected] void QOpenGLWidget::initializeGL()
Cette fonction virtuelle est appelée une fois avant le premier appel à ` paintGL()` ou ` resizeGL()`. Réimplémentez-la dans une sous-classe.
Cette fonction doit initialiser toutes les ressources OpenGL nécessaires.
Il n’est pas nécessaire d’appeler makeCurrent() car cela a déjà été fait lors de l’appel de cette fonction. Notez toutefois que le framebuffer n’est pas encore disponible à ce stade ; évitez donc d’émettre des appels de dessin à partir d’ici. Reportez plutôt ces appels à paintGL().
Voir également paintGL() et resizeGL().
bool QOpenGLWidget::isValid() const
Renvoie « true » si le widget et les ressources OpenGL, telles que le contexte, ont été initialisées avec succès. Notez que la valeur renvoyée est toujours « false » tant que le widget n'est pas affiché.
void QOpenGLWidget::makeCurrent()
Prépare le rendu du contenu OpenGL pour ce widget en activant le contexte correspondant et en associant l'objet framebuffer à ce contexte.
Il n'est pas nécessaire d'appeler cette fonction dans la plupart des cas, car elle est appelée automatiquement avant l'appel de ` paintGL()`.
Voir également context(), paintGL() et doneCurrent().
[since 6.5] void QOpenGLWidget::makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)
Prépare le rendu du contenu OpenGL pour ce widget en définissant comme contextuel le tampon transmis et en associant l’objet framebuffer à ce contexte.
Remarque : l'appelde cette fonction n'a de sens que lorsque le rendu stéréoscopique est activé. Rien ne se passera si le tampon de droite est demandé alors qu'il est désactivé.
Dans la plupart des cas, il n’est pas nécessaire d’appeler cette fonction, car elle est appelée automatiquement avant l’appel de ` paintGL()`.
Cette fonction a été introduite dans Qt 6.5.
Voir également context(), paintGL() et doneCurrent().
[override virtual protected] int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
Réimplémente : QWidget::metric(QPaintDevice::PaintDeviceMetric m) const.
[override virtual protected] QPaintEngine *QOpenGLWidget::paintEngine() const
Réimplémente : QWidget::paintEngine() const.
[override virtual protected] void QOpenGLWidget::paintEvent(QPaintEvent *e)
Réimplémente : QWidget::paintEvent(QPaintEvent *event).
Gère les événements de dessin.
L’appel de ` QWidget::update()` entraîne l’envoi d’un événement de dessin ` e`, ce qui provoque l’appel de cette fonction. (NB : cette opération est asynchrone et se produira à un moment donné après le retour de ` update()`). Cette fonction appellera ensuite, après quelques préparatifs, la fonction virtuelle ` paintGL()` afin de mettre à jour le contenu du tampon de trame de ` QOpenGLWidget`. La fenêtre de niveau supérieur du widget composera ensuite la texture du framebuffer avec le reste de la fenêtre.
[virtual protected] void QOpenGLWidget::paintGL()
Cette fonction virtuelle est appelée chaque fois que le widget doit être redessiné. Réimplémentez-la dans une sous-classe.
Il n'est pas nécessaire d'appeler ` makeCurrent()` car cela a déjà été fait lors de l'appel de cette fonction.
Avant l'appel de cette fonction, le contexte et le framebuffer sont liés, et la fenêtre d'affichage est configurée par un appel à glViewport(). Aucun autre état n'est défini et aucune opération de vidage ou de dessin n'est effectuée par le framework.
L’implémentation par défaut effectue un glClear(). Les sous-classes ne sont pas censées appeler l’implémentation de la classe de base et doivent effectuer l’effacement elles-mêmes.
Remarque : pour garantir la portabilité, ne comptez pas sur la persistance de l’état défini dans ` initializeGL()`. Définissez plutôt tous les états nécessaires, par exemple en appelant glEnable(), dans paintGL(). En effet, certaines plateformes, telles que WebAssembly avec WebGL, peuvent présenter des limitations concernant les contextes OpenGL dans certaines situations, ce qui peut conduire à ce que le contexte utilisé avec l’ QOpenGLWidget soit également utilisé à d’autres fins.
Lorsque l’option « QSurfaceFormat::StereoBuffers » est activée, cette fonction sera appelée deux fois : une fois pour chaque tampon. Vérifiez quel tampon est actuellement lié en appelant currentTargetBuffer().
Remarque : le framebuffer de chaque cible sera dessiné même si le rendu stéréoscopique n’est pas pris en charge par le matériel. Seul le tampon de gauche sera réellement visible dans la fenêtre.
Voir également initializeGL(), resizeGL() et currentTargetBuffer().
[override virtual protected] QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)
Réimplémente : QWidget::resizeEvent(QResizeEvent *event).
Gère les événements de redimensionnement transmis via le paramètre d'événement e. Appelle la fonction virtuelle resizeGL().
Remarque : évitez de redéfinir cette fonction dans les classes dérivées. Si cela n’est pas possible, assurez-vous que l’implémentation de ` QOpenGLWidget` soit également appelée. Sinon, l’objet framebuffer sous-jacent et les ressources associées ne seront pas redimensionnés correctement, ce qui entraînera un rendu incorrect.
[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)
Cette fonction virtuelle est appelée chaque fois que le widget est redimensionné. Réimplémentez-la dans une sous-classe. La nouvelle taille est transmise via les paramètres ` w ` et ` h`.
Il n'est pas nécessaire d'appeler makeCurrent() car cela a déjà été fait lors de l'appel de cette fonction. De plus, le framebuffer est également lié.
Voir également initializeGL() et paintGL().
[signal] void QOpenGLWidget::resized()
Ce signal est émis immédiatement après la recréation de l'objet framebuffer suite au redimensionnement du widget.
void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
Définit l'format de surface demandée.
Lorsque le format n’est pas explicitement défini via cette fonction, c’est le format renvoyé par ` QSurfaceFormat::defaultFormat()` qui sera utilisé. Cela signifie que, lorsque l’on dispose de plusieurs widgets OpenGL, les appels individuels à cette fonction peuvent être remplacés par un seul appel à ` QSurfaceFormat::setDefaultFormat()` avant la création du premier widget.
Remarque : demander un tampon alpha via cette fonction ne donnera pas les résultats escomptés si l’objectif est de rendre visibles les autres widgets situés en dessous. Utilisez plutôt ` Qt::WA_AlwaysStackOnTop ` pour activer des instances de ` QOpenGLWidget ` semi-transparentes, avec les autres widgets visibles en dessous. Gardez toutefois à l’esprit que cela modifie l’ordre d’empilement ; il ne sera donc plus possible d’avoir d’autres widgets par-dessus les ` QOpenGLWidget`.
Voir également format(), Qt::WA_AlwaysStackOnTop et QSurfaceFormat::setDefaultFormat().
void QOpenGLWidget::setTextureFormat(GLenum texFormat)
Définit un format de texture interne personnalisé de type « texFormat ».
Lorsque vous travaillez avec des framebuffers sRGB, il est nécessaire de spécifier un format tel que GL_SRGB8_ALPHA8. Pour ce faire, il suffit d’appeler cette fonction.
Remarque : cette fonction n'a aucun effet si elle est appelée après l'affichage du widget, celui-ci ayant alors déjà effectué son initialisation.
Remarque : cette fonction doit généralement être utilisée en combinaison avec un appel à ` QSurfaceFormat::setColorSpace()` qui définit l’espace colorimétrique sur ` QColorSpace::SRgb`.
Voir également textureFormat().
void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)
Définit le comportement de mise à jour de ce widget sur « updateBehavior ».
Voir également updateBehavior().
GLenum QOpenGLWidget::textureFormat() const
Renvoie le format de texture interne actif si le widget a déjà été initialisé, le format demandé s’il a été défini mais que le widget n’a pas encore été rendu visible, ou ` nullptr ` si la fonction ` setTextureFormat()` n’a pas été appelée et que le widget n’a pas encore été rendu visible.
Voir également ` setTextureFormat()`.
QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
Renvoie le comportement de mise à jour du widget.
Voir aussi ` setUpdateBehavior()`.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.