問題描述:
在OSG的事件處理GUIEventHandler中,當我們需要操作開啟一個模態對話方塊(非模態的對話方塊並沒有問題)的時候,在關閉模態對話方塊之後導致後續的事件處理異常。
本文以osgQt為例,描述這一問題的出現以及解決方式。程式很簡單,在視窗中點擊右鍵彈出對話方塊,載入選擇的模型。代碼如下:
class ViewerWidget : public QWidget, public osgViewer::Viewer{Q_OBJECTpublic:ViewerWidget(QWidget* parent = 0, Qt::WindowFlags f = 0): QWidget(parent, f){setThreadingModel(ViewerBase::SingleThreaded);QWidget* renderWidget = createViewWidget(createGraphicsWindow(0, 0, 100, 100), nullptr);QGridLayout* grid = new QGridLayout;grid->addWidget(renderWidget, 0, 0);grid->setMargin(1);setLayout(grid);_testHandler = new TestHandler();this->addEventHandler(_testHandler);connect(_testHandler, SIGNAL(rightButtonClickedSignal()), this, SLOT(rightButtonClickedSlot()));connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));_timer.start(10);}QWidget* createViewWidget(osgQt::GraphicsWindowQt* gw, osg::Node* scene){osg::Camera* camera = this->getCamera();camera->setGraphicsContext(gw);const osg::GraphicsContext::Traits* traits = gw->getTraits();camera->setClearColor(osg::Vec4(0.2, 0.2, 0.4, 1.0));camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));camera->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width) / static_cast<double>(traits->height), 1.0f, 10000.0f);this->setSceneData(scene);this->setCameraManipulator(new osgGA::TrackballManipulator());return gw->getGLWidget();}osgQt::GraphicsWindowQt* createGraphicsWindow(int x, int y, int w, int h, const std::string& name = "", bool windowDecoration = false){osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();osg::ref_ptr<osg::GraphicsContext::Traits> 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();return new osgQt::GraphicsWindowQt(traits.get());}virtual void paintEvent(QPaintEvent* event){frame();}public slots:bool rightButtonClickedSlot();protected:QTimer _timer;TestHandler* _testHandler;};
該類中添加的TestHandler的代碼如下:
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa){switch (ea.getEventType()){case osgGA::GUIEventAdapter::PUSH:{if (ea.getButton() == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON){ QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"),"/home",tr("Models (*.osg *.osgb *.osgt)")); if (!fileName.isEmpty()) { QByteArray fileByteArray = fileName.toLocal8Bit(); std::string fileNameStdString = fileByteArray.constData(); osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa); if (viewer) { viewer->setSceneData(osgDB::readNodeFile(fileNameStdString)); } }}return false;}break;case osgGA::GUIEventAdapter::DRAG:{if (ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON){return false;}else if (ea.getButtonMask() == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON){return false;}return false;}case osgGA::GUIEventAdapter::MOVE:{return false;}default:break;}return false;}
運行程式,在點擊右鍵之後彈出開啟檔案的對話方塊,選擇osg的檔案之後,發現情境載入進來之後滑鼠在移動的時候觸發了右鍵的Drag事件,導致模型一直在縮放中。
滑鼠的Move事件被解析為了右鍵的Drag事件(感覺上是右鍵一直在被按下的效果)。 解決方式:
出現的原因暫時未知,但是感覺上是OSG的事件封裝和Qt的事件出現了衝突,於是將彈窗放到Qt的事件中處理:
讓TestEventHandler繼承自QObject,並將表單的事件處理器安裝在EventTest上,在事件處理器中處理右鍵點擊開啟檔案:
class TestHandler : public QObject, public osgGA::GUIEventHandler{Q_OBJECTpublic:TestHandler(QWidget *w){ _widget = w;}~TestHandler(){ }bool eventFilter(QObject *obj, QEvent *event){if (event->type() == QEvent::MouseButtonPress){QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);if (mouseEvent->button() == Qt::RightButton){QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"),"/home",tr("Models (*.osg *.osgb *.osgt)"));if (!fileName.isEmpty()){QByteArray fileByteArray = fileName.toLocal8Bit();std::string fileNameStdString = fileByteArray.constData();osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(_widget);if (viewer){viewer->setSceneData(osgDB::readNodeFile(fileNameStdString));}}return true;}else{return QObject::eventFilter(obj, event);}}else {return QObject::eventFilter(obj, event);}}bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa){switch (ea.getEventType()){case osgGA::GUIEventAdapter::PUSH:{//if (ea.getButton() == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON)//{//}return false;}break;case osgGA::GUIEventAdapter::DRAG:{if (ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON){return false;}else if (ea.getButtonMask() == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON){return false;}return false;}case osgGA::GUIEventAdapter::MOVE:{return false;}default:break;}return false;}signals:bool rightButtonClickedSignal();protected:QWidget* _widget;};
同時在主視窗的建構函式中安裝這個事件處理器
_testHandler = new TestHandler(this);this->addEventHandler(_testHandler);renderWidget->installEventFilter(_testHandler);
修改代碼之後再次運行程式,右鍵調出對話方塊選擇檔案載入之後,一切正常了。
本文提供了一種處理的思路,其實也可以不用事件過濾這種方式來做,直接在主表單的事件中處理即可。只是想說明這個問題的處理方式是讓彈窗的處理在Qt中進行。這可能是OSG的一個事件封裝的Bug。