DiagramDesigner/pluginManager/source/pluginManager.cpp

389 lines
9.8 KiB
C++

//pluginManager.cpp
#include "pluginManager/include/pluginManager.h"
//#include "common/include/pluginCommon/iPlugin.h"
#include <QPluginLoader>
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <QMutex>
#include <QMutexLocker>
// 私有实现
class PluginManager::Private
{
public:
struct PluginInfo {
QPluginLoader *loader = nullptr;
IPlugin *plugin = nullptr;
PluginDescriptor descriptor;
QString filePath;
~PluginInfo() {
if (plugin) {
plugin->shutdown();
}
if (loader) {
loader->unload();
delete loader;
}
}
};
QMutex mutex;
QMap<QString, PluginInfo*> plugins; // pluginId -> PluginInfo*
QMap<QString, QString> shapeToPlugin; // shapeId -> pluginId
QMap<QString, ShapeDescriptor> shapeDescriptors; // shapeId -> descriptor
// 内置形状
QMap<QString, std::function<ICanvasItem*()>> builtinCreators;
QMap<QString, ShapeDescriptor> builtinDescriptors;
QStringList pluginDirs;
bool loadPluginInternal(const QString &filePath, QString *error = nullptr);
void unloadPluginInternal(const QString &pluginId);
};
PluginManager::PluginManager(QObject *parent)
: QObject(parent)
, d(new Private)
{
// 注册一些内置形状
//registerBuiltinShapes();
}
PluginManager::~PluginManager()
{
QMutexLocker locker(&d->mutex);
// 卸载所有插件
auto pluginIds = d->plugins.keys();
for (const QString &pluginId : pluginIds) {
d->unloadPluginInternal(pluginId);
}
}
PluginManager* PluginManager::instance()
{
static PluginManager instance;
return &instance;
}
bool PluginManager::loadPlugin(const QString &filePath)
{
QMutexLocker locker(&d->mutex);
return d->loadPluginInternal(filePath);
}
bool PluginManager::unloadPlugin(const QString &pluginId)
{
QMutexLocker locker(&d->mutex);
if (!d->plugins.contains(pluginId)) {
qWarning() << "Plugin not found:" << pluginId;
return false;
}
d->unloadPluginInternal(pluginId);
emit pluginUnloaded(pluginId);
emit shapesChanged();
return true;
}
void PluginManager::loadAllPlugins(const QString &directory)
{
QDir dir(directory);
if (!dir.exists()) {
qWarning() << "Plugin directory does not exist:" << directory;
return;
}
QStringList filters;
#ifdef Q_OS_WIN
filters << "*.dll";
#elif defined(Q_OS_MAC)
filters << "*.dylib" << "*.so";
#else
filters << "*.so";
#endif
QStringList pluginFiles = dir.entryList(filters, QDir::Files);
for (const QString &file : pluginFiles) {
QString filePath = dir.absoluteFilePath(file);
if (!loadPlugin(filePath)) {
qWarning() << "Failed to load plugin:" << filePath;
}
}
}
bool PluginManager::Private::loadPluginInternal(const QString &filePath, QString *error)
{
QFileInfo fileInfo(filePath);
if (!fileInfo.exists()) {
if (error) *error = "File does not exist";
return false;
}
QPluginLoader *loader = new QPluginLoader(filePath);
if (!loader->load()) {
if (error) *error = loader->errorString();
delete loader;
return false;
}
QObject *pluginInstance = loader->instance();
if (!pluginInstance) {
if (error) *error = "Failed to get plugin instance";
loader->unload();
delete loader;
return false;
}
IPlugin *plugin = qobject_cast<IPlugin*>(pluginInstance);
if (!plugin) {
if (error) *error = "Plugin does not implement IPlugin interface";
loader->unload();
delete loader;
return false;
}
if (!plugin->initialize()) {
if (error) *error = "Plugin initialization failed";
loader->unload();
delete loader;
return false;
}
PluginDescriptor descriptor = plugin->descriptor();
if (plugins.contains(descriptor.id)) {
if (error) *error = "Plugin already loaded: " + descriptor.id;
loader->unload();
delete loader;
return false;
}
PluginInfo *info = new PluginInfo;
info->loader = loader;
info->plugin = plugin;
info->descriptor = descriptor;
info->filePath = filePath;
plugins[descriptor.id] = info;
// 注册形状
QList<ShapeDescriptor> shapes = plugin->shapes();
for (const ShapeDescriptor &shape : shapes) {
shapeDescriptors[shape.id] = shape;
shapeToPlugin[shape.id] = descriptor.id;
}
qInfo() << "Plugin loaded:" << descriptor.name << "(" << descriptor.id << ")";
return true;
}
void PluginManager::Private::unloadPluginInternal(const QString &pluginId)
{
if (!plugins.contains(pluginId)) {
return;
}
PluginInfo *info = plugins.take(pluginId);
if (!info) {
return;
}
// 移除关联的形状
auto shapeIds = shapeToPlugin.keys();
for (const QString &shapeId : shapeIds) {
if (shapeToPlugin[shapeId] == pluginId) {
shapeDescriptors.remove(shapeId);
shapeToPlugin.remove(shapeId);
}
}
delete info;
}
QStringList PluginManager::availableShapes() const
{
QMutexLocker locker(&d->mutex);
QStringList shapes = d->shapeDescriptors.keys();
shapes.append(d->builtinDescriptors.keys());
return shapes;
}
bool PluginManager::contains(const QString &shapeId) const
{
QMutexLocker locker(&d->mutex);
return d->shapeDescriptors.contains(shapeId) ||
d->builtinDescriptors.contains(shapeId);
}
ShapeDescriptor PluginManager::shapeDescriptor(const QString &shapeId) const
{
QMutexLocker locker(&d->mutex);
if (d->shapeDescriptors.contains(shapeId)) {
return d->shapeDescriptors[shapeId];
}
if (d->builtinDescriptors.contains(shapeId)) {
return d->builtinDescriptors[shapeId];
}
return ShapeDescriptor();
}
void PluginManager::registerBuiltinShape(const QString &shapeId,
const QString &name,
std::function<ICanvasItem*()> creator,
const QString &iconPath)
{
QMutexLocker locker(&d->mutex);
ShapeDescriptor desc;
desc.id = shapeId;
desc.name = name;
desc.category = "内置形状";
desc.iconPath = iconPath;
d->builtinDescriptors[shapeId] = desc;
d->builtinCreators[shapeId] = creator;
emit shapesChanged();
}
QIcon PluginManager::shapeIcon(const QString &shapeId) const
{
QMutexLocker locker(&d->mutex);
ShapeDescriptor desc = shapeDescriptor(shapeId);
if (desc.id.isEmpty()) {
return QIcon();
}
// 首先尝试从资源文件加载
if (desc.iconPath.startsWith(":/")) {
if (QFile::exists(desc.iconPath)) {
return QIcon(desc.iconPath);
}
}
// 尝试从文件系统加载
if (QFile::exists(desc.iconPath)) {
return QIcon(desc.iconPath);
}
// 尝试从插件目录查找
if (d->shapeToPlugin.contains(shapeId)) {
QString pluginId = d->shapeToPlugin[shapeId];
if (d->plugins.contains(pluginId)) {
QString pluginDir = QFileInfo(d->plugins[pluginId]->filePath).absolutePath();
// 尝试相对路径
QString relativePath = pluginDir + "/" + desc.iconPath;
if (QFile::exists(relativePath)) {
return QIcon(relativePath);
}
// 尝试在icons子目录中查找
QString iconName = QFileInfo(desc.iconPath).fileName();
QString iconDirPath = pluginDir + "/icons/" + iconName;
if (QFile::exists(iconDirPath)) {
return QIcon(iconDirPath);
}
}
}
// 返回默认图标
return QIcon(":/shapes/default.png");
}
void PluginManager::addPluginDirectory(const QString &dir)
{
QMutexLocker locker(&d->mutex);
if (!d->pluginDirs.contains(dir)) {
d->pluginDirs.append(dir);
}
}
void PluginManager::setPluginDirectories(const QStringList &dirs)
{
QMutexLocker locker(&d->mutex);
d->pluginDirs = dirs;
// 重新扫描所有目录
for (const QString &dir : dirs) {
loadAllPlugins(dir);
}
}
QStringList PluginManager::pluginDirectories() const
{
QMutexLocker locker(&d->mutex);
return d->pluginDirs;
}
ICanvasItem* PluginManager::createItem(const QString &shapeId)
{
QMutexLocker locker(&d->mutex);
// 1. 首先检查内置形状
if (d->builtinCreators.contains(shapeId)) {
ICanvasItem *item = d->builtinCreators[shapeId]();
if (item) {
qDebug() << "Created builtin shape:" << shapeId;
return item;
}
}
// 2. 检查插件形状
if (!d->shapeToPlugin.contains(shapeId)) {
qWarning() << "Shape not found:" << shapeId;
return nullptr;
}
QString pluginId = d->shapeToPlugin[shapeId];
if (!d->plugins.contains(pluginId)) {
qWarning() << "Plugin not loaded for shape:" << shapeId;
return nullptr;
}
// 3. 通过插件创建形状
IPlugin *plugin = d->plugins[pluginId]->plugin;
if (!plugin) {
qWarning() << "Plugin interface is null for:" << pluginId;
return nullptr;
}
ICanvasItem *item = plugin->createShape(shapeId);
if (item) {
qDebug() << "Created plugin shape:" << shapeId << "from plugin:" << pluginId;
} else {
qWarning() << "Plugin failed to create shape:" << shapeId;
}
return item;
}
PluginDescriptor PluginManager::pluginDescriptor(const QString &pluginId) const
{
QMutexLocker locker(&d->mutex);
if (d->plugins.contains(pluginId)) {
return d->plugins[pluginId]->descriptor;
}
return PluginDescriptor();
}
QStringList PluginManager::loadedPlugins() const
{
QMutexLocker locker(&d->mutex);
return d->plugins.keys();
}