QOpenGLWidget Class
La classe QOpenGLWidget est un widget pour le rendu des graphiques OpenGL. Plus d'informations...
| En-tête : | #include <QOpenGLWidget> |
| CMake : | find_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets)target_link_libraries(mytarget PRIVATE Qt6::OpenGLWidgets) |
| qmake : | QT += openglwidgets |
| Héritages : | 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 une fonctionnalité permettant d'afficher des graphiques OpenGL intégrés dans une application Qt. Il est très simple à utiliser : Faites en sorte que votre classe en hérite et utilisez la sous-classe comme n'importe quelle autre QWidget, sauf que vous avez le choix entre l'utilisation de 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 typiques :
- paintGL() - Rend la scène OpenGL. Elle est appelée chaque fois que le widget doit être mis à jour.
- resizeGL() - Configure le viewport OpenGL, la projection, etc. Est appelé chaque fois que le widget a été redimensionné (et également lorsqu'il est affiché pour la première fois, car tous les widgets nouvellement créés reçoivent automatiquement un événement de redimensionnement).
- initializeGL() - Configure les ressources et l'état OpenGL. Elle est appelée une fois avant le premier appel à resizeGL() ou paintGL().
Si vous devez déclencher un repeint à 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 programmer une mise à jour.
Le contexte de rendu OpenGL de votre widget est mis à jour 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 peinture), vous devez d'abord appeler makeCurrent().
Tous les rendus sont effectués dans un objet framebuffer OpenGL. makeCurrent L'appel à () permet de s'assurer 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 paintGL(). Ne reliez jamais le framebuffer avec l'ID 0. Au lieu de cela, appelez defaultFramebufferObject() pour obtenir l'ID qui doit être lié.
QOpenGLWidget permet d'utiliser différentes versions et profils OpenGL lorsque la plateforme le supporte. Il suffit de définir le format requis via setFormat(). Gardez cependant à l'esprit qu'avoir plusieurs instances de QOpenGLWidget dans la 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 surmonter ce problème, préférez utiliser QSurfaceFormat::setDefaultFormat() au lieu de setFormat().
Note : L'appel à 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 s'assurer que le partage des ressources entre les contextes reste fonctionnel car tous les contextes internes sont créés en utilisant la bonne version et le bon profil.
Techniques de peinture
Comme décrit ci-dessus, sous-classez QOpenGLWidget pour rendre du contenu 3D pur de la manière suivante :
- Réimplémenter les fonctions initializeGL() et resizeGL() pour configurer l'état OpenGL et fournir une transformation de perspective.
- Réimplémenter paintGL() pour peindre la scène 3D, en appelant uniquement les fonctions OpenGL.
Il est également possible de dessiner des graphiques 2D sur une sous-classe de QOpenGLWidget en utilisant QPainter:
- Dans paintGL(), au lieu d'émettre des commandes OpenGL, construisez un objet QPainter à utiliser sur le widget.
- Dessinez des primitives en utilisant les fonctions membres de QPainter.
- Les commandes OpenGL directes peuvent toujours être émises. Cependant, vous devez vous assurer qu'elles sont entourées d'un appel aux fonctions beginNativePainting() et endNativePainting() du peintre.
Lorsque le dessin est effectué uniquement à l'aide de QPainter, il est également possible d'effectuer la peinture comme pour les widgets ordinaires : en réimplémentant paintEvent().
- Réimplémentez la fonction paintEvent().
- Construire un objet QPainter ciblant le widget. Passez le widget au constructeur ou à la fonction QPainter::begin().
- Dessiner des primitives à l'aide des fonctions membres de QPainter.
- La peinture est terminée et l'instance QPainter est détruite. Il est également possible d'appeler QPainter::end() explicitement.
Appels de fonctions OpenGL, en-têtes et QOpenGLFunctions
Lors des appels de fonctions OpenGL, il est fortement recommandé d'éviter d'appeler les fonctions directement. A la place, préférez utiliser QOpenGLFunctions (lors de la création d'applications portables) ou les variantes versionnées (par exemple, QOpenGLFunctions_3_2_Core et similaires, lors de l'utilisation d'OpenGL moderne, uniquement pour les ordinateurs de bureau). De cette manière, l'application fonctionnera correctement dans toutes les configurations de compilation de Qt, y compris celles qui effectuent un chargement dynamique de l'implémentation OpenGL, ce qui signifie que les applications ne sont pas directement liées à une implémentation GL et que les appels directs de fonctions ne sont donc pas réalisables.
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 à être utilisée peut être récupérée en appelant QOpenGLContext::functions(). Une alternative au préfixage de chaque appel GL est d'hériter de QOpenGLFunctions et d'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 comme GL.h. Les en-têtes Qt liés à OpenGL incluront qopengl.h qui inclura à son tour un en-tête approprié pour le système. Cela peut être un en-tête OpenGL ES 3.x ou 2.0, la plus haute version disponible, ou un gl.h fourni par le système. De plus, une copie des en-têtes d'extension (appelée glext.h sur certains systèmes) est fournie dans Qt à la fois pour OpenGL et OpenGL ES. Ceux-ci seront inclus automatiquement sur les plates-formes où cela est possible. Cela signifie que les constantes et les typedefs de pointeurs de fonctions des extensions ARB, EXT, OES sont automatiquement disponibles.
Exemples de code
Pour commencer, la sous-classe la plus simple de QOpenGLWidget pourrait ressembler à ce qui suit :
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); ... } };
Alternativement, le préfixage de chaque appel OpenGL peut être évité en dérivant de 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 les 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 shown
Note : Il appartient à l'application de s'assurer que les tampons de profondeur et de stencil sont demandés à l'interface du système de fenêtrage sous-jacent. Sans demander une taille de tampon de profondeur non nulle, il n'y a aucune garantie qu'un tampon de profondeur sera disponible, et par conséquent les opérations OpenGL liées au test de profondeur peuvent ne pas fonctionner comme prévu. Les tailles de tampon de profondeur et de stencil couramment demandées sont respectivement 24 et 8.
Dans les contextes OpenGL 3.0+, lorsque la portabilité n'est pas importante, les variantes versionnées de QOpenGLFunctions donnent un accès facile à toutes les fonctions modernes d'OpenGL 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 robuste de définir le format demandé de manière globale afin qu'il s'applique à toutes les fenêtres et à tous les contextes pendant 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(); }
Multi-échantillonnage
Pour activer le multi-échantillonnage, définissez le nombre d'échantillons demandés dans le fichier QSurfaceFormat transmis à setFormat(). Sur les systèmes qui ne le supportent pas, la demande peut être ignorée.
La prise en charge du multi-échantillonnage nécessite la prise en charge des renderbuffers multi-échantillonnés et des blits de framebuffer. Sur les implémentations d'OpenGL ES 2.0, il est probable que ces éléments ne soient pas présents. Cela signifie que le multi-échantillonnage ne sera pas disponible. Avec les versions modernes d'OpenGL et OpenGL ES 3.0 et plus, ce n'est généralement plus un problème.
Le threading
Effectuer un rendu hors écran sur des threads de travail, par exemple pour générer des textures qui sont ensuite utilisées dans le GUI/main thread dans paintGL(), est supporté en exposant le widget QOpenGLContext de sorte que des contextes additionnels le partageant puissent être créés sur chaque thread.
Dessiner directement dans le framebuffer du QOpenGLWidget en dehors du GUI/thread principal est possible en réimplémentant paintEvent() pour ne rien faire. L'affinité du contexte avec le thread doit être modifiée via QObject::moveToThread(). Après cela, makeCurrent() et doneCurrent() sont utilisables sur le thread du travailleur. Veillez à replacer le contexte dans le thread GUI/principal par la suite.
Déclencher un échange de tampon uniquement pour le QOpenGLWidget n'est pas possible puisqu'il n'y a pas de surface native réelle à l'écran pour lui. C'est à la pile du widget de gérer la composition et les changements de tampon sur le thread de l'interface graphique. Lorsqu'un thread a fini de mettre à jour le framebuffer, appelez update() sur le thread principal de l'interface graphique pour planifier la composition.
Des précautions supplémentaires doivent être prises pour éviter d'utiliser le framebuffer lorsque le thread GUI/main est en train d'effectuer la composition. Les signaux aboutToCompose() et frameSwapped() sont émis au début et à la fin de la composition. Ils sont émis sur le fil d'exécution principal de l'interface graphique. Cela signifie qu'en utilisant une connexion directe, aboutToCompose() peut bloquer le fil d'exécution principal de l'interface graphique jusqu'à ce que le fil d'exécution de l'assistant ait terminé son rendu. Après cela, le fil d'exécution du travailleur ne doit plus effectuer de rendu jusqu'à ce que le signal frameSwapped() soit émis. Si cela n'est pas acceptable, le fil d'exécution doit mettre en œuvre un mécanisme de double mise en mémoire tampon. Cela implique de dessiner en utilisant une cible de rendu alternative, qui est entièrement contrôlée par le thread, par exemple un objet framebuffer supplémentaire, et de blitter dans le framebuffer de QOpenGLWidget à un moment approprié.
Partage du contexte
Lorsque plusieurs QOpenGLWidgets sont ajoutés en tant qu'enfants au même widget de niveau supérieur, leurs contextes seront partagés les uns avec les autres. Cela ne s'applique pas aux instances de QOpenGLWidget qui appartiennent à des fenêtres différentes.
Cela signifie que tous les QOpenGLWidgets de la même fenêtre peuvent accéder aux ressources partageables des autres, comme les textures, et qu'il n'y a pas besoin d'un contexte supplémentaire de "partage global".
Pour mettre en place le partage entre les 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 QOpenGLContext supplémentaires qui partagent des ressources comme les textures avec le contexte du QOpenGLWidget. Il suffit de passer le pointeur retourné par context() à QOpenGLContext::setShareContext() avant d'appeler QOpenGLContext::create(). Le contexte résultant peut également être utilisé dans un autre thread, ce qui permet de générer des textures de manière threadée et de télécharger des textures de manière asynchrone.
Notez que QOpenGLWidget s'attend à une implémentation standard du partage des ressources en ce qui concerne les pilotes graphiques sous-jacents. Par exemple, certains pilotes, en particulier pour le matériel mobile et embarqué, ont des problèmes avec la mise en place du partage entre un contexte existant et d'autres qui sont créés plus tard. D'autres pilotes peuvent se comporter de manière inattendue lorsqu'ils tentent d'utiliser des ressources partagées entre différents threads.
Initialisation et nettoyage des ressources
Le contexte OpenGL associé au QOpenGLWidget est garanti d'être à jour lorsque initializeGL() et paintGL() sont invoqués. N'essayez pas de créer des ressources OpenGL avant que initializeGL() ne soit appelé. Par exemple, tenter de compiler des shaders, d'initialiser des objets tampons de vertex ou de télécharger des données de texture échouera si cela est fait dans le constructeur d'une sous-classe. Ces opérations doivent être reportées à initializeGL(). Certaines des classes d'aide OpenGL de Qt, comme QOpenGLBuffer ou QOpenGLVertexArrayObject, ont un comportement différé similaire : elles peuvent être instanciées sans contexte, mais toute l'initialisation est différée jusqu'à un appel à create(), ou similaire. Cela signifie qu'elles peuvent être utilisées comme des variables membres normales (sans pointeur) dans une sous-classe de QOpenGLWidget, mais la fonction create() ou similaire ne peut être appelée qu'à partir de initializeGL(). Sachez cependant que toutes les classes ne sont pas conçues de cette manière. En cas de doute, faites de la variable membre un pointeur et créez et détruisez l'instance dynamiquement dans initializeGL() et le destructeur, respectivement.
La libération des ressources nécessite également que le contexte soit à jour. Par conséquent, les destructeurs qui effectuent un tel nettoyage doivent appeler makeCurrent() avant de détruire les ressources OpenGL ou les wrappers. Évitez la suppression différée via deleteLater() ou le mécanisme de parentage de QObject. Il n'y a aucune garantie que le contexte correct sera courant au moment où l'instance en question est réellement détruite.
Une sous-classe typique ressemblera donc souvent à ce qui suit en ce qui concerne l'initialisation et la 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 ce n'est pas une solution générique idéale. Lorsque le widget est réparti de telle sorte qu'il se retrouve dans une fenêtre de niveau supérieur complètement différente, quelque chose de plus est nécessaire : en se connectant au signal aboutToBeDestroyed() de QOpenGLContext, un nettoyage peut être effectué chaque fois que le contexte OpenGL est sur le point d'être libéré.
Note : Pour les widgets qui changent de fenêtre de premier niveau plusieurs fois au cours de leur vie, une approche de nettoyage combinée, comme le montre l'extrait de code ci-dessous, est essentielle. Chaque fois que le widget ou l'un de ses parents est reparamétré de sorte que la fenêtre de premier niveau devient différente, le contexte associé au widget est détruit et un nouveau contexte est créé. Ceci est suivi d'un appel à initializeGL() où toutes les ressources OpenGL doivent être réinitialisées. Pour cette raison, la seule option pour effectuer un nettoyage correct est de se connecter au signal aboutToBeDestroyed() du contexte. Notez que le contexte en question peut ne pas être le contexte actuel lorsque le signal est émis. C'est pourquoi il est bon d'appeler makeCurrent() dans le slot connecté. En outre, les mêmes étapes de nettoyage doivent être effectuées à partir du destructeur de la classe dérivée, puisque le slot ou lambda connecté au signal peut ne pas être invoqué 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 Qt::AA_ShareOpenGLContexts est défini, le contexte du widget ne change jamais, même lors du reparentage, car la texture associée au widget sera également accessible à partir du contexte du nouveau niveau supérieur. Par conséquent, agir sur le signal aboutToBeDestroyed() du contexte n'est pas obligatoire lorsque ce drapeau est activé.
Un nettoyage correct est particulièrement important en raison du partage du contexte. Même si le contexte associé à chaque QOpenGLWidget est détruit en même temps que le QOpenGLWidget, les ressources partageables dans ce contexte, comme les textures, resteront valides jusqu'à ce que la fenêtre de niveau supérieur, dans laquelle se trouvait le QOpenGLWidget, soit détruite. En outre, des paramètres tels que Qt::AA_ShareOpenGLContexts et certains modules Qt peuvent déclencher une portée encore plus large pour le partage des contextes, conduisant potentiellement à garder les ressources en question en vie pendant toute la durée de vie de l'application. Par conséquent, le plus sûr et le plus robuste est toujours d'effectuer un nettoyage explicite pour toutes les ressources et les enveloppes de ressources utilisées 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 non-OpenGL, et les solutions de type transparent ne sont donc pas réalisables. D'autres types de dispositions, comme avoir des widgets au-dessus du QOpenGLWidget, fonctionneront comme prévu.
Lorsque cela est absolument nécessaire, cette limitation peut être surmontée en définissant l'attribut Qt::WA_AlwaysStackOnTop sur le QOpenGLWidget. Sachez cependant que cela rompt l'ordre d'empilement, par exemple il ne sera pas possible d'avoir d'autres widgets au dessus du QOpenGLWidget, donc cela ne devrait être utilisé que dans des situations où un QOpenGLWidget semi-transparent avec d'autres widgets visibles en dessous est nécessaire.
Notez que cela ne s'applique pas lorsqu'il n'y a pas d'autres widgets en dessous et que l'intention est d'avoir 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, alors Qt::WA_NoSystemBackground devra être ramené à false après avoir activé Qt::WA_TranslucentBackground. De plus, demander un canal alpha pour le contexte de QOpenGLWidget via setFormat() peut aussi être nécessaire, selon le système.
QOpenGLWidget supporte plusieurs comportements de mise à jour, tout comme QOpenGLWindow. En mode préservé, le contenu rendu lors de l'appel précédent à paintGL() est disponible lors de l'appel suivant, ce qui permet un rendu incrémental. En mode non préservé, le contenu est perdu et les implémentations de paintGL() doivent redessiner tout ce qui se trouve dans la vue.
Avant Qt 5.5, le comportement par défaut de QOpenGLWidget était de préserver le contenu du rendu entre les appels à paintGL(). Depuis Qt 5.5, le comportement par défaut est la non-conservation car cela permet de meilleures performances et la majorité des applications n'ont pas besoin du contenu précédent. Cela ressemble également à la sémantique d'un QWindow basé sur OpenGL et correspond au comportement par défaut de QOpenGLWindow dans la mesure où les tampons de couleur et les tampons auxiliaires sont invalidés pour chaque image. Pour rétablir le comportement préservé, appelez setUpdateBehavior() avec PartialUpdate.
Note : Lors de l'ajout dynamique d'un QOpenGLWidget dans une hiérarchie de widgets, par exemple en parentant un nouveau QOpenGLWidget à un widget où le widget de niveau supérieur correspondant est déjà affiché à l'écran, la fenêtre native associée peut être implicitement détruite et recréée si le QOpenGLWidget est le premier de son genre à l'intérieur de sa fenêtre. C'est parce que le type de fenêtre change de RasterSurface à OpenGLSurface et que cela a des implications spécifiques à la plateforme. Ce comportement est nouveau dans 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 nettoyé via un rendu basé sur l'OpenGL. Les widgets autres que le QOpenGLWidget continuent à dessiner leur contenu en utilisant un peintre logiciel, mais la composition finale est réalisée par l'API 3D.
Note : L'affichage d'un QOpenGLWidget nécessite un canal alpha dans le backing store de la fenêtre de premier niveau associée, en raison de la manière dont fonctionne la composition avec d'autres contenus basés sur QWidget. Si aucun canal alpha n'est présent, la composition finale se fait via l'API 3D. S'il n'y a pas de canal alpha, le contenu rendu par le QOpenGLWidget ne sera pas visible. Cela peut devenir particulièrement important sous Linux/X11 dans les configurations d'affichage à distance (comme avec Xvnc), lors de l'utilisation d'une profondeur de couleur inférieure à 24. Par exemple, une profondeur de couleur de 16 correspondra typiquement à l'utilisation d'une image de sauvegarde au format QImage::Format_RGB16 (RGB565), ne laissant pas de place pour un canal alpha. Par conséquent, si vous rencontrez des problèmes pour obtenir le contenu d'un QOpenGLWidget correctement composé avec d'autres widgets dans 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 OpenGL pour l'ensemble de la fenêtre. Dans certains cas particuliers, cela peut ne pas être idéal, et le comportement de l'ancien QGLWidget avec une fenêtre enfant séparée et native est souhaité. Les applications de bureau qui comprennent les limites de cette approche (par exemple en ce qui concerne les chevauchements, la transparence, les vues déroulantes et les zones MDI), peuvent utiliser QOpenGLWindow avec QWidget::createWindowContainer(). C'est une alternative moderne à QGLWidget et elle est plus rapide que QOpenGLWidget en raison de l'absence d'é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 est connue pour avoir des problèmes sur certaines plateformes de bureau (par exemple macOS). La solution stable et multiplateforme est toujours QOpenGLWidget.
Rendu stéréoscopique
À partir de la version 6.5, QOpenGLWidget supporte le rendu stéréoscopique. Pour l'activer, positionnez le drapeau QSurfaceFormat::StereoBuffers globalement avant la création de la fenêtre, en utilisant QSurfaceFormat::SetDefaultFormat().
Remarque : l'utilisation de setFormat() ne fonctionnera pas nécessairement en raison de la manière dont l'indicateur est géré en interne.
Cela déclenchera l'appel de paintGL() deux fois par image, une fois pour chaque QOpenGLWidget::TargetBuffer. Dans paintGL(), appelez currentTargetBuffer() pour demander quel est le dessin en cours.
Remarque : pour mieux contrôler les tampons de couleur gauche et droit, il est préférable d'utiliser QOpenGLWindow + QWidget::createWindowContainer().
Note : Ce type de rendu 3D a certaines exigences matérielles, comme la carte graphique qui doit être configurée avec le support de la stéréo.
OpenGL est une marque déposée de Silicon Graphics, Inc. aux États-Unis et dans d'autres pays.
Voir aussi QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts, et UpdateBehavior.
Type de membre Documentation
[since 6.5] enum QOpenGLWidget::TargetBuffer
Spécifie le tampon à utiliser lorsque le rendu stéréoscopique est activé, ce qui est possible grâce au paramètre QSurfaceFormat::StereoBuffers.
Remarque : LeftBuffer est toujours la valeur par défaut et est utilisée comme 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 liste 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 L'enum permet d'ignorer le contenu du tampon de couleur et des tampons auxiliaires après que QOpenGLWidget a été rendu à l'écran. Il s'agit du même comportement que celui auquel on peut s'attendre en appelant QOpenGLContext::swapBuffers avec, comme argument, une page QWindow activée par défaut pour Opengl. NoPartialUpdate peut offrir des avantages en termes de performances sur certaines architectures matérielles courantes dans l'espace mobile et embarqué lorsqu'un objet framebuffer est utilisé comme cible de rendu. L'objet framebuffer est invalidé entre les images avec glInvalidateFramebuffer (si supporté), ou, comme fallback, glDiscardFramebufferEXT (si supporté) ou un appel à glClear. |
QOpenGLWidget::PartialUpdate | 1 | Les objets framebuffer color buffer et ancillary buffers 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())
Construit un widget qui est un enfant de parent, avec les drapeaux de widget fixés à f.
[virtual noexcept] QOpenGLWidget::~QOpenGLWidget()
Détruit l'instance QOpenGLWidget, en libérant ses ressources.
Le contexte de QOpenGLWidget est mis à jour dans le destructeur, permettant une destruction sûre de tout objet enfant qui pourrait avoir besoin de libérer les ressources OpenGL appartenant au contexte fourni par ce widget.
Attention : si vous avez des objets enveloppant des ressources OpenGL (tels que QOpenGLBuffer, QOpenGLShaderProgram, etc.) comme membres d'une sous-classe d'OpenGLWidget, vous pouvez avoir besoin d'ajouter un appel à makeCurrent() dans le destructeur de cette sous-classe également. En raison des règles de destruction des objets C++, ces objets seront détruits avant d' appeler cette fonction (mais après que le destructeur de la sous-classe se soit exécuté), donc rendre le contexte OpenGL courant dans cette fonction arrive trop tard pour leur permettre de se débarrasser d'eux en toute sécurité.
Voir aussi makeCurrent.
[signal] void QOpenGLWidget::aboutToCompose()
Ce signal est émis lorsque la fenêtre de premier niveau du widget est sur le point de commencer à composer les textures de ses enfants QOpenGLWidget et des autres widgets.
[signal] void QOpenGLWidget::aboutToResize()
Ce signal est émis lorsque la taille du widget est modifiée et que l'objet framebuffer doit être recréé.
QOpenGLContext *QOpenGLWidget::context() const
Renvoie l'adresse QOpenGLContext utilisée par ce widget ou 0 si elle n'a pas encore été initialisée.
Remarque : Le contexte et l'objet framebuffer utilisés par le widget changent lors du reparentage du widget via setParent().
Voir aussi QOpenGLContext::setShareContext() et defaultFramebufferObject().
[since 6.5] QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const
Renvoie le tampon cible actuellement actif. Par défaut, il s'agit du tampon de gauche, le tampon de droite n'étant utilisé que lorsque QSurfaceFormat::StereoBuffers est activé. Lorsque le rendu stéréoscopique est activé, il peut être interrogé dans paintGL() pour savoir quel tampon est actuellement utilisé. paintGL() sera appelée deux fois, une fois pour chaque cible.
Cette fonction a été introduite dans Qt 6.5.
Voir aussi paintGL().
GLuint QOpenGLWidget::defaultFramebufferObject() const
Retourne le handle de l'objet framebuffer ou 0 s'il n'a pas encore été initialisé.
Remarque : L'objet framebuffer appartient au contexte retourné par context() et ne peut pas être accessible à partir d'autres contextes.
Remarque : le contexte et l'objet framebuffer utilisés par le widget changent lors du reparentage du widget via setParent(). De plus, l'objet framebuffer change à chaque redimensionnement.
Voir également context().
[since 6.5] GLuint QOpenGLWidget::defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const
Renvoie le gestionnaire de l'objet framebuffer du tampon cible spécifié ou 0 s'il n'a pas encore été initialisé.
L'appel à cette surcharge n'a de sens que si QSurfaceFormat::StereoBuffers est activé et pris en charge par le matériel. Si ce n'est pas le cas, 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 à partir d'autres contextes. Le contexte et l'objet framebuffer utilisés par le widget changent lors du reparentage du widget via setParent(). En outre, 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.
Il n'est pas nécessaire d'appeler cette fonction dans la plupart des cas, car le widget s'assurera que le contexte est lié et libéré correctement lorsqu'il invoquera 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 être différent du format demandé si la plateforme n'a pas pu répondre à la demande. Il est également possible d'obtenir des tailles de tampon de couleur plus grandes que celles demandées.
Lorsque la fenêtre du widget et les ressources OpenGL associées ne sont pas encore initialisées, la valeur de retour est le format qui a été défini via setFormat().
Voir aussi setFormat() et context().
[signal] void QOpenGLWidget::frameSwapped()
Ce signal est émis après que la fenêtre de premier niveau du widget a terminé sa composition et est revenue de son appel potentiellement bloquant QOpenGLContext::swapBuffers().
QImage QOpenGLWidget::grabFramebuffer()
Rend et renvoie une image RVB 32 bits du framebuffer.
Remarque : il s'agit d'une opération potentiellement coûteuse car elle repose sur glReadPixels() pour lire les pixels. Cette opération peut être lente et bloquer le pipeline du GPU.
[since 6.5] QImage QOpenGLWidget::grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)
Rend et renvoie une image RVB 32 bits du framebuffer du tampon cible spécifié. Cette surcharge n'a de sens que si QSurfaceFormat::StereoBuffers est activé. La saisie du framebuffer du bon tampon cible 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 car elle s'appuie sur glReadPixels() pour relire les pixels. Cette opération peut être lente et peut 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 mettre en place toutes les ressources OpenGL nécessaires.
Il n'est pas nécessaire d'appeler makeCurrent() car cela a déjà été fait lorsque cette fonction est appelée. Notez cependant 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
Retourne vrai si le widget et les ressources OpenGL, comme le contexte, ont été initialisés avec succès. Notez que la valeur de retour est toujours fausse jusqu'à ce que le widget soit affiché.
void QOpenGLWidget::makeCurrent()
Prépare le rendu du contenu OpenGL pour ce widget en rendant le contexte correspondant actuel et en liant l'objet framebuffer dans ce contexte.
Il n'est pas nécessaire d'appeler cette fonction dans la plupart des cas, car elle est appelée automatiquement avant d'invoquer paintGL().
Voir aussi context(), paintGL() et doneCurrent().
[since 6.5] void QOpenGLWidget::makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)
Prépare le rendu du contenu OpenGL pour ce widget en rendant le contexte du tampon passé courant et en liant l'objet framebuffer dans ce contexte.
Note : Ceci n'a de sens que lorsque le rendu stéréoscopique est activé. Il ne se passera rien si le bon tampon est demandé alors qu'il est désactivé.
Il n'est pas nécessaire d'appeler cette fonction dans la plupart des cas, car elle est appelée automatiquement avant d'invoquer paintGL().
Cette fonction a été introduite dans Qt 6.5.
Voir aussi 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 peinture.
L'appel à QWidget::update() entraînera l'envoi d'un événement de peinture à e, et donc l'invocation de cette fonction. (NB : ceci est asynchrone et se produira à un moment donné après le retour de update()). Après une certaine préparation, cette fonction appellera la fonction virtuelle paintGL() pour mettre à jour le contenu du framebuffer de QOpenGLWidget. La fenêtre de premier niveau du widget composera alors 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 peint. Réimplémentez-la dans une sous-classe.
Il n'est pas nécessaire d'appeler makeCurrent() car cela a déjà été fait lorsque cette fonction est appelée.
Avant d'invoquer cette fonction, le contexte et le framebuffer sont liés, et le viewport est configuré par un appel à glViewport(). Aucun autre état n'est défini et aucun effacement ou dessin n'est effectué par le cadre.
L'implémentation par défaut effectue un glClear(). Les sous-classes ne sont pas censées invoquer l'implémentation de la classe de base et doivent procéder elles-mêmes à l'effacement.
Remarque : pour assurer la portabilité, ne vous attendez pas à ce que l'état défini dans initializeGL() persiste. Il faut plutôt définir tous les états nécessaires, par exemple en appelant glEnable(), dans paintGL(). En effet, certaines plateformes, telles que WebAssembly avec WebGL, peuvent avoir des limitations sur les contextes OpenGL dans certaines situations, ce qui peut conduire à l'utilisation du contexte utilisé avec QOpenGLWidget à d'autres fins.
Lorsque QSurfaceFormat::StereoBuffers est activé, cette fonction sera appelée deux fois - une fois pour chaque tampon. Pour savoir quel tampon est actuellement lié, appelez 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 gauche sera 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 qui sont passés dans le paramètre e event. Appelle la fonction virtuelle resizeGL().
Remarque : évitez de surcharger cette fonction dans les classes dérivées. Si ce n'est pas possible, assurez-vous que l'implémentation de QOpenGLWidget est également invoquée. Dans le cas contraire, 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 a été redimensionné. Réimplémentez-la dans une sous-classe. La nouvelle taille est transmise dans w et h.
Il n'est pas nécessaire d'appeler makeCurrent() car cela a déjà été fait lorsque cette fonction est appelée. De plus, le framebuffer est également lié.
Voir également initializeGL() et paintGL().
[signal] void QOpenGLWidget::resized()
Ce signal est émis juste après que l'objet framebuffer a été recréé suite au redimensionnement du widget.
void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
Définit la surface demandée format.
Lorsque le format n'est pas explicitement défini par cette fonction, le format retourné par QSurfaceFormat::defaultFormat() sera utilisé. Cela signifie que lorsqu'il y a plusieurs widgets OpenGL, les appels individuels à cette fonction peuvent être remplacés par un seul appel à QSurfaceFormat::setDefaultFormat() avant de créer le premier widget.
Note : Demander un tampon alpha via cette fonction ne conduira pas aux résultats souhaités si l'intention est de rendre les autres widgets visibles en dessous. Utilisez plutôt Qt::WA_AlwaysStackOnTop pour activer des instances QOpenGLWidget semi-transparentes avec d'autres widgets visibles en dessous. Gardez toutefois à l'esprit que cela rompt l'ordre d'empilement et qu'il ne sera donc plus possible d'avoir d'autres widgets au-dessus de l'instance QOpenGLWidget.
Voir aussi format(), Qt::WA_AlwaysStackOnTop, et QSurfaceFormat::setDefaultFormat().
void QOpenGLWidget::setTextureFormat(GLenum texFormat)
Définit un format de texture interne personnalisé de texFormat.
Lorsque vous travaillez avec des framebuffers sRGB, il est nécessaire de spécifier un format comme GL_SRGB8_ALPHA8. Ceci peut être réalisé en appelant cette fonction.
Note : Cette fonction n'a aucun effet si elle est appelée alors que le widget a déjà été affiché et qu'elle a donc effectué l'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 à QColorSpace::SRgb.
Voir également textureFormat().
void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)
Définit le comportement de mise à jour de ce widget à updateBehavior.
Voir aussi updateBehavior().
GLenum QOpenGLWidget::textureFormat() const
Renvoie le format de texture interne actif si le widget a déjà été initialisé, le format demandé si un format a été défini mais que le widget n'a pas encore été rendu visible, ou nullptr si setTextureFormat() n'a pas été appelé et que le widget n'a pas encore été rendu visible.
Voir aussi setTextureFormat().
QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
Renvoie le comportement de mise à jour du widget.
Voir également 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.