瀏覽代碼

Merge remote-tracking branch 'jcfr/REBASED-3361-tweak-ctkVTKConnection-with-benchmark'

* jcfr/REBASED-3361-tweak-ctkVTKConnection-with-benchmark:
  Fix ctkVTKConnectionTest1 removing connection time ratio test
  Improve performance disabling "deletion" observation
  Fix ctkVTKConnectionTestObjectDelete
  Add ctkVTKConnectionTestObjectDelete
  Add method to return VTK object associated with ctkVTKConnection
  Allow ctkVTKConnection pimpl to be subclassed
  Split disconnect function
  Update ctkVTKConnectionTest1 to allow each test case to be run independently
  Update ctkVTKConnectionTest1 to use ctkCallback
  Refactor ctkVTKConnectionTest1 for easier maintenance and readability
  Remove unused signal "isBroke()"
  Improve documentation of setStrictTypeCheck() method
  Fix qDebug operator to handle case when VTKObject is null
Jean-Christophe Fillion-Robin 11 年之前
父節點
當前提交
aacc20dfe4

+ 8 - 0
Libs/Visualization/VTK/Core/Testing/Cpp/CMakeLists.txt

@@ -5,6 +5,7 @@ set(KIT ${PROJECT_NAME})
 #
 set(TEST_SOURCES
   ctkVTKConnectionTest1.cpp
+  ctkVTKConnectionTestObjectDelete.cpp
   ctkVTKObjectTest1.cpp
   )
 
@@ -36,8 +37,14 @@ set(KIT_HELPER_SRCS
 
 if(CTK_QT_VERSION VERSION_GREATER "4")
   QT5_WRAP_CPP(KIT_HELPER_SRCS ctkVTKObjectTestHelper.h)
+  QT5_GENERATE_MOCS(
+    ctkVTKConnectionTestObjectDelete.cpp
+    )
 else()
   QT4_WRAP_CPP(KIT_HELPER_SRCS ctkVTKObjectTestHelper.h)
+  QT4_GENERATE_MOCS(
+    ctkVTKConnectionTestObjectDelete.cpp
+    )
 endif()
 
 #
@@ -62,6 +69,7 @@ target_link_libraries(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES})
 #
 
 SIMPLE_TEST( ctkVTKConnectionTest1 )
+SIMPLE_TEST( ctkVTKConnectionTestObjectDelete )
 SIMPLE_TEST( ctkVTKObjectTest1 )
 
 #

+ 151 - 73
Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKConnectionTest1.cpp

@@ -22,7 +22,11 @@
 #include <QCoreApplication>
 #include <QDebug>
 #include <QList>
-#include <QTimer>
+#include <QStringList>
+
+
+// CTK includes
+#include <ctkCallback.h>
 
 // CTKVTK includes
 #include "ctkVTKConnection.h"
@@ -30,6 +34,7 @@
 // VTK includes
 #include <vtkCallbackCommand.h>
 #include <vtkCommand.h>
+#include <vtkNew.h>
 #include <vtkObject.h>
 #include <vtkSmartPointer.h>
 #include <vtkTimerLog.h>
@@ -38,119 +43,199 @@
 #include <cstdlib>
 #include <iostream>
 
