QGLWidget within a QAbstractScrollArea



  • Hello.

    I've been trying to put a QGLWidget as a viewport in a QAbstractScrollArea subclass.
    But for some reason my QGLWidget won't draw.

    Do I need to inherit any functions or call protected functions to make it work?



  • Can you not just use QScrollArea and QScrollArea::setWidget()? Something like:

    @
    QScrollArea* scroll = new QScrollArea( this );
    QGLWidget* glWidget = new QGLWidget;
    scroll->setWidget( glWidget );
    @

    Not tested, but I think it should work. You may need to set a size for your QGLWidget.

    Edit: Fixed typo. s/QScrollWidget/QScrollArea/



  • [quote author="ZapB" date="1301599554"]Can you not just use QScrollArea and QScrollArea::setWidget()? Something like:

    @
    QScrollWidget* scroll = new QScrollWidget( this );
    QGLWidget* glWidget = new QGLWidget;
    scroll->setWidget( glWidget );
    @

    Not tested, but I think it should work. You may need to set a size for your QGLWidget.[/quote]
    What's a QScrollWidget? I don't have it, I'm using Qt 4.7.



  • Oopsy sorry, I meant QScrollArea not QScrollWidget. The text was right the code was wrong.



  • I tried doing that.
    It still doesn't render.

    I think I really do need to inherit QAbstractScrollArea.

    Any more suggestions?



  • It works for me (tm). Here is an example showing it in action.

    Create a new Qt C++ application using qt-creator and choose QMainWindow as the base class for the main window in the wizard.

    Then in design mode, add a QScrollArea to the main window and lay it out vertically. I also changed the name of the scroll area to m_scrollArea (as you'll see in the code below).

    Now edit the project so that it looks like this:

    mainwindow.h : no changes needed.

    mainwindow.cpp:

    @
    #include "mainwindow.h"
    #include "ui_mainwindow.h"

    #include "solarsystem.h"

    MainWindow::MainWindow( QWidget* parent )
    : QMainWindow( parent ),
    ui( new Ui::MainWindow )
    {
    ui->setupUi( this );
    SolarSystem* solar = new SolarSystem;
    solar->setMinimumSize( 1024, 768 );
    ui->m_scrollArea->setWidget( solar );
    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }
    @

    All I do is create a SolarSystem widget (which is derived from QGLWidget - source below). This just gives us something to look at ;-)

    main.cpp:

    @
    #include <QtGui/QApplication>
    #include "mainwindow.h"

    #include <GL/glut.h>

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    glutInit( &argc, argv );
    MainWindow w;
    w.show();

    return a.exec&#40;&#41;;
    

    }
    @

    For completeness here is my SolarSystem class (ported from one of the examples in the OpenGL superbible book):

    solarsystem.h:

    @
    #ifndef SOLARSYSTEM_H
    #define SOLARSYSTEM_H

    #include <QGLWidget>

    class SolarSystem : public QGLWidget
    {
    Q_OBJECT
    public:
    SolarSystem( QWidget* parent = 0 );

    public slots:
    void advance();

    protected:
    virtual void initializeGL();
    virtual void resizeGL( int w, int h );
    virtual void paintGL();

    virtual void keyPressEvent( QKeyEvent* e );
    

    private:
    float m_thetaEarth;
    float m_thetaMoon;
    };

    #endif // SOLARSYSTEM_H
    @

    solarsystem.cpp:

    @
    #include "solarsystem.h"

    #include <QApplication>
    #include <QKeyEvent>
    #include <QTimer>
    #include <QVector3D>

    #include <GL/glut.h>

    SolarSystem::SolarSystem( QWidget* parent )
    : QGLWidget( parent ),
    m_thetaEarth( 0.0f ),
    m_thetaMoon( 0.0f )
    {
    QTimer* timer = new QTimer( this );
    connect( timer, SIGNAL( timeout() ), SLOT( advance() ) );
    timer->start( 1000 / 60 );
    }

    void SolarSystem::advance()
    {
    m_thetaEarth += 0.25f;
    if ( m_thetaEarth > 360.0f )
    m_thetaEarth -= 360.0f;

    m_thetaMoon += 2.0f;
    if ( m_thetaMoon > 360.0f )
        m_thetaMoon -= 360.0f;
    
    updateGL();
    

    }

    void SolarSystem::initializeGL()
    {
    // Set the clear color to black
    glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );

    // Enable depth testing
    glEnable( GL_DEPTH_TEST );
    
    // Enable back face culling
    glFrontFace( GL_CCW ); // Counter clock-wise polygons face out
    glEnable( GL_CULL_FACE );
    
    // Enable lighting
    const float whiteLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
    const float sourceLight[] = { 0.8f, 0.8f, 0.8f, 1.0f };
    glEnable(GL_LIGHTING);
    
    // Setup and enable light 0
    glLightModelfv( GL_LIGHT_MODEL_AMBIENT, whiteLight );
    glLightfv( GL_LIGHT0, GL_DIFFUSE, sourceLight );
    glEnable( GL_LIGHT0 );
    
    // Enable color tracking
    glEnable( GL_COLOR_MATERIAL );
    
    // Set Material properties to follow glColor values
    glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
    

    }

    void SolarSystem::resizeGL( int w, int h )
    {
    // Prevent a divde by zero
    if ( h == 0 )
    h = 1;

    // Set the viewport to window dimensions
    glViewport( 0, 0, w, h );
    
    // reset the coordinate system
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    
    // Establish the clipping volume by setting up an orthographic projection
    double aspectRatio = double( w ) / double( h );
    double verticalViewingAngle = 45.0;
    gluPerspective( verticalViewingAngle, aspectRatio, 1.0, 425.0 );
    
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    

    }

    void SolarSystem::paintGL()
    {
    // Clear the buffer with the current clearing color
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // Set the identity modelview transform
    glMatrixMode( GL_MODELVIEW );
    glPushMatrix();
    
    // Translate the whole scene a little
    glTranslatef( 0.0f, 0.0f, -300.0f );
    
    // Set drawing colour to yellow and draw the Sun
    glColor3f( 1.0f, 1.0f, 0.0f );
    glDisable( GL_LIGHTING );
    glutSolidSphere( 15.0f, 30, 17 );
    glEnable( GL_LIGHTING );
    
    // Position a light to simulate the Sun as a light source
    const float lightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    glLightfv( GL_LIGHT0, GL_POSITION, lightPos );
    
    // Draw the Earth
    glColor3f( 0.0f, 0.0f, 1.0f );
    glRotatef( m_thetaEarth, 0.0f, 1.0f, 0.0f );
    glTranslatef( 105.0f, 0.0f, 0.0f );
    glutSolidSphere( 15.0f, 30, 17 );
    
    // Rotate from Earth based coords and draw the moon
    glColor3f( 0.7f, 0.7f, 0.7f );
    glRotatef( m_thetaMoon, 0.0f, 1.0f, 0.0f );
    glTranslatef( 30.0f, 0.0f, 0.0f );
    glutSolidSphere( 6.0f, 15, 15 );
    
    glPopMatrix();
    

    }

    void SolarSystem::keyPressEvent( QKeyEvent* e )
    {
    switch ( e->key() )
    {
    case Qt::Key_Escape:
    QCoreApplication::instance()->quit();
    break;

        default:
            QGLWidget::keyPressEvent( e );
    }
    

    }
    @

    Anyway, the above all runs and works as expected. I see scroll bars when the mainwindow and scroll area are too small to accomodate the minimum size of the SolarSystem widget.



  • Apparently I needed to set the minimum size.

    It works and all but I need control over the scrollbars..
    So I've to use the QAbstractScrollArea anyway.

    MapView.h (QAbstractScrollArea class decleration):

    @#ifndef MAPVIEW_H
    #define MAPVIEW_H

    #include <QAbstractScrollArea>

    class Map;
    class MapRenderer;

    class MapView : public QAbstractScrollArea
    {
    Q_OBJECT
    public:
    explicit MapView(QWidget *parent = 0);

    void setMap(Map *map);
    

    private:
    Map* mMap;
    MapRenderer *mMapRenderer;
    };

    #endif // MAPVIEW_H
    @

    MapView.cpp (QAbstractScrollArea class implementation):
    @#include "mapview.h"
    #include "map.h"
    #include "maprenderer.h"

    #include <QScrollBar>

    MapView::MapView(QWidget *parent) :
    QAbstractScrollArea(parent),
    mMap(0),
    mMapRenderer(0)
    {
    setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    }

    void MapView::setMap(Map *map)
    {
    mMap = map;
    mMapRenderer = new MapRenderer(map);

    setViewport(mMapRenderer);
    

    }
    @

    MapRenderer.cpp (QGLWidget subclass implementation):
    @#include "maprenderer.h"

    MapRenderer::MapRenderer(Map *map, QWidget *parent) :
    QGLWidget(parent),
    mMap(map)
    {
    }

    void MapRenderer::initializeGL()
    {
    glViewport(0, 0, width(), height());

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, 320.0 / 240.0, 0.001, 100);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 20.0, 10.0,
              0.0, 0.0, 0.0,
              0.0, 1.0, 0.0);
    

    }

    void MapRenderer::resizeGL(int w, int h)
    {
    glViewport(0, 0, w, h);
    }

    void MapRenderer::paintGL()
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }
    @

    This is how I use the MapView:
    @mMapView = new MapView();
    setCentralWidget(mMapView);@



  • Glad that you got it working. Good luck with the rest of your project.



  • [quote author="ZapB" date="1301668583"]Glad that you got it working. Good luck with the rest of your project.[/quote]
    It doesn't work..



  • Which aspect? The example I posted worked in that it embedded a QGLWidget into the scroll view. What else is missing?



  • I'm talking about my code. I said I needed help with inheriting QAbstractScrollArea not QScrollArea, that's because I need control over the scrollbars.
    [quote author="ZapB" date="1302016156"]Which aspect? The example I posted worked in that it embedded a QGLWidget into the scroll view. What else is missing?[/quote]



  • But you have not said how it doesn't work or what you are trying to achieve. Simply saying that you want/need to use QAbstractScrollArea to control the scroll bars is not enough info. What does QScrollArea do with the scroll bars that is wrong in your case?

    I am only trying to help but I need more information to do so.



  • [quote author="ZapB" date="1302016490"]But you have not said how it doesn't work or what you are trying to achieve. Simply saying that you want/need to use QAbstractScrollArea to control the scroll bars is not enough info. What does QScrollArea do with the scroll bars that is wrong in your case?

    I am only trying to help but I need more information to do so.[/quote]
    What doesn't work is that I don't see the graphics produced by the QGLWidget in my QAbstractScrollArea.
    QGLWidget is set as its viewport.
    I tried setting a breakpoint in the paintGL and it did reach it, so I guess the problem is in the QAbstractScrollArea.



  • QAbstractScrollArea by default filters some events for its viewport widget, including the paint event (QPaintEvent / QEvent::Paint). You need to manually forward it from the derived QAbstractScrollArea to your QGLWidget.

    One way to do this, is to override viewportEvent() in your MapView, and return false to indicate that the paint event should be propagated to the viewport(), i.e.:

    @bool MapView ::viewportEvent(QEvent* e)
    {
    if (e->type() == QEvent::Paint)
    return false;
    return QAbstractScrollArea::viewportEvent(e);
    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.