GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/gui/osgwidget.cc Lines: 1 220 0.5 %
Date: 2020-05-14 11:23:33 Branches: 2 599 0.3 %

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/osgwidget.hh"
18
#include "gepetto/gui/mainwindow.hh"
19
#include <gepetto/gui/pick-handler.hh>
20
21
#include <QAction>
22
#include <QFileDialog>
23
#include <QProcess>
24
#include <QTextBrowser>
25
#include <QDockWidget>
26
27
#include <osg/Camera>
28
29
#include <osg/DisplaySettings>
30
#include <osg/Geode>
31
#include <osg/Material>
32
#include <osg/Shape>
33
#include <osg/ShapeDrawable>
34
#include <osg/StateSet>
35
36
#include <osgGA/EventQueue>
37
#include <osgGA/KeySwitchMatrixManipulator>
38
#include <osgGA/TrackballManipulator>
39
40
#include <osgUtil/IntersectionVisitor>
41
#include <osgUtil/PolytopeIntersector>
42
43
#include <osgViewer/View>
44
#include <osgViewer/ViewerEventHandlers>
45
46
#include <cassert>
47
48
#include <stdexcept>
49
#include <vector>
50
51
#include <QDebug>
52
#include <QKeyEvent>
53
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
54
# include <QStandardPaths>
55
#endif
56
#include <QToolBar>
57
#include <QVBoxLayout>
58
#include <QWheelEvent>
59
60
#include <gepetto/viewer/urdf-parser.h>
61
#include <gepetto/viewer/OSGManipulator/keyboard-manipulator.h>
62
#include <gepetto/viewer/macros.h>
63
#include <gepetto/viewer/window-manager.h>
64
65
#include <gepetto/gui/windows-manager.hh>
66
#include <gepetto/gui/selection-event.hh>
67
#include <gepetto/gui/action-search-bar.hh>
68
69
namespace gepetto {
70
  namespace gui {
71
    OSGWidget::OSGWidget(WindowsManagerPtr_t wm,
72
                         const std::string & name,
73
                         MainWindow *parent, Qt::WindowFlags f ,
74
                         osgViewer::ViewerBase::ThreadingModel threadingModel)
75
    : QWidget( parent, f )
76
    , graphicsWindow_()
77
    , wsm_ (wm)
78
    , pickHandler_ (new PickHandler (this, wsm_))
79
    , wid_ ()
80
    , wm_ ()
81
    , viewer_ (new osgViewer::Viewer)
82
    , screenCapture_ ()
83
    , toolBar_ (new QToolBar (QString::fromStdString(name) + " tool bar"))
84
    , process_ (new QProcess (this))
85
    , showPOutput_ (new QDialog (this, Qt::Dialog | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint))
86
    , pOutput_ (new QTextBrowser())
87
    , fullscreen_ (new QWidget (NULL, Qt::Window | Qt::WindowStaysOnTopHint))
88
    {
89
      initGraphicsWindowsAndViewer (parent, name);
90
      initToolBar ();
91
92
      wid_ = wm->createWindow (name, this, viewer_, graphicsWindow_.get());
93
      wm_ = wsm_->getWindowManager (wid_);
94
95
      viewer_->setThreadingModel(threadingModel);
96
97
      osgQt::GLWidget* glWidget = graphicsWindow_->getGLWidget();
98
      //glWidget->setForwardKeyEvents(true);
99
      QVBoxLayout* hblayout = new QVBoxLayout (this);
100
      hblayout->setContentsMargins(1,1,1,1);
101
      setLayout (hblayout);
102
      hblayout->addWidget(toolBar_);
103
      hblayout->addWidget(glWidget);
104
      glWidget->setMinimumSize(50,10);
105
106
      // TODO Adding the properties here is problematic. They won't be
107
      // shown in the GUI because the display is created before this code is run.
108
      wm_->addProperty (
109
        viewer::BoolProperty::create("WindowFixedSize",
110
          viewer::BoolProperty::getterFromMemberFunction (this, &OSGWidget::isFixedSize),
111
          viewer::BoolProperty::setterFromMemberFunction (this, &OSGWidget::setFixedSize)));
112
      wm_->addProperty (
113
        viewer::Vector2Property::create("WindowSize",
114
          viewer::Vector2Property::getterFromMemberFunction (wm_.get(), &viewer::WindowManager::getWindowDimension),
115
          viewer::Vector2Property::Setter_t()));
116
117
      connect( &timer_, SIGNAL(timeout()), this, SLOT(update()) );
118
      timer_.start(parent->settings_->refreshRate);
119
120
      // Setup widgets to record movies.
121
      process_->setProcessChannelMode(QProcess::MergedChannels);
122
      connect (process_, SIGNAL (readyReadStandardOutput ()), SLOT (readyReadProcessOutput()));
123
      showPOutput_->setModal(false);
124
      showPOutput_->setLayout(new QHBoxLayout ());
125
      showPOutput_->layout()->addWidget(pOutput_);
126
    }
127
128
    OSGWidget::~OSGWidget()
129
    {
130
      viewer_->setDone(true);
131
      viewer_->removeEventHandler(pickHandler_);
132
      pickHandler_ = NULL;
133
      wm_.reset();
134
      wsm_.reset();
135
      delete fullscreen_;
136
    }
137
138
    void OSGWidget::paintEvent(QPaintEvent*)
139
    {
140
      viewer::ScopedLock lock(wsm_->osgFrameMutex());
141
      wm_->frame();
142
    }
143
144
    WindowsManager::WindowID OSGWidget::windowID() const
145
    {
146
      return wid_;
147
    }
148
149
    WindowManagerPtr_t OSGWidget::window() const
150
    {
151
      return wm_;
152
    }
153
154
    WindowsManagerPtr_t OSGWidget::osg() const
155
    {
156
      return wsm_;
157
    }
158
159
    void OSGWidget::onHome()
160
    {
161
      viewer_->home ();
162
    }
163
164
    void OSGWidget::addFloor()
165
    {
166
      wsm_->addFloor("hpp-gui/floor");
167
    }
168
169
    void OSGWidget::toggleCapture (bool active)
170
    {
171
      MainWindow* main = MainWindow::instance();
172
      if (active) {
173
        QDir tmp ("/tmp");
174
        tmp.mkpath ("gepetto-gui/record"); tmp.cd("gepetto-gui/record");
175
        foreach (QString f, tmp.entryList(QStringList() << "img_0_*.jpeg", QDir::Files))
176
          tmp.remove(f);
177
        QString path = tmp.absoluteFilePath("img");
178
        const char* ext = "jpeg";
179
        osg ()->startCapture(windowID(), path.toLocal8Bit().data(), ext);
180
        main->log("Saving images to " + path + "_*." + ext);
181
      } else {
182
        osg()->stopCapture(windowID());
183
        QString outputFile = QFileDialog::getSaveFileName(this, tr("Save video to"), "untitled.mp4");
184
        if (!outputFile.isNull()) {
185
          if (QFile::exists(outputFile))
186
            QFile::remove(outputFile);
187
          QString avconv = main->settings_->avconv;
188
189
          QStringList args;
190
          QString input = "/tmp/gepetto-gui/record/img_0_%d.jpeg";
191
          args << main->settings_->avConvInputOptions
192
            << "-i" << input
193
            << main->settings_->avConvOutputOptions
194
            << outputFile;
195
          qDebug () << args;
196
197
          showPOutput_->setWindowTitle(avconv + " " + args.join(" "));
198
          pOutput_->clear();
199
          showPOutput_->resize(main->size() / 2);
200
          showPOutput_->show();
201
          process_->start(avconv, args);
202
          bool started = process_->waitForStarted(-1);
203
          if (!started) {
204
            showPOutput_->hide();
205
            switch (process_->error()) {
206
              case QProcess::FailedToStart:
207
                main->logError ("Failed to start " + avconv + ". Either it is missing, "
208
                    "or you may have insufficient permissions to invoke it.\n");
209
                break;
210
              case QProcess::Crashed      :
211
                main->logError ("" + avconv + " crashed some time after starting successfully.");
212
                break;
213
              case QProcess::Timedout     :
214
              case QProcess::WriteError   :
215
              case QProcess::ReadError    :
216
              case QProcess::UnknownError :
217
                main->logError ("An unknown error made " + avconv + " stopped before "
218
                    "finishing.");
219
                break;
220
            }
221
            main->logError ("You can manually run\n" + avconv + " " + args.join(" "));
222
          }
223
        }
224
      }
225
    }
226
227
    void OSGWidget::captureFrame ()
228
    {
229
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
230
      QString pathname = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
231
#else
232
      QString pathname = QDir::home().filePath("Pictures");
233
#endif
234
      if (pathname.isEmpty()) {
235
        qDebug() << "Unable to get the directory where to write images";
236
        return;
237
      }
238
      QDir path (pathname);
239
      if (!path.mkpath(".")) {
240
        qDebug() << "Unable to create directory" << pathname;
241
        return;
242
      }
243
      QString filename (path.filePath (
244
            QDateTime::currentDateTime().toString("yyyy-MM-dd.hh-mm-ss'.png'")));
245
      captureFrame (filename.toStdString());
246
    }
247
248
    void OSGWidget::captureFrame (const std::string& filename)
249
    {
250
      viewer::ScopedLock lock(wsm_->osgFrameMutex());
251
      wm_->captureFrame (filename);
252
    }
253
254
    bool OSGWidget::startCapture (const std::string& filename,
255
        const std::string& extension)
256
    {
257
      viewer::ScopedLock lock(wsm_->osgFrameMutex());
258
      wm_->startCapture (filename, extension);
259
      return true;
260
    }
261
262
    bool OSGWidget::stopCapture ()
263
    {
264
      viewer::ScopedLock lock(wsm_->osgFrameMutex());
265
      wm_->stopCapture ();
266
      return true;
267
    }
268
269
    bool OSGWidget::isFixedSize () const
270
    {
271
      try {
272
        return wm_->property("WindowSize")->hasWriteAccess();
273
      } catch (const std::invalid_argument&) {
274
        return false;
275
      }
276
    }
277
278
    void OSGWidget::setFixedSize (bool fixedSize)
279
    {
280
      try {
281
        viewer::Property& size(*wm_->property("WindowSize"));
282
        if (size.hasWriteAccess() == fixedSize) return;
283
284
        // Cast to Vector2Property
285
        viewer::Vector2Property& vsize (dynamic_cast<viewer::Vector2Property&>(size));
286
        osgQt::GLWidget* glWidget = graphicsWindow_->getGLWidget();
287
        if (fixedSize) {
288
          glWidget->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
289
          // Add write access
290
          vsize.setter (viewer::Vector2Property::Setter_t(
291
                viewer::Vector2Property::setterFromMemberFunction (this, &OSGWidget::setWindowDimension)
292
                ));
293
        } else {
294
          glWidget->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred);
295
          // Remove write access
296
          vsize.setter (viewer::Vector2Property::Setter_t());
297
        }
298
      } catch (const std::invalid_argument&) {
299
        return;
300
      } catch (const std::bad_cast&) {
301
        return;
302
      }
303
    }
304
305
    void OSGWidget::setWindowDimension (const osgVector2& size)
306
    {
307
      osgQt::GLWidget* glWidget = graphicsWindow_->getGLWidget();
308
      if (isFixedSize()) {
309
        glWidget->resize ((int)size[0], (int)size[1]);
310
        glWidget->setMinimumSize((int)size[0], (int)size[1]);
311
      } else
312
        glWidget->setMinimumSize(50,10);
313
    }
314
315
    void OSGWidget::readyReadProcessOutput()
316
    {
317
      pOutput_->append(process_->readAll());
318
    }
319
320
    QIcon iconFromTheme (const QString& name)
321
    {
322
      QIcon icon;
323
      if (QIcon::hasThemeIcon(name)) {
324
        icon = QIcon::fromTheme(name);
325
      } else {
326
        icon.addFile(QString::fromUtf8(""), QSize(), QIcon::Normal, QIcon::Off);
327
      }
328
      return icon;
329
    }
330
331
    void OSGWidget::initToolBar ()
332
    {
333
      toolBar_->addAction(
334
          iconFromTheme("zoom-fit-best"), "Zoom to fit", this, SLOT(onHome()))
335
        ->setToolTip("Zoom to fit");
336
337
      QIcon icon;
338
      icon.addFile(QString::fromUtf8(":/icons/floor.png"), QSize(), QIcon::Normal, QIcon::Off);
339
      QAction* addFloor = toolBar_->addAction(icon, "Add floor", this, SLOT(addFloor()));
340
      addFloor->setToolTip("Add a floor");
341
      MainWindow* main = MainWindow::instance();
342
      main->actionSearchBar()->addAction (addFloor);
343
344
      toolBar_->addAction(iconFromTheme("insert-image"), "Take snapshot",
345
          this, SLOT(captureFrame()))
346
        ->setToolTip("Take a snapshot");
347
348
      QAction* recordMovie = toolBar_->addAction(iconFromTheme("media-record"), "Record movie");
349
      connect(recordMovie, SIGNAL(triggered(bool)), SLOT(toggleCapture(bool)), Qt::QueuedConnection);
350
      recordMovie->setCheckable (true);
351
      recordMovie->setToolTip("Record the central widget as a sequence of images. You can find the images in /tmp/gepetto-gui/record/img_%d.jpeg");
352
353
      QAction* toggleFullscreen = new QAction(iconFromTheme("view-fullscreen"),
354
          "Toggle fullscreen mode", this);
355
      toggleFullscreen->setShortcut (Qt::SHIFT | Qt::Key_F);
356
      toggleFullscreen->setCheckable (true);
357
      toggleFullscreen->setChecked (false);
358
      connect(toggleFullscreen, SIGNAL(toggled(bool)), SLOT(toggleFullscreenMode(bool)));
359
      toolBar_->addAction (toggleFullscreen);
360
361
      fullscreen_->setLayout(new QVBoxLayout);
362
      fullscreen_->layout()->setContentsMargins (0,0,0,0);
363
    }
364
365
    void OSGWidget::initGraphicsWindowsAndViewer (MainWindow* parent, const std::string& name)
366
    {
367
      osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
368
      osg::ref_ptr <osg::GraphicsContext::Traits> traits_ptr (new osg::GraphicsContext::Traits(ds));
369
      traits_ptr->windowName = name;
370
      traits_ptr->x = this->x();
371
      traits_ptr->y = this->y();
372
      traits_ptr->width = this->width();
373
      traits_ptr->height = this->height();
374
      traits_ptr->windowDecoration = false;
375
      traits_ptr->doubleBuffer = true;
376
      traits_ptr->vsync = true;
377
      //  traits_ptr->sharedContext = 0;
378
379
      graphicsWindow_ = new osgQt::GraphicsWindowQt ( traits_ptr );
380
381
      osg::Camera* camera = viewer_->getCamera();
382
      camera->setGraphicsContext( graphicsWindow_ );
383
384
      camera->setClearColor( osg::Vec4(0.2f, 0.2f, 0.6f, 1.0f) );
385
      camera->setViewport( new osg::Viewport(0, 0, traits_ptr->width, traits_ptr->height) );
386
      camera->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits_ptr->width)/static_cast<double>(traits_ptr->height), 1.0f, 10000.0f );