+namespace
+{
+
+//-----------------------------------------------------------------------------
+int total_event_count;
+
+//-----------------------------------------------------------------------------
+void spy(void* data)
+{
+  Q_UNUSED(data);
+  ++total_event_count;
+}
+
 //-----------------------------------------------------------------------------
 void doit(vtkObject* vtkNotUsed(obj), unsigned long vtkNotUsed(event),
           void* client_data, void* vtkNotUsed(param))
 {
-  QTimer* t = reinterpret_cast<QTimer*>(client_data);
-  t->stop();
+  ctkCallback* t = reinterpret_cast<ctkCallback*>(client_data);
+  t->invoke();
+}
+
+//-----------------------------------------------------------------------------
+void displayDartMeasurement(const char* name, double value)
+{
+  std::cout << "<DartMeasurement name=\""<< name <<"\" "
+            << "type=\"numeric/double\">"
+            << value << "</DartMeasurement>" << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+// This function will trigger a ModifiedEvent <eventCount> times
+// where <objectCount> objects listen that same event.
+bool computeConnectionTiming(const char* name,
+                               int eventCount,
+                               int objectCount, vtkObject* obj,
+                               double & elapsedTime)
+{
+  elapsedTime = 0;
+  total_event_count = 0;
+
+  vtkNew<vtkTimerLog> timerLog;
+  timerLog->StartTimer();
+  for (int i = 0; i < eventCount; ++i)
+    {
+    obj->Modified();
+    }
+  timerLog->StopTimer();
+
+  int expected_total_event_count = eventCount * objectCount;
+  if (total_event_count != expected_total_event_count)
+    {
+    std::cerr << "Problem with " << name << "\n"
+              << "\tcurrent total_event_count:" << total_event_count << "\n"
+              << "\texpected total_event_count:" << expected_total_event_count
+              << std::endl;
+    return false;
+    }
+
+  elapsedTime = timerLog->GetElapsedTime();
+
+  QString measurementName = QString("time-%1-%2-%3").arg(name).arg(eventCount).arg(objectCount);
+  displayDartMeasurement(qPrintable(measurementName), elapsedTime);
+
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool computeConnectionTiming(const char* name,
+                               int eventCount,
+                               int objectCount, vtkObject* obj)
+{
+  double elapsedTime = 0;
+  return computeConnectionTiming(name, eventCount, objectCount, obj, elapsedTime);
 }
 
+} // end of anonymous namespace
+
 //-----------------------------------------------------------------------------
 int ctkVTKConnectionTest1( int argc, char * argv [] )
 {
   QCoreApplication app(argc, argv);
 
+  int testCase = -1;
+  if (argc > 1)
+    {
+    testCase = app.arguments().at(1).toInt();
+    }
+
+  total_event_count = 0;
   int objects = 1000;
   int events = 100;
 
-  vtkObject* obj = vtkObject::New();
+  vtkObject* obj1 = vtkObject::New();
   vtkObject* obj2 = vtkObject::New();
   vtkObject* obj3 = vtkObject::New();
   vtkObject* obj4 = vtkObject::New();
   vtkObject* obj5 = vtkObject::New();
 
+  // NA for connection1
+  int connection2_observeDeletion = 0;
+  int connection3_observeDeletion = 1;
+  int connection4_observeDeletion = 0;
+  int connection5_observeDeletion = 1;
+
   QObject*   topObject = new QObject(0);
-  // It could be here any kind of Qt object, QTimer has a no op slot so use it
-  QTimer*    slotObject = new QTimer(topObject);
+  ctkCallback*    slotObject = new ctkCallback(spy, topObject);
 
   for (int i = 0; i < objects; ++i)
     {
-    ctkVTKConnection* connection = new ctkVTKConnection(topObject);
-    connection->observeDeletion(false);
-    connection->setup(obj, vtkCommand::ModifiedEvent,
-                      slotObject, SLOT(stop()));
-
+    // connection1: regular callback
     vtkCallbackCommand* callback = vtkCallbackCommand::New();
     callback->SetClientData(slotObject);
     callback->SetCallback(doit);
-
-    obj2->AddObserver(vtkCommand::ModifiedEvent, callback);
+    obj1->AddObserver(vtkCommand::ModifiedEvent, callback);
     callback->Delete();
 
+    // connection2
     ctkVTKConnection* connection2 = new ctkVTKConnection(topObject);
-    connection2->observeDeletion(true);
-    connection2->setup(obj3, vtkCommand::ModifiedEvent,
-                      slotObject, SLOT(stop()));
+    connection2->observeDeletion(connection2_observeDeletion);
+    connection2->setup(obj2, vtkCommand::ModifiedEvent,
+                      slotObject, SLOT(invoke()));
 
+    // connection3
     ctkVTKConnection* connection3 = new ctkVTKConnection(topObject);
-    connection3->observeDeletion(false);
-    connection3->setup(obj4, vtkCommand::ModifiedEvent,
-                      new QTimer(topObject), SLOT(stop()));
+    connection3->observeDeletion(connection3_observeDeletion);
+    connection3->setup(obj3, vtkCommand::ModifiedEvent,
+                      slotObject, SLOT(invoke()));
 
+    // connection4
     ctkVTKConnection* connection4 = new ctkVTKConnection(topObject);
-    connection4->observeDeletion(true);
-    connection4->setup(obj5, vtkCommand::ModifiedEvent,
-                      slotObject, SLOT(stop()));
+    connection4->observeDeletion(connection4_observeDeletion);
+    connection4->setup(obj4, vtkCommand::ModifiedEvent,
+                      new ctkCallback(spy, topObject), SLOT(invoke()));
+
+    // connection5
+    ctkVTKConnection* connection5 = new ctkVTKConnection(topObject);
+    connection5->observeDeletion(connection5_observeDeletion);
+    connection5->setup(obj5, vtkCommand::ModifiedEvent,
+                      new ctkCallback(spy, topObject), SLOT(invoke()));
     }
 
-  vtkSmartPointer<vtkTimerLog> timerLog =
-    vtkSmartPointer<vtkTimerLog>::New();
-
-  timerLog->StartTimer();
-  for (int i = 0; i < events; ++i)
+  // Compute timing for connection1: Callback only
+  double time_connection1 = 0;
+  if (testCase == -1 || testCase == 1)
     {
-    obj->Modified();
+    if (!computeConnectionTiming("connection1", events , objects, obj1, time_connection1))
+      {
+      return EXIT_FAILURE;
+      }
     }
-  timerLog->StopTimer();
-
-  double t1 = timerLog->GetElapsedTime();
-  qDebug() << events << "events listened by" << objects << "objects (ctkVTKConnection): " << t1 << "seconds";
 
-  // Callback only
-
-  vtkSmartPointer<vtkTimerLog> timerLog2 =
-    vtkSmartPointer<vtkTimerLog>::New();
-  timerLog2->StartTimer();
-  for (int i = 0; i < events; ++i)
+  // Compute timing for connection2
+  // observeDeletion = 0
+  double time_connection2 = 0;
+  if (testCase == -1 || testCase == 2)
     {
-    obj2->Modified();
+    if (!computeConnectionTiming("connection2", events , objects, obj2, time_connection2))
+      {
+      return EXIT_FAILURE;
+      }
     }
-  timerLog2->StopTimer();
-
-  double t2 = timerLog2->GetElapsedTime();
-  qDebug() << events << "events listened by" << objects <<"objects (vtkCallbacks): " << t2 << "seconds";
-  double ratio = t1 / t2;
-  qDebug() << "ctkVTKConnection / vtkCallbacks: " << ratio;
-
-  vtkSmartPointer<vtkTimerLog> timerLog3 =
-    vtkSmartPointer<vtkTimerLog>::New();
 
-  timerLog3->StartTimer();
-  for (int i = 0; i < events; ++i)
+  // Compute timing for connection3
+  // observeDeletion = 1
+  if (testCase == -1 || testCase == 3)
     {
-    obj3->Modified();
+    if (!computeConnectionTiming("connection3", events , objects, obj3))
+      {
+      return EXIT_FAILURE;
+      }
     }
-  timerLog3->StopTimer();
 
-  double t3 = timerLog3->GetElapsedTime();
-  qDebug() << events << "events listened by" << objects << "objects (observed ctkVTKConnection): " << t3 << "seconds";
-
-  vtkSmartPointer<vtkTimerLog> timerLog4 =
-    vtkSmartPointer<vtkTimerLog>::New();
-
-  timerLog4->StartTimer();
-  for (int i = 0; i < events; ++i)
+  // Compute timing for connection4 - 1-1
+  // observeDeletion = 0
+  if (testCase == -1 || testCase == 4)
     {
-    obj4->Modified();
+    if (!computeConnectionTiming("connection4", events , objects, obj4))
+      {
+      return EXIT_FAILURE;
+      }
     }
-  timerLog4->StopTimer();
-
-  double t4 = timerLog4->GetElapsedTime();
-  qDebug() << events << "events listened by" << objects << "objects (ctkVTKConnection, 1-1): " << t4 << "seconds";
 
+  // Compute timing for connection5 - 1-1
+  // observeDeletion = 1
+  if (testCase == -1 || testCase == 5)
+    {
+    if (!computeConnectionTiming("connection5", events , objects, obj4))
+      {
+      return EXIT_FAILURE;
+      }
+    }
 
-  obj->Delete();
+  obj1->Delete();
   obj2->Delete();
   obj3->Delete();
 
@@ -159,12 +244,5 @@ int ctkVTKConnectionTest1( int argc, char * argv [] )
   obj4->Delete();
   obj5->Delete();
 
-#ifdef QT_NO_DEBUG // In Debug mode, the ratio can be over 2 !
-  // Ideally a ratio ~= 1.
-  if (ratio > 1.2)
-    {
-    return EXIT_FAILURE;
-    }
-#endif
   return EXIT_SUCCESS;
 }

+ 274 - 0
Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKConnectionTestObjectDelete.cpp

@@ -0,0 +1,274 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QCoreApplication>
+#include <QStringList>
+#include <QTimer>
+
+// CTK includes
+#include <ctkCallback.h>
+
+// CTKVTK includes
+#include <ctkVTKConnection.h>
+
+// VTK includes
+#include <vtkCallbackCommand.h>
+#include <vtkCommand.h>
+#include <vtkNew.h>
+#include <vtkObject.h>
+#include <vtkSmartPointer.h>
+#include <vtkTimerLog.h>
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+#include "moc_ctkVTKConnectionTestObjectDelete.cpp"
+
+namespace
+{
+
+//-----------------------------------------------------------------------------
+int total_event_count;
+
+//-----------------------------------------------------------------------------
+void spy(void* data)
+{
+  Q_UNUSED(data);
+  ++total_event_count;
+}
+
+//-----------------------------------------------------------------------------
+template<typename T>
+bool check(int line, const char* valueName, T current, T expected)
+{
+  if (current != expected)
+    {
+    std::cerr << "Line " << line << "\n"
+              << "\tcurrent " << valueName << ":" << current << "\n"
+              << "\texpected " << valueName << ":" << expected
+              << std::endl;
+    return false;
+    }
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+void displayDartMeasurement(const char* name, double value)
+{
+  std::cout << "<DartMeasurement name=\""<< name <<"\" "
+            << "type=\"numeric/double\">"
+            << value << "</DartMeasurement>" << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+bool computeTimingAfterObjectDelete(bool observeDeletion,
+                                    int connectionCount,
+                                    int objectCount,
+                                    bool deleteVTK,
+                                    bool deleteQt)
+{
+  vtkNew<vtkTimerLog> timerLog;
+
+  QObject* topObject = new QObject(0);
+  ctkCallback* slotObject = new ctkCallback(spy, topObject);
+
+  QList< ctkVTKConnection* > connections;
+  QList< vtkSmartPointer<vtkObject> > objects;
+
+  total_event_count = 0;
+
+  for (int connectionIdx = 0; connectionIdx < connectionCount; ++connectionIdx)
+    {
+
+    for (int objectIdx = 0; objectIdx < objectCount; ++objectIdx)
+      {
+      vtkNew<vtkObject> object;
+
+      ctkVTKConnection* connection = new ctkVTKConnection(topObject);
+      connection->observeDeletion(observeDeletion);
+      connection->setup(object.GetPointer(), vtkCommand::ModifiedEvent,
+                        slotObject, SLOT(invoke()));
+
+      connections.append(connection);
+      objects.append(object.GetPointer());
+      }
+    }
+
+  QString attribute(observeDeletion ? "observeDeletion" : "noObserveDeletion");
+
+  {
+    timerLog->StartTimer();
+    foreach(vtkSmartPointer<vtkObject> object, objects)
+      {
+      object->Modified();
+      }
+    timerLog->StopTimer();
+
+    QString measurementName = QString("time_%1-%2-%3-%4").arg(
+          "connection-object-modified").arg(attribute).arg(connectionCount).arg(objectCount);
+    displayDartMeasurement(qPrintable(measurementName), timerLog->GetElapsedTime());
+
+    if (!check<int>(__LINE__, "total_event_count",
+                    /* current= */ total_event_count,
+                    /* expected = */ connectionCount * objectCount))
+      {
+      return false;
+      }
+  }
+
+  if (deleteVTK)
+    {
+    total_event_count = 0;
+
+    timerLog->StartTimer();
+    objects.clear();
+    timerLog->StopTimer();
+
+    QString measurementName = QString("time_%1-%2-%3-%4").arg(
+          "connection-vtkobject-deleted").arg(attribute).arg(connectionCount).arg(objectCount);
+    displayDartMeasurement(qPrintable(measurementName), timerLog->GetElapsedTime());
+
+    if (!check<int>(__LINE__, "total_event_count",
+                    /* current= */ total_event_count,
+                    /* expected = */ 0))
+      {
+      return false;
+      }
+
+    if (!check<void*>(__LINE__, "connection_0_vtkobject",
+                    /* current= */ connections.at(0)->vtkobject(),
+                    /* expected = */ 0))
+      {
+      return false;
+      }
+
+    if (!check<void*>(__LINE__, "connection_last_vtkobject",
+                    /* current= */ connections.last()->vtkobject(),
+                    /* expected = */ 0))
+      {
+      return false;
+      }
+    }
+
+  if (deleteQt)
+    {
+    total_event_count = 0;
+
+    timerLog->StartTimer();
+    QTimer::singleShot(0, slotObject, SLOT(deleteLater()));
+    QCoreApplication::sendPostedEvents();
+    QCoreApplication::processEvents();
+    timerLog->StopTimer();
+
+    QString measurementName = QString("time_%1-%2-%3-%4").arg(
+          "connection-qtobject-deleted").arg(attribute).arg(connectionCount).arg(objectCount);
+    displayDartMeasurement(qPrintable(measurementName), timerLog->GetElapsedTime());
+
+    if (!check<int>(__LINE__, "total_event_count",
+                    /* current= */ total_event_count,
+                    /* expected = */ 0))
+      {
+      return false;
+      }
+
+    if (!check<void*>(__LINE__, "connection_0_qtobject",
+                    /* current= */ connections.at(0)->object(),
+                    /* expected = */ 0))
+      {
+      return false;
+      }
+
+    if (!check<void*>(__LINE__, "connection_last_qtobject",
+                    /* current= */ connections.last()->object(),
+                    /* expected = */ 0))
+      {
+      return false;
+      }
+    }
+
+  delete topObject;
+
+  return true;
+}
+
+} // end of anonymous namespace
+
+//-----------------------------------------------------------------------------
+int ctkVTKConnectionTestObjectDelete( int argc, char * argv [] )
+{
+  QCoreApplication app(argc, argv);
+
+  int testCase = -1;
+  if (argc > 1)
+    {
+    testCase = app.arguments().at(1).toInt();
+    }
+
+  int connectionCount = 1000;
+  int objectCount = 100;
+
+  if (testCase == -1 || testCase == 1)
+    {
+    if (!computeTimingAfterObjectDelete(/* observeDeletion = */ true,
+                                        connectionCount, objectCount,
+                                        /* deleteVTK = */ true,
+                                        /* deleteQt = */ false))
+      {
+      return EXIT_FAILURE;
+      }
+    }
+
+  if (testCase == -1 || testCase == 2)
+    {
+    if (!computeTimingAfterObjectDelete(/* observeDeletion = */ false,
+                                        connectionCount, objectCount,
+                                        /* deleteVTK = */ true,
+                                        /* deleteQt = */ false))
+      {
+      return EXIT_FAILURE;
+      }
+    }
+
+  if (testCase == -1 || testCase == 3)
+    {
+    if (!computeTimingAfterObjectDelete(/* observeDeletion = */ true,
+                                        connectionCount, objectCount,
+                                        /* deleteVTK = */ false,
+                                        /* deleteQt = */ true))
+      {
+      return EXIT_FAILURE;
+      }
+    }
+
+  if (testCase == -1 || testCase == 4)
+    {
+    if (!computeTimingAfterObjectDelete(/* observeDeletion = */ false,
+                                        connectionCount, objectCount,
+                                        /* deleteVTK = */ false,
+                                        /* deleteQt = */ true))
+      {
+      return EXIT_FAILURE;
+      }
+    }
+
+  return EXIT_SUCCESS;
+}

+ 36 - 26
Libs/Visualization/VTK/Core/ctkVTKConnection.cpp

@@ -52,7 +52,6 @@ ctkVTKConnectionPrivate::ctkVTKConnectionPrivate(ctkVTKConnection& object)
   this->Callback    = vtkSmartPointer<vtkCallbackCommand>::New();
   this->Callback->SetCallback(ctkVTKConnectionPrivate::DoCallback);
   this->Callback->SetClientData(this);
-  this->VTKObject   = 0;
   this->QtObject    = 0;
   this->VTKEvent    = vtkCommand::NoEvent;
   this->Priority    = 0.0;
@@ -111,15 +110,17 @@ void ctkVTKConnectionPrivate::connect()
       {
       this->VTKObject->AddObserver(vtkCommand::DeleteEvent, this->Callback);
       }
-    // Remove itself from its parent when vtkObject is deleted
-    QObject::connect(this->QtObject, SIGNAL(destroyed(QObject*)), 
-                     q, SLOT(qobjectDeleted()));
     }
+
+  // When Qt object is destroyed: (1) remove VTK observers and
+  // (2) set QtObject pointer to 0.
+  QObject::connect(this->QtObject, SIGNAL(destroyed(QObject*)),
+                   q, SLOT(qobjectDeleted()));
   this->Connected = true;
 }
 
 //-----------------------------------------------------------------------------
-void ctkVTKConnectionPrivate::disconnect()
+void ctkVTKConnectionPrivate::disconnectSlots()
 {
   Q_Q(ctkVTKConnection);
   
@@ -150,20 +151,21 @@ void ctkVTKConnectionPrivate::disconnect()
       }
     }
 
+  this->Connected = false;
+}
+
+//-----------------------------------------------------------------------------
+void ctkVTKConnectionPrivate::disconnectVTKObject()
+{
+  Q_Q(ctkVTKConnection);
   if (this->VTKObject)
     {
     q->removeObserver(this->VTKObject, this->VTKEvent, this->Callback);
-    this->VTKObject->RemoveObservers(vtkCommand::DeleteEvent, this->Callback);
-    }
-
-  if (this->ObserveDeletion && this->QtObject)
-    {
-    //this->VTKObject->AddObserver(vtkCommand::DeleteEvent, this->Callback); has already been removed
-    QObject::disconnect(this->QtObject, SIGNAL(destroyed(QObject*)),
-                        q, SIGNAL(isBroke()));
+    if (this->ObserveDeletion)
+      {
+      this->VTKObject->RemoveObservers(vtkCommand::DeleteEvent, this->Callback);
+      }
     }
-
-  this->Connected = false;
 }
 
 //-----------------------------------------------------------------------------
