From: Leonardo Florez-Valencia Date: Tue, 19 Jan 2016 22:53:41 +0000 (-0500) Subject: ... X-Git-Tag: v0.1~265 X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=19a9e1774044cc32c415ad38695800c1d169820d;p=cpPlugins.git ... --- diff --git a/appli/ImageMPR/ImageMPR.cxx b/appli/ImageMPR/ImageMPR.cxx index ea198ac..c7921b1 100644 --- a/appli/ImageMPR/ImageMPR.cxx +++ b/appli/ImageMPR/ImageMPR.cxx @@ -240,13 +240,13 @@ _execPlugin( ) // Configure paramereters auto dlg_res = this->m_Plugins.ConfigureActiveFilter( ); - if( dlg_res == TPlugins::TProcessObject::DialogResult_Cancel ) + if( !dlg_res ) { // Just deactivate filter, since it was canceled this->m_Plugins.DeactivateFilter( ); return; } - else if( dlg_res == TPlugins::TProcessObject::DialogResult_NoModal ) + else { // Execute automatic filter and associate outputs this->UpdateActualFilter( ); diff --git a/appli/cpPipelineEditor/App_cpPipelineEditor.cxx b/appli/cpPipelineEditor/App_cpPipelineEditor.cxx index 8c0a155..864d96f 100644 --- a/appli/cpPipelineEditor/App_cpPipelineEditor.cxx +++ b/appli/cpPipelineEditor/App_cpPipelineEditor.cxx @@ -5,18 +5,23 @@ #include #include + +#include +#include + #include +#include // ------------------------------------------------------------------------- #define App_cpPipelineEditor_ConnectAction( ACTION ) \ - QObject::connect( \ + this->connect( \ this->m_UI->Action##ACTION, SIGNAL( triggered( ) ), \ this, SLOT( _Action##ACTION( ) ) \ ) // ------------------------------------------------------------------------- #define App_cpPipelineEditor_ConnectButton( BUTTON ) \ - QObject::connect( \ + this->connect( \ this->m_UI->Button##BUTTON, SIGNAL( clicked( ) ), \ this, SLOT( _Button##BUTTON( ) ) \ ) @@ -52,12 +57,28 @@ App_cpPipelineEditor( int argc, char* argv[], QWidget* parent ) this->m_Workspace = new cpPlugins::Interface::Workspace( ); this->m_Workspace->SetPlugins( this->m_Plugins ); this->m_UI->Canvas->editor( )->setWorkspace( this->m_Workspace ); + this->m_Workspace->AddInteractor( this->m_UI->Viewer->GetInteractor( 0 ) ); + this->m_Workspace->AddInteractor( this->m_UI->Viewer->GetInteractor( 1 ) ); + this->m_Workspace->AddInteractor( this->m_UI->Viewer->GetInteractor( 2 ) ); + this->m_Workspace->AddInteractor( this->m_UI->Viewer->GetInteractor( 3 ) ); // Connect actions to slots App_cpPipelineEditor_ConnectButton( LoadPluginsFile ); App_cpPipelineEditor_ConnectButton( LoadPluginsPath ); App_cpPipelineEditor_ConnectAction( OpenWorkspace ); App_cpPipelineEditor_ConnectAction( SaveWorkspace ); + this->connect( + this->m_UI->Canvas->editor( ), + SIGNAL( execFilter( const std::string& ) ), + this, + SLOT( _ExecFilter( const std::string& ) ) + ); + this->connect( + this->m_UI->Canvas->editor( ), + SIGNAL( showFilterOutput( const std::string&, const std::string& ) ), + this, + SLOT( _ShowFilterOutput( const std::string&, const std::string& ) ) + ); } // ------------------------------------------------------------------------- @@ -252,4 +273,62 @@ _ActionSaveWorkspace( ) ); } + +// ------------------------------------------------------------------------- +void App_cpPipelineEditor:: +_ExecFilter( const std::string& filter_name ) +{ + if( this->m_Workspace != NULL ) + { + // Update filter, if needed + std::string err = this->m_Workspace->Execute( filter_name ); + if( err != "" ) + QMessageBox::critical( + this, + QMessageBox::tr( "Error executing filter" ), + QMessageBox::tr( err.c_str( ) ) + ); + + } // fi +} + +// ------------------------------------------------------------------------- +void App_cpPipelineEditor:: +_ShowFilterOutput( + const std::string& filter_name, const std::string& output_name + ) +{ + typedef cpPlugins::Interface::DataObject _TDataObject; + + // Update filter, if needed + this->_ExecFilter( filter_name ); + + // Get output + auto filter = this->m_Workspace->GetFilter( filter_name ); + if( filter != NULL ) + { + auto output = filter->GetOutput< _TDataObject >( output_name ); + if( output != NULL ) + { + std::string data_name = output_name + "@" + filter_name; + if( this->m_UI->Viewer->AddData( output, data_name, "" ) ) + { + if( this->m_UI->Viewer->GetNumberOfData( ) > 1 ) + this->m_UI->Viewer->SetDataColor( data_name, 1, 0, 0 ); + else + this->m_UI->Viewer->SetMainImage( data_name ); + this->m_UI->Viewer->ShowData( data_name ); + } + else + QMessageBox::critical( + this, + QMessageBox::tr( "Error showing data" ), + QMessageBox::tr( "No known VTK conversion!" ) + ); + + } // fi + + } // fi +} + // eof - $RCSfile$ diff --git a/appli/cpPipelineEditor/App_cpPipelineEditor.h b/appli/cpPipelineEditor/App_cpPipelineEditor.h index 9d0d99a..831ef36 100644 --- a/appli/cpPipelineEditor/App_cpPipelineEditor.h +++ b/appli/cpPipelineEditor/App_cpPipelineEditor.h @@ -45,6 +45,10 @@ protected slots: void _ButtonLoadPluginsPath( ); void _ActionOpenWorkspace( ); void _ActionSaveWorkspace( ); + void _ExecFilter( const std::string& filter_name ); + void _ShowFilterOutput( + const std::string& filter_name, const std::string& output_name + ); private: Ui::App_cpPipelineEditor* m_UI; diff --git a/appli/cpPipelineEditor/App_cpPipelineEditor.ui b/appli/cpPipelineEditor/App_cpPipelineEditor.ui index faf9217..f54b6fd 100644 --- a/appli/cpPipelineEditor/App_cpPipelineEditor.ui +++ b/appli/cpPipelineEditor/App_cpPipelineEditor.ui @@ -22,116 +22,129 @@ - + - Qt::Horizontal + Qt::Vertical - + - 251 - 331 + 0 + 200 - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 202 - 67 - - - - - 202 - 67 - - - - Plugins - - - - - - - 85 - 31 - - - - - 85 - 31 - - - - File - - - - - - - - 85 - 31 - - - - - 85 - 31 - - - - Path - - - - - - - - - - - 229 - 0 - - - - true - - - QAbstractItemView::DragOnly - - - - Loaded plugins - - - - - - - - - 500 - 0 - - - - true + + + Qt::Horizontal + + + + 251 + 331 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 202 + 67 + + + + + 202 + 67 + + + + Plugins + + + + + + + 85 + 31 + + + + + 85 + 31 + + + + File + + + + + + + + 85 + 31 + + + + + 85 + 31 + + + + Path + + + + + + + + + + + 229 + 0 + + + + true + + + QAbstractItemView::DragOnly + + + + Loaded plugins + + + + + + + + + + 500 + 0 + + + + true + + @@ -187,6 +200,12 @@
cpPipelineEditor/Canvas.h
1 + + cpPlugins::Interface::SimpleMPRWidget + QWidget +
cpPlugins/Interface/SimpleMPRWidget.h
+ 1 +
diff --git a/lib/cpExtensions/Interaction/ImageInteractorStyle.cxx b/lib/cpExtensions/Interaction/ImageInteractorStyle.cxx index 0e6dce0..c037193 100644 --- a/lib/cpExtensions/Interaction/ImageInteractorStyle.cxx +++ b/lib/cpExtensions/Interaction/ImageInteractorStyle.cxx @@ -108,12 +108,22 @@ GetNumberOfSeeds( ) const // ------------------------------------------------------------------------- void cpExtensions::Interaction::ImageInteractorStyle:: -GetSeed( unsigned int id, double pos[ 3 ] ) const +GetSeedAsPoint( unsigned int id, double pos[ 3 ] ) const { if( this->m_SeedWidget != NULL ) this->m_SeedWidget->Representation->GetSeedWorldPosition( id, pos ); } +// ------------------------------------------------------------------------- +void cpExtensions::Interaction::ImageInteractorStyle:: +GetSeedAsIndex( unsigned int id, int idx[ 3 ] ) const +{ + /* TODO + if( this->m_SeedWidget != NULL ) + this->m_SeedWidget->Representation->GetSeedWorldPosition( id, pos ); + */ +} + // ------------------------------------------------------------------------- cpExtensions::Interaction::ImageInteractorStyle:: ImageInteractorStyle( ) diff --git a/lib/cpExtensions/Interaction/ImageInteractorStyle.h b/lib/cpExtensions/Interaction/ImageInteractorStyle.h index 45a349f..ec54f5e 100644 --- a/lib/cpExtensions/Interaction/ImageInteractorStyle.h +++ b/lib/cpExtensions/Interaction/ImageInteractorStyle.h @@ -68,7 +68,8 @@ namespace cpExtensions void SeedWidgetOff( ); void SetSeedWidgetCommand( vtkCommand* cmd ); unsigned int GetNumberOfSeeds( ) const; - void GetSeed( unsigned int id, double pos[ 3 ] ) const; + void GetSeedAsPoint( unsigned int id, double pos[ 3 ] ) const; + void GetSeedAsIndex( unsigned int id, int pos[ 3 ] ) const; protected: ImageInteractorStyle( ); diff --git a/lib/cpPipelineEditor/Block.cxx b/lib/cpPipelineEditor/Block.cxx index 7263cad..f27a7c8 100644 --- a/lib/cpPipelineEditor/Block.cxx +++ b/lib/cpPipelineEditor/Block.cxx @@ -11,6 +11,7 @@ #include "Port.h" #include "Connection.h" +#include "Editor.h" // ------------------------------------------------------------------------- cpPipelineEditor::Block:: @@ -20,7 +21,8 @@ Block( TFilter* filter, QGraphicsItem* parent, QGraphicsScene* scene ) m_VertMargin( 5 ), m_NamePort( NULL ), m_TypePort( NULL ), - m_Filter( filter ) + m_Filter( filter ), + m_Editor( NULL ) { QPainterPath p; p.addRoundedRect( -50, -15, 100, 30, 5, 5 ); @@ -57,6 +59,27 @@ cpPipelineEditor::Block:: { } +// ------------------------------------------------------------------------- +cpPipelineEditor::Editor* cpPipelineEditor::Block:: +editor( ) +{ + return( this->m_Editor ); +} + +// ------------------------------------------------------------------------- +const cpPipelineEditor::Editor* cpPipelineEditor::Block:: +editor( ) const +{ + return( this->m_Editor ); +} + +// ------------------------------------------------------------------------- +void cpPipelineEditor::Block:: +setEditor( cpPipelineEditor::Editor* editor ) +{ + this->m_Editor = editor; +} + // ------------------------------------------------------------------------- void cpPipelineEditor::Block:: setNamePort( const QString& txt ) @@ -266,8 +289,7 @@ contextMenuEvent( QGraphicsSceneContextMenuEvent* evt ) auto res = this->m_Filter->ExecConfigurationDialog( NULL ); } else if( selectedAction == updateAction ) - { - } // fi + this->m_Editor->updateFilter( this->namePort( ).toStdString( ) ); } // eof - $RCSfile$ diff --git a/lib/cpPipelineEditor/Block.h b/lib/cpPipelineEditor/Block.h index 763ecda..b6a7b33 100644 --- a/lib/cpPipelineEditor/Block.h +++ b/lib/cpPipelineEditor/Block.h @@ -7,6 +7,7 @@ namespace cpPipelineEditor { + class Editor; class Port; class NamePort; class TypePort; @@ -32,6 +33,10 @@ namespace cpPipelineEditor ); virtual ~Block( ); + Editor* editor( ); + const Editor* editor( ) const; + void setEditor( Editor* editor ); + void setNamePort( const QString& txt ); InputPort* addInputPort( const QString& txt ); OutputPort* addOutputPort( const QString& txt ); @@ -51,7 +56,7 @@ namespace cpPipelineEditor const QStyleOptionGraphicsItem* option, QWidget* widget ); - + protected: QVariant itemChange( GraphicsItemChange change, const QVariant& value ); void _setTypePort( const QString& txt ); @@ -72,6 +77,7 @@ namespace cpPipelineEditor std::map< std::string, OutputPort* > m_OutputPorts; TFilter* m_Filter; + Editor* m_Editor; }; } // ecapseman diff --git a/lib/cpPipelineEditor/Editor.cxx b/lib/cpPipelineEditor/Editor.cxx index 9ab6b35..2a0fca6 100644 --- a/lib/cpPipelineEditor/Editor.cxx +++ b/lib/cpPipelineEditor/Editor.cxx @@ -11,6 +11,7 @@ #include #include #include +#include #include "Port.h" #include "Connection.h" @@ -156,6 +157,7 @@ _createBlock( TFilter* f, const QPointF& pnt ) // Add block Block* b = new Block( f, 0, this->m_Scene ); + b->setEditor( this ); b->setPos( pnt ); // Mark exposed inputs @@ -232,6 +234,22 @@ eventFilter( QObject* o, QEvent* e ) return( this->Superclass::eventFilter( o, e ) ); } +// ------------------------------------------------------------------------- +void cpPipelineEditor::Editor:: +updateFilter( const std::string& filter_name ) +{ + emit execFilter( filter_name ); +} + +// ------------------------------------------------------------------------- +void cpPipelineEditor::Editor:: +showOutputData( + const std::string& filter_name, const std::string& output_name + ) +{ + emit showFilterOutput( filter_name, output_name ); +} + // ------------------------------------------------------------------------- cpPipelineEditor_Editor_Callback_CODE( ContextMenu ) { diff --git a/lib/cpPipelineEditor/Editor.h b/lib/cpPipelineEditor/Editor.h index f7a2615..ca273b2 100644 --- a/lib/cpPipelineEditor/Editor.h +++ b/lib/cpPipelineEditor/Editor.h @@ -61,6 +61,19 @@ namespace cpPipelineEditor void install( QGraphicsScene* s ); bool eventFilter( QObject* o, QEvent* e ); + void updateFilter( const std::string& filter_name ); + void showOutputData( + const std::string& filter_name, + const std::string& output_name + ); + + signals: + void execFilter( const std::string& filter_name ); + void showFilterOutput( + const std::string& filter_name, + const std::string& output_name + ); + private: QGraphicsItem* itemAt( const QPointF& pos ); diff --git a/lib/cpPipelineEditor/Port.cxx b/lib/cpPipelineEditor/Port.cxx index ab478c2..a2125f7 100644 --- a/lib/cpPipelineEditor/Port.cxx +++ b/lib/cpPipelineEditor/Port.cxx @@ -1,8 +1,12 @@ #include "Port.h" #include "Connection.h" +#include "Block.h" +#include "Editor.h" #include +#include #include +#include #include #include @@ -292,4 +296,28 @@ itemChange( GraphicsItemChange change, const QVariant& value ) return( value ); } +// ------------------------------------------------------------------------- +void cpPipelineEditor::OutputPort:: +contextMenuEvent( QGraphicsSceneContextMenuEvent* evt ) +{ + if( this->m_Block == NULL ) + return; + + QMenu menu; + QAction* showAction = menu.addAction( "Show" ); + QAction* hideAction = menu.addAction( "Hide" ); + QAction* selectedAction = menu.exec( evt->screenPos( ) ); + + if( selectedAction == showAction ) + { + this->m_Block->editor( )->showOutputData( + this->m_Block->namePort( ).toStdString( ), + this->name( ).toStdString( ) + ); + } + else if( selectedAction == hideAction ) + { + } // fi +} + // eof - $RCSfile$ diff --git a/lib/cpPipelineEditor/Port.h b/lib/cpPipelineEditor/Port.h index 0f64a4b..18bcd94 100644 --- a/lib/cpPipelineEditor/Port.h +++ b/lib/cpPipelineEditor/Port.h @@ -179,6 +179,8 @@ namespace cpPipelineEditor QVariant itemChange( GraphicsItemChange change, const QVariant& value ); virtual void _updateLabels( ); + virtual void contextMenuEvent( QGraphicsSceneContextMenuEvent* evt ); + protected: QVector< Connection* > m_Connections; }; diff --git a/lib/cpPlugins/Interface/Parameters.h b/lib/cpPlugins/Interface/Parameters.h index 765f82f..c93e160 100644 --- a/lib/cpPlugins/Interface/Parameters.h +++ b/lib/cpPlugins/Interface/Parameters.h @@ -118,7 +118,9 @@ class TiXmlElement; if( i->second.first == Self::Y##List ) \ { \ std::stringstream str; \ - str << i->second.second << "#" << v; \ + if( i->second.second != "" ) \ + str << i->second.second << "#"; \ + str << v; \ i->second.second = str.str( ); \ } \ } \ diff --git a/lib/cpPlugins/Interface/ParametersQtDialog.cxx b/lib/cpPlugins/Interface/ParametersQtDialog.cxx index 731fc77..e502c6e 100644 --- a/lib/cpPlugins/Interface/ParametersQtDialog.cxx +++ b/lib/cpPlugins/Interface/ParametersQtDialog.cxx @@ -24,6 +24,7 @@ #include #include +/* TODO class SingleSeedCommand : public vtkCommand { @@ -87,12 +88,14 @@ public: cpPlugins::Interface::ParametersQtDialog* Dialog; std::string Name; }; +*/ // ------------------------------------------------------------------------- cpPlugins::Interface::ParametersQtDialog:: ParametersQtDialog( QWidget* parent, Qt::WindowFlags f ) : QDialog( parent, f ), - m_Parameters( NULL ) + m_Parameters( NULL ), + m_Interactive( false ) { this->m_Title = new QLabel( this ); this->m_Title->setText( "Parameters dialog title" ); @@ -143,6 +146,34 @@ getInteractors( ) const return( this->m_Interactors ); } +// ------------------------------------------------------------------------- +bool cpPlugins::Interface::ParametersQtDialog:: +isInteractive( ) const +{ + return( this->m_Interactive ); +} + +// ------------------------------------------------------------------------- +void cpPlugins::Interface::ParametersQtDialog:: +setInteractive( bool i ) +{ + this->m_Interactive = i; +} + +// ------------------------------------------------------------------------- +void cpPlugins::Interface::ParametersQtDialog:: +interactiveOn( ) +{ + this->m_Interactive = true; +} + +// ------------------------------------------------------------------------- +void cpPlugins::Interface::ParametersQtDialog:: +interactiveOff( ) +{ + this->m_Interactive = false; +} + // ------------------------------------------------------------------------- bool cpPlugins::Interface::ParametersQtDialog:: setParameters( Parameters* parameters ) @@ -158,6 +189,7 @@ setParameters( Parameters* parameters ) this->m_Title->setText( title.str( ).c_str( ) ); // Put values + this->m_Widgets.clear( ); auto& raw_params = this->m_Parameters->GetRawParameters( ); for( auto pIt = raw_params.begin( ); pIt != raw_params.end( ); ++pIt ) { diff --git a/lib/cpPlugins/Interface/ParametersQtDialog.h b/lib/cpPlugins/Interface/ParametersQtDialog.h index 4d4d37f..40b8514 100644 --- a/lib/cpPlugins/Interface/ParametersQtDialog.h +++ b/lib/cpPlugins/Interface/ParametersQtDialog.h @@ -7,10 +7,12 @@ #include #include -#include #include +#include +#include + #include #include #include @@ -31,9 +33,10 @@ namespace cpPlugins Q_OBJECT; public: - typedef cpExtensions::Interaction::ImageInteractorStyle TStyle; - typedef std::set< vtkRenderWindowInteractor* > TInteractors; + typedef + std::map< std::string, vtkSmartPointer< vtkInteractorObserver > > + TWidgets; public: ParametersQtDialog( QWidget* parent = 0, Qt::WindowFlags f = 0 ); @@ -43,6 +46,10 @@ namespace cpPlugins void addInteractor( vtkRenderWindowInteractor* interactor ); TInteractors& getInteractors( ); const TInteractors& getInteractors( ) const; + bool isInteractive( ) const; + void setInteractive( bool i ); + void interactiveOn( ); + void interactiveOff( ); bool setParameters( Parameters* parameters ); virtual int exec( ); @@ -68,6 +75,8 @@ namespace cpPlugins QDialogButtonBox* m_Buttons; TInteractors m_Interactors; + TWidgets m_Widgets; + bool m_Interactive; }; } // ecapseman diff --git a/lib/cpPlugins/Interface/Plugins.cxx b/lib/cpPlugins/Interface/Plugins.cxx index ece9dd0..c5727c2 100644 --- a/lib/cpPlugins/Interface/Plugins.cxx +++ b/lib/cpPlugins/Interface/Plugins.cxx @@ -342,7 +342,7 @@ ReadImage( const std::string& parent ) this->_ActivateIOFilter( "ImageReader" ); // Try to configure source - if( this->ConfigureActiveFilter( ) == TProcessObject::DialogResult_Cancel ) + if( !( this->ConfigureActiveFilter( ) ) ) { this->DeactivateFilter( ); return( "" ); @@ -361,7 +361,7 @@ ReadDicomSeries( const std::string& parent ) this->_ActivateIOFilter( "DicomSeriesReader" ); // Try to configure source - if( this->ConfigureActiveFilter( ) == TProcessObject::DialogResult_Cancel ) + if( !( this->ConfigureActiveFilter( ) ) ) { this->DeactivateFilter( ); return( "" ); @@ -395,7 +395,7 @@ ReadMesh( const std::string& parent ) this->_ActivateIOFilter( "MeshReader" ); // Try to configure source - if( this->ConfigureActiveFilter( ) == TProcessObject::DialogResult_Cancel ) + if( !( this->ConfigureActiveFilter( ) ) ) { this->DeactivateFilter( ); return( "" ); @@ -455,7 +455,7 @@ WriteDataObject( const std::string& name ) } // fi // Try to configure source - if( this->ConfigureActiveFilter( ) == TProcessObject::DialogResult_Cancel ) + if( !( this->ConfigureActiveFilter( ) ) ) { this->DeactivateFilter( ); return( false ); @@ -656,7 +656,7 @@ ConfigureActiveFilter( ) if( this->m_ActiveFilter.IsNotNull( ) ) return( this->m_ActiveFilter->ExecConfigurationDialog( this->m_Widget ) ); else - return( TProcessObject::DialogResult_Cancel ); + return( false ); } // ------------------------------------------------------------------------- diff --git a/lib/cpPlugins/Interface/PointList.cxx b/lib/cpPlugins/Interface/PointList.cxx new file mode 100644 index 0000000..87a3570 --- /dev/null +++ b/lib/cpPlugins/Interface/PointList.cxx @@ -0,0 +1,33 @@ +#include + +// ------------------------------------------------------------------------- +unsigned long cpPlugins::Interface::PointList:: +GetNumberOfPoints( ) const +{ + return( this->m_NumberOfPoints ); +} + +// ------------------------------------------------------------------------- +void cpPlugins::Interface::PointList:: +Clear( ) +{ + this->m_ITKObject = NULL; + this->m_VTKObject = NULL; + this->m_NumberOfPoints = 0; +} + +// ------------------------------------------------------------------------- +cpPlugins::Interface::PointList:: +PointList( ) + : Superclass( ) +{ + this->Clear( ); +} + +// ------------------------------------------------------------------------- +cpPlugins::Interface::PointList:: +~PointList( ) +{ +} + +// eof - $RCSfile$ diff --git a/lib/cpPlugins/Interface/PointList.h b/lib/cpPlugins/Interface/PointList.h new file mode 100644 index 0000000..f41fb18 --- /dev/null +++ b/lib/cpPlugins/Interface/PointList.h @@ -0,0 +1,58 @@ +#ifndef __CPPLUGINS__INTERFACE__POINTLIST__H__ +#define __CPPLUGINS__INTERFACE__POINTLIST__H__ + +#include + +namespace cpPlugins +{ + namespace Interface + { + /** + */ + class cpPlugins_Interface_EXPORT PointList + : public DataObject + { + public: + typedef PointList Self; + typedef DataObject Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + public: + itkNewMacro( Self ); + itkTypeMacro( PointList, DataObject ); + cpPlugins_Id_Macro( PointList, DataObject ); + + public: + unsigned long GetNumberOfPoints( ) const; + + void Clear( ); + + template< class P > + inline void AddPoint( const P& p ); + + template< class P > + inline P GetPoint( const unsigned long& i ) const; + + protected: + PointList( ); + virtual ~PointList( ); + + private: + // Purposely not implemented + PointList( const Self& ); + Self& operator=( const Self& ); + + protected: + unsigned long m_NumberOfPoints; + }; + + } // ecapseman + +} // ecapseman + +#include + +#endif // __CPPLUGINS__INTERFACE__POINTLIST__H__ + +// eof - $RCSfile$ diff --git a/lib/cpPlugins/Interface/PointList.hxx b/lib/cpPlugins/Interface/PointList.hxx new file mode 100644 index 0000000..95e9e1f --- /dev/null +++ b/lib/cpPlugins/Interface/PointList.hxx @@ -0,0 +1,53 @@ +#ifndef __CPPLUGINS__INTERFACE__POINTLIST__HXX__ +#define __CPPLUGINS__INTERFACE__POINTLIST__HXX__ + +#include +#include +#include + +// ------------------------------------------------------------------------- +template< class P > +void cpPlugins::Interface::PointList:: +AddPoint( const P& p ) +{ + typedef itk::SimpleDataObjectDecorator< std::vector< P > > _T; + + _T* container = NULL; + if( this->m_NumberOfPoints == 0 ) + { + typename _T::Pointer obj = _T::New( ); + container = obj.GetPointer( ); + this->m_ITKObject = container; + } + else + container = dynamic_cast< _T* >( this->m_ITKObject.GetPointer( ) ); + + if( container != NULL ) + { + container->Get( ).push_back( p ); + this->m_NumberOfPoints += 1; + + } // fi +} + +// ------------------------------------------------------------------------- +template< class P > +P cpPlugins::Interface::PointList:: +GetPoint( const unsigned long& i ) const +{ + typedef itk::SimpleDataObjectDecorator< std::vector< P > > _T; + + P ret; + if( i < this->m_NumberOfPoints ) + { + _T* container = dynamic_cast< _T* >( this->m_ITKObject.GetPointer( ) ); + if( container != NULL ) + ret = container->Get( )[ i ]; + + } // fi + return( ret ); +} + +#endif // __CPPLUGINS__INTERFACE__POINTLIST__HXX__ + +// eof - $RCSfile$ diff --git a/lib/cpPlugins/Interface/ProcessObject.cxx b/lib/cpPlugins/Interface/ProcessObject.cxx index db873e9..1db1a61 100644 --- a/lib/cpPlugins/Interface/ProcessObject.cxx +++ b/lib/cpPlugins/Interface/ProcessObject.cxx @@ -131,6 +131,14 @@ DisconnectOutputs( ) i->second->DisconnectPipeline( ); } +// ------------------------------------------------------------------------- +const cpPlugins::Interface::ProcessObject:: +TInteractors& cpPlugins::Interface::ProcessObject:: +GetInteractors( ) const +{ + return( this->m_Interactors ); +} + // ------------------------------------------------------------------------- void cpPlugins::Interface::ProcessObject:: AddInteractor( vtkRenderWindowInteractor* interactor ) @@ -144,11 +152,10 @@ AddInteractor( vtkRenderWindowInteractor* interactor ) } // ------------------------------------------------------------------------- -cpPlugins::Interface::ProcessObject:: -DialogResult cpPlugins::Interface::ProcessObject:: +bool cpPlugins::Interface::ProcessObject:: ExecConfigurationDialog( QWidget* parent ) { - DialogResult r = Self::DialogResult_Cancel; + bool r = false; #ifdef cpPlugins_Interface_QT4 @@ -165,14 +172,11 @@ ExecConfigurationDialog( QWidget* parent ) this->m_ParametersDialog->setParent( NULL ); this->m_ParametersDialog->setParameters( this->m_Parameters ); - if( this->m_ParametersDialog->exec( ) == 1 ) - r = Self::DialogResult_NoModal; - else - r = Self::DialogResult_Cancel; + r = ( this->m_ParametersDialog->exec( ) == 1 ); } else - r = Self::DialogResult_Cancel; - + r = false; + #endif // cpPlugins_Interface_QT4 return( r ); diff --git a/lib/cpPlugins/Interface/ProcessObject.h b/lib/cpPlugins/Interface/ProcessObject.h index b6cf116..52b5654 100644 --- a/lib/cpPlugins/Interface/ProcessObject.h +++ b/lib/cpPlugins/Interface/ProcessObject.h @@ -43,13 +43,7 @@ namespace cpPlugins typedef Parameters TParameters; typedef std::set< vtkRenderWindowInteractor* > TInteractors; - - enum DialogResult - { - DialogResult_NoModal = 0, - DialogResult_Modal, - DialogResult_Cancel - }; + typedef bool DialogResult; public: itkTypeMacro( ProcessObject, Object ); @@ -85,8 +79,9 @@ namespace cpPlugins virtual std::string Update( ); virtual void DisconnectOutputs( ); + virtual const TInteractors& GetInteractors( ) const; virtual void AddInteractor( vtkRenderWindowInteractor* interactor ); - virtual DialogResult ExecConfigurationDialog( QWidget* parent ); + virtual bool ExecConfigurationDialog( QWidget* parent ); template< class T > inline T* GetITK( ); diff --git a/lib/cpPlugins/Interface/SimpleMPRWidget.cxx b/lib/cpPlugins/Interface/SimpleMPRWidget.cxx index c7c3058..7906883 100644 --- a/lib/cpPlugins/Interface/SimpleMPRWidget.cxx +++ b/lib/cpPlugins/Interface/SimpleMPRWidget.cxx @@ -63,6 +63,13 @@ cpPlugins::Interface::SimpleMPRWidget:: delete this->m_UI; } +// ------------------------------------------------------------------------- +unsigned int cpPlugins::Interface::SimpleMPRWidget:: +GetNumberOfData( ) const +{ + return( this->m_Data.size( ) ); +} + // ------------------------------------------------------------------------- bool cpPlugins::Interface::SimpleMPRWidget:: AddData( diff --git a/lib/cpPlugins/Interface/SimpleMPRWidget.h b/lib/cpPlugins/Interface/SimpleMPRWidget.h index 7fe49b6..48d3979 100644 --- a/lib/cpPlugins/Interface/SimpleMPRWidget.h +++ b/lib/cpPlugins/Interface/SimpleMPRWidget.h @@ -66,6 +66,7 @@ namespace cpPlugins virtual ~SimpleMPRWidget( ); // Data management + unsigned int GetNumberOfData( ) const; bool AddData( DataObject* data, const std::string& name, const std::string& parent @@ -104,7 +105,7 @@ namespace cpPlugins protected: static double cm_Colors[ 8 ][ 3 ]; - Ui::SimpleMPRWidget* m_UI; + Ui::SimpleMPRWidget* m_UI; vtkSmartPointer< TMPRObjects > m_MPRObjects; QVTKWidget* m_VTK[ 4 ]; diff --git a/lib/cpPlugins/Interface/Workspace.cxx b/lib/cpPlugins/Interface/Workspace.cxx index 67eaf0a..528f774 100644 --- a/lib/cpPlugins/Interface/Workspace.cxx +++ b/lib/cpPlugins/Interface/Workspace.cxx @@ -116,6 +116,13 @@ CreateFilter( const std::string& filter, const std::string& name ) if( f.IsNotNull( ) ) { f->SetName( name ); + for( + auto iIt = this->m_Interactors.begin( ); + iIt != this->m_Interactors.end( ); + ++iIt + ) + f->AddInteractor( *iIt ); + TObject::Pointer o = f.GetPointer( ); this->m_Graph->SetVertex( name, o ); return( true ); @@ -147,6 +154,21 @@ RemoveFilter( const std::string& name ) { } +// ------------------------------------------------------------------------- +const cpPlugins::Interface::Workspace:: +TInteractors& cpPlugins::Interface::Workspace:: +GetInteractors( ) const +{ + return( this->m_Interactors ); +} + +// ------------------------------------------------------------------------- +void cpPlugins::Interface::Workspace:: +AddInteractor( vtkRenderWindowInteractor* interactor ) +{ + this->m_Interactors.insert( interactor ); +} + // ------------------------------------------------------------------------- bool cpPlugins::Interface::Workspace:: Connect( @@ -294,7 +316,7 @@ GetExposedOutputPorts( ) const // ------------------------------------------------------------------------- std::string cpPlugins::Interface::Workspace:: -Execute( QWidget* p ) +Execute( ) { // Find sinks std::set< std::string > sinks = this->m_Graph->GetSinks( ); @@ -303,7 +325,7 @@ Execute( QWidget* p ) std::string err = ""; for( auto sIt = sinks.begin( ); sIt != sinks.end( ); ++sIt ) { - std::string lerr = this->Execute( *sIt, p ); + std::string lerr = this->Execute( *sIt ); if( lerr != "" ) err += lerr + std::string( "\n" ); @@ -313,7 +335,7 @@ Execute( QWidget* p ) // ------------------------------------------------------------------------- std::string cpPlugins::Interface::Workspace:: -Execute( const std::string& name, QWidget* p ) +Execute( const std::string& name ) { // Get filter TFilter* f = this->GetFilter( name ); @@ -324,16 +346,7 @@ Execute( const std::string& name, QWidget* p ) ); // Execute and return - if( p != NULL ) - { - auto diag_res = f->ExecConfigurationDialog( p ); - if( diag_res == TFilter::DialogResult_NoModal ) - return( f->Update( ) ); - else - return( "" ); - } - else - return( f->Update( ) ); + return( f->Update( ) ); } // eof - $RCSfile$ diff --git a/lib/cpPlugins/Interface/Workspace.h b/lib/cpPlugins/Interface/Workspace.h index 92315d7..3d78f02 100644 --- a/lib/cpPlugins/Interface/Workspace.h +++ b/lib/cpPlugins/Interface/Workspace.h @@ -11,6 +11,7 @@ // Some forward declarations class QWidget; +class vtkRenderWindowInteractor; namespace cpPlugins { @@ -31,6 +32,7 @@ namespace cpPlugins typedef std::set< std::string > TStringContainer; typedef std::pair< std::string, std::string > TExposedPort; typedef std::map< std::string, TExposedPort > TExposedPorts; + typedef std::set< vtkRenderWindowInteractor* > TInteractors; // Graph type typedef std::pair< std::string, std::string > TConnection; @@ -68,6 +70,10 @@ namespace cpPlugins ); void RemoveFilter( const std::string& name ); + // Widgets management + virtual const TInteractors& GetInteractors( ) const; + virtual void AddInteractor( vtkRenderWindowInteractor* interactor ); + // Connection management bool Connect( const std::string& orig_filter, const std::string& dest_filter, @@ -111,8 +117,8 @@ namespace cpPlugins const TData* GetExposedOutput( const std::string& name ) const; // Pipeline execution - std::string Execute( QWidget* p = NULL ); - std::string Execute( const std::string& name, QWidget* p = NULL ); + std::string Execute( ); + std::string Execute( const std::string& name ); protected: // Plugins interface @@ -122,6 +128,8 @@ namespace cpPlugins typename TGraph::Pointer m_Graph; TExposedPorts m_ExposedInputPorts; TExposedPorts m_ExposedOutputPorts; + + TInteractors m_Interactors; }; } // ecapseman diff --git a/lib/cpPlugins/Plugins/BasicFilters/DoubleFloodImageFilter.cxx b/lib/cpPlugins/Plugins/BasicFilters/DoubleFloodImageFilter.cxx index c6aef7e..61ff7d2 100644 --- a/lib/cpPlugins/Plugins/BasicFilters/DoubleFloodImageFilter.cxx +++ b/lib/cpPlugins/Plugins/BasicFilters/DoubleFloodImageFilter.cxx @@ -86,7 +86,7 @@ accept( ) if( nTotalSeeds < 2 ) { double seed[ 3 ]; - istyle->GetSeed( s, seed ); + istyle->GetSeedAsPoint( s, seed ); if( nTotalSeeds == 0 ) this->m_Filter->GetParameters( )->SetPoint( "Seed0", 3, seed ); else @@ -149,15 +149,15 @@ ExecConfigurationDialog( QWidget* parent ) } // rof if( !at_least_one ) - return( Self::DialogResult_Cancel ); - + return( false ); + // Create dialog this->m_Dialog = new DoubleFloodImageFilter_Dialog( NULL, this ); this->m_Dialog->show( ); - return( Self::DialogResult_Modal ); + return( true ); #else // cpPlugins_Interface_QT4 - return( Self::DialogResult_Cancel ); + return( false ); #endif // cpPlugins_Interface_QT4 } diff --git a/lib/cpPlugins/Plugins/BasicFilters/MacheteFilter.cxx b/lib/cpPlugins/Plugins/BasicFilters/MacheteFilter.cxx index 8116e21..1f4709c 100644 --- a/lib/cpPlugins/Plugins/BasicFilters/MacheteFilter.cxx +++ b/lib/cpPlugins/Plugins/BasicFilters/MacheteFilter.cxx @@ -121,8 +121,8 @@ ExecConfigurationDialog( QWidget* parent ) } // rof if( iren == NULL ) - return( Self::DialogResult_Cancel ); - + return( false ); + // Get bounding box double bbox[ 6 ]; cpPlugins::Interface::Image* image = @@ -143,7 +143,7 @@ ExecConfigurationDialog( QWidget* parent ) } // fi if( !input_found ) - return( Self::DialogResult_Cancel ); + return( false ); // Create plane widget if( this->m_PlaneWidget != NULL ) @@ -179,9 +179,9 @@ ExecConfigurationDialog( QWidget* parent ) this->m_Dialog = new MacheteFilter_Dialog( NULL, this ); this->m_Dialog->show( ); - return( Self::DialogResult_Modal ); + return( true ); #else // cpPlugins_Interface_QT4 - return( Self::DialogResult_Cancel ); + return( false ); #endif // cpPlugins_Interface_QT4 } diff --git a/lib/cpPlugins/Plugins/CMakeLists.txt b/lib/cpPlugins/Plugins/CMakeLists.txt index 6fe1263..4d70488 100644 --- a/lib/cpPlugins/Plugins/CMakeLists.txt +++ b/lib/cpPlugins/Plugins/CMakeLists.txt @@ -1,6 +1,7 @@ SUBDIRS( IO BasicFilters + Widgets ) ## eof - $RCSfile$ diff --git a/lib/cpPlugins/Plugins/IO/DicomSeriesReader.cxx b/lib/cpPlugins/Plugins/IO/DicomSeriesReader.cxx index e2aeb31..4841bcf 100644 --- a/lib/cpPlugins/Plugins/IO/DicomSeriesReader.cxx +++ b/lib/cpPlugins/Plugins/IO/DicomSeriesReader.cxx @@ -20,11 +20,10 @@ #include // ------------------------------------------------------------------------- -cpPlugins::IO::DicomSeriesReader:: -DialogResult cpPlugins::IO::DicomSeriesReader:: +bool cpPlugins::IO::DicomSeriesReader:: ExecConfigurationDialog( QWidget* parent ) { - DialogResult r = Self::DialogResult_Cancel; + bool r = false; #ifdef cpPlugins_Interface_QT4 @@ -38,7 +37,7 @@ ExecConfigurationDialog( QWidget* parent ) dialog.setFileMode( QFileDialog::DirectoryOnly ); dialog.setDirectory( QFileDialog::tr( "." ) ); if( !dialog.exec( ) ) - return( Self::DialogResult_Cancel ); + return( false ); // Prepare dialog QApplication::setOverrideCursor( Qt::WaitCursor ); @@ -120,7 +119,7 @@ ExecConfigurationDialog( QWidget* parent ) { delete tree_widget; delete tree_dialog; - return( Self::DialogResult_Cancel ); + return( false ); } // fi @@ -150,7 +149,7 @@ ExecConfigurationDialog( QWidget* parent ) parent->setEnabled( true ); if( tree_dialog->exec( ) == 0 ) - return( Self::DialogResult_Cancel ); + return( false ); QTreeWidgetItem* item = tree_widget->currentItem( ); if( item != NULL ) @@ -165,12 +164,12 @@ ExecConfigurationDialog( QWidget* parent ) std::string serie_dir = item_parent->text( 0 ).toStdString( ); std::string serie_id = item->text( 0 ).toStdString( ); serie_id = serie_id.substr( serie_id.find_first_of( " " ) + 1 ); - this->m_Parameters->ClearStringList( "FileNames" ); + this->m_Parameters->ClearOpenFileNameList( "FileNames" ); const TStringList& names = found_files[ serie_dir ][ serie_id ]; for( unsigned int f = 0; f < names.size( ); ++f ) - this->m_Parameters->AddToStringList( "FileNames", names[ f ] ); + this->m_Parameters->AddToOpenFileNameList( "FileNames", names[ f ] ); - r = Self::DialogResult_NoModal; + r = true; QApplication::restoreOverrideCursor( ); if( parent != NULL ) diff --git a/lib/cpPlugins/Plugins/IO/DicomSeriesReader.h b/lib/cpPlugins/Plugins/IO/DicomSeriesReader.h index fb8edb0..33295bf 100644 --- a/lib/cpPlugins/Plugins/IO/DicomSeriesReader.h +++ b/lib/cpPlugins/Plugins/IO/DicomSeriesReader.h @@ -35,7 +35,7 @@ namespace cpPlugins cpPlugins_Id_Macro( cpPlugins::IO::DicomSeriesReader, IO ); public: - virtual DialogResult ExecConfigurationDialog( QWidget* parent ); + virtual bool ExecConfigurationDialog( QWidget* parent ); protected: DicomSeriesReader( ); diff --git a/lib/cpPlugins/Plugins/Widgets/CMakeLists.txt b/lib/cpPlugins/Plugins/Widgets/CMakeLists.txt new file mode 100644 index 0000000..5c1a47c --- /dev/null +++ b/lib/cpPlugins/Plugins/Widgets/CMakeLists.txt @@ -0,0 +1,55 @@ +SET(LIBRARY_NAME cpPluginsWidgets) + +## =============== +## = Source code = +## =============== + +FILE(GLOB LIB_HEADERS_H "*.h") +FILE(GLOB LIB_HEADERS_HPP "*.hpp") +FILE(GLOB LIB_HEADERS_HXX "*.hxx") +FILE(GLOB LIB_SOURCES_C "*.c") +FILE(GLOB LIB_SOURCES_CPP "*.cpp") +FILE(GLOB LIB_SOURCES_CXX "*.cxx") + +## ===================== +## = Compilation rules = +## ===================== + +ADD_CUSTOM_COMMAND( + OUTPUT ${LIBRARY_NAME}_Host.cxx + DEPENDS ${cpPlugins_createHost_APP} ${LIB_HEADERS_H} + COMMAND ${cpPlugins_createHost_APP} ${LIBRARY_NAME}_Host.cxx cpPlugins::Widgets ${LIB_HEADERS_H} + ) + +ADD_LIBRARY( + ${LIBRARY_NAME} + SHARED + ${LIBRARY_NAME}_Host.cxx + ${LIB_SOURCES_C} + ${LIB_SOURCES_CPP} + ${LIB_SOURCES_CXX} + ) +GENERATE_EXPORT_HEADER( + ${LIBRARY_NAME} + BASE_NAME ${LIBRARY_NAME} + EXPORT_MACRO_NAME ${LIBRARY_NAME}_EXPORT + EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/lib/cpPlugins/Widgets/${LIBRARY_NAME}_Export.h + STATIC_DEFINE ${LIBRARY_NAME}_BUILT_AS_STATIC + ) +TARGET_LINK_LIBRARIES( + ${LIBRARY_NAME} + cpPlugins_Interface + ) + +## ======================== +## -- Installation rules -- +## ======================== + +INSTALL( + TARGETS ${LIBRARY_NAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + ) + +## eof - $RCSfile$ diff --git a/lib/cpPlugins/Plugins/Widgets/SeedWidget.cxx b/lib/cpPlugins/Plugins/Widgets/SeedWidget.cxx new file mode 100644 index 0000000..129a8a6 --- /dev/null +++ b/lib/cpPlugins/Plugins/Widgets/SeedWidget.cxx @@ -0,0 +1,96 @@ +#include "SeedWidget.h" + +#include +#include +#include + +#include + +// ------------------------------------------------------------------------- +cpPlugins::Widgets::SeedWidget:: +SeedWidget( ) + : Superclass( ), + m_Configured( false ) +{ + this->_AddInput( "ReferenceImage" ); + this->_MakeOutput< cpPlugins::Interface::PointList >( "Output" ); + + this->m_Parameters->ConfigureAsBool( "SeedsAreInRealSpace" ); + this->m_Parameters->SetBool( "SeedsAreInRealSpace", true ); +} + +// ------------------------------------------------------------------------- +cpPlugins::Widgets::SeedWidget:: +~SeedWidget( ) +{ +} + +// ------------------------------------------------------------------------- +std::string cpPlugins::Widgets::SeedWidget:: +_GenerateData( ) +{ + typedef itk::ImageBase< 2 > _2DImage; + typedef itk::ImageBase< 3 > _3DImage; + + cpPlugins::Interface::Image* image = + this->GetInput< cpPlugins::Interface::Image >( "ReferenceImage" ); + if( image == NULL ) + return( "SeedWidget: No input image." ); + + itk::DataObject* itk_image = image->GetITK< _2DImage >( ); + if( itk_image != NULL ) + return( this->_GD0< _2DImage >( itk_image ) ); + else + { + itk_image = image->GetITK< _3DImage >( ); + if( itk_image != NULL ) + return( this->_GD0< _3DImage >( itk_image ) ); + + } // fi + + return( "SeedWidget: Input image dimension not supported." ); +} + +// ------------------------------------------------------------------------- +template< class I > +std::string cpPlugins::Widgets::SeedWidget:: +_GD0( itk::DataObject* image ) +{ + typedef cpExtensions::Interaction::ImageInteractorStyle _S; + + I* base_image = dynamic_cast< I* >( image ); + cpPlugins::Interface::PointList* out = + this->GetOutput< cpPlugins::Interface::PointList >( "Output" ); + double aux_pnt[ 3 ]; + unsigned int dim = ( I::ImageDimension < 3 )? I::ImageDimension: 3; + + out->Clear( ); + auto iIt = this->m_Interactors.begin( ); + for( ; iIt != this->m_Interactors.end( ); ++iIt ) + { + _S* s = dynamic_cast< _S* >( ( *iIt )->GetInteractorStyle( ) ); + if( s != NULL ) + { + if( this->m_Configured ) + { + for( unsigned int i = 0; i < s->GetNumberOfSeeds( ); ++i ) + { + s->GetSeedAsPoint( i, aux_pnt ); + typename I::PointType seed; + for( unsigned int d = 0; d < dim; ++d ) + seed[ d ] = aux_pnt[ d ]; + out->AddPoint( seed ); + + } // rof + } + else + s->SeedWidgetOn( ); + + } // fi + + } // rof + this->m_Configured = true; + return( "" ); +} + +// eof - $RCSfile$ diff --git a/lib/cpPlugins/Plugins/Widgets/SeedWidget.h b/lib/cpPlugins/Plugins/Widgets/SeedWidget.h new file mode 100644 index 0000000..709a9c6 --- /dev/null +++ b/lib/cpPlugins/Plugins/Widgets/SeedWidget.h @@ -0,0 +1,54 @@ +#ifndef __CPPLUGINS__PLUGINS__SEEDWIDGET__H__ +#define __CPPLUGINS__PLUGINS__SEEDWIDGET__H__ + +#include +#include + +namespace cpPlugins +{ + namespace Widgets + { + /** + */ + class cpPluginsWidgets_EXPORT SeedWidget + : public cpPlugins::Interface::ProcessObject + { + public: + typedef SeedWidget Self; + typedef cpPlugins::Interface::ProcessObject Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + public: + itkNewMacro( Self ); + itkTypeMacro( SeedWidget, cpPlugins::Interface::ProcessObject ); + cpPlugins_Id_Macro( cpPlugins::Widgets::SeedWidget, Widgets ); + + protected: + SeedWidget( ); + virtual ~SeedWidget( ); + + virtual std::string _GenerateData( ); + + template< class I > + inline std::string _GD0( itk::DataObject* image ); + + private: + // Purposely not implemented + SeedWidget( const Self& ); + Self& operator=( const Self& ); + + protected: + bool m_Configured; + }; + + // --------------------------------------------------------------------- + CPPLUGINS_INHERIT_PROVIDER( SeedWidget ); + + } // ecapseman + +} // ecapseman + +#endif // __CPPLUGINS__PLUGINS__SEEDWIDGET__H__ + +// eof - $RCSfile$