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