Qt插件开发

一、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插件audioQt多媒体不区分大小写
QDeclarativeVideoBackendFactoryInterfacevideo/declarativevideobackendQt多媒体不区分大小写
QGstBufferPool 插件video/bufferpoolQt多媒体不区分大小写
QMediaPlaylistIO插件playlistformatsQt多媒体不区分大小写
QMediaResourcePolicyPluginresourcepolicyQt多媒体不区分大小写
QMediaServiceProvider插件mediaserviceQt多媒体不区分大小写
QSGVideoNodeFactory插件video/videonodeQt多媒体不区分大小写
QBearerEngine插件bearerQt网络区分大小写
QPlatformInputContextPluginplatforminputcontextsQt平台抽象不区分大小写
QPlatformIntegrationPluginplatformsQt平台抽象不区分大小写
QPlatform主题插件platformthemesQt平台抽象不区分大小写
QGeoPositionInfoSourceFactorypositionQt定位区分大小写
QPlatformPrinterSupportPluginprintsupportQt 打印支持不区分大小写
QSG上下文插件scenegraphQt快速区分大小写
QScript 扩展插件scriptQt脚本区分大小写
QSensorGesturePluginInterfacesensorgesturesQt 传感器区分大小写
QSensor插件接口sensorsQt 传感器区分大小写
QSqlDriver插件sqldriversQt SQL区分大小写
QIconEngine插件iconenginesQt SVG不区分大小写
QAccessible插件accessibleQt 小部件区分大小写
QStyle插件stylesQt 小部件不区分大小写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());
			}
		}
	}
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值