Commit 7110a19e authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

Fixed BUG #9471. Addded support for python-animation track.

Added support for a Python animation track that accepts a python script.
parent 0e0f5668
......@@ -124,6 +124,7 @@ ENDIF (PARAVIEW_USE_ICE_T AND VTK_USE_MPI)
IF(PARAVIEW_ENABLE_PYTHON)
SET(Kit_SRCS ${Kit_SRCS}
vtkPythonAnimationCue.cxx
vtkPythonCalculator.cxx
vtkPythonProgrammableFilter.cxx
)
......
/*=========================================================================
Program: ParaView
Module: $RCSfile$
Copyright (c) Kitware, Inc.
All rights reserved.
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkPythonAnimationCue.h"
#include "vtkCommand.h"
#include "vtkObjectFactory.h"
#include "vtkProcessModule.h"
#include "vtkPVPythonInterpretor.h"
#include "vtkPythonProgrammableFilter.h"
#include "vtkPVOptions.h"
#include <vtksys/ios/sstream>
vtkStandardNewMacro(vtkPythonAnimationCue);
//----------------------------------------------------------------------------
vtkPythonAnimationCue::vtkPythonAnimationCue()
{
this->Enabled = true;
this->Script = 0;
this->AddObserver(vtkCommand::StartAnimationCueEvent,
this, &vtkPythonAnimationCue::HandleStartCueEvent);
this->AddObserver(vtkCommand::AnimationCueTickEvent,
this, &vtkPythonAnimationCue::HandleTickEvent);
this->AddObserver(vtkCommand::EndAnimationCueEvent,
this, &vtkPythonAnimationCue::HandleEndCueEvent);
this->Interpretor = NULL;
}
//----------------------------------------------------------------------------
vtkPythonAnimationCue::~vtkPythonAnimationCue()
{
this->SetScript(NULL);
if (this->Interpretor)
{
this->Interpretor->Delete();
this->Interpretor = NULL;
}
}
//----------------------------------------------------------------------------
vtkPVPythonInterpretor* vtkPythonAnimationCue::GetInterpretor()
{
if (!this->Interpretor)
{
this->Interpretor = vtkPVPythonInterpretor::New();
this->Interpretor->SetCaptureStreams(true);
const char* argv0 = vtkProcessModule::GetProcessModule()->
GetOptions()->GetArgv0();
this->Interpretor->InitializeSubInterpretor(1, (char**)&argv0);
this->Interpretor->ExecuteInitFromGUI();
vtkProcessModule::GetProcessModule()->AddObserver(vtkCommand::ExitEvent,
this, &vtkPythonAnimationCue::DeleteInterpretor);
}
return this->Interpretor;
}
//----------------------------------------------------------------------------
void vtkPythonAnimationCue::HandleStartCueEvent()
{
// Set self to point to this
char addrofthis[1024];
sprintf(addrofthis, "%p", this);
char *aplus = addrofthis;
if ((addrofthis[0] == '0') &&
((addrofthis[1] == 'x') || addrofthis[1] == 'X'))
{
aplus += 2; //skip over "0x"
}
if (this->Script)
{
vtksys_ios::ostringstream stream;
stream << "def start_cue(foo): pass" << endl;
stream << this->Script << endl;
stream << "_me = servermanager.vtkPythonAnimationCue('" << aplus << "')\n";
stream << "try:\n";
stream << " start_cue(_me)\n";
stream << "finally:\n"
" del _me\n"
" import gc\n"
" gc.collect()\n";
this->GetInterpretor()->RunSimpleString(stream.str().c_str());
this->GetInterpretor()->FlushMessages();
}
}
//----------------------------------------------------------------------------
void vtkPythonAnimationCue::HandleTickEvent()
{
// Set self to point to this
char addrofthis[1024];
sprintf(addrofthis, "%p", this);
char *aplus = addrofthis;
if ((addrofthis[0] == '0') &&
((addrofthis[1] == 'x') || addrofthis[1] == 'X'))
{
aplus += 2; //skip over "0x"
}
if (this->Script)
{
vtksys_ios::ostringstream stream;
stream << this->Script << endl;
stream << "_me = servermanager.vtkPythonAnimationCue('" << aplus << "')\n";
stream << "try:\n";
stream << " tick(_me)\n";
stream << "finally:\n"
" del _me\n"
" import gc\n"
" gc.collect()\n";
this->GetInterpretor()->RunSimpleString(stream.str().c_str());
this->GetInterpretor()->FlushMessages();
}
}
//----------------------------------------------------------------------------
void vtkPythonAnimationCue::HandleEndCueEvent()
{
// Set self to point to this
char addrofthis[1024];
sprintf(addrofthis, "%p", this);
char *aplus = addrofthis;
if ((addrofthis[0] == '0') &&
((addrofthis[1] == 'x') || addrofthis[1] == 'X'))
{
aplus += 2; //skip over "0x"
}
if (this->Script)
{
vtksys_ios::ostringstream stream;
stream << "def end_cue(foo): pass" << endl;
stream << this->Script << endl;
stream << "_me = servermanager.vtkPythonAnimationCue('" << aplus << "')\n";
stream << "try:\n";
stream << " end_cue(_me)\n";
stream << "finally:\n"
" del _me\n"
" import gc\n"
" gc.collect()\n";
this->GetInterpretor()->RunSimpleString(stream.str().c_str());
this->GetInterpretor()->FlushMessages();
}
}
//----------------------------------------------------------------------------
void vtkPythonAnimationCue::DeleteInterpretor()
{
if (this->Interpretor)
{
this->Interpretor->Delete();
this->Interpretor = NULL;
}
}
//----------------------------------------------------------------------------
void vtkPythonAnimationCue::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Enabled: " << this->Enabled << endl;
os << indent << "Script: " << this->Script << endl;
}
/*=========================================================================
Program: ParaView
Module: $RCSfile$
Copyright (c) Kitware, Inc.
All rights reserved.
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 vtkPythonAnimationCue
// .SECTION Description
//
#ifndef __vtkPythonAnimationCue_h
#define __vtkPythonAnimationCue_h
#include "vtkAnimationCue.h"
class vtkPVPythonInterpretor;
class VTK_EXPORT vtkPythonAnimationCue : public vtkAnimationCue
{
public:
static vtkPythonAnimationCue* New();
vtkTypeMacro(vtkPythonAnimationCue, vtkAnimationCue);
void PrintSelf(ostream& os, vtkIndent indent);
// Description:
// Enable/Disable this cue.
vtkSetMacro(Enabled, bool);
vtkGetMacro(Enabled, bool);
vtkBooleanMacro(Enabled, bool);
// Description:
// Get/Set the python script to execute. The script must have the following
// functions:
// \li start_cue(cue): (optional) if present, called when the cue starts.
// \li tick(cue) : (required) called on every tick.
// \li end_cue(cue): (optional) if present, called when the cue ends.
vtkSetStringMacro(Script);
vtkGetStringMacro(Script);
//BTX
protected:
vtkPythonAnimationCue();
~vtkPythonAnimationCue();
// Description:
// Returns the interpretor. Creates a new one the first time this method is
// called.
vtkPVPythonInterpretor* GetInterpretor();
// Description:
// Callbacks that forward the call to corresponding Python function.
virtual void HandleStartCueEvent();
virtual void HandleTickEvent();
virtual void HandleEndCueEvent();
// Description:
// Cleans the interpretor.
void DeleteInterpretor();
bool Enabled;
char* Script;
vtkPVPythonInterpretor* Interpretor;
private:
vtkPythonAnimationCue(const vtkPythonAnimationCue&); // Not implemented
void operator=(const vtkPythonAnimationCue&); // Not implemented
//ETX
};
#endif
......@@ -224,6 +224,7 @@
</SourceProxy>
</ProxyGroup>
<!-- =================================================================== -->
<ProxyGroup name="sources">
<SourceProxy name="ProgrammableSource"
......@@ -323,4 +324,60 @@ Interpretor, a python interpretor to run the script upon the first
</ProxyGroup>
<!-- =================================================================== -->
<ProxyGroup name="animation">
<Proxy name="PythonAnimationCue" class="vtkPythonAnimationCue"
processes="client">
<Documentation>
Animation cue that can use python script for animation.
</Documentation>
<IntVectorProperty name="Enabled"
command="SetEnabled"
number_of_elements="1"
default_values="1">
<BooleanDomain name="bool" />
<Documentation>
Enables this animation cue.
</Documentation>
</IntVectorProperty>
<IntVectorProperty name="TimeMode"
command="SetTimeMode"
number_of_elements="1"
default_values="0">
<EnumerationDomain name="enum">
<Entry value="0" text="Normalized" />
<Entry value="1" text="Relative" />
</EnumerationDomain>
</IntVectorProperty>
<DoubleVectorProperty name="StartTime"
command="SetStartTime"
number_of_elements="1"
default_values="0">
</DoubleVectorProperty>
<DoubleVectorProperty name="EndTime"
command="SetEndTime"
number_of_elements="1"
default_values="1">
</DoubleVectorProperty>
<StringVectorProperty name="Script"
command="SetScript"
number_of_elements="1"
default_values="def start_cue(self): pass&#xa;&#xa;def tick(self): pass&#xa;&#xa;def end_cue(self): pass" >
<Hints>
<Widget type="multi_line"/>
</Hints>
<Documentation>
This property contains the text for the script to execute.
</Documentation>
</StringVectorProperty>
<!-- end of PythonAnimationCue -->
</Proxy>
</ProxyGroup>
</ServerManagerConfiguration>
......@@ -67,6 +67,7 @@ SET (PY_TESTS
PythonPVSimpleSphere
PythonSMTraceTest1
PythonSMTraceTest2
PythonAnimationTrack
# SMUndoRedo
# SMCompoundProxyUndoRedo
)
......
import SMPythonTesting
from paraview.simple import *
import sys
Sphere()
Show()
Render()
AnimationScene1 = GetAnimationScene()
PythonAnimationCue1 = PythonAnimationCue()
PythonAnimationCue1.Script= """
def start_cue(self):
print "Start Cue"
def tick(self):
GetActiveSource().StartTheta = 180 * self.GetAnimationTime()
Render()
def end_cue(self):
print "end"
"""
AnimationScene1.Cues.append(PythonAnimationCue1)
AnimationScene1.Play()
AnimationScene1.GoToFirst()
AnimationScene1.GoToNext()
AnimationScene1.GoToNext()
AnimationScene1.GoToNext()
# delete the cue
AnimationScene1.Cues = []
Delete(PythonAnimationCue1)
del PythonAnimationCue1
if not SMPythonTesting.DoRegressionTesting(GetActiveView().SMProxy):
# This will lead to VTK object leaks.
sys.exit(1)
......@@ -44,7 +44,11 @@ void vtkSMObject::SetProxyManager(vtkSMProxyManager* pm)
}
if (vtkSMObject::ProxyManager)
{
vtkSMObject::ProxyManager->UnRegister(0);
// this indirect cleanup ensures that we don't accidentally end up calling
// the destructor on the vtkSMObject::ProxyManager twice.
vtkSMProxyManager* temp = vtkSMObject::ProxyManager;
vtkSMObject::ProxyManager = NULL;
temp->UnRegister(0);
}
if (pm)
{
......
......@@ -254,6 +254,7 @@ SET(UI_FORMS
Resources/UI/pqPluginDialog.ui
Resources/UI/pqPointSourceControls.ui
Resources/UI/pqProxyInformationWidget.ui
Resources/UI/pqPythonAnimationCue.ui
Resources/UI/pqQueryClauseWidget.ui
Resources/UI/pqQueryCompositeTreeDialog.ui
Resources/UI/pqQueryDialog.ui
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PythonAnimationCue</class>
<widget class="QDialog" name="PythonAnimationCue">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>367</width>
<height>446</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Python Animation Track</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Script:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="script">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PythonAnimationCue</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PythonAnimationCue</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
......@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=========================================================================*/
#include "pqAnimationViewWidget.h"
#include "ui_pqPythonAnimationCue.h"
#include <QComboBox>
#include <QDialog>
......@@ -40,9 +41,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QLabel>
#include <QLineEdit>
#include <QPointer>
#include <QPushButton>
#include <QSignalMapper>
#include <QSpinBox>
#include <QPushButton>
#include <QtDebug>
#include <QVBoxLayout>
#include "pqActiveView.h"
......@@ -69,6 +71,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "pqSMAdaptor.h"
#include "pqUndoStack.h"
#include "vtkCamera.h"
#include "vtkPVConfig.h"
#include "vtkSMProperty.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxy.h"
......@@ -133,6 +136,10 @@ public:
{
name = "Camera";
}
else if (this->pythonCue(cue))
{
name = "Python";
}
else
{
pqServerManagerModel* model =
......@@ -200,6 +207,16 @@ public:
return false;
}
/// returns true if the cue is a python cue.
bool pythonCue(pqAnimationCue* cue)
{
if(QString("PythonAnimationCue") == cue->getProxy()->GetXMLName())
{
return true;
}
return false;
}
int numberOfTicks()
{
vtkSMProxy* pxy = this->Scene->getProxy();
......@@ -298,6 +315,9 @@ pqAnimationViewWidget::pqAnimationViewWidget(QWidget* _parent) : QWidget(_parent
this->Internal->CreateSource = new pqAnimatableProxyComboBox(w)
<< pqSetName("ProxyCombo");
#ifdef PARAVIEW_ENABLE_PYTHON
this->Internal->CreateSource->addProxy(0, "Python", NULL);
#endif
this->Internal->CreateProperty = new pqAnimatablePropertiesComboBox(w)
<< pqSetName("PropertyCombo");
this->Internal->CreateSource->setSizeAdjustPolicy(QComboBox::AdjustToContents);
......@@ -488,6 +508,7 @@ void pqAnimationViewWidget::onSceneCuesChanged()
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::keyFramesChanged(QObject* cueObject)
{
pqAnimationCue* cue = qobject_cast<pqAnimationCue*>(cueObject);
......@@ -541,6 +562,7 @@ void pqAnimationViewWidget::keyFramesChanged(QObject* cueObject)
}
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::updateSceneTimeRange()
{
pqAnimationModel* animModel =
......@@ -550,6 +572,7 @@ void pqAnimationViewWidget::updateSceneTimeRange()
animModel->setEndTime(timeRange.second);
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::updateSceneTime()
{
double time =
......@@ -560,6 +583,7 @@ void pqAnimationViewWidget::updateSceneTime()
animModel->setCurrentTime(time);
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::setCurrentTime(double t)
{
vtkSMPropertyHelper(
......@@ -567,6 +591,7 @@ void pqAnimationViewWidget::setCurrentTime(double t)
this->Internal->Scene->getProxy()->UpdateVTKObjects();
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::setKeyFrameTime(pqAnimationTrack* track,
pqAnimationKeyFrame* kf,
int edge, double time)
......@@ -600,6 +625,7 @@ void pqAnimationViewWidget::setKeyFrameTime(pqAnimationTrack* track,
}
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::trackSelected(pqAnimationTrack* track)
{
pqAnimationCue* cue = this->Internal->findCue(track);
......@@ -619,6 +645,21 @@ void pqAnimationViewWidget::trackSelected(pqAnimationTrack* track)
this->Internal->Editor =
new pqPipelineTimeKeyFrameEditor(this->Internal->Scene, cue, NULL);
}
else if (this->Internal->pythonCue(cue))
{
QDialog dialog(this);
Ui::PythonAnimationCue ui;
ui.setupUi(&dialog);
ui.script->setPlainText(
vtkSMPropertyHelper(cue->getProxy(), "Script").GetAsString());
if (dialog.exec() == QDialog::Accepted)
{
vtkSMPropertyHelper(cue->getProxy(), "Script").Set(
ui.script->toPlainText().toAscii().data());
cue->getProxy()->UpdateVTKObjects();
}
return;
}
else
{
this->Internal->Editor = new QDialog;
......@@ -649,6 +690,7 @@ void pqAnimationViewWidget::trackSelected(pqAnimationTrack* track)
this->Internal->Editor->show();
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::updatePlayMode()
{
pqAnimationModel* animModel =
......@@ -704,6 +746,7 @@ void pqAnimationViewWidget::updatePlayMode()
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::updateTicks()
{
pqAnimationModel* animModel =
......@@ -751,6 +794,7 @@ void pqAnimationViewWidget::deleteTrack(pqAnimationTrack* track)
END_UNDO_SET();
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::setActiveView(pqView* view)
{
pqRenderView* rview = qobject_cast<pqRenderView*>(view);
......@@ -761,6 +805,7 @@ void pqAnimationViewWidget::setActiveView(pqView* view)
}
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::setCurrentSelection(pqServerManagerModelItem* item)
{
pqProxy* pxy = qobject_cast<pqProxy*>(item);
......@@ -774,6 +819,7 @@ void pqAnimationViewWidget::setCurrentSelection(pqServerManagerModelItem* item)
}
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::setCurrentProxy(vtkSMProxy* pxy)
{
if(vtkSMRenderViewProxy::SafeDownCast(pxy))
......@@ -794,6 +840,7 @@ void pqAnimationViewWidget::setCurrentProxy(vtkSMProxy* pxy)
}
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::createTrack()
{
vtkSMRenderViewProxy* ren =
......@@ -813,9 +860,25 @@ void pqAnimationViewWidget::createTrack()
pindex = 0;
}
if(!curProxy)
{
// curProxy == NULL is only used for "Python" track for now. Of course,
// we only support that when python is enabled.
// we allow creating as many python tracks as needed, hence we don't check
// if there exists a track already (which is the case with others).
#ifdef PARAVIEW_ENABLE_PYTHON
this->createPythonTrack();
#endif
return;
}
// check that we don't already have one
foreach(pqAnimationCue* cue, this->Internal->TrackMap.keys())
{
if (cue->getAnimatedProxy() == NULL)
{
continue; // skip Python tracks.
}
if(cue->getAnimatedProxy() == curProxy &&
cue->getAnimatedProxy()->GetPropertyName(cue->getAnimatedProperty()) == pname &&
cue->getAnimatedPropertyIndex() == pindex)
......@@ -824,12 +887,6 @@ void pqAnimationViewWidget::createTrack()
}
}
if(!curProxy)
{
// hmm something went wrong
return;
}
pqOrbitCreatorDialog creator(this);
// if mode=="orbit" show up a dialog allowing the user to customize the
......@@ -885,3 +942,19 @@ void pqAnimationViewWidget::createTrack()
END_UNDO_SET();
}
//-----------------------------------------------------------------------------
void pqAnimationViewWidget::createPythonTrack()
{
#ifdef PARAVIEW_ENABLE_PYTHON
BEGIN_UNDO_SET("Add Animation Track");
pqAnimationCue* cue = this->Internal->Scene->createCue("PythonAnimationCue");
Q_ASSERT(cue != NULL);
(void)cue;
END_UNDO_SET();
#else
qCritical() << "Python support not enabled. Please recompile ParaView "
"with Python enabled.";
#endif
}
......@@ -89,6 +89,10 @@ protected slots:
// called when creating a track
void createTrack();
/// called to create a new python animation track.
/// we allow creating as many python tracks as needed.
void createPythonTrack();
// set active view changed
void setActiveView(pqView*);
// set the current proxy selection
......
......@@ -72,9 +72,12 @@ pqAnimationCue::pqAnimationCue(const QString& group, const QString& name,