~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to doc/html/tools-plugandpaint.html

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="iso-8859-1"?>
 
2
<!DOCTYPE html
 
3
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
 
4
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 
5
<!-- /tmp/qt-4.0.0-espenr-1119621036935/qt-x11-opensource-desktop-4.0.0/doc/src/examples/plugandpaint.qdoc -->
 
6
<head>
 
7
    <title>Qt 4.0: Plug &amp; Paint Example</title>
 
8
    <style>h3.fn,span.fn { margin-left: 1cm; text-indent: -1cm; }
 
9
a:link { color: #004faf; text-decoration: none }
 
10
a:visited { color: #672967; text-decoration: none }
 
11
td.postheader { font-family: sans-serif }
 
12
tr.address { font-family: sans-serif }
 
13
body { background: #ffffff; color: black; }</style>
 
14
</head>
 
15
<body>
 
16
<table border="0" cellpadding="0" cellspacing="0" width="100%">
 
17
<tr>
 
18
<td align="left" valign="top" width="32"><img src="images/qt-logo.png" align="left" width="32" height="32" border="0" /></td>
 
19
<td width="1">&nbsp;&nbsp;</td><td class="postheader" valign="center"><a href="index.html"><font color="#004faf">Home</font></a>&nbsp;&middot; <a href="classes.html"><font color="#004faf">All&nbsp;Classes</font></a>&nbsp;&middot; <a href="mainclasses.html"><font color="#004faf">Main&nbsp;Classes</font></a>&nbsp;&middot; <a href="annotated.html"><font color="#004faf">Annotated</font></a>&nbsp;&middot; <a href="groups.html"><font color="#004faf">Grouped&nbsp;Classes</font></a>&nbsp;&middot; <a href="functions.html"><font color="#004faf">Functions</font></a></td>
 
20
<td align="right" valign="top" width="230"><img src="images/trolltech-logo.png" align="right" width="203" height="32" border="0" /></td></tr></table><h1 align="center">Plug &amp; Paint Example</h1>
 
21
<p>Files:</p>
 
22
<ul>
 
23
<li><a href="tools-plugandpaint-interfaces-h.html">tools/plugandpaint/interfaces.h</a></li>
 
24
<li><a href="tools-plugandpaint-mainwindow-cpp.html">tools/plugandpaint/mainwindow.cpp</a></li>
 
25
<li><a href="tools-plugandpaint-mainwindow-h.html">tools/plugandpaint/mainwindow.h</a></li>
 
26
<li><a href="tools-plugandpaint-paintarea-cpp.html">tools/plugandpaint/paintarea.cpp</a></li>
 
27
<li><a href="tools-plugandpaint-paintarea-h.html">tools/plugandpaint/paintarea.h</a></li>
 
28
<li><a href="tools-plugandpaint-plugindialog-cpp.html">tools/plugandpaint/plugindialog.cpp</a></li>
 
29
<li><a href="tools-plugandpaint-plugindialog-h.html">tools/plugandpaint/plugindialog.h</a></li>
 
30
<li><a href="tools-plugandpaint-main-cpp.html">tools/plugandpaint/main.cpp</a></li>
 
31
</ul>
 
32
<p>The Plug &amp; Paint example demonstrates how to write Qt applications that can be extended through plugins.</p>
 
33
<center><img src="images/plugandpaint.png" alt="Screenshot of the Plug &amp; Paint example" /></center><p>A plugin is a dynamic library that can be loaded at run-time to extend an application. Qt makes it possible to create custom plugins and to load them using <a href="qpluginloader.html">QPluginLoader</a>. The Plug &amp; Paint example uses plugins to support custom brushes, shapes, and image filters. A single plugin can provide multiple brushes, shapes, and/or filters.</p>
 
34
<p>If you want to learn how to make your own application extensible through plugins, we recommend that you start by reading this overview, which explains how to make an application use plugins. Afterward, you can read the <a href="tools-plugandpaintplugins-basictools.html">Basic Tools</a> and <a href="tools-plugandpaintplugins-extrafilters.html">Extra Filters</a> overviews, which show how to implement plugins.</p>
 
35
<p>Plug &amp; Paint consists of the following classes:</p>
 
36
<ul>
 
37
<li><tt>MainWindow</tt> is a <a href="qmainwindow.html">QMainWindow</a> subclass that provides the menu system and that contains a <tt>PaintArea</tt> as the central widget.</li>
 
38
<li><tt>PaintArea</tt> is a <a href="qwidget.html">QWidget</a> that allows the user to draw using a brush and to insert shapes.</li>
 
39
<li><tt>PluginDialog</tt> is a dialog that shows information about the plugins detected by the application.</li>
 
40
<li><tt>BrushInterface</tt>, <tt>ShapeInterface</tt>, and <tt>FilterInterface</tt> are abstract base classes that can be implemented by plugins to provide custom brushes, shapes, and image filters.</li>
 
41
</ul>
 
42
<a name="the-plugin-interfaces"></a>
 
43
<h2>The Plugin Interfaces</h2>
 
44
<p>We will start by reviewing the interfaces defined in <tt>interfaces.h</tt>. These interfaces are used by the Plug &amp; Paint application to access extra functionality. They are implemented in the plugins.</p>
 
45
<pre>&nbsp;   class BrushInterface
 
46
    {
 
47
    public:
 
48
        virtual ~BrushInterface() {}
 
49
 
 
50
        virtual QStringList brushes() const = 0;
 
51
        virtual QRect mousePress(const QString &amp;brush, QPainter &amp;painter,
 
52
                                 const QPoint &amp;pos) = 0;
 
53
        virtual QRect mouseMove(const QString &amp;brush, QPainter &amp;painter,
 
54
                                const QPoint &amp;oldPos, const QPoint &amp;newPos) = 0;
 
55
        virtual QRect mouseRelease(const QString &amp;brush, QPainter &amp;painter,
 
56
                                   const QPoint &amp;pos) = 0;
 
57
    };</pre>
 
58
<p>The <tt>BrushInterface</tt> class declares four pure virtual functions. The first pure virtual function, <tt>brushes()</tt>, returns a list of strings that identify the brushes provided by the plugin. By returning a <a href="qstringlist.html">QStringList</a> instead of a <a href="qstring.html">QString</a>, we make it possible for a single plugin to provide multiple brushes. The other functions have a <tt>brush</tt> parameter to identify which brush (among those returned by <tt>brushes()</tt>) is used.</p>
 
59
<p><tt>mousePress()</tt>, <tt>mouseMove()</tt>, and <tt>mouseRelease()</tt> take a <a href="qpainter.html">QPainter</a> and one or two <a href="qpoint.html">QPoint</a>s, and return a <a href="qrect.html">QRect</a> identifying which portion of the image was altered by the brush.</p>
 
60
<p>The class also has a virtual destructor. Interface classes usually don't need such a destructor (because it would make little sense to <tt>delete</tt> the object that implements the interface through a pointer to the interface), but some compilers emit a warning for classes that declare virtual functions but no virtual destructor. We provide the destructor to keep these compilers happy.</p>
 
61
<pre>&nbsp;   class ShapeInterface
 
62
    {
 
63
    public:
 
64
        virtual ~ShapeInterface() {}
 
65
 
 
66
        virtual QStringList shapes() const = 0;
 
67
        virtual QPainterPath generateShape(const QString &amp;shape,
 
68
                                           QWidget *parent) = 0;
 
69
    };</pre>
 
70
<p>The <tt>ShapeInterface</tt> class declares a <tt>shapes()</tt> function that works the same as <tt>BrushInterface</tt>'s <tt>brushes()</tt> function, and a <tt>generateShape()</tt> function that has a <tt>shape</tt> parameter. Shapes are represented by a <a href="qpainterpath.html">QPainterPath</a>, a data type that can represent arbitrary 2D shapes or combination of shapes. The <tt>parent</tt> parameter can be used by the plugin to pop up a dialog asking the user to specify more information.</p>
 
71
<pre>&nbsp;   class FilterInterface
 
72
    {
 
73
    public:
 
74
        virtual ~FilterInterface() {}
 
75
 
 
76
        virtual QStringList filters() const = 0;
 
77
        virtual QImage filterImage(const QString &amp;filter, const QImage &amp;image,
 
78
                                   QWidget *parent) = 0;
 
79
    };</pre>
 
80
<p>The <tt>FilterInterface</tt> class declares a <tt>filters()</tt> function that returns a list of filter names, and a <tt>filterImage()</tt> function that applies a filter to an image.</p>
 
81
<pre>&nbsp;   Q_DECLARE_INTERFACE(BrushInterface,
 
82
                        &quot;com.trolltech.PlugAndPaint.BrushInterface/1.0&quot;)
 
83
    Q_DECLARE_INTERFACE(ShapeInterface,
 
84
                        &quot;com.trolltech.PlugAndPaint.ShapeInterface/1.0&quot;)
 
85
    Q_DECLARE_INTERFACE(FilterInterface,</pre>
 
86
<p>To make it possible to query at run-time whether a plugin implements a given interface, we must use the <tt>Q_DECLARE_INTERFACE()</tt> macro. The first argument is the name of the interface. The second argument is a string identifying the interface in a unique way. By convention, we use a &quot;Java package name&quot; syntax to identify interfaces. If we later change the interfaces, we must use a different string to identify the new interface; otherwise, the application might crash. It is therefore a good idea to include a version number in the string, as we did above.</p>
 
87
<p>The <a href="tools-plugandpaintplugins-basictools.html">Basic Tools</a> plugin and the <a href="tools-plugandpaintplugins-extrafilters.html">Extra Filters</a> plugin shows how to derive from <tt>BrushInterface</tt>, <tt>ShapeInterface</tt>, and <tt>FilterInterface</tt>.</p>
 
88
<p>A note on naming: It might have been tempting to give the <tt>brushes()</tt>, <tt>shapes()</tt>, and <tt>filters()</tt> functions a more generic name, such as <tt>keys()</tt> or <tt>features()</tt>. However, that would have made multiple inheritance impracticable. When creating interfaces, we should always try to give unique names to the pure virtual functions.</p>
 
89
<a name="the-mainwindow-class"></a>
 
90
<h2>The MainWindow Class</h2>
 
91
<p>The <tt>MainWindow</tt> class is a standard <a href="qmainwindow.html">QMainWindow</a> subclass, as found in many of the other examples (notably <a href="mainwindows-application.html">Application</a>). Here, we'll concentrate on the parts of the code that are related to plugins.</p>
 
92
<pre>&nbsp;   void MainWindow::loadPlugins()
 
93
    {
 
94
        pluginsDir = QDir(qApp-&gt;applicationDirPath());
 
95
    #if defined(Q_OS_WIN)
 
96
        if (pluginsDir.dirName() == &quot;debug&quot; || pluginsDir.dirName() == &quot;release&quot;)
 
97
            pluginsDir.cdUp();
 
98
    #elif defined(Q_OS_MAC)
 
99
        if (pluginsDir.dirName() == &quot;MacOS&quot;) {
 
100
            pluginsDir.cdUp();
 
101
            pluginsDir.cdUp();
 
102
            pluginsDir.cdUp();
 
103
        }
 
104
    #endif
 
105
        pluginsDir.cd(&quot;plugins&quot;);</pre>
 
106
<p>The <tt>loadPlugins()</tt> function is called from the <tt>MainWindow</tt> constructor to detect plugins and update the <b>Brush</b>, <b>Shapes</b>, and <b>Filters</b> menus. We start by initializing the <tt>pluginsDir</tt> member variable to refer to the <tt>plugins</tt> directory of the Plug &amp; Paint example. On Unix, this is just a matter of initializing the <a href="qdir.html">QDir</a> variable with <a href="qcoreapplication.html#applicationDirPath">QApplication::applicationDirPath</a>(), the path of the executable file, and to do a <a href="qdir.html#cd">cd()</a>. On Windows and Mac OS X, this file is usually located in a subdirectory, so we need to take this into account.</p>
 
107
<pre>&nbsp;       foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
 
108
            QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
 
109
            QObject *plugin = loader.instance();
 
110
            if (plugin) {
 
111
                BrushInterface *iBrush = qobject_cast&lt;BrushInterface *&gt;(plugin);
 
112
                if (iBrush)
 
113
                    addToMenu(plugin, iBrush-&gt;brushes(), brushMenu,
 
114
                              SLOT(changeBrush()), brushActionGroup);
 
115
 
 
116
                ShapeInterface *iShape = qobject_cast&lt;ShapeInterface *&gt;(plugin);
 
117
                if (iShape)
 
118
                    addToMenu(plugin, iShape-&gt;shapes(), shapesMenu,
 
119
                              SLOT(insertShape()));
 
120
 
 
121
                FilterInterface *iFilter = qobject_cast&lt;FilterInterface *&gt;(plugin);
 
122
                if (iFilter)
 
123
                    addToMenu(plugin, iFilter-&gt;filters(), filterMenu,
 
124
                              SLOT(applyFilter()));
 
125
 
 
126
                pluginFileNames += fileName;
 
127
            }
 
128
        }</pre>
 
129
<p>We use <a href="qdir.html#entryList">QDir::entryList</a>() to get a list of all files in that directory. Then we iterate over the result using <a href="containers.html#foreach">foreach</a> and try to load the plugin using <a href="qpluginloader.html">QPluginLoader</a>.</p>
 
130
<p>To the application that loads the plugin, a Qt plugin is simply a <a href="qobject.html">QObject</a>. That <a href="qobject.html">QObject</a> may implement extra interfaces using multiple inheritance. The object is accessible through <a href="qpluginloader.html#instance">QPluginLoader::instance</a>(). If the dynamic library isn't a Qt plugin, or if it was compiled against an incompatible version of the Qt library, <a href="qpluginloader.html#instance">QPluginLoader::instance</a>() returns a null pointer.</p>
 
131
<p>If <a href="qpluginloader.html#instance">QPluginLoader::instance</a>() is non-null, we check which interfaces it implements using <a href="qobject.html#qobject_cast">qobject_cast</a>(). First, we try to cast the plugin instance to a <tt>BrushInterface</tt>; if it works, we call the private function <tt>addToMenu()</tt> with the list of brushes returned by <tt>brushes()</tt>. Then we do the same with the <tt>ShapeInterface</tt> and the <tt>FilterInterface</tt>.</p>
 
132
<pre>&nbsp;       brushMenu-&gt;setEnabled(!brushActionGroup-&gt;actions().isEmpty());
 
133
        shapesMenu-&gt;setEnabled(!shapesMenu-&gt;actions().isEmpty());
 
134
        filterMenu-&gt;setEnabled(!filterMenu-&gt;actions().isEmpty());
 
135
    }</pre>
 
136
<p>At the end, we enable or disable the <b>Brush</b>, <b>Shapes</b>, and <b>Filters</b> menus based on whether they contain any items.</p>
 
137
<pre>&nbsp;   void MainWindow::aboutPlugins()
 
138
    {
 
139
        PluginDialog dialog(pluginsDir.path(), pluginFileNames, this);
 
140
        dialog.exec();
 
141
    }</pre>
 
142
<p>The <tt>aboutPlugins()</tt> slot is called on startup and can be invoked at any time through the <b>About Plugins</b> action. It pops up a <tt>PluginDialog</tt>, providing information about the loaded plugins.</p>
 
143
<center><img src="images/plugandpaint-plugindialog.png" alt="Screenshot of the Plugin dialog" /></center><p>The <tt>addToMenu()</tt> function is called from <tt>loadPlugin()</tt> to create <a href="qaction.html">QAction</a>s for custom brushes, shapes, or filters and add them to the relevant menu. The <a href="qaction.html">QAction</a> is created with the plugin from which it comes from as the parent; this is a trick to get access to the plugin later.</p>
 
144
<pre>&nbsp;   void MainWindow::changeBrush()
 
145
    {
 
146
        QAction *action = qobject_cast&lt;QAction *&gt;(sender());
 
147
        BrushInterface *iBrush = qobject_cast&lt;BrushInterface *&gt;(action-&gt;parent());
 
148
        QString brush = action-&gt;text();
 
149
 
 
150
        paintArea-&gt;setBrush(iBrush, brush);
 
151
    }</pre>
 
152
<p>The <tt>changeBrush()</tt> slot is invoked when the user chooses one of the brushes from the <b>Brush</b> menu. We start by finding out which action invoked the slot using <a href="qobject.html#sender">QObject::sender</a>(). Then we get the <tt>BrushInterface</tt> out of the plugin (which we conveniently passed as the <a href="qaction.html">QAction</a>'s parent) and we call <tt>PaintArea::setBrush()</tt> with the <tt>BrushInterface</tt> and the string identifying the brush. Next time the user draws on the paint area, <tt>PaintArea</tt> will use this brush.</p>
 
153
<pre>&nbsp;   void MainWindow::insertShape()
 
154
    {
 
155
        QAction *action = qobject_cast&lt;QAction *&gt;(sender());
 
156
        ShapeInterface *iShape = qobject_cast&lt;ShapeInterface *&gt;(action-&gt;parent());
 
157
 
 
158
        QPainterPath path = iShape-&gt;generateShape(action-&gt;text(), this);
 
159
        if (!path.isEmpty())
 
160
            paintArea-&gt;insertShape(path);
 
161
    }</pre>
 
162
<p>The <tt>insertShape()</tt> is invoked when the use chooses one of the shapes from the <b>Shapes</b> menu. We retrieve the <a href="qaction.html">QAction</a> that invoked the slot, then the <tt>ShapeInterface</tt> associated with that <a href="qaction.html">QAction</a>, and finally we call <tt>ShapeInterface::generateShape()</tt> to obtain a <a href="qpainterpath.html">QPainterPath</a>.</p>
 
163
<pre>&nbsp;   void MainWindow::applyFilter()
 
164
    {
 
165
        QAction *action = qobject_cast&lt;QAction *&gt;(sender());
 
166
        FilterInterface *iFilter =
 
167
                qobject_cast&lt;FilterInterface *&gt;(action-&gt;parent());
 
168
 
 
169
        QImage image = iFilter-&gt;filterImage(action-&gt;text(), paintArea-&gt;image(),
 
170
                                            this);
 
171
        paintArea-&gt;setImage(image);
 
172
    }</pre>
 
173
<p>The <tt>applyFilter()</tt> slot is similar: We retrieve the <a href="qaction.html">QAction</a> that invoked the slot, then the <tt>FilterInterface</tt> associated to that <a href="qaction.html">QAction</a>, and finally we call <tt>FilterInterface::filterImage()</tt> to apply the filter onto the current image.</p>
 
174
<a name="the-paintarea-class"></a>
 
175
<h2>The PaintArea Class</h2>
 
176
<p>The <tt>PaintArea</tt> class contains some code that deals with <tt>BrushInterface</tt>, so we'll review it briefly.</p>
 
177
<pre>&nbsp;   void PaintArea::setBrush(BrushInterface *brushInterface, const QString &amp;brush)
 
178
    {
 
179
        this-&gt;brushInterface = brushInterface;
 
180
        this-&gt;brush = brush;
 
181
    }</pre>
 
182
<p>In <tt>setBrush()</tt>, we simply store the <tt>BrushInterface</tt> and the brush that are given to us by <tt>MainWindow</tt>.</p>
 
183
<pre>&nbsp;   void PaintArea::mouseMoveEvent(QMouseEvent *event)
 
184
    {
 
185
        if ((event-&gt;buttons() &amp; Qt::LeftButton) &amp;&amp; lastPos != QPoint(-1, -1)) {
 
186
            if (brushInterface) {
 
187
                QPainter painter(&amp;theImage);
 
188
                setupPainter(painter);
 
189
                QRect rect = brushInterface-&gt;mouseMove(brush, painter, lastPos,
 
190
                                                       event-&gt;pos());
 
191
                update(rect);
 
192
            }
 
193
 
 
194
            lastPos = event-&gt;pos();
 
195
        }
 
196
    }</pre>
 
197
<p>In the <a href="qwidget.html#mouseMoveEvent">mouse move event handler</a>, we call the <tt>BrushInterface::mouseMove()</tt> function on the current <tt>BrushInterface</tt>, with the current brush. The mouse press and mouse release handlers are very similar.</p>
 
198
<a name="the-plugindialog-class"></a>
 
199
<h2>The PluginDialog Class</h2>
 
200
<p>The <tt>PluginDialog</tt> class provides information about the loaded plugins to the user. Its constructor takes a path to the plugins and a list of plugin file names. It calls <tt>populateTreeWidget()</tt> to fill the QTreeWdiget with information about the plugins:</p>
 
201
<pre>&nbsp;   void PluginDialog::populateTreeWidget(const QString &amp;path,
 
202
                                          const QStringList &amp;fileNames)
 
203
    {
 
204
        if (fileNames.isEmpty()) {
 
205
            label-&gt;setText(tr(&quot;Plug &amp; Paint couldn't find any plugins in the %1 &quot;
 
206
                              &quot;directory.&quot;)
 
207
                           .arg(QDir::convertSeparators(path)));
 
208
            treeWidget-&gt;hide();
 
209
        } else {
 
210
            label-&gt;setText(tr(&quot;Plug &amp; Paint found the following plugins in the %1 &quot;
 
211
                              &quot;directory:&quot;)
 
212
                           .arg(QDir::convertSeparators(path)));
 
213
 
 
214
            QDir dir(path);
 
215
 
 
216
            foreach (QString fileName, fileNames) {
 
217
                QPluginLoader loader(dir.absoluteFilePath(fileName));
 
218
                QObject *plugin = loader.instance();
 
219
 
 
220
                QTreeWidgetItem *pluginItem = new QTreeWidgetItem(treeWidget);
 
221
                pluginItem-&gt;setText(0, fileName);
 
222
                treeWidget-&gt;setItemExpanded(pluginItem, true);
 
223
 
 
224
                QFont boldFont = pluginItem-&gt;font(0);
 
225
                boldFont.setBold(true);
 
226
                pluginItem-&gt;setFont(0, boldFont);
 
227
 
 
228
                if (plugin) {
 
229
                    BrushInterface *iBrush = qobject_cast&lt;BrushInterface *&gt;(plugin);
 
230
                    if (iBrush)
 
231
                        addItems(pluginItem, &quot;BrushInterface&quot;, iBrush-&gt;brushes());
 
232
 
 
233
                    ShapeInterface *iShape = qobject_cast&lt;ShapeInterface *&gt;(plugin);
 
234
                    if (iShape)
 
235
                        addItems(pluginItem, &quot;ShapeInterface&quot;, iShape-&gt;shapes());
 
236
 
 
237
                    FilterInterface *iFilter =
 
238
                            qobject_cast&lt;FilterInterface *&gt;(plugin);
 
239
                    if (iFilter)
 
240
                        addItems(pluginItem, &quot;FilterInterface&quot;, iFilter-&gt;filters());
 
241
                }
 
242
            }
 
243
        }
 
244
    }</pre>
 
245
<p>The <tt>populateTreeWidget()</tt> is very similar to <tt>MainWindow::loadPlugins()</tt>. It uses <a href="qpluginloader.html">QPluginLoader</a> to load the plugins and uses <a href="qobject.html#qobject_cast">qobject_cast</a>() to find out which interfaces are implemented by the plugins.</p>
 
246
<p>This completes our review of the Plug &amp; Paint application. At this point, you might want to take a look at the <a href="tools-plugandpaintplugins-basictools.html">Basic Tools</a> example plugin.</p>
 
247
<p /><address><hr /><div align="center">
 
248
<table width="100%" cellspacing="0" border="0"><tr class="address">
 
249
<td width="30%">Copyright &copy; 2005 <a href="trolltech.html">Trolltech</a></td>
 
250
<td width="40%" align="center"><a href="trademarks.html">Trademarks</a></td>
 
251
<td width="30%" align="right"><div align="right">Qt 4.0.0</div></td>
 
252
</tr></table></div></address></body>
 
253
</html>