我有一个用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()
),但它可能不太可靠,我自己也没有尝试过。