• 首页
  • Qt文档
  • DTK文档
  • 玲珑文档
  • 控制中心插件

    本文档将会指导大家一步一步的构建出控制中心的插件。本项目使用CMake进行项目管理,Qt/C++进行项目开发,如不会CMake或者Qt,需要先进行学习准备。

    CMake文件

    首先需要新建出CMakeLists.txt,并设置好项目名称和插件名称。

    set(PLUGIN_NAME "helloworld")
    project(${PLUGIN_NAME})
    

    创建src目录,并新建helloworldplugin.cpp文件,向cmake中添加该文件。

    set(SRCS src/helloworldplugin.cpp)
    

    现在添加基础的依赖。

    # 载入Qt基础库
    find_package(Qt5 REQUIRE COMPONENT Core Widgets)
    #载入控制中心开发库
    find_package(DdeControlCenter REQUIRED)
    

    为项目添加生成的动态库,需要设置名称、库的类型和所需的依赖。

    # 设置动态库的名称、类型,shared是动态库。
    add_library(${PLUGIN_NAME} SHARED ${SRCS})
    # 设置项目需要的访问的头文件,这里是控制中心的插件头文件
    target_include_directories(${PLUGIN_NAME} PUBLIC ${DdeControlCenter_INCLUDE_DIR})
    # 设置项目链接的依赖,这里是Qt和控制中心的控件动态库
    target_link_libraries(${PLUGIN_NAME} PRIVATE
    ${DdeControlCenter_LIBRARIES})
    

    插件代码开发

    当所有的准备工作都完成以后,我们可以进行插件的代码开发工作了。

    在src/helloworldplugin.cpp文件中,导入控制中心插件接口的头文件。

    #include "interface/namespace.h"
    #include "interface/moduleinterface.h"
    #include "interface/frameproxyinterface.h"
    

    首先先说明一下控制中心插件的接口:

        // preInitialize会在模块初始化时被调用,用于模块在准备阶段进行资源的初始化;
        // preInitialize不允许进行高资源的操作;
        virtual void preInitialize(bool sync = false,FrameProxyInterface::PushType = FrameProxyInterface::PushType::Normal) {Q_UNUSED(sync)}
        // initialize初始化相应的模块,参数proxy用于Moudle向Frame信息查询和主动调用;
        // 返回Module的id;
        // initialize的时候不能做资源占用较高的操作;
        virtual void initialize() = 0;
        // 保留,该接口用于重设插件的所有设置。
        virtual void reset() {}
        ///
        /// \brief name
        /// 插件提供的模块名称,用于内部标识。
        /// \return
        ///
        virtual const QString name() const = 0;
        ///
        /// \brief name
        /// 模块名,用于显示
        /// \return
        ///
        virtual const QString displayName() const = 0;
        ///
        /// \brief icon
        /// 获取模块的图标路径。
        /// \return
        ///
        virtual QIcon icon() const {
            return QIcon::fromTheme(QString("dcc_nav_%1").arg(name()));
        }
        ///
        /// \brief translationPath
        /// 获取多语言文件的路径,用以搜索
        /// \return QString
        ///
        virtual QString translationPath() const {
            return QStringLiteral(":/translations/dde-control-center_%1.ts");
        }
        // 应该暂时不需要finalize;
        // virtual void finalize();
        // 获取Module的Metadata;
        // virtual ModuleMetadata getMetadata();
        ///
        /// \brief showPage
        /// 显示指定的某个页面
        /// \param pageName
        /// the page name
        ///
        virtual void showPage(const QString &pageName) { Q_UNUSED(pageName); }
        // 返回模块主Widget;
        virtual QWidget *moduleWidget() { return nullptr;}
        ///
        /// \brief contentPopped
        /// 弹出指定的页面
        /// \param w
        ///
        virtual void contentPopped(QWidget *const w) { Q_UNUSED(w);}
        ///
        /// \brief active
        /// 当模块第一次被点击进入时,active会被调用,如果是插件,重载的时候必须声明为slots,否则加载不了
        virtual void active() {}
        ///
        /// \brief active
        /// 当模块被销毁时,deactive会被调用
        virtual void deactive() {}
        ///
        /// \brief load
        /// 当搜索到相关字段后,lead会被调用
        /// 如果可以正常显示则返回 0, 否则返回非0
        virtual int load(const QString &path) {
            Q_UNUSED(path);
            return 0;
        }
        virtual QStringList availPage() const { return QStringList(); }
        /**
         * @brief path
         * @return 插件级别及二级菜单插件所属模块
         */
        virtual QString path() const {
            return QString();
        }
        /**
         * @brief follow
         * @return 插件插入位置,可以字符串或者数字
         */
        virtual QString follow() const {
            return QString();
        }
        /**
         * @brief enabled
         * @return 插件是否处于可用状态
         */
        virtual bool enabled() const {
            return true;
        }
    

    我们的helloworld模块只需要重写必要的纯虚函数,还需要重写moduleWidget方法,返回我们提供的页面。 新建一个HelloWorldPlugin类,并继承自QObject和DCC_NAMESPACE::ModuleInterface。不要担心C++的交叉继承,它只在部分特殊场景需要注意,特别是访问基类之间同名的方法。

    class HelloWorldPlugin : public QObject, public DCC_NAMESPACE::ModuleInterface {
        Q_OBJECT
        Q_PLUGIN_METADATA(IID ModuleInterface_iid)
        Q_INTERFACES(DCC_NAMESPACE::ModuleInterface)
    public:
        HelloWorldPlugin(QObject *parent = nullptr)
        : QObject(parent)
        , DCC_NAMESPACE::ModuleInterface() 
        {}
        
        // 重写纯虚函数
        virtual void initialize() override {
            // 进行初始化操作,对于我们的插件来说,不需要做任何事情。
        }
        virtual const QString name() const override {
            return "helloworldplugin";
        }
        virtual const QString displayName() const override {
            return tr("HelloWorld Plugin");
        }
        // 重写虚函数
        virtual QWidget* moduleWidget() const override {
            if (!m_mainPage) {
              m_mainPage = new QWidget(this);
            }
            
            return m_mainPage;
        }
    private:
        QPointer<QWidget> m_mainPage;
      };
    #define ModuleInterface_iid "com.deepin.dde.ControlCenter.module/1.0"
    Q_DECLARE_INTERFACE(DCC_NAMESPACE::ModuleInterface, ModuleInterface_iid)
    

    此时如果进行构建,我们会看到Build目录出现了libhelloworldplugin.so了。但是我们还没实现安装,我们并不能执行make install将插件安装到系统里面。 返回CMakeLists.txt文件,追加上安装的配置。

    install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION lib/dde-control-center/plugins/)
    

    还未说我们如何执行构建和安装呢,新建一个build目录,然后执行cmake的命令生成makefile,再通过make命令执行编译和安装。

    mkdir build
    cd build
    cmake ../
    make
    sudo make install
    

    然后运行控制中心,就可以看到我们的插件被加载了。 项目自带的例子:https://github.com/linuxdeepin/dde-control-center/tree/master/src/frame/plugins/privacy