by Vivi Glьckstad Karlsen |
The Qt 4 release incorporated a new multi-component Qt Designer. One of the main ideas behind the new version of Qt Designer was to provide the application as a collection of interchangeable components. It is complemented by the QtDesigner module, a library that you can use to write plugins and extensions for Qt Designer, as this article will show.
This article begins by taking a closer look at how you can create your own custom widget plugins, and then goes on to describe how you can extend and access Qt Designer's components using the classes provided by the QtDesigner module.
The module also makes it possible to embed Qt Designer's components into Integrated Development Environments (IDEs), a more advanced topic that we will not cover here.
Creating a Custom Widget Plugin |
In Qt Designer, the widget box component provides a selection of standard Qt widgets, layouts, and other objects that can be added to the forms by dragging them from the widget box and dropping them where they are required.
Using the QtDesigner module, you can create custom widget plugins that will appear in the widget box, and that can be added to forms using the same simple approach. In fact, writing a custom widget plugin is one of the more common ways to extend Qt Designer, and all the Qt 3 support widgets are integrated into Qt Designer this way.
When creating a custom widget plugin, you must supply a self-contained implementation of the custom widget. If you want the plugin's properties to be available and editable in Qt Designer's property editor, you must ensure that the properties are declared using the Q_PROPERTY() macro. Qt Designer uses Qt's property system to populate the editor, and if a property is declared in some other way, Qt Designer won't know about it.
You can implement several different extensions to make your widget even more interactive with Qt Designer. A container extension, for example, allows the user to add pages and widgets to a custom multi-page container widget (like a widget stack or tab widget) within Qt Designer's workspace. We will come back to the subject of extensions later.
Exposing the Plugin to Qt Designer |
To expose a custom widget to Qt Designer, and give enough information to construct the widget, you must provide it with an interface derived from the QDesignerCustomWidgetInterface class, located in the QtDesigner module.
While some of the QDesignerCustomWidgetInterface functions are pure virtual and must be reimplemented, others are not always relevant and can safely be omitted, such as the codeTemplate() that returns code to be included in .ui files, and the domXml() function that returns XML to describe the custom widget's default properties in Qt Designer.
Custom plugins also declare the interface that they expose with the Q_INTERFACES() macro.
A typical custom widget plugin is defined like this:
#include <QDesignerCustomWidgetInterface> class MyPlugin : public QObject, public QDesignerCustomWidgetInterface { Q_OBJECT Q_INTERFACES(QDesignerCustomWidgetInterface) public: MyPlugin(QObject *parent = 0); QString name() const; QString group() const; QString toolTip() const; QString whatsThis() const; QString includeFile() const; QIcon icon() const; bool isContainer() const; QWidget *createWidget(QWidget *parent); bool isInitialized() const; void initialize(QDesignerFormEditorInterface *editor); QString codeTemplate() const; QString domXml() const; private: bool initialized; };
The documentation for the Custom Widget Plugin example, supplied with Qt, provides a detailed description of these functions. The main issue to have in mind when implementing the plugin class, is that you must remember to export the plugin:
Q_EXPORT_PLUGIN2(mypluginsname, MyPlugin)
This macro ensures that the plugin can be accessed and constructed. Without it, Qt Designer cannot use the custom widget.
In most cases, exporting the plugin in this way is sufficient to ensure that Qt Designer and other applications can access the widget when using uic or with Qt's dynamic form loading facilities. If you want to use the custom widget directly by linking your application against the plugin, you must export the custom widget class explicitly in the following way:
#include <QDesignerExportWidget> class QDESIGNER_WIDGET_EXPORT MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget *parent = 0); }
With a self-contained custom widget implementation and an associated interface in place, we need to add some information to the corresponding .pro file:
TEMPLATE = lib CONFIG += designer plugin debug_and_release HEADERS = mywidget.h \ mywidgetplugin.h SOURCES = mywidget.cpp \ mywidgetplugin.cpp
The TEMPLATE and CONFIG lines tell qmake to create a library that is suitable for use as a Qt Designer plugin, relying on components supplied with Qt Designer. This means that the plugin will link against the designer library (e.g., libQtDesigner.so on Unix).
The header and source files for the custom widget are declared in the usual way, and in addition we provide the implementation of the plugin interface.
target.path = $$[QT_INSTALL_PLUGINS]/designer INSTALLS += target
The target.path and INSTALL lines ensure that the plugin is installed alongside the other Qt Designer widget plugins.
Provided that everything compiles and links, Qt Designer will add the custom widget to its widget box. Note that if a plugin is built in a mode that is incompatible with Qt Designer, it won't be loaded. The debug_and_release value is added to the CONFIG definition to ensure that our plugin will be loaded no matter how Qt Designer was built.
For more information about plugins, see the How to Create Qt Plugins page in Qt's reference documentation.
The Extensions Classes |
When implementing a custom multi-page container widget, you can make Qt Designer recognize it as such by providing it with an appropriate interface in addition to the plugin interface. Derive the interface from the QDesignerContainerExtension class provided by the QtDesigner module.
There are four available extension classes in the module:
QDesignerPropertySheetExtension and QDesignerMemberSheetExtension let you manipulate the appearance of a widget's properties and member functions in Qt Designer's different components and editing modes. All widgets in Qt Designer have a default member sheet which is used by the signals and slots editing mode, and a default property sheet that is used to populate Qt Designer's property editor. By reimplementing a member or property sheet extension, you can (and will) override the defaults. In other situations, you might want to use these interfaces to check if a particular member or property is supported by a given widget.
Creating an Extension |
To create an extension, you must first subclass the relevant extension class and reimplement its pure virtual functions. For example, a container extension's definition might look like this:
class MyContainerExtension : public QObject, public QDesignerContainerExtension { Q_OBJECT Q_INTERFACES(QDesignerContainerExtension) public: MyContainerExtension(MyContainerWidget *widget, QObject *parent); void addWidget(QWidget *widget); int count() const; int currentIndex() const; void insertWidget(int index, QWidget *widget); void remove(int index); void setCurrentIndex(int index); QWidget *widget(int index) const; private: MyContainerWidget *containerWidget; };
Be aware that although it is called QDesignerContainerExtension class, the class really encapsulates multi-page widgets, such as QTabWidget and QStackedWidget. This means that your widget must have a suitable page-based API. For example:
int MyContainerExtension::currentIndex() const { return myWidget->currentIndex(); }
To fully enable Qt Designer to manage and manipulate your custom multi-page widget, you must reimplement all the functions of the QDesignerContainerExtension class.
Initializing the Extension |
The Qt Designer extensions are not created until they are required, and their creation is controlled by Qt Designer's extension manager. For that reason, when implementing an extension, you must also create an extension factory – a class that is able to make instances of your derived extension class – then register it with the extension manager.
An extension factory can be implemented to create one or more types of extension. Derive your custom extension factory from QExtensionFactory and reimplement its createExtension() function in the following way:
QObject *MyExtFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const { MyWidget *widget = qobject_cast<MyWidget*>(object); if (!widget) return 0; if (iid == Q_TYPEID(QDesignerContainerExtension)) return new MyContainerExtension(widget, parent); else if (iid == Q_TYPEID(QDesignerTaskMenuExtension)) return new MyTaskMenuExtension(widget, parent); return 0; }
When an extension is required, Qt Designer's extension manager will look through all its registered factories, calling the createExtension() function for each one until it finds one that is able to create the requested extension. For this reason, in the createExtension() function, you must check that the QObject for which the extension is required and the requested extension type correspond to a widget type and extension type that your factory supports. Remember that your custom widget must contain the Q_OBJECT macro for this to work correctly.
If an extension factory does not support the requested widget, it simply returns a null pointer, allowing Qt Designer's extension manager to continue its search for a suitable factory.
Although the extensions are used in different situations, you can use the same implementation pattern when creating any extension by simply replacing the respective extension base class throughout the process.
To register your factory, you must access Qt Designer's current QDesignerFormEditorInterface object, which holds information about all of Qt Designer's components. We will take a closer look at the QDesignerFormEditorInterface class shortly, but note that the QDesignerCustomWidgetInterface::initialize() function is supplied with a pointer to the current QDesignerFormEditorInterface object.
The plugin's initialize() function is the best place to register an extension factory for the custom widget. The interface to the extension manager is obtained by using the QDesignerFormEditorInterface::extensionManager() function.
The initialize() function will look like this:
void MyPlugin::initialize( QDesignerFormEditorInterface *editor) { if (initialized) return; QExtensionManager *mgr = editor->extensionManager(); if (!mgr) return; mgr->registerExtensions(new MyExtensionFactory(mgr), Q_TYPEID(QDesignerTaskMenuExtension)); mgr->registerExtensions(new MyExtensionFactory(mgr), Q_TYPEID(QDesignerContainerExtension)); initialized = true; }
Note that an extension factory must be registered once for each of the extension types that it supports.
Accessing Qt Designer's Components |
The QtDesigner module contains several classes whose purpose is to provide access to Qt Designer's components, managers, and workspace. Be aware that these classes are not intended to be instantiated directly.
The QDesignerFormEditorInterface class provides access to the logic that integrates all of Qt Designer's components, managers, and workspace into a coherent application. You can use Qt Designer's current QDesignerFormEditorInterface object to retrieve interfaces to the various components.
The QDesignerActionEditorInterface and QDesignerObjectInspectorInterface classes allow you to change the behavior of Qt Designer's action editor and object inspector. The QDesignerPropertyEditorInterface class lets you query and manipulate the current state of the property editor, while the QDesignerWidgetBoxInterface class enables you to control the contents of the widget box.
In addition, you can use QDesignerFormEditorInterface to retrieve interfaces to Qt Designer's extension manager (QExtensionManager) and form window manager (QDesignerFormWindowManagerInterface). The extension manager controls the construction of extensions as they are required, while the form window manager controls the form windows in Qt Designer's workspace.
Once you have an interface to Qt Designer's form window manager (QDesignerFormWindowManagerInterface), you can use it to gain access to all the form windows currently residing in Qt Designer's workspace: The QDesignerFormWindowInterface class allows you to query and manipulate a form window, and it provides an interface to the form window's cursor. The QDesignerFormWindowCursorInterface class is a convenience class allowing you to query and modify a given form window's widget selection and, in addition, modify the properties of the form's widgets.
Summary |
In this article, we have given an overview of Qt Designer's components and extensions framework. Qt 4.1 is supplied with an updated Qt Designer manual, containing more detailed information about the available components. The QtDesigner module is now fully documented, and the release contains several new examples. We hope that you will take the opportunity to build on Qt Designer's new foundations.
Copyright © 2006 Trolltech | Trademarks |