ArmarXMainWindow.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25#include "ArmarXMainWindow.h"
26
27#include <filesystem>
28#include <optional>
29#include <sstream>
30
31#include <sys/types.h>
32#include <sys/wait.h>
33#include <unistd.h>
34
35#include <QActionGroup>
36#include <QComboBox>
37#include <QCompleter>
38#include <QDesktopServices>
39#include <QDialog>
40#include <QDialogButtonBox>
41#include <QFile>
42#include <QFileDialog>
43#include <QGridLayout>
44#include <QHBoxLayout>
45#include <QHostInfo>
46#include <QInputDialog>
47#include <QLabel>
48#include <QList>
49#include <QListView>
50#include <QListWidget>
51#include <QMdiArea>
52#include <QMenu>
53#include <QMessageBox>
54#include <QModelIndex>
55#include <QMutex>
56#include <QPainter>
57#include <QPlainTextEdit>
58#include <QPluginLoader>
59#include <QProxyStyle>
60#include <QPushButton>
61#include <QScrollArea>
62#include <QSignalMapper>
63#include <QSpinBox>
64#include <QSplitter>
65#include <QStackedWidget>
66#include <QStringList>
67#include <QStringListModel>
68#include <QTimer>
69#include <QToolButton>
70#include <QUrl>
71#include <QWidgetAction>
72#include <QtCore/QDirIterator>
73#include <QtSvg/QSvgRenderer>
74
75#include <SimoxUtility/algorithm/string/string_tools.h>
76
82
84#include <ArmarXGui/applications/ArmarXGui/ui_ArmarXMainWindow.h>
89
93#include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
94#include <Inventor/nodes/SoPerspectiveCamera.h>
95//#include <omp.h>
96
97// To start ArViz Godot
98#include <fcntl.h>
99
100namespace armarx
101{
102 // static const char* ARMARX_ORGANIZATION = "KIT";
103 static const char* ARMARX_GUI_APPLICATION_NAME = "ArmarX";
104
105 static const char* CONFIG_LOAD_LAST_CONFIG = "LoadLastConfig";
106 static const char* CONFIG_BLACKLISTED_TIPS = "BlacklistedTips";
107
109 const std::vector<std::string>& packages,
110 const QString& configToLoad,
111 bool disablePreloading) :
112 QMainWindow(NULL),
113 pluginCache(ArmarXManagerPtr::dynamicCast(registry)),
114 defaultPackageNames(packages),
115 widgetsLocked(false),
116 settingsFile(armarx::ArmarXDataPath::GetDefaultUserConfigPath() + "/" +
117 ARMARX_GUI_APPLICATION_NAME + ".conf"),
118 mainSettings(QString(settingsFile.c_str()), QSettings::NativeFormat)
119 {
120 setStyleSheet(R"(
121 QMainWindow::separator {
122 width: 3px; /* when vertical */
123 height: 3px; /* when horizontal */
124 }
125 QMainWindow::separator:hover {
126 background: gray;
127 }
128 )");
129
130 mainWidgetNames = QStringList() << "Meta.LogViewer"
131 << "Meta.SystemStateMonitor"
132 << "Meta.ScenarioManager"
133 << "Statecharts.StatechartEditor"
134 << "ArMem.MemoryViewer";
135
136 splashscreenPrefix = "v" + QString::fromStdString(Application::GetVersion()) + " - ";
137 QPixmap pm(QString("://icons/ArmarX-Splashscreen.png"));
138 splashSceen = new QSplashScreen(pm);
139 // splashSceen->show();
140
141 ui = new ::Ui::ArmarXMainWindow();
142 mutex3d = std::make_shared<std::recursive_mutex>();
143 ArmarXWidgetInfoPtr viewerWidgetInfo(
146 QIcon()));
147 pluginCache.cacheWidget(ARMARX_VIEWER_NAME, viewerWidgetInfo);
148
149 setTag(ARMARX_GUI_APPLICATION_NAME);
150 QString username = qgetenv("USER");
151 if (username.isEmpty())
152 {
153 username = qgetenv("USERNAME");
154 }
155
156 guiWindowBaseName = QString{ARMARX_GUI_APPLICATION_NAME};
157 setWindowTitle(guiWindowBaseName);
158 this->registry = registry;
159 setAttribute(Qt::WA_QuitOnClose);
160
161 ui->setupUi(this);
162 setCentralWidget(NULL);
163
164 tipDialog = new TipDialog(this);
165 tipDialog->setBlackListedStrings(
166 mainSettings.value(CONFIG_BLACKLISTED_TIPS).toStringList());
167 ui->actionLoadLastConfig->setChecked(mainSettings.value(CONFIG_LOAD_LAST_CONFIG).toBool());
168
169 addWidgetAction = new AddArmarXWidgetAction("", this, this);
170 connect(addWidgetAction,
171 SIGNAL(clicked(QString, QString)),
172 this,
173 SLOT(createArmarXWidget(QString, QString)),
174 Qt::UniqueConnection);
175
176
177 connect(ui->actionLoadPlugin, SIGNAL(triggered()), this, SLOT(pluginDialog()));
178 connect(ui->actionSave_Gui, SIGNAL(triggered()), this, SLOT(saveGuiConfig()));
179 connect(ui->actionLoad_Gui_Config, SIGNAL(triggered()), this, SLOT(loadGuiConfig()));
180 connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close()));
181 connect(ui->actionSave_Gui_as, SIGNAL(triggered()), this, SLOT(saveGuiConfigAs()));
182 connect(ui->actionFullscreen, SIGNAL(triggered()), this, SLOT(enterFullscreenMode()));
183 connect(ui->actionLock_Widgets, SIGNAL(triggered()), this, SLOT(toggleWidgetLock()));
184
185 // EmergencyStop
186 emergencyStopWidget = EmergencyStopWidgetPtr::dynamicCast(
188 this->registry->addObject(ManagedIceObjectPtr::dynamicCast(emergencyStopWidget));
189
190 // Battery widget
191 batteryWidget = BatteryWidgetPtr::dynamicCast(
193 this->registry->addObject(ManagedIceObjectPtr::dynamicCast(batteryWidget));
194
195 // plugins
196 QSet<QString> pluginDirs;
197
198 for (const std::string& packageName : packages)
199 {
200 pluginDirs.insert(getLibraryPathFromPackage(packageName.c_str()));
201 }
202 loadPlugins(pluginDirs, false);
203 this->enteredOnce = true;
204 if (!disablePreloading)
205 {
206 pluginCache.preloadAsync(getFavoriteWidgets() + mainWidgetNames);
207 }
208 // instantiatePlugins();
209
211
212
213 connectionStatusTimer = new QTimer(this);
214 connect(connectionStatusTimer, SIGNAL(timeout()), this, SLOT(updateStatusOfOpenWidgets()));
215 connectionStatusTimer->start(300);
216 //changeLayout(2);
217 QStringList recentlyFiles = mainSettings.value("RecentlyFiles").toStringList();
218 splashSceen->finish(this);
219
220 guiUseCaseSelector = new GuiUseCaseSelector(
221 this, mainSettings.value("DoNotShowUseCaseDialog").toBool(), this);
222
223
224 if (!configToLoad.isEmpty())
225 {
226 loadGuiConfig(configToLoad);
227 }
228 else if (!mainSettings.value("DoNotShowUseCaseDialog").toBool() &&
229 guiUseCaseSelector->exec() == QDialog::Accepted)
230 {
231 QString path = guiUseCaseSelector->getSelectedConfigFilePath();
232 ARMARX_INFO << VAROUT(path);
233 if (!path.isEmpty())
234 {
235 loadGuiConfig(path, false);
236 }
237 }
238 else if (recentlyFiles.size() > 0 && mainSettings.value(CONFIG_LOAD_LAST_CONFIG).toBool())
239 {
240 //set to false in case a plugin crashes the gui
241 mainSettings.setValue(CONFIG_LOAD_LAST_CONFIG, false);
242 mainSettings.sync();
243 loadGuiConfig(recentlyFiles.first());
244 mainSettings.setValue(CONFIG_LOAD_LAST_CONFIG, ui->actionLoadLastConfig->isChecked());
245 mainSettings.sync();
246 }
247 else
248 {
249 QStringList defaultWidgets{"Meta.LogViewer"};
250 for (auto& widgetName : defaultWidgets)
251 {
252 if (existsWidget(widgetName))
253 {
254 createArmarXWidget(widgetName, widgetName);
255 }
256 }
257 }
258
259 mainSettings.setValue("DoNotShowUseCaseDialog", guiUseCaseSelector->getDoNotShowAgain());
260 }
261
263 {
264 delete ui;
265 connectionStatusTimer->stop();
266 mainSettings.setValue(CONFIG_BLACKLISTED_TIPS, tipDialog->getBlackListedStrings());
267 ARMARX_INFO << "~GuiWindow() ";
268 delete splashSceen;
269 }
270
271 void
272 ArmarXMainWindow::removeViewerWidget(QObject* widget)
273 {
274 ARMARX_WARNING << "Removing 3D viewer" << std::endl;
275
276 if (!widget)
277 {
278 ARMARX_INFO << "viewerDockWidget ptr is NULL";
279 }
280
281 removeArmarXWidget(widget);
282 }
283
285 ArmarXMainWindow::setupViewerWidget()
286 {
287 if (listOpenWidgets.find(ARMARX_VIEWER_NAME) != listOpenWidgets.end())
288 {
289 return Viewer3DWidgetPtr::dynamicCast(
290 listOpenWidgets.find(ARMARX_VIEWER_NAME).value().second);
291 }
292
293 Viewer3DWidgetPtr viewer = Viewer3DWidgetPtr::dynamicCast(
294 createArmarXWidget(ARMARX_VIEWER_NAME, ARMARX_VIEWER_NAME));
295
296 // ensure that all drawings are protected with a mutex
297 viewer->setMutex3D(mutex3d);
298
299 return viewer;
300 }
301
302 void
303 ArmarXMainWindow::pluginDialog()
304 {
305 QFileDialog dialog(this);
306 dialog.setFileMode(QFileDialog::ExistingFiles);
307 dialog.setNameFilter(tr("Libraries (*.dll *.so *.dylib)"));
308 QStringList fileNames;
309
310 if (dialog.exec())
311 {
312 fileNames = dialog.selectedFiles();
313 }
314
315 foreach (QString filePath, fileNames)
316 {
317 pluginCache.removePluginFromCache(filePath);
318 ARMARX_INFO << filePath.toStdString() << armarx::flush;
319 loadPlugin(filePath);
320 }
321 }
322
323 void
324 ArmarXMainWindow::loadPlugins(const QStringList& fileNames)
325 {
326 const QStringList suffixes = {
327 "_qt_plugin.so", "GuiPlugin.so", "GuiPlugin.dll", "GuiPlugin.dylib"};
328 QStringList guiFiles;
329 for (const QString& fileName : fileNames)
330 {
331 // only try to load files ending with relevant suffixes
332 for (const QString& suffix : suffixes)
333 {
334 if (fileName.endsWith(suffix))
335 {
336 guiFiles.push_back(fileName);
337 break;
338 }
339 }
340 }
341
342 auto start = IceUtil::Time::now();
343 for (int i = 0; i < guiFiles.size(); i++)
344 {
345 QString fileName = guiFiles.at(i);
346 if ((IceUtil::Time::now() - start).toSeconds() >= 2)
347 {
348 splashSceen->show();
349 splashSceen->showMessage(splashscreenPrefix + fileName,
350 Qt::AlignJustify | Qt::AlignBottom);
351 qApp->processEvents();
352 }
353 pluginCache.cachePlugin(fileName);
354 }
355 updateAvailableWidgetList();
356 }
357
358 QAction*
359 ArmarXMainWindow::addActionToToolBar(QAction* action, bool allowText)
360 {
361 QToolButton* button = new QToolButton(ui->toolBar);
362
363 std::optional<QSize> textSize;
364 if (allowText)
365 {
366 button->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
367 button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
368
369 // Get the width of the button when just showing text.
370 button->setText(action->text());
371 textSize = button->sizeHint();
372 }
373
374 button->setDefaultAction(action);
375 if (not action->icon().isNull())
376 {
377 const QSize maxSize{256, 24};
378 const QSize iconSize = action->icon().actualSize(maxSize);
379 // Setting the action has already set the icon, so this line is unnecessary.
380 // button->setIcon(action->icon());
381 button->setIconSize(iconSize);
382 button->setFixedSize(textSize.has_value() ? QSize{textSize->width() + iconSize.width(),
383 iconSize.height()}
384 : iconSize);
385 }
386 return ui->toolBar->addWidget(button);
387 }
388
389 QString
391 {
392 CMakePackageFinder packageFinder(packageName.toStdString());
393 std::string libPaths = packageFinder.getLibraryPaths();
394 QString dir = QString::fromStdString(libPaths);
395 dir = QDir::cleanPath(dir);
396
397 return dir;
398 }
399
400 QStringList
402 {
403 auto widgetUsageHistory = mainSettings.value("WidgetUsageHistory").toList();
404 std::map<QString, int> widgetUsage;
405 for (auto& widgetName : widgetUsageHistory)
406 {
407 if (widgetUsage.count(widgetName.toString()) == 0)
408 {
409 widgetUsage[widgetName.toString()] = 1;
410 }
411 else
412 {
413 widgetUsage[widgetName.toString()] += 1;
414 }
415 }
416 std::multimap<int, QString> ranking;
417 for (auto it = widgetUsage.begin(); it != widgetUsage.end(); it++)
418 {
419 ranking.insert(std::make_pair(it->second, it->first));
420 }
421 int i = 0;
422 const int favCount = mainSettings.value("FavoriteWidgetCount", 6).toInt();
423 QStringList favoriteWidgetNames;
424 for (auto it = ranking.rbegin(); it != ranking.rend() && i < favCount; it++)
425 {
426 auto& widgetName = it->second;
427 if (!mainWidgetNames.contains(widgetName))
428 {
429 favoriteWidgetNames << widgetName;
430 i++;
431 }
432 }
433 return favoriteWidgetNames;
434 }
435
436 void
438 {
439 QSet<QString> pluginDirs;
440 splashSceen->showMessage(splashscreenPrefix + packageName,
441 Qt::AlignJustify | Qt::AlignBottom);
442 qApp->processEvents();
443
444 ARMARX_INFO << "Looking for gui plugins in package " << packageName;
445 QString dir = getLibraryPathFromPackage(packageName);
446 pluginDirs.insert(dir);
447 loadedPackages << packageName;
448 loadPlugins(pluginDirs, false);
449 }
450
451 void
452 ArmarXMainWindow::loadPlugins(const QString& pluginDir, bool searchRecursive)
453 {
454 QSet<QString> dirs;
455 dirs.insert(pluginDir);
456 loadPlugins(dirs, searchRecursive);
457 }
458
459 void
460 ArmarXMainWindow::loadPlugins(const QSet<QString>& pluginDirs, bool searchRecursive)
461 {
462 // only search paths ending with lib
463 QStringList filters;
464 filters << "*.so"
465 << "*.dll"
466 << "*.dylib";
467 QList<QDir> directoriesToCheck;
468 QStringList allFileNames;
469
470 for (const auto& pluginDir : pluginDirs)
471 {
472 directoriesToCheck.push_back(QDir(pluginDir));
473
474 while (directoriesToCheck.size() > 0)
475 {
476 ARMARX_VERBOSE_S << "Checking dir : " + directoriesToCheck.front().absolutePath()
477 << std::endl;
478 splashSceen->showMessage(splashscreenPrefix +
479 directoriesToCheck.front().absolutePath(),
480 Qt::AlignJustify | Qt::AlignBottom);
481 qApp->processEvents();
482 QDir currentPluginDir = directoriesToCheck.front();
483 directoriesToCheck.pop_front();
484 QString curPath = currentPluginDir.path();
485
486 if (curPath.length() == 0)
487 {
488 continue;
489 }
490
491 QStringList fileNames = currentPluginDir.entryList(filters, QDir::Files);
492
493 for (auto& fileName : fileNames)
494 {
495 fileName = currentPluginDir.absoluteFilePath(fileName);
496 }
497
498 allFileNames.append(fileNames);
499
500 if (searchRecursive)
501 {
502 QDirIterator directoryIterator(currentPluginDir.absolutePath(),
503 QStringList(),
504 QDir::Dirs | QDir::NoDotAndDotDot,
505 QDirIterator::Subdirectories);
506
507 while (directoryIterator.hasNext())
508 {
509 directoryIterator.next();
510 directoriesToCheck.push_back(QDir(directoryIterator.filePath()));
511 }
512 }
513 }
514 }
515
516 loadPlugins(allFileNames);
517 }
518
519 bool
520 ArmarXMainWindow::existsWidget(const QString& widgetName)
521 {
522 auto list = pluginCache.getAvailableWidgetNames();
523 return list.contains(widgetName);
524 }
525
526 void
528 {
529 QFileInfo pathInfo(filePath);
530 QString fileName(pathInfo.fileName());
531 pluginCache.cachePlugin(filePath);
532 ARMARX_INFO << "Plugin Path: " << pathInfo.absoluteFilePath().toStdString()
533 << armarx::flush;
534 updateAvailableWidgetList();
535 // if (!loadedPluginFilepaths.contains(pathInfo.absoluteFilePath()))
536 // {
537 // QPluginLoader loader(filePath);
538 // // ARMARX_INFO << "loader created" << armarx::flush;
539 // instantiatePlugin(loader);
540 // }
541 // else
542 // {
543 // ARMARX_INFO << "Plugin already loaded" << armarx::flush;
544 // }
545 }
546
547 void
549 {
550 emit closeRequest();
551 event->accept();
552 }
553
554 void
555 ArmarXMainWindow::closeAllWidgets()
556 {
557
558
559 OpenWidgetMap listOpenWidgetsTemp = listOpenWidgets;
560 OpenWidgetMap::iterator it = listOpenWidgetsTemp.begin();
561
562 for (; it != listOpenWidgetsTemp.end(); ++it)
563 {
564 ARMARX_INFO << "Closing widget " << it.key() << " size: " << listOpenWidgetsTemp.size();
565 QDockWidget* w = it.value().first;
566
567 if (w)
568 {
569 if (!w->close())
570 {
571 ARMARX_WARNING << "Could not close widget!" << std::endl;
572 }
573 }
574 }
575
576 // if (viewerDockWidget)
577 // removeViewerWidget(viewerDockWidget);
578
579 // causes segfault during redraw of 3d scene ?!
580 //QApplication::instance()->processEvents();
581
582 ARMARX_VERBOSE << "Closed all widgets";
583 listOpenWidgets.clear();
584
585 this->configFilepath = "";
586 appendFileToWindowTitle(configFilepath);
587 }
588
589 void
590 ArmarXMainWindow::closeAllWidgetsWithDialog()
591 {
592 QMessageBox dialog(QMessageBox::Question,
593 tr("Closing all widgets"),
594 tr("Do you really want to close all widgets?"));
595 dialog.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
596 dialog.setDefaultButton(QMessageBox::Yes);
597
598 if (dialog.exec() == QMessageBox::Yes)
599 {
600 closeAllWidgets();
601 }
602 }
603
604 void
606 {
607
608 QDir path(configFilepath);
609 configFilepath = path.absolutePath();
610
611 QStringList recentlyFiles = mainSettings.value("RecentlyFiles").toStringList();
612 recentlyFiles.removeAll(configFilepath);
613 recentlyFiles.push_front(configFilepath);
614
615 if (recentlyFiles.size() > 10)
616 {
617 recentlyFiles.pop_back();
618 }
619
620 mainSettings.setValue("RecentlyFiles", recentlyFiles);
621 }
622
623 std::vector<std::string>
625 {
626 return defaultPackageNames;
627 }
628
631 {
632 if (listOpenWidgets.find(ARMARX_VIEWER_NAME) != listOpenWidgets.end())
633 {
634 return Viewer3DWidgetPtr::dynamicCast(
635 listOpenWidgets.find(ARMARX_VIEWER_NAME).value().second);
636 }
637 else
638 {
639 return NULL;
640 }
641 }
642
643 void
644 ArmarXMainWindow::loadGuiConfig(QString configFilepath, bool showAsOpenGuiConfig)
645 {
646
647 if (configFilepath.length() == 0)
648 {
649 QFileDialog dialog(this);
650 dialog.setFileMode(QFileDialog::ExistingFile);
651 dialog.setAcceptMode(QFileDialog::AcceptOpen);
652 QStringList nameFilters;
653 nameFilters << "ArmarX Gui-Files (*.armarxgui)"
654 << "All Files (*.*)";
655 dialog.setNameFilters(nameFilters);
656
657 if (dialog.exec())
658 {
659 if (dialog.selectedFiles().size() == 0)
660 {
661 return;
662 }
663
664 configFilepath = dialog.selectedFiles()[0];
665 }
666 else
667 {
668 ARMARX_INFO << "load config dialog canceled" << std::endl;
669 return;
670 }
671 }
672
673
674 closeAllWidgets();
675
676 QDir dir(configFilepath);
677 dir.makeAbsolute();
678 configFilepath = dir.absolutePath();
679
680 if (!QFile::exists(configFilepath))
681 {
682 ArmarXWidgetController::showMessageBox("GUI configuration file '" + configFilepath +
683 "' does not exist.");
684 return;
685 }
686
687 ARMARX_VERBOSE << "Loading config file: " << configFilepath;
688
689 if (showAsOpenGuiConfig)
690 {
691 addToRecentlyOpenedFileList(configFilepath);
693 }
694
695 QSettings settings(configFilepath, QSettings::IniFormat);
696
697
698 if (settings.allKeys().size() == 0)
699 {
700 ArmarXWidgetController::showMessageBox("Could not find any settings in '" +
701 configFilepath + "'!");
702 return;
703 }
704
705 // QStringList packagesToLoad = settings.value("loadedPackages").toStringList();
707
708 // force deterministic loading of plugins by name
709 std::sort(packagesStd.begin(),
710 packagesStd.end(),
711 [](const std::string& lhs, const std::string& rhs) -> bool
712 { return simox::alg::to_lower(lhs) < simox::alg::to_lower(rhs); });
713
714 QStringList packagesToLoad;
715 ARMARX_VERBOSE << "Discovered: ";
716 for (const auto& pkg : packagesStd)
717 {
718 packagesToLoad.push_back(QString::fromStdString(pkg));
719 ARMARX_VERBOSE << "- " << pkg;
720 }
721
722 settings.setValue("loadedPackages", packagesToLoad);
723
724 ARMARX_INFO << "Loaded packages" << packagesStd;
725
726 foreach (QString package, packagesToLoad)
727 {
728 loadPluginsFromPackage(package);
729 }
730
731 QStringList widgetNames = settings.value("WidgetCustomNames").toStringList();
732 foreach (QString widgetCustomName, widgetNames)
733 {
734 settings.beginGroup(widgetCustomName);
735 // if(widgetCustomName != ARMARX_VIEWER_NAME || !view3D)
736 {
737 ARMARX_INFO << "Creating widget " << settings.value("WidgetBaseName").toString()
738 << "," << widgetCustomName;
739 ArmarXWidgetControllerPtr widget = createArmarXWidget(
740 settings.value("WidgetBaseName").toString(), widgetCustomName, &settings);
741 // if(widgetCustomName == ARMARX_VIEWER_NAME && !view3D)
742 // view3D = Viewer3DWidgetPtr::dynamicCast(widget);
743 }
744 qApp->processEvents();
745 // widget->getWidget()->resize(settings.value("widgetWidth").toInt(), settings.value("widgetHeight").toInt());<-- not working
746 settings.endGroup();
747 }
748 auto geometryByteArray = settings.value("MainWindowGeometry").toByteArray();
749 if (geometryByteArray.size() > 0)
750 {
751 // for legacy
752 restoreGeometry(geometryByteArray);
753 }
754 else
755 {
756 QRect geometry = settings.value("MainWindowGeometry").toRect();
757 setGeometry(geometry);
758 }
759
760 if (!restoreState(settings.value("DockWidgetsState").toByteArray()))
761 {
762 ARMARX_WARNING << "Failed to restore state";
763 }
764
765 foreach (QString widgetCustomName, widgetNames)
766 {
767 settings.beginGroup(widgetCustomName);
768 OpenWidgetMap::iterator it = listOpenWidgets.find(widgetCustomName);
769
770 if (it != listOpenWidgets.end())
771 {
772 it->first->resize(settings.value("widgetWidth").toInt(),
773 settings.value("widgetHeight").toInt());
774 }
775
776 settings.endGroup();
777 }
778 if (showAsOpenGuiConfig)
779 {
780 this->configFilepath = configFilepath;
781 appendFileToWindowTitle(configFilepath);
782 }
783 updateOpenWidgetList();
784 QFileInfo file(configFilepath);
785 statusBar()->showMessage("'" + file.fileName() + "' loaded.", 10000);
786 }
787
788 void
789 ArmarXMainWindow::saveGuiConfig()
790 {
791 if (configFilepath.length() == 0)
792 {
793 QFileDialog dialog(this);
794 dialog.setFileMode(QFileDialog::AnyFile);
795 dialog.setAcceptMode(QFileDialog::AcceptSave);
796 QStringList nameFilters;
797 nameFilters << "ArmarX Gui-Files (*.armarxgui)"
798 << "All Files (*.*)";
799 dialog.setNameFilters(nameFilters);
800
801 if (dialog.exec())
802 {
803 if (dialog.selectedFiles().size() == 0)
804 {
805 return;
806 }
807
808 QString file = dialog.selectedFiles()[0];
809 QFileInfo fileInfo(file);
810
811 if (fileInfo.suffix().isEmpty())
812 {
813 file += ".armarxgui";
814 }
815
816 configFilepath = file;
817 }
818 else
819 {
820 return;
821 }
822 }
823
824
825 QSettings settings(configFilepath, QSettings::IniFormat);
826 settings.clear();
827 // first make all libpaths relative
828 // for (int i = 0; i < loadedPluginFilepaths.size(); ++i) {
829 // loadedPluginFilepaths[i] = QString::fromStdString(loadedPluginFilepaths[i].toStdString());
830 // }
831 settings.setValue("loadedPackages", loadedPackages);
832 settings.setValue("MainWindowGeometry", geometry());
833 settings.setValue("DockWidgetsState", saveState());
834 QStringList widgetCustomNames;
835 OpenWidgetMap::iterator it = listOpenWidgets.begin();
836
837 for (; it != listOpenWidgets.end(); it++)
838 {
839 QString prefix = it.value().first->objectName();
840 ARMARX_VERBOSE << "Saving widget: " << prefix.toStdString();
841 settings.beginGroup(prefix);
842 ArmarXWidgetControllerPtr w = ArmarXWidgetControllerPtr::dynamicCast(it.value().second);
843
844 if (w /*&& prefix != ARMARX_VIEWER_NAME*/)
845 {
846 settings.setValue("WidgetBaseName", w->getWidgetName());
847 settings.setValue("widgetWidth", w->getWidget()->width());
848 settings.setValue("widgetHeight", w->getWidget()->height());
849 w->saveSettings(&settings);
850 widgetCustomNames.push_back(prefix);
851 }
852 else
853 {
854 ARMARX_WARNING << "Could not cast widget: " << prefix;
855 }
856
857 settings.endGroup();
858 }
859
860 settings.setValue("WidgetCustomNames", widgetCustomNames);
861 ARMARX_INFO << "Saved config to " << configFilepath;
862 appendFileToWindowTitle(configFilepath);
863
864 // update recently opened file list
865 addToRecentlyOpenedFileList(configFilepath);
867 QFileInfo file(configFilepath);
868 statusBar()->showMessage("'" + file.fileName() + "' saved.", 10000);
869 }
870
871 void
872 ArmarXMainWindow::saveGuiConfigAs()
873 {
874 configFilepath.clear();
875 saveGuiConfig();
876 }
877
878 void
880 {
881 if (filepath.length() > 0)
882 {
883 QDir path(filepath);
884 std::filesystem::path file(filepath.toStdString());
885 path.cdUp();
886
887 setWindowTitle(guiWindowBaseName + " - " +
888 QString::fromStdString(file.filename().string()) + " in " +
889 path.absolutePath());
890 }
891 else
892 {
893 setWindowTitle(guiWindowBaseName);
894 }
895 }
896
898 ArmarXMainWindow::createArmarXWidget(QString widgetName,
899 QString customInstanceName,
900 QSettings* settings)
901 {
902
903 if (listOpenWidgets.find("Dock" + customInstanceName) != listOpenWidgets.end())
904 {
905 ARMARX_WARNING << "A Widget with that name already exists";
906 return NULL;
907 }
908
909 // HACK: Creating an OpenGL context fails after libPointCloudViewerGuiPlugin.so is loaded.
910 // Therefore, we need to check before calling getWidgetCreator since this loads
911 // the shared object. If we do not do this here, then the following error occurs:
912 // "Can't set up a valid opengl canvas something is seriously wrong with the system"
913 if (widgetName == "VisionX.PointCloudViewer")
914 {
915 ARMARX_INFO << "Creating SoQtExaminerViewer for " << widgetName.toStdString();
916 SoQtExaminerViewer(nullptr, "", TRUE, SoQtExaminerViewer::BUILD_NONE);
917 }
918
919 auto creator = pluginCache.getWidgetCreator(widgetName);
920
921 if (!creator)
922 {
923 ArmarXWidgetController::showMessageBox("Could not find widget with Name: " +
924 widgetName);
925 return NULL;
926 }
927 auto widgetUsage = mainSettings.value("WidgetUsageHistory").toList();
928 widgetUsage.push_back(widgetName);
929 if (widgetUsage.size() > widgetUsageHistoryLength)
930 {
931 widgetUsage.pop_front();
932 }
933 mainSettings.setValue("WidgetUsageHistory", widgetUsage);
934
935
936 ArmarXWidgetControllerPtr w = creator->createInstance();
937
938 w->setMainWindow(this);
939 w->setInstanceName(customInstanceName);
940 w->setTipDialog(tipDialog);
941
942 if (settings)
943 {
944 w->loadSettings(settings);
945 }
946 else if (w->getConfigDialog(this))
947 {
948
950 ManagedIceObjectPtr::dynamicCast(w->getConfigDialog().data());
951
952 if (comp)
953 {
954 comp->__setNoDelete(true);
955 registry->addObject(comp, false);
956 }
957
958 w->getConfigDialog()->setModal(true);
959 connect(
960 w->getConfigDialog().data(), SIGNAL(accepted()), w.get(), SLOT(configAccepted()));
961 connect(w.get(),
962 SIGNAL(configAccepted(ArmarXWidgetControllerPtr)),
963 this,
964 SLOT(addArmarXWidget(ArmarXWidgetControllerPtr)));
965 connect(
966 w->getConfigDialog().data(), SIGNAL(rejected()), w.get(), SLOT(configRejected()));
967 connect(w.get(),
968 SIGNAL(configRejected(ArmarXWidgetControllerPtr)),
969 this,
970 SLOT(addArmarXWidgetCanceled(ArmarXWidgetControllerPtr)));
971
972 pendingWidgets.push_back(w);
973 w->getConfigDialog()->show();
974 w->getConfigDialog()->raise();
975 w->getConfigDialog()->activateWindow();
976 return w;
977 }
978
979 // if we are loading a gui config we do not want to create the viewer widget
980 // it will be created later during the loading process
981 addArmarXWidget(w, !settings);
982 return w;
983 }
984
986 ArmarXMainWindow::addArmarXWidget(ArmarXWidgetControllerPtr newWidgetController,
987 bool createViewerWidget)
988 {
989 for (unsigned int i = 0; i < pendingWidgets.size(); ++i)
990 {
991 if (pendingWidgets[i].get() == newWidgetController.get())
992 {
993 pendingWidgets.erase(pendingWidgets.begin() + i);
994 break;
995 }
996 }
997
998 QString widgetName = newWidgetController->getWidgetName();
999 QString customInstanceName = newWidgetController->getInstanceName();
1000
1001 if (listOpenWidgets.find(customInstanceName) != listOpenWidgets.end())
1002 {
1003 return NULL;
1004 }
1005
1006 ARMARX_VERBOSE << "Adding new widget: " << customInstanceName;
1007 assert(widgetName == newWidgetController->getWidgetName());
1008 ArmarXDockWidget* dockWidget =
1009 new ArmarXDockWidget(customInstanceName, newWidgetController);
1010 //w->connectDestroySlot();
1011 connect(dockWidget, SIGNAL(destroyed(QObject*)), this, SLOT(removeArmarXWidget(QObject*)));
1012
1013 dockWidget->setArmarXWidget(newWidgetController->getWidget());
1014 dockWidget->setObjectName(customInstanceName);
1015 StatusDockWidgetTitleBar* customTitlebar = new StatusDockWidgetTitleBar(dockWidget, this);
1016 dockWidget->setTitleBarWidget(customTitlebar);
1017 customTitlebar->addCustomWidget(newWidgetController->getCustomTitlebarWidget(dockWidget));
1018
1019 if (listOpenWidgets.find(dockWidget->objectName()) != listOpenWidgets.end())
1020 {
1021 ArmarXWidgetController::showMessageBox("A Widget with the title '" +
1022 customInstanceName + "' already exists.");
1023 delete dockWidget;
1025 }
1026
1027 QDockWidget* biggestOpenDockWidget = getBiggestOpenWidget();
1028 listOpenWidgets[customInstanceName] =
1029 qMakePair<QDockWidget*, ArmarXWidgetControllerPtr>(dockWidget, newWidgetController);
1030 addDockWidget(Qt::RightDockWidgetArea, dockWidget);
1031 newWidgetController->postDocking();
1032
1033 QSize widgetStartSize = newWidgetController->getWidget()->size();
1034 if (widgetStartSize.width() * widgetStartSize.height() >= 400 * 400)
1035 if (biggestOpenDockWidget)
1036 {
1037 tabifyDockWidget(biggestOpenDockWidget, dockWidget);
1038 }
1039
1040 dockWidget->show();
1041 dockWidget->raise();
1042
1043 updateOpenWidgetList();
1044
1045 // set it even if no 3d scene may be currently available
1046 newWidgetController->setMutex3D(mutex3d);
1047
1048 QApplication::instance()
1049 ->processEvents(); // process events so that the widgets are completetly intialized?
1050 ManagedIceObjectPtr comp = ManagedIceObjectPtr::dynamicCast(newWidgetController);
1051
1052 if (comp)
1053 {
1054 registry->addObject(comp, false);
1055 ARMARX_INFO << "Waiting for widget initialization " << widgetName << armarx::flush;
1056 int timeoutMs = 30000;
1057
1058 if (!comp->getObjectScheduler()->waitForObjectState(eManagedIceObjectInitialized,
1059 timeoutMs))
1060 {
1061 ARMARX_WARNING << "Widget " << customInstanceName
1062 << " was not connected to Ice after " << timeoutMs / 1000
1063 << " seconds" << std::endl;
1064 }
1065 }
1066
1067 ARMARX_INFO << "Widget initialization done for " << widgetName << armarx::flush;
1068
1069
1070 SoNode* node = newWidgetController->getScene();
1071
1072 if (node)
1073 {
1074 Viewer3DWidgetPtr viewerInstance;
1075 ARMARX_INFO << "3D Scene available in widget " << widgetName << armarx::flush;
1076 OpenWidgetMap::Iterator it = listOpenWidgets.begin();
1077
1078 for (; it != listOpenWidgets.end(); it++)
1079 {
1080 viewerInstance = Viewer3DWidgetPtr::dynamicCast(it.value().second);
1081
1082 if (viewerInstance)
1083 {
1084 break;
1085 }
1086 }
1087
1088 if (!viewerInstance && createViewerWidget)
1089 // if(listOpenWidgets.find(ARMARX_VIEWER_NAME) == listOpenWidgets.end())
1090 {
1091 ARMARX_INFO << "Setting up CoinQt Viewer";
1092 viewerInstance = setupViewerWidget();
1093 }
1094
1095 SoSeparator* sep = new SoSeparator;
1096 sep->ref();
1097
1098 SoPerspectiveCamera* camera = new SoPerspectiveCamera;
1099
1100 sep->addChild(camera);
1101 sep->addChild(node);
1102
1103 viewer3DMap[customInstanceName] = {sep, newWidgetController->getSceneConfigDialog()};
1104 emit updateSceneList(viewer3DMap);
1105 }
1106
1107
1108 return newWidgetController;
1109 }
1110
1111 void
1112 ArmarXMainWindow::addArmarXWidgetCanceled(ArmarXWidgetControllerPtr newWidgetController)
1113 {
1114 for (unsigned int i = 0; i < pendingWidgets.size(); ++i)
1115 {
1116 if (pendingWidgets[i].get() == newWidgetController.get())
1117 {
1118 pendingWidgets.erase(pendingWidgets.begin() + i);
1119 break;
1120 }
1121 }
1122 }
1123
1124 void
1125 ArmarXMainWindow::removeArmarXWidget(QObject* widget)
1126 {
1127 ARMARX_DEBUG << "removing widgetname: " << widget->objectName();
1128 QDockWidget* dockWidget = qobject_cast<QDockWidget*>(widget);
1129
1130
1131 OpenWidgetMap::iterator it = listOpenWidgets.begin();
1132
1133 for (; it != listOpenWidgets.end(); it++)
1134 {
1135 if (widget == it.value().first)
1136 {
1137
1138 QString widgetName;
1139
1140 if (it.value().second)
1141 {
1142 widgetName = it.value().second->getInstanceName();
1143 }
1144 else
1145 {
1146 if (it.value().first)
1147 {
1148 widgetName = it.value().first->objectName();
1149 }
1150 }
1151
1152
1153 listOpenWidgets.erase(it);
1154 updateOpenWidgetList();
1155
1156
1157 if (viewer3DMap.contains(widgetName) /*&& view3D->cb3DViewers*/)
1158 {
1159 auto& node = viewer3DMap[widgetName].node;
1160
1161 if (node)
1162 {
1163 node->unref();
1164 }
1165
1166 ARMARX_DEBUG << "Removing from 3D list: " << widgetName;
1167 viewer3DMap.remove(widgetName);
1168 emit updateSceneList(viewer3DMap);
1169 }
1170
1171 break;
1172 }
1173 }
1174
1175 removeDockWidget(dockWidget);
1176 }
1177
1178 QPointer<QDockWidget>
1179 ArmarXMainWindow::getBiggestOpenWidget()
1180 {
1181 OpenWidgetMap::iterator it = listOpenWidgets.begin();
1182 QPointer<QDockWidget> biggestWidget = NULL;
1183
1184 for (; it != listOpenWidgets.end(); it++)
1185 {
1186 QDockWidget* dockWidget = it.value().first;
1187
1188 if (!biggestWidget ||
1189 dockWidget->size().width() * dockWidget->size().height() >
1190 biggestWidget->size().width() * biggestWidget->size().height())
1191 {
1192 biggestWidget = dockWidget;
1193 }
1194 }
1195
1196 return biggestWidget;
1197 }
1198
1199 QMenu*
1200 ArmarXMainWindow::getCategoryMenu(const std::string& categoryString,
1201 QMenu* menu,
1202 QIcon categoryIcon)
1203 {
1204 Ice::StringSeq items = simox::alg::split(categoryString, ".");
1205
1206 if (items.size() <= 1)
1207 {
1208 return menu;
1209 }
1210
1211 auto actions = menu->actions();
1212
1213 for (QAction* action : actions)
1214 {
1215 if (action->text().toStdString() == *items.begin())
1216 {
1217 items.erase(items.begin(), items.begin() + 1);
1218 std::string rest = simox::alg::join(items, ".");
1219
1220 if (!action->menu())
1221 {
1222 action->setMenu(new QMenu(QString::fromStdString(*items.begin()), this));
1223 }
1224
1225 if (action->icon().isNull() && !categoryIcon.isNull())
1226 {
1227 action->setIcon(categoryIcon);
1228 action->setIconVisibleInMenu(true);
1229 }
1230
1231 return getCategoryMenu(rest, action->menu(), categoryIcon);
1232 }
1233 }
1234
1235 return menu->addMenu(QString::fromStdString(*items.begin()));
1236 }
1237
1238 void
1239 ArmarXMainWindow::updateAvailableWidgetList()
1240 {
1241 if (enteredOnce)
1242 {
1243 updatefavoriteActions();
1244 return;
1245 }
1246
1247 ui->menuAdd_Widget->clear();
1248 searchField = new QLineEdit(ui->menuAdd_Widget);
1249 searchField->setToolTip("Search and add a new widget from all loaded ArmarX Gui Plugins");
1250 searchField->setMaximumWidth(250);
1251 searchField->setPlaceholderText("Widget Search");
1252 ui->toolBar->clear();
1253
1254 QStringList widgetNameList;
1255
1256
1257 // EmergencyStop
1258 ui->toolBar->addWidget(emergencyStopWidget->getButtonWidget());
1259
1260 // Battery Widget
1261 ui->toolBar->addWidget(batteryWidget->getBatteryWidget());
1262
1263 ui->toolBar->addSeparator();
1264
1265 ui->toolBar->setIconSize(QSize(256, 24));
1266
1267 QMap<QString, QAction*> actionsForToolBar;
1268
1269 actionList.clear();
1270 for (const auto& [fullWidgetName, widgetInfo] : pluginCache.getAvailableWidgets())
1271 {
1272 QString widgetName = fullWidgetName;
1273 widgetName = widgetName.remove(0, widgetName.lastIndexOf(".") + 1);
1274 widgetNameList << fullWidgetName;
1275 QIcon categoryIcon;
1276 if (widgetInfo)
1277 {
1278 categoryIcon = widgetInfo->getCategoryIcon();
1279 }
1280 else
1281 {
1282 if (QFile::exists(PluginCache::GetCategoryIconPath(fullWidgetName)))
1283 {
1284 categoryIcon = QIcon(PluginCache::GetCategoryIconPath(fullWidgetName));
1285 }
1286 }
1287 auto menu =
1288 getCategoryMenu(fullWidgetName.toStdString(), ui->menuAdd_Widget, categoryIcon);
1289 AddArmarXWidgetAction* action = new AddArmarXWidgetAction(widgetName, menu, this);
1290 QIcon widgetIcon;
1291 if (widgetInfo)
1292 {
1293 widgetIcon = widgetInfo->getIcon();
1294 }
1295 else
1296 {
1297 if (QFile::exists(PluginCache::GetIconPath(fullWidgetName)))
1298 {
1299 widgetIcon = QIcon(PluginCache::GetIconPath(fullWidgetName));
1300 }
1301 }
1302 action->setIcon(widgetIcon);
1303 action->setIconVisibleInMenu(true);
1304
1305 action->setData(fullWidgetName);
1306 menu->addAction(action);
1307
1308 if (mainWidgetNames.contains(fullWidgetName))
1309 {
1310 actionsForToolBar[fullWidgetName] = action;
1311 }
1312 actionList[fullWidgetName] = action;
1313 connect(
1314 action, SIGNAL(triggered()), action, SLOT(addArmarXWidget()), Qt::UniqueConnection);
1315 connect(action,
1316 SIGNAL(clicked(QString, QString)),
1317 this,
1318 SLOT(createArmarXWidget(QString, QString)),
1319 Qt::UniqueConnection);
1320 }
1321
1322 // Populate toolbar.
1323 for (const QString& widgetName : mainWidgetNames)
1324 {
1325 if (QAction* action = actionsForToolBar.value(widgetName))
1326 {
1327 const bool allowText = false;
1328 addActionToToolBar(action, allowText);
1329 }
1330 }
1331 addArVizGodotIcon();
1332
1333 AddArmarXWidgetAction* completerAction =
1334 new AddArmarXWidgetAction("", ui->menuAdd_Widget, this);
1335 InfixCompleter* completer = new InfixCompleter(widgetNameList, searchField);
1336 connect(
1337 searchField, SIGNAL(textEdited(QString)), completer, SLOT(setCompletionInfix(QString)));
1338
1339 // connect(completer, SIGNAL(activated(QString)), completerAction, SLOT(triggered(QString)));
1340 connect(completerAction, SIGNAL(accepted()), searchField, SLOT(clear()));
1341 connect(completerAction,
1342 SIGNAL(clicked(QString, QString)),
1343 this,
1344 SLOT(createArmarXWidget(QString, QString)),
1345 Qt::UniqueConnection);
1346 searchField->setCompleter(completer);
1347
1348
1349 ui->toolBar->addSeparator();
1350 ui->toolBar->addWidget(searchField);
1351 QIcon icon;
1352 icon.addFile(
1353 QString::fromUtf8("://icons/edit-add.ico"), QSize(), QIcon::Normal, QIcon::Off);
1354 openWidgetButton = new QToolButton(this);
1355 openWidgetButton->setEnabled(false);
1356 openWidgetButton->setIcon(icon);
1357 openWidgetButton->setFixedSize({24, 24});
1358 openWidgetButton->setToolTip("Open selected widget");
1359 connect(openWidgetButton,
1360 SIGNAL(clicked()),
1361 this,
1362 SLOT(openWidgetButtonClicked()),
1363 Qt::UniqueConnection);
1364 ui->toolBar->addWidget(openWidgetButton);
1365 connect(searchField,
1366 SIGNAL(textChanged(QString)),
1367 this,
1368 SLOT(updateOpenWidgetButtonStatus(QString)),
1369 Qt::UniqueConnection);
1370
1371 connect(searchField,
1372 SIGNAL(returnPressed()),
1373 this,
1374 SLOT(openWidgetButtonClicked()),
1375 Qt::UniqueConnection);
1376
1377 {
1378 ui->toolBar->addSeparator();
1379 favoritesLabel = new QLabel("Favorites:");
1380 favoritesLabel->setToolTip("The favorites are generated from the usage frequency over "
1381 "the last X widget creations."
1382 " Rightclick to change the number of displayed favorites");
1383 ui->toolBar->addWidget(favoritesLabel);
1384 //add menu
1385 favoritesLabel->setContextMenuPolicy(Qt::CustomContextMenu);
1386 connect(favoritesLabel,
1387 SIGNAL(customContextMenuRequested(const QPoint&)),
1388 this,
1389 SLOT(onContextMenuFavoritesRequested(const QPoint&)));
1390 }
1391 updatefavoriteActions();
1392 }
1393
1394 void
1395 ArmarXMainWindow::updateOpenWidgetList()
1396 {
1397 ui->menuWindows->clear();
1398 QAction* closeAllAction = new QAction("Close all widgets", ui->menuWindows);
1399 connect(closeAllAction, SIGNAL(triggered()), this, SLOT(closeAllWidgetsWithDialog()));
1400 ui->menuWindows->addAction(closeAllAction);
1401 ui->menuWindows->addSeparator();
1402
1403 OpenWidgetMap::iterator it = listOpenWidgets.begin();
1404
1405 for (; it != listOpenWidgets.end(); it++)
1406 {
1407 QDockWidget* dockWidget = it.value().first;
1408 QAction* action = new QAction(dockWidget->objectName(), ui->menuWindows);
1409 action->setCheckable(true);
1410 action->setChecked(!dockWidget->isHidden());
1411
1412 connect(action, SIGNAL(toggled(bool)), dockWidget, SLOT(setVisible(bool)));
1413 // connect(object, SIGNAL(visibilityChanged(bool)), action, SLOT(setChecked(bool)));
1414 ui->menuWindows->addAction(action);
1415 }
1416 }
1417
1418 void
1419 ArmarXMainWindow::addArVizGodotIcon()
1420 {
1421 const char* path = std::getenv("arviz_godot_DIR");
1422 if (path == nullptr)
1423 {
1424 return;
1425 }
1426
1427 std::filesystem::path buildDirectory(path);
1428 std::filesystem::path mainDirectory = buildDirectory.parent_path();
1429
1430 std::filesystem::path binaryPath = buildDirectory / "bin" / "arviz-godot";
1431 std::filesystem::path iconPath =
1432 mainDirectory / "source" / "arviz_godot" / "project" / "icon.png";
1433
1434 if (not std::filesystem::exists(binaryPath) or not std::filesystem::exists(iconPath))
1435 {
1436 return;
1437 }
1438
1439 QIcon icon(iconPath.c_str());
1440 QString name("ArViz Godot");
1441
1442 QAction* action = new QAction(icon, name, this);
1443
1444 bool allowText = false;
1445 addActionToToolBar(action, allowText);
1446
1447 auto slot = [this, binaryPath]()
1448 {
1449 if (not std::filesystem::exists(binaryPath))
1450 {
1451 QMessageBox errorBox;
1452 errorBox.critical(
1453 nullptr, "Error", "The ArViz Godot executable is no longer available.");
1454
1455 ARMARX_ERROR << "Failed to find ArViz Godot";
1456
1457 return;
1458 }
1459
1460 pid_t pid = fork();
1461
1462 if (pid == -1)
1463 {
1464 ARMARX_ERROR << "Failed to start ArViz Godot";
1465 return;
1466 }
1467
1468 if (pid != 0)
1469 {
1470 return;
1471 }
1472
1473 int null = open("/dev/null", O_WRONLY);
1474 dup2(null, STDOUT_FILENO);
1475 dup2(null, STDERR_FILENO);
1476
1477 execl(binaryPath.c_str(), "arviz-godot", nullptr);
1478 exit(-1);
1479 };
1480
1481 connect(action, &QAction::triggered, this, slot, Qt::UniqueConnection);
1482 }
1483
1484 void
1486 {
1487 QStringList recentlyFiles = mainSettings.value("RecentlyFiles").toStringList();
1488 QMenu* menu = ui->menuRecently_Opened_Files;
1489 auto actions = menu->actions();
1490
1491 for (QAction* action : actions)
1492 {
1493 if (!action->isCheckable() && !action->isSeparator())
1494 {
1495 menu->removeAction(action);
1496 }
1497 }
1498
1499 foreach (QString file, recentlyFiles)
1500 {
1502 new OpenRecentlyOpenedFileAction(file, menu, this);
1503
1504 action->setCheckable(false);
1505 connect(action, SIGNAL(triggered()), action, SLOT(openFile()));
1506 menu->addAction(action);
1507 }
1508 }
1509
1510 void
1511 ArmarXMainWindow::updateStatusOfOpenWidgets()
1512 {
1513 OpenWidgetMap::iterator it = listOpenWidgets.begin();
1514
1515 for (; it != listOpenWidgets.end(); it++)
1516 {
1517 QDockWidget* dockWidget = it.value().first;
1518 ManagedIceObjectPtr comp = ManagedIceObjectPtr::dynamicCast(it.value().second);
1519 StatusDockWidgetTitleBar* titlebar =
1520 qobject_cast<StatusDockWidgetTitleBar*>(dockWidget->titleBarWidget());
1521
1522 if (titlebar && comp)
1523 {
1524
1525 ManagedIceObjectConnectivity con = comp->getConnectivity();
1526 QStringList dependencies;
1527
1528 for (DependencyMap::iterator i = con.dependencies.begin();
1529 i != con.dependencies.end();
1530 i++)
1531 {
1532 ManagedIceObjectDependencyBasePtr& dep = i->second;
1533
1534 if (!dep->getResolved() || comp->getState() == eManagedIceObjectStarted)
1535 {
1536 dependencies << QString::fromStdString(dep->getName());
1537 }
1538 }
1539
1540 titlebar->changeStatus((ManagedIceObjectState)comp->getState(), dependencies);
1541 }
1542 }
1543 }
1544
1545 void
1546 ArmarXMainWindow::enterFullscreenMode()
1547 {
1548 showFullScreen();
1549 ui->toolBar->hide();
1550 ui->menubar->hide();
1551 ui->statusbar->hide();
1552
1553 // Add actions for leaving fullscreen mode
1554 leaveFullScreenActionF11 = new QAction(this);
1555 leaveFullScreenActionF11->setShortcut(Qt::Key_F11);
1556 connect(leaveFullScreenActionF11, SIGNAL(triggered()), this, SLOT(leaveFullscreenMode()));
1557 addAction(leaveFullScreenActionF11);
1558
1559 leaveFullScreenActionEsc = new QAction(this);
1560 leaveFullScreenActionEsc->setShortcut(Qt::Key_Escape);
1561 connect(leaveFullScreenActionEsc, SIGNAL(triggered()), this, SLOT(leaveFullscreenMode()));
1562 addAction(leaveFullScreenActionEsc);
1563 }
1564
1565 void
1566 ArmarXMainWindow::leaveFullscreenMode()
1567 {
1568 // Remove actions for leaving fullscreen mode (menu will be available again)
1569 removeAction(leaveFullScreenActionF11);
1570 leaveFullScreenActionF11 = nullptr;
1571
1572 removeAction(leaveFullScreenActionEsc);
1573 leaveFullScreenActionEsc = nullptr;
1574
1575 showNormal();
1576 ui->toolBar->show();
1577 ui->menubar->show();
1578 ui->statusbar->show();
1579 }
1580
1581 void
1582 ArmarXMainWindow::toggleWidgetLock()
1583 {
1584 QList<ArmarXDockWidget*> dockWidgets = findChildren<ArmarXDockWidget*>();
1585 if (widgetsLocked)
1586 {
1587 ARMARX_INFO << "Unlocking widgets";
1588 for (auto& widget : dockWidgets)
1589 {
1590 widget->unlockWidget();
1591 }
1592 }
1593 else
1594 {
1595 ARMARX_INFO << "Locking widgets";
1596 for (auto& widget : dockWidgets)
1597 {
1598 widget->lockWidget();
1599 }
1600 }
1601
1602 widgetsLocked = !widgetsLocked;
1603 }
1604
1605 /////////////////////////////////////////////////////////////////////////
1606 //// Additional Helper Classes
1607 /////////////////////////////////////////////////////////////////////////
1608
1610 QObject* parent,
1611 ArmarXMainWindow* mainGui) :
1612 QAction(widgetName, parent), mainGui(mainGui)
1613 {
1614 dialog = new WidgetNameDialog(widgetName, mainGui);
1615 connect(dialog, SIGNAL(accepted()), this, SLOT(dialogAccepted()));
1616 }
1617
1619 {
1620 delete dialog;
1621 }
1622
1623 void
1625 {
1626 if (widgetName.isEmpty())
1627 {
1628 QLineEdit* edit = qobject_cast<QLineEdit*>(sender());
1629 if (edit)
1630 {
1631 widgetName = edit->text();
1632 }
1633 else
1634 {
1635 return;
1636 }
1637 }
1638 ARMARX_INFO_S << "Setting action name to " << widgetName;
1639 setText(widgetName);
1640 setData(widgetName);
1642 }
1643
1644 void
1646 {
1647 dialog->editWidgetName->setText(""); // reset so that TextChanged is called for sure
1648 dialog->editWidgetName->setText(this->text());
1649
1650 if (!dialog->checkWidgetName(this->text()))
1651 {
1652 dialog->setModal(true);
1653 dialog->show();
1654 }
1655 else
1656 {
1658 }
1659 }
1660
1661 void
1663 {
1664 emit clicked(this->data().toString(), dialog->editWidgetName->text());
1665 emit accepted();
1666 }
1667
1669 QDockWidget(name), controller(controller)
1670 {
1671 scrollArea = new QScrollArea();
1672 scrollArea->setWidgetResizable(true);
1673
1674 QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1675 scrollArea->setSizePolicy(sizePolicy);
1676 setWidget(scrollArea);
1677
1678 savedTitleBar = nullptr;
1679 }
1680
1682 {
1683 // ARMARX_WARNING_S << "Deleting dockwidget: " << this->windowTitle();
1684 delete scrollArea;
1685 }
1686
1687 void
1689 {
1690 scrollArea->setWidget(widget);
1691 }
1692
1693 void
1695 {
1696 if (controller && !controller->onClose())
1697 {
1698 event->ignore();
1699 return;
1700 }
1701 else
1702 {
1703 event->accept();
1704 }
1705
1706 emit destroyed(this); // calls removeArmarXWidget slot
1707
1708 deleteLater(); // use this function, so that pending signals are processed correctly
1709 }
1710
1711 void
1713 {
1714 ARMARX_INFO << "Locking widget: " << objectName();
1715 savedTitleBar = titleBarWidget();
1716 setTitleBarWidget(new QWidget());
1717 if (controller)
1718 {
1719 controller->onLockWidget();
1720 }
1721 }
1722
1723 void
1725 {
1726 ARMARX_INFO << "Unlocking widget: " << objectName();
1727 if (savedTitleBar != nullptr)
1728 {
1729 QWidget* old = titleBarWidget();
1730 setTitleBarWidget(savedTitleBar);
1731 savedTitleBar = nullptr;
1732 if (old)
1733 {
1734 old->deleteLater();
1735 }
1736 }
1737 if (controller)
1738 {
1739 controller->onUnlockWidget();
1740 }
1741 }
1742
1744 QObject* parent,
1745 ArmarXMainWindow* mainGui) :
1746 QAction(text, parent), mainGui(mainGui)
1747 {
1748 }
1749
1750 void
1752 {
1753 if (mainGui)
1754 {
1755 mainGui->loadGuiConfig(text());
1756 }
1757 }
1758
1759 void
1760 armarx::ArmarXMainWindow::on_actionClear_tip_blacklist_triggered()
1761 {
1762 tipDialog->setBlackListedStrings(QStringList());
1763 mainSettings.setValue(CONFIG_BLACKLISTED_TIPS, tipDialog->getBlackListedStrings());
1764 mainSettings.sync();
1765 ARMARX_INFO << "Blacklist cleared";
1766 }
1767
1768 void
1769 armarx::ArmarXMainWindow::on_actionAbout_triggered()
1770 {
1771 QMessageBox::about(
1772 this,
1773 "ArmarX Graphical User Interface",
1774 QString("ArmarX is being developed at the High Performance Humanoid Technologies (H2T) "
1775 "lab at the Karlsruhe Institute of Technology (KIT)\nCopyright H2T, KIT, ") +
1776 QString(&__DATE__[7]) +
1777 +"\nVersion: " + QString::fromStdString(Application::GetVersion()));
1778 }
1779
1780 void
1781 armarx::ArmarXMainWindow::on_actionLoadLastConfig_toggled(bool toggled)
1782 {
1783 mainSettings.setValue(CONFIG_LOAD_LAST_CONFIG, toggled);
1784 mainSettings.sync();
1785 }
1786
1787 void
1788 armarx::ArmarXMainWindow::on_actionArmarX_Documentation_triggered()
1789 {
1790 QDesktopServices::openUrl(QUrl("http://armarx.humanoids.kit.edu/"));
1791 }
1792
1793 void
1794 armarx::ArmarXMainWindow::on_actionOpen_Use_Case_triggered()
1795 {
1796 guiUseCaseSelector->setCancelButtonText("Cancel");
1797 if (guiUseCaseSelector->exec() == QDialog::Accepted)
1798 {
1799 QString path = guiUseCaseSelector->getSelectedConfigFilePath();
1800 ARMARX_INFO << VAROUT(path);
1801 if (!path.isEmpty())
1802 {
1803 loadGuiConfig(path, false);
1804 }
1805 }
1806 mainSettings.setValue("DoNotShowUseCaseDialog", guiUseCaseSelector->getDoNotShowAgain());
1807 }
1808
1809 void
1810 armarx::ArmarXMainWindow::on_actionClear_plugin_cache_triggered()
1811 {
1812 pluginCache.clearCacheFile();
1813 }
1814
1815 void
1816 ArmarXMainWindow::updateOpenWidgetButtonStatus(QString widgetName)
1817 {
1818 openWidgetButton->setEnabled(pluginCache.getAvailableWidgetNames().contains(widgetName));
1819 }
1820
1821 void
1822 ArmarXMainWindow::openWidgetButtonClicked()
1823 {
1824 addWidgetAction->triggered(searchField->text());
1825 }
1826
1827 void
1828 ArmarXMainWindow::onContextMenuFavoritesRequested(const QPoint& pos)
1829 {
1830 QMenu menu;
1831
1832 auto numberOfFavIcons = new QSpinBox(&menu);
1833 numberOfFavIcons->setRange(1, 100);
1834 numberOfFavIcons->setSingleStep(1);
1835 numberOfFavIcons->setValue(mainSettings.value("FavoriteWidgetCount", 6).toInt());
1836 numberOfFavIcons->setToolTip("Max number of favorites");
1837 connect(numberOfFavIcons,
1838 SIGNAL(valueChanged(int)),
1839 this,
1840 SLOT(onNumberOfMaxFavoritesChanged(int)));
1841
1842 QWidgetAction* action = new QWidgetAction{&menu};
1843 action->setDefaultWidget(numberOfFavIcons);
1844 menu.addAction(action);
1845 menu.exec(favoritesLabel->mapToGlobal(pos));
1846 }
1847
1848 void
1849 ArmarXMainWindow::onNumberOfMaxFavoritesChanged(int i)
1850 {
1851 mainSettings.setValue("FavoriteWidgetCount", i);
1852 updatefavoriteActions();
1853 }
1854
1855 void
1856 ArmarXMainWindow::updatefavoriteActions()
1857 {
1858 const auto list = getFavoriteWidgets();
1859 if (favoriteActionWidgetNames == list
1860 //&&
1861 //favoriteActions.size() == static_cast<std::size_t>(favoriteActionWidgetNames.size())
1862 )
1863 {
1864 return;
1865 }
1866 favoriteActionWidgetNames = list;
1867 for (auto* action : favoriteActions)
1868 {
1869 ui->toolBar->removeAction(action);
1870 action->deleteLater();
1871 }
1872 favoriteActions.clear();
1873 for (auto widgetName : favoriteActionWidgetNames)
1874 {
1875 if (actionList.contains(widgetName))
1876 {
1877 bool allowText = true;
1878 favoriteActions.emplace_back(addActionToToolBar(actionList[widgetName], allowText));
1879 }
1880 }
1881 }
1882} // namespace armarx
std::shared_ptr< ArmarXWidgetInfo > ArmarXWidgetInfoPtr
#define VAROUT(x)
#define ARMARX_VIEWER_NAME
The ArmarXWidgetInfo class.
AddArmarXWidgetAction(QString widgetName, QObject *parent=0, ArmarXMainWindow *mainGui=0)
void triggered(QString widgetName=QString())
void clicked(QString widgetName, QString customInstanceName)
static std::string GetVersion()
The ArmarXDataPath class provides static methods to handle ArmarX data directories.
void closeEvent(QCloseEvent *event) override
void setArmarXWidget(QWidget *widget)
ArmarXDockWidget(QString name, ArmarXWidgetControllerPtr controller)
The ArmarXMainWindow class.
void loadPlugin(QString filePath)
loads a plugin with the given file path
ArmarXMainWindow(const armarx::ManagedIceObjectRegistryInterfacePtr &registry, const std::vector< std::string > &packages, const QString &configToLoad, bool disablePreloading)
QString getLibraryPathFromPackage(const QString &packageName)
void appendFileToWindowTitle(const QString &filepath="")
void loadPluginsFromPackage(const QString &packageName)
void addToRecentlyOpenedFileList(QString configFilepath)
std::vector< std::string > getDefaultPackageNames()
getDefaultPackageNames returns the names of all packages which are searched for plugins when the Arma...
void closeRequest()
emitted, when the main window should be closed
void closeEvent(QCloseEvent *event) override
emits the closeRequest signal
void loadGuiConfig(QString configFilepath="", bool showAsOpenGuiConfig=true)
bool existsWidget(const QString &widgetName)
Viewer3DWidgetPtr getViewerWidget()
Provides access to a viewer widget, if existent.
void updateSceneList(QMap< QString, Viewer3DInfo >)
static int showMessageBox(const QString &msg)
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getLibraryPaths() const
Returns the library paths seperated by semi-colons.
static std::vector< std::string > FindAllArmarXSourcePackages()
void setTag(const LogTag &tag)
Definition Logging.cpp:54
OpenRecentlyOpenedFileAction(QString text, QObject *parent=0, ArmarXMainWindow *mainGui=0)
static QString GetIconPath(const QString &widgetName)
static QString GetCategoryIconPath(const QString &widgetName)
The StatusDockWidgetTitleBar class.
void changeStatus(ManagedIceObjectState state, QStringList dependencies)
The TipDialog is a dialog to show tips/hints to the user, which are optionally only shown once.
Definition TipDialog.h:48
static QIcon GetWidgetIcon()
The WidgetNameDialog class.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_VERBOSE_S
Definition Logging.h:207
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< ArmarXManager > ArmarXManagerPtr
IceUtil::Handle< ManagedIceObjectRegistryInterface > ManagedIceObjectRegistryInterfacePtr
Definition Application.h:83
IceUtil::Handle< Viewer3DWidget > Viewer3DWidgetPtr
const LogSender::manipulator flush
Definition LogSender.h:251
IceUtil::Handle< ArmarXWidgetController > ArmarXWidgetControllerPtr
IceInternal::Handle< ManagedIceObject > ManagedIceObjectPtr
Definition ArmarXFwd.h:42
std::function< void()> slot
Definition timer.hpp:36