Commit 473d26bd authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[FIX #347] fixed a lot os small bugs and circumvented even more problems in QT...

[FIX #347] fixed a lot os small bugs and circumvented even more problems in QT and OSG to properly delete WQtGLWidgets.
parent db50e0e8
......@@ -24,6 +24,8 @@
#include <iostream>
#include "core/common/WLogger.h"
#include "WGEGraphicsWindow.h"
#include "exceptions/WGEInitFailed.h"
......@@ -105,17 +107,30 @@ void WGEGraphicsWindow::createContext( int x, int y, int width, int height )
void WGEGraphicsWindow::resize( int width, int height )
{
if( !m_GraphicsWindow )
{
return;
}
m_GraphicsWindow->getEventQueue()->windowResize( 0, 0, width, height );
m_GraphicsWindow->resized( 0, 0, width, height );
}
void WGEGraphicsWindow::close()
{
m_GraphicsWindow->getEventQueue()->closeWindow();
setClosed( true );
// this causes segfaults ...
// m_GraphicsWindow->getEventQueue()->closeWindow();
}
void WGEGraphicsWindow::keyEvent( KeyEvents eventType, int key )
{
if( !m_GraphicsWindow )
{
return;
}
switch( eventType )
{
case KEYPRESS:
......@@ -129,6 +144,10 @@ void WGEGraphicsWindow::keyEvent( KeyEvents eventType, int key )
void WGEGraphicsWindow::mouseEvent( MouseEvents eventType, int x, int y, int button )
{
if( !m_GraphicsWindow )
{
return;
}
switch( eventType )
{
case MOUSEPRESS:
......
......@@ -253,6 +253,9 @@ void WGEViewer::resize( int width, int height )
void WGEViewer::close()
{
// delete/unset all the objects we sponsored a "shared_from_this" pointer to ensure the viewer gets deleted after close
m_effectImageOverlay->setReferenceViewer( WGEViewer::SPtr() );
// forward close event
WGEGraphicsWindow::close();
}
......
......@@ -66,9 +66,13 @@ WGraphicsEngine::WGraphicsEngine():
m_viewer->setThreadingModel( osgViewer::ViewerBase::SingleThreaded );
#endif
m_viewer->setRunMaxFrameRate( 60.0 );
// initialize members
m_rootNode = new WGEScene();
m_viewersUpdate = false;
setThreadName( "WGE" );
}
......@@ -139,43 +143,99 @@ boost::shared_ptr<WGEViewer> WGraphicsEngine::createViewer( std::string name, os
viewer->setBgColor( bgColor );
viewer->setScene( getScene() );
#ifdef WGEMODE_MULTITHREADED
// finally add view
m_viewer->addView( viewer->getView() );
#endif
// store it in viewer list
boost::mutex::scoped_lock lock( m_viewersLock );
boost::unique_lock< boost::shared_mutex > lock( m_viewersLock );
bool insertSucceeded = m_viewers.insert( make_pair( name, viewer ) ).second;
assert( insertSucceeded == true ); // if false, viewer with same name already exists
#ifdef WGEMODE_MULTITHREADED
WCondition::SPtr viewerUpdateCondition( new WConditionOneShot() );
// finally add view (in the GE thread)
m_addViewers.push_back( viewer );
m_viewersUpdate = true;
#else
m_viewer->addView( viewer->getView() );
#endif
lock.unlock();
return viewer;
}
void WGraphicsEngine::closeViewer( boost::shared_ptr< WGEViewer > viewer )
{
boost::unique_lock< boost::shared_mutex > lock( m_viewersLock );
// close and erase
viewer->close();
if( m_viewers.count( viewer->getName() ) > 0 )
{
m_viewers.erase( viewer->getName() );
}
#ifdef WGEMODE_MULTITHREADED
WCondition::SPtr viewerUpdateCondition( new WConditionOneShot() );
m_viewerUpdateNotifiers.push_back( viewerUpdateCondition );
// finally remove view (in the GE thread)
m_removeViewers.push_back( viewer );
m_viewersUpdate = true;
#else
m_viewer->removeView( viewer->getView() );
#endif
lock.unlock();
#ifdef WGEMODE_MULTITHREADED
// wait until the GE thread processed our request.
viewerUpdateCondition->wait();
#endif
}
void WGraphicsEngine::closeViewer( const std::string name )
{
boost::mutex::scoped_lock lock( m_viewersLock );
boost::unique_lock< boost::shared_mutex > lock( m_viewersLock );
if( m_viewers.count( name ) > 0 )
{
m_viewers[name]->close();
m_viewers.erase( name );
#ifdef WGEMODE_MULTITHREADED
WCondition::SPtr viewerUpdateCondition( new WConditionOneShot() );
m_viewerUpdateNotifiers.push_back( viewerUpdateCondition );
// finally remove view (in the GE thread)
m_removeViewers.push_back( m_viewers[name] );
m_viewersUpdate = true;
#else
m_viewer->removeView( m_viewers[name]->getView() );
#endif
lock.unlock();
#ifdef WGEMODE_MULTITHREADED
// wait until the GE thread processed our request.
viewerUpdateCondition->wait();
#endif
}
else
{
lock.unlock();
}
}
boost::shared_ptr< WGEViewer > WGraphicsEngine::getViewerByName( std::string name )
{
boost::mutex::scoped_lock lock( m_viewersLock );
boost::shared_lock< boost::shared_mutex > lock( m_viewersLock );
boost::shared_ptr< WGEViewer > out = m_viewers.count( name ) > 0 ?
m_viewers[name] :
boost::shared_ptr< WGEViewer >();
lock.unlock();
return out;
}
boost::shared_ptr< WGEViewer > WGraphicsEngine::getViewer()
{
boost::mutex::scoped_lock lock( m_viewersLock );
return m_viewers[ "Main View" ];
boost::shared_lock< boost::shared_mutex > lock( m_viewersLock );
boost::shared_ptr< WGEViewer > result = m_viewers[ "Main View" ];
lock.unlock();
return result;
}
bool WGraphicsEngine::isRunning()
......@@ -205,6 +265,36 @@ bool WGraphicsEngine::waitForStartupComplete()
return isRunning();
}
void WGraphicsEngine::applyViewerListUpdates()
{
// any new or removed views?
if( m_viewersUpdate )
{
boost::unique_lock< boost::shared_mutex > lock( m_viewersLock );
// add all views
for( std::vector< WGEViewer::SPtr >::iterator it = m_addViewers.begin(); it != m_addViewers.end(); ++it )
{
m_viewer->addView( ( *it )->getView() );
}
m_addViewers.clear();
for( std::vector< WGEViewer::SPtr >::iterator it = m_removeViewers.begin(); it != m_removeViewers.end(); ++it )
{
m_viewer->removeView( ( *it )->getView() );
}
m_removeViewers.clear();
m_viewersUpdate = false;
lock.unlock();
// notify all of them
for( std::vector< WCondition::SPtr >::iterator it = m_viewerUpdateNotifiers.begin(); it != m_viewerUpdateNotifiers.end(); ++it )
{
( *it )->notify();
}
m_viewerUpdateNotifiers.clear();
}
}
void WGraphicsEngine::threadMain()
{
WLogger::getLogger()->addLogMessage( "Starting Graphics Engine", "GE", LL_INFO );
......@@ -217,7 +307,20 @@ void WGraphicsEngine::threadMain()
m_startThreadingCondition.wait();
m_running = true;
m_viewer->startThreading();
m_viewer->run();
// do this before calling realize to ensure the initial views where added
applyViewerListUpdates();
if( !m_viewer->isRealized() )
{
m_viewer->realize();
}
while( !m_viewer->done() )
{
// added or removed views?
applyViewerListUpdates();
m_viewer->frame();
}
m_viewer->stopThreading();
m_running = false;
#else
......
......@@ -27,6 +27,7 @@
#include <map>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/signals2/signal.hpp>
......@@ -42,6 +43,7 @@
#include "../common/WThreadedRunner.h"
#include "../common/WConditionOneShot.h"
#include "../common/WColor.h"
#include "../common/WCondition.h"
#include "WGEGraphicsWindow.h"
#include "WGEScene.h"
#include "WGEViewer.h"
......@@ -92,6 +94,13 @@ public:
*/
void closeViewer( const std::string name );
/**
* Closes a viewer and deletes it from the list of viewers.
*
* \param viewer the viewer
*/
void closeViewer( boost::shared_ptr< WGEViewer > viewer );
/**
* Searches for a viewer with a given name and returns it, if found.
*
......@@ -188,15 +197,41 @@ protected:
*/
osg::ref_ptr<WGEScene> m_rootNode;
/**
* Map between name of viewer and viewer
*/
typedef std::map< std::string, boost::shared_ptr< WGEViewer > > ViewerMap;
/**
* All registered viewers.
*/
std::map< std::string, boost::shared_ptr< WGEViewer > > m_viewers;
ViewerMap m_viewers;
/**
* Mutex used to lock the map of viewers.
*/
boost::mutex m_viewersLock;
boost::shared_mutex m_viewersLock;
/**
* If true, the view thread checks for updates in the m_viewers list
*/
bool m_viewersUpdate;
/**
* List of viewers to add to m_viewer via addView. Protected by m_viewersLock.
*/
std::vector< WGEViewer::SPtr > m_addViewers;
/**
* List of viewers to remove from m_viewer via addView. Protected by m_viewersLock.
*/
std::vector< WGEViewer::SPtr > m_removeViewers;
/**
* A list of conditions to notify when the GE thread processed the m_addViewers and m_removeViewers lists. Protected by
* m_viewersLock.
*/
std::vector< WCondition::SPtr > m_viewerUpdateNotifiers;
/**
* OpenSceneGraph composite viewer. Contains all created osgViewer::Views.
......@@ -223,6 +258,11 @@ private:
* This condition is fired externally if all the GUI startup is done to ensure all OGL stuff is initialized prior to OSG threading startup.
*/
WConditionOneShot m_startThreadingCondition;
/**
* Apply updates in m_addViewers and m_removeViewers. Needs to be run in the GE thread
*/
void applyViewerListUpdates();
};
/**
......
......@@ -24,10 +24,14 @@
#include <string>
#include "core/common/WConditionOneShot.h"
#include "core/common/WLogger.h"
#include "WUIWidgetBase.h"
WUIWidgetBase::WUIWidgetBase( std::string title ):
m_title( title )
m_title( title ),
m_closeCondition( new WConditionOneShot() )
{
// initialize members
}
......@@ -42,3 +46,12 @@ std::string WUIWidgetBase::getTitle() const
return m_title;
}
void WUIWidgetBase::onClose()
{
m_closeCondition->notify();
}
WCondition::SPtr WUIWidgetBase::getCloseCondition() const
{
return m_closeCondition;
}
......@@ -27,6 +27,8 @@
#include <string>
#include "core/common/WCondition.h"
#include <boost/shared_ptr.hpp>
/**
......@@ -78,9 +80,17 @@ public:
virtual bool isVisible() const = 0;
/**
* Close the widget. When done, the widget can be safely deleted.
* Close the widget. When done, the widget can be safely deleted. You cannot re-open the widget with \ref show(). If you want to hide widget,
* use setVisible( false ) instead.
*/
virtual void close() = 0;
/**
* Return the condition that fires when the widgets closes.
*
* \return the condition fired whenever the widget closes
*/
WCondition::SPtr getCloseCondition() const;
protected:
/**
* Default constructor.
......@@ -88,11 +98,21 @@ protected:
* \param title the title of the widget
*/
explicit WUIWidgetBase( std::string title );
/**
* Called directly before closing the widget.
*/
virtual void onClose();
private:
/**
* The widget's title string.
*/
std::string m_title;
/**
* Close condition. Notified when widget closes.
*/
WCondition::SPtr m_closeCondition;
};
#endif // WUIWIDGETBASE_H
......
......@@ -218,6 +218,7 @@ void WMHistogramView::moduleMain()
eh->subscribeMove( boost::bind( &WMHistogramView::handleMouseMove, this, _1 ) );
eh->subscribeResize( boost::bind( &WMHistogramView::handleResize, this, _1, _2, _3, _4 ) );
m_widget->addEventHandler( eh );
m_widget->show();
if( m_widget )
{
......@@ -298,7 +299,10 @@ void WMHistogramView::moduleMain()
debugLog() << "Shutting down...";
// clear main node, just in case
m_mainNode->clear();
if( m_mainNode )
{
m_mainNode->clear();
}
m_widget->close();
......
......@@ -76,13 +76,14 @@
#include "core/kernel/WROIManager.h"
#include "core/kernel/WSelectionManager.h"
#include "events/WEventTypes.h"
#include "events/WDeferredCallEvent.h"
#include "events/WModuleCrashEvent.h"
#include "events/WModuleReadyEvent.h"
#include "events/WModuleRemovedEvent.h"
#include "events/WDeferredCallEvent.h"
#include "events/WLoadFinishedEvent.h"
#include "events/WLogEvent.h"
#include "guiElements/WQtPropertyBoolAction.h"
#include "abstractUI/WQtWidgetBase.h"
#include "WQtMessagePopup.h"
#include "WQt4Gui.h"
#include "WQtCombinerToolbar.h"
......@@ -258,7 +259,7 @@ void WMainWindow::setupGUI()
m_mainGLDock->restoreSettings();
m_mainGLWidget = m_mainGLDock->getGLWidget();
m_glDock->addDockWidget( Qt::RightDockWidgetArea, m_mainGLDock );
connect( m_mainGLWidget.get(), SIGNAL( renderedFirstFrame() ), this, SLOT( handleGLVendor() ) );
connect( m_mainGLWidget, SIGNAL( renderedFirstFrame() ), this, SLOT( handleGLVendor() ) );
addDockWidget( Qt::RightDockWidgetArea, m_controlPanel );
......@@ -273,8 +274,8 @@ void WMainWindow::setupGUI()
}
// NOTE: we abuse the gl widgets first frame event to handle startup news.
connect( m_mainGLWidget.get(), SIGNAL( renderedFirstFrame() ), this, SLOT( handleStartMessages() ) );
connect( m_mainGLWidget.get(), SIGNAL( renderedFirstFrame() ), this, SLOT( closeSplash() ) );
connect( m_mainGLWidget, SIGNAL( renderedFirstFrame() ), this, SLOT( handleStartMessages() ) );
connect( m_mainGLWidget, SIGNAL( renderedFirstFrame() ), this, SLOT( closeSplash() ) );
m_permanentToolBar = new WQtToolBar( "Standard Toolbar", this );
addToolBar( Qt::TopToolBarArea, m_permanentToolBar );
......@@ -376,7 +377,7 @@ void WMainWindow::setupGUI()
if( showNavWidgets->get() )
{
m_navAxial = boost::shared_ptr< WQtNavGLWidget >( new WQtNavGLWidget( "Axial View", "Axial View", this, "Axial Slice",
m_mainGLWidget.get() ) );
m_mainGLWidget ) );
m_navAxial->setFeatures( QDockWidget::AllDockWidgetFeatures );
m_navAxial->setSliderProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropAxialPos() );
m_navAxial->getGLWidget()->setCameraManipulator( WQtGLWidget::NO_OP );
......@@ -384,7 +385,7 @@ void WMainWindow::setupGUI()
m_glDock->addDockWidget( Qt::LeftDockWidgetArea, m_navAxial.get() );
m_navCoronal = boost::shared_ptr< WQtNavGLWidget >( new WQtNavGLWidget( "Coronal View", "Coronal View", this, "Coronal Slice",
m_mainGLWidget.get() ) );
m_mainGLWidget ) );
m_navCoronal->setFeatures( QDockWidget::AllDockWidgetFeatures );
m_navCoronal->setSliderProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropCoronalPos() );
m_navCoronal->getGLWidget()->setCameraManipulator( WQtGLWidget::NO_OP );
......@@ -393,7 +394,7 @@ void WMainWindow::setupGUI()
m_navSagittal =
boost::shared_ptr< WQtNavGLWidget >( new WQtNavGLWidget( "Sagittal View", "Sagittal View", this, "Sagittal Slice",
m_mainGLWidget.get() ) );
m_mainGLWidget ) );
m_navSagittal->setFeatures( QDockWidget::AllDockWidgetFeatures );
m_navSagittal->setSliderProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropSagittalPos() );
m_navSagittal->getGLWidget()->setCameraManipulator( WQtGLWidget::NO_OP );
......@@ -793,11 +794,17 @@ void WMainWindow::closeEvent( QCloseEvent* e )
saveWindowState();
// close all registered custom widgets
for( CustomWidgets::iterator it = m_customWidgets.begin(); it != m_customWidgets.end(); ++it )
{
( *it )->guiShutDown();
}
// signal everybody to shut down properly.
m_splash->showMessage( "Shutting down kernel. Waiting for modules to finish." );
WKernel::getRunningKernel()->finalize();
// now nobody acesses the osg anymore
// now nobody accesses the osg anymore
m_splash->showMessage( "Shutting down GUI." );
// clean up gl widgets
......@@ -1275,10 +1282,19 @@ WQtMessageDock* WMainWindow::getMessageDock()
return m_messageDock;
}
void WMainWindow::execInGUIThread( boost::function< void( void ) > function )
void WMainWindow::registerCustomWidget( WQtWidgetBase* widget )
{
CustomWidgets::iterator it = std::find( m_customWidgets.begin(), m_customWidgets.end(), widget );
if( it == m_customWidgets.end() )
{
m_customWidgets.push_back( widget );
}
}
void WMainWindow::deregisterCustomWidget( WQtWidgetBase* widget )
{
WDeferredCallEvent* ev = new WDeferredCallEvent( function );
QCoreApplication::postEvent( this, ev );
ev->wait();
// remove
CustomWidgets::iterator newEnd = std::remove( m_customWidgets.begin(), m_customWidgets.end(), widget );
m_customWidgets.erase( newEnd, m_customWidgets.end() );
}
......@@ -56,6 +56,7 @@ class WQtPropertyBoolAction;
class WPropertyBase;
class WQtControlPanel;
class WQtGLScreenCapture;
class WQtWidgetBase;
/**
* This class contains the main window and the layout of the widgets within the window.
......@@ -234,11 +235,20 @@ public:
WQtMessageDock* getMessageDock();
/**
* Call a given function from within the GUI thread.
* Register a custom widget. This is important as the main window needs to manage the close/delete of these widgets. Only call from withing
* the GUI thread.
*
* \param function the function to call
* \param widget the widget.
*/
void execInGUIThread( boost::function< void( void ) > function );
void registerCustomWidget( WQtWidgetBase* widget );
/**
* De-register a custom widget.
*
* \param widget the widget.
*/
void deregisterCustomWidget( WQtWidgetBase* widget );
protected:
/**
* Setup the GUI by handling special modules. NavSlices for example setup several toolbar buttons.
......@@ -416,7 +426,7 @@ private:
WQtNetworkEditor* m_networkEditor; //!< network editor
boost::shared_ptr< WQtGLWidget > m_mainGLWidget; //!< the main GL widget of the GUI
WQtGLWidget* m_mainGLWidget; //!< the main GL widget of the GUI
boost::shared_ptr< WQtNavGLWidget > m_navAxial; //!< the axial view widget GL widget of the GUI
boost::shared_ptr< WQtNavGLWidget > m_navCoronal; //!< the coronal view widget GL widget of the GUI
......@@ -426,14 +436,14 @@ private:
WQtGLDockWidget* m_mainGLDock; //!< the dock containing the main gl widget
/**
* All registered WQtCustomDockWidgets.
* Container for core/UI widgetd
*/
std::map< std::string, boost::shared_ptr< WQtCustomDockWidget > > m_customDockWidgets;
typedef std::vector< WQtWidgetBase* > CustomWidgets;
/**
* Mutex used to lock the map of WQtCustomDockWidgets.
* All registered widgets created by the core/UI api.
*/
boost::mutex m_customDockWidgetsLock;
CustomWidgets m_customWidgets;
boost::signals2::signal1< void, std::vector< std::string > > m_loaderSignal; //!< boost signal for open file dialog
......
......@@ -50,6 +50,7 @@
#include "core/kernel/WProjectFile.h"
#include "core/kernel/WROIManager.h"
#include "controlPanel/WQtControlPanel.h"
#include "events/WDeferredCallEvent.h"
#include "events/WModuleAssocEvent.h"
#include "events/WModuleConnectEvent.h"
#include "events/WModuleCrashEvent.h"
......@@ -419,3 +420,22 @@ WUIWidgetFactory::SPtr WQt4Gui::getWidgetFactory() const
{
return m_widgetFactory;
}
void WQt4Gui::execInGUIThread( boost::function< void( void ) > function, WCondition::SPtr notify )
{
if( !notify )
{
// the user did not specify a condition. We create our own
notify = WCondition::SPtr( new WConditionOneShot() );
}
WDeferredCallEvent* ev = new WDeferredCallEvent( function, notify );
QCoreApplication::postEvent( getMainWindow(), ev );