一、Qt插件机制
1、Qt插件简介
插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。
2、Qt插件API
Qt提供了两种API用于创建插件:一种是高阶API,用于扩展Qt本身的功能,如自定义数据库驱动,图像格式,文本编码,自定义样式等;一种是低阶API,用于扩展Qt应用程序。
二、高级 API:编写 Qt 扩展
编写一个扩展 Qt 本身的插件是通过继承适当的插件基类、实现一些功能并添加一个宏来实现的。
有几个插件基类。派生插件默认存储在标准插件目录的子目录中。如果插件未存储在适当的目录中,Qt 将找不到它们。
下表总结了插件基类。一些类是私有的,因此没有记录。您可以使用它们,但没有与更高版本的 Qt 兼容的承诺。
基类 | 目录名称 | Qt 模块 | 关键案例敏感性 | 功能 |
---|---|---|---|---|
QAccessibleBridge插件 | accessiblebridge | 图形用户界面 | 区分大小写 | |
QImageIO插件 | imageformats | 图形用户界面 | 区分大小写 | 加载图片的插件 |
QPictureFormatPlugin (已过时) | pictureformats | 图形用户界面 | 区分大小写 | |
QAudioSystem插件 | audio | Qt多媒体 | 不区分大小写 | |
QDeclarativeVideoBackendFactoryInterface | video/declarativevideobackend | Qt多媒体 | 不区分大小写 | |
QGstBufferPool 插件 | video/bufferpool | Qt多媒体 | 不区分大小写 | |
QMediaPlaylistIO插件 | playlistformats | Qt多媒体 | 不区分大小写 | |
QMediaResourcePolicyPlugin | resourcepolicy | Qt多媒体 | 不区分大小写 | |
QMediaServiceProvider插件 | mediaservice | Qt多媒体 | 不区分大小写 | |
QSGVideoNodeFactory插件 | video/videonode | Qt多媒体 | 不区分大小写 | |
QBearerEngine插件 | bearer | Qt网络 | 区分大小写 | |
QPlatformInputContextPlugin | platforminputcontexts | Qt平台抽象 | 不区分大小写 | |
QPlatformIntegrationPlugin | platforms | Qt平台抽象 | 不区分大小写 | |
QPlatform主题插件 | platformthemes | Qt平台抽象 | 不区分大小写 | |
QGeoPositionInfoSourceFactory | position | Qt定位 | 区分大小写 | |
QPlatformPrinterSupportPlugin | printsupport | Qt 打印支持 | 不区分大小写 | |
QSG上下文插件 | scenegraph | Qt快速 | 区分大小写 | |
QScript 扩展插件 | script | Qt脚本 | 区分大小写 | |
QSensorGesturePluginInterface | sensorgestures | Qt 传感器 | 区分大小写 | |
QSensor插件接口 | sensors | Qt 传感器 | 区分大小写 | |
QSqlDriver插件 | sqldrivers | Qt SQL | 区分大小写 | |
QIconEngine插件 | iconengines | Qt SVG | 不区分大小写 | |
QAccessible插件 | accessible | Qt 小部件 | 区分大小写 | |
QStyle插件 | styles | Qt 小部件 | 不区分大小写 | z自定义样式 |
2、开发一个读取csv的图片插件
Qt 图像 I/O API 提供了一个高级 API,用于读取和写入 Qt 支持的流行格式的图像,包括 JPEG、PNG 和 BMP。 但是,如果您的应用程序需要以 Qt 当前不支持的格式读取或写入图像,就只能自己开发插件?
这些插件保存在
E:\Qt\Qt5.14.0\5.14.0\msvc2017\plugins\imageformats
QImage 函数依赖于类 QImageReader 和 QImageWriter 分别提供图像读取和写入。这两个类的主要工作是将实际的图像 I/O 委托给适当的图像 I/O 处理程序
图像 I/O 处理程序封装了特定图像格式的低级图像 I/O 的细节。处理程序是接口类 QImageIOHandler 的子类,并实现了 QImageReader 和 QImageWriter 将用于将图像 I/O 委托给处理程序的虚函数
Qt 图像 I/O 插件使 QImageIOHandler 在运行时可用于 Qt。该插件是 QImageIOPlugin 的子类,提供两个基本服务。首先,可以查询插件以确定它是否具有可以执行请求的 I/O 的图像 I/O 处理程序。其次,插件充当图像 I/O 处理程序本身的工厂,允许 Qt 获取处理程序的实例
构造一个 QImage,将文件名交给 QImage 构造函数。作为响应, QImage 指示 QImageReader 读取图像。 QImageReader 的主要构造函数以 QIODevice 作为参数,这意味着可以使用 QIODevice 的任何子类,从 QFile 到 QTcpSocket,甚至自定义设备。 (在上图所示的情况下,将根据给定的文件名构造一个 QFile。)
可选地,可以将格式字符串传递给 QImage 构造函数;像文件名一样,这个字符串被传递给 QImageReader。在没有格式字符串的情况下,图像 I/O 插件应该“窥视”给定 QIODevice 的内容(例如检查图像标题)并自动检测格式。
QImageReader 依次查询各个插件的读取能力。如果任何插件都不能提供合适的处理程序,则阅读器将退回到默认处理程序,如果给定格式存在一个(图中未显示)。最后,QImageReader 在已建立的 I/O 处理程序上调用 read(),它执行真正的图像读取。
定义图片格式
CSVImage\n
width,height\n
颜色值,颜色值...........颜色值,颜色值
........................................................
........................................................
颜色值,颜色值...........颜色值,颜色值
自定义图片读写类
class CSVImageIOHandler : public QImageIOHandler
{
public:
//从io中读取数据到Qimage中
virtual bool read(QImage *image);
//将Qimage写到io中
virtual bool write(const QImage &image);
//判断该处理对象是否能读io
virtual bool canRead() const;
static bool canRead(QIODevice *device);
};
bool CSVImageIOHandler::read(QImage *image)
{
QTextStream stream(device());
QString line = stream.readLine();
if (line != "CSVImage" || stream.status() != QTextStream::Ok)
{
return false;
}
line = stream.readLine();
QStringList list = line.split(",");
if (list.size() != 2)
{
return false;
}
int width = list[0].toInt();
int height = list[1].toInt();
QImage result(width, height, QImage::Format_ARGB32);
for (int y = 0; y < height; ++y)
{
line = stream.readLine();
list = line.split(",");
for (int x = 0; x < width; ++x)
{
if (x < list.size())
{
result.setPixel(x, y, list[x].toUInt());
}
}
}
if (stream.status() != QTextStream::Ok)
{
return false;
}
*image = result;
return true;
}
bool CSVImageIOHandler::write(const QImage & image)
{
QTextStream stream(device());
stream << "CSVImage\n";
stream << image.width() << "," << image.height() << "\n";
for (int y = 0; y < image.height(); ++y)
{
for (int x = 0; x < image.width(); ++x)
{
QRgb rgb = image.pixel(x, y);
stream << rgb << ",";
}
stream << "\n";
}
if (stream.status() != QTextStream::Ok)
{
return false;
}
return true;
}
bool CSVImageIOHandler::canRead() const
{
return canRead(device());
}
bool CSVImageIOHandler::canRead(QIODevice * device)
{
if (device->peek(8) == "CSVImage")
{
return true;
}
return false;
}
实现插件子类
class CSVImageIOPlugin : public QImageIOPlugin
{
Q_OBJECT
//iid不能错
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "CSVImageIOPlugin.json")
public:
//判断该插件对该格式的处理能力
virtual Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
//创建 QImageIOHandler 处理对象
virtual QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
};
QImageIOPlugin::Capabilities CSVImageIOPlugin::capabilities(QIODevice * device, const QByteArray & format) const
{
qDebug() << "CSVImageIOPlugin::capabilities";
if (format == "csv")
{
return Capabilities(CanRead | CanWrite);
}
if (!format.isEmpty())
{
return 0;
}
if (!device->isOpen())
{
return 0;
}
QImageIOPlugin::Capabilities result;
if (device->isReadable() && CSVImageIOHandler::canRead(device))
{
result |= QImageIOPlugin::CanRead;
}
if (device->isWritable()) {
result |= QImageIOPlugin::CanWrite;
}
return result;
}
QImageIOHandler * CSVImageIOPlugin::create(QIODevice * device, const QByteArray & format) const
{
qDebug() << "CSVImageIOPlugin::create";
QImageIOHandler* pHandler = new CSVImageIOHandler;
pHandler->setDevice(device);
pHandler->setFormat(format);
return pHandler;
}
二、低级 API:编写 自己程序的 扩展
1、通过插件扩展应用程序功能
A、定义一个接口集(只有纯虚函数的类),用来与插件交流。
B、用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。
C、应用程序中用QPluginLoader来加载插件。
D、用宏qobject_cast()来判断一个插件是否实现了接口。
2、创建插件
创建一个插件的步骤如下:
A、声明插件类,插件类继承自QObject和插件实现的接口。
B、用宏Q_INTERFACES()将插件接口告诉Qt元对象系统。
C、用宏Q_EXPORT_PLUGIN2()导出插件类。
D、用适当的.pro文件构建插件。
在加载插件前, QCoreApplication对象必须被初始化
3、为记事本开发插件
定义插件接口
#include <QString>
#include <QPlainTextEdit>
#include <QMenu>
// 定义接口
class CPluginInterface
{
public:
//获取插件名称
virtual QString name() = 0;
//插件菜单
virtual QMenu* getMenu() = 0;
//初始化插件
virtual void initPlugin(QPlainTextEdit* pTextEdit) = 0;
//反初始化插件
virtual void unInitPlugin(QPlainTextEdit* pTextEdit) = 0;
};
QT_BEGIN_NAMESPACE
// Q_DECLARE_INTERFACE(接口类名, 接口标识符)
// 声明接口,Q_DECLARE_INTERFACE 宏告诉Qt 这个纯虚类是一个插件接口类
Q_DECLARE_INTERFACE(CPluginInterface, "Notepad.Plugin.CPluginInterface");
QT_END_NAMESPACE
实现插件接口
class CMyPluginInterface : public QObject, public CPluginInterface
{
Q_OBJECT
Q_INTERFACES(CPluginInterface)
Q_PLUGIN_METADATA(IID "Notepad.Plugin.CMyPluginInterface" FILE "MyPlugin.json")
public:
CMyPluginInterface();
virtual QString name();
virtual QMenu* getMenu();
virtual void initPlugin(QPlainTextEdit* pTextEdit);
virtual void unInitPlugin(QPlainTextEdit* pTextEdit);
public slots:
//ascii转十六进制
void SlotAsciiToHex();
//将记事本的内容json格式化
void SlotFormatJson();
private:
QPlainTextEdit* m_pTextEdit;
};
#include <QJsonParseError>
#include <QJsonDocument>
#include <QMessageBox>
CMyPluginInterface::CMyPluginInterface()
{
m_pTextEdit = nullptr;
}
QString CMyPluginInterface::name()
{
return QString(u8"我的插件");
}
QMenu * CMyPluginInterface::getMenu()
{
QMenu* pMenu = new QMenu(QString(u8"我的插件"));
QAction* pAction = new QAction(QString(u8"Ascii -> Hex"));
connect(pAction, &QAction::triggered, this, &CMyPluginInterface::SlotAsciiToHex);
pMenu->addAction(pAction);
pAction = new QAction(QString(u8"Json格式化"));
connect(pAction, &QAction::triggered, this, &CMyPluginInterface::SlotFormatJson);
pMenu->addAction(pAction);
return pMenu;
}
void CMyPluginInterface::initPlugin(QPlainTextEdit* pTextEdit)
{
m_pTextEdit = pTextEdit;
}
void CMyPluginInterface::unInitPlugin(QPlainTextEdit* pTextEdit)
{
}
void CMyPluginInterface::SlotAsciiToHex()
{
if (m_pTextEdit == nullptr)
{
return;
}
QString str = m_pTextEdit->textCursor().selectedText();
m_pTextEdit->textCursor().clearSelection();
m_pTextEdit->insertPlainText(str.toUtf8().toHex());
}
void CMyPluginInterface::SlotFormatJson()
{
if (m_pTextEdit == nullptr)
{
return;
}
QJsonParseError jsonError;
QJsonDocument jsonDocument(QJsonDocument::fromJson(m_pTextEdit->toPlainText().toUtf8(), &jsonError));
if (jsonError.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr, QString(u8"提示"), QString(u8"格式化json失败"));
return;
}
m_pTextEdit->clear();
m_pTextEdit->setPlainText(jsonDocument.toJson(QJsonDocument::JsonFormat::Indented));
}
记事本程序
class CMainWindow : public QMainWindow
{
Q_OBJECT
public:
CMainWindow(QWidget *parent = Q_NULLPTR);
private:
void loadPlugin();
private:
Ui::CMainWindowClass ui;
};
CMainWindow::CMainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
loadPlugin();
}
void CMainWindow::loadPlugin()
{
//获取当前应用程序所在路径
QDir pluginsDir(qApp->applicationDirPath());
//切换到插件目录
pluginsDir.cd("plugins");
//遍历plugins目录下所有文件
foreach(QString fileName, pluginsDir.entryList(QDir::Files))
{
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *pPlugin = pluginLoader.instance();
if (pPlugin)
{
//插件名称
QString pluginName = pPlugin->metaObject()->className();
CPluginInterface* pMyPlugin = qobject_cast<CPluginInterface*>(pPlugin);
if (pMyPlugin)
{
pMyPlugin->initPlugin(ui.plainTextEdit);
ui.menuPlugin->addMenu(pMyPlugin->getMenu());
}
}
}
}