Commit 526a7e65 authored by Alexander Wiebel's avatar Alexander Wiebel
Browse files

[MERGE]

parents 0ae7ff3b 5541319c
......@@ -22,9 +22,15 @@
//
//---------------------------------------------------------------------------
#include <vector>
#include <string>
#include "WLogger.h"
#include "WProjectFileIO.h"
WProjectFileIO::WProjectFileIO()
WProjectFileIO::WProjectFileIO():
m_errors()
{
// initialize
}
......@@ -39,3 +45,19 @@ void WProjectFileIO::done()
// do nothing here. Overwrite this method if your specific parser needs to do some post processing.
}
bool WProjectFileIO::hadErrors() const
{
return m_errors.size();
}
const std::vector< std::string >& WProjectFileIO::getErrors() const
{
return m_errors;
}
void WProjectFileIO::addError( std::string description )
{
wlog::error( "Project Loader" ) << description;
m_errors.push_back( description );
}
......@@ -27,11 +27,12 @@
#include <ostream>
#include <string>
#include <vector>
/**
* A base class for all parts of OpenWalnut which can be serialized to a project file. It is used by WProjectFile to actually parse the file line
* by line. Derive from this class if you write your own parser and use it to fill your internal data structures.
* by line. Derive from this class if you write your own parser and use it to fill your internal data structures. But write it in a very
* error-tolerant way. We want to avoid that small problems in the project file cause the whole file to be useless.
*/
class WProjectFileIO // NOLINT
{
......@@ -47,7 +48,8 @@ public:
virtual ~WProjectFileIO();
/**
* This method parses the specified line and interprets it. It gets called line by line by WProjectFile.
* This method parses the specified line and interprets it. It gets called line by line by WProjectFile. You should avoid applying anything
* of the loaded information here. You should use \ref done for this.
*
* \param line the current line as string
* \param lineNumber the current line number. Useful for error/warning/debugging output.
......@@ -57,8 +59,9 @@ public:
virtual bool parse( std::string line, unsigned int lineNumber ) = 0;
/**
* Called whenever the end of the project file has been reached. This is useful if your specific parser class wants to do some post
* processing after parsing line by line.
* Called whenever the end of the project file has been reached. Use this to actually apply your loaded settings. Do this in a error-tolerant
* way and apply as most settings as possible even if some other settings are erroneous. Add errors with \ref addError. Try avoiding
* exceptions if possible.
*/
virtual void done();
......@@ -69,8 +72,34 @@ public:
*/
virtual void save( std::ostream& output ) = 0; // NOLINT
/**
* Checks whether there where errors during load or save.
*
* \return true if there where.
*/
bool hadErrors() const;
/**
* Get error list.
*
* \return the list
*/
const std::vector< std::string >& getErrors() const;
protected:
/**
* Add an error. Use this when you encounter some difficulties during parsing or applying settings. Provide useful errors. They will be
* presented to the user.
*
* \param description the error description
*/
void addError( std::string description );
private:
/**
* List of errors if any.
*/
std::vector< std::string > m_errors;
};
#endif // WPROJECTFILEIO_H
......
......@@ -22,9 +22,11 @@
//
//---------------------------------------------------------------------------
#include <algorithm>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <boost/regex.hpp>
......@@ -34,6 +36,7 @@
#include "../graphicsEngine/WGEProjectFileIO.h"
#include "../common/exceptions/WFileNotFound.h"
#include "../common/exceptions/WFileOpenFailed.h"
#include "../common/WStringUtils.h"
#include "WProjectFile.h"
......@@ -54,10 +57,27 @@ WProjectFile::WProjectFile( boost::filesystem::path project ):
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WGEProjectFileIO() ) );
}
WProjectFile::WProjectFile( boost::filesystem::path project, ProjectLoadCallback doneCallback ):
WThreadedRunner(),
boost::enable_shared_from_this< WProjectFile >(),
m_project( project ),
m_signalLoadDoneConnection( m_signalLoadDone.connect( doneCallback ) )
{
// The module graph parser
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WModuleProjectFileCombiner() ) );
// The ROI parser
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WRoiProjectFileIO() ) );
// The Camera parser
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WGEProjectFileIO() ) );
}
WProjectFile::~WProjectFile()
{
// cleanup
m_parsers.clear();
m_signalLoadDoneConnection.disconnect();
}
boost::shared_ptr< WProjectFileIO > WProjectFile::getCameraWriter()
......@@ -116,12 +136,21 @@ void WProjectFile::threadMain()
// Parse the file
wlog::info( "Project File" ) << "Loading project file \"" << m_project.string() << "\".";
// store some errors
std::vector< std::string > errors;
// read the file
std::ifstream input( m_project.string().c_str() );
if( !input.is_open() )
{
throw WFileNotFound( std::string( "The project file \"" ) + m_project.string() +
std::string( "\" does not exist." ) );
errors.push_back( std::string( "The project file \"" ) + m_project.string() + std::string( "\" does not exist." ) );
// give some feedback
m_signalLoadDone( m_project, errors );
m_signalLoadDoneConnection.disconnect();
// also throw an exception
throw WFileNotFound( *errors.begin() );
}
// the comment
......@@ -152,14 +181,15 @@ void WProjectFile::threadMain()
}
catch( const std::exception& e )
{
wlog::error( "Project Loader" ) << "Line " << i << ": Parsing caused an exception. Line Malformed? Skipping.";
errors.push_back( "Parse error on line " + string_utils::toString( i ) + ": " + e.what() );
wlog::error( "Project Loader" ) << errors.back();
}
}
// did someone match this line? Or is it empty or a comment?
if( !match && !line.empty() && !boost::regex_match( line, matches, commentRe ) )
{
// no it is something else -> warning!
// no it is something else -> warning! Not a critical error.
wlog::warn( "Project Loader" ) << "Line " << i << ": Malformed. Skipping.";
}
}
......@@ -169,9 +199,23 @@ void WProjectFile::threadMain()
// finally, let every one know that we have finished
for( std::vector< boost::shared_ptr< WProjectFileIO > >::const_iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
{
( *iter )->done();
try
{
( *iter )->done();
// append errors
std::copy( ( *iter )->getErrors().begin(), ( *iter )->getErrors().end(), std::back_inserter( errors ) );
}
catch( const std::exception& e )
{
errors.push_back( "Exception while applying settings: " + std::string( e.what() ) );
wlog::error( "Project Loader" ) << errors.back();
}
}
// give some feedback
m_signalLoadDone( m_project, errors );
m_signalLoadDoneConnection.disconnect();
// remove from thread list
WKernel::getRunningKernel()->getRootContainer()->finishedPendingThread( shared_from_this() );
}
......
......@@ -30,6 +30,8 @@
#include <boost/filesystem.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/signals2/signal.hpp>
#include "../common/WProjectFileIO.h"
......@@ -38,8 +40,8 @@
* Class loading project files. This class opens an file and reads it line by line. It delegates the actual parsing to each of the known
* WProjectFileIO instances which then do their job.
*/
class WProjectFile: public WThreadedRunner,
public boost::enable_shared_from_this< WProjectFile >
class WProjectFile: public WThreadedRunner,
public boost::enable_shared_from_this< WProjectFile >
{
public:
/**
......@@ -53,12 +55,30 @@ public:
typedef boost::shared_ptr< const WProjectFile > ConstSPtr;
/**
* Default constructor. It does NOT parse the file. Parsing is done by apply().
* A callback function type reporting bach a finished load job. The given string vector contains a list of errors if any.
*/
typedef boost::function< void( boost::filesystem::path, std::vector< std::string > ) > ProjectLoadCallback;
/**
* A callback function signal type reporting bach a finished load job. The given string vector contains a list of errors if any.
*/
typedef boost::signals2::signal< void( boost::filesystem::path, std::vector< std::string > ) > ProjectLoadCallbackSignal;
/**
* Default constructor. It does NOT parse the file. Parsing is done by using load.
*
* \param project the project file to load.
* \param project the project file to load or save.
*/
explicit WProjectFile( boost::filesystem::path project );
/**
* Default constructor. It does NOT parse the file. Parsing is done by using load.
*
* \param project the project file to load.
* \param doneCallback gets called whenever the load thread finishes.
*/
WProjectFile( boost::filesystem::path project, ProjectLoadCallback doneCallback );
/**
* Destructor.
*/
......@@ -126,8 +146,17 @@ protected:
* \param e the exception
*/
virtual void onThreadException( const WException& e );
private:
/**
* Signal used to callback someone that the loader was finished.
*/
ProjectLoadCallbackSignal m_signalLoadDone;
/**
* Connection managing the signal m_signalLoadDone. This is the one and only connection allowed and will be disconnected upon thread has
* finished.
*/
boost::signals2::connection m_signalLoadDoneConnection;
};
#endif // WPROJECTFILE_H
......
......@@ -92,12 +92,11 @@ bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumbe
// data modules are not allowed here
if( !proto )
{
wlog::error( "Project Loader" ) << "There is no prototype available for module \"" << matches[2] << "\". Skipping.";
addError( "There is no prototype available for module \"" + matches[2] + "\". Skipping." );
}
else if( proto->getType() == MODULE_DATA )
{
wlog::error( "Project Loader" ) << "Data modules are not allowed to be specified in a \"MODULE\" Statement." <<
" Use the \"DATA\" statement instead. Skipping.";
addError( "Data modules are not allowed to be specified in a \"MODULE\" Statement. Use the \"DATA\" statement instead. Skipping." );
}
else
{
......@@ -116,8 +115,7 @@ bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumbe
boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( "Data Module" );
if( !proto )
{
wlog::error( "Project Loader" ) << "There is no prototype available for module \"" << "Data Module" << "\"."
<< " This should not happen!. Skipping.";
addError( "There is no prototype available for module \"Data Module\". This should not happen!. Skipping." );
}
else
{
......@@ -125,7 +123,7 @@ bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumbe
boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
if( parameter.empty() )
{
wlog::error( "Project Loader" ) << "Data modules need an additional filename parameter. Skipping.";
addError( "Data modules need an additional filename parameter. Skipping." );
}
else
{
......@@ -184,10 +182,8 @@ void WModuleProjectFileCombiner::apply()
// if isReady now is false, the module has crashed before it got ready -> remove the module from the list
if( ( *iter ).second->isCrashed()() )
{
wlog::warn( "Project Loader" ) << "In the module with ID "
<< ( *iter ).first
<< " a problem occurred. Connections and properties relating to this"
<< " module will fail.";
addError( "In the module with ID " + ( *iter ).first +
std::string( " a problem occurred. Connections and properties relating to this module will fail." ) );
m_modules.erase( iter );
}
}
......@@ -198,8 +194,8 @@ void WModuleProjectFileCombiner::apply()
// grab corresponding module
if( !m_modules.count( ( *iter ).first.first ) )
{
wlog::error( "Project Loader" ) << "There is no module with ID \"" << ( *iter ).first.first << "\" to set the property \"" <<
( *iter ).first.second << "\" for. Skipping.";
addError( "There is no module with ID \"" + string_utils::toString( ( *iter ).first.first ) + "\" to set the property \"" +
( *iter ).first.second + std::string( "\" for. Skipping." ) );
continue;
}
boost::shared_ptr< WModule > m = m_modules[ ( *iter ).first.first ];
......@@ -208,8 +204,8 @@ void WModuleProjectFileCombiner::apply()
boost::shared_ptr< WPropertyBase > prop = m->getProperties()->findProperty( ( *iter ).first.second );
if( !prop )
{
wlog::error( "Project Loader" ) << "The module \"" << m->getName() << "\" has no property named \"" <<
( *iter ).first.second << "\". Skipping.";
addError( "The module \"" + m->getName() + std::string( "\" has no property named \"" ) + ( *iter ).first.second +
std::string( "\". Skipping." ) );
continue;
}
else
......@@ -220,14 +216,13 @@ void WModuleProjectFileCombiner::apply()
bool result = prop->setAsString( ( *iter ).second );
if( !result )
{
wlog::error( "Project Loader" ) << "Failed to set property " << ( *iter ).first.second << " in module \"" <<
m->getName() << "\".";
addError( "Failed to set property " + ( *iter ).first.second + " in module \"" + m->getName() + "\"." );
}
}
else
{
wlog::error( "Project Loader" ) << "The module \"" << m->getName() << "\" has a property named \"" <<
( *iter ).first.second << "\" which is an INFORMATION property. Skipping.";
addError( "The module \"" + m->getName() + "\" has a property named \"" +
( *iter ).first.second + "\" which is an INFORMATION property. Skipping." );
}
}
}
......@@ -244,9 +239,9 @@ void WModuleProjectFileCombiner::apply()
boost::shared_ptr< WModule > m1;
if( !m_modules.count( c1.first ) )
{
wlog::error( "Project Loader" ) << "There is no module with ID \"" << c1.first << "\" for the connection "
<< "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second << "). Skipping.";
addError( "There is no module with ID \"" + string_utils::toString( c1.first ) + "\" for the connection "
+ "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) + "," +
c2.second + "). Skipping." );
continue;
}
m1 = m_modules[ c1.first ];
......@@ -254,8 +249,9 @@ void WModuleProjectFileCombiner::apply()
boost::shared_ptr< WModule > m2;
if( !m_modules.count( c2.first ) )
{
wlog::error( "Project Loader" ) << "There is no module with ID \"" << c2.first << "\" for the connection "
<< "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second << "). Skipping.";
addError( "There is no module with ID \"" + string_utils::toString( c2.first ) + "\" for the connection "
+ "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) +
"," + c2.second + "). Skipping." );
continue;
}
......@@ -271,7 +267,7 @@ void WModuleProjectFileCombiner::apply()
}
catch( const WModuleConnectorNotFound& e )
{
wlog::error( "Project Loader" ) << "There is no output connector \"" << c1.second << "\" in module \"" << m1->getName() << "\"";
addError( "There is no output connector \"" + c1.second + "\" in module \"" + m1->getName() + "\"" );
continue;
}
boost::shared_ptr< WModuleInputConnector > con2;
......@@ -281,7 +277,7 @@ void WModuleProjectFileCombiner::apply()
}
catch( const WModuleConnectorNotFound& e )
{
wlog::error( "Project Loader" ) << "There is no input connector \"" << c2.second << "\" in module \"" << m2->getName() << "\"";
addError( "There is no input connector \"" + c2.second + "\" in module \"" + m2->getName() + "\"" );
continue;
}
......@@ -292,8 +288,9 @@ void WModuleProjectFileCombiner::apply()
}
catch( const WException& e )
{
wlog::error( "Project Loader" ) << "Connection " << "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second <<
") could not be created. Incompatible connectors?. Skipping.";
addError( "Connection (" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +
string_utils::toString( c2.first ) + "," + c2.second +
") could not be created. Incompatible connectors?. Skipping." );
continue;
}
}
......
......@@ -44,7 +44,7 @@
* WProjectFileIO to allow WProjectFile to fill this combiner.
*/
class WModuleProjectFileCombiner: public WModuleCombiner,
public WProjectFileIO
public WProjectFileIO
{
public:
/**
......
......@@ -122,7 +122,7 @@ void WMEEGView::properties()
m_propCondition );
m_proofOfConcept = m_properties->addProperty( "Enable POC",
"Use proof of concept (POC) ROI positioning instead of real dipoles position.",
true,
false,
m_propCondition );
m_butterfly = m_properties->addProperty( "Butterfly plot",
"Overlay all curves in one row.",
......@@ -302,10 +302,11 @@ void WMEEGView::moduleMain()
{
debugLog() << "New event position: " << event->getTime();
if( m_roi )
for( std::vector< osg::ref_ptr< WROIBox > >::iterator iter = m_rois.begin(); iter != m_rois.end(); ++iter)
{
WKernel::getRunningKernel()->getRoiManager()->removeRoi( m_roi );
WKernel::getRunningKernel()->getRoiManager()->removeRoi( *iter );
}
m_rois.clear();
if( m_sourceCalculator )
{
......@@ -313,19 +314,25 @@ void WMEEGView::moduleMain()
{
WPosition position = m_sourceCalculator->calculate( event );
float halfWidth = m_ROIsize->get( true ) * 0.5;
m_roi = new WROIBox( position - WVector3d( halfWidth, halfWidth, halfWidth ),
position + WVector3d( halfWidth, halfWidth, halfWidth ) );
WKernel::getRunningKernel()->getRoiManager()->addRoi( m_roi );
m_rois.push_back( new WROIBox( position - WVector3d( halfWidth, halfWidth, halfWidth ),
position + WVector3d( halfWidth, halfWidth, halfWidth ) ) );
WKernel::getRunningKernel()->getRoiManager()->addRoi( m_rois.back() );
}
else if( m_dipoles->getData() )
{
if( m_dipoles->getData()->getMagnitude( event->getTime() ) != 0 )
boost::shared_ptr< WDataSetDipoles > dipoles = m_dipoles->getData();
debugLog() << "Number of Dipoles: " << dipoles->getNumberOfDipoles();
for( size_t dipoleId = 0; dipoleId < dipoles->getNumberOfDipoles(); ++dipoleId )
{
float halfWidth = m_ROIsize->get( true ) * 0.5;
WPosition position = m_dipoles->getData()->getPosition();
m_roi = new WROIBox( position - WVector3d( halfWidth, halfWidth, halfWidth ),
position + WVector3d( halfWidth, halfWidth, halfWidth ) );
WKernel::getRunningKernel()->getRoiManager()->addRoi( m_roi );
debugLog() << "Dipole[" << dipoleId << "]: " << dipoles->getMagnitude( event->getTime(), dipoleId );
if( dipoles->getMagnitude( event->getTime(), dipoleId ) != 0 )
{
float halfWidth = m_ROIsize->get( true ) * 0.5;
WPosition position = dipoles->getPosition( dipoleId );
m_rois.push_back( new WROIBox( position - WVector3d( halfWidth, halfWidth, halfWidth ),
position + WVector3d( halfWidth, halfWidth, halfWidth ) ) );
WKernel::getRunningKernel()->getRoiManager()->addRoi( m_rois.back() );
}
}
}
else
......@@ -333,10 +340,6 @@ void WMEEGView::moduleMain()
debugLog() << "No dipoles found and not in POC mode: placing NO ROI.";
}
}
else
{
m_roi.release();
}
m_currentEventTime = event->getTime();
}
......
......@@ -26,6 +26,7 @@
#define WMEEGVIEW_H
#include <string>
#include <vector>
#include <osg/Texture1D>
#include <osgSim/ScalarsToColors>
......@@ -266,11 +267,11 @@ private:
osg::ref_ptr< osg::Node > m_labelsNode;
/**
* The ROI around the source dipole position at the time determined by
* The ROIs around the source dipole positions at the time determined by
* m_event.
* Used to select the fibers around this dipole.
* Used to select the fibers around this dipoles.
*/
osg::ref_ptr< WROIBox > m_roi;
std::vector< osg::ref_ptr< WROIBox > > m_rois;
/**
* Bool flag which gets set when the data was changed.
......
......@@ -218,7 +218,7 @@ boost::shared_ptr< WDataSetDipoles > WMReadDipoles::readFiles( std::vector< std:
for( size_t fileId = 1; fileId < filenames.size(); ++fileId )
{
readFile( filenames[fileId], &pos, &times, &magnitudes );
loadedData->addDipole( pos, times, magnitudes );
loadedData->addDipole( pos, magnitudes, times );
}
return loadedData;
......
......@@ -45,6 +45,7 @@
#include <QtGui/QVBoxLayout>
#include <QtGui/QWidget>
#include <QtCore/QSettings>
#include <QtCore/QUrl>
#include <QtGui/QInputDialog>
#ifndef QT4GUI_NOWEBKIT
......@@ -79,6 +80,7 @@
#include "events/WModuleRemovedEvent.h"
#include "events/WOpenCustomDockWidgetEvent.h"
#include "events/WCloseCustomDockWidgetEvent.h"
#include "events/WLoadFinishedEvent.h"
#include "guiElements/WQtPropertyBoolAction.h"
#include "WQt4Gui.h"
#include "WQtCombinerToolbar.h"
......@@ -689,9 +691,7 @@ void WMainWindow::openLoadDialog()
// is this a project file?
if( ( suffix == ".owp" ) || ( suffix == ".owproj" ) )
{
WProjectFile::SPtr proj( new WProjectFile( fn.string() ) );
// This call is asynchronous. It parses the file and the starts a thread to actually do all the stuff
proj->load();
asyncProjectLoad( fn.string() );
}
else
{
......@@ -703,6 +703,23 @@ void WMainWindow::openLoadDialog()
m_loaderSignal( loadDataFilenames );
}
void WMainWindow::asyncProjectLoad( std::string filename )
{
WProjectFile::SPtr proj( new WProjectFile( filename, boost::bind( &WMainWindow::slotLoadFinished, this, _1, _2 ) ) );
proj->load();
}
void WMainWindow::slotLoadFinished( boost::filesystem::path file, std::vector< std::string > errors )
{
// as this function might be called from outside the gui thread, use an event:
QCoreApplication::postEvent( this, new WLoadFinishedEvent( file, errors ) );
if( errors.size() )
{
wlog::warn( "MainWindow" ) << "Async load error occurred. Informing user.";
}
}
void WMainWindow::openAboutQtDialog()
{
QMessageBox::aboutQt( this, "About Qt" );
......@@ -963,6 +980,40 @@ bool WMainWindow::event( QEvent* event )
}
}
if( event->type() == WQT_LOADFINISHED )
{
// convert event
WLoadFinishedEvent* e1 = dynamic_cast< WLoadFinishedEvent* >( event );