4.3.4. Plugin WAF Module

At this point, the project needs to be extended. The new widget-library/plugin module is added as shown in the following listing:

widget-library
├── plugin
│   ├── src
│   │   ├── include
│   │   │   └── cut
│   │   │       └── examples
│   │   │           └── widgets
│   │   │               └── plugin
│   │   │                   ├── QeExampleDmsLabelPlugin.hpp
│   │   │                   └── QeExamplesWidgetCollection.hpp
│   │   ├── QeExampleDmsLabelPlugin.cpp
│   │   └── QeExamplesWidgetCollection.cpp
│   └── wscript
├── widgets
│   ├── src
│   │   ├── include
│   │   │   ├── cut
│   │   │   │   └── examples
│   │   │   │       └── widgets
│   │   │   │           └── widgets
│   │   │   │               └── QeExampleDmsLabel.hpp
│   │   │   └── QeExamplesWidgets.h
│   │   └── QeExampleDmsLabel.cpp
│   └── wscript
└── wscript

The plugin/wscript file looks a bit different than the one in widgets WAF module:

../../../../examples/widget_library/plugin/wscript
 1from wtools.module import declare_qt5cshlib
 2
 3# In the "use" parameter, if using modules from the same project,
 4#  you should use the name of the directory. It can be different from
 5#  the name of the target of that directory.
 6declare_qt5cshlib(
 7    target='cutExamplesWidgetsPlugin',
 8    use='widgets QT5CORE QT5GUI Qt5Widgets Qt5Designer Qt5Xml Qt5UiPlugin',
 9    qt5_plugin=True
10)

This wscript file also uses the declare_qt5cshlib wtools instruction. The main difference here is the additional keyword argument qt5_plugin=True. This will indicate to the install command that the resulting library should be place into $INTROOT/lib64/designer. Then the QT_PLUGIN_PATH environment variable can be used by Qt Designer to find any widgets plugin. QT_PLUGIN_PATH shall be $INTROOT/lib64, as the designer expects a certain directory structure. One of these directories is designer, which is the one used to store any plugin intended for Qt Designer.

The second different is that the use argument for declare_qt5cshlib command now include widgets. This indicates WAF that widgets module is a dependency, and therefore, must be compiled before this module. Also, when compiling this plugin module, its includes added to include paths, and its library added for linking instructions. widgets string must be the names of the directory structure, not the target names. If many directory levels are used in the project, then replace / with ..

Also, the installation instruction does not copy the includes from this module to the $INTROOT. As the plugins are not intended to be used for development, these files are not meant for further development.

4.3.4.1. Header files

Two classes, one for QeExampleDmsLabel plugin, and another for a Collection, are defined in this module.

The listing of the QeExampleDmsLabelPlugin.hpp is given below:

examples/widget_library/plugin/src/include/cut/examples/widgets/plugin/QeExampleDmsLabelPlugin.hpp
 1/**
 2 * @file
 3 * @ingroup widgets_plugin
 4 * @copyright ESO - European Southern Observatory
 5 * @author Arturo Hoffstadt Urrutia <ahoffsta@eso.org>
 6 *
 7 * @brief QeDmsLabel plugin for Qt Designer
 8 */
 9
10#ifndef QEEXAMPLEDMSLABELPLUGIN_HPP
11#define QEEXAMPLEDMSLABELPLUGIN_HPP
12
13#include <QtUiPlugin/QDesignerCustomWidgetInterface>
14
15/**
16 * @brief QeExampleDmsLabel Plugin for Qt Designer. Enables WYSIWYG design while using
17 * Qt Designer.
18 * @class QeExampleDmsLabelPlugin
19 * @ingroup widgets_plugin
20 *
21 * This class implements the QDesignerCustomWidgetInterface that enables
22 * Qt Designer to create new instances of the QeDmsLabel widget, and obtain
23 * information to present the widget, in the WYSIWYG view of the form, its
24 * toolbar, and properties view.
25 *
26 * @warning This class is not intended for developers, and is internal to CUT.
27 */
28class QeExampleDmsLabelPlugin : public QObject, public QDesignerCustomWidgetInterface
29{
30    Q_OBJECT
31    Q_INTERFACES(QDesignerCustomWidgetInterface)
32
33public:
34    QeExampleDmsLabelPlugin(QObject *parent = 0);
35
36    bool isContainer() const;
37    bool isInitialized() const;
38    QIcon icon() const;
39    QString domXml() const;
40    QString group() const;
41    QString includeFile() const;
42    QString name() const;
43    QString toolTip() const;
44    QString whatsThis() const;
45    QWidget *createWidget(QWidget *parent);
46    void initialize(QDesignerFormEditorInterface *core);
47
48private:
49    bool m_initialized;
50};
51
52#endif // QEEXAMPLEDMSLABELPLUGIN_HPP

This file does not change much from widget to widget. Only the name of the class does which is used for Class name, and constructor.

The contents of the Collection are also not very dynamic:

