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

时间:2013-08-08 11:00:12

标签: qt opengl glut qglwidget

我有一个用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,一切都很好。

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

1 个答案:

答案 0 :(得分:0)

  

因此,必须有一种方法可以为继承的QGLWidget提供指向视图对象的指针以及何时应该绘制视图

如果您从demo_app::display()内拨打QGLWidget::paintGL,它将会正常工作并将场景绘制到QGLWidget上下文中。但是,这将使用QGLWidget的上下文,因此您需要将所有初始化函数移动到QGLWidget。我不记得是否可以跨上下文共享OpenGL对象,但如果不能这样做,就不会感到惊讶。

  

但是如果视图不了解它,我怎么能告诉Widget更新()。

好吧,在视图中添加指向QWidget的指针并调用QWidget->update()。应该很明显。

如果你想从基于GLUT的窗口中绘制qt小部件......

即。如果你想把Qt小部件“嵌入”GLUT窗口。

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

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

您还必须从框架转发鼠标和键盘事件并将其转换为Qt事件。

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

另见EmbeddedDialogs demo和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())但它可能不太可靠而且我没有我自己试过了。