From 6d046e9fa13a90e0ad191dafdad49d6082703d60 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 30 Apr 2010 12:22:31 +0000 Subject: [PATCH] From Wang Rui, "I've just made another OSG+Qt (GUI) examples to demonstrate how to create OSG views, add them to a CompositeViewer, and add corresponding widgets to Qt layouts or as popup windows. The example inherits a GraphicsWindowQt from the GraphicsWindow base class and implements most of the virtual methods. A QGLWidget is created at the same time to perform keyboards and mouse events, who is also added as the main widget's child. The new example, named osgviewerQtContext (because of deriving from GraphicsContext), works fine on Windows XP SP3 and Qt 4.5.0, with 4 widgets in QGridLayout and a popup window and 60Hz frame rate. I haven't tested it on Unix/Linux and Mac OSX yet. So any feedback from these platforms is appreciated. I wish this example be a useful complement to current osgviewerQt and osgviewerQtWidgets ones. :) Some unfinished functionalities: inheritedWindowData, sharedContext, and more tests needed." --- examples/CMakeLists.txt | 1 + examples/osgviewerQtContext/CMakeLists.txt | 15 + examples/osgviewerQtContext/GraphicsWindowQt | 90 +++++ .../osgviewerQtContext/GraphicsWindowQt.cpp | 342 ++++++++++++++++++ .../osgviewerQtContext/osgviewerQtContext.cpp | 92 +++++ 5 files changed, 540 insertions(+) create mode 100644 examples/osgviewerQtContext/CMakeLists.txt create mode 100644 examples/osgviewerQtContext/GraphicsWindowQt create mode 100644 examples/osgviewerQtContext/GraphicsWindowQt.cpp create mode 100644 examples/osgviewerQtContext/osgviewerQtContext.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 20aedbf23..2ea9125b5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -189,6 +189,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) IF (BUILD_QT_EXAMPLES AND QT_QTOPENGL_LIBRARY) ADD_SUBDIRECTORY(osgviewerQT) + ADD_SUBDIRECTORY(osgviewerQtContext) ENDIF() IF (BUILD_QT_EXAMPLES AND QT4_FOUND) diff --git a/examples/osgviewerQtContext/CMakeLists.txt b/examples/osgviewerQtContext/CMakeLists.txt new file mode 100644 index 000000000..80aba84e4 --- /dev/null +++ b/examples/osgviewerQtContext/CMakeLists.txt @@ -0,0 +1,15 @@ +SET( TARGET_SRC + GraphicsWindowQt + GraphicsWindowQt.cpp + osgviewerQtContext.cpp +) + +IF( QT4_FOUND ) + SET(TARGET_EXTERNAL_LIBRARIES ${QT_QTCORE_LIBRARY_RELEASE} ${QT_QTGUI_LIBRARY_RELEASE} ${QT_QTOPENGL_LIBRARY_RELEASE} ) + ADD_DEFINITIONS(-DUSE_QT4) +ELSE( QT4_FOUND ) + SET(TARGET_EXTERNAL_LIBRARIES ${QT_LIBRARIES} ) +ENDIF( QT4_FOUND ) + +INCLUDE_DIRECTORIES( ${QT_INCLUDE_DIR} ) +SETUP_EXAMPLE( osgviewerQtContext ) diff --git a/examples/osgviewerQtContext/GraphicsWindowQt b/examples/osgviewerQtContext/GraphicsWindowQt new file mode 100644 index 000000000..951dfd9af --- /dev/null +++ b/examples/osgviewerQtContext/GraphicsWindowQt @@ -0,0 +1,90 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 2009 Wang Rui + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGVIEWER_GRAPHICSWINDOWQT +#define OSGVIEWER_GRAPHICSWINDOWQT + +#include +#include +#include +#include + +class GraphWidget : public QGLWidget +{ +public: + GraphWidget( const QGLFormat& format, QWidget* parent=0, const QGLWidget* shareWidget=0, Qt::WindowFlags f=0 ); + + inline void setGraphicsWindow( osgViewer::GraphicsWindow* gw ) { _gw = gw; } + + void setKeyboardModifiers( QInputEvent* event ); + + virtual void resizeEvent( QResizeEvent* event ); + virtual void keyPressEvent( QKeyEvent* event ); + virtual void keyReleaseEvent( QKeyEvent* event ); + virtual void mousePressEvent( QMouseEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + virtual void mouseDoubleClickEvent( QMouseEvent* event ); + virtual void mouseMoveEvent( QMouseEvent* event ); + virtual void wheelEvent( QWheelEvent* event ); + +protected: + osgViewer::GraphicsWindow* _gw; +}; + +class GraphicsWindowQt : public osgViewer::GraphicsWindow +{ +public: + GraphicsWindowQt( osg::GraphicsContext::Traits* traits ); + virtual ~GraphicsWindowQt(); + + inline GraphWidget* getGraphWidget() { return _widget; } + inline const GraphWidget* getGraphWidget() const { return _widget; } + + struct WindowData : public osg::Referenced + { + WindowData( GraphWidget* widget ): _widget(widget) {} + GraphWidget* _widget; + }; + + bool init(); + + virtual bool setWindowRectangleImplementation( int x, int y, int width, int height ); + virtual void getWindowRectangle( int& x, int& y, int& width, int& height ); + virtual bool setWindowDecorationImplementation( bool windowDecoration ); + virtual bool getWindowDecoration() const; + virtual void grabFocus(); + virtual void grabFocusIfPointerInWindow(); + virtual void raiseWindow(); + virtual void setWindowName( const std::string& name ); + virtual std::string getWindowName(); + virtual void useCursor( bool cursorOn ); + virtual void setCursor( MouseCursor cursor ); + + virtual bool valid() const; + virtual bool realizeImplementation(); + virtual bool isRealizedImplementation() const; + virtual void closeImplementation(); + virtual bool makeCurrentImplementation(); + virtual bool releaseContextImplementation(); + virtual void swapBuffersImplementation(); + + virtual void requestWarpPointer( float x, float y ); + +protected: + GraphWidget* _widget; + QCursor _currentCursor; + bool _initialized; + bool _realized; +}; + +#endif diff --git a/examples/osgviewerQtContext/GraphicsWindowQt.cpp b/examples/osgviewerQtContext/GraphicsWindowQt.cpp new file mode 100644 index 000000000..1aa96d3c3 --- /dev/null +++ b/examples/osgviewerQtContext/GraphicsWindowQt.cpp @@ -0,0 +1,342 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 2009 Wang Rui + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include "GraphicsWindowQt" + +GraphWidget::GraphWidget( const QGLFormat& format, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f ) +: QGLWidget(format, parent, shareWidget, f) +{ + setAutoBufferSwap( false ); + setMouseTracking( true ); +} + +void GraphWidget::setKeyboardModifiers( QInputEvent* event ) +{ + int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier); + unsigned int mask = 0; + if ( modkey & Qt::ShiftModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT; + if ( modkey & Qt::ControlModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL; + if ( modkey & Qt::AltModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_ALT; + _gw->getEventQueue()->getCurrentEventState()->setModKeyMask( mask ); +} + +void GraphWidget::resizeEvent( QResizeEvent* event ) +{ + const QSize& size = event->size(); + _gw->getEventQueue()->windowResize( 0, 0, size.width(), size.height() ); + _gw->resized( 0, 0, size.width(), size.height() ); +} + +void GraphWidget::keyPressEvent( QKeyEvent* event ) +{ + setKeyboardModifiers( event ); + _gw->getEventQueue()->keyPress( (osgGA::GUIEventAdapter::KeySymbol) *(event->text().toAscii().data()) ); +} + +void GraphWidget::keyReleaseEvent( QKeyEvent* event ) +{ + setKeyboardModifiers( event ); + _gw->getEventQueue()->keyRelease( (osgGA::GUIEventAdapter::KeySymbol) *(event->text().toAscii().data()) ); +} + +void GraphWidget::mousePressEvent( QMouseEvent* event ) +{ + int button = 0; + switch ( event->button() ) + { + case Qt::LeftButton: button = 1; break; + case Qt::MidButton: button = 2; break; + case Qt::RightButton: button = 3; break; + case Qt::NoButton: button = 0; break; + default: button = 0; break; + } + setKeyboardModifiers( event ); + _gw->getEventQueue()->mouseButtonPress( event->x(), event->y(), button ); +} + +void GraphWidget::mouseReleaseEvent( QMouseEvent* event ) +{ + int button = 0; + switch ( event->button() ) + { + case Qt::LeftButton: button = 1; break; + case Qt::MidButton: button = 2; break; + case Qt::RightButton: button = 3; break; + case Qt::NoButton: button = 0; break; + default: button = 0; break; + } + setKeyboardModifiers( event ); + _gw->getEventQueue()->mouseButtonRelease( event->x(), event->y(), button ); +} + +void GraphWidget::mouseDoubleClickEvent( QMouseEvent* event ) +{ + int button = 0; + switch ( event->button() ) + { + case Qt::LeftButton: button = 1; break; + case Qt::MidButton: button = 2; break; + case Qt::RightButton: button = 3; break; + case Qt::NoButton: button = 0; break; + default: button = 0; break; + } + setKeyboardModifiers( event ); + _gw->getEventQueue()->mouseDoubleButtonPress( event->x(), event->y(), button ); +} + +void GraphWidget::mouseMoveEvent( QMouseEvent* event ) +{ + setKeyboardModifiers( event ); + _gw->getEventQueue()->mouseMotion( event->x(), event->y() ); +} + +void GraphWidget::wheelEvent( QWheelEvent* event ) +{ + setKeyboardModifiers( event ); + _gw->getEventQueue()->mouseScroll( + event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN ); +} + +GraphicsWindowQt::GraphicsWindowQt( osg::GraphicsContext::Traits* traits ) +: _widget(0), + _initialized(false), + _realized(false) +{ + _traits = traits; + _initialized = init(); + + if ( valid() ) + { + setState( new osg::State ); + getState()->setGraphicsContext(this); + + if ( _traits.valid() && _traits->sharedContext ) + { + getState()->setContextID( _traits->sharedContext->getState()->getContextID() ); + incrementContextIDUsageCount( getState()->getContextID() ); + } + else + { + getState()->setContextID( osg::GraphicsContext::createNewContextID() ); + } + } +} + +GraphicsWindowQt::~GraphicsWindowQt() +{ + close(); +} + +bool GraphicsWindowQt::init() +{ + QGLFormat format( QGLFormat::defaultFormat() ); + format.setAlphaBufferSize( _traits->alpha ); + format.setRedBufferSize( _traits->red ); + format.setGreenBufferSize( _traits->green ); + format.setBlueBufferSize( _traits->blue ); + format.setDepthBufferSize( _traits->depth ); + format.setStencilBufferSize( _traits->stencil ); + format.setSampleBuffers( _traits->sampleBuffers ); + format.setSamples( _traits->samples ); + + format.setAlpha( _traits->alpha>0 ); + format.setDepth( _traits->depth>0 ); + format.setStencil( _traits->stencil>0 ); + format.setDoubleBuffer( _traits->doubleBuffer ); + format.setSwapInterval( _traits->vsync ? 1 : 0 ); + + WindowData* windowData = _traits.get() ? dynamic_cast(_traits->inheritedWindowData.get()) : 0; + _widget = windowData ? windowData->_widget : 0; + if ( !_widget ) + { + GraphicsWindowQt* sharedContextQt = dynamic_cast(_traits->sharedContext); + QGLWidget* shareWidget = sharedContextQt ? sharedContextQt->getGraphWidget() : 0; + + Qt::WindowFlags flags = Qt::Window|Qt::CustomizeWindowHint;//|Qt::WindowStaysOnTopHint; + if ( _traits->windowDecoration ) + flags |= Qt::WindowTitleHint|Qt::WindowMinMaxButtonsHint|Qt::WindowSystemMenuHint; + + _widget = new GraphWidget( format, 0, shareWidget, flags ); + } + + _widget->setWindowTitle( _traits->windowName.c_str() ); + _widget->move( _traits->x, _traits->y ); + if ( !_traits->supportsResize ) _widget->setFixedSize( _traits->width, _traits->height ); + else _widget->resize( _traits->width, _traits->height ); + + _widget->setFocusPolicy( Qt::WheelFocus ); + _widget->setGraphicsWindow( this ); + useCursor( _traits->useCursor ); + return true; +} + +bool GraphicsWindowQt::setWindowRectangleImplementation( int x, int y, int width, int height ) +{ + if ( _widget ) _widget->setGeometry( x, y, width, height ); + return _widget!=NULL; +} + +void GraphicsWindowQt::getWindowRectangle( int& x, int& y, int& width, int& height ) +{ + if ( _widget ) + { + const QRect& geom = _widget->geometry(); + x = geom.x(); + y = geom.y(); + width = geom.width(); + height = geom.height(); + } +} + +bool GraphicsWindowQt::setWindowDecorationImplementation( bool windowDecoration ) +{ + Qt::WindowFlags flags = Qt::Window|Qt::CustomizeWindowHint;//|Qt::WindowStaysOnTopHint; + if ( windowDecoration ) + flags |= Qt::WindowTitleHint|Qt::WindowMinMaxButtonsHint|Qt::WindowSystemMenuHint; + _traits->windowDecoration = windowDecoration; + + // FIXME: Calling setWindowFlags or reparent widget will recreate the window handle, + // which makes QGLContext no longer work...How to deal with that? + //if ( _widget ) _widget->setWindowFlags( flags ); + return false; +} + +bool GraphicsWindowQt::getWindowDecoration() const +{ + return _traits->windowDecoration; +} + +void GraphicsWindowQt::grabFocus() +{ + if ( _widget ) + _widget->setFocus( Qt::ActiveWindowFocusReason ); +} + +void GraphicsWindowQt::grabFocusIfPointerInWindow() +{ + if ( _widget->underMouse() ) + _widget->setFocus( Qt::ActiveWindowFocusReason ); +} + +void GraphicsWindowQt::raiseWindow() +{ + if ( _widget ) + _widget->raise(); +} + +void GraphicsWindowQt::setWindowName( const std::string& name ) +{ + if ( _widget ) + _widget->setWindowTitle( name.c_str() ); +} + +std::string GraphicsWindowQt::getWindowName() +{ + return _widget ? _widget->windowTitle().toStdString() : ""; +} + +void GraphicsWindowQt::useCursor( bool cursorOn ) +{ + if ( _widget ) + { + _traits->useCursor = cursorOn; + if ( !cursorOn ) _widget->setCursor( Qt::BlankCursor ); + else _widget->setCursor( _currentCursor ); + } +} + +void GraphicsWindowQt::setCursor( MouseCursor cursor ) +{ + if ( cursor==InheritCursor && _widget ) + { + _widget->unsetCursor(); + } + + switch ( cursor ) + { + case NoCursor: _currentCursor = Qt::BlankCursor; break; + case RightArrowCursor: case LeftArrowCursor: _currentCursor = Qt::ArrowCursor; break; + case InfoCursor: _currentCursor = Qt::SizeAllCursor; break; + case DestroyCursor: _currentCursor = Qt::ForbiddenCursor; break; + case HelpCursor: _currentCursor = Qt::WhatsThisCursor; break; + case CycleCursor: _currentCursor = Qt::ForbiddenCursor; break; + case SprayCursor: _currentCursor = Qt::SizeAllCursor; break; + case WaitCursor: _currentCursor = Qt::WaitCursor; break; + case TextCursor: _currentCursor = Qt::IBeamCursor; break; + case CrosshairCursor: _currentCursor = Qt::CrossCursor; break; + case HandCursor: _currentCursor = Qt::OpenHandCursor; break; + case UpDownCursor: _currentCursor = Qt::SizeVerCursor; break; + case LeftRightCursor: _currentCursor = Qt::SizeHorCursor; break; + case TopSideCursor: case BottomSideCursor: _currentCursor = Qt::UpArrowCursor; break; + case LeftSideCursor: case RightSideCursor: _currentCursor = Qt::SizeHorCursor; break; + case TopLeftCorner: _currentCursor = Qt::SizeBDiagCursor; break; + case TopRightCorner: _currentCursor = Qt::SizeFDiagCursor; break; + case BottomRightCorner: _currentCursor = Qt::SizeBDiagCursor; break; + case BottomLeftCorner: _currentCursor = Qt::SizeFDiagCursor; break; + default: break; + }; + if ( _widget ) _widget->setCursor( _currentCursor ); +} + +bool GraphicsWindowQt::valid() const +{ + return _widget && _widget->isValid(); +} + +bool GraphicsWindowQt::realizeImplementation() +{ + if ( !_initialized ) + _initialized = init(); + + // A makeCurrent()/doneCurrent() seems to be required for + // realizing the context(?) before starting drawing + _widget->makeCurrent(); + _widget->doneCurrent(); + + _realized = true; + return true; +} + +bool GraphicsWindowQt::isRealizedImplementation() const +{ + return _realized; +} + +void GraphicsWindowQt::closeImplementation() +{ + if ( _widget ) + _widget->close(); +} + +bool GraphicsWindowQt::makeCurrentImplementation() +{ + _widget->makeCurrent(); + return true; +} + +bool GraphicsWindowQt::releaseContextImplementation() +{ + _widget->doneCurrent(); + return true; +} + +void GraphicsWindowQt::swapBuffersImplementation() +{ + _widget->swapBuffers(); +} + +void GraphicsWindowQt::requestWarpPointer( float x, float y ) +{ + if ( _widget ) + QCursor::setPos( _widget->mapToGlobal(QPoint((int)x,(int)y)) ); +} diff --git a/examples/osgviewerQtContext/osgviewerQtContext.cpp b/examples/osgviewerQtContext/osgviewerQtContext.cpp new file mode 100644 index 000000000..2cca33f41 --- /dev/null +++ b/examples/osgviewerQtContext/osgviewerQtContext.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "GraphicsWindowQt" + +class ViewerWidget : public QWidget, public osgViewer::CompositeViewer +{ +public: + ViewerWidget() : QWidget() + { + setThreadingModel(osgViewer::CompositeViewer::SingleThreaded); + + QWidget* widget1 = addViewWidget( createCamera(0,0,100,100), osgDB::readNodeFile("cow.osg") ); + QWidget* widget2 = addViewWidget( createCamera(0,0,100,100), osgDB::readNodeFile("glider.osg") ); + QWidget* widget3 = addViewWidget( createCamera(0,0,100,100), osgDB::readNodeFile("axes.osg") ); + QWidget* widget4 = addViewWidget( createCamera(0,0,100,100), osgDB::readNodeFile("fountain.osg") ); + QWidget* popupWidget = addViewWidget( createCamera(900,100,320,240,"Popup window",true), + osgDB::readNodeFile("dumptruck.osg") ); + popupWidget->show(); + + QGridLayout* grid = new QGridLayout; + grid->addWidget( widget1, 0, 0 ); + grid->addWidget( widget2, 0, 1 ); + grid->addWidget( widget3, 1, 0 ); + grid->addWidget( widget4, 1, 1 ); + setLayout( grid ); + + connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) ); + _timer.start( 10 ); + } + + QWidget* addViewWidget( osg::Camera* camera, osg::Node* scene ) + { + osgViewer::View* view = new osgViewer::View; + view->setCamera( camera ); + addView( view ); + + view->setSceneData( scene ); + view->addEventHandler( new osgViewer::StatsHandler ); + view->setCameraManipulator( new osgGA::TrackballManipulator ); + + GraphicsWindowQt* gw = dynamic_cast( camera->getGraphicsContext() ); + return gw ? gw->getGraphWidget() : NULL; + } + + osg::Camera* createCamera( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false ) + { + osg::DisplaySettings* ds = osg::DisplaySettings::instance(); + osg::ref_ptr traits = new osg::GraphicsContext::Traits; + traits->windowName = name; + traits->windowDecoration = windowDecoration; + traits->x = x; + traits->y = y; + traits->width = w; + traits->height = h; + traits->doubleBuffer = true; + traits->alpha = ds->getMinimumNumAlphaBits(); + traits->stencil = ds->getMinimumNumStencilBits(); + traits->sampleBuffers = ds->getMultiSamples(); + traits->samples = ds->getNumMultiSamples(); + + osg::ref_ptr camera = new osg::Camera; + camera->setGraphicsContext( new GraphicsWindowQt(traits.get()) ); + + camera->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) ); + camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); + camera->setProjectionMatrixAsPerspective( + 30.0f, static_cast(traits->width)/static_cast(traits->height), 1.0f, 10000.0f ); + return camera.release(); + } + + virtual void paintEvent( QPaintEvent* event ) + { frame(); } + +protected: + + QTimer _timer; +}; + +int main( int argc, char** argv ) +{ + QApplication app(argc, argv); + ViewerWidget* viewWidget = new ViewerWidget; + viewWidget->setGeometry( 100, 100, 800, 600 ); + viewWidget->show(); + return app.exec(); +}