我在一个小部件中有一个简单的 3D 渲染框架,其中包含负责渲染每一帧的update()
方法。目前,我正在使用QTimer
每秒触发更新60次,这似乎工作正常:
void RenderingWidget::init()
{
// ...
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(16);
}
此解决方案的问题在于,无论渲染速度如何,帧之间都可能有 16 毫秒的空闲时间。在本机 Windows 应用程序中,如果没有其他事件要处理,我可以实现自己的消息循环并连续呈现帧。我可以在Qt中做类似的事情吗?
这是我考虑过的事情,它似乎也可以正常工作,但是我读到这可能是一个不好的做法(即使用QApplication::processEvents()
):
void RenderingWidget::run()
{
running = true;
while(running)
{
update();
QApplication::processEvents();
}
}
我终于想通了!虽然Qt中没有直接访问消息循环,但有一个QObject::event()
方法,其作用类似于Windows API中的WndProc()
。
问题中提到的一种方法是重新实现此方法,方法是对QWidget
或QApplication
进行子类化,然后通过调用 update()
方法处理QEvent::UpdateRequest
事件,或者在任何其他情况下,将事件传递给默认处理程序将处理它的基类(类似于 win API 中的默认窗口过程)。执行更新后,将另一个更新请求添加到队列中,除非中间有其他事件,否则控件将返回到update()
,依此类推。
下面是一个示例实现:
class RenderingWidget : public QWidget
{
Q_OBJECT
public:
explicit RenderingWidget(QWidget *parent = 0);
virtual ~RenderingWidget();
public slots:
void update()
{
// do all rendering work
updateLater(); // queue another update
}
void updateLater()
{
if (!m_updatePending)
{
m_updatePending = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest);
}
}
protected:
bool m_updatePending;
bool event(QEvent* event)
{
if (event == QEvent::UpdateRequest)
{
m_updagePending = false;
update();
return true;
}
else
{
return QWidget::event(event);
}
}
};