Ver código fonte

ctkVTKAbstractView controls the update rate

It ensures that the DesiredUpdateRate of the render window is respected.
(DesiredUpdateRate is typically set by the interactor style using the
 values from the render window interactor).
Julien Finet 13 anos atrás
pai
commit
d928095dbb

+ 59 - 7
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp

@@ -46,7 +46,7 @@ ctkVTKAbstractViewPrivate::ctkVTKAbstractViewPrivate(ctkVTKAbstractView& object)
 {
 {
   this->RenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
   this->RenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
   this->CornerAnnotation = vtkSmartPointer<vtkCornerAnnotation>::New();
   this->CornerAnnotation = vtkSmartPointer<vtkCornerAnnotation>::New();
-  this->RenderPending = false;
+  this->RequestTimer = 0;
   this->RenderEnabled = true;
   this->RenderEnabled = true;
 }
 }
 
 
@@ -63,8 +63,16 @@ void ctkVTKAbstractViewPrivate::init()
   q->layout()->setSpacing(0);
   q->layout()->setSpacing(0);
   q->layout()->addWidget(this->VTKWidget);
   q->layout()->addWidget(this->VTKWidget);
 
 
+  this->RequestTimer = new QTimer(q);
+  this->RequestTimer->setSingleShot(true);
+  QObject::connect(this->RequestTimer, SIGNAL(timeout()),
+                   q, SLOT(forceRender()));
+
   this->setupCornerAnnotation();
   this->setupCornerAnnotation();
   this->setupRendering();
   this->setupRendering();
+
+  // block renders and observe interactor to enforce framerate
+  q->setInteractor(this->RenderWindow->GetInteractor());
 }
 }
 
 
 // --------------------------------------------------------------------------
 // --------------------------------------------------------------------------
@@ -143,18 +151,44 @@ void ctkVTKAbstractView::scheduleRender()
 {
 {
   Q_D(ctkVTKAbstractView);
   Q_D(ctkVTKAbstractView);
 
 
-  logger.trace(QString("scheduleRender - RenderEnabled: %1 - RenderPending: %2").
+  logger.trace(QString("scheduleRender - RenderEnabled: %1 - Request render elapsed: %2ms").
                arg(d->RenderEnabled ? "true" : "false")
                arg(d->RenderEnabled ? "true" : "false")
-               .arg(d->RenderPending ? "true:" : "false"));
+               .arg(d->RequestTime.elapsed()));
 
 
   if (!d->RenderEnabled)
   if (!d->RenderEnabled)
     {
     {
     return;
     return;
     }
     }
-  if (!d->RenderPending)
+
+  double msecsBeforeRender = 100. / d->RenderWindow->GetDesiredUpdateRate();
+  if(d->VTKWidget->testAttribute(Qt::WA_WState_InPaintEvent))
+    {
+    // If the request comes from the system (widget exposed, resized...), the
+    // render must be done immediately.
+    this->forceRender();
+    }
+  else if (!d->RequestTime.isValid())
+    {
+    // If the DesiredUpdateRate is in "still mode", the requested framerate
+    // is fake, it is just a way to allocate as much time as possible for the
+    // rendering, it doesn't really mean that rendering must occur only once
+    // every couple seconds. It just means it should be done when there is
+    // time to do it. A timer of 0, kind of mean a rendering is done next time
+    // it is idle.
+    if (msecsBeforeRender > 10000)
+      {
+      msecsBeforeRender = 0;
+      }
+    d->RequestTime.start();
+    d->RequestTimer->start(static_cast<int>(msecsBeforeRender));
+    }
+  else if (d->RequestTime.elapsed() > msecsBeforeRender)
     {
     {
-    d->RenderPending = true;
-    QTimer::singleShot(0, this, SLOT(forceRender()));
+    // The rendering hasn't still be done, but msecsBeforeRender milliseconds
+    // have already been elapsed, it is likely that RequestTimer has already
+    // timed out, but the event queue hasn't been processed yet, rendering is
+    // done now to ensure the desired framerate is respected.
+    this->forceRender();
     }
     }
 }
 }
 
 
