Kaynağa Gözat

Merge branch 'ensure-python-is-finalized'

* ensure-python-is-finalized:
  Fix segfault when vtkObject observed in python is deleted after interpreter is "ended"
  Fix segfaults associated with ctkSimplePythonShell tests
  Fix memory leaks associated with ctkSimplePythonShell tests
  Memory leaks associated with ctkSimplePythonShell tests are failure
  Set sys.path with vtk package path independently of VTK build tree location
Jean-Christophe Fillion-Robin 13 yıl önce
ebeveyn
işleme
7b364f436f

+ 4 - 2
Applications/ctkSimplePythonShell/CMakeLists.txt

@@ -22,8 +22,9 @@ set(KIT_SRCS
 if(CTK_LIB_Scripting/Python/Core_PYTHONQT_USE_VTK)
   list(APPEND KIT_SRCS
     ctkTestWrappedQListOfVTKObject.h
-    ctkTestWrappedVTKSlot.h
+    ctkTestWrappedVTKObserver.h
     ctkTestWrappedVTKQInvokable.h
+    ctkTestWrappedVTKSlot.h
     )
 endif()
 
@@ -39,8 +40,9 @@ set(KIT_MOC_SRCS
 if(CTK_LIB_Scripting/Python/Core_PYTHONQT_USE_VTK)
   list(APPEND KIT_MOC_SRCS
     ctkTestWrappedQListOfVTKObject.h
-    ctkTestWrappedVTKSlot.h
+    ctkTestWrappedVTKObserver.h
     ctkTestWrappedVTKQInvokable.h
+    ctkTestWrappedVTKSlot.h
     )
 endif()
 

+ 1 - 0
Applications/ctkSimplePythonShell/Testing/Python/CMakeLists.txt

@@ -21,6 +21,7 @@ endif()
 if(CTK_LIB_Scripting/Python/Core_PYTHONQT_USE_VTK)
   list(APPEND SCRIPTS
     vtkPythonSmoke.py
+    wrappedVTKObserverTest.py
     wrappedVTKQInvokableTest.py
     wrappedVTKSlotTest.py
     )

+ 7 - 7
Applications/ctkSimplePythonShell/Testing/Python/vtkPythonSmoke.py

@@ -3,19 +3,19 @@
 # Copied from VTK/Common/Testing/Python/PythonSmoke.py
 #
 
-import sys
+import qt
 
 try:
   import vtk
 
 except:
   print "Cannot import vtk"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 try:
   print dir(vtk)
 except:
   print "Cannot print dir(vtk)"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
 try:
   try:
@@ -30,7 +30,7 @@ try:
     print "Using Common"
 except:
   print "Cannot create vtkObject"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
 try:
   print o
@@ -38,7 +38,7 @@ try:
   print "Class name: %s" % o.GetClassName()
 except:
   print "Cannot print object"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
 try:
   b = vtk.vtkObject()
@@ -46,7 +46,7 @@ try:
   print b, d
 except:
   print "Cannot downcast"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
-sys.exit(0)
+qt.QApplication.exit(0)
 

+ 6 - 6
Applications/ctkSimplePythonShell/Testing/Python/wrappedQInvokableTest.py

@@ -1,10 +1,10 @@
-import sys
+import qt
 
 if (_testWrappedQInvokableInstance.value() != 0):
-  sys.exit(1)
-  
+  qt.QApplication.exit(1)
+
 _testWrappedQInvokableInstance.setValue(74)
 if (_testWrappedQInvokableInstance.value() != 74):
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 6 - 6
Applications/ctkSimplePythonShell/Testing/Python/wrappedQPropertyTest.py

@@ -1,11 +1,11 @@
 
-import sys
+import qt
 
 if (_testWrappedQPropertyInstance.value != 0):
-  sys.exit(1)
-  
+  qt.QApplication.exit(1)
+
 _testWrappedQPropertyInstance.value = 74
 if (_testWrappedQPropertyInstance.value != 74):
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 6 - 6
Applications/ctkSimplePythonShell/Testing/Python/wrappedSlotTest.py

@@ -1,10 +1,10 @@
-import sys
+import qt
 
 if (_testWrappedSlotInstance.value() != 0):
-  sys.exit(1)
-  
+  qt.QApplication.exit(1)
+
 _testWrappedSlotInstance.setValue(74)
 if (_testWrappedSlotInstance.value() != 74):
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 17 - 0
Applications/ctkSimplePythonShell/Testing/Python/wrappedVTKObserverTest.py

@@ -0,0 +1,17 @@
+
+import qt
+
+# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent
+# call to vtkPythonUtil::GetObjectFromPointer() from segfaulting.
+# PythonQt internally uses vtkPythonUtil to properly wrap/unwrap VTK objects
+from vtk import *
+
+t = _testWrappedVTKObserverInstance.getTable()
+
+def onTableModified(caller, event):
+    print("Table modified !")
+
+t.AddObserver(vtkCommand.ModifiedEvent, onTableModified)
+t.Modified()
+
+qt.QApplication.exit(0)

+ 5 - 5
Applications/ctkSimplePythonShell/Testing/Python/wrappedVTKQInvokableTest.py

@@ -1,7 +1,7 @@
 
-import sys
+import qt
 
-# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent 
+# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent
 # call to vtkPythonUtil::GetObjectFromPointer() from segfaulting.
 # PythonQt internally uses vtkPythonUtil to properly wrap/unwrap VTK objects
 from vtk import *
@@ -12,6 +12,6 @@ print t.GetClassName()
 t2 = vtkTable()
 _testWrappedVTKQInvokableInstance.setTable(t2)
 if _testWrappedVTKQInvokableInstance.getTable() != t2:
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 5 - 5
Applications/ctkSimplePythonShell/Testing/Python/wrappedVTKSlotTest.py

@@ -1,7 +1,7 @@
 
-import sys
+import qt
 
-# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent 
+# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent
 # call to vtkPythonUtil::GetObjectFromPointer() from segfaulting.
 # PythonQt internally uses vtkPythonUtil to properly wrap/unwrap VTK objects
 from vtk import *
@@ -12,6 +12,6 @@ print t.GetClassName()
 t2 = vtkTable()
 _testWrappedVTKSlotInstance.setTable(t2)
 if _testWrappedVTKSlotInstance.getTable() != t2:
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 1 - 6
Applications/ctkSimplePythonShell/ctkSimplePythonManager.cpp.in

@@ -61,14 +61,9 @@ QStringList ctkSimplePythonManager::pythonPaths()
 #ifdef CTK_WRAP_PYTHONQT_USE_VTK
 
   // Try to put the VTK python module location in sys.path.
-  QString vtk_build_dir = "/../../CMakeExternals/Build/VTK/Wrapping/Python";
+  QString vtk_package_dir = "@VTK_DIR@/Wrapping/Python";
   bool found_vtk = false;
-  QString vtk_package_dir = self_dir;
 
-#if defined(CMAKE_INTDIR)
-  vtk_package_dir.append("/..");
-#endif
-  vtk_package_dir.append(vtk_build_dir);
   QFileInfo fi(vtk_package_dir);
   vtk_package_dir = fi.absoluteFilePath();
   if (fi.isDir())

+ 99 - 60
Applications/ctkSimplePythonShell/ctkSimplePythonShellMain.cpp

@@ -21,8 +21,10 @@
 // Qt includes
 #include <QApplication>
 #include <QTextStream>
+#include <QTimer>
 
 // CTK includes
+#include <ctkCallback.h>
 #include <ctkPythonConsole.h>
 #include <ctkCommandLineParser.h>
 
@@ -34,83 +36,120 @@
 
 #ifdef CTK_WRAP_PYTHONQT_USE_VTK
 # include "ctkTestWrappedQListOfVTKObject.h"
-# include "ctkTestWrappedVTKSlot.h"
+# include "ctkTestWrappedVTKObserver.h"
 # include "ctkTestWrappedVTKQInvokable.h"
+# include "ctkTestWrappedVTKSlot.h"
+# include <vtkDebugLeaks.h>
 #endif
 
-int main(int argc, char** argv)
+namespace
+{
+//-----------------------------------------------------------------------------
+void executeScripts(void * data)
 {
-  QApplication app(argc, argv);
+  ctkSimplePythonManager * pythonManager = reinterpret_cast<ctkSimplePythonManager*>(data);
+  QStringList scripts = pythonManager->property("scripts").toStringList();
+  foreach(const QString& script, scripts)
+    {
+    pythonManager->executeFile(script);
+    if (pythonManager->pythonErrorOccured())
+      {
+      QApplication::exit(EXIT_FAILURE);
+      }
+    }
+}
 
-  ctkCommandLineParser parser;
-  // Use Unix-style argument names
-  parser.setArgumentPrefix("--", "-");
+} // end of anonymous namespace
 
-  // Add command line argument names
-  parser.addArgument("help", "h", QVariant::Bool, "Print usage information and exit.");
-  parser.addArgument("interactive", "I", QVariant::Bool, "Enable interactive mode");
+//-----------------------------------------------------------------------------
+int main(int argc, char** argv)
+{
+#ifdef CTK_WRAP_PYTHONQT_USE_VTK
+  vtkDebugLeaks::SetExitError(true);
+  ctkTestWrappedVTKObserver testWrappedVTKObserver;
+#endif
 
-  // Parse the command line arguments
-  bool ok = false;
-  QHash<QString, QVariant> parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok);
-  if (!ok)
+  int exitCode = EXIT_FAILURE;
   {
-    QTextStream(stderr, QIODevice::WriteOnly) << "Error parsing arguments: "
-                                              << parser.errorString() << "\n";
-    return EXIT_FAILURE;
-  }
+    QApplication app(argc, argv);
 
-  // Show a help message
-  if (parsedArgs.contains("help") || parsedArgs.contains("h"))
-  {
-    QTextStream(stdout, QIODevice::WriteOnly) << "ctkSimplePythonShell\n"
-          << "Usage\n\n"
-          << "  ctkSimplePythonShell [options] [<path-to-python-script> ...]\n\n"
-          << "Options\n"
-          << parser.helpText();
-    return EXIT_SUCCESS;
-  }
-  
-  ctkSimplePythonManager pythonManager;
-  
-  ctkPythonConsole console;
-  console.initialize(&pythonManager);
-  console.setAttribute(Qt::WA_QuitOnClose, true);
-  console.resize(600, 280);
-  console.show();
+    ctkCommandLineParser parser;
+    // Use Unix-style argument names
+    parser.setArgumentPrefix("--", "-");
+
+    // Add command line argument names
+    parser.addArgument("help", "h", QVariant::Bool, "Print usage information and exit.");
+    parser.addArgument("interactive", "I", QVariant::Bool, "Enable interactive mode");
+
+    // Parse the command line arguments
+    bool ok = false;
+    QHash<QString, QVariant> parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok);
+    if (!ok)
+    {
+      QTextStream(stderr, QIODevice::WriteOnly) << "Error parsing arguments: "
+                                                << parser.errorString() << "\n";
+      return EXIT_FAILURE;
+    }
 
-  console.setProperty("isInteractive", parsedArgs.contains("interactive"));
+    // Show a help message
+    if (parsedArgs.contains("help") || parsedArgs.contains("h"))
+    {
+      QTextStream(stdout, QIODevice::WriteOnly) << "ctkSimplePythonShell\n"
+            << "Usage\n\n"
+            << "  ctkSimplePythonShell [options] [<path-to-python-script> ...]\n\n"
+            << "Options\n"
+            << parser.helpText();
+      return EXIT_SUCCESS;
+    }
 
-  QStringList list;
-  list << "qt.QPushButton";
-  console.completer()->setAutocompletePreferenceList(list);
+    ctkSimplePythonManager pythonManager;
 
-  pythonManager.addObjectToPythonMain("_ctkPythonConsoleInstance", &console);
+    ctkPythonConsole console;
+    console.initialize(&pythonManager);
+    console.setAttribute(Qt::WA_QuitOnClose, true);
+    console.resize(600, 280);
+    console.show();
 
-  ctkTestWrappedQProperty testWrappedQProperty;
-  pythonManager.addObjectToPythonMain("_testWrappedQPropertyInstance", &testWrappedQProperty);
+    console.setProperty("isInteractive", parsedArgs.contains("interactive"));
 
-  ctkTestWrappedQInvokable testWrappedQInvokable;
-  pythonManager.addObjectToPythonMain("_testWrappedQInvokableInstance", &testWrappedQInvokable);
+    QStringList list;
+    list << "qt.QPushButton";
+    console.completer()->setAutocompletePreferenceList(list);
 
-  ctkTestWrappedSlot testWrappedSlot;
-  pythonManager.addObjectToPythonMain("_testWrappedSlotInstance", &testWrappedSlot);
+    pythonManager.addObjectToPythonMain("_ctkPythonConsoleInstance", &console);
 
-#ifdef CTK_WRAP_PYTHONQT_USE_VTK
-  ctkTestWrappedVTKQInvokable testWrappedVTKQInvokable;
-  pythonManager.addObjectToPythonMain("_testWrappedVTKQInvokableInstance", &testWrappedVTKQInvokable);
+    ctkTestWrappedQProperty testWrappedQProperty;
+    pythonManager.addObjectToPythonMain("_testWrappedQPropertyInstance", &testWrappedQProperty);
 
-  ctkTestWrappedVTKSlot testWrappedVTKSlot;
-  pythonManager.addObjectToPythonMain("_testWrappedVTKSlotInstance", &testWrappedVTKSlot);
+    ctkTestWrappedQInvokable testWrappedQInvokable;
+    pythonManager.addObjectToPythonMain("_testWrappedQInvokableInstance", &testWrappedQInvokable);
 
-  ctkTestWrappedQListOfVTKObject testWrappedQListOfVTKObject;
-  pythonManager.addObjectToPythonMain("_testWrappedQListOfVTKObjectInstance", &testWrappedQListOfVTKObject);
-#endif
+    ctkTestWrappedSlot testWrappedSlot;
+    pythonManager.addObjectToPythonMain("_testWrappedSlotInstance", &testWrappedSlot);
 
-  foreach(const QString& script, parser.unparsedArguments())
-    {
-    pythonManager.executeFile(script);
-    }
-  
-  return app.exec();
+  #ifdef CTK_WRAP_PYTHONQT_USE_VTK
+    pythonManager.addObjectToPythonMain("_testWrappedVTKObserverInstance", &testWrappedVTKObserver);
+
+    ctkTestWrappedVTKQInvokable testWrappedVTKQInvokable;
+    pythonManager.addObjectToPythonMain("_testWrappedVTKQInvokableInstance", &testWrappedVTKQInvokable);
+
+    ctkTestWrappedVTKSlot testWrappedVTKSlot;
+    pythonManager.addObjectToPythonMain("_testWrappedVTKSlotInstance", &testWrappedVTKSlot);
+
+  //  ctkTestWrappedQListOfVTKObject testWrappedQListOfVTKObject;
+  //  pythonManager.addObjectToPythonMain("_testWrappedQListOfVTKObjectInstance", &testWrappedQListOfVTKObject);
+  #endif
+
+    ctkCallback callback;
+    callback.setCallbackData(&pythonManager);
+    pythonManager.setProperty("scripts", parser.unparsedArguments());
+    callback.setCallback(executeScripts);
+    QTimer::singleShot(0, &callback, SLOT(invoke()));
+
+    exitCode = app.exec();
+  }
+#ifdef CTK_WRAP_PYTHONQT_USE_VTK
+  testWrappedVTKObserver.getTable()->Modified();
+#endif
+  return exitCode;
 }

