问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Qt自定义插件开发实战:以SQLite数据库访问为例

创作时间:
作者:
@小白创作中心

Qt自定义插件开发实战:以SQLite数据库访问为例

引用
1
来源
1.
https://www.cnblogs.com/bclshuai/p/18413502

本文将介绍如何在Qt中开发一个自定义插件,用于提供数据库访问接口。通过这个插件,其他组件可以方便地访问SQLite数据库中的内容。文章将详细讲解插件的创建、接口定义、实现以及在调用工程中的使用方法。

1. 需求描述

设备管理组件保存了设备信息和通道信息到SQLite数据库,其他组件也想要访问这个数据库中的内容;需要开发一个自定义插件,用于提供接口给其他组件访问数据库;
开发环境:vs2015+Qt5.9.6

2. 插件介绍

插件主要面向接口编程,通过接口实现功能的扩展,而不需要访问.lib文件。插件在程序运行时即使.dll不存在,程序也能正常启动,只是相应插件功能无法正常使用。相比之下,DLL(动态链接库)需要访问.lib文件,并且在程序运行时必须保证.dll存在,否则无法正常启动。插件在调用时只需要.dll文件,不需要头文件和lib文件,这表明插件的设计更加注重于功能的动态添加和热插拔,而DLL则更侧重于提供可重用的代码和数据。

3. 自定义插件实现

(1)创建一个Qt Library工程;将工程输出设置为dll;

(2)定义一个纯虚函数接口

#pragma once
#ifndef DbPluginInterface_H
#define DbPluginInterface_H
#include <QVariantMap>
#include <QString>
class DbPluginInterface {
public:
 virtual ~DbPluginInterface() {}
 virtual int initLocalDb(QString strDbPath)=0;
 virtual int finishLocalDb() = 0;
 /*执行SQL语句*/
 virtual int ExcuateSql(QString strSql, QVariantMap& replyData, QString& strMsg)=0;
 virtual int ExcuateSql(QString strSql, QString& strMsg)=0;
};
//声明接口,和接口id。
Q_DECLARE_INTERFACE(DbPluginInterface,"org.qter.Example.myplugin.RexExpInterface")
#endif 

(3)继承纯虚函数接口实现导出类

#ifndef DBPLUGIN_H
#define DBPLUGIN_H
#include "dbplugin_global.h"
#include <QSqlDatabase>
#include <QObject>
#include "DbPluginInterface.h"
class DbPlugin :public QObject,public DbPluginInterface
{
 Q_OBJECT
 Q_PLUGIN_METADATA(IID "org.qter.Example.myplugin.RexExpInterface" FILE "DbPlugin.json")
 Q_INTERFACES(DbPluginInterface)
public:
 DbPlugin();
 ~DbPlugin();
 int initLocalDb(QString strDbPath);
 int finishLocalDb();
 /*执行SQL语句*/
 int ExcuateSql(QString strSql, QVariantMap& replyData, QString& strMsg);
 int ExcuateSql(QString strSql, QString& strMsg);
private:
 QSqlDatabase db_;
};
#endif // DBPLUGIN_H

要在类定义中加入下面两行宏定义:

  • Q_PLUGIN_METADATA(IID "org.qter.Example.myplugin.RexExpInterface" FILE "DbPlugin.json")
  • Q_INTERFACES(DbPluginInterface)

Q_PLUGIN_METADATA介绍

要在类定义中加入下面两行宏定义:

  • Q_PLUGIN_METADATA
  • Q_OBJECT
  • Q_INTERFACES

Q_PLUGIN_METADATA宏在 Qt 插件开发中用于声明和提供插件的元数据(metadata),使得 Qt 的插件机制能够识别并正确加载该插件。通常与 Q_OBJECTQ_INTERFACES 一起使用。

Q_PLUGIN_METADATA宏将插件的信息嵌入到生成的共享库中。这个信息包含插件的标识符、版本号、描述等,可供插件加载器 (QPluginLoader) 在运行时识别和使用。这些信息也可以在插件加载前进行检索,从而允许应用程序根据插件的元数据作出决策。

Q_PLUGIN_METADATA 宏的基本语法如下:

Q_PLUGIN_METADATA(IID "插件接口标识符" FILE "元数据文件.json")
  • IID: 插件接口标识符,用于唯一标识插件接口。通常是一个字符串,与 Q_DECLARE_INTERFACE 中声明的标识符相对应。
  • FILE: 可选参数,用于指定一个包含插件元数据的 JSON 文件,内容如下。DbPlugin.json文件放到工程路径(dbplugin.cpp同目录下)下,否则编译时会报错Plugin Metadata file ".json" does not exist.
{
 "name": "MyPlugin",
 "version": "1.0",
 "description": "This is a sample plugin for demonstration purposes.",
 "author": "Your Name",
 "license": "GPL"
}

调用者可以通过 QPluginLoadermetaData().metaData().toVariantMap().value("MetaData") 方法获取到这个元数据,可以通过元数据的内容了解插件的信息;

QDir pluginsDir("../x64/Release/");
QPluginLoader loader(pluginsDir.absoluteFilePath("DbPlugin.dll"));
QVariantMap metadata=loader.metaData().toVariantMap();

