| Line |
Branch |
Exec |
Source |
| 1 |
|
|
// Copyright (c) 2015-2018, LAAS-CNRS |
| 2 |
|
|
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) |
| 3 |
|
|
// |
| 4 |
|
|
// This file is part of gepetto-viewer. |
| 5 |
|
|
// gepetto-viewer is free software: you can redistribute it |
| 6 |
|
|
// and/or modify it under the terms of the GNU Lesser General Public |
| 7 |
|
|
// License as published by the Free Software Foundation, either version |
| 8 |
|
|
// 3 of the License, or (at your option) any later version. |
| 9 |
|
|
// |
| 10 |
|
|
// gepetto-viewer is distributed in the hope that it will be |
| 11 |
|
|
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| 12 |
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 |
|
|
// General Lesser Public License for more details. You should have |
| 14 |
|
|
// received a copy of the GNU Lesser General Public License along with |
| 15 |
|
|
// gepetto-viewer. If not, see <http://www.gnu.org/licenses/>. |
| 16 |
|
|
|
| 17 |
|
|
#include "gepetto/gui/windows-manager.hh" |
| 18 |
|
|
|
| 19 |
|
|
#include <gepetto/viewer/group-node.h> |
| 20 |
|
|
#include <gepetto/viewer/window-manager.h> |
| 21 |
|
|
|
| 22 |
|
|
#include <gepetto/gui/bodytreewidget.hh> |
| 23 |
|
|
|
| 24 |
|
|
#include "gepetto/gui/mainwindow.hh" |
| 25 |
|
|
#include "gepetto/gui/osgwidget.hh" |
| 26 |
|
|
#include "gepetto/gui/tree-item.hh" |
| 27 |
|
|
|
| 28 |
|
|
namespace gepetto { |
| 29 |
|
|
namespace gui { |
| 30 |
|
|
using viewer::ScopedLock; |
| 31 |
|
|
|
| 32 |
|
✗ |
Qt::ConnectionType connectionType(QObject* o, int blocking) { |
| 33 |
|
✗ |
if (o->thread() == QThread::currentThread()) |
| 34 |
|
✗ |
return Qt::DirectConnection; |
| 35 |
|
✗ |
else if (blocking) |
| 36 |
|
✗ |
return Qt::BlockingQueuedConnection; |
| 37 |
|
|
else |
| 38 |
|
✗ |
return Qt::QueuedConnection; |
| 39 |
|
|
} |
| 40 |
|
|
|
| 41 |
|
✗ |
WindowsManagerPtr_t WindowsManager::create(BodyTreeWidget* bodyTree) { |
| 42 |
|
✗ |
return WindowsManagerPtr_t(new WindowsManager(bodyTree)); |
| 43 |
|
|
} |
| 44 |
|
|
|
| 45 |
|
✗ |
WindowsManager::WindowID WindowsManager::createWindow(QString windowName) { |
| 46 |
|
✗ |
return createWindow(windowName.toStdString()); |
| 47 |
|
|
} |
| 48 |
|
|
|
| 49 |
|
✗ |
WindowsManager::WindowID WindowsManager::createWindow( |
| 50 |
|
|
const std::string& windowName) { |
| 51 |
|
✗ |
if (getWindowManager(windowName, false)) return windowName; |
| 52 |
|
✗ |
MainWindow* main = MainWindow::instance(); |
| 53 |
|
|
OSGWidget* widget; |
| 54 |
|
✗ |
QMetaObject::invokeMethod(main, "createView", connectionType(main, true), |
| 55 |
|
✗ |
Q_RETURN_ARG(OSGWidget*, widget), |
| 56 |
|
✗ |
Q_ARG(std::string, windowName)); |
| 57 |
|
✗ |
return widget->windowID(); |
| 58 |
|
|
} |
| 59 |
|
|
|
| 60 |
|
✗ |
WindowsManager::WindowID WindowsManager::createWindow( |
| 61 |
|
|
const std::string& windowName, gepetto::gui::OSGWidget* widget, |
| 62 |
|
|
osgViewer::Viewer* viewer, osg::GraphicsContext* gc) { |
| 63 |
|
✗ |
if (getWindowManager(windowName, false)) return windowName; |
| 64 |
|
|
viewer::WindowManagerPtr_t newWindow = |
| 65 |
|
✗ |
viewer::WindowManager::create(viewer, gc); |
| 66 |
|
✗ |
WindowID windowId = addWindow(windowName, newWindow); |
| 67 |
|
✗ |
assert(windowId == windowName); |
| 68 |
|
✗ |
widgets_.insert(std::make_pair(windowId, widget)); |
| 69 |
|
✗ |
return windowId; |
| 70 |
|
|
} |
| 71 |
|
|
|
| 72 |
|
✗ |
WindowsManager::WindowsManager(BodyTreeWidget* bodyTree) |
| 73 |
|
✗ |
: Parent_t(), bodyTree_(bodyTree), refreshIsSynchronous_(false) {} |
| 74 |
|
|
|
| 75 |
|
✗ |
void WindowsManager::addNode(const std::string& nodeName, NodePtr_t node, |
| 76 |
|
|
GroupNodePtr_t parent) { |
| 77 |
|
✗ |
Parent_t::addNode(nodeName, node, parent); |
| 78 |
|
✗ |
if (parent) initParent(node, parent, false); |
| 79 |
|
|
} |
| 80 |
|
|
|
| 81 |
|
✗ |
void WindowsManager::insertNode(const std::string& nodeName, NodePtr_t node) { |
| 82 |
|
✗ |
ScopedLock lock(osgFrameMutex()); |
| 83 |
|
✗ |
addNode(nodeName, node, GroupNodePtr_t()); |
| 84 |
|
|
} |
| 85 |
|
|
|
| 86 |
|
✗ |
void WindowsManager::addGroup(const std::string& groupName, |
| 87 |
|
|
GroupNodePtr_t group, GroupNodePtr_t parent) { |
| 88 |
|
✗ |
Parent_t::addGroup(groupName, group, parent); |
| 89 |
|
✗ |
if (!parent || !initParent(group, parent, true)) { |
| 90 |
|
|
// Consider it a root group |
| 91 |
|
✗ |
BodyTreeItem* bti = new BodyTreeItem(NULL, group); |
| 92 |
|
✗ |
nodeItemMap_[groupName].first.push_back(bti); |
| 93 |
|
✗ |
nodeItemMap_[groupName].second = true; |
| 94 |
|
✗ |
if (bti->thread() != bodyTree_->thread()) |
| 95 |
|
✗ |
bti->moveToThread(bodyTree_->thread()); |
| 96 |
|
✗ |
bti->initialize(); |
| 97 |
|
✗ |
bodyTree_->model()->appendRow(bti); |
| 98 |
|
|
} |
| 99 |
|
|
} |
| 100 |
|
|
|
| 101 |
|
✗ |
void WindowsManager::addToGroup(const std::string& nodeName, |
| 102 |
|
|
const std::string& groupName, |
| 103 |
|
|
const NodePtr_t& node, |
| 104 |
|
|
const BodyTreeItems_t& groups, bool isGroup) { |
| 105 |
|
✗ |
for (std::size_t i = 0; i < groups.size(); ++i) { |
| 106 |
|
✗ |
BodyTreeItem* bti = new BodyTreeItem(NULL, node); |
| 107 |
|
✗ |
nodeItemMap_[nodeName].first.push_back(bti); |
| 108 |
|
✗ |
nodeItemMap_[nodeName].second = isGroup; |
| 109 |
|
✗ |
bti->setParentGroup(groupName); |
| 110 |
|
✗ |
if (bti->thread() != bodyTree_->thread()) |
| 111 |
|
✗ |
bti->moveToThread(bodyTree_->thread()); |
| 112 |
|
✗ |
bti->initialize(); |
| 113 |
|
✗ |
groups[i]->appendRow(bti); |
| 114 |
|
|
} |
| 115 |
|
|
} |
| 116 |
|
|
|
| 117 |
|
✗ |
bool WindowsManager::addToGroup(const std::string& nodeName, |
| 118 |
|
|
const std::string& groupName) { |
| 119 |
|
✗ |
if (Parent_t::addToGroup(nodeName, groupName)) { |
| 120 |
|
✗ |
NodePtr_t node = getNode(nodeName, false); |
| 121 |
|
✗ |
bool isGroup = true; |
| 122 |
|
|
try { |
| 123 |
|
✗ |
getGroup(nodeName, true); |
| 124 |
|
✗ |
} catch (const std::invalid_argument& exc) { |
| 125 |
|
✗ |
isGroup = false; |
| 126 |
|
|
} |
| 127 |
|
✗ |
assert(node); |
| 128 |
|
✗ |
BodyTreeItemMap_t::const_iterator _groups = nodeItemMap_.find(groupName); |
| 129 |
|
✗ |
assert(_groups != nodeItemMap_.end()); |
| 130 |
|
✗ |
assert(!_groups->second.first.empty()); |
| 131 |
|
✗ |
assert(_groups->second.second); |
| 132 |
|
✗ |
assert(getGroup(groupName)); |
| 133 |
|
✗ |
addToGroup(nodeName, groupName, node, _groups->second.first, isGroup); |
| 134 |
|
✗ |
return true; |
| 135 |
|
|
} |
| 136 |
|
✗ |
return false; |
| 137 |
|
|
} |
| 138 |
|
|
|
| 139 |
|
✗ |
bool WindowsManager::removeFromGroup(const std::string& nodeName, |
| 140 |
|
|
const std::string& groupName) { |
| 141 |
|
✗ |
bool ret = Parent_t::removeFromGroup(nodeName, groupName); |
| 142 |
|
✗ |
if (ret) { |
| 143 |
|
✗ |
BodyTreeItemMap_t::iterator _nodes = nodeItemMap_.find(nodeName); |
| 144 |
|
✗ |
BodyTreeItemMap_t::iterator _groups = nodeItemMap_.find(groupName); |
| 145 |
|
✗ |
assert(_nodes != nodeItemMap_.end()); |
| 146 |
|
✗ |
assert(_groups != nodeItemMap_.end()); |
| 147 |
|
✗ |
BodyTreeItems_t& nodes = _nodes->second.first; |
| 148 |
|
✗ |
const BodyTreeItems_t& groups = _groups->second.first; |
| 149 |
|
✗ |
bool found = false; |
| 150 |
|
✗ |
for (BodyTreeItems_t::iterator _node = nodes.begin(); _node != nodes.end(); |
| 151 |
|
✗ |
++_node) { |
| 152 |
|
✗ |
BodyTreeItems_t::const_iterator _group = std::find( |
| 153 |
|
✗ |
groups.begin(), groups.end(), (*_node)->QStandardItem::parent()); |
| 154 |
|
✗ |
if (_group == groups.end()) continue; |
| 155 |
|
✗ |
bodyTree_->model()->takeRow((*_node)->row()); |
| 156 |
|
✗ |
nodes.erase(_node); |
| 157 |
|
✗ |
found = true; |
| 158 |
|
✗ |
break; |
| 159 |
|
|
} |
| 160 |
|
✗ |
if (!found) |
| 161 |
|
✗ |
qDebug() << "Could not find body tree item parent" << groupName.c_str() |
| 162 |
|
|
<< "of" << nodeName.c_str(); |
| 163 |
|
|
} |
| 164 |
|
✗ |
return ret; |
| 165 |
|
|
} |
| 166 |
|
|
|
| 167 |
|
✗ |
bool WindowsManager::deleteNode(const std::string& nodeName, bool all) { |
| 168 |
|
✗ |
bool ret = Parent_t::deleteNode(nodeName, all); |
| 169 |
|
✗ |
if (ret) deleteBodyItem(nodeName); |
| 170 |
|
✗ |
return ret; |
| 171 |
|
|
} |
| 172 |
|
|
|
| 173 |
|
✗ |
BodyTreeItems_t WindowsManager::bodyTreeItems(const std::string& name) const { |
| 174 |
|
✗ |
BodyTreeItemMap_t::const_iterator _btis = nodeItemMap_.find(name); |
| 175 |
|
✗ |
if (_btis != nodeItemMap_.end()) return _btis->second.first; |
| 176 |
|
✗ |
return BodyTreeItems_t(); |
| 177 |
|
|
} |
| 178 |
|
|
|
| 179 |
|
✗ |
void WindowsManager::deleteBodyItem(const std::string& nodeName) { |
| 180 |
|
✗ |
BodyTreeItemMap_t::iterator _nodes = nodeItemMap_.find(nodeName); |
| 181 |
|
✗ |
assert(_nodes != nodeItemMap_.end()); |
| 182 |
|
✗ |
for (std::size_t i = 0; i < _nodes->second.first.size(); ++i) { |
| 183 |
|
✗ |
BodyTreeItem* bti = _nodes->second.first[i]; |
| 184 |
|
✗ |
QStandardItem* parent = bti->QStandardItem::parent(); |
| 185 |
|
✗ |
if (parent == NULL) { |
| 186 |
|
✗ |
bodyTree_->model()->takeRow(bti->row()); |
| 187 |
|
|
} else { |
| 188 |
|
✗ |
parent->takeRow(bti->row()); |
| 189 |
|
|
} |
| 190 |
|
✗ |
bti->deleteLater(); |
| 191 |
|
✗ |
_nodes->second.first[i] = NULL; |
| 192 |
|
|
} |
| 193 |
|
✗ |
nodeItemMap_.erase(_nodes); |
| 194 |
|
|
} |
| 195 |
|
|
|
| 196 |
|
✗ |
bool WindowsManager::initParent(NodePtr_t node, GroupNodePtr_t parent, |
| 197 |
|
|
bool isGroup) { |
| 198 |
|
|
BodyTreeItemMap_t::const_iterator _groups = |
| 199 |
|
✗ |
nodeItemMap_.find(parent->getID()); |
| 200 |
|
✗ |
if (_groups != nodeItemMap_.end() && _groups->second.second) { |
| 201 |
|
✗ |
assert(!_groups->second.first.empty()); |
| 202 |
|
✗ |
addToGroup(node->getID(), parent->getID(), node, _groups->second.first, |
| 203 |
|
|
isGroup); |
| 204 |
|
✗ |
return true; |
| 205 |
|
|
} |
| 206 |
|
✗ |
return false; |
| 207 |
|
|
} |
| 208 |
|
|
|
| 209 |
|
✗ |
void WindowsManager::captureFrame(const WindowID wid, |
| 210 |
|
|
const std::string& filename) { |
| 211 |
|
✗ |
WindowManagerPtr_t wm = getWindowManager(wid, true); |
| 212 |
|
✗ |
OSGWidget* widget = widgets_[wid]; |
| 213 |
|
✗ |
assert(widget->windowID() == wid); |
| 214 |
|
|
// Here, it is not requred that invokeMethod is blocking. However, it may |
| 215 |
|
|
// be suprising in user script to have this call done later... |
| 216 |
|
✗ |
QMetaObject::invokeMethod(widget, "captureFrame", |
| 217 |
|
|
connectionType(widget, true), |
| 218 |
|
✗ |
Q_ARG(std::string, filename)); |
| 219 |
|
|
} |
| 220 |
|
|
|
| 221 |
|
✗ |
bool WindowsManager::startCapture(const WindowID wid, |
| 222 |
|
|
const std::string& filename, |
| 223 |
|
|
const std::string& extension) { |
| 224 |
|
✗ |
WindowManagerPtr_t wm = getWindowManager(wid, true); |
| 225 |
|
✗ |
OSGWidget* widget = widgets_[wid]; |
| 226 |
|
✗ |
assert(widget->windowID() == wid); |
| 227 |
|
|
bool res; |
| 228 |
|
✗ |
QMetaObject::invokeMethod( |
| 229 |
|
|
widget, "startCapture", connectionType(widget, true), |
| 230 |
|
✗ |
Q_RETURN_ARG(bool, res), Q_ARG(std::string, filename), |
| 231 |
|
✗ |
Q_ARG(std::string, extension)); |
| 232 |
|
✗ |
return res; |
| 233 |
|
|
} |
| 234 |
|
|
|
| 235 |
|
✗ |
bool WindowsManager::stopCapture(const WindowID wid) { |
| 236 |
|
✗ |
WindowManagerPtr_t wm = getWindowManager(wid, true); |
| 237 |
|
✗ |
OSGWidget* widget = widgets_[wid]; |
| 238 |
|
✗ |
assert(widget->windowID() == wid); |
| 239 |
|
|
bool res; |
| 240 |
|
✗ |
QMetaObject::invokeMethod(widget, "stopCapture", connectionType(widget, true), |
| 241 |
|
✗ |
Q_RETURN_ARG(bool, res)); |
| 242 |
|
✗ |
return res; |
| 243 |
|
|
} |
| 244 |
|
|
|
| 245 |
|
|
struct ApplyConfigurationFunctor { |
| 246 |
|
✗ |
void operator()(const viewer::NodeConfiguration& nc) const { |
| 247 |
|
✗ |
nc.node->applyConfiguration(nc); |
| 248 |
|
|
} |
| 249 |
|
|
}; |
| 250 |
|
|
|
| 251 |
|
✗ |
void WindowsManager::refresh() { |
| 252 |
|
✗ |
if (refreshIsSynchronous_) { |
| 253 |
|
|
{ |
| 254 |
|
✗ |
ScopedLock lock(configListMtx_); |
| 255 |
|
|
{ |
| 256 |
|
✗ |
ScopedLock lock(osgFrameMutex()); |
| 257 |
|
|
// refresh scene with the new configuration |
| 258 |
|
✗ |
std::for_each(newNodeConfigurations_.begin(), |
| 259 |
|
|
newNodeConfigurations_.end(), |
| 260 |
|
|
ApplyConfigurationFunctor()); |
| 261 |
|
|
} |
| 262 |
|
✗ |
newNodeConfigurations_.resize(0); |
| 263 |
|
|
} |
| 264 |
|
✗ |
if (autoCaptureTransform_) captureTransform(); |
| 265 |
|
|
} else { |
| 266 |
|
|
{ |
| 267 |
|
✗ |
ScopedLock lock1(configsAsyncMtx_); |
| 268 |
|
✗ |
ScopedLock lock2(configListMtx_); |
| 269 |
|
✗ |
if (configsAsync_.size() == 0) { |
| 270 |
|
✗ |
configsAsync_.swap(newNodeConfigurations_); |
| 271 |
|
✗ |
QMetaObject::invokeMethod(this, "asyncRefresh", Qt::QueuedConnection); |
| 272 |
|
|
} else { |
| 273 |
|
✗ |
configsAsync_.insert(configsAsync_.end(), |
| 274 |
|
|
newNodeConfigurations_.begin(), |
| 275 |
|
|
newNodeConfigurations_.end()); |
| 276 |
|
✗ |
newNodeConfigurations_.resize(0); |
| 277 |
|
|
// No need to reinvoke asyncRefresh as it hasn't been ran yet. |
| 278 |
|
|
} |
| 279 |
|
|
} |
| 280 |
|
|
} |
| 281 |
|
|
} |
| 282 |
|
|
|
| 283 |
|
✗ |
void WindowsManager::asyncRefresh() { |
| 284 |
|
✗ |
NodeConfigurations_t& cfgs = configsAsync_; |
| 285 |
|
|
{ |
| 286 |
|
✗ |
ScopedLock lock(configsAsyncMtx_); |
| 287 |
|
|
{ |
| 288 |
|
✗ |
ScopedLock lock(osgFrameMutex()); |
| 289 |
|
|
// refresh scene with the new configuration |
| 290 |
|
✗ |
std::for_each(cfgs.begin(), cfgs.end(), ApplyConfigurationFunctor()); |
| 291 |
|
|
} |
| 292 |
|
✗ |
cfgs.resize(0); |
| 293 |
|
|
} |
| 294 |
|
✗ |
if (autoCaptureTransform_) captureTransform(); |
| 295 |
|
|
} |
| 296 |
|
|
|
| 297 |
|
✗ |
void WindowsManager::setRefreshIsSynchronous(bool synchonous) { |
| 298 |
|
✗ |
refreshIsSynchronous_ = synchonous; |
| 299 |
|
|
} |
| 300 |
|
|
} // namespace gui |
| 301 |
|
|
} // namespace gepetto |
| 302 |
|
|
|