Accelerate your Widgets with OpenGL

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
(поправил номер выпуска)
 
(4 промежуточные версии не показаны)
Строка 1: Строка 1:
-
[[Категория:Qt_Издания]]
+
{{Панель навигации по Qt Quarterly|Выпуск 26}}
-
__NOTOC__
+
-
 
+
==Accelerate your Widgets with OpenGL==
==Accelerate your Widgets with OpenGL==
-
 
by Samuel R&oslash;dal<br /><div class="introduction"><br />Qt 4.4 introduced a powerful feature to allow any QWidget subclass tobe put into QGraphicsView. It is now possible to embed ordinary widgets in aQGLWidget, a feature that has been missing in Qt's OpenGL support forsome time. In this article, we will show how to use QGraphicsView to placewidgets in a scene rendered with standard OpenGL commands.<br />
by Samuel R&oslash;dal<br /><div class="introduction"><br />Qt 4.4 introduced a powerful feature to allow any QWidget subclass tobe put into QGraphicsView. It is now possible to embed ordinary widgets in aQGLWidget, a feature that has been missing in Qt's OpenGL support forsome time. In this article, we will show how to use QGraphicsView to placewidgets in a scene rendered with standard OpenGL commands.<br />
-
*[[#turbocharginggraphicsview | Turbo Charging Graphics View]]
+
__TOC__
-
*[[#renderingtheopenglscene | Rendering the OpenGL Scene]]
+
-
*[[#embeddingwidgetsinthescene | Embedding Widgets in the Scene]]
+
-
*[[#wrappingup | Wrapping Up]]
+
</div>
</div>
-
In some applications, such as those used in Computer Aided Design (CAD), or inapplications that use OpenGL rendering to improve performance, being able torender widgets inside a [[Qt:Документация 4.4.3/qglwidget | QGLWidget]] is a desirable feature. For example,using semi-transparent widgets on top of an OpenGL-rendered scene can betterutilize screen real estate. Also, widgets can be used as tooltips or labels toannotate objects in a 3D scene.
+
In some applications, such as those used in Computer Aided Design (CAD), or inapplications that use OpenGL rendering to improve performance, being able torender widgets inside a [[Qt:Документация 4.3.2/qglwidget | QGLWidget]] is a desirable feature. For example,using semi-transparent widgets on top of an OpenGL-rendered scene can betterutilize screen real estate. Also, widgets can be used as tooltips or labels toannotate objects in a 3D scene.
-
One way to achieve this is to perform OpenGL drawing in a [[Qt:Документация 4.4.3/qgraphicsview | QGraphicsView]],as opposed to the traditional way of subclassing [[Qt:Документация 4.4.3/qglwidget | QGLWidget]] and overridingits <tt>paintGL()</tt> function.
+
One way to achieve this is to perform OpenGL drawing in a [[Qt:Документация 4.3.2/qgraphicsview | QGraphicsView]],as opposed to the traditional way of subclassing [[Qt:Документация 4.3.2/qglwidget | QGLWidget]] and overridingits <tt>paintGL()</tt> function.
To demonstrate this technique, we will use a model viewer which loads a 3Dmodel stored in the <tt>.obj</tt> file format and renders it using OpenGL. On topof this model, we will draw a set of widgets to control the renderingparameters and display various bits of information about the model. This isdone simply by adding the widgets to the scene.
To demonstrate this technique, we will use a model viewer which loads a 3Dmodel stored in the <tt>.obj</tt> file format and renders it using OpenGL. On topof this model, we will draw a set of widgets to control the renderingparameters and display various bits of information about the model. This isdone simply by adding the widgets to the scene.
[[Image:qq26-openglwidgets2.png|center]]
[[Image:qq26-openglwidgets2.png|center]]
-
<div id="turbocharginggraphicsview"></div>
+
 
 +
 
===Turbo Charging Graphics View===
===Turbo Charging Graphics View===
-
In our example, the actual OpenGL scene rendering and widget controls will behandled in a [[Qt:Документация 4.4.3/qgraphicsscene | QGraphicsScene]] subclass, but first we need to correctly setup a [[Qt:Документация 4.4.3/qgraphicsview | QGraphicsView]] with a [[Qt:Документация 4.4.3/qglwidget | QGLWidget]] viewport.
+
In our example, the actual OpenGL scene rendering and widget controls will behandled in a [[Qt:Документация 4.3.2/qgraphicsscene | QGraphicsScene]] subclass, but first we need to correctly setup a [[Qt:Документация 4.3.2/qgraphicsview | QGraphicsView]] with a [[Qt:Документация 4.3.2/qglwidget | QGLWidget]] viewport.
-
Since we want our widgets to be positioned relative to the view coordinates,we need to keep the scene rectangle synchronized with the size of the[[Qt:Документация 4.4.3/qgraphicsview | QGraphicsView]]. To achieve this, we create a simple subclass of[[Qt:Документация 4.4.3/qgraphicsview | QGraphicsView]] which updates the scene's rectangle whenever the view itselfis resized.
+
Since we want our widgets to be positioned relative to the view coordinates,we need to keep the scene rectangle synchronized with the size of the[[Qt:Документация 4.3.2/qgraphicsview | QGraphicsView]]. To achieve this, we create a simple subclass of[[Qt:Документация 4.3.2/qgraphicsview | QGraphicsView]] which updates the scene's rectangle whenever the view itselfis resized.
The custom <tt>GraphicsView</tt> class looks like this:
The custom <tt>GraphicsView</tt> class looks like this:
Строка 44: Строка 39:
     };
     };
</source>  
</source>  
-
In our main function we instantiate this class and set the [[Qt:Документация 4.4.3/qgraphicsview | QGraphicsView]]parameters that are needed in our case. First of all the viewport needs to bea [[Qt:Документация 4.4.3/qglwidget | QGLWidget]] in order to do OpenGL rendering in our graphics scene. We usethe <tt>SampleBuffers</tt> format specifier to enable multisample anti-aliasing inour rendering code.
+
In our main function we instantiate this class and set the [[Qt:Документация 4.3.2/qgraphicsview | QGraphicsView]]parameters that are needed in our case. First of all the viewport needs to bea [[Qt:Документация 4.3.2/qglwidget | QGLWidget]] in order to do OpenGL rendering in our graphics scene. We usethe <tt>SampleBuffers</tt> format specifier to enable multisample anti-aliasing inour rendering code.
<source lang="cpp-qt">
<source lang="cpp-qt">
     int main(int argc, char **argv)
     int main(int argc, char **argv)
Строка 62: Строка 57:
     }
     }
</source>  
</source>  
-
Next, we set the viewport update mode of the [[Qt:Документация 4.4.3/qgraphicsview | QGraphicsView]] to<tt>FullViewportUpdate</tt> as a [[Qt:Документация 4.4.3/qglwidget | QGLWidget]] cannot perform partial updates.Thus, we need to redraw everything whenever a part of the scene changes.We set as the scene an instance of our <tt>OpenGLScene</tt> class, a subclass of[[Qt:Документация 4.4.3/qgraphicsscene | QGraphicsScene]], and resize the view to a decent size.
+
Next, we set the viewport update mode of the [[Qt:Документация 4.3.2/qgraphicsview | QGraphicsView]] to<tt>FullViewportUpdate</tt> as a [[Qt:Документация 4.3.2/qglwidget | QGLWidget]] cannot perform partial updates.Thus, we need to redraw everything whenever a part of the scene changes.We set as the scene an instance of our <tt>OpenGLScene</tt> class, a subclass of[[Qt:Документация 4.3.2/qgraphicsscene | QGraphicsScene]], and resize the view to a decent size.
 +
 
-
<div id="renderingtheopenglscene"></div>
 
===Rendering the OpenGL Scene===
===Rendering the OpenGL Scene===
-
The actual OpenGL rendering is done by reimplementing [[Qt:Документация 4.4.3/qgraphicsscene | QGraphicsScene]]'s<tt>drawBackground()</tt> function. By rendering the OpenGL scene in<tt>drawBackground()</tt>, all widgets and other graphics items are drawn on top.It is possible to reimplement <tt>drawForeground()</tt> to do OpenGL renderingon top of the graphics scene, if that is required.
+
The actual OpenGL rendering is done by reimplementing [[Qt:Документация 4.3.2/qgraphicsscene | QGraphicsScene]]'s<tt>drawBackground()</tt> function. By rendering the OpenGL scene in<tt>drawBackground()</tt>, all widgets and other graphics items are drawn on top.It is possible to reimplement <tt>drawForeground()</tt> to do OpenGL renderingon top of the graphics scene, if that is required.
-
Unlike when subclassing [[Qt:Документация 4.4.3/qglwidget | QGLWidget]], it is not sufficient to set up commonstate in <tt>initializeGL()</tt> or <tt>resizeGL()</tt>. A painter is already active on the GL context when <tt>drawBackground()</tt> is called, and [[Qt:Документация 4.4.3/qpainter | QPainter]]changes the GL state when <tt>begin()</tt> is called. For example, the model viewand projection matrices will be changed so that we have a coordinate systemthat maps to the [[Qt:Документация 4.4.3/qwidget | QWidget]] coordinate system.
+
Unlike when subclassing [[Qt:Документация 4.3.2/qglwidget | QGLWidget]], it is not sufficient to set up commonstate in <tt>initializeGL()</tt> or <tt>resizeGL()</tt>. A painter is already active on the GL context when <tt>drawBackground()</tt> is called, and [[Qt:Документация 4.3.2/qpainter | QPainter]]changes the GL state when <tt>begin()</tt> is called. For example, the model viewand projection matrices will be changed so that we have a coordinate systemthat maps to the [[Qt:Документация 4.3.2/qwidget | QWidget]] coordinate system.
Here is how our reimplemented <tt>drawBackground()</tt> looks:
Here is how our reimplemented <tt>drawBackground()</tt> looks:
Строка 89: Строка 84:
It is the application developer's responsibility to restore any state changesmade when issuing OpenGL commands inside an active painter, like pushing andpopping the matrices used. If you change a state and do not restore it, thereis a chance that the paint engine's state gets out of sync, causing paintingissues when the actual graphics items in the scene are painted.
It is the application developer's responsibility to restore any state changesmade when issuing OpenGL commands inside an active painter, like pushing andpopping the matrices used. If you change a state and do not restore it, thereis a chance that the paint engine's state gets out of sync, causing paintingissues when the actual graphics items in the scene are painted.
-
If you do any painting using [[Qt:Документация 4.4.3/qpainter | QPainter]] instead of using pure OpenGLcommands, you will need to invoke <tt>save()</tt> and <tt>restore()</tt> on the painter.
+
If you do any painting using [[Qt:Документация 4.3.2/qpainter | QPainter]] instead of using pure OpenGLcommands, you will need to invoke <tt>save()</tt> and <tt>restore()</tt> on the painter.
</div></div>
</div></div>
Since the painter passed to <tt>drawBackground()</tt> is already active we knowthat we have an active GL context, and we are ready to issue GL commands. Westart by clearing the background and performing any setup that is required,such as initializing the matrices.
Since the painter passed to <tt>drawBackground()</tt> is already active we knowthat we have an active GL context, and we are ready to issue GL commands. Westart by clearing the background and performing any setup that is required,such as initializing the matrices.
Строка 142: Строка 137:
We will not look further into how the actual 3D model is rendered as itis beyond the scope of this article---more details can be found in the sourcecode available on the Qt Quarterly Web site.
We will not look further into how the actual 3D model is rendered as itis beyond the scope of this article---more details can be found in the sourcecode available on the Qt Quarterly Web site.
-
<div id="embeddingwidgetsinthescene"></div>
+
 
===Embedding Widgets in the Scene===
===Embedding Widgets in the Scene===
-
In our <tt>OpenGLScene</tt> constructor, which we won't quote here, we add thewidgets and graphics items to control rendering parameters. In order to geta window frame for widgets that are embedded in the graphics scene, weconstruct them using [[Qt:Документация 4.4.3/qdialog | QDialog]], with the <tt>CustomizeWindowHint</tt>and <tt>WindowTitleHint</tt> flags set, disabling their close buttons.
+
In our <tt>OpenGLScene</tt> constructor, which we won't quote here, we add thewidgets and graphics items to control rendering parameters. In order to geta window frame for widgets that are embedded in the graphics scene, weconstruct them using [[Qt:Документация 4.3.2/qdialog | QDialog]], with the <tt>CustomizeWindowHint</tt>and <tt>WindowTitleHint</tt> flags set, disabling their close buttons.
The helper function used for this is as follows:
The helper function used for this is as follows:
Строка 163: Строка 158:
Setting the window opacity causes the embedded widgets to be slightlytransparent, which makes it possible to discern OpenGL scene elements that arepartially hidden behind the control widgets. We also use<tt>setWindowTitle()</tt> to add a title to each embedded widget's window frame.
Setting the window opacity causes the embedded widgets to be slightlytransparent, which makes it possible to discern OpenGL scene elements that arepartially hidden behind the control widgets. We also use<tt>setWindowTitle()</tt> to add a title to each embedded widget's window frame.
-
Subwidgets are added to each embedded widget by creating a layout and addingwidgets to it before using [[Qt:Документация 4.4.3/qgraphicsscene | QGraphicsScene]]'s <tt>addWidget()</tt> function toembed the widget in the scene. Here's how the instructions widget is createdand added to the scene:
+
Subwidgets are added to each embedded widget by creating a layout and addingwidgets to it before using [[Qt:Документация 4.3.2/qgraphicsscene | QGraphicsScene]]'s <tt>addWidget()</tt> function toembed the widget in the scene. Here's how the instructions widget is createdand added to the scene:
<source lang="cpp-qt">
<source lang="cpp-qt">
     QWidget *instructions = createDialog(tr("Instructions"));
     QWidget *instructions = createDialog(tr("Instructions"));
Строка 175: Строка 170:
     addWidget(instructions);
     addWidget(instructions);
</source>  
</source>  
-
Once we have embedded all the widgets in the graphics scene, we iterate throughthe resulting [[Qt:Документация 4.4.3/qgraphicsitem | QGraphicsItem]]s and position them beneath each other in theleft part of the view. We use the <tt>ItemIsMovable</tt> flag to allow the user tomove the widgets around with the mouse, and we set the cache mode to<tt>DeviceCoordinateCache</tt> so that widgets are cached in [[Qt:Документация 4.4.3/qpixmap | QPixmap]]s. Thesepixmaps are then mapped to OpenGL textures, making the widgets very cheap todraw when they are not being updated.
+
Once we have embedded all the widgets in the graphics scene, we iterate throughthe resulting [[Qt:Документация 4.3.2/qgraphicsitem | QGraphicsItem]]s and position them beneath each other in theleft part of the view. We use the <tt>ItemIsMovable</tt> flag to allow the user tomove the widgets around with the mouse, and we set the cache mode to<tt>DeviceCoordinateCache</tt> so that widgets are cached in [[Qt:Документация 4.3.2/qpixmap | QPixmap]]s. Thesepixmaps are then mapped to OpenGL textures, making the widgets very cheap todraw when they are not being updated.
<source lang="cpp-qt">
<source lang="cpp-qt">
     QPointF pos(10, 10);
     QPointF pos(10, 10);
Строка 188: Строка 183:
     }
     }
</source>  
</source>  
-
For event handling, we need to reimplement the relevant function in[[Qt:Документация 4.4.3/qgraphicsscene | QGraphicsScene]]. Here is how our mouse move event handling is done:
+
For event handling, we need to reimplement the relevant function in[[Qt:Документация 4.3.2/qgraphicsscene | QGraphicsScene]]. Here is how our mouse move event handling is done:
<source lang="cpp-qt">
<source lang="cpp-qt">
     void OpenGLScene::mouseMoveEvent(
     void OpenGLScene::mouseMoveEvent(
Строка 209: Строка 204:
     }
     }
</source>  
</source>  
-
Since we are handling events for widgets in a graphics scene, we use[[Qt:Документация 4.4.3/qgraphicsscenemouseevent | QGraphicsSceneMouseEvent]] instead of [[Qt:Документация 4.4.3/qmouseevent | QMouseEvent]]. We begin by callingthe base class implementation to give other items on top of the OpenGL scenethe chance to handle events first. If the event has already been handled wereturn, otherwise we handle the event ourselves and call <tt>accept()</tt>.
+
Since we are handling events for widgets in a graphics scene, we use[[Qt:Документация 4.3.2/qgraphicsscenemouseevent | QGraphicsSceneMouseEvent]] instead of [[Qt:Документация 4.3.2/qmouseevent | QMouseEvent]]. We begin by callingthe base class implementation to give other items on top of the OpenGL scenethe chance to handle events first. If the event has already been handled wereturn, otherwise we handle the event ourselves and call <tt>accept()</tt>.
 +
 
 +
Note that calling <tt>scenePos()</tt> on the [[Qt:Документация 4.3.2/qgraphicsscenemouseevent | QGraphicsSceneMouseEvent]]will give us the coordinates in the actual view as, earlier, we made sure to set thescene rectangle to match the view rectangle. [[Qt:Документация 4.3.2/qgraphicsscenemouseevent | QGraphicsSceneMouseEvent]]also provides the convenient <tt>lastScenePos()</tt> function which simplifies ourcode a bit. Finally we call <tt>update()</tt> to redraw the scene and background.
 +
 
-
Note that calling <tt>scenePos()</tt> on the [[Qt:Документация 4.4.3/qgraphicsscenemouseevent | QGraphicsSceneMouseEvent]]will give us the coordinates in the actual view as, earlier, we made sure to set thescene rectangle to match the view rectangle. [[Qt:Документация 4.4.3/qgraphicsscenemouseevent | QGraphicsSceneMouseEvent]]also provides the convenient <tt>lastScenePos()</tt> function which simplifies ourcode a bit. Finally we call <tt>update()</tt> to redraw the scene and background.
 
-
<div id="wrappingup"></div>
 
===Wrapping Up===
===Wrapping Up===

Текущая версия на 01:57, 20 февраля 2009

Image:qt-logo_new.png Image:qq-title-article.png
Qt Quarterly | Выпуск 26 | Документация


[править] Accelerate your Widgets with OpenGL

by Samuel Rødal

Qt 4.4 introduced a powerful feature to allow any QWidget subclass tobe put into QGraphicsView. It is now possible to embed ordinary widgets in aQGLWidget, a feature that has been missing in Qt's OpenGL support forsome time. In this article, we will show how to use QGraphicsView to placewidgets in a scene rendered with standard OpenGL commands.

Содержание

In some applications, such as those used in Computer Aided Design (CAD), or inapplications that use OpenGL rendering to improve performance, being able torender widgets inside a QGLWidget is a desirable feature. For example,using semi-transparent widgets on top of an OpenGL-rendered scene can betterutilize screen real estate. Also, widgets can be used as tooltips or labels toannotate objects in a 3D scene.

One way to achieve this is to perform OpenGL drawing in a QGraphicsView,as opposed to the traditional way of subclassing QGLWidget and overridingits paintGL() function.

To demonstrate this technique, we will use a model viewer which loads a 3Dmodel stored in the .obj file format and renders it using OpenGL. On topof this model, we will draw a set of widgets to control the renderingparameters and display various bits of information about the model. This isdone simply by adding the widgets to the scene.

center


[править] Turbo Charging Graphics View

In our example, the actual OpenGL scene rendering and widget controls will behandled in a QGraphicsScene subclass, but first we need to correctly setup a QGraphicsView with a QGLWidget viewport.

Since we want our widgets to be positioned relative to the view coordinates,we need to keep the scene rectangle synchronized with the size of the QGraphicsView. To achieve this, we create a simple subclass of QGraphicsView which updates the scene's rectangle whenever the view itselfis resized.

The custom GraphicsView class looks like this:

    class GraphicsView : public QGraphicsView
    {
    public:
        GraphicsView()
        {
            setWindowTitle(tr("3D Model Viewer"));
        }
 
    protected:
        void resizeEvent(QResizeEvent *event) {
            if (scene())
                scene()->setSceneRect(
                    QRect(QPoint(0, 0), event->size()));
            QGraphicsView::resizeEvent(event);
        }
    };

In our main function we instantiate this class and set the QGraphicsViewparameters that are needed in our case. First of all the viewport needs to bea QGLWidget in order to do OpenGL rendering in our graphics scene. We usethe SampleBuffers format specifier to enable multisample anti-aliasing inour rendering code.

    int main(int argc, char **argv)
    {
        QApplication app(argc, argv);
 
        GraphicsView view;
        view.setViewport(new QGLWidget(
            QGLFormat(QGL::SampleBuffers)));
        view.setViewportUpdateMode(
            QGraphicsView::FullViewportUpdate);
        view.setScene(new OpenGLScene);
        view.show();
 
        view.resize(1024, 768);
        return app.exec();
    }

Next, we set the viewport update mode of the QGraphicsView toFullViewportUpdate as a QGLWidget cannot perform partial updates.Thus, we need to redraw everything whenever a part of the scene changes.We set as the scene an instance of our OpenGLScene class, a subclass of QGraphicsScene, and resize the view to a decent size.


[править] Rendering the OpenGL Scene

The actual OpenGL rendering is done by reimplementing QGraphicsScene'sdrawBackground() function. By rendering the OpenGL scene indrawBackground(), all widgets and other graphics items are drawn on top.It is possible to reimplement drawForeground() to do OpenGL renderingon top of the graphics scene, if that is required.

Unlike when subclassing QGLWidget, it is not sufficient to set up commonstate in initializeGL() or resizeGL(). A painter is already active on the GL context when drawBackground() is called, and QPainterchanges the GL state when begin() is called. For example, the model viewand projection matrices will be changed so that we have a coordinate systemthat maps to the QWidget coordinate system.

Here is how our reimplemented drawBackground() looks:

    void OpenGLScene::drawBackground(QPainter *painter,
                                     const QRectF &)
    {
        if (painter->paintEngine()->type()
                != QPaintEngine::OpenGL) {
            qWarning("OpenGLScene: drawBackground needs a "
                     "QGLWidget to be set as viewport on the "
                     "graphics view");
            return;
        }

First, we ensure that we actually have an OpenGL paint engine, otherwise weissue a warning and return immediately.

Since the painter passed to drawBackground() is already active we knowthat we have an active GL context, and we are ready to issue GL commands. Westart by clearing the background and performing any setup that is required,such as initializing the matrices.

      glClearColor(m_backgroundColor.redF(),
                     m_backgroundColor.greenF(),
                     m_backgroundColor.blueF(), 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        if (!m_model)
            return;
 
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        gluPerspective(70, width() / height(), 0.01, 1000);
 
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

Once the matrices have been set up, we position the light and render the model.Afterwards, we restore the matrices and schedule a repaint.

      const float pos[] = { m_lightItem->x() - width() / 2,
                              height() / 2 - m_lightItem->y(),
                              512, 0 };
        glLightfv(GL_LIGHT0, GL_POSITION, pos);
        glColor4f(m_modelColor.redF(), m_modelColor.greenF(),
                  m_modelColor.blueF(), 1.0f);
 
        const int delta = m_time.elapsed() - m_lastTime;
        m_rotation += m_angularMomentum * (delta / 1000.0);
        m_lastTime += delta;
 
        glTranslatef(0, 0, -m_distance);
        glRotatef(m_rotation.x, 1, 0, 0);
        glRotatef(m_rotation.y, 0, 1, 0);
        glRotatef(m_rotation.z, 0, 0, 1);
 
        glEnable(GL_MULTISAMPLE);
        m_model->render(m_wireframeEnabled, m_normalsEnabled);
        glDisable(GL_MULTISAMPLE);
 
        glPopMatrix();
 
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
 
        QTimer::singleShot(20, this, SLOT(update()));
    }

We will not look further into how the actual 3D model is rendered as itis beyond the scope of this article---more details can be found in the sourcecode available on the Qt Quarterly Web site.


[править] Embedding Widgets in the Scene

In our OpenGLScene constructor, which we won't quote here, we add thewidgets and graphics items to control rendering parameters. In order to geta window frame for widgets that are embedded in the graphics scene, weconstruct them using QDialog, with the CustomizeWindowHintand WindowTitleHint flags set, disabling their close buttons.

The helper function used for this is as follows:

    QDialog *OpenGLScene::createDialog(
             const QString &windowTitle) const
    {
        QDialog *dialog = new QDialog(0,
            Qt::CustomizeWindowHint | Qt::WindowTitleHint);
 
        dialog->setWindowOpacity(0.8);
        dialog->setWindowTitle(windowTitle);
        dialog->setLayout(new QVBoxLayout);
 
        return dialog;
    }

Setting the window opacity causes the embedded widgets to be slightlytransparent, which makes it possible to discern OpenGL scene elements that arepartially hidden behind the control widgets. We also usesetWindowTitle() to add a title to each embedded widget's window frame.

Subwidgets are added to each embedded widget by creating a layout and addingwidgets to it before using QGraphicsScene's addWidget() function toembed the widget in the scene. Here's how the instructions widget is createdand added to the scene:

    QWidget *instructions = createDialog(tr("Instructions"));
    instructions->layout()->addWidget(new QLabel(
        tr("Use mouse wheel to zoom model, and click and "
           "drag to rotate model")));
    instructions->layout()->addWidget(new QLabel(
        tr("Move the sun around to change the light "
           "position")));
    ...
    addWidget(instructions);

Once we have embedded all the widgets in the graphics scene, we iterate throughthe resulting QGraphicsItems and position them beneath each other in theleft part of the view. We use the ItemIsMovable flag to allow the user tomove the widgets around with the mouse, and we set the cache mode toDeviceCoordinateCache so that widgets are cached in QPixmaps. Thesepixmaps are then mapped to OpenGL textures, making the widgets very cheap todraw when they are not being updated.

    QPointF pos(10, 10);
    foreach (QGraphicsItem *item, items()) {
        item->setFlag(QGraphicsItem::ItemIsMovable);
        item->setCacheMode(
            QGraphicsItem::DeviceCoordinateCache);
 
        const QRectF rect = item->boundingRect();
        item->setPos(pos.x() - rect.x(), pos.y() - rect.y());
        pos += QPointF(0, 10 + rect.height());
    }

For event handling, we need to reimplement the relevant function in QGraphicsScene. Here is how our mouse move event handling is done:

    void OpenGLScene::mouseMoveEvent(
                      QGraphicsSceneMouseEvent *event)
    {
        QGraphicsScene::mouseMoveEvent(event);
        if (event->isAccepted()) return;
        if (event->buttons() & Qt::LeftButton) {
            const QPointF delta =
                event->scenePos() - event->lastScenePos();
            const Point3d angularImpulse =
                Point3d(delta.y(), delta.x(), 0) * 0.1;
 
            m_rotation += angularImpulse;
            m_accumulatedMomentum += angularImpulse;
 
            event->accept();
            update();
        }
    }

Since we are handling events for widgets in a graphics scene, we use QGraphicsSceneMouseEvent instead of QMouseEvent. We begin by callingthe base class implementation to give other items on top of the OpenGL scenethe chance to handle events first. If the event has already been handled wereturn, otherwise we handle the event ourselves and call accept().

Note that calling scenePos() on the QGraphicsSceneMouseEventwill give us the coordinates in the actual view as, earlier, we made sure to set thescene rectangle to match the view rectangle. QGraphicsSceneMouseEventalso provides the convenient lastScenePos() function which simplifies ourcode a bit. Finally we call update() to redraw the scene and background.


[править] Wrapping Up

With the development of new underlying technologies in Qt, the Graphics Viewframework is becoming a focal point for new experiments in user interfacedesign and a proving ground for initiatives to improve rendering performance.

The example code outlined in this article is part of a Qt Labs project:

http://labs.trolltech.com/page/Graphics/About/Dojo

A snapshot of the code from this project is available from the Qt Quarterly Web site.

Обсудить на форуме...