diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0fd071649..7f9f40d38 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -168,7 +168,10 @@ IF(DYNAMIC_OPENSCENEGRAPH) IF (QT_FOUND AND QT_QTOPENGL_LIBRARY) ADD_SUBDIRECTORY(osgviewerQT) - ENDIF(QT_FOUND AND QT_QTOPENGL_LIBRARY) + IF (QT4_FOUND) + ADD_SUBDIRECTORY(osgviewerQtWidget) + ENDIF() + ENDIF() IF (FLTK_FOUND) ADD_SUBDIRECTORY(osgviewerFLTK) diff --git a/examples/osgviewerQtWidget/CMakeLists.txt b/examples/osgviewerQtWidget/CMakeLists.txt new file mode 100644 index 000000000..8725bf1dd --- /dev/null +++ b/examples/osgviewerQtWidget/CMakeLists.txt @@ -0,0 +1,42 @@ +#user interface compilation +SET(SOURCES_UI testMainWin.ui testOutboardWin.ui) +QT4_WRAP_UI( SOURCES_UI_H ${SOURCES_UI}) + +#ressources +#SET(QtApp_RCCS ressources/images/icons.qrc) +#QT4_ADD_RESOURCES(QtApp_RCC_SRCS ${QtApp_RCCS}) + +# for the macro Q_OBJECT.. +SET(SOURCES_H + CompositeViewerQOSG.h + testMainWin.h + testOutboardWin.h +) + +QT4_WRAP_CPP( SOURCES_H_MOC ${SOURCES_H} ) + +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} "./") + +SET(TARGET_SRC + ${SOURCES_H_MOC} + ${SOURCES_UI_H} + QOSGWidget.h + testOutboardWin.cpp + CompositeViewerQOSG.cpp + QOSGWidget.cpp + testMainWin.cpp + main.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} ) + +#### end var setup ### +SETUP_EXAMPLE(osgviewerQtWidget) + diff --git a/examples/osgviewerQtWidget/CompositeViewerQOSG.cpp b/examples/osgviewerQtWidget/CompositeViewerQOSG.cpp new file mode 100644 index 000000000..a2dc2d313 --- /dev/null +++ b/examples/osgviewerQtWidget/CompositeViewerQOSG.cpp @@ -0,0 +1,22 @@ +// CompositeViewerQOSG.cpp + +// #include +#include "CompositeViewerQOSG.h" + +//////////////////////////////////////////////////////////////////////////////// +CompositeViewerQOSG::CompositeViewerQOSG( QWidget * parent, Qt::WindowFlags f) + : QWidget( parent, f ), osgViewer::CompositeViewer() +{ + setThreadingModel(osgViewer::CompositeViewer::SingleThreaded); + + connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); + _timer.start(10); // Don't know why 10, but 1 was no faster. +} + + +void CompositeViewerQOSG::paintEvent( QPaintEvent * /* event */ ) +{ + frame(); +} + + diff --git a/examples/osgviewerQtWidget/CompositeViewerQOSG.h b/examples/osgviewerQtWidget/CompositeViewerQOSG.h new file mode 100644 index 000000000..48129dd91 --- /dev/null +++ b/examples/osgviewerQtWidget/CompositeViewerQOSG.h @@ -0,0 +1,28 @@ +#ifndef _COMPOSITE_VIEWER_QOSG_HPP_ +#define _COMPOSITE_VIEWER_QOSG_HPP_ + +#include +#include +#include + +class QPaintEvent; + +//------------------------------------------------------------------------------ +class CompositeViewerQOSG : public QWidget, public osgViewer::CompositeViewer +{ + Q_OBJECT + + +public: + + CompositeViewerQOSG( QWidget * parent = 0, Qt::WindowFlags f = 0 ); + virtual ~CompositeViewerQOSG() {} + + void paintEvent( QPaintEvent * /* event */ ); + +protected: + QTimer _timer; + +}; // CompositeViewerQOSG + +#endif // _COMPOSITE_VIEWER_QOSG_HPP_ diff --git a/examples/osgviewerQtWidget/QOSGWidget.cpp b/examples/osgviewerQtWidget/QOSGWidget.cpp new file mode 100644 index 000000000..a269a4305 --- /dev/null +++ b/examples/osgviewerQtWidget/QOSGWidget.cpp @@ -0,0 +1,606 @@ +/* OpenSceneGraph example, osganimate. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +#include +#include +#include + +#if defined(WIN32) && !defined(__CYGWIN__) +#include +typedef HWND WindowHandle; +typedef osgViewer::GraphicsWindowWin32::WindowData WindowData; +#elif defined(__APPLE__) // Assume using Carbon on Mac. +// #include +#include +typedef WindowRef WindowHandle; +typedef osgViewer::GraphicsWindowCarbon::WindowData WindowData; +#else // all other unix +#include +typedef Window WindowHandle; +typedef osgViewer::GraphicsWindowX11::WindowData WindowData; +// 10/17/08 LM -- osgViewer/api/X11/GraphicsWindowX11 includes X.h. +// X.h defines KeyPress and KeyRelease. +// By doing so, using QEvent::KeyPress resolves into QEvent::2, +// causing a compile error. +#undef KeyPress +#undef KeyRelease +#endif + +QOSGWidget::QOSGWidget( QWidget * parent, WindowFlags f) + : QWidget(parent, f), + _gw(0) +{ +#if 0 + // This was a win on Linux. Same as WA_PaintOnScreen? + extern void qt_x11_set_global_double_buffer(bool); + qt_x11_set_global_double_buffer(false); +#endif + createContext(parent); + +// Date: Tue, 16 Jun 2009 10:07:16 +0000 +// From: "Eric Pouliquen" +// Subject: Re: [osg-submissions] New QOSGWidget demo with a 4-way split +// window and bonus outboard window. +// Suggested replacing the two setAttribute calls with this... +// setAttribute(Qt::WA_OpaquePaintEvent); +// This solverd a flickering problem on Windows +// 8600 GT (185.85) and a Quadro FX1400 (182.65) +// but causes a visible black border to be visible in the rendering +// windows on Linux. This problem gone when WA_PaintOnScreen +// is used in combination. + + // Hmmm... + // According to Qt doc, WA_PaintOnScreen is X11 only and disables + // double-buffering. I think this just means it disables a + // buffer swap under Qt control. We want OSG to have full control. + // + // Equivalent to qt_x11_set_global_double_buffer(false)? + // + // Tried turning it off and got severe flashing on Linux. + // Looks like without this we get an extraneous clear and + // buffer swap form Qt. + setAttribute(Qt::WA_PaintOnScreen); + // This flags that something other than Qt is responsible for + // all rendering in the window under the widget's control. + setAttribute(Qt::WA_OpaquePaintEvent); + // This seems superfluous now. + // setAttribute(Qt::WA_NoSystemBackground); + +// Here or in ViewQOSG? +// Either way is OK; but since this class is also the one implementing +// eventFilter(), I thought it might be wiser here. +// Qt + // If you want to see how TAB and SHIFT-TAB work wrt focus, + // uncomment the following line. + // NOTE: If focusPolicy was set in the .ui file, that setting will + // override this. So don't set focusPolicy in the .ui file! + if (parent) + { + + // The desire is to allow tabbing among the views of the + // composite viewer. On Linux, I could get that to work in many + // different ways, including setting StrongFocus on the QOSGWidget and + // re-implementing focusInEvent() in ViewQOSG. But the only thing + // that worked on Windows was to set StrongFocus on the *parent*, NOT + // on this QOSGWidget, AND to have ViewQOSG::keyReleaseEvent() do + // something special on the release of a Tab or Shift-Tab key. (The + // release is seen by the ViewQOSG that is getting the focus.) + + parent->setFocusPolicy( Qt::StrongFocus ); + + // This instance of the QOSGWidget becomes the filter object on its + // 'parent'. Ie, the child is now filtering events for the parent + // QWidget. + parent->installEventFilter( this ); + + qDebug() << "parent->width() is " << parent->width(); + qDebug() << "parent->height() is " << parent->height(); + qDebug() << "parent->x() is " << parent->x(); + qDebug() << "parent->y() is " << parent->y(); + + qDebug() << "width() is " << width(); + qDebug() << "height() is " << height(); + qDebug() << "x() is " << x(); + qDebug() << "y() is " << y(); + } + else + setFocusPolicy( Qt::StrongFocus ); +} + +void QOSGWidget::createContext(QWidget * parent) +{ + osg::DisplaySettings* ds = osg::DisplaySettings::instance(); + + osg::ref_ptr traits = + new osg::GraphicsContext::Traits; + + traits->readDISPLAY(); + if (traits->displayNum<0) + traits->displayNum = 0; + + traits->windowName = "qosgwidget"; + traits->screenNum = 0; +// original location: +// traits->x = x(); +// traits->y = y(); +// traits->width = width(); +// traits->height = height(); + traits->alpha = ds->getMinimumNumAlphaBits(); + traits->stencil = ds->getMinimumNumStencilBits(); + traits->windowDecoration = false; + traits->doubleBuffer = true; + traits->sharedContext = 0; + traits->sampleBuffers = ds->getMultiSamples(); + traits->samples = ds->getNumMultiSamples(); + +#if defined(__APPLE__) + // Extract a WindowPtr from the HIViewRef that QWidget::winId() returns. + // Without this change, the peer tries to call GetWindowPort on the HIViewRef + // which returns 0 and we only render white. + traits->inheritedWindowData = new WindowData(HIViewGetWindow((HIViewRef)winId())); + +#else // all others + traits->inheritedWindowData = new WindowData(winId()); +#endif + if (ds->getStereo()) + { + switch(ds->getStereoMode()) + { + case(osg::DisplaySettings::QUAD_BUFFER): + traits->quadBufferStereo = true; + break; + case(osg::DisplaySettings::VERTICAL_INTERLACE): + case(osg::DisplaySettings::CHECKERBOARD): + case(osg::DisplaySettings::HORIZONTAL_INTERLACE): + traits->stencil = 8; + break; + default: break; + } + } + + osg::ref_ptr gc = + osg::GraphicsContext::createGraphicsContext(traits.get()); + + _gw = dynamic_cast(gc.get()); + + // A person named Lukas on the OSG group posted that to support embedding + // in another window, the following traits had to be set after the + // graphics context was created. And he's right, if embedding, you + // definitely have to do this AFTER creating the context.... Since it + // also works when creating a top-level window, do it here for all cases + + // We may have just gotten some Bad Window errors from X11 calling + // XGetWindowAttributes before the Window has been realized. + // We'll also have garbage in the traits which get fixed up next. + + // _overrideTraits is superfluous now. + + if (parent) + { + traits->x = parent->x(); + traits->y = parent->y(); + traits->width = parent->width(); + traits->height = parent->height(); + } + else + { + traits->x = x(); + traits->y = y(); + traits->width = width(); + traits->height = height(); + } +} + +//------------------------------------------------------------------------------ +// From the Qt Doc: +// "eventFilter() can accept or reject the event, and allow or deny further +// processing of the event. +// +// If all event filters allow further processing of an event (by each +// returning false), the event is sent to the target object, in this case, the +// parent of this QOSGWidget instance. +// +// If one of them stops processing (by returning true), the target (ie the +// parent widget) and any later event filters do not get to see the event at +// all. " +// +// Effectively, this filter takes all the events it's interested in, most +// notably keyboard events, and passes them along to OSG and *prevents* them +// from reaching their intended target, this widget's parent. +// +// Since QOSGWidget sets Qt focusPolicy to StrongFocus, eventFilter() isn't +// needed for the keyboard events...at least this what I've observed on Linux; +// however it is essential to support resizing in embedded Qt windows that +// aren't a top-level window. +// +// Addendum: I think the above statement is true only if the parent has the +// NoFocus policy? If the parent also has StrongFocus, then +// eventFilter() has to return false for key events or tabbing +// breaks. +//------------------------------------------------------------------------------ +// Return false to allow event to go to the intended target, the parent of +// this QOSGWidget. The only events we really want to go to the parent are +// TAB or shift-TAB presses and releases. +bool QOSGWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj != parent()) + { + return false; + } + else if (event->type() == QEvent::KeyPress) + { + QKeyEvent *ke = static_cast(event); + + if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) + { + qDebug() << "QOSGWidget::eventFilter: TAB Press on " + << qPrintable(objectName()); + + // Empirically have found that it's not necessary to call + // keyPressEvent on tab press ... my guess is that OSG ignores it. + keyPressEvent( ke ); + + // Return false so that the parent QWidget will process the tab. + return false; + } + else + { + qDebug() << "QOSGWidget::eventFilter: KeyPress on " + << qPrintable(objectName()); + keyPressEvent( ke ); + // event handled, return true because parent does not have to see + // this event + return true; + } + } + else if (event->type() == QEvent::KeyRelease) + { + QKeyEvent *ke = static_cast(event); + + if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) + { + qDebug() << "QOSGWidget::eventFilter: TAB Release on " + << qPrintable(objectName()); + + keyReleaseEvent( ke ); + // Return false so that the parent QWidget will process the tab.. + return false; + } + else + { + qDebug() << "QOSGWidget::eventFilter: KeyRelease on " + << qPrintable(objectName()); + keyReleaseEvent( ke ); + // event handled, return true because parent does not have to see + // this event + return true; + } + } + else if (event->type() == QEvent::Resize) + { + QResizeEvent *re = static_cast(event); + + qDebug() << "QOSGWidget::eventFilter: width is " + << re->size().width() + << "; height is " << re->size().height() + << " on " << qPrintable(objectName()) + ; + + // Call setGeometry on 'this', which will trigger + // QOSGWidget::resizeEvent + setGeometry(0, 0, re->size().width(), re->size().height()); + + // event handled, return true because parent does not have to see + // this event + return true; + } + else if (event->type() == QEvent::Close) + { + QCloseEvent *ce = static_cast(event); + closeEvent( ce ); + } + + return false; +} + +// Skip all of the event queue reimplementations on WIN32. +// On second thought, all but the key events. +#ifndef WIN32 + +void QOSGWidget::destroyEvent(bool /* destroyWindow */, bool /* destroySubWindows */ ) +{ + qDebug() << "QOSGWidget::destroyEvent"; + _gw->getEventQueue()->closeWindow(); +} + + +void QOSGWidget::closeEvent( QCloseEvent * /* event */ ) +{ + qDebug() << "QOSGWidget::closeEvent"; + _gw->getEventQueue()->closeWindow(); +} + + +void QOSGWidget::resizeEvent( QResizeEvent * event ) +{ + const QSize & size = event->size(); + + qDebug() << "QOSGWidget::resizeEvent on " << qPrintable(objectName()) + << " - width is " << size.width() + << "; height is " << size.height(); + + _gw->getEventQueue()->windowResize(0, 0, size.width(), size.height() ); + _gw->resized(0, 0, size.width(), size.height()); + +} + +void QOSGWidget::mousePressEvent( QMouseEvent* event ) +{ + qDebug() << "QOSGWidget::mousePressEvent on " + << qPrintable(objectName()) << " at X, Y = " + << event->x() << ", " << event->y(); + 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; + } + _gw->getEventQueue()->mouseButtonPress(event->x(), event->y(), button); +} + +void QOSGWidget::mouseDoubleClickEvent ( QMouseEvent * event ) +{ + qDebug() << "QOSGWidget::mouseDoubleClickEvent on " + << qPrintable(objectName()); + 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; + } + _gw->getEventQueue()->mouseDoubleButtonPress(event->x(), event->y(), button); +} +void QOSGWidget::mouseReleaseEvent( QMouseEvent* event ) +{ + qDebug() << "QOSGWidget::mouseReleaseEvent on " + << qPrintable(objectName()) << " at X, Y = " + << event->x() << ", " << event->y(); + + 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; + } + _gw->getEventQueue()->mouseButtonRelease(event->x(), event->y(), button); +} + +void QOSGWidget::mouseMoveEvent( QMouseEvent* event ) +{ + qDebug() << "QOSGWidget::mouseMoveEvent"; + _gw->getEventQueue()->mouseMotion(event->x(), event->y()); +} + +#endif // ndef WIN32 + +void QOSGWidget::keyPressEvent( QKeyEvent* event ) +{ + qDebug() << "QOSGWidget::keyPressEvent on " << qPrintable(objectName()); + _gw->getEventQueue()->keyPress( + (osgGA::GUIEventAdapter::KeySymbol) *(event->text().toAscii().data() ) ); +} + +void QOSGWidget::keyReleaseEvent( QKeyEvent* event ) +{ + qDebug() << "QOSGWidget::keyReleaseEvent on " << qPrintable(objectName()); + if (event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) + { + qDebug() << "---- It's a TAB Release" ; + } + else + { + int c = *event->text().toAscii().data(); + _gw->getEventQueue()->keyRelease( (osgGA::GUIEventAdapter::KeySymbol) (c) ); + } +} + +//--------------------------------------------------------------------------- +// ViewQOSG creates the traits... +ViewQOSG::ViewQOSG( QWidget *parent ) + : osgViewer::View(), + QOSGWidget( parent ), + _daw(parent) +{ + +// OSG + setGeometry( 0, 0, parent->width(), parent->height() ); + + getCamera()->setGraphicsContext( getGraphicsWindow() ); + + getCamera()->setProjectionMatrixAsPerspective( + 30.0f, + static_cast(parent->width()) / + static_cast(parent->height()), 1.0, 1000.0); + + getCamera()->setViewport( + new osg::Viewport( 0, 0, parent->width(), parent->height())); + + setupManipulatorAndHandler( *this ); + +} // ViewQOSG::ViewQOSG + +//--------------------------------------------------------------------------- +// note: since ViewQOSG has focusPolicy of Qt::NoFocus, this won't be called, +// but leaving it here anyway. +// See keyReleaseEvent() +void ViewQOSG::focusInEvent( QFocusEvent * /* event */) +{ + qDebug() << "ViewQOSG::focusInEvent on " << qPrintable(objectName()); + + CompositeViewerQOSG *cv = + dynamic_cast(getViewerBase()); + if (cv) + // Tell the viewer that this view's camera should now have focus + cv->setCameraWithFocus( getCamera() ); +} + +#ifndef WIN32 +//--------------------------------------------------------------------------- +void ViewQOSG::resizeEvent( QResizeEvent * event ) +{ + const QSize & size = event->size(); + + qDebug() << "ViewQOSG::resizeEvent: width is " + << size.width() << "; height is " << size.height(); + + QOSGWidget::resizeEvent( event ); + + // This call seems to be essential to getting a picture after view's + // parent QWidget has been collapsed due to QSplitter activity. + getCamera()->setProjectionMatrixAsPerspective( + 30.0f, + static_cast(size.width()) / + static_cast(size.height()), 1.0, 1000.0); + +//// +// This is an attempt to prevent Qt from giving tab focus to a ViewQOSG that +// has been collapsed; in our example, this can happen by using the QSplitters +// in the QMainWindow. +// +// However, while testing this, discovered the bigger problem: +// On *any* resize, osgViewer::CompositeViewer::eventTraversal() calls +// setCameraWithFocus(0) (ie, to the first view). +// Meanwhile, Qt's focus widget may or may not be the correpsonding ViewQOSG. +// Possibly worse, the ViewQOSG corresponding to the first view in the +// composite viewer may not even be visible. +// Doesn't seem to matter in this sense: regardless of which VIEWQOSG gets a +// keyboard event, the compositeViewer applies the key's corresponding action +// to the current camera/view. +// +// All of which suggests that trying to support TAB focus on the ViewQOSG +// widgets just isn't worth it. + +// if (size.width() == 0 || size.height() == 0) +// setFocusPolicy( Qt::NoFocus ); +// else +// setFocusPolicy( Qt::StrongFocus ); + +//// + +} // ViewQOSG::resizeEvent +#endif // WIN32 + +//--------------------------------------------------------------------------- +// Reimplementing keyReleaseEvent from QOSGWidget. +// When tabbing in Qt, the current widget sees the TAB Press and the widget +// that gets the focus sees the TAB Release. +// On that TAB Release, we need to tell the CompositeViewer the camera that +// should now have the focus. +// +// SEE COMMENTS IN ViewQOSG::resizeEvent re how resizing screws this all up... + +void ViewQOSG::keyReleaseEvent( QKeyEvent* event ) +{ + qDebug() << "ViewQOSG::keyReleaseEvent on " << qPrintable(objectName()); + + if (event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) + { + if (event->key() == Qt::Key_Tab) + qDebug() << "... and it's a TAB"; + else + qDebug() << "... and it's a SHIFT-TAB"; + + CompositeViewerQOSG *cv = + dynamic_cast(getViewerBase()); + if (cv) + { + // Tell the viewer that this view's camera should now have focus + cv->setCameraWithFocus( getCamera() ); + + // Note that the Stats come up wherever; having little success in + // getting them to come up in a specific osgView. +// cv->setEventQueue( _gw->getEventQueue() ); + } + // and otherwise ignore the event + } + else + { + QOSGWidget::keyReleaseEvent( event ); + } + +} // ViewQOSG::keyReleaseEvent + +//--------------------------------------------------------------------------- +void ViewQOSG::setData( osg::ref_ptr loadedModel ) +{ + setSceneData(loadedModel.get()); +} + +//--------------------------------------------------------------------------- +float ViewQOSG::aspectRatio( int width, int height ) +{ + return static_cast(width) / static_cast(height); +} + +//--------------------------------------------------------------------------- +void setupManipulatorAndHandler(osgViewer::View & view + /*, osg::ArgumentParser & arguments*/) +{ + // set up the camera manipulators. + { + osg::ref_ptr keyswitchManipulator = + new osgGA::KeySwitchMatrixManipulator; + + keyswitchManipulator->addMatrixManipulator( + '1', "Trackball", new osgGA::TrackballManipulator() ); + keyswitchManipulator->addMatrixManipulator( + '2', "Flight", new osgGA::FlightManipulator() ); + keyswitchManipulator->addMatrixManipulator( + '3', "Drive", new osgGA::DriveManipulator() ); + keyswitchManipulator->addMatrixManipulator( + '4', "Terrain", new osgGA::TerrainManipulator() ); + + view.setCameraManipulator( keyswitchManipulator.get() ); + } + + // add the state manipulator + view.addEventHandler( new osgGA::StateSetManipulator( + view.getCamera()->getOrCreateStateSet()) ); + + // add the thread model handler + view.addEventHandler(new osgViewer::ThreadingHandler); + + // add the window size toggle handler + view.addEventHandler(new osgViewer::WindowSizeHandler); + + // add the stats handler + view.addEventHandler(new osgViewer::StatsHandler); + + // add the help handler + view.addEventHandler(new osgViewer::HelpHandler(/*arguments.getApplicationUsage()*/)); +} + diff --git a/examples/osgviewerQtWidget/QOSGWidget.h b/examples/osgviewerQtWidget/QOSGWidget.h new file mode 100644 index 000000000..61a7c6c68 --- /dev/null +++ b/examples/osgviewerQtWidget/QOSGWidget.h @@ -0,0 +1,163 @@ +/* OpenSceneGraph example, osganimate. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ +#ifndef _QOSG_WIDGET_HPP_ +#define _QOSG_WIDGET_HPP_ + + +#include +//#include +#include +#include + +#include +#include +#include + + +using Qt::WindowFlags; + +// Port Note 10/14/08 LM -- I tried putting some of these headers into the +// source file, but ran into compile problems. Very order dependent? +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class QOSGWidget : public QWidget +{ +public: + + QOSGWidget( QWidget * parent = 0, WindowFlags f = 0 ); + + virtual ~QOSGWidget() {} + + osgViewer::GraphicsWindow* getGraphicsWindow() { + return _gw.get(); + } + const osgViewer::GraphicsWindow* getGraphicsWindow() const { + return _gw.get(); + } + +protected: + + void init(); + void createContext( QWidget *qwindow = 0 ); + bool eventFilter(QObject *obj, QEvent *event); + + // Looking at the doc for Qt::WA_PaintOnScreen this may be appropriate. + // Didn't seem to help or hurt. + virtual QPaintEngine *paintEngine() { return 0; } + +// Grabbed this from Martin Beckett: +// The GraphincsWindowWin32 implementation already takes care of message handling. +// We don't want to relay these on Windows, it will just cause duplicate messages +// with further problems downstream (i.e. not being able to throw the trackball + +#ifndef WIN32 + virtual void mouseDoubleClickEvent ( QMouseEvent * event ); + virtual void closeEvent( QCloseEvent * event ); + virtual void destroyEvent( bool destroyWindow = true, + bool destroySubWindows = true); + virtual void resizeEvent( QResizeEvent * event ); + virtual void mousePressEvent( QMouseEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + virtual void mouseMoveEvent( QMouseEvent* event ); +#endif // ndef WIN32 + virtual void keyPressEvent( QKeyEvent* event ); + virtual void keyReleaseEvent( QKeyEvent* event ); + + osg::ref_ptr _gw; + bool _overrideTraits; + +}; // QOSGWidget + + +//------------------------------------------------------------------------------ +// I could get Linux to work in so many different ways. +// +// But I could get Windows to work in only one way: +// 1. If QOSGWidget is constructed with a parent QWidget, the parent's +// focusPolicy is set to StrongFocus. The QOSGWidget widget +// will have NoFocus, the default for QWidget. +// +// 2. If ViewQOSG is part of a osgViewer::CompositeViewer, +// ViewQOSG::keyReleaseEvent(), on a TAB or Shift-TAB key release, +// sets the Viewer's cameraWithFocus to the ViewQOSG's camera. +// All other key releases are passed up to QOSGWidget::keyReleaseEvent. +// +// 3. Since the QOSGWidget's focusPolicy is noFocus, and hence ViewQOSG's +// is as well, ViewQOSG::focusInEvent() will never be called. +// +// This was the only way I could get Windows to allow tabbing to change +// which View of the CompositeViewer should have focus. +// +// Using StrongFocus on the QOSGWidget (and hence ViewQOSG) instead of its +// parent caused all sorts of behavioral problems on Windows. For example, +// tabbing didn't work as expected. Or it did, but it didn't matter +// because the CompositeViewer considered the "current ViewQOSG" to be the +// one under the mouse. +// +// The code we based this on created a class that inherited from +// QOSGWidget and osgViewer::Viewer. We instead inherit from +// QOSGWidget and osgViewer::View. This allows us to post a different +// scene graph in each view that the viewer manages. + +class ViewQOSG : public osgViewer::View, public QOSGWidget +{ +public: + ViewQOSG( QWidget *parent /*, osg::GraphicsContext::Traits* traits*/ ); + + virtual ~ViewQOSG() {} + +// Reimplement from QWidget +#ifndef WIN32 + virtual void resizeEvent( QResizeEvent * event ); +#endif // ndef WIN32 + virtual void keyReleaseEvent( QKeyEvent* event ); + + void setData( osg::ref_ptr loadedModel ); + + QWidget * getDrawingAreaWidget() { return _daw; } + + float aspectRatio( int width, int height ); + +protected: + void focusInEvent( QFocusEvent *event ); + + QWidget *_daw; // drawing area widget + int _x, _y, _width, _height; + +}; + + +extern void setupManipulatorAndHandler(osgViewer::View & viewer + /*, osg::ArgumentParser & arguments*/); +#endif // _QOSG_WIDGET_HPP_ diff --git a/examples/osgviewerQtWidget/README b/examples/osgviewerQtWidget/README new file mode 100644 index 000000000..d15e39f37 --- /dev/null +++ b/examples/osgviewerQtWidget/README @@ -0,0 +1,107 @@ +This now builds with cmake if added to the osg examples tree. A change to +examples/CMakeLists.txt is also required. + +It is also possible to build this stand alone with qmake using the .pro file. +I think I've made the .pro sufficiently generic to build anywhere. The first +couple of lines in the file need to be adjusted for anyone's local environment. + +This builds and runs on Linux and Win32 OK. See status section. + +Mac has some problems. Qt was 4.4.2 and Mac OS X 10.5.2. 1) Had to adjust the +.pro file to hide QT_NO_DEBUG_OUTPUT or problems with a Frameworks header. 2) +Had to eliminate the outboard window to avoid a crash. 3) Only the lower-left +of the 4 views is OK. The other 3 views filled with frame buffer garbage. 4) +Maximize twice and we loose the lower-left too. + +My colleague Liz Martin collaborated on the design and did the Qt work. +She is known for her copious comments. -- Don Leich, Intelligent Light + + +Linux/Win32 builds (on Mac substitute make for gmake): + + gmake clean + qmake qosgwidget.pro + gmake + + + +12/05/08 Status +--------------- +o Implemented the skeleton of CompositeViewerQOSG and ViewQOSG (in + QOSGWidget.cpp) + +o In testMainWin.ui, osgGraphicsArea is a "promoted" widget: It is promoted to + CompositeViewerQOSG + + CompositeViewerQOSG inherits from both QWidget and osgViewer::CompositeViewer + +o ViewQOSG inherits from QOSGWidget (which inherits from QWidget) and + osgViewer::View + +o Bagged on allowing tab focus between the ViewQOSG widgets because: + + o on resizing, OSG's CompositeViewer sets the camera (and view) focus to its + first osgViewer::View, which can create a disconnect between the Qt widget + (ie ViewQOSG) that has the focus and is receiving keyboard events, and the + actual ViewQOSG that the composite viewer acts on + + o From empirical observation, it didn't seem to matter which ViewQOSG + received the keyboard event; the composite viewer applied the designated + action to the view the composite viewer consider current + + o TAB focus would be given to a ViewQOSG even it had been completely + collapsed via QSplitter action in the QMainWindow. I was not successful + in my attempts to modify this behavior. + +12/15/08 Status +--------------- + +o In this area, in testMainWin.ui, not only is osgGraphicsArea a promoted + widget (to CompositeViewerQOSG), but the 4 subviews are now also promoted + widgets, to ViewQOSG. + +o I eliminated QOSGWidget::eventFilter(). + +o In QOSGWidget::QOSGWidget(), I no longer take x, y, width, and height from + the 'parent', I simply take it from itself. + +Unfortunately, these changes don't help the Windows exe at all. Linux +continues to run just fine. But Windows doesn't see that TAB, and doesn't see +any *results* of the Key presses (Like 'l' to change lighting.) + + +5/15/09 Status +--------------- + +o Applied Martin Beckett's fix for WIN32 event queue handling. + +5/19/09 Status +--------------- + +o Altered WIN32 event queue handling to not skip key events. + +o Got Mac to build and sort of run. Qt is 4.4.2 and Mac OS X 10.5.2. +1) Had to adjust the .pro file to hide QT_NO_DEBUG_OUTPUT or problems with +a Frameworks header. 2) Had to eliminate the outboard window to +avoid a crash. 3) Only the lower-left of the 4 views is OK. +The other 3 views filled with frame buffer garbage. 4) Maximize +twice and we loose the lower-left too. + + +6/18/09 Status +--------------- + +o Simon Loic provided cmake infrastructure and help me debug some cmake +configuration problems. I needed to wipe my out-of-source object tree +and start over explicitly defining QT version 4, ala: + ccmake ../OpenSceneGraph-2.8.1 -DDESIRED_QT_VERSION=4 + +o Reimplemented QWidget::paintEngine to return 0 as recommended by doc. +This is purported to stop Qt from rendering anything in our windows. + +o Eric Pouliquen recommended replacing setAttribute Qt::WA_PaintOnScreen +and Qt::WA_NoSystemBackground with Qt::WA_OpaquePaintEvent. Seems best +if we add Qt::WA_OpaquePaintEvent, get rid of Qt::WA_NoSystemBackground, +but keep Qt::WA_PaintOnScreen. + + diff --git a/examples/osgviewerQtWidget/main.cpp b/examples/osgviewerQtWidget/main.cpp new file mode 100644 index 000000000..cfb30be9a --- /dev/null +++ b/examples/osgviewerQtWidget/main.cpp @@ -0,0 +1,118 @@ +// Demo for a 4-way split window with a different scene graph in +// each of the 4 views. Also, an optional 5th view in it's own window. +// Qt 4.4.3 was used for this example. +// +#include +#include +#include +#include + +#include +#include + +#include "testMainWin.h" +#include "testOutboardWin.h" + +#include + +#ifdef __APPLE__ +#define DO_OUTBOARD_WINDOW 0 // won't go on Mac +#else +#define DO_OUTBOARD_WINDOW 1 +#endif + +using namespace std; + + +int main( int argc, char **argv ) + +{ + QApplication app( argc, argv ); + + // load some standard files + osg::ref_ptr Cow = osgDB::readNodeFile("cow.osg"); + if (!Cow) + { + qDebug() << "No cow loaded."; + return 1; + } + + osg::ref_ptr Truck = osgDB::readNodeFile("dumptruck.osg"); + if (!Truck) + { + qDebug() << "No truck loaded."; + return 1; + } + + osg::ref_ptr Spaceship = osgDB::readNodeFile("spaceship.osg"); + if (!Spaceship) + { + qDebug() << "No spaceship loaded."; + return 1; + } + + osg::ref_ptr Cessna = osgDB::readNodeFile("cessna.osg"); + if (!Cessna) + { + qDebug() << "No cessna loaded."; + return 1; + } + + osg::ref_ptr Fountain = osgDB::readNodeFile("fountain.osg"); + if (!Fountain) + { + qDebug() << "No fountain loaded."; + return 1; + } + + /////////////////////////////////////////////////////////////////////////// + app.connect( &app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()) ); + + osg::ArgumentParser arguments(&argc, argv); + + QPointer myMainWindow = new testMainWin; + +#if DO_OUTBOARD_WINDOW + QPointer mySecondaryWindow = new testOutboardWin; +#endif + + // The .ui file uses the "Promoted" widget, CompositeViewerQOSG + osg::ref_ptr compositeViewer = myMainWindow->ui.osgGraphicsArea; + + osg::ref_ptr view1 = new ViewQOSG( myMainWindow->ui.graphicsView1 ); + view1->setObjectName("ViewQOSG 1"); + osg::ref_ptr view2 = new ViewQOSG( myMainWindow->ui.graphicsView2 ); + view2->setObjectName("ViewQOSG 2"); + osg::ref_ptr view3 = new ViewQOSG( myMainWindow->ui.graphicsView3 ); + view3->setObjectName("ViewQOSG 3"); + osg::ref_ptr view4 = new ViewQOSG( myMainWindow->ui.graphicsView4 ); + view4->setObjectName("ViewQOSG 4"); + + view1->setData( Cow ); + view2->setData( Truck ); + view3->setData( Spaceship ); + view4->setData( Cessna ); + + compositeViewer->addView( view1.get() ); + compositeViewer->addView( view2.get() ); + compositeViewer->addView( view3.get() ); + compositeViewer->addView( view4.get() ); + + myMainWindow->show(); + +#if DO_OUTBOARD_WINDOW + QWidget *outboardGfx = mySecondaryWindow->getDrawingAreaWidget(); + osg::ref_ptr outboardView = static_cast( outboardGfx ); + outboardView->setObjectName("ViewQOSG Outboard"); + outboardView->setData( Fountain ); + + // Note that outboardView, in a completely different window, is going to be + // managed by the compositeViewer in the QMainWindow. + compositeViewer->addView( outboardView.get() ); + + mySecondaryWindow->show(); +#endif + + return app.exec(); + +} diff --git a/examples/osgviewerQtWidget/qosgwidget.pro b/examples/osgviewerQtWidget/qosgwidget.pro new file mode 100644 index 000000000..74c1e84e4 --- /dev/null +++ b/examples/osgviewerQtWidget/qosgwidget.pro @@ -0,0 +1,80 @@ +# Adjust these for your build environment. +win32 { + OSGHOME=c:/Program Files/OpenSceneGraph +} else { + OSGHOME=/usr/osg/OpenSceneGraph-2.8.1 + #OUT_OF_SOURCE_POSTFIX= + # OUT_OF_SOURCE_POSTFIX=.build_debug + OUT_OF_SOURCE_POSTFIX=.build_release +} + +D= +# Uncomment to enable debug library linkinig +# CONFIG += debug +# D=d + +#----------------------------------------------------------------------------- +# This is untested... +# Added for Qt-4.5.1 which now requires linking with Frameworks +# /usr/local/Trolltech/Qt-4.5.1/lib/QtGui.framework/QtGui +# +# QMAKE_FLAGS += -F/usr/local/Trolltech/Qt-4.5.1/lib/ +# LIBS += -framework QtGui -framework QtCore + +#----------------------------------------------------------------------------- +TEMPLATE = app +QMAKE_CXXFLAGS_THREAD += -pthread +CONFIG += thread + +#----------------------------------------------------------------------------- +win32 { + cpu_type = win32_x86 +} else { + + cpu_type = $$system(uname -m) + OBJECTS_DIR = $${cpu_type} + TARGET = $${OBJECTS_DIR}/qosgwidget +} + +#----------------------------------------------------------------------------- +win32 { + CONFIG += console + DEPENDPATH += . + INCLUDEPATH += . $$quote("\"$${OSGHOME}/include\"") + +} else { + # Need the extra path to find OpenThreads/Config with out-of-source builds. + INCLUDEPATH += $${OSGHOME}$${OUT_OF_SOURCE_POSTFIX}/include $${OSGHOME}/include +} + +macx { + CONFIG += x86 + # Uncomment to turn off qDebug() messages: + # When uncommented there's a problem with a Frameworks header file??!! + # DEFINES += QT_NO_DEBUG_OUTPUT +} else { + # Comment out to turn on qDebug() messages: + DEFINES += QT_NO_DEBUG_OUTPUT +} + + +FORMS = testMainWin.ui testOutboardWin.ui + +HEADERS = testMainWin.h testOutboardWin.h QOSGWidget.h CompositeViewerQOSG.h + +SOURCES += main.cpp testMainWin.cpp testOutboardWin.cpp QOSGWidget.cpp \ + CompositeViewerQOSG.cpp + +OSG_LIBS= -losgText$${D} -losgGA$${D} -losgFX$${D} \ + -losgDB$${D} -losgUtil$${D} -losg$${D} \ + -lOpenThreads$${D} -losgViewer$${D} \ + +#----------------------------------------------------------------------------- +win32 { + OSG_LIB_DIR=$${OSGHOME}/lib + QMAKE_LFLAGS += /LIBPATH:$$quote("\"$${OSG_LIB_DIR}\"") + LIBS += $${OSG_LIBS} +} else { + LIBS += -L$${OSGHOME}$${OUT_OF_SOURCE_POSTFIX}/lib $${OSG_LIBS} +} + diff --git a/examples/osgviewerQtWidget/testMainWin.cpp b/examples/osgviewerQtWidget/testMainWin.cpp new file mode 100644 index 000000000..5e7d677fa --- /dev/null +++ b/examples/osgviewerQtWidget/testMainWin.cpp @@ -0,0 +1,11 @@ +#include "testMainWin.h" + +testMainWin::testMainWin() + : QMainWindow() +{ + ui.setupUi( this ); + connect ( ui.actionExit, SIGNAL(activated(void)), this, SLOT(close()) ); + +} + + diff --git a/examples/osgviewerQtWidget/testMainWin.h b/examples/osgviewerQtWidget/testMainWin.h new file mode 100644 index 000000000..56ab55705 --- /dev/null +++ b/examples/osgviewerQtWidget/testMainWin.h @@ -0,0 +1,18 @@ +#ifndef TESTMAINWIN_HPP_ +#define TESTMAINWIN_HPP_ + +#include "ui_testMainWin.h" + +class testMainWin : public QMainWindow // QWidget +{ + Q_OBJECT + +public: + testMainWin(); + +public: // for now + Ui::testMainWin ui; +}; + + +#endif // TESTMAINWIN_HPP_ diff --git a/examples/osgviewerQtWidget/testMainWin.ui b/examples/osgviewerQtWidget/testMainWin.ui new file mode 100644 index 000000000..5338a92a6 --- /dev/null +++ b/examples/osgviewerQtWidget/testMainWin.ui @@ -0,0 +1,108 @@ + + testMainWin + + + + 0 + 0 + 621 + 547 + + + + MainWindow + + + + + + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + + 0 + 0 + 621 + 28 + + + + + Main + + + + + + + + + toolBar + + + LeftToolBarArea + + + false + + + + + toolBar_2 + + + TopToolBarArea + + + false + + + + + Exit + + + + + + CompositeViewerQOSG + QWidget +
CompositeViewerQOSG.h
+ 1 +
+
+ + graphicsView1 + graphicsView2 + graphicsView3 + graphicsView4 + + + +
diff --git a/examples/osgviewerQtWidget/testOutboardWin.cpp b/examples/osgviewerQtWidget/testOutboardWin.cpp new file mode 100644 index 000000000..2e5c160ce --- /dev/null +++ b/examples/osgviewerQtWidget/testOutboardWin.cpp @@ -0,0 +1,12 @@ +#include "testOutboardWin.h" + +testOutboardWin::testOutboardWin(QWidget *parent) + : QDialog( parent ) +{ + ui.setupUi( this ); +} + +QWidget * testOutboardWin::getDrawingAreaWidget(void) +{ + return ui.graphicsView; +} diff --git a/examples/osgviewerQtWidget/testOutboardWin.h b/examples/osgviewerQtWidget/testOutboardWin.h new file mode 100644 index 000000000..53e0353c8 --- /dev/null +++ b/examples/osgviewerQtWidget/testOutboardWin.h @@ -0,0 +1,20 @@ +#ifndef TESTPLOTWIN_HPP_ +#define TESTPLOTWIN_HPP_ + +#include "ui_testOutboardWin.h" + +class testOutboardWin : public QDialog +{ + Q_OBJECT + +public: + testOutboardWin( QWidget *parent = 0 ); + QWidget *getDrawingAreaWidget(); + +private: + Ui::testOutboardWindow ui; + +}; + + +#endif // TESTOUTBOARDWIN_HPP_ diff --git a/examples/osgviewerQtWidget/testOutboardWin.ui b/examples/osgviewerQtWidget/testOutboardWin.ui new file mode 100644 index 000000000..213d97f91 --- /dev/null +++ b/examples/osgviewerQtWidget/testOutboardWin.ui @@ -0,0 +1,38 @@ + + testOutboardWindow + + + + 0 + 0 + 278 + 292 + + + + The Outboard Window + + + + + + + 100 + 100 + + + + + + + + + ViewQOSG + QWidget +
QOSGWidget.h
+ 1 +
+
+ + +