Integrate Qt into prewritten Application/Framework

喜夏-厌秋 提交于 2019-12-14 03:09:41

问题


I have a small visualization framework written in c++ and want to use Qt to have a proper GUI and control over my visualizations to interact with them. currently, I am using GLUT to create a window and draw a view in it. so everything I do is initialize an object of the class Visualization which does everything for me: holding model and views. the view itself holds a controller to process user input and manipulate the model. my main loop looks like this:

Visualization* vis = new Visualization();
vis->createModel("some_file.txt");
vis->createView("unknown");

// ...

void demo_app::display()
{
  // clear the framebuffer
  glClearColor(0.3, 0.3, 0.3, 1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  vis.update(); // looks for dirty scenes to update
  vis.render(); // draws all views which are stored in vis

  glutSwapBuffers();
}

with GLUT I have a single window in which I arrange multiple views. when I order the view to draw during the main loop I can execute glCommands and everything is fine.

so now my problem is I want to use Qt with several windows (QDialogs+QGLWidgets). I am familiar with Qt and I already ported the framework to Qt. but the problem is that I have to do too much Qt integration within the framework and this is not what I want. I want to control the visualization with Qt and its GUI elements, but the draw call for a view should be made by my Visualization class as shown by the GLUT example. So there must be a way to give an inherited QGLWidget a pointer to a view object and whenever the view should be drawn the widget has to call makeCurrent() so the glCommands can draw on the context. I also can use a texture in which my view paints the current scene and then draw the texture in the paintGL or glDraw() function of the QGLWidget, but how can I tell the Widget to update() when the view has no knowledge about it. I will emulate the main loop with a QTimer set to 0. all I want to do is to be able to trigger the rendering of the QGLWidget from inside my framework. any suggestion? links? examples?


回答1:


So there must be a way to give an inherited QGLWidget a pointer to a view object and whenever the view should be drawn

If you call demo_app::display() from within QGLWidget::paintGL, it will work and will draw scene onto QGLWidget context. However, this will use QGLWidget's context, so you'll need to move All initialization functions into QGLWidget. I don't remember whether OpenGL objects can be shared across context, but won't be really surprised if this can't be done.

but how can i tell the Widget to update() when the view has no knowledge about it.

Well, add pointer to QWidget to the view and call QWidget->update(). Should be obvious.

If you want to draw qt widgets from within GLUT-based window...

I.e. if you want to "embed" Qt widgets into GLUT window.

Every Qt widget has render method. You can use this method to draw widget onto any QPaintDevice or using any QPainter.

So, to draw widget from within the framework, you'll have to provide one of those to the widget and call render manually.

You'll also have to forward mouse and keyboard event from the framework and convert them into Qt events.

That should be doable but it will be major pain to implement.

Also see EmbeddedDialogs demo and QGraphicsProxyWidget.

If you simply want to use GUI widgets in addition to glut window, without embedding them into glut window...

Wrap everything related to your framework and glut messages into QObject, and put glut message handling into this object's slot. Connect this slot to a timer running with 0 timeout.

Example from my code (uses SDL):

class Game: public QObject{
Q_OBJECT
protected:
    void processSdlEvents();
...
protected slots:
    void onGameEnd();
    void timerSlot();
...
};

Game::Game(/*arguments*/ ...){
...
    gameTimer = new QTimer(this);
    gameTimer->setSingleShot(false);
    gameTimer->setInterval(0);
    connect(gameTimer, SIGNAL(timeout()), this, SLOT(timerSlot()));
...
}

void Game::timerSlot(){
    processSdlEvents();
    update();
    render();
}

void Game::processSdlEvents(){
    SDL_Event event;
    QList<GameEventHandler*> eventHandlers = findChildren<GameEventHandler*>();
    /*WARNING: This is NOT an infinite loop. */
    while (SDL_PollEvent(&event) != 0){
        if (event.type == SDL_QUIT){
            emit gameEnded();
            continue;
        }
        bool processed = false;
        for (int i = 0; i < eventHandlers.size(); i++){
            if (eventHandlers[i]->processEvent(&event)){
                processed = true;
                break;
            }
        }
        if (processed)
            continue;
        for (int i = 0; i < joysticks.size(); i++){
            if (joysticks[i]->processEvent(&event)){
                processed = true;
                break;
            }
        }
        /*if (processed)
            continue;*/
    }
}


int main(int argc, char** argv){
    QApplication app(argc, argv);
    int result = 0;
    try{
        Game game;
        QObject::connect(&game, SIGNAL(gameEnded()), &app, SLOT(quit()));
        game.start();
        result = app.exec();
    }
    catch(const QString& s){
        reportException(s);
    }
    catch(std::exception& e){
        reportException(e);
    }
    catch(...){
        reportException();
    }
    return result;
}

Please note that processSdlEvents is NOT an infinite loops. It gets called once in a while and processes all events received since the last call and then terminates.

In this scenario your framework specific functions are called from within Qt main loop.

You could also probably do it other way around and call Qt-specific functions from main loop in your own framework (using either QEventLoop class or QApplication::processEvents()) but it might be less reliable and I haven't tried that myself.



来源:https://stackoverflow.com/questions/18124328/integrate-qt-into-prewritten-application-framework

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!