上次写的不全面,太复杂,在这里补一份
这里的接收器是简单的接收器,dde-dock的接收器请看对应的仓库(分散在dde-dock的仓库中)
dde-dock插件编写指导中有dde-dock插件的编写教程
#include #include #include class Plugin_Root; class PluginController : public QObject, public PluginProxyInterface { Q_OBJECT public: explicit PluginController(QObject *parent = nullptr, Plugin_Root *plugin_root = nullptr); ~PluginController() override; virtual void itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) override; virtual void itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) override; virtual void itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) override; virtual void requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide) override; virtual void requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey) override; virtual void requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible) override; virtual void saveValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &value) override; virtual const QVariant getValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant& fallback = QVariant()) override; virtual void removeValue(PluginsItemInterface *const itemInter, const QStringList &keyList) override; private: Plugin_Root *root = nullptr; QSettings *m_settings = nullptr; QString buildKey(PluginsItemInterface *itemInter, const QString &key) const; }; class Plugin_Root : public QObject { Q_OBJECT public: explicit Plugin_Root(QObject *parent); ~Plugin_Root(); private: QPluginLoader *plugin_loader = new QPluginLoader(this); PluginController *plugin_controller = new PluginController(this, this); }
其中interfances文件夹是从/usr/include/dde-dock中复制的,该文件夹不会默认安装,需要执行如下命令安装
sudo apt install dde-dock-dev
PluginController::PluginController(QObject *parent, Plugin_Root *plugin_root) :QObject(parent) ,root(plugin_root) { m_settings = new QSettings("Program", "plugins", this); } PluginController::~PluginController() { m_settings->sync(); } QString PluginController::buildKey(PluginsItemInterface *itemInter, const QString &key) const { if (itemInter) { return QString("%1/%2").arg(itemInter->pluginName()).arg(key); } else { return QString("unknown_plugin/%1").arg(key); } } void PluginController::itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) { //通过root对UI进行控制,当然,也可以emit出去 } void PluginController::itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) {} void PluginController::itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) {} void PluginController::requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide) {} void PluginController::requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey) {} void PluginController::requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible) {} void PluginController::saveValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &value) { QString fullKey = buildKey(itemInter, key); m_settings->setValue(fullKey, value); } const QVariant PluginController::getValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &fallback) { QString fullKey = buildKey(itemInter, key); QVariant value = m_settings->value(fullKey, fallback); return value; } void PluginController::removeValue(PluginsItemInterface * const itemInter, const QStringList &keyList) { for (const QString &key : keyList) { QString fullKey = buildKey(itemInter, key); m_settings->remove(fullKey); } }
此处用PluginsItemInterface的方法
QWidget *itemWidget(const QString &itemKey); QWidget *itemTipsWidget(const QString &itemKey); QWidget *itemPopupApplet(const QString &itemKey);
获取对应的控件,并放到UI上
void Plugin_Root::load_plugin(QString filepath) { unload_plugin(); if (filepath.isEmpty()) return; plugin_loader->setFileName(filepath); if (!plugin_loader->load()) { qDebug() << "插件导入失败:" << plugin_loader->errorString(); return; } QObject *pluginInstance = plugin_loader->instance(); if (!pluginInstance) { plugin_loader->unload(); return; } PluginsItemInterface *plugin_interface = qobject_cast(pluginInstance); if (plugin_interface) { plugin_interface->init(plugin_controller); plugin_controller->itemUpdate(plugin_interface, "");//强制刷新插件 plugin_interface->positionChanged(plugin_position);//设置插件位置(针对插件套插件的情况) } } void Plugin_Root::unload_plugin() { if (!plugin_loader->isLoaded()) return; QObject *pluginInstance = plugin_loader->instance(); if (pluginInstance) { PluginsItemInterface *plugin_interface = qobject_cast(pluginInstance); if (plugin_interface) { if (plugin_interface->pluginIsAllowDisable() && !plugin_interface->pluginIsDisable()) { plugin_interface->pluginStateSwitched(); } } } plugin_loader->unload(); }
dde-dock插件没有定义怎么卸载,故卸载的时候大概会打出SIGSEGV
故动态卸载的方式只能是禁用插件+隐藏插件的QWidget载体
Plugin_Root::~Plugin_Root() { only_hide(); return; } void Plugin_Root::only_hide() { this->setParent(nullptr); if (!plugin_loader->isLoaded()) { unload_plugin(); this->deleteLater(); return; } if (has_been_closed) return; has_been_closed = true; QObject *pluginInstance = plugin_loader->instance(); if (pluginInstance) { PluginsItemInterface *plugin_interface = qobject_cast(pluginInstance); if (plugin_interface) { if (plugin_interface->pluginIsAllowDisable()) { if (!plugin_interface->pluginIsDisable()) { plugin_interface->pluginStateSwitched(); } } } } //实现隐藏UI }
Plugin_Item_Widget是插件的Item主控件的载体
void Plugin_Item_Widget::set_extra_menu(QString data) { for (auto child : plugin_extra_context_menu->children()) { child->disconnect(); child->deleteLater(); } if (data.isEmpty()) { use_plugin_context = false; return; } else { use_plugin_context = true; QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { return; } if (!doc.isObject()) { return; } //(O_O)!好像没有详细定义 QJsonObject menuObj = doc.object(); bool checkableMenu = menuObj["checkableMenu"].toBool(false); bool singleCheck = menuObj["singleCheck"].toBool(false); if (singleCheck && checkableMenu) { QActionGroup *exclusiveGroup = new QActionGroup(plugin_extra_context_menu); exclusiveGroup->setExclusive(true); } if (!menuObj.contains("items") || !menuObj["items"].isArray()) { return; } QJsonArray itemsArray = menuObj["items"].toArray(); parseMenuItemsArray(itemsArray, checkableMenu, singleCheck); } } void Plugin_Item_Widget::parseMenuItemsArray(const QJsonArray &itemsArray, bool checkableMenu, bool singleCheck) { QActionGroup *exclusiveGroup = nullptr; if (singleCheck && checkableMenu) { exclusiveGroup = new QActionGroup(plugin_extra_context_menu); exclusiveGroup->setExclusive(true); } for (const QJsonValue &itemValue : itemsArray) { if (!itemValue.isObject()) { continue; } QJsonObject itemObj = itemValue.toObject(); QString itemId = itemObj["itemId"].toString(); QString itemText = itemObj["itemText"].toString(); bool isActive = itemObj["isActive"].toBool(true); if (itemText.isEmpty()) { continue; } QAction *action = plugin_extra_context_menu->addAction(itemText); if (!itemId.isEmpty()) { action->setData(itemId); } action->setEnabled(isActive); if (checkableMenu) { action->setCheckable(true); if (itemObj.contains("checked")) { bool checked = itemObj["checked"].toBool(false); action->setChecked(checked); } if (singleCheck && exclusiveGroup) { exclusiveGroup->addAction(action); } } if (itemObj.contains("items") && itemObj["items"].isArray()) { QMenu *subMenu = new QMenu(itemText, plugin_extra_context_menu); action->setMenu(subMenu); QJsonArray subItemsArray = itemObj["items"].toArray(); parseMenuItemsArray(subMenu, subItemsArray, checkableMenu, singleCheck); } connect(action, &QAction::triggered, this, [this, action, itemId] { emit this->extra_menu_call(itemId, action->isChecked()); }); } } void Plugin_Item_Widget::parseMenuItemsArray(QMenu* parentMenu, const QJsonArray& itemsArray, bool checkableMenu, bool singleCheck) { QActionGroup *exclusiveGroup = nullptr; if (singleCheck && checkableMenu) { exclusiveGroup = new QActionGroup(parentMenu); exclusiveGroup->setExclusive(true); } for (const QJsonValue &itemValue : itemsArray) { if (!itemValue.isObject()) { continue; } QJsonObject itemObj = itemValue.toObject(); QString itemId = itemObj["itemId"].toString(); QString itemText = itemObj["itemText"].toString(); bool isActive = itemObj["isActive"].toBool(true); if (itemText.isEmpty()) { continue; } QAction *action = parentMenu->addAction(itemText); if (!itemId.isEmpty()) { action->setData(itemId); } action->setEnabled(isActive); if (checkableMenu) { action->setCheckable(true); if (itemObj.contains("checked")) { bool checked = itemObj["checked"].toBool(false); action->setChecked(checked); } if (singleCheck && exclusiveGroup) { exclusiveGroup->addAction(action); } } if (itemObj.contains("items") && itemObj["items"].isArray()) { QMenu *subMenu = new QMenu(itemText, parentMenu); action->setMenu(subMenu); QJsonArray subItemsArray = itemObj["items"].toArray(); parseMenuItemsArray(subMenu, subItemsArray, checkableMenu, singleCheck); } connect(action, &QAction::triggered, this, [this, action, itemId] { emit this->extra_menu_call(itemId, action->isChecked()); }); } } void Plugin_Item_Widget::contextMenuEvent(QContextMenuEvent *event) { plugin_extra_context_menu->exec(mapToGlobal(event->pos())); }
通过
set_extra_menu(itemInter->itemContextMenu(itemKey));
设置右键菜单,注意,itemInter->itemContextMenu(itemKey);只能调用1次
void Plugin_Root::click_call() { QObject *pluginInstance = plugin_loader->instance(); if (pluginInstance) { PluginsItemInterface *plugin_interface = qobject_cast(pluginInstance); if (plugin_interface) { QString command = plugin_interface->itemCommand(plugin_itemKey); if (command.isNull() || command.isEmpty()) return; QProcess process; process.setProgram("/bin/bash"); process.setArguments(QStringList() << "-c" << command); process.setStandardOutputFile("/dev/null"); process.setStandardErrorFile("/dev/null"); process.startDetached(); } } }
在单击的时候触发Plugin_Root::click_call();即可
virtual void contextMenuEvent(QContextMenuEvent *event); virtual void mousePressEvent(QMouseEvent *event); virtual void mouseReleaseEvent(QMouseEvent *event); virtual void enterEvent(QEvent *event); virtual void leaveEvent(QEvent *event);
实现tips,popup窗口的显示与隐藏
对于某些插件,一般是使用了DTK的插件,导入插件时会崩溃,下图为崩溃时候的Debug信息
解决方案是将main.cpp的QApplication改为DApplication
QT += dtkcore dtkgui dtkwidget
... #include int main(int argc, char* argv[]) { Dtk::Widget::DApplication app(argc, argv); ... }
至于CMake,我一直用的是QMake,不知道CMake怎么写
该更改已经提交的github上
不过还有一个点.我不明白,之前的灰色字体挺好的,为什么要改成黑色?
以及dde-file-manager的搜索框的图像有偏移,QMenu的Checked的QAction好像也有类似的问题
在更新包的时候出现的这样的问题,不知道是只有20.9有问题,还是其他版本也有问题
支持一下!
Featured Collection
Popular Events
自实现dde-dock插件接收器解决方案
上次写的不全面,太复杂,在这里补一份
前提
这里的接收器是简单的接收器,dde-dock的接收器请看对应的仓库(分散在dde-dock的仓库中)
dde-dock插件编写指导中有dde-dock插件的编写教程
0.插件接收器结构构造
其中interfances文件夹是从/usr/include/dde-dock中复制的,该文件夹不会默认安装,需要执行如下命令安装
1.定义PluginController
此处用PluginsItemInterface的方法
获取对应的控件,并放到UI上
2.使用QPluginLoader导入与卸载插件
3.动态卸载插件的方案
dde-dock插件没有定义怎么卸载,故卸载的时候大概会打出SIGSEGV
故动态卸载的方式只能是禁用插件+隐藏插件的QWidget载体
4.实现右键菜单
Plugin_Item_Widget是插件的Item主控件的载体
通过
设置右键菜单,注意,itemInter->itemContextMenu(itemKey);只能调用1次
5.实现左键Command
在单击的时候触发Plugin_Root::click_call();即可
6.杂项
通过
实现tips,popup窗口的显示与隐藏
dde-dock插件编写指导中有dde-dock插件的编写教程
DTK插件的特殊解决方案
对于某些插件,一般是使用了DTK的插件,导入插件时会崩溃,下图为崩溃时候的Debug信息
解决方案是将main.cpp的QApplication改为DApplication
至于CMake,我一直用的是QMake,不知道CMake怎么写
该更改已经提交的github上
题外话
不过还有一个点.我不明白,之前的灰色字体挺好的,为什么要改成黑色?
以及dde-file-manager的搜索框的图像有偏移,QMenu的Checked的QAction好像也有类似的问题
在更新包的时候出现的这样的问题,不知道是只有20.9有问题,还是其他版本也有问题
