- Extending Qt with Plugins
- Making Applications Plugin-Aware
- Writing Application Plugins
Writing Application Plugins
An application plugin is a subclass of QObject and of the interfaces it wants to provide. The examples that accompany this book includes two plugins for the Text Art application presented in the preceding section, to show that the application correctly handles multiple plugins.
Here, we will review the code for only one of them, the Basic Effects plugin. We will assume that the plugin's source code is located in a directory called basiceffectsplugin and that the Text Art application is located in a parallel directory called textart. Here's the declaration of the plugin class:
class BasicEffectsPlugin : public QObject, public TextArtInterface { Q_OBJECT Q_INTERFACES(TextArtInterface) public: QStringList effects() const; QPixmap applyEffect(const QString &effect, const QString &text, const QFont &font, const QSize &size, const QPen &pen, const QBrush &brush); };
The plugin implements only one interface, TextArtInterface. In addition to Q_OBJECT, we must use the Q_INTERFACES() macro for each interface that is subclassed to ensure smooth cooperation between moc and qobject_cast<T>().
QStringList BasicEffectsPlugin::effects() const { return QStringList() << "Plain" << "Outline" << "Shadow"; }
The effects() function returns a list of text effects supported by the plugin. This plugin supports three effects, so we just return a list containing the name of each one.
The applyEffect() function provides the plugin's functionality and is slightly involved, so we will review it in pieces.
QPixmap BasicEffectsPlugin::applyEffect(const QString &effect, const QString &text, const QFont &font, const QSize &size, const QPen &pen, const QBrush &brush) { QFont myFont = font; QFontMetrics metrics(myFont); while ((metrics.width(text) > size.width() || metrics.height() > size.height()) && myFont.pointSize() > 9) { myFont.setPointSize(myFont.pointSize() - 1); metrics = QFontMetrics(myFont); }
We want to ensure that the given text will fit in the specified size, if possible. For this reason, we use the font's metrics to see whether the text is too large to fit, and if it is, we enter a loop where we reduce the point size until we find a size that will fit, or until we reach 9 points, our fixed minimum size.
QPixmap pixmap(size); QPainter painter(&pixmap); painter.setFont(myFont); painter.setPen(pen); painter.setBrush(brush); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::TextAntialiasing, true); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); painter.eraseRect(pixmap.rect());
We create a pixmap of the required size and a painter to paint onto the pixmap. We also set some render hints to ensure the smoothest possible results. The call to eraseRect() clears the pixmap with the background color.
if (effect == "Plain") { painter.setPen(Qt::NoPen); } else if (effect == "Outline") { QPen pen(Qt::black); pen.setWidthF(2.5); painter.setPen(pen); } else if (effect == "Shadow") { QPainterPath path; painter.setBrush(Qt::darkGray); path.addText(((size.width() - metrics.width(text)) / 2) + 3, (size.height() - metrics.descent()) + 3, myFont, text); painter.drawPath(path); painter.setBrush(brush); }
For the "Plain" effect, no outline is required. For the "Outline" effect, we ignore the original pen and create our own black pen with a 2.5-pixel width. For the "Shadow" effect, we need to draw the shadow first so that the text can be painted on top of it.
QPainterPath path; path.addText((size.width() - metrics.width(text)) / 2, size.height() - metrics.descent(), myFont, text); painter.drawPath(path); return pixmap; }
We now have the pen and brushes set appropriately for each text effect, and in the "Shadow" effect case we have drawn the shadow. We are now ready to render the text. The text is horizontally centered and drawn far enough above the bottom of the pixmap to allow room for descenders.
Q_EXPORT_PLUGIN2(basiceffectsplugin, BasicEffectsPlugin)
At the end of the .cpp file, we use the Q_EXPORT_PLUGIN2() macro to make the plugin available to Qt.
The .pro file is similar to the one we used for the Bronze style plugin earlier in this chapter (p. 493):
TEMPLATE = lib CONFIG += plugin HEADERS = ../textart/textartinterface.h basiceffectsplugin.h SOURCES = basiceffectsplugin.cpp DESTDIR = ../textart/plugins
If this chapter has whetted your appetite for application plugins, you might like to study the more advanced Plug & Paint example provided with Qt. The application supports three different interfaces and includes a useful Plugin Information dialog that lists the plugins and interfaces that are available to the application.