@@ -208,14 +210,18 @@ ctkVTKConnection::ctkVTKConnection(QObject* _parent):
 {
 }
 
+// --------------------------------------------------------------------------
+ctkVTKConnection::ctkVTKConnection(ctkVTKConnectionPrivate* pimpl, QObject* _parent)
+  : Superclass(_parent)
+  , d_ptr(pimpl)
+{
+}
+
 //-----------------------------------------------------------------------------
 ctkVTKConnection::~ctkVTKConnection()
 {
   Q_D(ctkVTKConnection);
-  if (d->ObserveDeletion)
-    {
-    this->disconnect();
-    }
+  d->disconnectVTKObject();
 }
 
 //-----------------------------------------------------------------------------
@@ -233,12 +239,19 @@ QObject* ctkVTKConnection::object()const
 }
 
 //-----------------------------------------------------------------------------
+vtkObject* ctkVTKConnection::vtkobject() const
+{
+  Q_D(const ctkVTKConnection);
+  return const_cast<vtkObject*>(d->VTKObject.GetPointer());
+}
+
+//-----------------------------------------------------------------------------
 QDebug operator<<(QDebug dbg, const ctkVTKConnection& connection)
 {
   const ctkVTKConnectionPrivate* d = connection.d_func();
   dbg.nospace() << "ctkVTKConnection:" << &connection << endl
                 << "Id:" << d->Id << endl
-                << " VTKObject:" << d->VTKObject->GetClassName()
+                << " VTKObject:" << (d->VTKObject ? d->VTKObject->GetClassName() : "<null>")
                 << "(" << d->VTKObject << ")" << endl
                 << " QtObject:" << d->QtObject << endl
                 << " VTKEvent:" << d->VTKEvent << endl
@@ -441,16 +454,14 @@ bool ctkVTKConnection::deletionObserved()const
 void ctkVTKConnection::disconnect()
 {
   Q_D(ctkVTKConnection);
-  d->disconnect();
+  d->disconnectVTKObject();
 }
 
 //-----------------------------------------------------------------------------
 void ctkVTKConnection::vtkObjectDeleted()
 {
   Q_D(ctkVTKConnection);
-  d->VTKObject = 0;
-  d->disconnect();
-  emit isBroke();
+  d->disconnectSlots();
 }
 
 //-----------------------------------------------------------------------------
@@ -458,8 +469,7 @@ void ctkVTKConnection::qobjectDeleted()
 {
   Q_D(ctkVTKConnection);
   d->QtObject = 0;
-  d->disconnect();
-  emit isBroke();
+  d->disconnectVTKObject();
 }
 
 //-----------------------------------------------------------------------------

+ 8 - 5
Libs/Visualization/VTK/Core/ctkVTKConnection.h

@@ -62,6 +62,7 @@ public:
   /// vtkObject*, void*, unsigned long, void*: sender, callData, eventId, clientData
   /// Of course the slot can contain less parameters, but always the same order
   /// though.
+  /// \sa object(), vtkobject()
   void setup(vtkObject* vtk_obj, unsigned long vtk_event,
     const QObject* qt_obj, const char* qt_slot, float priority = 0.f,
     Qt::ConnectionType connectionType = Qt::AutoConnection);
@@ -86,8 +87,14 @@ public:
   /// 
   /// Return a string uniquely identifying the connection within the current process
   QString  id()const;
+
+  ///
+  /// Return the QObject set using setup() method.
   QObject* object()const;
 
+  /// Return the vtkObject set using setup() method.
+  vtkObject* vtkobject() const;
+
   /// false by default, it is slower to observe vtk object deletion
   void observeDeletion(bool enable);
   bool deletionObserved()const;
@@ -104,17 +111,13 @@ Q_SIGNALS:
   /// connect(obj1,SIGNAL(signalFunc(A,B,C,D)),obj2,SLOT(slotFunc(A)));
   void emitExecute(vtkObject* caller, void* call_data, unsigned long vtk_event, void* client_data);
 
-  /// The signal is fired when the observed vtk object or the receiving qt 
-  /// object is deleted. It can conveniently connected to the deleteLater 
-  /// slot
-  void isBroke();
-
 protected Q_SLOTS:
   void vtkObjectDeleted();
   void qobjectDeleted();
 
 protected:
   QScopedPointer<ctkVTKConnectionPrivate> d_ptr;
+  ctkVTKConnection(ctkVTKConnectionPrivate* pimpl, QObject* _parent);
 
   void disconnect();
   virtual void addObserver(vtkObject* caller, unsigned long vtk_event, vtkCallbackCommand* callback, float priority=0.0f);

+ 7 - 3
Libs/Visualization/VTK/Core/ctkVTKConnection_p.h

@@ -30,12 +30,15 @@ class QObject;
 
 // VTK includes
 #include <vtkSmartPointer.h>
+#include <vtkWeakPointer.h>
 class vtkObject;
 class vtkCallbackCommand;
 
+#include "ctkVisualizationVTKCoreExport.h"
+
 //-----------------------------------------------------------------------------
 /// \ingroup Visualization_VTK_Core
-class ctkVTKConnectionPrivate
+class CTK_VISUALIZATION_VTK_CORE_EXPORT ctkVTKConnectionPrivate
 {
   Q_DECLARE_PUBLIC(ctkVTKConnection);
 protected:
@@ -53,7 +56,8 @@ public:
   ~ctkVTKConnectionPrivate();
 
   void connect();
-  void disconnect();
+  void disconnectSlots();
+  void disconnectVTKObject();
 
   bool IsSameQtSlot(const char* qt_slot)const;
 
@@ -67,7 +71,7 @@ public:
   void execute(vtkObject* vtk_obj, unsigned long vtk_event, void* client_data, void* call_data);
 
   vtkSmartPointer<vtkCallbackCommand> Callback;
-  vtkObject*                          VTKObject;
+  vtkWeakPointer<vtkObject>           VTKObject;
   const QObject*                      QtObject;
   unsigned long                       VTKEvent;
   QString                             QtSlot;

+ 1 - 2
Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.cpp

@@ -139,8 +139,7 @@ ctkVTKObjectEventsObserverPrivate::ctkVTKObjectEventsObserverPrivate(ctkVTKObjec
 {
   this->StrictTypeCheck = false;
   this->AllBlocked = false;
-  // ObserveDeletion == false  hasn't been that well tested...
-  this->ObserveDeletion = true;
+  this->ObserveDeletion = false;
 }
 
 //-----------------------------------------------------------------------------

+ 1 - 0
Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.h

@@ -70,6 +70,7 @@ public:
   bool strictTypeCheck()const;
   /// Set the strictTypeCheck value.
   /// \sa strictTypeCheck, strictTypeCheck()
+  /// \note By default, strict type checking is disabled.
   void setStrictTypeCheck(bool check);
 
   ///