将Qt集成到预先编写的应用程序/框架中



我有一个用c++编写的小型可视化框架,希望使用Qt来拥有一个合适的GUI,并控制我的可视化与它们交互。目前,我正在使用GLUT创建一个窗口并在其中绘制一个视图。所以我所做的一切都是初始化Visualization类的一个对象,它为我做了一切:保存模型和视图。视图本身包含一个控制器来处理用户输入并操纵模型。我的主循环看起来是这样的:

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();
}

使用GLUT,我有一个单独的窗口,可以在其中排列多个视图。当我在主循环中命令视图绘制时,我可以执行glCommands,一切都很好。

所以现在我的问题是我想在几个窗口(QDialogs+QGLWidgets)中使用Qt。我熟悉Qt,并且我已经将该框架移植到Qt。但问题是,我必须在框架内进行过多的Qt集成,这不是我想要的。我想用Qt及其GUI元素控制可视化,但视图的绘制调用应该由我的visualization类进行,如GLUT示例所示。因此,必须有一种方法为继承的QGLWidget提供一个指向视图对象的指针,无论何时绘制视图,小部件都必须调用makeCurrent(),以便glCommands可以在上下文中绘制。我也可以使用我的视图绘制当前场景的纹理,然后在QGLWidget的paintGL或glDraw()函数中绘制纹理,但当视图对此一无所知时,我如何将Widget告知update()。我将使用设置为0的QTimer来模拟主循环。我所想做的就是能够从我的框架内触发QGLWidget的呈现。有什么建议吗?链接?示例?

因此,必须有一种方法为继承的QGLWidget提供一个指向视图对象的指针,并且无论何时都应该绘制视图

若从QGLWidget::paintGL中调用demo_app::display(),它将起作用,并将场景绘制到QGLWidget上下文中。但是,这将使用QGLWidget的上下文,因此需要将所有初始化函数移到QGLWidget中。我不记得OpenGL对象是否可以在上下文中共享,但如果不能做到这一点,我也不会感到惊讶。

但是,当视图对此一无所知时,我如何告诉Widget更新()呢?

将指向QWidget的指针添加到视图中,然后调用QWidget->update()。应该是显而易见的。

如果您想在基于GLUT的窗口中绘制qt小部件

也就是说,如果您想将Qt小部件"嵌入"到GLUT窗口中。

每个Qt小部件都有render方法。您可以使用此方法在任何QPaintDevice或使用任何QPainter上绘制小部件。

因此,要从框架中绘制小部件,您必须向小部件提供其中一个,并手动调用render。

您还必须转发框架中的鼠标和键盘事件,并将它们转换为Qt事件。

这应该是可行的,但实施起来会很痛苦。

另请参阅EmbeddedDialogs演示和QGraphicsProxyWidget。

如果您只是想在供过于求窗口之外使用GUI小部件,而不想将它们嵌入供过于求的窗口

将与您的框架和供过于求消息相关的所有内容打包到QObject中,并将供过于求的消息处理放入该对象的槽中。将此插槽连接到运行0超时的计时器。

我的代码示例(使用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;
}

请注意,processSdlEvents不是一个无限循环。它偶尔会被调用一次,并处理自上次调用以来接收到的所有事件,然后终止。

在这种情况下,您的框架特定函数是从Qt主循环中调用的。

你也可以用其他方式,在你自己的框架中从主循环调用Qt特定的函数(使用QEventLoop类或QApplication::processEvents()),但它可能不太可靠,我自己也没有尝试过。

最新更新