@@ -163,7 +197,19 @@ void ctkVTKAbstractView::forceRender()
 {
 {
   Q_D(ctkVTKAbstractView);
   Q_D(ctkVTKAbstractView);
 
 
-  d->RenderPending = false;
+  if (this->sender() == d->RequestTimer  &&
+      !d->RequestTime.isValid())
+    {
+    // The slot associated to the timeout signal is now called, however the
+    // render has already been executed meanwhile. There is no need to do it
+    // again.
+    return;
+    }
+
+  // The timer can be stopped if it hasn't timed out yet.
+  d->RequestTimer->stop();
+  d->RequestTime = QTime();
+
   logger.trace(QString("forceRender - RenderEnabled: %1")
   logger.trace(QString("forceRender - RenderEnabled: %1")
                .arg(d->RenderEnabled ? "true" : "false"));
                .arg(d->RenderEnabled ? "true" : "false"));
 
 
@@ -185,6 +231,12 @@ void ctkVTKAbstractView::setInteractor(vtkRenderWindowInteractor* newInteractor)
   logger.trace("setInteractor");
   logger.trace("setInteractor");
 
 
   d->RenderWindow->SetInteractor(newInteractor);
   d->RenderWindow->SetInteractor(newInteractor);
+  // Prevent the interactor to call Render() on the render window; only
+  // scheduleRender() and forceRender() can Render() the window.
+  // This is done to ensure the desired framerate is respected.
+  newInteractor->SetEnableRender(false);
+  qvtkReconnect(d->RenderWindow->GetInteractor(), newInteractor,
+                vtkCommand::RenderEvent, this, SLOT(scheduleRender()));
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------

+ 12 - 2
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h

@@ -28,6 +28,7 @@
 #include <QVTKWidget.h>
 #include <QVTKWidget.h>
 
 
 // CTK includes
 // CTK includes
+#include "ctkVTKObject.h"
 #include "ctkVisualizationVTKWidgetsExport.h"
 #include "ctkVisualizationVTKWidgetsExport.h"
 class ctkVTKAbstractViewPrivate;
 class ctkVTKAbstractViewPrivate;
 
 
@@ -39,6 +40,7 @@ class vtkRenderWindow;
 class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKAbstractView : public QWidget
 class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKAbstractView : public QWidget
 {
 {
   Q_OBJECT
   Q_OBJECT
+  QVTK_OBJECT
   Q_PROPERTY(QString cornerAnnotationText READ cornerAnnotationText WRITE setCornerAnnotationText)
   Q_PROPERTY(QString cornerAnnotationText READ cornerAnnotationText WRITE setCornerAnnotationText)
   Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
   Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
   Q_PROPERTY(QColor backgroundColor2 READ backgroundColor2 WRITE setBackgroundColor)
   Q_PROPERTY(QColor backgroundColor2 READ backgroundColor2 WRITE setBackgroundColor)
@@ -51,11 +53,15 @@ public:
   virtual ~ctkVTKAbstractView();
   virtual ~ctkVTKAbstractView();
 
 
 public slots:
 public slots:
-
-  /// If a render has already been scheduled, this called is a no-op
+  /// Notify QVTKWidget that the view needs to be rendered.
+  /// scheduleRender() respects the desired framerate of the render window,
+  /// it won't render the window more than what the current render window
+  /// framerate is.
   void scheduleRender();
   void scheduleRender();
 
 
   /// Force a render even if a render is already ocurring
   /// Force a render even if a render is already ocurring
+  /// Be careful when calling forceRender() as it can slow down your
+  /// application. It is preferable to use scheduleRender() instead.
   void forceRender();
   void forceRender();
 
 
   /// Set the background color of the rendering screen.
   /// Set the background color of the rendering screen.
@@ -81,6 +87,10 @@ public:
 
 
   /// Set/Get window interactor
   /// Set/Get window interactor
   Q_INVOKABLE vtkRenderWindowInteractor* interactor()const;
   Q_INVOKABLE vtkRenderWindowInteractor* interactor()const;
+  /// QVTKWidget catches all render requests, and ensure the desired framerate
+  /// is respected.
+  /// The interactor never calls Render() on the render window.
+  /// TBD: can we only set a QVTKRenderWindowInteractor ?
   virtual void setInteractor(vtkRenderWindowInteractor* interactor);
   virtual void setInteractor(vtkRenderWindowInteractor* interactor);
 
 
   /// Get current interactor style
   /// Get current interactor style

+ 4 - 1
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView_p.h

@@ -23,6 +23,8 @@
 
 
 // Qt includes
 // Qt includes
 #include <QObject>
 #include <QObject>
+#include <QTime>
+class QTimer;
 
 
 // CTK includes
 // CTK includes
 #include "ctkVTKAbstractView.h"
 #include "ctkVTKAbstractView.h"
@@ -56,7 +58,8 @@ public:
 
 
   QVTKWidget*                                   VTKWidget;
   QVTKWidget*                                   VTKWidget;
   vtkSmartPointer<vtkRenderWindow>              RenderWindow;
   vtkSmartPointer<vtkRenderWindow>              RenderWindow;
-  bool                                          RenderPending;
+  QTimer*                                       RequestTimer;
+  QTime                                         RequestTime;
   bool                                          RenderEnabled;
   bool                                          RenderEnabled;
 
 
   vtkSmartPointer<vtkCornerAnnotation>          CornerAnnotation;
   vtkSmartPointer<vtkCornerAnnotation>          CornerAnnotation;

+ 4 - 0
Libs/Visualization/VTK/Widgets/ctkVTKRenderView.cpp

@@ -147,6 +147,7 @@ void ctkVTKRenderViewPrivate::yaw(int rotateDegrees,
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void ctkVTKRenderViewPrivate::doSpin()
 void ctkVTKRenderViewPrivate::doSpin()
 {
 {
+  Q_Q(ctkVTKRenderView);
   if (!this->SpinEnabled)
   if (!this->SpinEnabled)
     {
     {
     return;
     return;
@@ -168,12 +169,14 @@ void ctkVTKRenderViewPrivate::doSpin()
       break;
       break;
     }
     }
 
 
+  q->forceRender();
   QTimer::singleShot(this->AnimationIntervalMs, this, SLOT(doSpin()));
   QTimer::singleShot(this->AnimationIntervalMs, this, SLOT(doSpin()));
 }
 }
 
 
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void ctkVTKRenderViewPrivate::doRock()
 void ctkVTKRenderViewPrivate::doRock()
 {
 {
+  Q_Q(ctkVTKRenderView);
   Q_ASSERT(this->Renderer->IsActiveCameraCreated());
   Q_ASSERT(this->Renderer->IsActiveCameraCreated());
 
 
   if (!this->RockEnabled)
   if (!this->RockEnabled)
@@ -195,6 +198,7 @@ void ctkVTKRenderViewPrivate::doRock()
   //Make the lighting follow the camera to avoid illumination changes
   //Make the lighting follow the camera to avoid illumination changes
   this->Renderer->UpdateLightsGeometryToFollowCamera();
   this->Renderer->UpdateLightsGeometryToFollowCamera();
 
 
+  q->forceRender();
   QTimer::singleShot(this->AnimationIntervalMs, this, SLOT(doRock()));
   QTimer::singleShot(this->AnimationIntervalMs, this, SLOT(doRock()));
 }
 }