examples/widget_library/plugin/src/include/cut/examples/widgets/plugin/QeExamplesWidgetCollection.hpp
 1#ifndef QEEXAMPLEWIDGETCOLLECTION_HPP
 2#define QEEXAMPLEWIDGETCOLLECTION_HPP
 3
 4#include <QtUiPlugin/QDesignerCustomWidgetInterface>
 5#include <qplugin.h>
 6
 7class QeExamplesWidgetCollection : public QObject, public QDesignerCustomWidgetCollectionInterface
 8{
 9    Q_OBJECT
10    Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
11#if QT_VERSION >= 0x050000
12    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface")
13#endif // QT_VERSION >= 0x050000
14
15public:
16    explicit QeExamplesWidgetCollection(QObject *parent = 0);
17
18    virtual QList<QDesignerCustomWidgetInterface*> customWidgets() const;
19
20private:
21    QList<QDesignerCustomWidgetInterface*> m_widgets;
22};
23
24#endif // QEEXAMPLEWIDGETCOLLECTION_HPP

The name of this class is tied to the name of the library instead of a particular widget. This Collection will provide access to all widgets in the plugin. The matter that we only provide only changes nothing. Qt Designer provides to us an interface ready to use to extend it with more widgets.

4.3.4.2. Implementation

The plugin implementation has more details:

examples/widget_library/plugin/src/QeExamplesWidgetCollection.cpp
 1#include "include/cut/examples/widgets/plugin/QeExamplesWidgetCollection.hpp"
 2#include "include/cut/examples/widgets/plugin/QeExampleDmsLabelPlugin.hpp"
 3
 4#if WAF
 5#include "include/cut/examples/widgets/plugin/QeExamplesWidgetCollection.moc"
 6#endif
 7
 8
 9QeExamplesWidgetCollection::QeExamplesWidgetCollection(QObject *parent)
10    : QObject(parent)
11{
12    m_widgets.append(new QeExampleDmsLabelPlugin(this));
13}
14
15QList<QDesignerCustomWidgetInterface*> QeExamplesWidgetCollection::customWidgets() const
16{
17    return m_widgets;
18}
examples/widget_library/plugin/src/QeExampleDmsLabelPlugin.cpp
  1#include "cut/examples/widgets/widgets/QeExampleDmsLabel.hpp"
  2#include "include/cut/examples/widgets/plugin/QeExampleDmsLabelPlugin.hpp"
  3
  4#include <QtPlugin>
  5
  6#if WAF
  7#include "include/cut/examples/widgets/plugin/QeExampleDmsLabelPlugin.moc"
  8#endif
  9
 10QeExampleDmsLabelPlugin::QeExampleDmsLabelPlugin(QObject *parent)
 11    : QObject(parent)
 12{
 13    m_initialized = false;
 14}
 15
 16void QeExampleDmsLabelPlugin::initialize(QDesignerFormEditorInterface * /* core */)
 17{
 18    if (m_initialized)
 19        return;
 20
 21    // Add extension registrations, etc. here
 22
 23    m_initialized = true;
 24}
 25
 26bool QeExampleDmsLabelPlugin::isInitialized() const
 27{
 28    return m_initialized;
 29}
 30
 31QWidget *QeExampleDmsLabelPlugin::createWidget(QWidget *parent)
 32{
 33    return new QeExampleDmsLabel(parent);
 34}
 35
 36QString QeExampleDmsLabelPlugin::name() const
 37{
 38    return QLatin1String("QeExampleDmsLabel");
 39}
 40
 41QString QeExampleDmsLabelPlugin::group() const
 42{
 43    return QLatin1String("Display Widgets");
 44}
 45
 46QIcon QeExampleDmsLabelPlugin::icon() const
 47{
 48    return QIcon();
 49}
 50
 51QString QeExampleDmsLabelPlugin::toolTip() const
 52{
 53    return QLatin1String("Displays as Degrees-Minutes-Seconds, or Hour-Minutes-Seconds, an angle in radians.");
 54}
 55
 56QString QeExampleDmsLabelPlugin::whatsThis() const
 57{
 58    return QLatin1String("");
 59}
 60
 61bool QeExampleDmsLabelPlugin::isContainer() const
 62{
 63    return false;
 64}
 65
 66QString QeExampleDmsLabelPlugin::domXml() const
 67{
 68    return QLatin1String("<widget class=\"QeExampleDmsLabel\" name=\"qeExampleDmsLabel\">\n"
 69                            "  <property name=\"radians\">\n"
 70                            "     <double>0.0</double>\n"
 71                            "  </property>\n"
 72                            "  <property name=\"precision\">\n"
 73                            "     <number>3</number>\n"
 74                            "  </property>\n"
 75                         "</widget>\n");
 76}
 77
 78/*
 79 * We use a small hack to allow compatibility between Python
 80 * and C++ resolution of classes on the UI compilers.
 81 * C++ UI compiler uses the includeFile() to construct its
 82 * include statement.
 83 *
 84 * #include QeWidgetExample.h
 85 * QeDartBoard* qeDartBoard = new QeDartBoard();
 86 *
 87 * Python UI compiler uses the includeFile() and the name()
 88 * to construct the import statement, but removes the '.h'
 89 * suffix.
 90 *
 91 * from QeWidgetExample import QeDartBoard
 92 *
 93 * Using an intermediary file allows us to use the same
 94 * includeFile() string under Python and C++.
 95 */
 96QString QeExampleDmsLabelPlugin::includeFile() const
 97{
 98    return QLatin1String("QeExamplesWidgets.h");
 99}
100
101////