问题描述:
在OSG的事件处理GUIEventHandler中,当我们需要操作打开一个模态对话框(非模态的对话框并没有问题)的时候,在关闭模态对话框之后导致后续的事件处理异常。
本文以osgQt为例,描述这一问题的出现以及解决方式。程序很简单,在窗口中点击右键弹出对话框,加载选择的模型。代码如下:
class ViewerWidget : public QWidget, public osgViewer::Viewer
{
Q_OBJECT
public:
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_OBJECT
public:
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。