diff --git a/etc/doxygen/pages/TutorialCreateAGuiPlugin.dox b/etc/doxygen/pages/TutorialCreateAGuiPlugin.dox index 50688d07e7dd3573e13baad3b0575f72315a81ac..9f7d6d17c9777862dae36953053d4bc7c3742908 100644 --- a/etc/doxygen/pages/TutorialCreateAGuiPlugin.dox +++ b/etc/doxygen/pages/TutorialCreateAGuiPlugin.dox @@ -1,5 +1,5 @@ /** -\page ArmarXGui-Tutorials-CreateGuiPlugin Tutorial: Creating a new Gui widget for ArmarX +\page ArmarXGui-Tutorials-CreateGuiPlugin Tutorial: Creating a new Gui Widget for ArmarX Prerequisites: None @@ -8,75 +8,100 @@ Prerequisites: None \section ArmarXGui-Tutorial-CreateGuiPlugin-introduction Introduction This tutorial will show you how to create a new ArmarX gui-plugin by implementing a simple timer as a ArmarX gui-plugin. -\attention This tutorial assumes that you have already installed ArmarX (at least ArmarXCore and ArmarXGui.) If you haven't done yet, -follow the \ref ArmarXCore-Installation "Installation instructions for ArmarXCore" to install them. +\attention This tutorial assumes that you have already installed ArmarX (at least ArmarXCore and ArmarXGui). +If you haven't done yet, follow the +\ref ArmarXCore-Installation "Installation instructions for ArmarXCore" to install them. \section ArmarXGui-Tutorial-CreateGuiPlugin-getting-started Getting Started -You can use the armarx-package tool to create new packages, gui-plugins, template files required for them, etc. +You can use the armarx-package tool to create new packages, gui-plugins, template files required for them, etc. -\subsection ArmarXGui-Tutorial-CreateGuiPlugin-create-package Create your own package +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-create-package Create Your Own Package -#If you already have a package in which you would like to add a new qui-plugin, go to the next step \ref ArmarXGui-Tutorial-CreateGuiPlugin-create-gui-plugin. +If you already have a package in which you would like to add a new qui-plugin, go to the next step: +\ref ArmarXGui-Tutorial-CreateGuiPlugin-create-gui-plugin. In order to create a new ArmarX package called "GuiPluginTutorials", execute the following code in the directory where you want to make your package, \code{.sh} -${ArmarX_DIR}/ArmarXCore/build/bin/armarx-package init GuiPluginTutorials +armarx-package init GuiPluginTutorials \endcode or you can use "-d" option to specify the directory. \code{.sh} -${ArmarX_DIR}/ArmarXCore/build/bin/armarx-package init GuiPluginTutorials -d ${ArmarX_DIR}/ +armarx-package init GuiPluginTutorials -d ${ArmarX_DIR}/ \endcode -\subsection ArmarXGui-Tutorial-CreateGuiPlugin-create-gui-plugin Generate template files of your new gui-plugin -Execute the following command in the toplevel directory of your package, all required files relevant to a new gui-plugin called "MyGuiPlugin" will be generated. +\note +If `armarx-package` is not found, try `${ArmarX_DIR}/ArmarXCore/build/bin/armarx-package`. +\endnote + +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-create-gui-plugin Generate Template Files for Your New Gui-Plugin +Execute the following command in the toplevel directory of your package. +All required files relevant to a new gui-plugin called "MyOwnTimer" will be generated. \code{.sh} -${ArmarX_DIR}/ArmarXCore/build/bin/armarx-package add gui-plugin MyGuiPlugin +armarx-package add gui-plugin MyOwnTimer \endcode +\note +Avoid suffixing your gui-plugin with "GuiPlugin". Instead, try to use an expressive name +such as "XYViewer", "XYEditor", "XYManager", "XYSegmenter", ... +\endnote + Have a look into the directory to check if everything worked as intended. You should find the following files: \code{.sh} source/GuiPluginTutorials/gui-plugins/CMakeLists.txt -source/GuiPluginTutorials/gui-plugins/MyGuiPlugin -source/GuiPluginTutorials/gui-plugins/MyGuiPlugin/MyGuiPluginGuiPlugin.h -source/GuiPluginTutorials/gui-plugins/MyGuiPlugin/MyGuiPluginGuiPlugin.cpp -source/GuiPluginTutorials/gui-plugins/MyGuiPlugin/MyGuiPluginWidgetController.h -source/GuiPluginTutorials/gui-plugins/MyGuiPlugin/MyGuiPluginWidgetController.cpp -source/GuiPluginTutorials/gui-plugins/MyGuiPlugin/MyGuiPluginWidget.ui +source/GuiPluginTutorials/gui-plugins/MyOwnTimer +source/GuiPluginTutorials/gui-plugins/MyOwnTimer/MyOwnTimerWidgetController.h +source/GuiPluginTutorials/gui-plugins/MyOwnTimer/MyOwnTimerWidgetController.cpp +source/GuiPluginTutorials/gui-plugins/MyOwnTimer/MyOwnTimerWidget.ui source/GuiPluginTutorials/gui-plugins/CMakeLists.txt \endcode +In older versions of ArmarX (or older gui-plugins), you may also find these files: +\code +source/GuiPluginTutorials/gui-plugins/MyOwnTimer/MyOwnTimerGuiPlugin.h +source/GuiPluginTutorials/gui-plugins/MyOwnTimer/MyOwnTimerGuiPlugin.cpp +\endcode + \section ArmarXGui-Tutorial-CreateGuiPlugin-implementing Start Implementing -Next step is implementation. +Next step is the implementation. -ArmarX Gui wigets are created based on Qt. Thus, using Qt Creator is good solution to edit ui files and C++ source. -Open your project on Qt Creator by following the \ref ArmarXCore-Tutorials-sce-edit-the-source-code "Tutorial: Counting with Statecharts -> Edit the source code"; then, you find the file tree like this: +ArmarX Gui wigets are created based on Qt. Thus, using QtCreator is good solution to edit `.ui` files and C++ source. +Open your project in QtCreator by following the +\ref ArmarXCore-Tutorials-sce-edit-the-source-code "Tutorial: Counting with Statecharts -> Edit the source code"; +then, you find the file tree like this: <!--\htmlonly <img src="tutorial-gp-fileTree.png" width="500px"> \endhtmlonly --> \image html tutorial-gp-fileTree.png -\note Before "run cmake", you have to add a dependency on the ArmarXGui package (otherwise, you fail the "run cmake".) -Add the following line into the CMakeLists.txt at the top-level directory of your project. +\note Before running `cmake`, make sure your package has a dependency on the ArmarXGui package +(otherwise, running cmake will fail.) +The CMakeLists.txt at the top-level directory of your project should contain this line: \code depends_on_armarx_package(ArmarXGui "OPTIONAL") \endcode -\subsection ArmarXGui-Tutorial-CreateGuiPlugin-design-gui-widget Design gui widget on QT-creator +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-design-gui-widget Design the GUI Widget in QtCreator The simple timer needs two buttons to start and stop and a text box to display the time on the widget. -Open MyGuiPluginWidget.ui from the file tree on the left side of Qt Creator, add Qt UI elements by selecting from the left-side list, and set paramters like the following way: +Open MyOwnTimerWidget.ui from the file tree on the left side of QtCreator. +Add Qt UI elements by selecting from the left-side list, and setting paramters like the following way: -\li Open MyGuiPluginWidget.ui +\li Open MyOwnTimerWidget.ui <!--\htmlonly <img src="tutorial-gp-open-ui.png" width="1000px"> \endhtmlonly--> \image html tutorial-gp-open-ui.png -\li Add "push botton" on the widget. Layout elements help to align child UI elements. -Visit Qt's HP (for example <a href="http://doc.qt.io/qt-5/qtexamplesandtutorials.html">Qt Examples And Tutorials</a>) to know how to design GUI by Qt and on Qt Creator. +\li Add "Vertical Layout" and a "Push Button" to the widget. +Layout elements help to align child UI elements. +Visit Qt's HP (for example <a href="http://doc.qt.io/qt-5/qtexamplesandtutorials.html">Qt Examples And Tutorials</a>) +to learn how to design a GUI in Qt and on QtCreator. <!--\htmlonly <img src="tutorial-gp-add-button.png" width="1000px"> \endhtmlonly--> \image html tutorial-gp-add-button.png -\li Change the instance name and initial text of the button. Repeat the same process for "stop" button. +\li Change the button's instance name to `buttonStart` and its initial text to "Start". <!--\htmlonly <img src="tutorial-gp-set-button-property.png" width="1000px"> \endhtmlonly--> \image html tutorial-gp-set-button-property.png -\li Add "label" to show the elapsed time, and change the instance name and initial text of the label. +\li Repeat the same process for "stop" button and call it `buttonStop`. + +\li Add a "Label" to show the elapsed time, and change its instance name to `labelTimer` +and initial text to "00:00:00.000". <!--\htmlonly <img src="tutorial-gp-add-text.png" width="1000px"> \endhtmlonly--> \image html tutorial-gp-add-text.png @@ -84,34 +109,77 @@ Visit Qt's HP (for example <a href="http://doc.qt.io/qt-5/qtexamplesandtutorials <!--\htmlonly <img src="tutorial-gp-last-design.png" width="1000px"> \endhtmlonly--> \image html tutorial-gp-last-design.png -\subsection ArmarXGui-Tutorial-CreateGuiPlugin-edit-source Edit the source code +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-edit-source Edit the Source Code -Next step is adding some souce code into the template files. -Now, you have two kinds of classes for the new gui-plugin: -\li MyGuiPluginGuiPlugin and -\li MyGuiPluginWidgetController. +Next step is adding some souce code into the template files. +The main file to edit is the `MyOwnTimerWidgetController.h`. +There, the functionality of your gui-plugin is defined and controlled. -MyGuiPluginGuiPlugin is something like an interface between your gui-plugin and ArmarXGui and MyGuiPluginWidgetController is the class to define and control the functionality of your gui-plugin. -Therefore, you should add some code in MyGuiPluginWidgetController. +\note +In older versions of ArmarX, you may have another file called `MyOwnTimerGuiPlugin.h`. +MyOwnTimerGuiPlugin forms the interface between your gui-plugin and ArmarXGui, +telling ArmarXGui that your plugin is there and how to load it. +You usually do not need to modify it, unless you rename your controller class. +In newer versions of ArmarX, this file is auto-generated by CMake. +\endnote -For MyGuiPluginWidgetController.h, add QTimer for periodic task of your gui-plugin and two slots related to the Qt's PushButton operation. +In `MyOwnTimerWidgetController.h`, +add a `QTimer` to build a periodic task in your gui-plugin and two slots related to the PushButtons' operations. \code{.h} public slots: - // QT slot declarations + void updateGui(); - void stopButtonPressed(); - void startButtonPressed(); + void startTimer(); + void stopTime(); + private: - Ui::MyGuiPluginWidget widget; - QTimer* updateTimer; + + Ui::MyOwnTimerWidget widget; + + QTimer* updateTimer; + IceUtil::Time startTime; + \endcode -For MyGuiPluginWidgetController.cpp, implement QTimer and Qt slots. +Signals and slots are Qt's event handling system. UI elements, such as buttons, fire signal +when something happens, e.g. when a button is pressed or a value has changed. +Signals can be connected to slots, which are functions to be called +when the event occurs. +Signals can carry arguments. + +\note +Try naming your slots according to what they are doing, not when they are called. +For example, prefer `startTimer()` over `startButtonPressed()`. Qt allows slots to be connected +to multiple signals, and vice versa, so it makes less sense to name a slot after the signal +that "usually" triggers it than to express what it will do when it is triggered. +\endnote + +\note +Qt has its own memory management system based on object ownership. A parent widget usually +frees all its child widget when it is destructed. Therefore, Qt client code makes heavy use +of raw pointers (`QTimer*`) instead of smart pointers (`std::unique_ptr<QTimer>`). +When creating Qt objects (e.g. `new QTimer()`), always make sure to add them to a parent widget +on construction (e.g. `new QTimer(parent)`) or after construction. +\endnote + +To be able to use `QTimer`, we either have to +\code{.h} +#include <QTimer> +\endcode +or forward declare it via (outside any namespace) +\code{.h} +class QTimer; +\endcode +Forward declarations are a nice way to reduce code included by the header (and header subsequently including yours). +If you use the forward declaration in the header, you must use the real include in the .cpp file. + -1) Create QTimer +Now, implement the slots and initilize the `QTimer` in `MyOwnTimerWidgetController.cpp`: + +1) Create the `QTimer` \code{.cpp} -MyGuiPluginWidgetController::MyGuiPluginWidgetController() +MyOwnTimerWidgetController::MyOwnTimerWidgetController() { widget.setupUi(getWidget()); updateTimer = new QTimer(this); // <-- add this line @@ -120,44 +188,58 @@ MyGuiPluginWidgetController::MyGuiPluginWidgetController() 2) Connect Qt's signals and slots \code{.cpp} -void MyGuiPluginWidgetController::onConnectComponent() +MyOwnTimerWidgetController::MyOwnTimerWidgetController() { - connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateGui())); // <-- add this line - connect(widget.buttonStart, SIGNAL(clicked()), this, SLOT(startButtonPressed())); // <-- add this line - connect(widget.buttonStop, SIGNAL(clicked()), this, SLOT(stopButtonPressed())); // <-- add this line + ... + + using This = MyOwnTimerWidgetController; + + connect(updateTimer, &QTimer::timeout, this, &This::updateGui); + connect(widget.buttonStart, &QPushButton::pressed, this, &This::startTimer); + connect(widget.buttonStop, &QPushButton::pressed, this, &This::stopTimer); } \endcode +\note +In Qt 5, signals can also be connected to lambdas: +\code{.cpp} +connect(widget.buttonStart, &QPushButton::pressed, []() +{ + // To something when the button was pressed. +}); +\endcode +\endnote + + 3) Implement the slots \code{.cpp} -void MyGuiPluginWidgetController::startButtonPressed() +void MyOwnTimerWidgetController::startTimer() { - ARMARX_INFO << getWidgetName() << ": startButtonPressed()"; - // start the qt-timer + ARMARX_INFO << getWidgetName() << ": startTimer()"; + // Start the timer. updateTimer->start(1000); - tStart = IceUtil::Time::now(); - widget.labelTimer->setText("00:00:00.000"); + startTime = IceUtil::Time::now(); + widget.labelTimer->setText(QString::fromStdString(IceUtil::Time::seconds(0).toDuration())); } -void MyGuiPluginWidgetController::stopButtonPressed() +void MyOwnTimerWidgetController::stopTimer() { - - ARMARX_INFO << getWidgetName() << ": stopButtonPressed()"; - // stop the qt-timer + ARMARX_INFO << getWidgetName() << ": stopTimer()"; + // Stop the timer. updateTimer->stop(); } -void MyGuiPluginWidgetController::updateGui() +void MyOwnTimerWidgetController::updateGui() { - // update the text of widget.labelTimer - IceUtil::Time tDiff = IceUtil::Time::now() - tStart; - QString qTxt = QString::fromStdString(tDiff.toDuration()); - widget.labelTimer->setText(qTxt); + // Update the text of widget.labelTimer. + IceUtil::Time timePassed = IceUtil::Time::now() - startTime; + QString text = QString::fromStdString(timePassed.toDuration()); + widget.labelTimer->setText(text); } \endcode -\subsection ArmarXGui-Tutorial-CreateGuiPlugin-build Build your package +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-build Build Your Package Then, you can build your package/gui-plugin. All you need to do for building is hammering the following lines into the console: \code{.sh} @@ -166,42 +248,64 @@ Then, you can build your package/gui-plugin. All you need to do for building is make \endcode -\section ArmarXGui-Tutorial-CreateGuiPlugin-execute Load your new gui-plugin from ArmarXGui -Finally, you can use your new gui-plugin!! + +\section ArmarXGui-Tutorial-CreateGuiPlugin-execute Load Your new Gui-Plugin in ArmarXGui +Finally, you can use your new gui-plugin!! + +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-execute-manual Manually Load the Gui-Plugin \li Start ice -\note Ice needs to be started only once on the system. If you have already started Ice, skip this process. +\note Ice needs to be started only once on the system. If you have already started Ice, skip this step. ArmarX relies on Ice (the Internet communication engine) to enable communication between its components. -Before you can use ArmarX functionality, such as the ArmarXGui, you need to start Ice. +Before you can use ArmarX functionality, such as the ArmarXGui, you need to start Ice. \code{.sh} -${ArmarX_DIR}/ArmarXCore/build/bin/armarx start +armarx start \endcode \li Start ArmarXGui Then, let's pen ArmarXGui by using the following command: \code{.sh} -${ArmarX_DIR}/ArmarXCore/build/bin/armarx gui +armarx gui \endcode or \code{.sh} -${ArmarX_DIR}/Gui/build/bin/ArmarXGuiRun +.../ArmarXGui/build/bin/ArmarXGuiRun \endcode \li Load your new gui-plugin <br> -First, you have to load the library of your gui-plugin in order to open it on ArmarXGui. On ArmarXGui, "File" -> "Load Plugin" -> select your library (libMyGuiPluginGuiPlugin.so in this case). +First, you have to load the library of your gui-plugin in order to open it on ArmarXGui. +In ArmarXGui, select "File" -> "Load Plugin" -> select your gui-plugin library +(in this case libMyOwnTimerGuiPlugin.so in GuiPluginTutorials/build/lib/). Then, you can select your library from "Add widget" as follows. \image html tutorial-gp-AmarXGui.png -\note The name of your gui-plugin presented in "Add widget" tree is defined in the getWidgetName() in MyGuiPluginWidgetController.h. You can determin the hierarchy of the tree by using "." for the name. +\note The name of your gui-plugin presented in "Add widget" tree is defined in the getWidgetName() in MyOwnTimerWidgetController.h. +You can determin the hierarchy of the tree by using "." for the name. \code{.h} virtual QString getWidgetName() const { - return "Tutorial.MyGuiPlugin"; + return "Tutorial.MyOwnTimer"; } \endcode -\li Execute your simple timer!! +\li Execute your simple timer!! \image html tutorial-gp-simpletimer.png + +\subsection ArmarXGui-Tutorial-CreateGuiPlugin-execute-auto Automate Loading of Gui-Plugins + +We can automate the loading of all gui-plugins from an ArmarX package. + +1. Open the file `~/.armarx/default.cfg`: +\code +gedit ~/.armarx/default.cfg +\endcode +2. Append your package to `ArmarX.AdditionalPackages` (after a comma, without a space): +\code +# Put your custom ArmarX Packages in this list, e.g. so that the gui can find their plugins. +ArmarX.AdditionalPackages=...,GuiPluginTutorials +\endcode + + */