Commit 1d247c8d authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

QVTKWidget: add ability to defer render in paintEvent

Calling iren->Render() in paint event is fraught with problems
especially for applications that want to show progress when rendering is
happening. Added a mode to QVTKWidget to allow deferring render requests
in paintEvent so they happen on idle. This avoids recursive paint call
errors due to updating progress bars to show progress of the render.
parent 58c9e5f2
Pipeline #16542 passed with stage
......@@ -77,6 +77,7 @@ QVTKWidget::QVTKWidget(QWidget* p, Qt::WindowFlags f)
: QWidget(p, f | Qt::MSWindowsOwnDC), mRenWin(NULL),
cachedImageCleanFlag(false),
automaticImageCache(false), maxImageCacheRenderRate(1.0),
mDeferRenderInPaintEvent(false),
renderEventCallbackObserverId(0)
{
this->UseTDx=false;
......@@ -105,6 +106,9 @@ QVTKWidget::QVTKWidget(QWidget* p, Qt::WindowFlags f)
mIrenAdapter = new QVTKInteractorAdapter(this);
this->mDeferedRenderTimer.setSingleShot(true);
this->mDeferedRenderTimer.setInterval(0);
this->connect(&this->mDeferedRenderTimer, SIGNAL(timeout()), SLOT(doDeferredRender()));
}
/*! destructor */
......@@ -328,6 +332,16 @@ double QVTKWidget::maxRenderRateForImageCache() const
return this->maxImageCacheRenderRate;
}
void QVTKWidget::setDeferRenderInPaintEvent(bool val)
{
this->mDeferRenderInPaintEvent = val;
}
bool QVTKWidget::deferRenderInPaintEvent() const
{
return this->mDeferRenderInPaintEvent;
}
vtkImageData* QVTKWidget::cachedImage()
{
// Make sure image is up to date.
......@@ -444,30 +458,39 @@ void QVTKWidget::moveEvent(QMoveEvent* e)
*/
void QVTKWidget::paintEvent(QPaintEvent* )
{
vtkRenderWindowInteractor* iren = NULL;
if(this->mRenWin)
{
iren = this->mRenWin->GetInteractor();
}
if(!iren || !iren->GetEnabled())
vtkRenderWindowInteractor* iren = this->mRenWin ? this->mRenWin->GetInteractor() : NULL;
if (!iren || !iren->GetEnabled())
{
return;
}
// In Qt 4.1+ let's support redirected painting
// if redirected, let's grab the image from VTK, and paint it to the device
QPaintDevice* device = QPainter::redirected(this);
bool usingRedirectedDevice = (device != NULL && device != this);
// if we have a saved image, use it
if (this->paintCachedImage())
if (this->paintCachedImage() == false)
{
return;
// we don't defer render in redirected painting is active since the target
// being painted to may not be around when the deferred render call happens.
if (!usingRedirectedDevice && this->mDeferRenderInPaintEvent)
{
this->deferRender();
}
else
{
iren->Render();
}
}
iren->Render();
// In Qt 4.1+ let's support redirected painting
// if redirected, let's grab the image from VTK, and paint it to the device
QPaintDevice* device = QPainter::redirected(this);
if(device != NULL && device != this)
// Irrespective of whether cache was used on or, if using redirected painting
// is being employed, we need to "paint" the image from the render window to
// the redirected target.
if (usingRedirectedDevice)
{
Q_ASSERT(device);
int w = this->width();
int h = this->height();
QImage img(w, h, QImage::Format_RGB32);
......@@ -480,7 +503,6 @@ void QVTKWidget::paintEvent(QPaintEvent* )
QPainter painter(this);
painter.drawImage(QPointF(0.0,0.0), img);
return;
}
}
......@@ -867,6 +889,10 @@ void QVTKWidget::renderEventCallback()
}
}
// Render happened. If we have requested a render to happen, it has happened,
// so no need to request another render. Stop the timer.
this->mDeferedRenderTimer.stop();
this->markCachedImageAsDirty();
if (this->isAutomaticImageCacheEnabled() &&
(this->mRenWin->GetDesiredUpdateRate() < this->maxRenderRateForImageCache()))
......@@ -875,3 +901,19 @@ void QVTKWidget::renderEventCallback()
}
}
}
//-----------------------------------------------------------------------------
void QVTKWidget::deferRender()
{
this->mDeferedRenderTimer.start();
}
//-----------------------------------------------------------------------------
void QVTKWidget::doDeferredRender()
{
vtkRenderWindowInteractor* iren = this->mRenWin ? this->mRenWin->GetInteractor() : NULL;
if (iren && iren->GetEnabled())
{
iren->Render();
}
}
......@@ -39,6 +39,7 @@
#include "vtkGUISupportQtModule.h" // For export macro
#include "QVTKInteractor.h"
#include <QWidget>
#include <QTimer>
class QVTKInteractorAdapter;
......@@ -74,6 +75,9 @@ class VTKGUISUPPORTQT_EXPORT QVTKWidget : public QWidget
Q_PROPERTY(double maxRenderRateForImageCache
READ maxRenderRateForImageCache
WRITE setMaxRenderRateForImageCache)
Q_PROPERTY(bool deferRenderInPaintEvent
READ deferRenderInPaintEvent
WRITE setDeferRenderInPaintEvent)
public:
//! constructor
......@@ -113,7 +117,7 @@ public:
// Description:
// If automatic image caching is enabled, then the image will be cached
// after every render with a DesiredUpdateRate that is greater than
// after every render with a DesiredUpdateRate that is less than
// this parameter. By default, the vtkRenderWindowInteractor will
// change the desired render rate depending on the user's
// interactions. (See vtkRenderWindow::DesiredUpdateRate,
......@@ -143,6 +147,18 @@ public:
void SetUseTDx(bool useTDx);
bool GetUseTDx() const;
// Description:
// When set to true (default is false), paintEvent() will never directly trigger
// a render on the vtkRenderWindow (via vtkRenderWindowInteractor::Render()).
// Instead, it starts a timer that then triggers the render on idle. This, in
// general is a good strategy for cases where Render may take a while with
// applications wanting to report progress and consequently trigger paint
// events on other widgets like progress bars, etc.
// There is one caveat: when paintEvent() is called using a redirected paint device,
// then this flag is ignored and the paintEvent() will trigger
// vtkRenderWindowInteractor::Render(), if needed.
void setDeferRenderInPaintEvent(bool val);
bool deferRenderInPaintEvent() const;
Q_SIGNALS:
// Description:
......@@ -180,6 +196,17 @@ public Q_SLOTS:
void setDevice(vtkTDxDevice *device);
#endif
protected Q_SLOTS:
// Description:
// Request to defer a render call i.e. start the mDeferedRenderTimer. When the
// timer times out, it will call doDeferredRender() to do the actual
// rendering.
virtual void deferRender();
// Description:
// Called when the mDeferedRenderTimer times out to do the rendering.
virtual void doDeferredRender();
protected:
// overloaded resize handler
virtual void resizeEvent(QResizeEvent* event);
......@@ -250,12 +277,13 @@ protected:
#endif
protected:
vtkImageData* mCachedImage;
bool cachedImageCleanFlag;
bool automaticImageCache;
double maxImageCacheRenderRate;
QVTKInteractorAdapter* mIrenAdapter;
bool mDeferRenderInPaintEvent;
private:
//! unimplemented operator=
......@@ -269,6 +297,7 @@ private:
// Callback called on every vtkCommand::RenderEvent fired by the
// vtkRenderWindow.
void renderEventCallback();
QTimer mDeferedRenderTimer;
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment