Qt中采用插件编程实现一个主界面模块化封装,并通过单例模式提供一个全局访问点,供其他模块操作主界面。
1. 单例类实现
首先,创建一个单例类来管理主界面操作:
// MainWindowManager.h
#ifndef MAINWINDOWMANAGER_H
#define MAINWINDOWMANAGER_H
#include <QObject>
class QMainWindow;
class MainWindowManagerPrivate;
class MainWindowManager : public QObject
{
Q_OBJECT
public:
static MainWindowManager* instance();
static void release();
// 设置主窗口
void setMainWindow(QMainWindow* mainWindow);
// 主界面操作接口
void showMainWindow();
void hideMainWindow();
void setWindowTitle(const QString& title);
void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget);
// 添加更多操作接口...
private:
explicit MainWindowManager(QObject* parent = nullptr);
~MainWindowManager();
MainWindowManagerPrivate* d;
static MainWindowManager* m_instance;
};
#endif // MAINWINDOWMANAGER_H
// MainWindowManager.cpp
#include "MainWindowManager.h"
#include <QMainWindow>
#include <QDockWidget>
class MainWindowManagerPrivate
{
public:
QMainWindow* mainWindow = nullptr;
};
MainWindowManager* MainWindowManager::m_instance = nullptr;
MainWindowManager::MainWindowManager(QObject* parent)
: QObject(parent)
, d(new MainWindowManagerPrivate)
{
}
MainWindowManager::~MainWindowManager()
{
delete d;
}
MainWindowManager* MainWindowManager::instance()
{
if (!m_instance) {
m_instance = new MainWindowManager();
}
return m_instance;
}
void MainWindowManager::release()
{
if (m_instance) {
delete m_instance;
m_instance = nullptr;
}
}
void MainWindowManager::setMainWindow(QMainWindow* mainWindow)
{
d->mainWindow = mainWindow;
}
void MainWindowManager::showMainWindow()
{
if (d->mainWindow) {
d->mainWindow->show();
}
}
void MainWindowManager::hideMainWindow()
{
if (d->mainWindow) {
d->mainWindow->hide();
}
}
void MainWindowManager::setWindowTitle(const QString& title)
{
if (d->mainWindow) {
d->mainWindow->setWindowTitle(title);
}
}
void MainWindowManager::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget)
{
if (d->mainWindow) {
d->mainWindow->addDockWidget(area, dockWidget);
}
}
2. 主界面插件实现
创建一个主界面插件,在初始化时设置主窗口:
// MainWindowPlugin.h
#ifndef MAINWINDOWPLUGIN_H
#define MAINWINDOWPLUGIN_H
#include <QObject>
#include <QtPlugin>
#include "PluginInterface.h"
class MainWindow;
class MainWindowPlugin : public QObject, public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.yourcompany.MainWindowPlugin" FILE "mainwindowplugin.json")
Q_INTERFACES(PluginInterface)
public:
MainWindowPlugin(QObject* parent = nullptr);
~MainWindowPlugin();
void initialize() override;
void uninitialize() override;
private:
MainWindow* m_mainWindow;
};
#endif // MAINWINDOWPLUGIN_H
// MainWindowPlugin.cpp
#include "MainWindowPlugin.h"
#include "MainWindowManager.h"
#include "MainWindow.h"
MainWindowPlugin::MainWindowPlugin(QObject* parent)
: QObject(parent)
, m_mainWindow(new MainWindow)
{
}
MainWindowPlugin::~MainWindowPlugin()
{
delete m_mainWindow;
}
void MainWindowPlugin::initialize()
{
// 设置主窗口到单例管理器
MainWindowManager::instance()->setMainWindow(m_mainWindow);
m_mainWindow->show();
}
void MainWindowPlugin::uninitialize()
{
MainWindowManager::release();
}
3. 其他模块使用单例操作主界面
其他插件或模块可以通过单例对象操作主界面:
// 在其他模块中使用示例
#include "MainWindowManager.h"
void SomeModule::updateMainWindow()
{
// 获取单例实例
MainWindowManager* manager = MainWindowManager::instance();
// 操作主界面
manager->setWindowTitle("New Title");
manager->addDockWidget(Qt::RightDockWidgetArea, myDockWidget);
}
4. 线程安全考虑
如果需要考虑线程安全,可以修改单例实现:
// 线程安全的单例实现
MainWindowManager* MainWindowManager::instance()
{
static QMutex mutex;
QMutexLocker locker(&mutex);
if (!m_instance) {
m_instance = new MainWindowManager();
}
return m_instance;
}
5. 单例管理器导出
确保在Windows平台上正确导出符号:
// 在头文件中添加导出宏
#if defined(MAINWINDOW_LIBRARY)
# define MAINWINDOW_EXPORT Q_DECL_EXPORT
#else
# define MAINWINDOW_EXPORT Q_DECL_IMPORT
#endif
class MAINWINDOW_EXPORT MainWindowManager : public QObject
{
// ...
};
总结
这种实现方式有以下优点:
-
提供全局唯一的访问点来操作主界面
-
降低模块间的耦合度
-
便于扩展主界面操作接口
-
插件化的架构便于维护和扩展
使用时需要注意:
-
主窗口插件应该是最先初始化的插件之一
-
在程序退出时调用release()释放资源
-
线程安全操作主界面(必要时使用QMetaObject::invokeMethod)
迭代优化
主界面对象本身进行优化,其不在插件中创建,而是通过单例类来构建和管理主界面对象。这样设计可以进一步解耦,让主界面的创建和管理完全由单例类控制。下面是修改后的实现方案:
1. 单例类实现(包含主界面构建)
// MainWindowManager.h
#ifndef MAINWINDOWMANAGER_H
#define MAINWINDOWMANAGER_H
#include <QObject>
#include <QScopedPointer>
class QMainWindow;
class MainWindowManagerPrivate;
class MainWindowManager : public QObject
{
Q_OBJECT
public:
static MainWindowManager* instance();
static void release();
// 初始化主窗口(首次调用时创建)
QMainWindow* getOrCreateMainWindow();
// 主界面操作接口
void showMainWindow();
void hideMainWindow();
void setWindowTitle(const QString& title);
void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget);
// 添加更多操作接口...
// 主窗口销毁
void destroyMainWindow();
private:
explicit MainWindowManager(QObject* parent = nullptr);
~MainWindowManager();
QScopedPointer<MainWindowManagerPrivate> d;
static QAtomicPointer<MainWindowManager> m_instance;
static QMutex m_mutex;
};
#endif // MAINWINDOWMANAGER_H
// MainWindowManager.cpp
#include "MainWindowManager.h"
#include <QMainWindow>
#include <QDockWidget>
#include <QMutexLocker>
// 主窗口实现类
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = nullptr) : QMainWindow(parent) {}
~MainWindow() {}
};
class MainWindowManagerPrivate
{
public:
QMainWindow* mainWindow = nullptr;
};
QAtomicPointer<MainWindowManager> MainWindowManager::m_instance = nullptr;
QMutex MainWindowManager::m_mutex;
MainWindowManager::MainWindowManager(QObject* parent)
: QObject(parent)
, d(new MainWindowManagerPrivate)
{
}
MainWindowManager::~MainWindowManager()
{
destroyMainWindow();
}
MainWindowManager* MainWindowManager::instance()
{
if (Q_UNLIKELY(!m_instance)) {
QMutexLocker locker(&m_mutex);
if (!m_instance) {
m_instance = new MainWindowManager();
}
}
return m_instance;
}
void MainWindowManager::release()
{
QMutexLocker locker(&m_mutex);
delete m_instance;
m_instance = nullptr;
}
QMainWindow* MainWindowManager::getOrCreateMainWindow()
{
if (!d->mainWindow) {
d->mainWindow = new MainWindow();
// 可以在这里进行主窗口的初始化设置
d->mainWindow->setObjectName("MainWindow");
d->mainWindow->setWindowTitle("Application Main Window");
}
return d->mainWindow;
}
void MainWindowManager::destroyMainWindow()
{
if (d->mainWindow) {
delete d->mainWindow;
d->mainWindow = nullptr;
}
}
void MainWindowManager::showMainWindow()
{
if (getOrCreateMainWindow()) {
d->mainWindow->show();
}
}
// 其他成员函数实现...
2. 插件实现(不包含主界面对象)
// MainWindowPlugin.h
#ifndef MAINWINDOWPLUGIN_H
#define MAINWINDOWPLUGIN_H
#include <QObject>
#include <QtPlugin>
#include "PluginInterface.h"
#include "MainWindowManager.h"
class MainWindowPlugin : public QObject, public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.yourcompany.MainWindowPlugin" FILE "mainwindowplugin.json")
Q_INTERFACES(PluginInterface)
public:
MainWindowPlugin(QObject* parent = nullptr);
~MainWindowPlugin();
void initialize() override;
void uninitialize() override;
};
#endif // MAINWINDOWPLUGIN_H
cpp
// MainWindowPlugin.cpp
#include "MainWindowPlugin.h"
MainWindowPlugin::MainWindowPlugin(QObject* parent)
: QObject(parent)
{
}
MainWindowPlugin::~MainWindowPlugin()
{
}
void MainWindowPlugin::initialize()
{
// 通过单例获取或创建主窗口并显示
MainWindowManager::instance()->showMainWindow();
}
void MainWindowPlugin::uninitialize()
{
// 可以选择在这里销毁主窗口,或者让单例在程序退出时自动销毁
// MainWindowManager::instance()->destroyMainWindow();
// 释放单例
MainWindowManager::release();
}
3. 使用示例
其他模块可以通过单例访问主界面:
// 在其他插件或模块中使用
#include "MainWindowManager.h"
void OtherModule::modifyMainWindow()
{
// 获取单例
MainWindowManager* manager = MainWindowManager::instance();
// 操作主界面
manager->setWindowTitle("Modified by OtherModule");
// 添加停靠窗口
QDockWidget* dock = new QDockWidget("Toolbox");
// 设置dock的内容...
manager->addDockWidget(Qt::LeftDockWidgetArea, dock);
}
4. 线程安全的单例访问
// 线程安全的主界面操作示例
void SomeThread::run()
{
// 通过单例访问主界面
QMetaObject::invokeMethod(MainWindowManager::instance(),
[=]() {
MainWindowManager::instance()->setWindowTitle("Updated from thread");
},
Qt::QueuedConnection); // 确保在主线程执行
}
设计特点
-
完全解耦:插件不直接包含主界面对象,只通过单例接口操作
-
延迟创建:主界面在第一次被请求时才创建
-
集中管理:所有主界面操作都通过单例类统一管理
-
线程安全:使用原子指针和互斥锁确保线程安全
-
资源管理:使用QScopedPointer自动管理私有实现类
进一步扩展迭代
-
可以添加信号槽机制,让主界面状态变化能够通知其他模块
-
可以扩展单例接口,支持自定义主窗口样式、菜单栏、工具栏等配置
-
可以考虑添加主窗口状态保存和恢复功能
-
对于复杂的主界面操作,可以使用命令模式封装操作
这种设计使得主界面的创建和管理完全由单例类控制,插件和其他模块只需要通过统一的接口与主界面交互,实现了高度的解耦和灵活性。