summaryrefslogtreecommitdiffstats
path: root/doc/src/examples/containerextension.qdoc
blob: 67910156bf3ae1f8dd43d4ea6f5f73f69a3e5e87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example designer/containerextension
    \title Container Extension Example

    \brief The Container Extension example shows how to create a custom
    multi-page plugin for Qt Designer using the
    QDesignerContainerExtension class.

    \image containerextension-example.png

    To provide a custom widget that can be used with \QD, we need to
    supply a self-contained implementation. In this example we use a
    custom multi-page widget designed to show the container extension
    feature.

    An extension is an object which modifies the behavior of \QD. The
    QDesignerContainerExtension enables \QD to manage and manipulate a
    custom multi-page widget, i.e. adding and deleting pages to the
    widget.

    There are four available types of extensions in \QD:

    \list
        \o QDesignerMemberSheetExtension  provides an extension that allows
           you to manipulate a widget's member functions which is displayed
           when configuring connections using Qt Designer's mode for editing
           signals and slots.
        \o QDesignerPropertySheetExtension provides an extension that
           allows you to manipulate a widget's properties which is displayed
           in Qt Designer's property editor.
        \o QDesignerTaskMenuExtension provides an extension that allows
           you to add custom menu entries to \QD's task menu.
        \o QDesignerContainerExtension provides an extension that allows
           you to add (and delete) pages to a multi-page container plugin
           in \QD.
    \endlist

    You can use all the extensions following the same pattern as in
    this example, only replacing the respective extension base
    class. For more information, see the \l {QtDesigner Module}.

    The Container Extension example consists of four classes:

    \list
    \o \c MultiPageWidget is a custom container widget that lets the user
       manipulate and populate its pages, and navigate among these
       using a combobox.
    \o \c MultiPageWidgetPlugin exposes the \c MultiPageWidget class
       to \QD.
    \o \c MultiPageWidgetExtensionFactory creates a
       \c MultiPageWidgetContainerExtension object.
    \o \c MultiPageWidgetContainerExtension provides the container
       extension.
    \endlist

    The project file for custom widget plugins needs some additional
    information to ensure that they will work within \QD. For example,
    custom widget plugins rely on components supplied with \QD, and
    this must be specified in the project file that we use. We will
    first take a look at the plugin's project file.

    Then we will continue by reviewing the \c MultiPageWidgetPlugin
    class, and take a look at the \c MultiPageWidgetExtensionFactory
    and \c MultiPageWidgetContainerExtension classes. Finally, we will
    take a quick look at the \c MultiPageWidget class definition.

    \section1 The Project File: containerextension.pro

    The project file must contain some additional information to
    ensure that the plugin will work as expected:

    \snippet examples/designer/containerextension/containerextension.pro 0
    \snippet examples/designer/containerextension/containerextension.pro 1

    The \c TEMPLATE variable's value makes \c qmake create the custom
    widget as a library. Later, we will ensure that the widget will be
    recognized as a plugin by Qt by using the Q_EXPORT_PLUGIN2() macro
    to export the relevant widget information.

    The \c CONFIG variable contains two values, \c designer and \c
    plugin:

    \list
        \o \c designer: Since custom widgets plugins rely on components
           supplied with \QD, this value ensures that our plugin links against
           \QD's library (\c libQtDesigner.so).

        \o \c plugin: We also need to ensure that \c qmake considers the
           custom widget a \e plugin library.
    \endlist

    When Qt is configured to build in both debug and release modes,
    \QD will be built in release mode.  When this occurs, it is
    necessary to ensure that plugins are also built in release
    mode. For that reason we add a \c debug_and_release value to the
    \c CONFIG variable. Otherwise, if a plugin is built in a mode that
    is incompatible with \QD, it won't be loaded and installed.

    The header and source files for the widget are declared in the
    usual way:

    \snippet examples/designer/containerextension/containerextension.pro 2

    We provide an implementation of the plugin interface so that \QD
    can use the custom widget. In this particular example we also
    provide implementations of the container extension interface and
    the extension factory.

    It is important to ensure that the plugin is installed in a
    location that is searched by \QD. We do this by specifying a
    target path for the project and adding it to the list of items to
    install:

    \snippet doc/src/snippets/code/doc_src_examples_containerextension.pro 0

    The container extension is created as a library, and will be
    installed alongside the other \QD plugins when the project is
    installed (using \c{make install} or an equivalent installation
    procedure).

    Note that if you want the plugins to appear in a Visual Studio
    integration, the plugins must be built in release mode and their
    libraries must be copied into the plugin directory in the install
    path of the integration (for an example, see \c {C:/program
    files/trolltech as/visual studio integration/plugins}).

    For more information about plugins, see the \l {How to Create Qt
    Plugins} documentation.

    \section1 MultiPageWidgetPlugin Class Definition

    The \c MultiPageWidgetPlugin class exposes the \c MultiPageWidget
    class to \QD. Its definition is similar to the \l
    {designer/customwidgetplugin}{Custom Widget Plugin} example's
    plugin class which is explained in detail. The parts of the class
    definition that is specific to this particular custom widget is
    the class name and a couple of private slots:

    \snippet examples/designer/containerextension/multipagewidgetplugin.h 0

    The plugin class provides \QD with basic information about our
    plugin, such as its class name and its include file. Furthermore
    it knows how to create instances of the \c MultiPageWidget widget.
    \c MultiPageWidgetPlugin also defines the \l
    {QDesignerCustomWidgetInterface::initialize()}{initialize()}
    function which is called after the plugin is loaded into \QD. The
    function's QDesignerFormEditorInterface parameter provides the
    plugin with a gateway to all of \QD's API's.

    In the case of a multipage widget such as ours, we must also implement
    two private slots, currentIndexChanged() and pageTitleChanged(),
    to be able to update \QD's property editor whenever the user views
    another page or changes one of the page titles. To be able to give
    each page their own title, we have chosen to use the
    QWidget::windowTitle property to store the page title (for more
    information see the MultiPageWidget class \l
    {designer/containerextension/multipagewidget.cpp}{implementation}). Note
    that currently there is no way of adding a custom property (e.g.,
    a page title) to the pages without using a predefined property as
    placeholder.

    The \c MultiPageWidgetPlugin class inherits from both QObject and
    QDesignerCustomWidgetInterface. It is important to remember, when
    using multiple inheritance, to ensure that all the interfaces
    (i.e. the classes that doesn't inherit Q_OBJECT) are made known to
    the meta object system using the Q_INTERFACES() macro. This
    enables \QD to use \l qobject_cast() to query for supported
    interfaces using nothing but a QObject pointer.

    \section1 MultiPageWidgetPlugin Class Implementation

    The MultiPageWidgetPlugin class implementation is in most parts
    equivalent to the \l {designer/customwidgetplugin}{Custom Widget
    Plugin} example's plugin class:

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 0
    \codeline
    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 3

    One of the functions that differ is the isContainer() function
    which returns true in this example since our custom widget is
    intended to be used as a container.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 1

    Another function that differ is the function creating our custom widget:

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 2

    In addition to create and return the widget, we connect our custom
    container widget's currentIndexChanged() signal to the plugin's
    currentIndexChanged() slot to ensure that \QD's property editor is
    updated whenever the user views another page. We also connect the
    widget's pageTitleChanged() signal to the plugin's
    pageTitleChanged() slot.

    The currentIndexChanged() slot is called whenever our custom
    widget's currentIndexChanged() \e signal is emitted, i.e. whenever
    the user views another page:

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 8

    First, we retrieve the object emitting the signal using the
    QObject::sender() and qobject_cast() functions. If it's called in
    a slot activated by a signal, QObject::sender() returns a pointer
    to the object that sent the signal; otherwise it returns 0.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 9

    Once we have the widget we can update the property editor. \QD
    uses the QDesignerPropertySheetExtension class to feed its
    property editor, and whenever a widget is selected in its
    workspace, Qt Designer will query for the widget's property sheet
    extension and update the property editor.

    So what we want to achieve is to notify \QD that our widget's \e
    internal selection has changed: First we use the static
    QDesignerFormWindowInterface::findFormWindow() function to
    retrieve the QDesignerFormWindowInterface object containing the
    widget. The QDesignerFormWindowInterface class allows you to query
    and manipulate form windows appearing in Qt Designer's
    workspace. Then, all we have to do is to emit its \l
    {QDesignerFormWindowInterface::emitSelectionChanged()}{emitSelectionChanged()}
    signal, forcing an update of the property editor.

    When changing a page title a generic refresh of the property
    editor is not enough because it is actually the page's property
    extension that needs to be updated. For that reason we need to
    access the QDesignerPropertySheetExtension object for the page
    which title we want to change. The QDesignerPropertySheetExtension
    class also allows you to manipulate a widget's properties, but to
    get hold of the extension we must first retrieve access to \QD's
    extension manager:

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 10
    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 11

    Again we first retrieve the widget emitting the signal, using the
    QObject::sender() and qobject_cast() functions. Then we retrieve
    the current page from the widget that emitted the signal, and we
    use the static QDesignerFormWindowInterface::findFormWindow()
    function to retrieve the form containing our widget.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 12

    Now that we have the form window, the QDesignerFormWindowInterface
    class provides the \l
    {QDesignerFormWindowInterface::core()}{core()} function which
    returns the current QDesignerFormEditorInterface object. The
    QDesignerFormEditorInterface class allows you to access Qt
    Designer's various components. In particular, the
    QDesignerFormEditorInterface::extensionManager() function returns
    a reference to the current extension manager.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 13

    Once we have the extension manager we can update the extension
    sheet: First we retrieve the property extension for the page which
    title we want to change, using the qt_extension() function.  Then
    we retrieve the index for the page title using the
    QDesignerPropertySheetExtension::indexOf() function. As previously
    mentioned, we have chosen to use the QWidget::windowTitle property
    to store the page title (for more information see the
    MultiPageWidget class \l
    {designer/containerextension/multipagewidget.cpp}{implementation}).
    Finally, we implicitly force an update of the page's property
    sheet by calling the
    QDesignerPropertySheetExtension::setChanged() function.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 4

    Note also the initialize() function: The \c initialize() function
    takes a QDesignerFormEditorInterface object as argument.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 5

    When creating extensions associated with custom widget plugins, we
    need to access \QD's current extension manager which we retrieve
    from the QDesignerFormEditorInterface parameter.

    In addition to allowing you to manipulate a widget's properties,
    the QExtensionManager class provides extension management
    facilities for \QD. Using \QD's current extension manager you can
    retrieve the extension for a given object. You can also register
    and unregister an extension for a given object. Remember that an
    extension is an object which modifies the behavior of \QD.

    When registrering an extension, it is actually the associated
    extension factory that is registered. In \QD, extension factories
    are used to look up and create named extensions as they are
    required. So, in this example, the container extension itself is
    not created until \QD must know whether the associated widget is a
    container, or not.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 6

    We create a \c MultiPageWidgetExtensionFactory object that we
    register using \QD's current \l {QExtensionManager}{extension
    manager} retrieved from the QDesignerFormEditorInterface
    parameter. The first argument is the newly created factory and the
    second argument is an extension identifier which is a string. The
    \c Q_TYPEID() macro simply convert the string into a
    QLatin1String.

    The \c MultiPageWidgetExtensionFactory class is a subclass of
    QExtensionFactory. When \QD must know whether a widget is a
    container, or not, \QD's extension manager will run through all
    its registered factories invoking the first one which is able to
    create a container extension for that widget. This factory will in
    turn create a \c MultiPageWidgetExtension object.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 7

    Finally, take a look at the \c domXml() function. This function
    includes default settings for the widget in the standard XML
    format used by \QD. In this case, we specify the container's first
    page; any inital pages of a multi-page widget must be specified
    within this function.

    \snippet examples/designer/containerextension/multipagewidgetplugin.cpp 14

    Remember to use the Q_EXPORT_PLUGIN2() macro to export the
    MultiPageWidgetPlugin class for use with Qt's plugin handling
    classes: This macro ensures that \QD can access and construct the
    custom widget. Without this macro, there is no way for \QD to use
    the widget.

    \section1 MultiPageWidgetExtensionFactory Class Definition

    The \c MultiPageWidgetExtensionFactory class inherits QExtensionFactory
    which provides a standard extension factory for \QD.

    \snippet examples/designer/containerextension/multipagewidgetextensionfactory.h 0

    The subclass's purpose is to reimplement the
    QExtensionFactory::createExtension() function, making it able to
    create a \c MultiPageWidget container extension.


    \section1 MultiPageWidgetExtensionFactory Class Implementation

    The class constructor simply calls the QExtensionFactory base
    class constructor:

    \snippet examples/designer/containerextension/multipagewidgetextensionfactory.cpp 0

    As described above, the factory is invoked when \QD must know
    whether the associated widget is a container, or not.

    \snippet examples/designer/containerextension/multipagewidgetextensionfactory.cpp 1

    \QD's behavior is the same whether the requested extension is
    associated with a container, a member sheet, a property sheet or a
    task menu: Its extension manager runs through all its registered
    extension factories calling \c createExtension() for each until
    one responds by creating the requested extension.

    So the first thing we do in \c
    MultiPageWidgetExtensionFactory::createExtension() is to check if
    the QObject, for which the extension is requested, is in fact a \c
    MultiPageWidget object. Then we check if the requested extension
    is a container extension.

    If the object is a MultiPageWidget requesting a container
    extension, we create and return a \c MultiPageWidgetExtension
    object. Otherwise, we simply return a null pointer, allowing \QD's
    extension manager to continue its search through the registered
    factories.


    \section1 MultiPageWidgetContainerExtension Class Definition

    The \c MultiPageWidgetContainerExtension class inherits
    QDesignerContainerExtension which allows you to add (and delete)
    pages to a multi-page container plugin in \QD.

    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.h 0

    It is important to recognize that the QDesignerContainerExtension
    class only is intended to provide \QD access to your custom
    multi-page widget's functionality; your custom multi-page widget
    must implement functionality corresponding to the extension's
    functions.

    Note also that we implement a constructor that takes \e two
    arguments: the parent widget, and the \c MultiPageWidget object
    for which the task menu is requested.

    QDesignerContainerExtension provides a couple of menu entries in
    \QD's task menu by default, enabling the user to add or delete
    pages to the associated custom multi-page widget in \QD's
    workspace.

    \section1 MultiPageWidgetContainerExtension Class Implementation

    In the constructor we save the reference to the \c MultiPageWidget
    object sent as parameter, i.e the widget associated with the
    extension. We will need this later to access the custom multi-page
    widget performing the requested actions.

    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 0

    To fully enable \QD to manage and manipulate your custom
    multi-page widget, you must reimplement all the functions of
    QDesignerContainerExtension:

    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 1
    \codeline
    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 2
    \codeline
    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 3

    You must reimplement \l
    {QDesignerContainerExtension::addWidget()}{addWidget()} adding a
    given page to the container, \l
    {QDesignerContainerExtension::count()}{count()} returning the
    number of pages in the container, and \l
    {QDesignerContainerExtension::currentIndex()}{currentIndex()}
    returning the index of the currently selected page.

    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 4
    \codeline
    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 5
    \codeline
    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 6
    \codeline
    \snippet examples/designer/containerextension/multipagewidgetcontainerextension.cpp 7

    You must reimplement \l
    {QDesignerContainerExtension::insertWidget()}{insertWidget()}
    adding a given page to the container at a given index, \l
    {QDesignerContainerExtension::remove()}{remove()} deleting the
    page at a given index, \l
    {QDesignerContainerExtension::setCurrentIndex()}{setCurrentIndex()}
    setting the index of the currently selected page, and finally \l
    {QDesignerContainerExtension::widget()}{widget()} returning the
    page at a given index.

    \section1 MultiPageWidget Class Definition

    The MultiPageWidget class is a custom container widget that lets
    the user manipulate and populate its pages, and navigate among
    these using a combobox.

    \snippet examples/designer/containerextension/multipagewidget.h 0

    The main detail to observe is that your custom multi-page widget
    must implement functionality corresponding to the
    QDesignerContainerExtension's member functions since the
    QDesignerContainerExtension class only is intended to provide Qt
    Designer access to your custom multi-page widget's functionality.

    In addition, we declare the \c currentIndex and \c pageTitle
    properties, and their associated set and get functions. By
    declaring these attributes as properties, we allow \QD to manage
    them in the same way it manages the properties the MultiPageWidget
    widget inherits from QWidget and QObject, for example featuring
    the property editor.

    Note the \c STORED attribute in the declaration of the \c
    pageTitle property: The \c STORED attribute indicates persistence,
    i.e. it declares whether the property's value must be remembered
    when storing an object's state. As mentioned above, we have chosen
    to store the page title using the QWidget::windowTitle property to
    be able to give each page their own title. For that reason the \c
    pageTitle property is a "fake" property, provided for editing
    purposes, and doesn't need to be stored.

    We must also implement and emit the currentIndexChanged() and
    pageTitleChanged() signals to ensure that \QD's property editor is
    updated whenever the user views another page or changes one of the
    page titles.

    See the MultiPageWidget class \l
    {designer/containerextension/multipagewidget.cpp}{implementation}
    for more details.
*/