387
388
      viewer_->setKeyEventSetsDone(0);
389
390
      // Manipulators
391
      osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
392
      keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
393
      keyswitchManipulator->addMatrixManipulator( '2', "First person", new ::osgGA::KeyboardManipulator(graphicsWindow_));
394
      keyswitchManipulator->selectMatrixManipulator (0);
395
      viewer_->setCameraManipulator( keyswitchManipulator.get() );
396
397
      // Event handlers
398
      screenCapture_ = new osgViewer::ScreenCaptureHandler (
399
            new osgViewer::ScreenCaptureHandler::WriteToFile (
400
              parent->settings_->captureDirectory + "/" + parent->settings_->captureFilename,
401
              parent->settings_->captureExtension),
402
            1);
403
      viewer_->addEventHandler(screenCapture_);
404
      viewer_->addEventHandler(new osgViewer::HelpHandler);
405
      viewer_->addEventHandler(pickHandler_);
406
      viewer_->addEventHandler(new osgViewer::StatsHandler);
407
    }
408
409
    void OSGWidget::toggleFullscreenMode (bool fullscreenOn)
410
    {
411
      if (!isVisible()) return;
412
      if (fullscreenOn) {
413
        QDockWidget* dockOSG = qobject_cast<QDockWidget*>(this->parentWidget());
414
        if (dockOSG) {
415
          normal_ = this->parentWidget();
416
          fullscreen_->layout()->addWidget (this);
417
          fullscreen_->showFullScreen();
418
        }
419
      } else {
420
        QDockWidget* dockOSG = qobject_cast<QDockWidget*>(normal_);
421
        if (dockOSG) {
422
          fullscreen_->hide();
423
          dockOSG->setWidget (this);
424
        }
425
      }
426
    }
427
  } // namespace gui
428

3
} // namespace gepetto