+ 57 - 0
Applications/ctkSimplePythonShell/ctkTestWrappedVTKObserver.h

@@ -0,0 +1,57 @@
+/*=========================================================================
+
+  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.
+
+=========================================================================*/
+
+#ifndef __ctkTestWrappedVTKObserver_h
+#define __ctkTestWrappedVTKObserver_h
+
+// Qt includes
+#include <QObject>
+
+// VTK includes
+#include <vtkSmartPointer.h>
+#include <vtkTable.h>
+
+class ctkTestWrappedVTKObserver : public QObject
+{
+  Q_OBJECT
+public:
+
+  ctkTestWrappedVTKObserver(QObject * newParent = 0) : QObject(newParent)
+    {
+    this->MyTable = vtkSmartPointer<vtkTable>::New();
+    }
+
+  virtual ~ctkTestWrappedVTKObserver()
+    {
+    }
+
+public Q_SLOTS:
+
+  /// Example of slot returning a VTK object
+  vtkTable* getTable() const
+    {
+    return this->MyTable;
+    }
+
+private:
+  vtkSmartPointer<vtkTable> MyTable;
+};
+
+#endif

+ 4 - 4
Applications/ctkSimplePythonShell/ctkTestWrappedVTKQInvokable.h

@@ -25,6 +25,7 @@
 #include <QObject>
 
 // VTK includes
