From d59a1b1a9048650e06c5e59f55d7cadef7dba87b Mon Sep 17 00:00:00 2001 From: Mirko Waechter <mirko.waechter@kit.edu> Date: Sun, 3 Jul 2016 17:10:34 +0200 Subject: [PATCH] Added Gui Use Cases for easier starting with ArmarX --- .../ScenarioControl.armarxgui | 28 ++++ .../GuiDefaultConfigs/Statecharts.armarxgui | 32 +++++ .../ArmarXGui/ArmarXMainWindow.cpp | 127 ++++++++++++----- .../applications/ArmarXGui/ArmarXMainWindow.h | 14 +- .../ArmarXGui/ArmarXMainWindow.ui | 6 + .../applications/ArmarXGui/CMakeLists.txt | 6 + .../ArmarXGui/Widgets/GuiUseCaseSelector.cpp | 130 ++++++++++++++++++ .../ArmarXGui/Widgets/GuiUseCaseSelector.h | 58 ++++++++ .../ArmarXGui/Widgets/GuiUseCaseSelector.ui | 96 +++++++++++++ .../ArmarXGui/Widgets/UseCaseSelectorItem.cpp | 98 +++++++++++++ .../ArmarXGui/Widgets/UseCaseSelectorItem.h | 64 +++++++++ .../ArmarXGui/Widgets/UseCaseSelectorItem.ui | 110 +++++++++++++++ 12 files changed, 729 insertions(+), 40 deletions(-) create mode 100644 data/ArmarXGui/GuiDefaultConfigs/ScenarioControl.armarxgui create mode 100644 data/ArmarXGui/GuiDefaultConfigs/Statecharts.armarxgui create mode 100644 source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.cpp create mode 100644 source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.h create mode 100644 source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.ui create mode 100644 source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.cpp create mode 100644 source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.h create mode 100644 source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.ui diff --git a/data/ArmarXGui/GuiDefaultConfigs/ScenarioControl.armarxgui b/data/ArmarXGui/GuiDefaultConfigs/ScenarioControl.armarxgui new file mode 100644 index 00000000..789fe6e1 --- /dev/null +++ b/data/ArmarXGui/GuiDefaultConfigs/ScenarioControl.armarxgui @@ -0,0 +1,28 @@ +[General] +ConfigTitle="Scenario Control" +ConfigDescription="GUI layout for control over scenarios" +IconPath=":icons/activity_monitor.png" +MainWindowGeometry=@ByteArray(\x1\xd9\xd0\xcb\0\x1\0\0\0\0\0\x18\0\0\0\x34\0\0\af\0\0\x4y\0\0\0\"\0\0\0U\0\0\a\\\0\0\x4o\0\0\0\0\0\0) +DockWidgetsState="@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\x1\0\0\0\x1\0\0\a;\0\0\x3\xc2\xfc\x2\0\0\0\x3\xfc\0\0\0\x43\0\0\x3\xc2\0\0\0\0\0\xff\xff\xff\xfc\x1\0\0\0\x2\xfc\0\0\0\0\0\0\x3\x32\0\0\0\0\0\xff\xff\xff\xfc\x2\0\0\0\x2\xfc\0\0\0\x43\0\0\x2%\0\0\0\0\0\xff\xff\xff\xfa\0\0\0\0\x2\0\0\0\x2\xfb\0\0\0$\0\x44\0o\0\x63\0k\0M\0\x65\0t\0\x61\0.\0L\0o\0g\0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0(\0\x44\0o\0\x63\0k\0K\0i\0n\0\x65\0m\0\x61\0t\0i\0\x63\0U\0n\0i\0t\0G\0U\0I\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0&\0\x44\0o\0\x63\0k\0P\0l\0\x61\0t\0\x66\0o\0r\0m\0U\0n\0i\0t\0G\0U\0I\x1\0\0\x2n\0\0\x1\x97\0\0\0\0\0\0\0\0\xfc\0\0\x3\x38\0\0\x4\x3\0\0\0\0\0\xff\xff\xff\xfa\0\0\0\0\x2\0\0\0\x2\xfb\0\0\0\x16\0\x44\0o\0\x63\0k\0R\0o\0\x62\0o\0t\0I\0K\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0\x36\0\x44\0o\0\x63\0k\0V\0i\0s\0u\0\x61\0l\0i\0z\0\x61\0t\0i\0o\0n\0.\0\x33\0\x44\0 \0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\x43\0\0\x3[\0\0\0\0\0\0\0\0\xfc\0\0\0\x43\0\0\x3\xc2\0\0\0\0\0\xff\xff\xff\xfa\0\0\0\0\x2\0\0\0\x3\xfb\0\0\0(\0\x44\0o\0\x63\0k\0S\0t\0\x61\0t\0\x65\0\x63\0h\0\x61\0r\0t\0\x45\0\x64\0i\0t\0o\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0(\0\x44\0o\0\x63\0k\0S\0t\0\x61\0t\0\x65\0\x63\0h\0\x61\0r\0t\0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0\x34\0\x44\0o\0\x63\0k\0O\0\x62\0s\0\x65\0r\0v\0\x65\0r\0s\0.\0O\0\x62\0s\0\x65\0r\0v\0\x65\0r\0V\0i\0\x65\0w\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfc\0\0\0\x43\0\0\x3\xc2\0\0\0\x8c\0\xff\xff\xff\xfc\x1\0\0\0\x2\xfc\0\0\0\0\0\0\x5\x38\0\0\0\xbc\0\xff\xff\xff\xfa\0\0\0\x1\x2\0\0\0\x2\xfb\0\0\0,\0\x44\0o\0\x63\0k\0S\0y\0s\0t\0\x65\0m\0S\0t\0\x61\0t\0\x65\0M\0o\0n\0i\0t\0o\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\x62\0\xff\xff\xff\xfb\0\0\0\x1a\0\x44\0o\0\x63\0k\0L\0o\0g\0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0n\0\xff\xff\xff\xfb\0\0\0&\0\x44\0o\0\x63\0k\0S\0\x63\0\x65\0n\0\x61\0r\0i\0o\0M\0\x61\0n\0\x61\0g\0\x65\0r\x1\0\0\x5>\0\0\x1\xfd\0\0\0\xdb\0\xff\xff\xff\0\0\0\0\0\0\x3\xc2\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\x1\0\0\0\x2\0\0\0\x1\0\0\0\xe\0t\0o\0o\0l\0\x42\0\x61\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0)" +WidgetCustomNames=LogViewer, ScenarioManager, SystemStateMonitor +loadedPackages=ArmarXCore, ArmarXGui, MemoryX, RobotAPI, RobotComponents, RobotSkillTemplates, ArmarXSimulation, VisionX, SpeechX, Armar3, Armar4, ArmarXCore, ArmarXGui, MemoryX, RobotAPI, RobotComponents, RobotSkillTemplates, ArmarXSimulation, VisionX, SpeechX, Armar3, Armar4, ArmarXCore, ArmarXGui, MemoryX, RobotAPI, RobotComponents, RobotSkillTemplates, ArmarXSimulation, VisionX, SpeechX, Armar3, Armar4, ArmarXCore, ArmarXGui, MemoryX, RobotAPI, RobotComponents, RobotSkillTemplates, ArmarXSimulation, VisionX, SpeechX, Armar3, Armar4 + +[LogViewer] +WidgetBaseName=Meta.LogViewer +widgetWidth=1334 +widgetHeight=898 +verbosityLevel=1 +autoFilterAdding=true + +[ScenarioManager] +WidgetBaseName=Meta.ScenarioManager +widgetWidth=507 +widgetHeight=928 + +[SystemStateMonitor] +WidgetBaseName=Meta.SystemStateMonitor +widgetWidth=1334 +widgetHeight=910 +ManagerRepository=@Invalid() +MonitoredManagers=AStarPathPlannerManager, ArmarXGuiManager, CommonStorageManager, ConditionHandlerManager, DebugObserverManager, FakeWorkingMemoryObjectLocalizerManager, ForceTorqueObserverManager, ForceTorqueUnitSimulationManager, GraphNodePoseResolverManager, HandMarkerLocalizationManager, HeadIKUnitManager, KinematicSelfLocalizationManager, KinematicUnitObserverManager, KinematicUnitSimulationManager, LeftHandUnitAppManager, LongtermMemoryManager, ObjectLocalizationSaliencyManager, ObjectMemoryObserverManager, PlatformUnitObserverManager, PlatformUnitSimulationManager, PriorKnowledgeManager, RightHandUnitAppManager, RobotIKManager, RobotStateComponentManager, SystemObserverManager, TCPControlUnitManager, ViewSelectionManager, WorkingMemoryManager + diff --git a/data/ArmarXGui/GuiDefaultConfigs/Statecharts.armarxgui b/data/ArmarXGui/GuiDefaultConfigs/Statecharts.armarxgui new file mode 100644 index 00000000..69de95b3 --- /dev/null +++ b/data/ArmarXGui/GuiDefaultConfigs/Statecharts.armarxgui @@ -0,0 +1,32 @@ +[General] +ConfigTitle="Statechart Programming" +ConfigDescription="GUI layout for programming statecharts" +IconPath=":statechart-editor/states.svg" +MainWindowGeometry=@ByteArray(\x1\xd9\xd0\xcb\0\x1\0\0\0\0\0\x18\0\0\0\x34\0\0\af\0\0\x4y\0\0\0\"\0\0\0U\0\0\a\\\0\0\x4o\0\0\0\0\0\0) +DockWidgetsState="@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\x1\0\0\0\x1\0\0\a;\0\0\x3\xc2\xfc\x2\0\0\0\x2\xfc\0\0\0\x43\0\0\x3\xc2\0\0\0\0\0\xff\xff\xff\xfc\x1\0\0\0\x2\xfc\0\0\0\0\0\0\x3\x32\0\0\0\0\0\xff\xff\xff\xfc\x2\0\0\0\x2\xfc\0\0\0\x43\0\0\x2%\0\0\0\0\0\xff\xff\xff\xfa\0\0\0\0\x2\0\0\0\x2\xfb\0\0\0$\0\x44\0o\0\x63\0k\0M\0\x65\0t\0\x61\0.\0L\0o\0g\0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0(\0\x44\0o\0\x63\0k\0K\0i\0n\0\x65\0m\0\x61\0t\0i\0\x63\0U\0n\0i\0t\0G\0U\0I\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0&\0\x44\0o\0\x63\0k\0P\0l\0\x61\0t\0\x66\0o\0r\0m\0U\0n\0i\0t\0G\0U\0I\x1\0\0\x2n\0\0\x1\x97\0\0\0\0\0\0\0\0\xfc\0\0\x3\x38\0\0\x4\x3\0\0\0\0\0\xff\xff\xff\xfa\0\0\0\0\x2\0\0\0\x2\xfb\0\0\0\x16\0\x44\0o\0\x63\0k\0R\0o\0\x62\0o\0t\0I\0K\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xfb\0\0\0\x36\0\x44\0o\0\x63\0k\0V\0i\0s\0u\0\x61\0l\0i\0z\0\x61\0t\0i\0o\0n\0.\0\x33\0\x44\0 \0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\x43\0\0\x3[\0\0\0\0\0\0\0\0\xfc\0\0\0\x43\0\0\x3\xc2\0\0\0\x8c\x1\0\0\x1d\xfa\0\0\0\0\x2\0\0\0\x4\xfb\0\0\0(\0\x44\0o\0\x63\0k\0S\0t\0\x61\0t\0\x65\0\x63\0h\0\x61\0r\0t\0\x45\0\x64\0i\0t\0o\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\x62\0\xff\xff\xff\xfb\0\0\0(\0\x44\0o\0\x63\0k\0S\0t\0\x61\0t\0\x65\0\x63\0h\0\x61\0r\0t\0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0n\0\xff\xff\xff\xfb\0\0\0\x1a\0\x44\0o\0\x63\0k\0L\0o\0g\0V\0i\0\x65\0w\0\x65\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0n\0\xff\xff\xff\xfb\0\0\0\x34\0\x44\0o\0\x63\0k\0O\0\x62\0s\0\x65\0r\0v\0\x65\0r\0s\0.\0O\0\x62\0s\0\x65\0r\0v\0\x65\0r\0V\0i\0\x65\0w\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\x62\0\xff\xff\xff\0\0\0\0\0\0\x3\xc2\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\x1\0\0\0\x2\0\0\0\x1\0\0\0\xe\0t\0o\0o\0l\0\x42\0\x61\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0)" +WidgetCustomNames=LogViewer, Observers.ObserverView, StatechartEditor, StatechartViewer +loadedPackages=ArmarXCore, ArmarXGui, MemoryX, RobotAPI, RobotComponents, RobotSkillTemplates, ArmarXSimulation, VisionX, SpeechX, Armar3, Armar4, ArmarXCore, ArmarXGui, MemoryX, RobotAPI, RobotComponents, RobotSkillTemplates, ArmarXSimulation, VisionX, SpeechX, Armar3, Armar4 + +[StatechartEditor] +WidgetBaseName=Statecharts.StatechartEditor +widgetWidth=1849 +widgetHeight=910 +selectedProfile=Armar3KinematicSimulation +openAllStatesSelected=true + +[LogViewer] +WidgetBaseName=Meta.LogViewer +widgetWidth=1847 +widgetHeight=864 +verbosityLevel=1 +autoFilterAdding=true + +[Observers.ObserverView] +WidgetBaseName=Observers.ObserverView +widgetWidth=1849 +widgetHeight=910 + +[StatechartViewer] +WidgetBaseName=Statecharts.StatechartViewer +widgetWidth=1847 +widgetHeight=864 diff --git a/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.cpp b/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.cpp index 5e004000..a62d6d90 100644 --- a/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.cpp +++ b/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.cpp @@ -91,6 +91,7 @@ #include <sstream> #include <omp.h> #include <QtSvg/QSvgRenderer> +#include <ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.h> #define ARMARX_ORGANIZATION "KIT" @@ -106,7 +107,6 @@ ArmarXMainWindow::ArmarXMainWindow(const armarx::ManagedIceObjectRegistryInterfa QMainWindow(NULL), defaultPackageNames(packages), mainSettings(QSettings::UserScope, ARMARX_ORGANIZATION, ARMARX_GUI_APPLICATION_NAME) - { splashscreenPrefix = "v" + QString::fromStdString(Application::GetVersion()) + " - "; QPixmap pm(QString("://icons/ArmarX-Splashscreen.png")); @@ -140,25 +140,15 @@ ArmarXMainWindow::ArmarXMainWindow(const armarx::ManagedIceObjectRegistryInterfa // connect(ui->actionClear_tip_blacklist, SIGNAL(triggered()), this, SLOT(on_actionClear_tip_blacklist_triggered())); // connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(on_actionAbout_triggered())); + // plugins QSet<QString> pluginDirs; - for (const std::string& packageName : packages) + for (const std::string & packageName : packages) { - ARMARX_INFO << "Looking for gui plugins in package " << packageName; - splashSceen->showMessage(splashscreenPrefix + QString::fromStdString(packageName), - Qt::AlignJustify | Qt::AlignBottom); - qApp->processEvents(); - CMakePackageFinder packageFinder(packageName); - // std::string libs = packageFinder.getLibs(); - std::string libPaths = packageFinder.getLibraryPaths(); - QString dir = QString::fromStdString(libPaths); - dir = QDir::cleanPath(dir); - pluginDirs.insert(dir); + pluginDirs.insert(getLibraryPathFromPackage(packageName.c_str())); } - loadPlugins(pluginDirs, false); - instantiatePlugins(); updateRecentlyOpenedFileList(); @@ -170,7 +160,21 @@ ArmarXMainWindow::ArmarXMainWindow(const armarx::ManagedIceObjectRegistryInterfa //changeLayout(2); QStringList recentlyFiles = mainSettings.value("RecentlyFiles").toStringList(); splashSceen->finish(this); - if (!configToLoad.isEmpty()) + + guiUseCaseSelector = new GuiUseCaseSelector(this, mainSettings.value("DoNotShowUseCaseDialog").toBool(), this); + + + + if (!mainSettings.value("DoNotShowUseCaseDialog").toBool() && guiUseCaseSelector->exec() == QDialog::Accepted) + { + QString path = guiUseCaseSelector->getSelectedConfigFilePath(); + ARMARX_INFO << VAROUT(path); + if (!path.isEmpty()) + { + loadGuiConfig(path, false); + } + } + else if (!configToLoad.isEmpty()) { loadGuiConfig(configToLoad); } @@ -186,7 +190,7 @@ ArmarXMainWindow::ArmarXMainWindow(const armarx::ManagedIceObjectRegistryInterfa else { QStringList defaultWidgets = {"Meta.LogViewer"}; - for (auto& widgetName : defaultWidgets) + for (auto & widgetName : defaultWidgets) { if (existsWidget(widgetName)) { @@ -195,6 +199,8 @@ ArmarXMainWindow::ArmarXMainWindow(const armarx::ManagedIceObjectRegistryInterfa } } + mainSettings.setValue("DoNotShowUseCaseDialog", guiUseCaseSelector->getDoNotShowAgain()); + } ArmarXMainWindow::~ArmarXMainWindow() @@ -284,7 +290,6 @@ void ArmarXMainWindow::loadPlugins(const QStringList& fileNames) for (int i = 0; i < guiFiles.size(); i++) { QString fileName = guiFiles.at(i); - ARMARX_INFO << "loading " << fileName.toStdString(); QMetaObject::invokeMethod(splashSceen, "showMessage", Q_ARG(QString, splashscreenPrefix + "loading " + fileName), Q_ARG(int, Qt::AlignJustify | Qt::AlignBottom), Q_ARG(QColor, Qt::black)); // splashSceen->showMessage("loading " + fileName, @@ -308,6 +313,31 @@ void ArmarXMainWindow::loadPlugins(const QStringList& fileNames) } +QString ArmarXMainWindow::getLibraryPathFromPackage(const QString& packageName) +{ + CMakePackageFinder packageFinder(packageName.toStdString()); + std::string libPaths = packageFinder.getLibraryPaths(); + QString dir = QString::fromStdString(libPaths); + dir = QDir::cleanPath(dir); + + return dir; +} + +void ArmarXMainWindow::loadPluginsFromPackage(const QString& packageName) +{ + QSet<QString> pluginDirs; + splashSceen->showMessage(splashscreenPrefix + packageName, + Qt::AlignJustify | Qt::AlignBottom); + qApp->processEvents(); + + ARMARX_INFO << "Looking for gui plugins in package " << packageName; + QString dir = getLibraryPathFromPackage(packageName); + pluginDirs.insert(dir); + loadedPackages << packageName; + loadPlugins(pluginDirs, false); +} + + void ArmarXMainWindow::loadPlugins(const QString& pluginDir, bool searchRecursive) { QSet<QString> dirs; @@ -315,6 +345,7 @@ void ArmarXMainWindow::loadPlugins(const QString& pluginDir, bool searchRecursiv loadPlugins(dirs, searchRecursive); } + void ArmarXMainWindow::loadPlugins(const QSet<QString>& pluginDirs, bool searchRecursive) { // only search paths ending with lib @@ -323,7 +354,7 @@ void ArmarXMainWindow::loadPlugins(const QSet<QString>& pluginDirs, bool searchR QList<QDir> directoriesToCheck; QStringList allFileNames; - for (const auto& pluginDir : pluginDirs) + for (const auto & pluginDir : pluginDirs) { directoriesToCheck.push_back(QDir(pluginDir)); @@ -344,7 +375,7 @@ void ArmarXMainWindow::loadPlugins(const QSet<QString>& pluginDirs, bool searchR QStringList fileNames = currentPluginDir.entryList(filters, QDir::Files); - for (auto& fileName : fileNames) + for (auto & fileName : fileNames) { fileName = currentPluginDir.absoluteFilePath(fileName); } @@ -454,7 +485,6 @@ void ArmarXMainWindow::instantiatePlugin(QPluginLoader& loader) { str << ", "; } - str << it->first.toStdString(); } @@ -544,6 +574,9 @@ void ArmarXMainWindow::closeAllWidgets() ARMARX_VERBOSE << "Closed all widgets"; listOpenWidgets.clear(); + + this->configFilepath = ""; + appendFileToWindowTitle(configFilepath); } void ArmarXMainWindow::closeAllWidgetsWithDialog() @@ -597,7 +630,7 @@ Viewer3DWidgetPtr ArmarXMainWindow::getViewerWidget() } } -void ArmarXMainWindow::loadGuiConfig(QString configFilepath) +void ArmarXMainWindow::loadGuiConfig(QString configFilepath, bool showAsOpenGuiConfig) { if (configFilepath.length() == 0) @@ -641,9 +674,11 @@ void ArmarXMainWindow::loadGuiConfig(QString configFilepath) ARMARX_VERBOSE << "Loading config file: " << configFilepath; - - addToRecentlyOpenedFileList(configFilepath); - updateRecentlyOpenedFileList(); + if (showAsOpenGuiConfig) + { + addToRecentlyOpenedFileList(configFilepath); + updateRecentlyOpenedFileList(); + } QSettings settings(configFilepath, QSettings::IniFormat); @@ -654,12 +689,10 @@ void ArmarXMainWindow::loadGuiConfig(QString configFilepath) return; } - QStringList pluginsToLoad = settings.value("loadedLibs").toStringList(); - foreach(QString pluginPath, pluginsToLoad) + QStringList packagesToLoad = settings.value("loadedPackages").toStringList(); + foreach(QString package, packagesToLoad) { - std::string absPath; - ArmarXDataPath::getAbsolutePath(pluginPath.toStdString(), absPath); - loadPlugin(QString::fromStdString(absPath)); + loadPluginsFromPackage(package); } QStringList widgetNames = settings.value("WidgetCustomNames").toStringList(); @@ -702,8 +735,11 @@ void ArmarXMainWindow::loadGuiConfig(QString configFilepath) settings.endGroup(); } - this->configFilepath = configFilepath; - appendFileToWindowTitle(configFilepath); + if (showAsOpenGuiConfig) + { + this->configFilepath = configFilepath; + appendFileToWindowTitle(configFilepath); + } updateOpenWidgetList(); QFileInfo file(configFilepath); statusBar()->showMessage("'" + file.fileName() + "' loaded.", 10000); @@ -754,7 +790,7 @@ void ArmarXMainWindow::saveGuiConfig() // for (int i = 0; i < loadedPluginFilepaths.size(); ++i) { // loadedPluginFilepaths[i] = QString::fromStdString(loadedPluginFilepaths[i].toStdString()); // } - settings.setValue("loadedLibs", loadedPluginFilepaths); + settings.setValue("loadedPackages", loadedPackages); settings.setValue("MainWindowGeometry", saveGeometry()); settings.setValue("DockWidgetsState", saveState()); QStringList widgetCustomNames; @@ -837,7 +873,7 @@ ArmarXWidgetControllerPtr ArmarXMainWindow::createArmarXWidget(QString widgetNam } auto widgetUsage = mainSettings.value("WidgetUsageHistory").toList(); widgetUsage.push_back(widgetName); - if (widgetUsage.size() > 100) + if (widgetUsage.size() > widgetUsageHistoryLength) { widgetUsage.pop_front(); } @@ -1096,7 +1132,7 @@ QMenu* ArmarXMainWindow::getCategoryMenu(const std::string& categoryString, QMen auto actions = menu->actions(); - for (QAction* action : actions) + for (QAction * action : actions) { if (action->text().toStdString() == *items.begin()) { @@ -1185,10 +1221,12 @@ void ArmarXMainWindow::updateAvailableWidgetList() if (widgetUsageHistory.size() > 0) { ui->toolBar->addSeparator(); - ui->toolBar->addWidget(new QLabel("Favorites:")); + auto label = new QLabel("Favorites:"); + label->setToolTip("The favorites are generated from the usage frequency over the last X widget creations."); + ui->toolBar->addWidget(label); } std::map<QString, int> widgetUsage; - for (auto& widgetName : widgetUsageHistory) + for (auto & widgetName : widgetUsageHistory) { if (widgetUsage.count(widgetName.toString()) == 0) { @@ -1250,7 +1288,7 @@ void ArmarXMainWindow::updateRecentlyOpenedFileList() QMenu* menu = ui->menuRecently_Opened_Files; auto actions = menu->actions(); - for (QAction* action : actions) + for (QAction * action : actions) { if (!action->isCheckable() && !action->isSeparator()) { @@ -1446,3 +1484,18 @@ void armarx::ArmarXMainWindow::on_actionArmarX_Documentation_triggered() { QDesktopServices::openUrl(QUrl("http://armarx.humanoids.kit.edu/")); } + +void armarx::ArmarXMainWindow::on_actionOpen_Use_Case_triggered() +{ + if (guiUseCaseSelector->exec() == QDialog::Accepted) + { + QString path = guiUseCaseSelector->getSelectedConfigFilePath(); + ARMARX_INFO << VAROUT(path); + if (!path.isEmpty()) + { + loadGuiConfig(path, false); + } + } + mainSettings.setValue("DoNotShowUseCaseDialog", guiUseCaseSelector->getDoNotShowAgain()); + +} diff --git a/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.h b/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.h index a7cd9846..402bd113 100644 --- a/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.h +++ b/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.h @@ -41,6 +41,7 @@ #include <ArmarXCore/core/application/Application.h> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/services/tasks/RunningTask.h> + #include <QSplashScreen> #include "Widgets/ViewerWidget.h" @@ -60,7 +61,7 @@ namespace Ui // project namespace armarx { - + class GuiUseCaseSelector; class ArmarXDockWidget; @@ -113,8 +114,11 @@ namespace armarx void instantiatePlugin(QPluginLoader& loader); bool existsWidget(const QString& widgetName); + QString getLibraryPathFromPackage(const QString& packageName); + public slots: - void loadGuiConfig(QString configFilepath = ""); + void loadPluginsFromPackage(const QString& packageName); + void loadGuiConfig(QString configFilepath = "", bool showAsOpenGuiConfig = true); signals: /** @@ -154,6 +158,8 @@ namespace armarx void on_actionArmarX_Documentation_triggered(); + void on_actionOpen_Use_Case_triggered(); + private: /** * @brief updates the menu entry with the available widgets. @@ -178,6 +184,7 @@ namespace armarx void instantiatePlugins(); QDir pluginsDir; + QStringList loadedPackages; std::vector<QPluginLoader*> pluginsToLoad; QList<QDir> directoriesToCheck; @@ -201,7 +208,7 @@ namespace armarx QListWidgetItem* viewerItem; QPointer<TipDialog> tipDialog; - + const int widgetUsageHistoryLength = 100; std::vector<std::string> defaultPackageNames; @@ -215,6 +222,7 @@ namespace armarx QTimer* connectionStatusTimer; QSplashScreen* splashSceen; QString splashscreenPrefix; + GuiUseCaseSelector* guiUseCaseSelector; boost::shared_ptr<boost::recursive_mutex> mutex3d; diff --git a/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.ui b/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.ui index b9890add..e06e435c 100644 --- a/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.ui +++ b/source/ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.ui @@ -56,6 +56,7 @@ <addaction name="actionLoadLastConfig"/> <addaction name="separator"/> </widget> + <addaction name="actionOpen_Use_Case"/> <addaction name="actionLoad_Gui_Config"/> <addaction name="actionSave_Gui"/> <addaction name="actionSave_Gui_as"/> @@ -205,6 +206,11 @@ <string>ArmarX Documentation</string> </property> </action> + <action name="actionOpen_Use_Case"> + <property name="text"> + <string>Open Use Case </string> + </property> + </action> </widget> <resources> <include location="../../libraries/ArmarXGuiBase/armarxicons.qrc"/> diff --git a/source/ArmarXGui/applications/ArmarXGui/CMakeLists.txt b/source/ArmarXGui/applications/ArmarXGui/CMakeLists.txt index 5d19f226..26aefc6b 100644 --- a/source/ArmarXGui/applications/ArmarXGui/CMakeLists.txt +++ b/source/ArmarXGui/applications/ArmarXGui/CMakeLists.txt @@ -26,6 +26,8 @@ set(SOURCES ArmarXGuiApp.cpp Widgets/ViewerWidget.cpp Widgets/TitlebarWidget.cpp Widgets/WidgetNameDialog.cpp + Widgets/UseCaseSelectorItem.cpp + Widgets/GuiUseCaseSelector.cpp ) set(HEADERS ArmarXGuiApp.h @@ -33,6 +35,8 @@ set(HEADERS ArmarXGuiApp.h Widgets/ViewerWidget.h Widgets/TitlebarWidget.h Widgets/WidgetNameDialog.h + Widgets/UseCaseSelectorItem.h + Widgets/GuiUseCaseSelector.h ) set(GUI_MOC_HDRS ${HEADERS}) @@ -42,6 +46,8 @@ set(GUI_UIS Widgets/ViewerWidget.ui Widgets/ViewerWidgetConfigDialog.ui Widgets/ExceptionDialog.ui + Widgets/UseCaseSelectorItem.ui + Widgets/GuiUseCaseSelector.ui ) find_package(OpenMP) diff --git a/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.cpp b/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.cpp new file mode 100644 index 00000000..c1a4b09c --- /dev/null +++ b/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.cpp @@ -0,0 +1,130 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2016 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "GuiUseCaseSelector.h" +#include "UseCaseSelectorItem.h" +#include <QPushButton> +#include <ArmarXGui/applications/ArmarXGui/ArmarXMainWindow.h> +#include <ArmarXGui/applications/ArmarXGui/ui_GuiUseCaseSelector.h> +#include <boost/filesystem.hpp> +namespace armarx +{ + + GuiUseCaseSelector::GuiUseCaseSelector(ArmarXMainWindow* mainWindow, bool doNotShowAgain, QWidget* parent) : + QDialog(parent), + ui(new Ui::GuiUseCaseSelector) + { + ui->setupUi(this); + for (auto & p : mainWindow->getDefaultPackageNames()) + { + CMakePackageFinder f(p); + if (!f.packageFound()) + { + continue; + } + boost::filesystem::path path = f.getDataDir(); + path /= p; + path /= "GuiDefaultConfigs"; + if (!boost::filesystem::exists(path)) + { + continue; + } + int i = 1; + for (boost::filesystem::recursive_directory_iterator end, dir(path); + dir != end ; ++dir, i++) + { + if (dir->path().extension() == ".armarxgui") + { + addDefaultConfig(dir->path().string().c_str(), p.c_str()); + } + + if (i % 100 == 0) + { + ARMARX_INFO_S << "Scanning file " << i << ": " << dir->path().c_str(); + } + } + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + ui->checkBoxDoNotShowAgain->setChecked(doNotShowAgain); + } + + GuiUseCaseSelector::~GuiUseCaseSelector() + { + delete ui; + } + + QString GuiUseCaseSelector::getSelectedConfigFilePath() const + { + for (int i = 0; i < ui->verticalLayout_3->count(); ++i) + { + UseCaseSelectorItem* item = qobject_cast<UseCaseSelectorItem*>(ui->verticalLayout_3->itemAt(i)->widget()); + if (item && item->isHighlighted()) + { + return item->getConfigFilePath(); + } + } + return ""; + } + + bool GuiUseCaseSelector::getDoNotShowAgain() const + { + return ui->checkBoxDoNotShowAgain->isChecked(); + } + + void GuiUseCaseSelector::addDefaultConfig(QString configPath, QString packageName) + { + ARMARX_INFO_S << "Adding " << configPath; + QSettings s(configPath, QSettings::IniFormat); + UseCaseSelectorItem* item = new UseCaseSelectorItem(s.value("ConfigTitle").toString(), + s.value("ConfigDescription").toString(), + configPath, + packageName, + s.value("IconPath").toString(), + this); + ui->verticalLayout_3->addWidget(item); + connect(item, SIGNAL(selected(QString)), this, SLOT(setSelection(QString))); + connect(item, SIGNAL(doubleClicked()), this, SLOT(accept())); + } + + void GuiUseCaseSelector::setSelection(QString selectedUseCase) + { + for (int i = 0; i < ui->verticalLayout_3->count(); ++i) + { + UseCaseSelectorItem* item = qobject_cast<UseCaseSelectorItem*>(ui->verticalLayout_3->itemAt(i)->widget()); + if (item) + { + if (item->getConfigFilePath() == selectedUseCase) + { + item->highlight(true); + } + else + { + item->highlight(false); + }; + } + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + + } + +} // namespace armarx diff --git a/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.h b/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.h new file mode 100644 index 00000000..f99c7fd5 --- /dev/null +++ b/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.h @@ -0,0 +1,58 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2016 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef ARMARX_GUIUSECASESELECTOR_H +#define ARMARX_GUIUSECASESELECTOR_H + +#include <QDialog> + +namespace armarx +{ + + class ArmarXMainWindow; + + namespace Ui + { + class GuiUseCaseSelector; + } + + class GuiUseCaseSelector : public QDialog + { + Q_OBJECT + + public: + explicit GuiUseCaseSelector(ArmarXMainWindow* mainWindow, bool doNotShowAgain = false, QWidget* parent = 0); + ~GuiUseCaseSelector(); + QString getSelectedConfigFilePath() const; + bool getDoNotShowAgain() const; + public slots: + void addDefaultConfig(QString configPath, QString packageName); + void setSelection(QString selectedUseCase); + private: + Ui::GuiUseCaseSelector* ui; + ArmarXMainWindow* mainWindow; + }; + + +} // namespace armarx +#endif // ARMARX_GUIUSECASESELECTOR_H diff --git a/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.ui b/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.ui new file mode 100644 index 00000000..4a9e3d88 --- /dev/null +++ b/source/ArmarXGui/applications/ArmarXGui/Widgets/GuiUseCaseSelector.ui @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>armarx::GuiUseCaseSelector</class> + <widget class="QDialog" name="armarx::GuiUseCaseSelector"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>756</width> + <height>382</height> + </rect> + </property> + <property name="windowTitle"> + <string>ArmarX GUI Use Case Selection</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Select a default use case or press cancel to open an empty GUI.</string> + </property> + </widget> + </item> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>734</width> + <height>280</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"/> + </widget> + </widget> + </item> + <item> + <widget class="QCheckBox" name="checkBoxDoNotShowAgain"> + <property name="text"> + <string>Do not show again</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>armarx::GuiUseCaseSelector</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>armarx::GuiUseCaseSelector</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.cpp b/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.cpp new file mode 100644 index 00000000..c804b290 --- /dev/null +++ b/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.cpp @@ -0,0 +1,98 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2016 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "UseCaseSelectorItem.h" +#include <ArmarXGui/applications/ArmarXGui/ui_UseCaseSelectorItem.h> +#include <boost/filesystem/path.hpp> +#include <ArmarXCore/core/logging/Logging.h> +namespace armarx +{ + + UseCaseSelectorItem::UseCaseSelectorItem(const QString& title, const QString& description, const QString& configFilePath, const QString& packageName, const QString& iconPath, QWidget* parent) : + QWidget(parent), + ui(new Ui::UseCaseSelectorItem), + configFilePath(configFilePath) + { + ui->setupUi(this); + this->setToolTip(configFilePath); + ui->titleLabel->setText(title); + ui->descriptionLabel->setText(description); + boost::filesystem::path p(configFilePath.toStdString()); + p.filename().stem(); + ui->filenameLabel->setText(QString(p.filename().string().c_str()) + " in " + packageName + " "); + if (!iconPath.isEmpty()) + { + QPixmap pixmap(iconPath); + if (!pixmap.isNull()) + { + ui->iconLabel->setPixmap(pixmap.scaled(pixmapSize, pixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + } + else + { + ARMARX_INFO_S << "Cannot find image: " << iconPath.toStdString(); + } + } + } + + UseCaseSelectorItem::~UseCaseSelectorItem() + { + delete ui; + } + + QString UseCaseSelectorItem::getConfigFilePath() const + { + return configFilePath; + } + + bool UseCaseSelectorItem::isHighlighted() const + { + return highlighted; + } + + void UseCaseSelectorItem::highlight(bool highlight) + { + highlighted = highlight; + QPalette p(palette()); + setAutoFillBackground(true); + if (highlight) + { + p.setColor(QPalette::Window, p.highlight().color()); + } + else + { + p = qobject_cast<QWidget*>(parent())->palette(); + } + setPalette(p); + } + + void UseCaseSelectorItem::mouseDoubleClickEvent(QMouseEvent*) + { + emit doubleClicked(); + } + + void UseCaseSelectorItem::mouseReleaseEvent(QMouseEvent* event) + { + emit selected(configFilePath); + } + +} diff --git a/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.h b/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.h new file mode 100644 index 00000000..0beea8fa --- /dev/null +++ b/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.h @@ -0,0 +1,64 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2016 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef ARMARX_USECASESELECTORITEM_H +#define ARMARX_USECASESELECTORITEM_H + +#include <QWidget> + +namespace armarx +{ + + namespace Ui + { + class UseCaseSelectorItem; + } + + class UseCaseSelectorItem : public QWidget + { + Q_OBJECT + + public: + explicit UseCaseSelectorItem(const QString& title, const QString& description, const QString& configFilePath, const QString& packageName, const QString& iconPath, QWidget* parent = 0); + ~UseCaseSelectorItem(); + QString getConfigFilePath() const; + bool isHighlighted() const; + const int pixmapSize = 64; + public slots: + void highlight(bool highlight); + signals: + void selected(QString filepath); + void doubleClicked(); + private: + Ui::UseCaseSelectorItem* ui; + QString configFilePath; + // QWidget interface + protected: + void mouseReleaseEvent(QMouseEvent*); + void mouseDoubleClickEvent(QMouseEvent*); + bool highlighted; + }; + + +} // namespace armarx +#endif // ARMARX_USECASESELECTORITEM_H diff --git a/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.ui b/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.ui new file mode 100644 index 00000000..0e2daadc --- /dev/null +++ b/source/ArmarXGui/applications/ArmarXGui/Widgets/UseCaseSelectorItem.ui @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>armarx::UseCaseSelectorItem</class> + <widget class="QWidget" name="armarx::UseCaseSelectorItem"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>74</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>74</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>82</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QLabel" name="titleLabel"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="font"> + <font> + <pointsize>12</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="indent"> + <number>10</number> + </property> + </widget> + </item> + <item row="0" column="0" rowspan="2"> + <widget class="QLabel" name="iconLabel"> + <property name="minimumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>64</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="filenameLabel"> + <property name="font"> + <font> + <pointsize>8</pointsize> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="indent"> + <number>5</number> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLabel" name="descriptionLabel"> + <property name="text"> + <string>No Description</string> + </property> + <property name="indent"> + <number>10</number> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> -- GitLab