Q_INTERFACES宏介绍

在实现插件时使用,用于指定插件实现了哪些接口,从而使运行时的Qt插件系统知晓该插件提供了哪些功能接口,并根据这些接口来调用插件的功能;与Q_DECLARE_INTERFACE宏定义配套使用,Q_DECLARE_INTERFACE在申明插件接口时使用:用于给插件接口类(ClassName)绑定一个唯一标识符(Identifier)。

(4)实现插件导出类dbpugin.cpp

#include "dbplugin.h"
#include <QCoreApplication>
#include <QFile>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include<QDir>
DbPlugin::DbPlugin()
{
}
DbPlugin::~DbPlugin()
{
}
int DbPlugin::initLocalDb(QString strDbPath)
{
 int iret = -1;
 do
 {
 QString strPath = QDir::currentPath();
 db_ = QSqlDatabase::addDatabase("QSQLITE");
 QFile file(strDbPath);
 if (!file.exists())
 {
 break;
 }
 db_.setDatabaseName(strDbPath);
 if (!db_.open())
 {
 break;
 }
 iret = 0;
 } while (0);
 return iret;
}
int DbPlugin::finishLocalDb()
{
 db_.close();
 return 0;
}
int DbPlugin::ExcuateSql(QString strSql, QVariantMap& replyData, QString& strMsg)
{
 int errorCode = -1;
 QSqlQuery query;
 query.prepare(strSql);
 if (!query.exec())
 {
 QSqlError error = query.lastError();
 errorCode = error.type();
 strMsg = error.text();
 //LOG_ERROR("ExcuateSql %s failed,code:%d,errormsg:%s", strSql.toStdString().c_str(), errorCode, strMsg.toStdString().c_str());
 return -1;
 }
 QVariantList dataList;
 while (query.next())
 {
 QSqlRecord record = query.record();
 int column = record.count();
 QVariantMap data;
 for (int i = 0; i < column; i++)
 {
 data.insert(record.fieldName(i), record.value(i));
 }
 dataList.append(data);
 }
 //QVariantMap replyData;
 replyData.insert("totalCount", dataList.size());
 replyData.insert("data", dataList);
 return 0;
}
int DbPlugin::ExcuateSql(QString strSql, QString& strMsg)
{
 int errorCode = -1;
 QSqlQuery query;
 query.prepare(strSql);
 if (!query.exec())
 {
 QSqlError error = query.lastError();
 errorCode = error.type();
 strMsg = error.text();
 //LOG_ERROR("ExcuateSql %s failed,code:%d,errormsg:%s", strSql.toStdString().c_str(), errorCode, strMsg.toStdString().c_str());
 return -1;
 }
 return 0;
}

要实现数据库的加载和访问,需要配置sql模块,增加sql驱动;这样才能正确访问数据库;

4. 调用工程如何使用插件

(1)创建一个工程,将接口文件DbPluginInterface.h复制到工程,创建数据库调用单例类,

#ifndef DBPLUGIN_H
#define DBPLUGIN_H
#include "base_define.h"
#include <QObject>
#include "DbPluginInterface.h"
class DbPlugin : public QObject,public singleton<DbPlugin>
{
 Q_OBJECT
public:
 DbPlugin();
 ~DbPlugin();
 int LoadDbPlugin();//加载插件
 /*执行SQL语句*/
 int ExcuateSql(QString strSql, QVariantMap& replyData, QString& strMsg);
 int ExcuateSql(QString strSql, QString& strMsg);
private:
 DbPluginInterface* pDbpluginInstance=NULL;
};
#endif // DBPLUGIN_H

(2)实现插件加载和接口封装,然后就可以再这个工程中调用单例类的接口,通过插件访问数据库

#include "DbPlugin.h"
#include <QPluginLoader>
#include <QDir>
DbPlugin::DbPlugin()
{
}
DbPlugin::~DbPlugin()
{
}
int DbPlugin::LoadDbPlugin()
{
 QDir pluginsDir("../x64/Release/");//指定插件的相对路径
 QPluginLoader loader(pluginsDir.absoluteFilePath("DbPlugin.dll"));///通过相对路径获取插件的绝对路径
 QObject *plugin = loader.instance();
 if (plugin) {
 pDbpluginInstance = qobject_cast<DbPluginInterface *>(plugin);
 if (pDbpluginInstance==NULL) {
 return -1;
 }
 }
 QString strDbPath = pluginsDir.absoluteFilePath("localSqliteDb.db");//sqlite和插件在同一路径,如果是其他路径,要传入数据库文件的路径,否则会报错;
 if (pDbpluginInstance->initLocalDb(strDbPath)!=0)
 {
 return -1;
 }
 return 0;
}
int DbPlugin::ExcuateSql(QString strSql, QVariantMap& replyData, QString& strMsg)
{
 if (pDbpluginInstance==NULL)
 {
 return-1;
 }
 return pDbpluginInstance->ExcuateSql(strSql, replyData, strMsg);
}
int DbPlugin::ExcuateSql(QString strSql, QString& strMsg) 
{
 if (pDbpluginInstance == NULL)
 {
 return -1;
 }
 return pDbpluginInstance->ExcuateSql(strSql, strMsg);
}
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号