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 |
|
|
|