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/mainwindow.hh" |
18 |
|
|
|
19 |
|
|
#include <QMessageBox> |
20 |
|
|
#include <QScrollBar> |
21 |
|
|
#include <QtGlobal> |
22 |
|
|
#include <sstream> |
23 |
|
|
|
24 |
|
|
#include "ui_mainwindow.h" |
25 |
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) |
26 |
|
|
#include <QtConcurrent> |
27 |
|
|
#endif |
28 |
|
|
|
29 |
|
|
#include <gepetto/gui/config-dep.hh> |
30 |
|
|
#include <osg/Version> |
31 |
|
|
|
32 |
|
|
#include "gepetto/gui/action-search-bar.hh" |
33 |
|
|
#include "gepetto/gui/dialog/dialogloadenvironment.hh" |
34 |
|
|
#include "gepetto/gui/dialog/dialogloadrobot.hh" |
35 |
|
|
#include "gepetto/gui/node-action.hh" |
36 |
|
|
#include "gepetto/gui/osgwidget.hh" |
37 |
|
|
#include "gepetto/gui/plugin-interface.hh" |
38 |
|
|
#include "gepetto/gui/selection-handler.hh" |
39 |
|
|
#include "gepetto/gui/shortcut-factory.hh" |
40 |
|
|
#include "gepetto/gui/tree-item.hh" |
41 |
|
|
#include "gepetto/gui/windows-manager.hh" |
42 |
|
|
|
43 |
|
|
#if GEPETTO_GUI_HAS_PYTHONQT |
44 |
|
|
#include <gepetto/gui/pythonwidget.hh> |
45 |
|
|
#endif |
46 |
|
|
|
47 |
|
|
#include "../log.hh" |
48 |
|
|
|
49 |
|
|
namespace gepetto { |
50 |
|
|
namespace gui { |
51 |
|
|
MainWindow* MainWindow::instance_ = NULL; |
52 |
|
|
|
53 |
|
✗ |
MainWindow::MainWindow(Settings* settings, QWidget* parent) |
54 |
|
|
: QMainWindow(parent), |
55 |
|
✗ |
settings_(settings), |
56 |
|
✗ |
ui_(new ::Ui::MainWindow), |
57 |
|
✗ |
centralWidget_(), |
58 |
|
✗ |
osgViewerManagers_(), |
59 |
|
✗ |
worker_(), |
60 |
|
✗ |
actionSearchBar_(new ActionSearchBar(this)) { |
61 |
|
✗ |
MainWindow::instance_ = this; |
62 |
|
✗ |
ui_->setupUi(this); |
63 |
|
|
|
64 |
|
|
// Setup the body tree view |
65 |
|
✗ |
osgViewerManagers_ = WindowsManager::create(ui_->bodyTreeContent); |
66 |
|
✗ |
ui_->bodyTreeContent->init(ui_->bodyTree, ui_->propertyArea); |
67 |
|
|
|
68 |
|
|
// This scene contains elements required for User Interaction. |
69 |
|
✗ |
osg()->createScene("gepetto-gui"); |
70 |
|
|
// TODO remove me. This is kept for backward compatibility |
71 |
|
✗ |
osg()->createScene("hpp-gui"); |
72 |
|
|
|
73 |
|
|
// Setup the main OSG widget |
74 |
|
✗ |
connect(ui_->actionRefresh, SIGNAL(triggered()), SLOT(requestRefresh())); |
75 |
|
|
|
76 |
|
✗ |
collisionLabel_ = new QLabel("No collisions."); |
77 |
|
✗ |
shortcutFactory_ = new ShortcutFactory; |
78 |
|
|
#if GEPETTO_GUI_HAS_PYTHONQT |
79 |
|
✗ |
pythonWidget_ = new PythonWidget(this); |
80 |
|
|
#endif |
81 |
|
✗ |
setupInterface(); |
82 |
|
✗ |
connect(ui_->actionChange_shortcut, SIGNAL(triggered()), shortcutFactory_, |
83 |
|
|
SLOT(open())); |
84 |
|
|
|
85 |
|
✗ |
selectionHandler_ = new SelectionHandler(osgViewerManagers_); |
86 |
|
✗ |
selectionHandler_->addMode(new MultiSelection(osgViewerManagers_)); |
87 |
|
✗ |
selectionHandler_->addMode(new UniqueSelection(osgViewerManagers_)); |
88 |
|
|
|
89 |
|
✗ |
ui_->mainToolBar->addWidget(selectionHandler_); |
90 |
|
|
} |
91 |
|
|
|
92 |
|
✗ |
MainWindow::~MainWindow() { |
93 |
|
✗ |
delete shortcutFactory_; |
94 |
|
|
#if GEPETTO_GUI_HAS_PYTHONQT |
95 |
|
✗ |
removeDockWidget(pythonWidget_); |
96 |
|
✗ |
delete pythonWidget_; |
97 |
|
|
#endif |
98 |
|
✗ |
pluginManager()->clearPlugins(); |
99 |
|
✗ |
osgViewerManagers_.reset(); |
100 |
|
✗ |
worker_.quit(); |
101 |
|
✗ |
worker_.wait(); |
102 |
|
✗ |
delete ui_; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
✗ |
MainWindow* MainWindow::instance() { return instance_; } |
106 |
|
|
|
107 |
|
✗ |
void MainWindow::insertDockWidget(QDockWidget* dock, Qt::DockWidgetArea area, |
108 |
|
|
Qt::Orientation orientation) { |
109 |
|
✗ |
addDockWidget(area, dock, orientation); |
110 |
|
✗ |
dock->setVisible(false); |
111 |
|
✗ |
dock->toggleViewAction()->setIcon(QIcon::fromTheme("window-new")); |
112 |
|
✗ |
dock->adjustSize(); |
113 |
|
✗ |
ui_->menuWindow->addAction(dock->toggleViewAction()); |
114 |
|
✗ |
actionSearchBar_->addAction(dock->toggleViewAction()); |
115 |
|
✗ |
connect(dock, SIGNAL(visibilityChanged(bool)), |
116 |
|
|
SLOT(dockVisibilityChanged(bool))); |
117 |
|
|
} |
118 |
|
|
|
119 |
|
✗ |
void MainWindow::removeDockWidget(QDockWidget* dock) { |
120 |
|
✗ |
ui_->menuWindow->removeAction(dock->toggleViewAction()); |
121 |
|
✗ |
disconnect(dock); |
122 |
|
✗ |
QMainWindow::removeDockWidget(dock); |
123 |
|
|
} |
124 |
|
|
|
125 |
|
✗ |
WindowsManagerPtr_t MainWindow::osg() const { return osgViewerManagers_; } |
126 |
|
|
|
127 |
|
✗ |
BodyTreeWidget* MainWindow::bodyTree() const { return ui_->bodyTreeContent; } |
128 |
|
|
|
129 |
|
✗ |
QList<OSGWidget*> MainWindow::osgWindows() const { return osgWindows_; } |
130 |
|
|
|
131 |
|
✗ |
PluginManager* MainWindow::pluginManager() { |
132 |
|
✗ |
return &(settings_->pluginManager_); |
133 |
|
|
} |
134 |
|
|
|
135 |
|
✗ |
ActionSearchBar* MainWindow::actionSearchBar() const { |
136 |
|
✗ |
return actionSearchBar_; |
137 |
|
|
} |
138 |
|
|
|
139 |
|
✗ |
void MainWindow::log(const QString& text) { |
140 |
|
✗ |
if (thread() != QThread::currentThread()) { |
141 |
|
✗ |
emit logString(text); |
142 |
|
✗ |
return; |
143 |
|
|
} |
144 |
|
✗ |
ui_->logText->insertHtml("<hr/><font color=black>" + text + "</font>"); |
145 |
|
|
} |
146 |
|
|
|
147 |
|
✗ |
void MainWindow::logError(const QString& text) { |
148 |
|
✗ |
if (thread() != QThread::currentThread()) { |
149 |
|
✗ |
emit logErrorString(text); |
150 |
|
✗ |
return; |
151 |
|
|
} |
152 |
|
✗ |
if (!ui_->dockWidget_log->isVisible()) { |
153 |
|
✗ |
ui_->dockWidget_log->show(); |
154 |
|
|
} |
155 |
|
✗ |
QScrollBar* sb = ui_->logText->verticalScrollBar(); |
156 |
|
✗ |
bool SBwasAtBottom = sb->value() == sb->maximum(); |
157 |
|
✗ |
ui_->logText->insertHtml("<hr/><font color=red>" + text + "</font>"); |
158 |
|
✗ |
if (SBwasAtBottom) sb->setValue(sb->maximum()); |
159 |
|
|
} |
160 |
|
|
|
161 |
|
✗ |
QMenu* MainWindow::pluginMenu() const { return ui_->menuWindow; } |
162 |
|
|
|
163 |
|
✗ |
void MainWindow::logJobStarted(int id, const QString& text) { |
164 |
|
✗ |
emit logString(QString("Starting job ") + QString::number(id) + ": " + text); |
165 |
|
|
} |
166 |
|
|
|
167 |
|
✗ |
void MainWindow::logJobDone(int id, const QString& text) { |
168 |
|
✗ |
emit logString(QString("Job ") + QString::number(id) + " done: " + text); |
169 |
|
|
} |
170 |
|
|
|
171 |
|
✗ |
void MainWindow::logJobFailed(int id, const QString& text) { |
172 |
|
✗ |
emit logErrorString(QString("Job ") + QString::number(id) + |
173 |
|
✗ |
" failed: " + text); |
174 |
|
|
} |
175 |
|
|
|
176 |
|
✗ |
OSGWidget* MainWindow::createView(const std::string& name) { |
177 |
|
✗ |
if (thread() != QThread::currentThread()) { |
178 |
|
✗ |
qDebug() << "createView must be called from the main thread."; |
179 |
|
✗ |
throw std::runtime_error("Cannot create a new window."); |
180 |
|
|
} |
181 |
|
✗ |
OSGWidget* osgWidget = new OSGWidget(osgViewerManagers_, name, this, 0 |
182 |
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) |
183 |
|
|
, |
184 |
|
|
osgViewer::Viewer::SingleThreaded |
185 |
|
|
#endif |
186 |
|
✗ |
); |
187 |
|
✗ |
osgWidget->setObjectName(name.c_str()); |
188 |
|
✗ |
addOSGWidget(osgWidget); |
189 |
|
✗ |
emit viewCreated(osgWidget); |
190 |
|
✗ |
return osgWidget; |
191 |
|
|
} |
192 |
|
|
|
193 |
|
✗ |
void MainWindow::requestRefresh() { emit refresh(); } |
194 |
|
|
|
195 |
|
✗ |
void MainWindow::createDefaultView() { |
196 |
|
✗ |
std::stringstream ss; |
197 |
|
✗ |
ss << "View " << osgWindows_.size(); |
198 |
|
✗ |
createView(ss.str()); |
199 |
|
|
} |
200 |
|
|
|
201 |
|
✗ |
void MainWindow::addOSGWidget(OSGWidget* osgWidget) { |
202 |
|
|
QDockWidget* dockOSG = |
203 |
|
✗ |
new QDockWidget(tr("Window ") + osgWidget->objectName(), this); |
204 |
|
✗ |
dockOSG->setObjectName("gepetto-gui.osg." + osgWidget->objectName()); |
205 |
|
✗ |
dockOSG->setWidget(osgWidget); |
206 |
|
✗ |
dockOSG->setSizePolicy(QSizePolicy::MinimumExpanding, |
207 |
|
|
QSizePolicy::MinimumExpanding); |
208 |
|
|
// TODO at the moment, when the widget is made floating and then non-floating |
209 |
|
|
// again, the OSGWidget becomes hidden. I could not find the bug so I removed |
210 |
|
|
// the feature DockWidgetFloatable. |
211 |
|
✗ |
dockOSG->setFeatures(QDockWidget::DockWidgetClosable | |
212 |
|
|
QDockWidget::DockWidgetMovable); |
213 |
|
✗ |
connect(dockOSG, SIGNAL(visibilityChanged(bool)), |
214 |
|
|
SLOT(dockVisibilityChanged(bool))); |
215 |
|
✗ |
addDockWidget(Qt::RightDockWidgetArea, dockOSG); |
216 |
|
✗ |
if (osgWindows_.empty()) { |
217 |
|
|
// This OSGWidget should be the central view |
218 |
|
✗ |
centralWidget_ = osgWidget; |
219 |
|
✗ |
osg()->addSceneToWindow("gepetto-gui", centralWidget_->windowID()); |
220 |
|
|
// TODO remove me. This is kept for backward compatibility |
221 |
|
✗ |
osg()->addSceneToWindow("hpp-gui", centralWidget_->windowID()); |
222 |
|
|
} |
223 |
|
✗ |
actionSearchBar_->addAction(new NodeAction( |
224 |
|
✗ |
"Attach camera " + osgWidget->objectName() + " to selected node", |
225 |
|
✗ |
osgWidget, this)); |
226 |
|
✗ |
osgWidget->addAction(actionSearchBar_->showAction()); |
227 |
|
✗ |
osgWindows_.append(osgWidget); |
228 |
|
✗ |
settings_->restoreDockWidgetsState(); |
229 |
|
|
// When creating a window from Python, it isn't desirable to create a |
230 |
|
|
// hidden window. We overwrite the visibility. |
231 |
|
✗ |
dockOSG->show(); |
232 |
|
|
|
233 |
|
|
// Add the widget to the window list. |
234 |
|
✗ |
QMenu* views3D = ui_->menuWindow->findChild<QMenu*>("3d views" |
235 |
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) |
236 |
|
|
, |
237 |
|
|
Qt::FindDirectChildrenOnly |
238 |
|
|
#endif |
239 |
|
|
); |
240 |
|
✗ |
views3D->addAction(dockOSG->toggleViewAction()); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
✗ |
void MainWindow::openLoadRobotDialog() { |
244 |
|
✗ |
statusBar()->showMessage("Loading a robot..."); |
245 |
|
✗ |
DialogLoadRobot* d = new DialogLoadRobot(this); |
246 |
|
✗ |
if (d->exec() == QDialog::Accepted) { |
247 |
|
✗ |
createCentralWidget(); |
248 |
|
✗ |
DialogLoadRobot::RobotDefinition rd = d->getSelectedRobotDescription(); |
249 |
|
|
|
250 |
|
✗ |
QString urdfFile = QString("package://%1/urdf/%2%3.urdf") |
251 |
|
✗ |
.arg(rd.package_) |
252 |
|
✗ |
.arg(rd.modelName_) |
253 |
|
✗ |
.arg(rd.urdfSuf_); |
254 |
|
|
try { |
255 |
|
✗ |
osgViewerManagers_->addURDF(rd.robotName_.toStdString(), |
256 |
|
✗ |
urdfFile.toStdString()); |
257 |
|
✗ |
osgViewerManagers_->addSceneToWindow(rd.robotName_.toStdString(), |
258 |
|
✗ |
centralWidget_->windowID()); |
259 |
|
✗ |
} catch (std::runtime_error& exc) { |
260 |
|
✗ |
logError(exc.what()); |
261 |
|
|
} |
262 |
|
✗ |
robotNames_.append(rd.robotName_); |
263 |
|
|
|
264 |
|
✗ |
QString what = QString("Loading robot ") + rd.name_; |
265 |
|
✗ |
foreach (ModelInterface* loader, pluginManager()->get<ModelInterface>()) { |
266 |
|
✗ |
QtConcurrent::run(loader, &ModelInterface::loadRobotModel, rd); |
267 |
|
✗ |
logString(what); |
268 |
|
|
} |
269 |
|
|
} |
270 |
|
✗ |
d->close(); |
271 |
|
✗ |
statusBar()->clearMessage(); |
272 |
|
✗ |
d->deleteLater(); |
273 |
|
|
} |
274 |
|
|
|
275 |
|
✗ |
void MainWindow::openLoadEnvironmentDialog() { |
276 |
|
✗ |
statusBar()->showMessage("Loading an environment..."); |
277 |
|
✗ |
DialogLoadEnvironment* e = new DialogLoadEnvironment(this); |
278 |
|
✗ |
if (e->exec() == QDialog::Accepted) { |
279 |
|
✗ |
createCentralWidget(); |
280 |
|
|
DialogLoadEnvironment::EnvironmentDefinition ed = |
281 |
|
✗ |
e->getSelectedDescription(); |
282 |
|
|
|
283 |
|
✗ |
QString urdfFile = QString("package://%1/urdf/%2.urdf") |
284 |
|
✗ |
.arg(ed.package_) |
285 |
|
✗ |
.arg(ed.urdfFilename_); |
286 |
|
|
try { |
287 |
|
✗ |
osgViewerManagers_->addUrdfObjects(ed.envName_.toStdString(), |
288 |
|
✗ |
urdfFile.toStdString(), true); |
289 |
|
✗ |
osgViewerManagers_->addSceneToWindow(ed.envName_.toStdString(), |
290 |
|
✗ |
centralWidget_->windowID()); |
291 |
|
✗ |
} catch (std::runtime_error& exc) { |
292 |
|
✗ |
log(exc.what()); |
293 |
|
|
} |
294 |
|
|
|
295 |
|
✗ |
QString what = QString("Loading environment ") + ed.name_; |
296 |
|
✗ |
foreach (ModelInterface* loader, pluginManager()->get<ModelInterface>()) { |
297 |
|
✗ |
QtConcurrent::run(loader, &ModelInterface::loadEnvironmentModel, ed); |
298 |
|
✗ |
logString(what); |
299 |
|
|
} |
300 |
|
|
} |
301 |
|
✗ |
statusBar()->clearMessage(); |
302 |
|
✗ |
e->close(); |
303 |
|
✗ |
e->deleteLater(); |
304 |
|
|
} |
305 |
|
|
|
306 |
|
✗ |
void MainWindow::handleWorkerDone(int /*id*/) {} |
307 |
|
|
|
308 |
|
✗ |
void MainWindow::resetConnection() { |
309 |
|
✗ |
foreach (ConnectionInterface* e, |
310 |
|
|
pluginManager()->get<ConnectionInterface>()) { |
311 |
|
✗ |
e->openConnection(); |
312 |
|
|
} |
313 |
|
|
} |
314 |
|
|
|
315 |
|
✗ |
void MainWindow::closeConnection() { |
316 |
|
✗ |
foreach (ConnectionInterface* e, |
317 |
|
|
pluginManager()->get<ConnectionInterface>()) { |
318 |
|
✗ |
e->closeConnection(); |
319 |
|
|
} |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
#define _to_str_(a) #a |
323 |
|
|
#define _to_str(a) _to_str_(a) |
324 |
|
|
#define _osg_version_str \ |
325 |
|
|
_to_str(OPENSCENEGRAPH_MAJOR_VERSION) "." _to_str( \ |
326 |
|
|
OPENSCENEGRAPH_MINOR_VERSION) "." _to_str(OPENSCENEGRAPH_PATCH_VERSION) |
327 |
|
|
|
328 |
|
✗ |
void MainWindow::about() { |
329 |
|
✗ |
QString devString; |
330 |
|
|
devString = |
331 |
|
✗ |
trUtf8( |
332 |
|
|
"<p>Version %1. For more information visit <a href=\"%2\">%2</a></p>" |
333 |
|
|
"<p><ul>" |
334 |
|
|
"<li>Compiled with Qt %3, run with Qt %4</li>" |
335 |
|
|
"<li>Compiled with OpenSceneGraph version " _osg_version_str |
336 |
|
|
", run with version %5</li>" |
337 |
|
|
#if GEPETTO_GUI_HAS_PYTHONQT |
338 |
|
|
"<li>Compiled with PythonQt.</li>" |
339 |
|
|
#else |
340 |
|
|
"<li>Compiled without PythonQt</li>" |
341 |
|
|
#endif |
342 |
|
|
"<li></li>" |
343 |
|
|
"<li></li>" |
344 |
|
|
"</ul></p>" |
345 |
|
|
"<p><small>Copyright (c) 2013-2020 CNRS<br/>By Joseph Mirabel and " |
346 |
|
|
"others.</small></p>" |
347 |
|
|
"<p><small>" |
348 |
|
|
"BSD 2-Clause License<br/>" |
349 |
|
|
"All rights reserved.<br/><br/>" |
350 |
|
|
"Redistribution and use in source and binary forms, with or without" |
351 |
|
|
"modification, are permitted provided that the following conditions " |
352 |
|
|
"are met:" |
353 |
|
|
"<ol><li>Redistributions of source code must retain the above " |
354 |
|
|
"copyright notice, this" |
355 |
|
|
"list of conditions and the following disclaimer.</li>" |
356 |
|
|
"<li>Redistributions in binary form must reproduce the above " |
357 |
|
|
"copyright notice," |
358 |
|
|
"this list of conditions and the following disclaimer in the " |
359 |
|
|
"documentation" |
360 |
|
|
"and/or other materials provided with the distribution.</li></ol>" |
361 |
|
|
"</small></p>") |
362 |
|
✗ |
.arg(QApplication::applicationVersion()) |
363 |
|
✗ |
.arg(QApplication::organizationDomain()) |
364 |
|
✗ |
.arg(QT_VERSION_STR) |
365 |
|
✗ |
.arg(qVersion()) |
366 |
|
✗ |
.arg(osgGetVersion()); |
367 |
|
|
|
368 |
|
✗ |
QMessageBox::about(this, QApplication::applicationName(), devString); |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
#undef _to_str |
372 |
|
|
#undef _osg_version_str |
373 |
|
|
|
374 |
|
✗ |
void MainWindow::activateCollision(bool activate) { |
375 |
|
✗ |
if (activate) { |
376 |
|
✗ |
requestConfigurationValidation(); |
377 |
|
✗ |
foreach (QString b, lastBodiesInCollision_) { |
378 |
|
✗ |
osg()->setHighlight(b.toLocal8Bit().data(), 1); |
379 |
|
|
} |
380 |
|
|
} else { |
381 |
|
✗ |
foreach (QString b, lastBodiesInCollision_) { |
382 |
|
✗ |
osg()->setHighlight(b.toLocal8Bit().data(), 0); |
383 |
|
|
} |
384 |
|
✗ |
collisionIndicator_->switchLed(true); |
385 |
|
✗ |
collisionLabel_->setText("No collisions."); |
386 |
|
|
} |
387 |
|
|
} |
388 |
|
|
|
389 |
|
✗ |
void MainWindow::dockVisibilityChanged(bool visible) { |
390 |
|
✗ |
QWidget* cw = centralWidget(); |
391 |
|
✗ |
if (visible && cw->isVisible()) |
392 |
|
✗ |
cw->hide(); |
393 |
|
|
else { |
394 |
|
✗ |
const QObjectList& objs = children(); |
395 |
|
✗ |
foreach (const QObject* obj, objs) { |
396 |
|
✗ |
const QDockWidget* dock = qobject_cast<const QDockWidget*>(obj); |
397 |
|
✗ |
if (dock != 0 && dock->isVisible()) return; |
398 |
|
|
} |
399 |
|
✗ |
cw->show(); |
400 |
|
|
} |
401 |
|
|
} |
402 |
|
|
|
403 |
|
✗ |
void MainWindow::hsplitTabifiedDockWidget() { |
404 |
|
✗ |
splitTabifiedDockWidget(Qt::Horizontal); |
405 |
|
|
} |
406 |
|
|
|
407 |
|
✗ |
void MainWindow::vsplitTabifiedDockWidget() { |
408 |
|
✗ |
splitTabifiedDockWidget(Qt::Vertical); |
409 |
|
|
} |
410 |
|
|
|
411 |
|
✗ |
QDockWidget* getParentDockWidget(QObject* child) { |
412 |
|
✗ |
QDockWidget* dock = NULL; |
413 |
|
✗ |
while (child != NULL) { |
414 |
|
✗ |
dock = qobject_cast<QDockWidget*>(child); |
415 |
|
✗ |
if (dock != NULL) break; |
416 |
|
✗ |
child = child->parent(); |
417 |
|
|
} |
418 |
|
✗ |
return dock; |
419 |
|
|
} |
420 |
|
|
|
421 |
|
✗ |
void MainWindow::splitTabifiedDockWidget(Qt::Orientation orientation) { |
422 |
|
|
// QDockWidget focused |
423 |
|
✗ |
QDockWidget* dock = getParentDockWidget(QApplication::focusWidget()); |
424 |
|
✗ |
if (dock == NULL) return; |
425 |
|
|
// QDockWidget under cursor |
426 |
|
|
QDockWidget* other = |
427 |
|
✗ |
getParentDockWidget(QApplication::widgetAt(QCursor::pos())); |
428 |
|
✗ |
if (other == NULL) return; |
429 |
|
✗ |
if (other == dock) return; |
430 |
|
✗ |
splitDockWidget(dock, other, orientation); |
431 |
|
|
} |
432 |
|
|
|
433 |
|
✗ |
void MainWindow::setupInterface() { |
434 |
|
|
// Menu "Window/Tool bar" |
435 |
|
✗ |
QMenu* toolbar = ui_->menuWindow->addMenu("Tool bar"); |
436 |
|
✗ |
toolbar->setIcon(QIcon::fromTheme("configure-toolbars")); |
437 |
|
✗ |
ui_->mainToolBar->setVisible(true); |
438 |
|
✗ |
toolbar->addAction(ui_->mainToolBar->toggleViewAction()); |
439 |
|
|
|
440 |
|
✗ |
ui_->menuWindow->addSeparator(); |
441 |
|
|
|
442 |
|
|
// Menu "Window/3D views" |
443 |
|
✗ |
QMenu* views3D = ui_->menuWindow->addMenu("3D views"); |
444 |
|
✗ |
views3D->setObjectName("3d views"); |
445 |
|
|
|
446 |
|
✗ |
ui_->menuWindow->addSeparator(); |
447 |
|
|
|
448 |
|
|
// Menu "Window" |
449 |
|
✗ |
ui_->dockWidget_bodyTree->setVisible(false); |
450 |
|
✗ |
ui_->dockWidget_bodyTree->toggleViewAction()->setIcon( |
451 |
|
✗ |
QIcon::fromTheme("window-new")); |
452 |
|
✗ |
ui_->dockWidget_bodyTree->toggleViewAction()->setShortcut(Qt::CTRL + Qt::ALT + |
453 |
|
|
Qt::Key_B); |
454 |
|
✗ |
ui_->menuWindow->addAction(ui_->dockWidget_bodyTree->toggleViewAction()); |
455 |
|
✗ |
ui_->dockWidget_log->setVisible(false); |
456 |
|
✗ |
ui_->dockWidget_log->toggleViewAction()->setIcon( |
457 |
|
✗ |
QIcon::fromTheme("window-new")); |
458 |
|
✗ |
ui_->dockWidget_log->toggleViewAction()->setShortcut(Qt::CTRL + Qt::ALT + |
459 |
|
|
Qt::Key_L); |
460 |
|
✗ |
ui_->menuWindow->addAction(ui_->dockWidget_log->toggleViewAction()); |
461 |
|
|
#if GEPETTO_GUI_HAS_PYTHONQT |
462 |
|
✗ |
insertDockWidget(pythonWidget_, Qt::BottomDockWidgetArea, Qt::Horizontal); |
463 |
|
✗ |
registerShortcut("Python console", "Toggle view", |
464 |
|
✗ |
pythonWidget_->toggleViewAction()); |
465 |
|
|
#endif |
466 |
|
|
|
467 |
|
|
// Add QActions to split dock widgets |
468 |
|
✗ |
QAction* vsplit = new QAction("Split focused dock widget vertically", this); |
469 |
|
✗ |
vsplit->setShortcut(Qt::CTRL + Qt::Key_S); |
470 |
|
✗ |
addAction(vsplit); |
471 |
|
✗ |
connect(vsplit, SIGNAL(triggered()), SLOT(vsplitTabifiedDockWidget())); |
472 |
|
✗ |
QAction* hsplit = new QAction("Split focused dock widget horizontally", this); |
473 |
|
✗ |
hsplit->setShortcut(Qt::CTRL + Qt::Key_H); |
474 |
|
✗ |
addAction(hsplit); |
475 |
|
✗ |
connect(hsplit, SIGNAL(triggered()), SLOT(hsplitTabifiedDockWidget())); |
476 |
|
|
|
477 |
|
✗ |
registerShortcut("Log widget", "Toggle view", |
478 |
|
✗ |
ui_->dockWidget_log->toggleViewAction()); |
479 |
|
✗ |
registerShortcut("Body tree widget", "Toggle view", |
480 |
|
✗ |
ui_->dockWidget_bodyTree->toggleViewAction()); |
481 |
|
✗ |
registerShortcut("Main window", ui_->actionLoad_robot_from_file); |
482 |
|
✗ |
registerShortcut("Main window", ui_->actionLoad_environment); |
483 |
|
✗ |
registerShortcut("Main window", ui_->actionChange_shortcut); |
484 |
|
✗ |
registerShortcut("Main window", ui_->actionQuit); |
485 |
|
✗ |
registerShortcut("Main window", ui_->actionReconnect); |
486 |
|
✗ |
registerShortcut("Main window", ui_->actionRefresh); |
487 |
|
✗ |
registerShortcut("Main window", ui_->actionPlugins); |
488 |
|
✗ |
registerShortcut("Main window", vsplit); |
489 |
|
✗ |
registerShortcut("Main window", hsplit); |
490 |
|
|
|
491 |
|
✗ |
ui_->menuWindow->addSeparator(); |
492 |
|
|
|
493 |
|
|
// Setup the status bar |
494 |
|
✗ |
collisionIndicator_ = new LedIndicator(statusBar()); |
495 |
|
✗ |
collisionValidationActivated_ = new QCheckBox(); |
496 |
|
✗ |
collisionValidationActivated_->setToolTip( |
497 |
|
✗ |
tr("Automatically validate configurations.")); |
498 |
|
✗ |
collisionValidationActivated_->setCheckState(Qt::Checked); |
499 |
|
✗ |
statusBar()->addPermanentWidget(collisionLabel_); |
500 |
|
✗ |
statusBar()->addPermanentWidget(collisionValidationActivated_); |
501 |
|
✗ |
statusBar()->addPermanentWidget(collisionIndicator_); |
502 |
|
|
|
503 |
|
✗ |
connect(collisionValidationActivated_, SIGNAL(clicked(bool)), |
504 |
|
|
SLOT(activateCollision(bool))); |
505 |
|
✗ |
connect(collisionIndicator_, SIGNAL(mouseClickEvent()), |
506 |
|
|
SLOT(requestConfigurationValidation())); |
507 |
|
✗ |
connect(ui_->actionAbout, SIGNAL(triggered()), SLOT(about())); |
508 |
|
✗ |
connect(ui_->actionReconnect, SIGNAL(triggered()), SLOT(resetConnection())); |
509 |
|
✗ |
connect(ui_->actionClose_connections, SIGNAL(triggered()), |
510 |
|
|
SLOT(closeConnection())); |
511 |
|
|
|
512 |
|
✗ |
connect(this, SIGNAL(logString(QString)), SLOT(log(QString))); |
513 |
|
✗ |
connect(this, SIGNAL(logErrorString(QString)), SLOT(logError(QString))); |
514 |
|
|
|
515 |
|
✗ |
actionSearchBar_->addAction( |
516 |
|
✗ |
new NodeAction(NodeAction::VISIBILITY_ON, "Show node", this)); |
517 |
|
✗ |
actionSearchBar_->addAction( |
518 |
|
✗ |
new NodeAction(NodeAction::VISIBILITY_OFF, "Hide node", this)); |
519 |
|
✗ |
actionSearchBar_->addAction( |
520 |
|
✗ |
new NodeAction(NodeAction::ALWAYS_ON_TOP, "Always on top", this)); |
521 |
|
✗ |
actionSearchBar_->addAction( |
522 |
|
✗ |
new NodeAction(NodeAction::REMOVE_NODE, "Remove node", this)); |
523 |
|
✗ |
actionSearchBar_->addAction(ui_->actionFetch_configuration); |
524 |
|
✗ |
actionSearchBar_->addAction(ui_->actionSend_configuration); |
525 |
|
✗ |
actionSearchBar_->addAction(ui_->actionClose_connections); |
526 |
|
✗ |
actionSearchBar_->addAction(ui_->actionReconnect); |
527 |
|
✗ |
actionSearchBar_->addAction(ui_->actionRefresh); |
528 |
|
|
} |
529 |
|
|
|
530 |
|
✗ |
void MainWindow::createCentralWidget() { |
531 |
|
✗ |
if (!osgWindows_.empty()) return; |
532 |
|
✗ |
createDefaultView(); |
533 |
|
|
} |
534 |
|
|
|
535 |
|
✗ |
void MainWindow::requestApplyCurrentConfiguration() { |
536 |
|
✗ |
emit applyCurrentConfiguration(); |
537 |
|
✗ |
if (collisionValidationActivated_->isChecked()) |
538 |
|
✗ |
requestConfigurationValidation(); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
✗ |
void MainWindow::requestConfigurationValidation() { |
542 |
|
✗ |
emit configurationValidation(); |
543 |
|
|
} |
544 |
|
|
|
545 |
|
✗ |
void MainWindow::onOpenPluginManager() { |
546 |
|
✗ |
PluginManagerDialog d(pluginManager(), this); |
547 |
|
✗ |
d.exec(); |
548 |
|
|
} |
549 |
|
|
|
550 |
|
✗ |
void MainWindow::registerSignal(const char* signal, QObject* obj) { |
551 |
|
✗ |
if (registeredSignals_.find(signal) == registeredSignals_.end()) { |
552 |
|
✗ |
registeredSignals_[signal] = obj; |
553 |
|
✗ |
gepetto::log() << signal << " registered" << std::endl; |
554 |
|
|
} else |
555 |
|
✗ |
gepetto::log() << "Signal" << signal << "already registered." << std::endl; |
556 |
|
|
} |
557 |
|
|
|
558 |
|
✗ |
QObject* MainWindow::getFromSignal(const char* signal) { |
559 |
|
✗ |
if (registeredSignals_.find(signal) == registeredSignals_.end()) { |
560 |
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) |
561 |
|
✗ |
gepetto::log() << "signal " << signal << "isn't registered" << std::endl; |
562 |
|
|
#endif |
563 |
|
✗ |
return NULL; |
564 |
|
|
} |
565 |
|
✗ |
return registeredSignals_[signal]; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
✗ |
void MainWindow::connectSignal(const char* signal, const char* slot, |
569 |
|
|
QObject* obj) { |
570 |
|
✗ |
QObject* obj_sig = getFromSignal(signal); |
571 |
|
✗ |
if (obj_sig != NULL) { |
572 |
|
✗ |
QObject::connect(obj_sig, signal, obj, slot); |
573 |
|
|
} |
574 |
|
|
} |
575 |
|
|
|
576 |
|
✗ |
void MainWindow::registerSlot(const char* slot, QObject* obj) { |
577 |
|
✗ |
if (registeredSlots_.find(slot) == registeredSlots_.end()) { |
578 |
|
✗ |
registeredSlots_[slot] = obj; |
579 |
|
✗ |
gepetto::log() << slot << " registered" << std::endl; |
580 |
|
|
} else |
581 |
|
✗ |
gepetto::log() << "Slot " << slot << "already registered." << std::endl; |
582 |
|
|
} |
583 |
|
|
|
584 |
|
✗ |
QObject* MainWindow::getFromSlot(const char* slot) { |
585 |
|
✗ |
if (registeredSlots_.find(slot) == registeredSlots_.end()) { |
586 |
|
✗ |
gepetto::log() << "slot" << slot << "isn't registered" << std::endl; |
587 |
|
✗ |
return NULL; |
588 |
|
|
} |
589 |
|
✗ |
return registeredSlots_[slot]; |
590 |
|
|
} |
591 |
|
|
|
592 |
|
✗ |
void MainWindow::connectSlot(const char* slot, const char* signal, |
593 |
|
|
QObject* obj) { |
594 |
|
✗ |
if (registeredSlots_.find(slot) != registeredSlots_.end()) { |
595 |
|
✗ |
QObject::connect(obj, signal, registeredSlots_[slot], slot); |
596 |
|
|
} |
597 |
|
|
} |
598 |
|
|
|
599 |
|
✗ |
void MainWindow::registerShortcut(QString widgetName, QString actionName, |
600 |
|
|
QAction* action) { |
601 |
|
✗ |
shortcutFactory_->addBinding(widgetName, actionName, action); |
602 |
|
|
} |
603 |
|
|
|
604 |
|
✗ |
void MainWindow::registerShortcut(QString widgetName, QAction* action) { |
605 |
|
✗ |
shortcutFactory_->addBinding(widgetName, action->text(), action); |
606 |
|
|
} |
607 |
|
|
|
608 |
|
✗ |
void MainWindow::configurationValidationStatusChanged(bool valid) { |
609 |
|
✗ |
collisionIndicator_->switchLed(valid); |
610 |
|
✗ |
int state = (valid) ? 0 : 1; |
611 |
|
✗ |
foreach (const QString& s, robotNames_) { |
612 |
|
✗ |
osg()->setHighlight(s.toLocal8Bit().data(), state); |
613 |
|
|
} |
614 |
|
|
} |
615 |
|
|
|
616 |
|
✗ |
void MainWindow::configurationValidationStatusChanged( |
617 |
|
|
QStringList bodiesInCollision) { |
618 |
|
✗ |
QStringList lastBodiesInCollision = lastBodiesInCollision_; |
619 |
|
✗ |
lastBodiesInCollision_.clear(); |
620 |
|
✗ |
collisionIndicator_->switchLed(bodiesInCollision.empty()); |
621 |
|
✗ |
foreach (QString b, lastBodiesInCollision) { |
622 |
|
✗ |
if (bodiesInCollision.removeAll(b) == 0) { |
623 |
|
|
/// This body is not in collision |
624 |
|
✗ |
osg()->setHighlight(b.toLocal8Bit().data(), 0); |
625 |
|
|
} else { |
626 |
|
|
/// This body is still in collision |
627 |
|
✗ |
lastBodiesInCollision_.append(b); |
628 |
|
|
} |
629 |
|
|
} |
630 |
|
✗ |
QString tooltip("Collision between "); |
631 |
|
✗ |
foreach (const QString& b, bodiesInCollision) { |
632 |
|
✗ |
osg()->setHighlight(b.toLocal8Bit().data(), 1); |
633 |
|
✗ |
lastBodiesInCollision_.append(b); |
634 |
|
|
} |
635 |
|
✗ |
tooltip += lastBodiesInCollision_.join(", "); |
636 |
|
✗ |
if (lastBodiesInCollision_.count() > 0) |
637 |
|
✗ |
collisionLabel_->setText(tooltip); |
638 |
|
|
else |
639 |
|
✗ |
collisionLabel_->setText("No collisions."); |
640 |
|
|
} |
641 |
|
|
|
642 |
|
✗ |
void MainWindow::requestSelectJointFromBodyName(const QString bodyName) { |
643 |
|
✗ |
emit selectJointFromBodyName(bodyName); |
644 |
|
|
} |
645 |
|
|
} // namespace gui |
646 |
|
|
} // namespace gepetto |
647 |
|
|
|