Extending Qt Designer

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
Image:qt-logo_new.png Image:qq-title-article.png
Qt Quarterly | Выпуск 16 | Документация


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 toprovide the application as a collection of interchangeable components. It iscomplemented by the QtDesigner module, a library that you can use to writeplugins and extensions for Qt Designer,as this article will show.

Содержание

This article begins by taking a closer look at how you can create yourown custom widget plugins, and then goes on to describe how you canextend and access Qt Designer'scomponents using the classes provided by the QtDesigner module.

The module also makes it possible to embed Qt Designer's components intoIntegrated Development Environments (IDEs), a more advanced topic thatwe will not cover here.

[править] Creating a Custom Widget Plugin

In Qt Designer, the widget boxcomponent provides a selection of standard Qt widgets, layouts, and otherobjects that can be added to the forms by dragging them from the widget boxand dropping them where they are required.

Using the QtDesigner module, you can createcustom widget plugins that will appear in the widget box, and that can beadded to forms using the same simple approach. In fact, writing acustom 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-containedimplementation of the custom widget. If you want the plugin's properties to beavailable and editable in Qt Designer'sproperty editor, you must ensure that the properties are declared using theQ_PROPERTY() macro. Qt Designer uses Qt's propertysystem 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 moreinteractive with Qt Designer.A container extension, for example, allows the user to add pages and widgetsto a custom multi-page container widget (like a widget stack or tabwidget) within Qt Designer'sworkspace. 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 enoughinformation to construct the widget, you must provide it with an interfacederived from the QDesignerCustomWidgetInterfaceclass, located in the QtDesigner module.

center

While some of the QDesignerCustomWidgetInterfacefunctions are pure virtual and must be reimplemented, others are not alwaysrelevant and can safely be omitted, such as the codeTemplate() thatreturns 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 theQ_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 Pluginexample, supplied with Qt, provides a detailed description of these functions.The main issue to have in mind when implementing the plugin class, is that youmust 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 customwidget.

In most cases, exporting the plugin in this way is sufficient to ensure that Qt Designer and other applicationscan access the widget when using uic or with Qt's dynamic form loadingfacilities. If you want to use the custom widget directly by linking yourapplication against the plugin, you must export the custom widget classexplicitly 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 anassociated interface in place, we need to add some information to thecorresponding .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 alibrary that is suitable for use as a Qt Designer plugin, relying oncomponents 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 aredeclared in the usual way, and in addition we provide theimplementation of the plugin interface.

target.path   = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS     += target

The target.path and INSTALL lines ensure that the pluginis installed alongside the other Qt Designer widget plugins.

Provided that everything compiles and links, Qt Designer will add the customwidget to its widget box. Note that if a plugin is built in a mode that isincompatible with Qt Designer, itwon't be loaded. The debug_and_release value is added to theCONFIG definition to ensure that our plugin will be loaded no matterhow Qt Designer was built.

For more information about plugins, see the How to Create Qt Plugins page in Qt'sreference documentation.

[править] The Extensions Classes

When implementing a custom multi-page container widget, you can make Qt Designer recognize it as suchby providing it with an appropriate interface in addition to the plugininterface. Derive the interface from the QDesignerContainerExtensionclass provided by the QtDesigner module.

There are four available extension classes in the module:

These extensions can be used in a variety of situations: QDesignerContainerExtensionprovides an interface that you can implement to allow custom multi-pagecontainer widgets to be manipulated within Qt Designer, while the QDesignerTaskMenuExtensionclass allows you to add custom menu entries to Qt Designer's task menu.

QDesignerPropertySheetExtensionand QDesignerMemberSheetExtensionlet you manipulate the appearance of a widget's properties and member functionsin Qt Designer's differentcomponents and editing modes. All widgets in Qt Designer have a default membersheet which is used by the signals and slots editing mode, and a defaultproperty 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 theseinterfaces to check if a particular member or property is supported by a givenwidget.

[править] Creating an Extension

To create an extension, you must first subclass the relevant extensionclass and reimplement its pure virtual functions. For example, acontainer 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 QDesignerContainerExtensionclass, the class really encapsulates multi-page widgets, such as QTabWidget and QStackedWidget. This means that yourwidget must have a suitable page-based API. For example:

int MyContainerExtension::currentIndex() const
{
	return myWidget->currentIndex();
}

To fully enable Qt Designer tomanage and manipulate your custom multi-page widget, you must reimplement allthe functions of the QDesignerContainerExtensionclass.

[править] Initializing the Extension

The Qt Designer extensions are notcreated 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 anextension factory – a class that is able to make instances of your derivedextension class – then register it with the extension manager.

An extension factory can be implemented to create one or more types ofextension. Derive your custom extension factory from QExtensionFactory and reimplementits createExtension() function in the following way:

QObject *MyExtFactory::createExtension(QObject *object,
        const QString &amp;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 managerwill look through all its registered factories, calling thecreateExtension() function for each one until it finds one that isable to create the requested extension. For this reason, in thecreateExtension() function, you must check that the QObject for which the extension is requiredand the requested extension type correspond to a widget type and extensiontype that your factory supports. Remember that your custom widget must containthe Q_OBJECT macro for this to workcorrectly.

If an extension factory does not support the requested widget, itsimply returns a null pointer, allowing Qt Designer's extension managerto continue its search for a suitable factory.

Although the extensions are used in different situations, you canuse the same implementation pattern when creating any extension bysimply replacing the respective extension base class throughout theprocess.

center

To register your factory, you must access Qt Designer's current QDesignerFormEditorInterfaceobject, which holds information about all of Qt Designer's components. We willtake a closer look at the QDesignerFormEditorInterfaceclass shortly, but note that theQDesignerCustomWidgetInterface::initialize()function is supplied with a pointer to the current QDesignerFormEditorInterfaceobject.

The plugin's initialize() function is the best place to register anextension factory for the custom widget. The interface to the extensionmanager is obtained by using theQDesignerFormEditorInterface::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 theextension types that it supports.

[править] Accessing Qt Designer's Components

The QtDesigner module contains several classes whose purposeis to provide access to Qt Designer's components, managers,and workspace. Be aware that these classes are not intended to be instantiateddirectly.

The QDesignerFormEditorInterfaceclass 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 QDesignerFormEditorInterfaceobject to retrieve interfaces to the various components.

The QDesignerActionEditorInterfaceand QDesignerObjectInspectorInterfaceclasses allow you to change the behavior of Qt Designer's action editor andobject inspector. The QDesignerPropertyEditorInterfaceclass 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 QDesignerFormEditorInterfaceto retrieve interfaces to Qt Designer's extension manager( QExtensionManager) and form windowmanager( QDesignerFormWindowManagerInterface).The extension manager controls the construction of extensions as they arerequired, 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 QDesignerFormWindowInterfaceclass allows you to query and manipulate a form window, and it provides aninterface to the form window's cursor. The QDesignerFormWindowCursorInterfaceclass is a convenience class allowing you to query and modify a given formwindow's widget selection and, in addition, modify the properties of the form'swidgets.

[править] Summary

In this article, we have given an overview of Qt Designer's components andextensions 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 fullydocumented, and the release contains several new examples. We hope that youwill take the opportunity to build on Qt Designer's new foundations.