鼠标事件处理"/>
【librviz源码解读】Tool类的添加和鼠标事件处理
文章目录
- 前言
- 1 Tool类的添加和鼠标事件处理
- 1.1 已注册工具类的添加
- 1.2 鼠标事件的传递和处理
- 1.3 实现工具类的无注册调用
- 总结
前言
在使用rviz库编写自己的工具的时候,需要使用这样的语句,将工具注册到pluginlib。
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(robot_upper_plugins::RouteGoalTool, rviz::Tool )
需要启用工具时,需要用注册时的名称来添加到tool_manager,类似于:
addTool("robot_upper_plugins/RouteGoalTool");
让我们来看看librviz是怎么从插件库中加载和启用工具类的。
1 Tool类的添加和鼠标事件处理
1.1 已注册工具类的添加
工具类添加到插件库后,当我们在rviz中点击添加工具,对应到代码里,就是调用了ToolManager类的addTool方法,用插件库内的唯一命名来创建工具,像这样:
void ToolManager::initialize()
{// 这些都是rviz中的默认插件addTool( "rviz/MoveCamera" );addTool( "rviz/Interact" );addTool( "rviz/Select" );addTool( "rviz/SetInitialPose" );addTool( "rviz/SetGoal" );
}
在visualization_manager.cpp内:
当VisualizationManager实例化ToolManager后,成为其上下文;
tool_manager_ = new ToolManager( this );
在ToolManager.cpp内:
addTool():根据插件class_id从工厂创建一个tool,将VisualizationManager作为context传入tool->initialize()。最后发出toolAdded(tool)信号;
ToolManager::ToolManager( DisplayContext* context ) : context_( context ) {}Tool* ToolManager::addTool( const QString& class_id )
{Tool* tool = factory_->make( class_id, &error );tools_.append( tool );tool->initialize( context_ );Q_EMIT toolAdded( tool );
}
在visualization_frame.cpp内:
addTool( Tool* tool ):toolAdded()的槽,当接收到toolAdded()信号,创建一个qaction到toolbar_actions_组里;
当这个qaction被触发时,调用ToolManager()->setCurrentToo();
connect( tool_man, SIGNAL( toolAdded( Tool* )), this, SLOT( addTool( Tool* )));void VisualizationFrame::addTool( Tool* tool )
{QAction* action = new QAction( tool->getName(), toolbar_actions_ );toolbar_->insertAction( add_tool_action_, action );tool_to_action_map_[ tool ] = action;
}toolbar_actions_ = new QActionGroup( this );
connect( toolbar_actions_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarActionTriggered( QAction* )));void VisualizationFrame::onToolbarActionTriggered( QAction* action )
{Tool* tool = action_to_tool_map_[ action ];manager_->getToolManager()->setCurrentTool( tool );
}
在ToolManager.cpp内:
setCurrentTool( Tool* tool ):设置当前工具后,发出toolChanged信号通知VisualizationManager;
void ToolManager::setCurrentTool( Tool* tool )
{ ...Q_EMIT toolChanged( current_tool_ );
}
在visualization_manager.cpp内:
connect( tool_manager_, SIGNAL( toolChanged( Tool* ) ), this, SLOT( onToolChanged( Tool* ) ));
void VisualizationManager::onToolChanged( Tool* tool ) {} /* 然鹅未启用 */
1.2 鼠标事件的传递和处理
我们知道,rviz::Tool类中有一个processMouseEvent( ViewportMouseEvent& event ),用于处理工具的鼠标事件,这是Tool类的核心功能,让我们来看一下它是怎么被调用的。
在render_panel.cpp中:
fake_mouse_move_event_timer_:定时器,定时触发sendMouseMoveEvent();
sendMouseMoveEvent():创建虚拟的鼠标事件fake_event并作为参数调用onRenderWindowMouseEvents( &fake_event ),这也就是按照一定周期(33ms)发送鼠标事件;
onRenderWindowMouseEvents( QMouseEvent* event ):获取鼠标的当前位置,创建ViewportMouseEvent,传递给上下文(就是VisualizationManager)的handleMouseEvent( QMouseEvent* event )进行处理;
connect( fake_mouse_move_event_timer_, SIGNAL( timeout() ), this, SLOT( sendMouseMoveEvent() ));
fake_mouse_move_event_timer_->start( 33 /*milliseconds*/ );void RenderPanel::sendMouseMoveEvent()
{...QMouseEvent fake_event( QEvent::MouseMove,mouse_rel_widget,QApplication::mouseButtons(),QApplication::keyboardModifiers() );onRenderWindowMouseEvents( &fake_event );
}void RenderPanel::onRenderWindowMouseEvents( QMouseEvent* event )
{int last_x = mouse_x_;int last_y = mouse_y_;if (context_){setFocus( Qt::MouseFocusReason );ViewportMouseEvent vme(this, getViewport(), event, last_x, last_y);context_->handleMouseEvent(vme);event->accept();}...}
在visualization_manager.cpp中:
handleMouseEvent( vme ):从ToolManager获取当前工具,调用current_tool->processMouseEvent( _vme );(终于找到你了!)
void VisualizationManager::handleMouseEvent( const ViewportMouseEvent& vme )
{//process pending mouse eventsTool* current_tool = tool_manager_->getCurrentTool();if( current_tool ){ViewportMouseEvent _vme = vme;flags = current_tool->processMouseEvent( _vme );vme.panel->setCursor( current_tool->getCursor() );}...
}
1.3 实现工具类的无注册调用
上面我们了解了已注册的工具类如何加载和调用,那么能不能在程序中引入插件类的头文件,实现我们自己实例化的插件类的添加和调用呢,当然是可以的,而且基于librviz优秀的代码设计,这实现起来很容易。
在tool.h中:
虚函数,提供给子类重新实现,实现多样的鼠标事件处理。
virtual int processMouseEvent( ViewportMouseEvent& event ) { return 0; }
举个例子,在move_tool.cpp中:
int MoveTool::processKeyEvent( QKeyEvent* event, RenderPanel* panel )
{if( context_->getViewManager()->getCurrent() ){context_->getViewManager()->getCurrent()->handleKeyEvent( event, panel );}return Render;
}
自己创建不被注册的工具类,想要其被启用,并且处理鼠标事件,关键在于这两点:
- Tool::initialize( VisualizationManager ); // 实例化你的工具类,调用initialize函数,把render_panel的VisualizationManager传进去。
- ToolManager::setCurrentTool( Tool ); // 设置为ToolManager的当前工具后,VisualizationManager便可以传递鼠标事件给工具了,让它进行处理了!
举个例子:
void initialize()
{// 创建3D面板、中央管理器和tool管理器render_panel_ = new rviz::RenderPanel();visualization_manager_ = new rviz::VisualizationManager(render_panel_);tool_manager_ = visualization_manager_->getToolManager();render_panel_->initialize(visualization_manager_->getSceneManager(), visualization_manager_);visualization_manager_->initialize();visualization_manager_->startUpdate();// 初始化工具 route_goal_tool_ = new rviz_plugins::RouteGoalTool();route_goal_tool_->initialize(visualization_manager_);
}// 启动工具
void startTool()
{tool_manager_->setCurrentTool(route_goal_tool_);
}
当然,这样虽然可以实现,但也有很明显的缺点,ToolManager::addTool()中除了创建工具,还会维护tools_和shortkey_to_tool_map_列表,一些操作如删除工具、属性更改等都是依赖tools_列表实现,快捷键映射则依赖于shortkey_to_tool_map_列表。而没有注册的插件需要自己管理,容易造成混乱。
Tool* ToolManager::addTool( const QString& class_id )
{Tool* tool = factory_->make( class_id, &error );tools_.append( tool );tool->setName( addSpaceToCamelCase( factory_->getClassName( class_id )));tool->setIcon( factory_->getIcon( class_id ) );tool->initialize( context_ );if( tool->getShortcutKey() != '\0' ){uint key;QString str = QString( tool->getShortcutKey() );if( toKey( str, key ) ){shortkey_to_tool_map_[ key ] = tool;}}Property* container = tool->getPropertyContainer();connect( container, SIGNAL( childListChanged( Property* )), this, SLOT( updatePropertyVisibility( Property* )));updatePropertyVisibility( container );Q_EMIT configChanged();return tool;
}
总结
第一次比较深入地去看源码,希望有一天也能写出这样的好代码~
更多推荐
【librviz源码解读】Tool类的添加和鼠标事件处理
发布评论