389 lines
9.8 KiB
C++
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();
|
|
}
|