+#include <vtkSmartPointer.h>
 #include <vtkTable.h>
 
 class ctkTestWrappedVTKQInvokable : public QObject
@@ -34,12 +35,11 @@ public:
 
   ctkTestWrappedVTKQInvokable(QObject * newParent = 0) : QObject(newParent)
     {
-    this->MyTable = vtkTable::New();
+    this->MyTable = vtkSmartPointer<vtkTable>::New();
     }
-    
+
   virtual ~ctkTestWrappedVTKQInvokable()
     {
-    this->MyTable->Delete();
     }
 
   /// Example of 'invokable' returning a VTK object
@@ -58,7 +58,7 @@ public:
     }
 
 private:
-  vtkTable * MyTable;
+  vtkSmartPointer<vtkTable> MyTable;
 };
 
 #endif

+ 4 - 4
Applications/ctkSimplePythonShell/ctkTestWrappedVTKSlot.h

@@ -25,6 +25,7 @@
 #include <QObject>
 
 // VTK includes
+#include <vtkSmartPointer.h>
 #include <vtkTable.h>
 
 class ctkTestWrappedVTKSlot : public QObject
@@ -34,12 +35,11 @@ public:
 
   ctkTestWrappedVTKSlot(QObject * newParent = 0) : QObject(newParent)
     {
-    this->MyTable = vtkTable::New();
+    this->MyTable = vtkSmartPointer<vtkTable>::New();
     }
-    
+
   virtual ~ctkTestWrappedVTKSlot()
     {
-    this->MyTable->Delete();
     }
 
 public Q_SLOTS:
@@ -57,7 +57,7 @@ public Q_SLOTS:
     }
 
 private:
-  vtkTable * MyTable;
+  vtkSmartPointer<vtkTable> MyTable;
 };
 
 #endif

+ 1 - 2
Libs/Scripting/Python/Core/ctkAbstractPythonManager.cpp

@@ -82,8 +82,7 @@ ctkAbstractPythonManager::~ctkAbstractPythonManager()
 {
   if (Py_IsInitialized())
     {
-    PyThreadState* state = PyThreadState_Get();
-    Py_EndInterpreter(state);
+    Py_Finalize();
     }
   PythonQt::cleanup();
 }