0

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

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 可以在上下文中绘制。我也可以使用我的视图绘制当前场景的纹理,然后在 paintGL 或glDraw()函数中绘制纹理QGLWidget,但是我如何告诉 Widgetupdate()当视图对此一无所知时。我将用QTimer设置为 0 来模拟主循环。我要做的就是能够QGLWidget在我的框架内触发渲染。有什么建议吗?链接?例子?

4

1 回答 1

0

所以必须有一种方法可以给继承的 QGLWidget 一个指向视图对象的指针,并且无论何时应该绘制视图

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

但是当视图不知道它时,我怎么能告诉小部件更新()。

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

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

即,如果您想将 Qt 小部件“嵌入”到 GLUT 窗口中。

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

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

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

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

另请参阅EmbeddedDialogs演示和QGraphicsProxyWidget

如果您只是想在 glut 窗口之外使用 GUI 小部件,而不是将它们嵌入到 glut 窗口中......

将与您的框架和 glut 消息相关的所有内容包装到QObject中,并将 glut 消息处理放入该对象的插槽中。将此插槽连接到超时运行的计时器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 特定函数(使用QEventLoopclass 或QApplication::processEvents()),但它可能不太可靠,我自己没有尝试过。

于 2013-08-08T12:37:25.853 回答