Commit 9182f039 authored by Pat Marion's avatar Pat Marion
Browse files

Add Qt widget for displaying objects tracked by vtkDebugLeaks

Change-Id: I3aed58d875bd7080d8e369769b476f59447fa5e5
parent 75ca1bd2
......@@ -12,6 +12,8 @@ SET(QVTKLibSrcs
QVTKPaintEngine.h
vtkQtAbstractModelAdapter.cxx
vtkQtAnnotationLayersModelAdapter.cxx
vtkQtDebugLeaksModel.cxx
vtkQtDebugLeaksView.cxx
vtkQtSQLDatabase.cxx
vtkQtSQLQuery.cxx
vtkQtTableModelAdapter.cxx
......@@ -28,6 +30,8 @@ SET(QVTKMocHeaders
QFilterTreeProxyModel.h
vtkQtAbstractModelAdapter.h
vtkQtAnnotationLayersModelAdapter.h
vtkQtDebugLeaksModel.h
vtkQtDebugLeaksView.h
vtkQtTableModelAdapter.h
vtkQtTreeModelAdapter.h
)
......
......@@ -2,6 +2,7 @@
SET(KIT QVTK)
# add tests that do not require data
SET(MyTests
TestQtDebugLeaksView.cxx
TestQtInitialization.cxx
TestQtSQLDatabase.cxx
TestQtTableModelAdapter.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestQtTreeModelAdapter.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// Tests vtkQtDebugLeaksModel and vtkQtDebugLeaksView.
#include "vtkConeSource.h"
#include "vtkDebugLeaks.h"
#include "vtkSmartPointer.h"
#include "vtkQtDebugLeaksModel.h"
#include "vtkQtDebugLeaksView.h"
#include <QApplication>
#include <QStandardItemModel>
#include <QTableView>
#define fail(msg) \
std::cout << msg << std::endl; \
return EXIT_FAILURE
int TestQtDebugLeaksView(int argc, char* argv[])
{
QApplication app(argc, argv);
if (vtkDebugLeaks::GetDebugLeaksObserver())
{
fail("Expected debug leaks observer to be null at start of test.");
}
vtkQtDebugLeaksView view;
vtkQtDebugLeaksModel* model = view.model();
if (!vtkDebugLeaks::GetDebugLeaksObserver())
{
fail("Expected debug leaks observer to be initialized after constructing view.");
}
// Normally the model is updated asynchronously during the application event loop.
// Since there is no event loop running during this test we'll call processEvents()
// whenever we need the model to update.
app.processEvents();
std::cout << "Expect a warning message to be printed:" << std::endl;
QList<vtkObjectBase*> cones = model->getObjects("vtkConeSource");
if (!cones.isEmpty())
{
fail("Expected number of vtkConeSource to be 0");
}
// The rest of the test requires that VTK_DEBUG_LEAKS is enabled.
// The begining of this test is still useful to ensure that the widget
// opens without crashing when debug leaks is disabled.
#ifdef VTK_DEBUG_LEAKS
vtkSmartPointer<vtkConeSource> cone = vtkSmartPointer<vtkConeSource>::New();
app.processEvents();
cones = model->getObjects("vtkConeSource");
if (cones.size() != 1 || cones[0] != cone)
{
fail("Debug leaks model failed to locate the cone");
}
view.setFilterEnabled(true);
view.setFilterText("vtkCone");
QTableView* classTable = view.findChild<QTableView*>("ClassTable");
if (classTable->model()->rowCount() != 1)
{
fail("Expected exactly 1 row in debug leaks view.");
}
classTable->selectRow(0);
QStandardItemModel* referenceModel = model->referenceCountModel("vtkConeSource");
QTableView* referenceTable = view.findChild<QTableView*>("ReferenceTable");
if (referenceTable->model() != referenceModel)
{
fail("Reference table has incorrect model");
}
view.setFilterEnabled(false);
if (classTable->model()->rowCount() <= 1)
{
fail("Expected more than 1 row in the debug leaks view");
}
if (view.filterText() != "vtkCone")
{
fail("Expected filter text to be 'vtkCone'");
}
int baseReferenceCount = cone->GetReferenceCount();
if (referenceModel->rowCount() != 1)
{
fail("Expected reference model to have exactly 1 row");
}
if (referenceModel->data(referenceModel->index(0, 1)) != baseReferenceCount)
{
fail("Incorrect reference count");
}
vtkSmartPointer<vtkConeSource> newReference = cone;
int newReferenceCount = cone->GetReferenceCount();
if (newReferenceCount <= baseReferenceCount)
{
fail("Expected reference count to increase after constructing smart pointer");
}
// Normally the reference count model is updated periodically during the application event loop.
// Since there is no event loop running in this test we'll directly invoke the update routine.
QMetaObject::invokeMethod(referenceModel, "updateReferenceCounts", Qt::DirectConnection);
if (referenceModel->data(referenceModel->index(0, 1)) != newReferenceCount)
{
fail("Incorrect reference count");
}
newReference = NULL;
QMetaObject::invokeMethod(referenceModel, "updateReferenceCounts", Qt::DirectConnection);
if (referenceModel->data(referenceModel->index(0, 1)) != baseReferenceCount)
{
fail("Incorrect reference count");
}
newReference = vtkSmartPointer<vtkConeSource>::New();
app.processEvents();
if (referenceModel->rowCount() != 2)
{
fail("Expected reference model to have exactly 2 rows");
}
newReference = NULL;
cone = NULL;
app.processEvents();
view.setFilterEnabled(true);
if (classTable->model()->rowCount() != 0)
{
fail("Expected 0 rows in the debug leaks view");
}
#endif
//uncomment to keep the widget open for interaction
//view.show();
//view.setAttribute(Qt::WA_QuitOnClose, true);
//app.exec();
return EXIT_SUCCESS;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkQtDebugLeaksModel.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkQtDebugLeaksModel.h"
#include "vtkDebugLeaks.h"
#include <QCoreApplication>
#include <QDebug>
#include <QList>
#include <QPointer>
#include <QTextStream>
#include <QTimer>
//-----------------------------------------------------------------------------
class vtkQtDebugLeaksModel::qObserver : public vtkDebugLeaksObserver {
public:
qObserver(vtkQtDebugLeaksModel& model) : Model(model)
{
vtkDebugLeaks::SetDebugLeaksObserver(this);
}
virtual ~qObserver()
{
vtkDebugLeaks::SetDebugLeaksObserver(0);
}
virtual void ConstructingObject(vtkObjectBase* object)
{
this->Model.addObject(object);
}
virtual void DestructingObject(vtkObjectBase* object)
{
this->Model.removeObject(object);
}
vtkQtDebugLeaksModel& Model;
};
//-----------------------------------------------------------------------------
class VTKClassInfo {
public:
VTKClassInfo(const QString& className) : Count(0), Name(className)
{
}
int Count;
QString Name;
QList<vtkObjectBase*> Objects;
};
//-----------------------------------------------------------------------------
class vtkQtDebugLeaksModel::qInternal
{
public:
qInternal() : ProcessPending(false) { }
bool ProcessPending;
QList<QString> Classes;
QList<VTKClassInfo> ClassInfo;
QList<vtkObjectBase*> ObjectsToProcess;
QHash<vtkObjectBase*, VTKClassInfo*> ObjectMap;
QHash<QString, QPointer<ReferenceCountModel> > ReferenceModels;
};
//-----------------------------------------------------------------------------
vtkQtDebugLeaksModel::vtkQtDebugLeaksModel(QObject* parent)
: QStandardItemModel(0, 2, parent)
{
this->Internal = new qInternal;
this->Observer = new qObserver(*this);
this->setHeaderData(0, Qt::Horizontal, QObject::tr("Class Name"));
this->setHeaderData(1, Qt::Horizontal, QObject::tr("Class Count"));
this->connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
SLOT(onAboutToQuit()));
}
//-----------------------------------------------------------------------------
vtkQtDebugLeaksModel::~vtkQtDebugLeaksModel()
{
delete this->Observer;
delete this->Internal;
}
//-----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::onAboutToQuit()
{
delete this->Observer;
this->Observer = 0;
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::addObject(vtkObjectBase* object)
{
this->Internal->ObjectsToProcess.append(object);
if (!this->Internal->ProcessPending)
{
this->Internal->ProcessPending = true;
QTimer::singleShot(0, this, SLOT(processPendingObjects()));
}
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::processPendingObjects()
{
this->Internal->ProcessPending = false;
foreach(vtkObjectBase* object, this->Internal->ObjectsToProcess)
{
this->registerObject(object);
}
this->Internal->ObjectsToProcess.clear();
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::registerObject(vtkObjectBase* object)
{
QString className = object->GetClassName();
int indexOf = this->Internal->Classes.indexOf(className);
if (indexOf < 0)
{
this->Internal->Classes.append(className);
this->Internal->ClassInfo.append(VTKClassInfo(className));
indexOf = this->rowCount();
this->insertRow(indexOf);
this->setData(this->index(indexOf, 0), className);
this->setData(this->index(indexOf, 1), 0);
}
VTKClassInfo& classInfo = this->Internal->ClassInfo[indexOf];
classInfo.Count += 1;
classInfo.Objects.append(object);
this->Internal->ObjectMap[object] = &classInfo;
this->setData(this->index(indexOf, 1), classInfo.Count);
ReferenceCountModel* model = this->Internal->ReferenceModels.value(className, 0);
if (model)
{
model->addObject(object);
}
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::removeObject(vtkObjectBase* object)
{
VTKClassInfo* classInfo = this->Internal->ObjectMap.value(object, 0);
if (classInfo)
{
QString className = classInfo->Name;
int row = this->Internal->Classes.indexOf(className);
classInfo->Count -= 1;
classInfo->Objects.removeOne(object);
this->Internal->ObjectMap.remove(object);
if (classInfo->Count <= 0)
{
this->Internal->Classes.removeAt(row);
this->Internal->ClassInfo.removeAt(row);
this->removeRow(row);
}
else
{
this->setData(this->index(row, 1), classInfo->Count);
}
ReferenceCountModel* model = this->Internal->ReferenceModels.value(className, 0);
if (model)
{
model->removeObject(object);
}
}
else
{
this->Internal->ObjectsToProcess.removeOne(object);
}
}
//-----------------------------------------------------------------------------
QList<vtkObjectBase*> vtkQtDebugLeaksModel::getObjects(const QString& className)
{
int indexOf = this->Internal->Classes.indexOf(className);
if (indexOf < 0)
{
qWarning() << "vtkQtDebugLeaksModel::getObjects: bad class name:" << className;
return QList<vtkObjectBase*>();
}
VTKClassInfo &classInfo = this->Internal->ClassInfo[indexOf];
return classInfo.Objects;
}
//----------------------------------------------------------------------------
QStandardItemModel* vtkQtDebugLeaksModel::referenceCountModel(const QString& className)
{
ReferenceCountModel* model = this->Internal->ReferenceModels.value(className, 0);
if (!model)
{
model = new ReferenceCountModel(this);
this->Internal->ReferenceModels[className] = model;
foreach (vtkObjectBase* obj, this->getObjects(className))
{
model->addObject(obj);
}
}
return model;
}
//-----------------------------------------------------------------------------
Qt::ItemFlags vtkQtDebugLeaksModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index);
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
//-----------------------------------------------------------------------------
Q_DECLARE_METATYPE(vtkObjectBase*);
//-----------------------------------------------------------------------------
ReferenceCountModel::ReferenceCountModel(QObject* parent)
: QStandardItemModel(0, 2, parent)
{
this->setHeaderData(0, Qt::Horizontal, QObject::tr("Pointer"));
this->setHeaderData(1, Qt::Horizontal, QObject::tr("Reference Count"));
QTimer::singleShot(100, this, SLOT(updateReferenceCounts()));
}
ReferenceCountModel::~ReferenceCountModel()
{
}
//-----------------------------------------------------------------------------
QString ReferenceCountModel::pointerAsString(void* ptr)
{
QString ptrStr;
QTextStream stream(&ptrStr);
stream << ptr;
return ptrStr;
}
//-----------------------------------------------------------------------------
void ReferenceCountModel::addObject(vtkObjectBase* obj)
{
int row = this->rowCount();
this->insertRow(row);
this->setData(this->index(row, 0), this->pointerAsString(obj));
this->setData(this->index(row, 0), qVariantFromValue(obj), Qt::UserRole);
this->setData(this->index(row, 1), obj->GetReferenceCount());
}
//-----------------------------------------------------------------------------
void ReferenceCountModel::removeObject(vtkObjectBase* obj)
{
QString pointerString = this->pointerAsString(obj);
for (int i = 0; i < this->rowCount(); ++i)
{
if (this->data(this->index(i, 0)) == pointerString)
{
this->removeRow(i);
return;
}
}
}
//-----------------------------------------------------------------------------
void ReferenceCountModel::updateReferenceCounts()
{
for (int row = 0; row < this->rowCount(); ++row)
{
QVariant pointerVariant = this->data(this->index(row, 0), Qt::UserRole);
vtkObjectBase* obj = pointerVariant.value<vtkObjectBase*>();
this->setData(this->index(row, 1), obj->GetReferenceCount());
}
QTimer::singleShot(100, this, SLOT(updateReferenceCounts()));
}
//-----------------------------------------------------------------------------
Qt::ItemFlags ReferenceCountModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index);
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkQtDebugLeaksModel.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME vtkQtDebugLeaksModel - model class that observes the vtkDebugLeaks singleton
//
// .SECTION Description
// This class is used internally by the vtkQtDebugLeaksView. It installs an
// observer on the vtkDebugLeaks singleton and uses the observer to maintain
// a model of all vtkObjectBase dervied objects that are alive in memory.
#ifndef __vtkQtDebugLeaksModel_h
#define __vtkQtDebugLeaksModel_h
#include "QVTKWin32Header.h"
#include <QStandardItemModel>
class vtkObjectBase;
class QVTK_EXPORT vtkQtDebugLeaksModel : public QStandardItemModel
{
Q_OBJECT
public:
vtkQtDebugLeaksModel(QObject* parent=0);
virtual ~vtkQtDebugLeaksModel();
// Description:
// Get the list of objects in the model that have the given class name
QList<vtkObjectBase*> getObjects(const QString& className);
// Description:
// Return an item model that contains only objects with the given class name.
// The model has two columns: object address (string), object reference count (integer)
// The caller is allowed to reparent or delete the returned model.
QStandardItemModel* referenceCountModel(const QString& className);
protected slots:
void addObject(vtkObjectBase* object);
void removeObject(vtkObjectBase* object);
void registerObject(vtkObjectBase* object);
void processPendingObjects();
void onAboutToQuit();
// Inherited method from QAbstractItemModel
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
private:
class qInternal;
qInternal* Internal;
class qObserver;
qObserver* Observer;
Q_DISABLE_COPY(vtkQtDebugLeaksModel);
};
// TODO - move to private
//-----------------------------------------------------------------------------
class ReferenceCountModel : public QStandardItemModel
{
Q_OBJECT
public:
ReferenceCountModel(QObject* parent=0);
~ReferenceCountModel();
void addObject(vtkObjectBase* obj);
void removeObject(vtkObjectBase* obj);
QString pointerAsString(void* ptr);
// Inherited method from QAbstractItemModel
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
protected slots:
void updateReferenceCounts();
};
#endif
/*=========================================================================
Program: Visualization Toolkit
Module: vtkQtDebugLeaksView.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkQtDebugLeaksView.h"
#include "vtkQtDebugLeaksModel.h"
#include <QCheckBox>
#include <QDesktopServices>