From 742ea36c3dd76c6935bde6de4ca2767474b8f036 Mon Sep 17 00:00:00 2001 From: baiYue Date: Thu, 26 Mar 2026 16:22:35 +0800 Subject: [PATCH] add custom HMI image element --- CMakeLists.txt | 16 +- common/core_model/types.h | 5 + common/frontend/monitor_item.h | 17 + common/include/baseProperty.h | 16 + common/include/pluginCommon/iCanvasItem.h | 32 ++ common/include/pluginCommon/iPlugin.h | 51 ++ common/include/pluginCommon/iPluginAdapter.h | 52 ++ common/include/pluginCommon/iShapeFactory.h | 24 + common/source/baseProperty.cpp | 11 + diagramCavas/CMakeLists.txt | 17 +- .../graphicsDataModel/fixedPortsModel.h | 13 + .../include/graphicsItem/electricBayItem.h | 2 +- .../electricFunctionModelSvgItemImage.h | 20 + .../graphicsFunctionModelItem.h | 2 +- .../include/graphicsItem/graphicsItemGroup.h | 2 +- .../graphicsItem/graphicsPolygonItem.h | 2 +- .../include/graphicsItem/pluginItemFactory.h | 53 ++ .../include/graphicsItem/pluginItemWrapper.h | 52 ++ diagramCavas/include/util/editingSelector.h | 2 +- .../electricBaseModelSvgItem.cpp | 1 - diagramCavas/source/designerScene.cpp | 6 - diagramCavas/source/diagramCavas.cpp | 8 + .../graphicsDataModel/fixedPortsModel.cpp | 158 +++++- .../electricFunctionModelSvgItem.cpp | 1 - .../electricFunctionModelSvgItemImage.cpp | 71 +++ .../source/graphicsItem/pluginItemFactory.cpp | 71 +++ .../source/graphicsItem/pluginItemWrapper.cpp | 451 ++++++++++++++++++ diagramCavas/source/monitorPanel.cpp | 79 ++- diagramCavas/source/util/baseSelector.cpp | 32 +- diagramCavas/source/util/movingSelector.cpp | 2 +- diagramCavas/source/util/rotationSelector.cpp | 2 +- diagramCavas/source/util/scalingSelector.cpp | 2 +- diagramUtils/include/dataBase.h | 6 + diagramUtils/source/dataBase.cpp | 252 +++++++++- include/configToolBar.h | 2 - include/draggableToolButton.h | 35 ++ include/enhancedToolBar.h | 45 +- pluginManager/CMakeLists.txt | 54 +++ pluginManager/include/genericPluginAdapter.h | 60 +++ pluginManager/include/pluginManager.h | 57 +++ pluginManager/source/genericPluginAdapter.cpp | 200 ++++++++ pluginManager/source/pluginManager.cpp | 388 +++++++++++++++ source/configToolBar.cpp | 14 +- source/draggableToolButton.cpp | 76 +++ source/enhancedToolBar.cpp | 214 ++++----- source/mainwindow.cpp | 54 +-- 46 files changed, 2483 insertions(+), 247 deletions(-) create mode 100644 common/include/pluginCommon/iCanvasItem.h create mode 100644 common/include/pluginCommon/iPlugin.h create mode 100644 common/include/pluginCommon/iPluginAdapter.h create mode 100644 common/include/pluginCommon/iShapeFactory.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h create mode 100644 diagramCavas/include/graphicsItem/pluginItemFactory.h create mode 100644 diagramCavas/include/graphicsItem/pluginItemWrapper.h create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.cpp create mode 100644 diagramCavas/source/graphicsItem/pluginItemFactory.cpp create mode 100644 diagramCavas/source/graphicsItem/pluginItemWrapper.cpp create mode 100644 include/draggableToolButton.h create mode 100644 pluginManager/CMakeLists.txt create mode 100644 pluginManager/include/genericPluginAdapter.h create mode 100644 pluginManager/include/pluginManager.h create mode 100644 pluginManager/source/genericPluginAdapter.cpp create mode 100644 pluginManager/source/pluginManager.cpp create mode 100644 source/draggableToolButton.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f1f1b9..3ebe843 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,9 +58,10 @@ set(H_HEADER_FILES include/monitorItemsDlg.h include/monitorPagesDlg.h include/baseDockWidget.h - include/enhancedToolBar.h include/toolBarConfig.h + include/enhancedToolBar.h include/configToolBar.h + include/draggableToolButton.h common/include/tools.h common/include/httpInterface.h @@ -71,6 +72,11 @@ set(H_HEADER_FILES common/include/structDataSource.h common/include/extraPropertyManager.h + common/include/pluginCommon/iCanvasItem.h + common/include/pluginCommon/iPlugin.h + common/include/pluginCommon/iShapeFactory.h + common/include/pluginCommon/iPluginAdapter.h + common/core_model/types.h common/core_model/topology.h common/core_model/diagram.h @@ -95,9 +101,10 @@ set(CPP_SOURCE_FILES source/monitorItemsDlg.cpp source/monitorPagesDlg.cpp source/baseDockWidget.cpp - source/enhancedToolBar.cpp source/toolBarConfig.cpp + source/enhancedToolBar.cpp source/configToolBar.cpp + source/draggableToolButton.cpp common/source/httpInterface.cpp common/source/tools.cpp @@ -152,6 +159,7 @@ include_directories(${PostgreSQL_INCLUDE_DIRS}) target_include_directories(DiagramDesigner PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_include_directories(DiagramDesigner PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/common") target_include_directories(DiagramDesigner PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/PropertyEditor/source/include)") +target_include_directories(DiagramDesigner PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/pluginManager/include)") target_link_libraries(DiagramDesigner PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) @@ -178,8 +186,10 @@ add_subdirectory(PropertyEditor) add_subdirectory(diagramCavas) add_subdirectory(diagramUtils) add_subdirectory(diagramCommunication) +add_subdirectory(pluginManager) target_link_libraries(DiagramDesigner PRIVATE PropertyEditor) -target_link_libraries(DiagramDesigner PRIVATE diagramCavas diagramUtils diagramCommunication) +target_link_libraries(DiagramDesigner PRIVATE diagramCavas diagramUtils diagramCommunication pluginManager) file(COPY setting.xml DESTINATION "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin") + diff --git a/common/core_model/types.h b/common/core_model/types.h index a986511..1466f8c 100644 --- a/common/core_model/types.h +++ b/common/core_model/types.h @@ -35,6 +35,11 @@ enum GraphicsItemType GIT_3wTransformer= QGraphicsItem::UserType + 67, GIT_node= QGraphicsItem::UserType + 79, GIT_bay= QGraphicsItem::UserType + 80, //间隔 + + GIT_Image = QGraphicsItem::UserType + 110, //图形item + GIT_Text = QGraphicsItem::UserType + 111, //文本item + + GIT_PluginItem = QGraphicsItem::UserType + 120, //自定义Item //====================================== GIT_baseNode = QGraphicsItem::UserType + 199, GIT_baseBus = QGraphicsItem::UserType + 200, diff --git a/common/frontend/monitor_item.h b/common/frontend/monitor_item.h index 7bb4095..6ac17f2 100644 --- a/common/frontend/monitor_item.h +++ b/common/frontend/monitor_item.h @@ -227,6 +227,23 @@ struct HMIImageRef //人机界面中的图片引用 } }; +struct HMICustomImageRef //人机界面中图片item的引用 +{ + int id = -1; + QUuid hmiId; + QString model; + QUuid itemId; + QByteArray hash256; + + bool operator==(const HMICustomImageRef& other) const + { + return hmiId == other.hmiId && + model == other.model && + itemId == other.itemId && + hash256 == other.hash256; + } +}; + struct MonitorItemDisplayInfo //监控模式显示信息 { int nItemType; //设备类型 diff --git a/common/include/baseProperty.h b/common/include/baseProperty.h index 695be24..cc02803 100644 --- a/common/include/baseProperty.h +++ b/common/include/baseProperty.h @@ -164,6 +164,22 @@ private: PropertyModel pm; //工程模的选择状态 }; +class GraphicProperty:public ModelProperty //非模型图元属性 +{ + Q_OBJECT +public: + GraphicProperty(QObject* parent); + virtual ~GraphicProperty(); + + void setGraphicType(int n) {nGraphicType = n;} + int getGraphicType() {return nGraphicType;} + void setContent(const QString& str) {sContent = str;} + QString getContent() {return sContent;} +protected: + int nGraphicType = -1; //图形类型 0图像1文字 + QString sContent; +}; + class BaseProperty:public ModelProperty //图像工程模模属性类,存放电路元件属性 { Q_OBJECT diff --git a/common/include/pluginCommon/iCanvasItem.h b/common/include/pluginCommon/iCanvasItem.h new file mode 100644 index 0000000..d1342f1 --- /dev/null +++ b/common/include/pluginCommon/iCanvasItem.h @@ -0,0 +1,32 @@ +//ICanvasItem.h +#pragma once + +#include +#include +#include + +class ICanvasItem : public QObject +{ + Q_OBJECT +public: + virtual ~ICanvasItem() = default; + + // 基本信息 + virtual QString typeId() const = 0; + virtual QString displayName() const = 0; + + // 几何属性 + virtual QRectF bounds() const = 0; + virtual void setBounds(const QRectF &rect) = 0; + + // 绘制 + virtual void draw(QPainter *painter, const QRectF &rect) = 0; + + // 属性管理 + virtual QVariant property(const QString &key) const = 0; + virtual void setProperty(const QString &key, const QVariant &value) = 0; + +signals: + void boundsChanged(const QRectF &newBounds); + void propertyChanged(const QString &key, const QVariant &value); +}; diff --git a/common/include/pluginCommon/iPlugin.h b/common/include/pluginCommon/iPlugin.h new file mode 100644 index 0000000..9b65d3c --- /dev/null +++ b/common/include/pluginCommon/iPlugin.h @@ -0,0 +1,51 @@ +// include/plugin_manager/IPlugin.h +#pragma once + +#include +#include +#include "iCanvasItem.h" + +// 形状描述 +struct ShapeDescriptor { + QString id; // 形状标识符 + QString name; // 显示名称 + QString category; // 分类 + QString iconPath; // 图标路径 + QVariantMap defaults; // 默认属性 +}; + +// 插件描述 +struct PluginDescriptor { + QString id; // 插件ID + QString name; // 插件名称 + QString version; // 版本 + QString author; // 作者 + QString description; // 描述 +}; + +// 插件接口 +class IPlugin : public QObject +{ + Q_OBJECT +public: + explicit IPlugin(QObject *parent = nullptr) : QObject(parent) {} + virtual ~IPlugin() = default; + + // 插件信息 + virtual PluginDescriptor descriptor() const = 0; + + // 支持的形状 + virtual QList shapes() const = 0; + + // 创建形状 + virtual ICanvasItem* createShape(const QString &shapeId) = 0; + + // 插件生命周期 + virtual bool initialize() = 0; + virtual void shutdown() = 0; + + // 图标获取 + virtual QIcon shapeIcon(const QString &shapeId) const = 0; +}; + +Q_DECLARE_INTERFACE(IPlugin, "com.canvas.plugin/1.0") diff --git a/common/include/pluginCommon/iPluginAdapter.h b/common/include/pluginCommon/iPluginAdapter.h new file mode 100644 index 0000000..7bb1e21 --- /dev/null +++ b/common/include/pluginCommon/iPluginAdapter.h @@ -0,0 +1,52 @@ +//iPluginAdapter.h +#pragma once + +#include +#include +#include +#include + +// 插件适配器抽象接口 +class IPluginAdapter : public QObject +{ + Q_OBJECT +public: + explicit IPluginAdapter(QObject *parent = nullptr) : QObject(parent) {} + virtual ~IPluginAdapter() = default; + + // 基本属性 + virtual QString adapterType() const = 0; + virtual QString pluginType() const = 0; + + // 几何属性 + virtual QRectF bounds() const = 0; + virtual void setBounds(const QRectF &bounds) = 0; + + // 绘制 + virtual void paint(QPainter *painter, + const QRectF &bounds) = 0; + + // 形状 + virtual QPainterPath shape() const = 0; + + // 属性访问 + virtual QVariant property(const QString &key) const = 0; + virtual void setProperty(const QString &key, const QVariant &value) = 0; + + // 操作 + virtual void move(const QPointF &delta) = 0; + virtual void resize(const QRectF &newBounds) = 0; + + // 状态 + virtual bool isSelected() const = 0; + virtual void setSelected(bool selected) = 0; + + // 序列化 + virtual QVariantMap saveState() const = 0; + virtual bool loadState(const QVariantMap &state) = 0; +signals: + void boundsChanged(const QRectF &newBounds); + void propertyChanged(const QString &key, const QVariant &value); + void selectionChanged(bool selected); + void adapterChanged(); +}; diff --git a/common/include/pluginCommon/iShapeFactory.h b/common/include/pluginCommon/iShapeFactory.h new file mode 100644 index 0000000..ff15779 --- /dev/null +++ b/common/include/pluginCommon/iShapeFactory.h @@ -0,0 +1,24 @@ +//IShapeFactory.h +#pragma once + +#include "iCanvasItem.h" +#include "iPlugin.h" +#include + +// 形状工厂接口(纯接口,不用于插件) +class IShapeFactory +{ +public: + virtual ~IShapeFactory() = default; + + // 创建形状 + virtual ICanvasItem* create(const QString &shapeId) = 0; + + // 查询 + virtual QStringList availableShapes() const = 0; + virtual bool contains(const QString &shapeId) const = 0; + + // 获取形状信息 + virtual ShapeDescriptor shapeDescriptor(const QString &shapeId) const = 0; + virtual QIcon shapeIcon(const QString &shapeId) const = 0; +}; diff --git a/common/source/baseProperty.cpp b/common/source/baseProperty.cpp index dac202b..7cfbc62 100644 --- a/common/source/baseProperty.cpp +++ b/common/source/baseProperty.cpp @@ -60,6 +60,17 @@ BaseModelProperty::BaseModelProperty(QObject* parent) BaseModelProperty::~BaseModelProperty() { +} + +/**************************图形类********************************/ +GraphicProperty::GraphicProperty(QObject* parent) + : ModelProperty(parent) +{ + +} +GraphicProperty::~GraphicProperty() +{ + } /****************************工程模****************************/ diff --git a/diagramCavas/CMakeLists.txt b/diagramCavas/CMakeLists.txt index 5a03eff..f300f69 100644 --- a/diagramCavas/CMakeLists.txt +++ b/diagramCavas/CMakeLists.txt @@ -53,14 +53,17 @@ set(DIAGRAMCAVAS_HEADER_FILES include/createHMIdlg.h include/graphicsDataModel/baseModel.h include/graphicsDataModel/fixedPortsModel.h - include/graphicsItem/graphicsBaseItem.h include/graphicsItem/graphicsItemGroup.h #include/graphicsItem/graphicsPolygonItem.h include/graphicsItem/handleRect.h include/graphicsItem/handleText.h - include/graphicsItem/itemControlHandle.h include/graphicsItem/itemPort.h include/graphicsItem/electricBayItem.h + include/graphicsItem/itemControlHandle.h + include/graphicsItem/graphicsBaseItem.h + + include/graphicsItem/pluginItemFactory.h + include/graphicsItem/pluginItemWrapper.h include/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h include/graphicsItem/functionModelItem/electricFunctionModelPortItem.h @@ -83,6 +86,7 @@ set(DIAGRAMCAVAS_HEADER_FILES include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h include/baseModelItem/electricBaseModelSvgItem.h include/baseModelItem/electricBaseModelLineItem.h @@ -177,14 +181,17 @@ set(DIAGRAMCAVAS_SOURCE_FILES source/createHMIdlg.cpp source/graphicsDataModel/baseModel.cpp source/graphicsDataModel/fixedPortsModel.cpp - source/graphicsItem/graphicsBaseItem.cpp source/graphicsItem/graphicsItemGroup.cpp #source/graphicsItem/graphicsPolygonItem.cpp source/graphicsItem/handleRect.cpp source/graphicsItem/handleText.cpp - source/graphicsItem/itemControlHandle.cpp source/graphicsItem/itemPort.cpp source/graphicsItem/electricBayItem.cpp + source/graphicsItem/itemControlHandle.cpp + source/graphicsItem/graphicsBaseItem.cpp + + source/graphicsItem/pluginItemFactory.cpp + source/graphicsItem/pluginItemWrapper.cpp source/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.cpp source/graphicsItem/functionModelItem/electricFunctionModelPortItem.cpp @@ -207,6 +214,7 @@ set(DIAGRAMCAVAS_SOURCE_FILES source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.cpp source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.cpp source/graphicsItem/functionModelItem/graphicsFunctionModelItem.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.cpp source/baseModelItem/electricBaseModelSvgItem.cpp source/baseModelItem/electricBaseModelLineItem.cpp @@ -291,6 +299,7 @@ target_include_directories(diagramCavas PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inclu target_link_libraries(diagramCavas PRIVATE diagramUtils) target_link_libraries(diagramCavas PRIVATE diagramCommunication) +target_link_libraries(diagramCavas PRIVATE pluginManager) target_link_libraries(diagramCavas PUBLIC PropertyEditor) target_compile_definitions(diagramCavas diff --git a/diagramCavas/include/graphicsDataModel/fixedPortsModel.h b/diagramCavas/include/graphicsDataModel/fixedPortsModel.h index 5dfbdbd..646b2e2 100644 --- a/diagramCavas/include/graphicsDataModel/fixedPortsModel.h +++ b/diagramCavas/include/graphicsDataModel/fixedPortsModel.h @@ -48,15 +48,20 @@ public: ~FixedPortsModel(); public: QMap allNodePos() const; + QMap allGraphicItemPos() const; //所有图形对象位置 QVector allConnectionProperty(); QMap& allItems(); + QMap& allGraphicItems() {return _graphicItem;} bool addNodeItem(QUuid uuid,GraphicsFunctionModelItem*); + bool addGraphicItem(QUuid uuid,GraphicsFunctionModelItem*); QString addNodeItem(QUuid id,QPointF pos,double width = 0,double height = 0,double rotate = 0); + QString addGraphicItem(QUuid id,QString name,QPointF pos,int type,QString sContent = "",double width = 0,double height = 0,double rotate = 0); //0图形1文字 GraphicsFunctionModelItem* nodeItem(QUuid uuid); BaseProperty* addNodeData(QUuid id,int type,QString name,QString modelName); //对应component数据,一个data可对应多个item(id,类型,名称,工程模名) void loadNodeDataFromDataBase(); //从数据库加载数据 QString addConnectLline(QUuid lineId,QUuid srcId,QUuid destId,QUuid srcPort,QUuid destPort); void deleteNodeItem(GraphicsFunctionModelItem*); + void deleteGraphicItem(GraphicsFunctionModelItem*); //QJsonObject saveNode(QUuid const) const; void saveNode(int nPageId); void setScene(DesignerScene* p){_scene = p;} @@ -79,7 +84,9 @@ public: void insertProjectModelName(QString,QString); //插入工程模类型(生成工程模时调用) void showProjectIconSettingDlg(GraphicsFunctionModelItem*); //显示工程模图标设置(设置使用图标) void updateItemIcon(QString sMeta,QString sModel,QMap,QString sIndex = "",int type = 0,int slot = 0); //更新指定模型的图标 sIndex:索引下标,为空全部更新 sTemplate模板名 type基础类型 slot HMI资源中的槽位 + void updateCustomItemIcon(QUuid itemId,QMap mapData); //更新graphic对象的图片 void updateModelIcon(QString sMeta,QString sModel,QMap,QString sIndex = ""); //更新某类模型的所有图标 + void updateCustomById(QUuid itemId,QMap); //更新指定custom对象的图片 /*************************间隔*****************************/ void addBayItem(QUuid,ModelFunctionType = ModelFunctionType::ProjectModel); bool addBayItem(QUuid,ElectricBayItem*,ModelFunctionType = ModelFunctionType::ProjectModel); @@ -116,8 +123,11 @@ public: QMap>& getMonitorDisplaySetting(){return m_monitorDisplaySetting;} QList& getHMIimageRef(){return _HMIimageRef;} + QList& getHMICustomImageRef(){return _HMICustomImageRef;} int imageRefExist(QString model,QByteArray hash256); + int customImageRefExist(QUuid uid,QByteArray hash256); void updateHMIRef(QUuid hmiId,QString model,QByteArray hash256,int slot); //更新img引用 + void updateHMICustomRef(QUuid hmiId,QString model,QUuid itemId,QByteArray hash256); //更新customImg引用 /************************数据显示*************************/ void setCurItemPropertyDlg(ItemPropertyDlg* p) {m_curPropertyDlg = p;} ItemPropertyDlg* getCurItemPropertyDlg() {return m_curPropertyDlg;} @@ -142,6 +152,7 @@ public: QMap getHMIItems(){return _nodeItem;} QMap getProjectBayItems(){return _bayItem;} + QMap getGraphicItems(){return _graphicItem;} void setPageState(int n) {_pageState = n;} int getPageState() {return _pageState;} @@ -165,6 +176,7 @@ private: QMap _nodeItem; //工程模对象 QMap _bayItem; //间隔对象 + QMap _graphicItem; //图形对象(图片,文字等) QString _pageName; QPointer _cavas; @@ -193,6 +205,7 @@ private: QString _lastSaveTime; //页面上次保存时间 QList _HMIimageRef; //当前HMI中图片的引用关系 + QList _HMICustomImageRef; //当前HMI中图片项的引用 public: static bool _dataInitialised; }; diff --git a/diagramCavas/include/graphicsItem/electricBayItem.h b/diagramCavas/include/graphicsItem/electricBayItem.h index 1ef1c22..170fda7 100644 --- a/diagramCavas/include/graphicsItem/electricBayItem.h +++ b/diagramCavas/include/graphicsItem/electricBayItem.h @@ -1,7 +1,7 @@ #ifndef ELECBAYITEM_H #define ELECBAYITEM_H -#include "graphicsBaseItem.h" +#include "graphicsItem/graphicsBaseItem.h" #include "baseProperty.h" class ElectricBayItem :public GraphicsNonStandardItem diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h new file mode 100644 index 0000000..392182a --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMIMAGE_H +#define ELECTRICFUNCTIONMODELSVGITEMIMAGE_H + +/*****************自定义图形类*******************/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemImage :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemImage(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemImage(); + virtual void setImage_1(QFileInfo) override; +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + void initial(); +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h b/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h index 384d3cd..d0c91ad 100644 --- a/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h +++ b/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h @@ -1,7 +1,7 @@ #ifndef GRAPHICSFUNCTIONMODELITEM_H #define GRAPHICSFUNCTIONMODELITEM_H -#include "../graphicsBaseItem.h" +#include "graphicsItem/graphicsBaseItem.h" class GraphicsFunctionModelItem : public GraphicsProjectModelItem //功能模item { diff --git a/diagramCavas/include/graphicsItem/graphicsItemGroup.h b/diagramCavas/include/graphicsItem/graphicsItemGroup.h index 85ecdfd..1b7ff03 100644 --- a/diagramCavas/include/graphicsItem/graphicsItemGroup.h +++ b/diagramCavas/include/graphicsItem/graphicsItemGroup.h @@ -1,7 +1,7 @@ #ifndef GRAPHICSITEMGROUP_H #define GRAPHICSITEMGROUP_H -#include "graphicsBaseItem.h" +#include "graphicsItem/graphicsBaseItem.h" class GraphicsItemGroup : public QObject, public AbstractShapeType diff --git a/diagramCavas/include/graphicsItem/graphicsPolygonItem.h b/diagramCavas/include/graphicsItem/graphicsPolygonItem.h index 1fc9ab7..a8090e5 100644 --- a/diagramCavas/include/graphicsItem/graphicsPolygonItem.h +++ b/diagramCavas/include/graphicsItem/graphicsPolygonItem.h @@ -1,7 +1,7 @@ #ifndef GRAPHICSPOLYGONITEM_H #define GRAPHICSPOLYGONITEM_H -#include "graphicsBaseItem.h" +#include "graphicsItem/graphicsBaseItem.h" class GraphicPolygonItem : public GraphicsProjectModelItem { diff --git a/diagramCavas/include/graphicsItem/pluginItemFactory.h b/diagramCavas/include/graphicsItem/pluginItemFactory.h new file mode 100644 index 0000000..63dea09 --- /dev/null +++ b/diagramCavas/include/graphicsItem/pluginItemFactory.h @@ -0,0 +1,53 @@ +// src/canvas/PluginItemFactory.h +#pragma once + +#include "export.hpp" +#include "functionModelItem/graphicsFunctionModelItem.h" +#include + +class PluginManager; +class ICanvasItem; + +class DIAGRAM_DESIGNER_PUBLIC PluginItemFactory : public QObject +{ + Q_OBJECT + +public: + static PluginItemFactory* instance(); + + // 删除拷贝构造函数和赋值运算符 + PluginItemFactory(const PluginItemFactory&) = delete; + PluginItemFactory& operator=(const PluginItemFactory&) = delete; + + // 设置插件管理器 + void setPluginManager(PluginManager *manager); + PluginManager* pluginManager() const; + + // 创建图形项 + GraphicsFunctionModelItem* createItem(const QString &shapeId, + QGraphicsItem *parent = nullptr); + + // 从插件项创建图形项 + GraphicsFunctionModelItem* createItemFromPlugin(ICanvasItem *pluginItem, + QGraphicsItem *parent = nullptr); + +signals: + void itemCreated(GraphicsFunctionModelItem *item); + void creationFailed(const QString &shapeId, const QString &error); + +private: + // 内联定义Private类 + class Private + { + public: + PluginManager *pluginManager = nullptr; + + Private() = default; + ~Private() = default; + }; + + explicit PluginItemFactory(QObject *parent = nullptr); + ~PluginItemFactory() = default; // QScopedPointer自动管理内存 + + QScopedPointer d; +}; diff --git a/diagramCavas/include/graphicsItem/pluginItemWrapper.h b/diagramCavas/include/graphicsItem/pluginItemWrapper.h new file mode 100644 index 0000000..702145d --- /dev/null +++ b/diagramCavas/include/graphicsItem/pluginItemWrapper.h @@ -0,0 +1,52 @@ +//pluginItemWrapper.h +#pragma once + +#include "functionModelItem/graphicsFunctionModelItem.h" +#include "pluginCommon/iCanvasItem.h" +#include + +// 内部包装器,不暴露给外部 +class PluginItemWrapper : public GraphicsFunctionModelItem +{ + Q_OBJECT + +public: + explicit PluginItemWrapper(ICanvasItem *pluginItem, QGraphicsItem *parent = nullptr); + virtual ~PluginItemWrapper(); + + // 克隆 + virtual GraphicsFunctionModelItem* clone() const override; + + // 绘图 + virtual void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget = nullptr) override; + + virtual QRectF boundingRect() const override; + virtual QPainterPath shape() const override; + + // 几何操作 + virtual void updateCoordinate() override; + virtual void resize(int handle, double dx, double dy, const QPointF &pos) override; + virtual void move(const QPointF &delta) override; + + // 获取插件项 + ICanvasItem* pluginItem() const; + +protected: + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + +private: + ICanvasItem *m_pluginItem = nullptr; + QRectF m_cachedBounds; + + void updateCache(); + void connectSignals(); + QVariantMap saveState() const; + bool loadStateIntoPluginItem(ICanvasItem *pluginItem, const QVariantMap &state) const; + void copyPropertiesFrom(const PluginItemWrapper *source); + +private slots: + void onPluginBoundsChanged(const QRectF &newBounds); + void onPluginPropertyChanged(const QString &key, const QVariant &value); +}; diff --git a/diagramCavas/include/util/editingSelector.h b/diagramCavas/include/util/editingSelector.h index 2f69313..de32e38 100644 --- a/diagramCavas/include/util/editingSelector.h +++ b/diagramCavas/include/util/editingSelector.h @@ -11,7 +11,7 @@ #include "baseSelector.h" //#include "global.h" -#include +#include "graphicsItem/graphicsBaseItem.h" class EditingSelector : public BaseSelector diff --git a/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp b/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp index 74bddce..669033c 100644 --- a/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp +++ b/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp @@ -1,5 +1,4 @@ #include "baseModelItem/electricBaseModelSvgItem.h" -#include "graphicsItem/itemControlHandle.h" #include #include diff --git a/diagramCavas/source/designerScene.cpp b/diagramCavas/source/designerScene.cpp index 6a2fa1e..040f261 100644 --- a/diagramCavas/source/designerScene.cpp +++ b/diagramCavas/source/designerScene.cpp @@ -144,8 +144,6 @@ void DesignerScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) if(m_pDrawingPanel) { DiagramMode mode = m_pDrawingPanel->getMode(); - if(mode == DM_run) - return; m_pDrawingPanel->selectorManager()->getWorkingSelector()->dragEnterEvent(event, this,mode); update(); } @@ -158,8 +156,6 @@ void DesignerScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) if(m_pDrawingPanel) { DiagramMode mode = m_pDrawingPanel->getMode(); - if(mode == DM_run) - return; m_pDrawingPanel->selectorManager()->getWorkingSelector()->dragMoveEvent(event, this,mode); update(); } @@ -172,8 +168,6 @@ void DesignerScene::dropEvent(QGraphicsSceneDragDropEvent *event) if(m_pDrawingPanel) { DiagramMode mode = m_pDrawingPanel->getMode(); - if(mode == DM_run) - return; m_pDrawingPanel->selectorManager()->getWorkingSelector()->dropEvent(event, this,mode); update(); } diff --git a/diagramCavas/source/diagramCavas.cpp b/diagramCavas/source/diagramCavas.cpp index 5ad9411..172b4cf 100644 --- a/diagramCavas/source/diagramCavas.cpp +++ b/diagramCavas/source/diagramCavas.cpp @@ -223,6 +223,14 @@ void DiagramCavas::onSignal_savePage() DataBase::GetInstance()->insertHMIimageRefBatch(imgRefLst); //全部重新插入 } + QList& imgCustomRefLst = pMonitor->getModelController()->getHMICustomImageRef(); //获取当前HMI所有custom引用 + if(imgCustomRefLst.size()){ + auto lstRef = DataBase::GetInstance()->getHMICustomRefAll(imgCustomRefLst.first().hmiId); + if(lstRef.size()) + DataBase::GetInstance()->removeHMICustomRefAll(imgCustomRefLst.first().hmiId); + DataBase::GetInstance()->insertHMICustomRefBatch(imgCustomRefLst); + } + if(DataBase::GetInstance()->getHMIdByName(pEntity->name()).isNull()) //不存在,创建 DataBase::GetInstance()->insertHMI(QUuid(pEntity->id()),pEntity->name(),pEntity->name(),pMonitor->getDiagramInfo()); else diff --git a/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp b/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp index efb3d34..8d75690 100644 --- a/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp +++ b/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp @@ -19,6 +19,7 @@ #include "graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h" #include "graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h" #include "graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h" #include "graphicsItem/itemPort.h" #include "designerScene.h" @@ -41,7 +42,6 @@ #include "projectModelManager.h" #include "projectIconSetting.h" #include "monitorPanel.h" -#include "designerView.h" #include "uiCommunicationBus.h" #include "instance/dataAccessor.h" #include "graphicsItem/handleText.h" @@ -102,6 +102,23 @@ QMap FixedPortsModel::allNodePos() const return map; } +QMap FixedPortsModel::allGraphicItemPos() const +{ + QMap map; + for(auto pItem:_graphicItem) + { + ItemPageInfo info; + double dWidth = pItem->boundingRect().width(); + double dHeight = pItem->boundingRect().height(); + info.pos = pItem->scenePos()/*+QPointF(dWidth*0.5,dHeight*0.5)*/; + info.dWidth = dWidth; + info.dHeight = dHeight; + info.dRotate = pItem->rotation(); + map.insert(pItem->itemId(),info); + } + return map; +} + QVector FixedPortsModel::allConnectionProperty() { QVector vec; @@ -155,9 +172,77 @@ bool FixedPortsModel::addNodeItem(QUuid uuid,GraphicsFunctionModelItem* pItem) } } +bool FixedPortsModel::addGraphicItem(QUuid uuid,GraphicsFunctionModelItem* pItem) +{ + if(_graphicItem.contains(uuid)) + return false; + else + { + pItem->setHandle(this); + _graphicItem.insert(uuid,pItem); + return true; + } +} + +QString FixedPortsModel::addGraphicItem(QUuid id,QString name,QPointF pos,int type,QString sContent,double width,double height,double rotate) +{ + GraphicProperty* pro = nullptr; + GraphicsFunctionModelItem* item = nullptr; + if(_graphicItem.contains(id)) + return "exist"; + double dX = 0.0; + double dY = 0.0; + if(type == 0){ //图像 + + if(width == 0 && height == 0){ + dX = 30; + dY = 30; + } + else{ + dX = width; + dY = height; + } + item = new ElectricFunctionModelSvgItemImage(QRect(-dX*0.5, -dY*0.5, dX, dY)); + item->setItemType(GIT_Image); + pro = new GraphicProperty(item); + pro->setGraphicsType(GIT_Image); + pro->setModelName("customImage"); + pro->setGraphicType(0); + //pro->setContent(); + } + else if(type == 1){ //文字 + //pro = new ModelProperty(); + } + + if(item) + { + item->setItemId(id); + pro->setUuid(id); + pro->setType(-1); + item->editShape(0, pos); + item->setPos(pos); + item->setRotation(rotate); + _scene->addItem(item); + item->addPoint(pos); + item->setProperty(pro); //绑定模型 + //item->updateByProperty(); //使用模型更新自身 + item->setHandle(this); + _graphicItem.insert(id,item); + if(name.isEmpty()){ //如果不给名称则给个默认值 + pro->setName("item_"+QString::number(_graphicItem.size())); + } + else{ + pro->setName(name); + } + + } + return pro?pro->name():"err"; +} + QString FixedPortsModel::addNodeItem(QUuid id,QPointF pos,double width,double height,double rotate) { //todo:load图形时必有拓扑实体,关联到对应的entity + BaseProperty* pro = nullptr; GraphicsFunctionModelItem* item = nullptr; QMap mapData = BasePropertyManager::instance().getEntityData(); //加载的图形必定关联component(todo:完善判断条件,如判断拓扑节点) @@ -905,6 +990,15 @@ void FixedPortsModel::deleteNodeItem(GraphicsFunctionModelItem* pItem) } } +void FixedPortsModel::deleteGraphicItem(GraphicsFunctionModelItem* pItem) +{ + for(auto iter = _graphicItem.begin();iter != _graphicItem.end();++iter){ + if(iter.value() == pItem){ + delete _graphicItem.take(iter.key()); + } + } +} + void FixedPortsModel::saveNode(int nPageId) { QList lstGrid = DataBase::GetInstance()->getAllGrid(); @@ -1933,6 +2027,24 @@ void FixedPortsModel::updateItemIcon(QString sMeta,QString sModel,QMap mapData) +{ + if(mapData.size() == 1){ //单项设置 + QByteArray sha256Hash = QCryptographicHash::hash(mapData.first(), QCryptographicHash::Sha256).toHex(); + QMap& mapResource = ProjectModelManager::instance().getHMIimageMap(); //更新总HMI资源 + if(!mapResource.contains(sha256Hash)){ //库中不存在则新建 + HMIImageInfo imageInfo; + imageInfo.baseType = -1; //未定义类型 + imageInfo.imageName = mapData.firstKey(); + imageInfo.hash256 = sha256Hash; + imageInfo.svgData = mapData.first(); + mapResource.insert(sha256Hash,imageInfo); + } + updateHMICustomRef(QUuid(_widget->getEntity()->id()),"customImage",itemId,sha256Hash); //更新本页的custom引用 + } + //updateCustomById(itemId,mapData); //不需要更新其他custom对象 +} + void FixedPortsModel::updateModelIcon(QString sMeta,QString sModel,QMap mapData,QString sIndex) { for(auto &pItem:_nodeItem){ @@ -1952,6 +2064,24 @@ void FixedPortsModel::updateModelIcon(QString sMeta,QString sModel,QMap mapData) +{ + for(auto &pItem:_graphicItem){ + auto pro = pItem->getProperty(); + + if(pro->uuid() == itemId){ + auto pI = dynamic_cast(pItem); + auto pG = dynamic_cast(pItem); + if(pI){ + pI->updateMapSvg(mapData,""); + } + if(pG){ + pG->updateMapSvg(mapData,""); + } + } + } +} + void FixedPortsModel::addBayItem(QUuid id,ModelFunctionType tpe) { if(tpe == ModelFunctionType::BaseModel){ @@ -2389,6 +2519,16 @@ int FixedPortsModel::imageRefExist(QString model,QByteArray hash256) return -1; } +int FixedPortsModel::customImageRefExist(QUuid uid,QByteArray hash256) +{ + for(auto& info:_HMICustomImageRef) + { + if(uid == info.itemId && hash256 == info.hash256) + return _HMICustomImageRef.indexOf(info); + } + return -1; +} + void FixedPortsModel::updateHMIRef(QUuid hmiId,QString model,QByteArray hash256,int slot) { HMIImageRef ref; @@ -2404,3 +2544,19 @@ void FixedPortsModel::updateHMIRef(QUuid hmiId,QString model,QByteArray hash256, _HMIimageRef.append(ref); } } + +void FixedPortsModel::updateHMICustomRef(QUuid hmiId,QString model,QUuid itemId,QByteArray hash256) +{ + HMICustomImageRef ref; + ref.hmiId = hmiId; + ref.model = model; + ref.itemId = itemId; + ref.hash256 = hash256; + int index = customImageRefExist(itemId,hash256); + if(index != -1){ //已存在,替换 + _HMICustomImageRef[index] = ref; + } + else{ //新建 + _HMICustomImageRef.append(ref); + } +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp index 264946f..497f2d5 100644 --- a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp @@ -1,5 +1,4 @@ #include "graphicsItem/functionModelItem/electricFunctionModelSvgItem.h" -#include "graphicsItem/itemControlHandle.h" #include #include diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.cpp new file mode 100644 index 0000000..592b24b --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.cpp @@ -0,0 +1,71 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemImage.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemImage::ElectricFunctionModelSvgItemImage(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemImage::~ElectricFunctionModelSvgItemImage() +{ + +} + +void ElectricFunctionModelSvgItemImage::setImage_1(QFileInfo info) +{ + QByteArray svgData; + QFile svgFile(info.absoluteFilePath()); + + if (svgFile.open(QIODevice::ReadOnly)) { + svgData = svgFile.readAll(); + svgFile.close(); + } else { + qDebug() << "can't open imgfile" << svgFile.errorString(); + } + + QMap mapData; + if(!svgData.isEmpty()){ + mapData["customImage"] = svgData; + updateMapSvg(mapData,"customImage"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + QUuid itemId; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + itemId = _property->uuid(); + } + + if(_pHandle) + _pHandle->updateCustomItemIcon(itemId,mapData); + + GraphicsBaseItem::setImage_1(info); +} + + +void ElectricFunctionModelSvgItemImage::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemImage::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("es")) + _itemImgIndex.append("es"); +} + diff --git a/diagramCavas/source/graphicsItem/pluginItemFactory.cpp b/diagramCavas/source/graphicsItem/pluginItemFactory.cpp new file mode 100644 index 0000000..2572534 --- /dev/null +++ b/diagramCavas/source/graphicsItem/pluginItemFactory.cpp @@ -0,0 +1,71 @@ +// src/canvas/PluginItemFactory.cpp +#include "diagramCavas/include/graphicsItem/pluginItemFactory.h" +#include "include/graphicsItem/pluginItemWrapper.h" +#include "pluginManager/include/pluginManager.h" +#include + +PluginItemFactory* PluginItemFactory::instance() +{ + static PluginItemFactory instance; + return &instance; +} + +PluginItemFactory::PluginItemFactory(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +void PluginItemFactory::setPluginManager(PluginManager *manager) +{ + d->pluginManager = manager; +} + +GraphicsFunctionModelItem* PluginItemFactory::createItem(const QString &shapeId, + QGraphicsItem *parent) +{ + if (!d->pluginManager) { + qWarning() << "PluginManager not set"; + emit creationFailed(shapeId, "PluginManager not set"); + return nullptr; + } + + // 1. 通过插件管理器创建原始插件项 + ICanvasItem *pluginItem = d->pluginManager->createItem(shapeId); + if (!pluginItem) { + qWarning() << "Failed to create plugin item:" << shapeId; + emit creationFailed(shapeId, "Plugin item creation failed"); + return nullptr; + } + + // 2. 创建包装器 + GraphicsFunctionModelItem *item = createItemFromPlugin(pluginItem, parent); + + if (item) { + emit itemCreated(item); + } + + return item; +} + +GraphicsFunctionModelItem* PluginItemFactory::createItemFromPlugin( + ICanvasItem *pluginItem, QGraphicsItem *parent) +{ + if (!pluginItem) { + return nullptr; + } + + // 创建包装器 + PluginItemWrapper *wrapper = new PluginItemWrapper(pluginItem, parent); + + if (!wrapper) { + qWarning() << "PluginItemFactory: Failed to create wrapper"; + return nullptr; + } + + // 设置基本属性 + wrapper->setItemType(GIT_PluginItem); + wrapper->setName(pluginItem->displayName()); + + return wrapper; +} diff --git a/diagramCavas/source/graphicsItem/pluginItemWrapper.cpp b/diagramCavas/source/graphicsItem/pluginItemWrapper.cpp new file mode 100644 index 0000000..6abaf2a --- /dev/null +++ b/diagramCavas/source/graphicsItem/pluginItemWrapper.cpp @@ -0,0 +1,451 @@ +// pluginItemWrapper.cpp +#include "graphicsItem/pluginItemWrapper.h" +#include "pluginManager/include/pluginManager.h" +#include +#include +#include + +PluginItemWrapper::PluginItemWrapper(ICanvasItem *pluginItem, QGraphicsItem *parent) + : GraphicsFunctionModelItem(parent) + , m_pluginItem(pluginItem) +{ + if (!m_pluginItem) { + qWarning() << "PluginItemWrapper: pluginItem is null!"; + return; + } + + // 设置类型 + m_Itemtype = GIT_PluginItem; + + // 设置初始名称 + setName(m_pluginItem->displayName()); + + // 连接信号 + connectSignals(); + + // 初始化缓存 + updateCache(); + + // 更新坐标 + updateCoordinate(); + + qDebug() << "PluginItemWrapper created for:" << m_pluginItem->typeId(); +} + +PluginItemWrapper::~PluginItemWrapper() +{ + // 删除插件项 + if (m_pluginItem) { + m_pluginItem->deleteLater(); + } +} + +GraphicsFunctionModelItem* PluginItemWrapper::clone() const +{ + if (!m_pluginItem) { + return nullptr; + } + + // 获取场景中的PluginManager + PluginManager *manager = PluginManager::instance(); + if (!manager) { + qWarning() << "PluginManager not available for cloning"; + return nullptr; + } + + // 获取插件类型 + QString pluginType = m_pluginItem->typeId(); + + // 保存当前状态 + QVariantMap state = saveState(); + + // 创建新的插件项 + ICanvasItem *newPluginItem = manager->createItem(pluginType); + if (!newPluginItem) { + qWarning() << "Failed to create plugin item for cloning:" << pluginType; + return nullptr; + } + + // 恢复状态 + if (!loadStateIntoPluginItem(newPluginItem, state)) { + qWarning() << "Failed to restore state for cloned item"; + delete newPluginItem; + return nullptr; + } + + // 创建新的包装器 + PluginItemWrapper *clone = new PluginItemWrapper(newPluginItem, parentItem()); + + // 复制变换属性 + clone->setPos(pos()); + clone->setRotation(rotation()); + clone->setScale(scale()); + clone->setZValue(zValue()); + + // 复制其他属性 + clone->copyPropertiesFrom(this); + + qDebug() << "Cloned plugin item:" << pluginType; + return clone; +} + +void PluginItemWrapper::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + if (!painter || !m_pluginItem) { + return; + } + + painter->save(); + + // 设置抗锯齿 + painter->setRenderHint(QPainter::Antialiasing, true); + + // 让插件绘制 + m_pluginItem->draw(painter, m_cachedBounds); + + // 如果选中,绘制控制框 + if (option && (option->state & QStyle::State_Selected)) { + painter->setPen(QPen(QColor(0, 120, 215), 1, Qt::DashLine)); + painter->setBrush(Qt::NoBrush); + + // 绘制选中框 + QRectF selectRect = m_cachedBounds.adjusted(-2, -2, 2, 2); + painter->drawRect(selectRect); + + // 绘制控制点 + const qreal handleSize = 6.0; + QRectF handleRect(-handleSize/2, -handleSize/2, handleSize, handleSize); + + QBrush handleBrush(QColor(0, 120, 215)); + QPen handlePen(Qt::white, 1); + + // 绘制8个控制点 + QPointF corners[] = { + m_cachedBounds.topLeft(), + m_cachedBounds.topRight(), + m_cachedBounds.bottomRight(), + m_cachedBounds.bottomLeft(), + QPointF(m_cachedBounds.center().x(), m_cachedBounds.top()), + QPointF(m_cachedBounds.right(), m_cachedBounds.center().y()), + QPointF(m_cachedBounds.center().x(), m_cachedBounds.bottom()), + QPointF(m_cachedBounds.left(), m_cachedBounds.center().y()) + }; + + for (const QPointF &corner : corners) { + painter->save(); + painter->translate(corner); + painter->setPen(handlePen); + painter->setBrush(handleBrush); + painter->drawEllipse(handleRect); + painter->restore(); + } + } + + painter->restore(); +} + +QRectF PluginItemWrapper::boundingRect() const +{ + qreal margin = 4.0; // 为控制点留出空间 + return m_cachedBounds.adjusted(-margin, -margin, margin, margin); +} + +QPainterPath PluginItemWrapper::shape() const +{ + QPainterPath path; + + if (!m_pluginItem) { + return path; + } + + // 创建简单的矩形路径 + // 插件可以提供更精确的形状,这里简化处理 + path.addRect(m_cachedBounds); + + return path; +} + +void PluginItemWrapper::updateCoordinate() +{ + if (!m_pluginItem) { + return; + } + + // 更新基类属性 + QRectF bounds = m_pluginItem->bounds(); + m_dWidth = bounds.width(); + m_dHeight = bounds.height(); + + // 更新包围框 + m_boundingRect = bounds; + + // 更新变换 + setTransformOriginPoint(bounds.center()); + + // 调用基类更新 + GraphicsFunctionModelItem::updateCoordinate(); + + // 更新句柄位置 + updateHandles(); +} + +void PluginItemWrapper::resize(int handle, double dx, double dy, const QPointF &pos) +{ + if (!m_pluginItem) { + return; + } + + QRectF bounds = m_pluginItem->bounds(); + QRectF newBounds = bounds; + + // 根据控制点调整大小 + switch (handle) { + case H_leftTop: + newBounds.setTopLeft(newBounds.topLeft() + QPointF(dx, dy)); + break; + case H_top: + newBounds.setTop(newBounds.top() + dy); + break; + case H_rightTop: + newBounds.setTopRight(newBounds.topRight() + QPointF(dx, dy)); + break; + case H_right: + newBounds.setRight(newBounds.right() + dx); + break; + case H_rightBottom: + newBounds.setBottomRight(newBounds.bottomRight() + QPointF(dx, dy)); + break; + case H_bottom: + newBounds.setBottom(newBounds.bottom() + dy); + break; + case H_leftBottom: + newBounds.setBottomLeft(newBounds.bottomLeft() + QPointF(dx, dy)); + break; + case H_left: + newBounds.setLeft(newBounds.left() + dx); + break; + default: + // 不是控制点,直接移动 + move(QPointF(dx, dy)); + return; + } + + // 确保大小有效 + if (newBounds.width() < 10) { + newBounds.setWidth(10); + } + if (newBounds.height() < 10) { + newBounds.setHeight(10); + } + + if (newBounds.isValid() && newBounds != bounds) { + m_pluginItem->setBounds(newBounds); + } +} + +void PluginItemWrapper::move(const QPointF &delta) +{ + if (!m_pluginItem || delta.isNull()) { + return; + } + + QRectF bounds = m_pluginItem->bounds(); + bounds.translate(delta); + m_pluginItem->setBounds(bounds); +} + +ICanvasItem* PluginItemWrapper::pluginItem() const +{ + return m_pluginItem; +} + +QVariant PluginItemWrapper::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (!m_pluginItem) { + return GraphicsFunctionModelItem::itemChange(change, value); + } + + switch (change) { + case ItemSelectedChange: + // 选中状态变化 + if (value.toBool()) { + // 选中时的处理 + setHandleVisible(true); + } else { + // 取消选中的处理 + setHandleVisible(false); + } + break; + + case ItemPositionChange: { + // 位置变化 + QPointF newPos = value.toPointF(); + QPointF delta = newPos - pos(); + + // 更新插件项的位置 + QRectF bounds = m_pluginItem->bounds(); + bounds.translate(delta); + m_pluginItem->setBounds(bounds); + + return newPos; + } + + case ItemRotationChange: + // 旋转变化 + // 插件项可能需要处理旋转 + break; + + case ItemScaleChange: + // 缩放变化 + // 插件项可能需要处理缩放 + break; + + case ItemZValueChange: + // Z值变化 + break; + + default: + break; + } + + return GraphicsFunctionModelItem::itemChange(change, value); +} + +void PluginItemWrapper::updateCache() +{ + if (m_pluginItem) { + m_cachedBounds = m_pluginItem->bounds(); + } +} + +void PluginItemWrapper::connectSignals() +{ + if (!m_pluginItem) { + return; + } + + // 使用旧式语法连接 + connect(m_pluginItem, SIGNAL(boundsChanged(QRectF)), + this, SLOT(onPluginBoundsChanged(QRectF))); + + connect(m_pluginItem, SIGNAL(propertyChanged(QString, QVariant)), + this, SLOT(onPluginPropertyChanged(QString, QVariant))); +} + +QVariantMap PluginItemWrapper::saveState() const +{ + QVariantMap state; + + if (!m_pluginItem) { + return state; + } + + // 保存基本属性 + state["type"] = m_pluginItem->typeId(); + state["displayName"] = m_pluginItem->displayName(); + + // 保存几何信息 + QRectF bounds = m_pluginItem->bounds(); + state["x"] = bounds.x(); + state["y"] = bounds.y(); + state["width"] = bounds.width(); + state["height"] = bounds.height(); + + // 保存插件特定属性 + // 这里可以保存插件的关键属性 + QStringList propertyKeys = {"fillColor", "borderColor", "borderWidth", "cornerRadius"}; + for (const QString &key : propertyKeys) { + QVariant value = m_pluginItem->property(key); + if (value.isValid()) { + state[key] = value; + } + } + + return state; +} + +bool PluginItemWrapper::loadStateIntoPluginItem(ICanvasItem *pluginItem, const QVariantMap &state) const +{ + if (!pluginItem) { + return false; + } + + // 设置几何信息 + if (state.contains("x") && state.contains("y") && + state.contains("width") && state.contains("height")) { + QRectF bounds( + state["x"].toReal(), + state["y"].toReal(), + state["width"].toReal(), + state["height"].toReal() + ); + + if (bounds.isValid()) { + pluginItem->setBounds(bounds); + } + } + + // 设置插件属性 + QStringList propertyKeys = {"fillColor", "borderColor", "borderWidth", "cornerRadius"}; + for (const QString &key : propertyKeys) { + if (state.contains(key)) { + pluginItem->setProperty(key, state[key]); + } + } + + return true; +} + +void PluginItemWrapper::copyPropertiesFrom(const PluginItemWrapper *source) +{ + if (!source) { + return; + } + + // 复制基类属性 + m_type = source->m_type; + m_pen = source->m_pen; + m_brush = source->m_brush; + m_dWidth = source->m_dWidth; + m_dHeight = source->m_dHeight; + m_boundingRect = source->m_boundingRect; + m_Itemtype = source->m_Itemtype; + + // 复制可见性和使能状态 + setVisible(source->isVisible()); + setEnabled(source->isEnabled()); + + // 复制工具提示 + setToolTip(source->toolTip()); + + // 复制自定义数据 + setData(0, source->data(0)); + + // 生成新的ID + setItemId(QUuid::createUuid()); +} + +void PluginItemWrapper::onPluginBoundsChanged(const QRectF &newBounds) +{ + m_cachedBounds = newBounds; + + // 更新坐标 + updateCoordinate(); + + // 通知变化 + emit itemChanged(); +} + +void PluginItemWrapper::onPluginPropertyChanged(const QString &key, const QVariant &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); + + // 属性变化,需要重绘 + update(); + + // 通知变化 + emit itemChanged(); +} diff --git a/diagramCavas/source/monitorPanel.cpp b/diagramCavas/source/monitorPanel.cpp index ce7f8d2..cdb7be3 100644 --- a/diagramCavas/source/monitorPanel.cpp +++ b/diagramCavas/source/monitorPanel.cpp @@ -48,8 +48,8 @@ void MonitorPanel::initial() createToolBar(); _sideBar = new MonitorSideBarDlg(this); - _hSplitter->addWidget(_sideBar); - _sideBar->show(); + //_hSplitter->addWidget(_sideBar); + //_sideBar->show(); _pConfigDlg = new MonitorConfigDlg(this); _detailAttributeDlg = new MonitorDetailAttributeDlg(this); _displaySettingDlg = new MonitorDisplaySettingDlg(this); @@ -151,6 +151,36 @@ QJsonObject MonitorPanel::getDiagramInfo() } obj["nodes"] = arr; + QJsonArray arrGraphic; + QMap mapGraphicPos = _pModel->allGraphicItemPos(); //graphic item位置 + QMap mapGraphicItem = _pModel->allGraphicItems(); + for(auto pItem:_pModel->allGraphicItems()) + { + ItemPageInfo info; + double dWidth = pItem->boundingRect().width(); + double dHeight = pItem->boundingRect().height(); + QPointF pos = pItem->scenePos(); + double rotation = pItem->rotation(); + + QJsonObject node; + node["id"] = pItem->itemId().toString(); + node["x"] = pos.x(); + node["y"] = pos.y(); + node["width"] = dWidth; + node["height"] = dHeight; + node["rotate"] = rotation; + + GraphicProperty* pPro = dynamic_cast(pItem->getProperty()); + if(pPro){ + node["name"] = pPro->name(); + node["type"] = pPro->getGraphicType(); + node["modeName"] = pPro->modelName(); + node["content"] = pPro->getContent(); + } + arrGraphic.append(node); + } + obj["graphicNode"] = arrGraphic; + QJsonArray arrConnect; QVector vec = _pModel->allConnectionProperty(); for(auto& pPro:vec){ @@ -219,6 +249,49 @@ void MonitorPanel::loadNodes(QJsonObject obj) } } + QMap mapHmiSource = ProjectModelManager::instance().getHMIimageMap(); //获取所有hmi资源 + + QList lstRef = DataBase::GetInstance()->getHMICustomRefAll(QUuid(_pEntity->id())); + _pModel->getHMICustomImageRef() = lstRef; + + QJsonArray graphicNodesJsonArray = obj["graphicNode"].toArray(); + + for (QJsonValueRef nodeJson : graphicNodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QUuid itemId = QUuid(node["id"].toString()); + QString sName = node["name"].toString(); + int nType = node["type"].toInt(); //图形item的类型 0图像1文字 + QString sModeName = node["modeName"].toString(); //模型名,定值 + QString sContent = node["content"].toString(); + double dX = node["x"].toDouble(); + double dY = node["y"].toDouble(); + double dWidth = node["width"].toDouble(); + double dHeight = node["height"].toDouble(); + double dRotate = node["rotate"].toDouble(); + + if(_pModel) + { + QString res = _pModel->addGraphicItem(itemId,sName,QPointF(dX,dY),nType,sModeName,dWidth,dHeight,dRotate); //创建图形对象 + if(res != "err"){ + if(nType == 0){ //图片类型 + QMap mapData; + for(auto& customRef:lstRef){ + if(customRef.itemId == itemId){ + QString sImage = mapHmiSource.value(customRef.hash256).imageName; + QByteArray data = mapHmiSource.value(customRef.hash256).svgData; + mapData.insert(sImage,data); + } + } + if(!mapData.isEmpty()) + _pModel->updateCustomById(itemId,mapData); //更新item引用的图片 + } + else if(nType == 1){ //文字类型 + + } + } + } + } QJsonArray connectArr = obj["connections"].toArray(); for(QJsonValueRef connectJson:connectArr) @@ -341,8 +414,6 @@ void MonitorPanel::loadNodes(QJsonObject obj) break; } - QMap mapHmiSource = ProjectModelManager::instance().getHMIimageMap(); //获取所有hmi资源 - QList lstFirst; for(auto& pOtherItem:_pModel->getProjectBayItems()) diff --git a/diagramCavas/source/util/baseSelector.cpp b/diagramCavas/source/util/baseSelector.cpp index 73a7894..95f84d3 100644 --- a/diagramCavas/source/util/baseSelector.cpp +++ b/diagramCavas/source/util/baseSelector.cpp @@ -572,32 +572,34 @@ void BaseSelector::dragMoveEvent(QGraphicsSceneDragDropEvent *event, DesignerSce void BaseSelector::dropEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode sceneMode) { - if(sceneMode == DM_edit) + if(sceneMode == DM_run) { if (event->mimeData()->hasText()) { QString text = event->mimeData()->text(); - QString uuid = QString::fromLocal8Bit(event->mimeData()->data("application/id")); + QString sType = QString::fromLocal8Bit(event->mimeData()->data("application/x-tooltype")); // 根据拖拽的数据创建相应的图形项并添加到场景中 - QGraphicsTextItem *textItem = new QGraphicsTextItem(text); - textItem->setPos(event->scenePos()); + //QGraphicsTextItem *textItem = new QGraphicsTextItem(text); + //textItem->setPos(event->scenePos()); //addItem(textItem); - event->acceptProposedAction(); - //根据data数据新增拖拽的item - QMap items = _model->allItems(); - if(items.contains(QUuid(uuid))){ - QMessageBox::information(NULL, QString::fromWCharArray(L"提示"), QString::fromWCharArray(L"此对象在当前页已存在")); - return; + int nGraphicType = -1; + if(sType == "image") + nGraphicType = 0; + else if(sType == "text") + nGraphicType = 1; + else + nGraphicType = -1; + + if(nGraphicType == 0 || nGraphicType == -1){ //图形类 + _model->addGraphicItem(QUuid::createUuid(),"",event->scenePos(),nGraphicType); } else{ - _model->addNodeItem(QUuid(uuid),event->scenePos()); + } + + event->acceptProposedAction(); } } - else if(sceneMode == DM_baseModel) - { - int a = 1; - } } diff --git a/diagramCavas/source/util/movingSelector.cpp b/diagramCavas/source/util/movingSelector.cpp index b1dd56c..6872eab 100644 --- a/diagramCavas/source/util/movingSelector.cpp +++ b/diagramCavas/source/util/movingSelector.cpp @@ -2,7 +2,7 @@ #include "graphicsItem/itemPort.h" #include #include -#include +#include "graphicsItem/graphicsBaseItem.h" #include "baseProperty.h" MovingSelector::MovingSelector(FixedPortsModel* model,QObject *parent) diff --git a/diagramCavas/source/util/rotationSelector.cpp b/diagramCavas/source/util/rotationSelector.cpp index 7ec02c9..85b0eb8 100644 --- a/diagramCavas/source/util/rotationSelector.cpp +++ b/diagramCavas/source/util/rotationSelector.cpp @@ -1,7 +1,7 @@ #include "util/rotationSelector.h" #include #include -#include +#include "graphicsItem/graphicsBaseItem.h" RotationSelector::RotationSelector(FixedPortsModel* model,QObject *parent) diff --git a/diagramCavas/source/util/scalingSelector.cpp b/diagramCavas/source/util/scalingSelector.cpp index 496cb60..6c62821 100644 --- a/diagramCavas/source/util/scalingSelector.cpp +++ b/diagramCavas/source/util/scalingSelector.cpp @@ -1,7 +1,7 @@ #include "util/scalingSelector.h" #include #include -#include +#include "graphicsItem/graphicsBaseItem.h" ScalingSelector::ScalingSelector(FixedPortsModel* model,QObject *parent) : BaseSelector(model,parent) diff --git a/diagramUtils/include/dataBase.h b/diagramUtils/include/dataBase.h index 70c31fc..5a60c74 100644 --- a/diagramUtils/include/dataBase.h +++ b/diagramUtils/include/dataBase.h @@ -177,6 +177,12 @@ public: bool removeHMIRef(QUuid hmiId,QString model); //移除hmi中某类模型的图片引用 bool removeHMIRefAll(QUuid hmiId); //移除某个HMI中所有引用关系 + bool inserHMICustomRef(QUuid hmiId,QString model,QUuid itemId,QByteArray hash256); //图片item使用的图片 + bool insertHMICustomRefBatch(const QList& refList); + QList getHMICustomRefAll(QUuid hmiId); + QList getHMICustomRef(QUuid hmiId,QUuid itemId); + bool removeHMICustomRef(QUuid hmiId,QUuid itemId); + bool removeHMICustomRefAll(QUuid hmiId); public: /***********************************editor编辑器**********************************************/ bool insertEditorProject(QUuid,QString,QString); diff --git a/diagramUtils/source/dataBase.cpp b/diagramUtils/source/dataBase.cpp index 32cbbc8..1b18d66 100644 --- a/diagramUtils/source/dataBase.cpp +++ b/diagramUtils/source/dataBase.cpp @@ -3408,31 +3408,83 @@ bool DataBase::insertHMIimagesBatch(const QVector& imageList) return true; } - // 准备批量参数 - QStringList sqlStatements; - QList paramsList; + int successCount = 0; + int totalCount = imageList.size(); - QString strSQL = "INSERT INTO diagramui_hmi_image(type, svg_name, svg_hash, svg_data) VALUES (?, ?, ?, ?)"; - - for (const auto& image : imageList) { - sqlStatements.append(strSQL); - - QVariantList params; - params.append(image.baseType); - params.append(image.imageName); - params.append(image.hash256); - params.append(image.svgData); - - paramsList.append(params); + // 使用单个事务 + if (!db.transaction()) { + LOG_ERROR("DB", "Start transaction failed."); + return false; } try { - // 使用现有批量执行函数,强制使用事务 - executeBatchSQL(sqlStatements, false, paramsList, true); - return true; + // 使用单个准备好的语句 + QSqlQuery insertQuery(db); + if (!insertQuery.prepare( + "INSERT INTO diagramui_hmi_image(type, svg_name, svg_hash, svg_data) " + "VALUES (?, ?, ?, ?)")) { + LOG_ERROR("DB", "Prepare insert statement failed."); + db.rollback(); + return false; + } + + for (const auto& image : imageList) { + // 重新绑定参数 + insertQuery.bindValue(0, image.baseType); + insertQuery.bindValue(1, image.imageName); + insertQuery.bindValue(2, image.hash256); + insertQuery.bindValue(3, image.svgData); + + if (!insertQuery.exec()) { + QSqlError error = insertQuery.lastError(); + QString errorText = error.databaseText(); + auto errorType = error.type(); // Qt6: 返回错误类型字符串 + + // 检查是否唯一约束冲突 + if (errorText.contains("重复键违反唯一约束", Qt::CaseInsensitive) || + errorText.contains("duplicate key", Qt::CaseInsensitive) || + errorText.contains("already exists", Qt::CaseInsensitive) || + errorText.contains("unique constraint", Qt::CaseInsensitive) || + error.driverText().contains("23505")) { // PostgreSQL唯一约束错误码 + + LOG_WARN("DB", QString("Duplicate image hash skipped: %1") + .arg(QString(image.hash256.toHex()))); + continue; // 跳过这条记录,继续下一条 + } else { + LOG_ERROR("DB", QString("Insert image failed: %1, error: %2, driver: %3") + .arg(image.imageName, errorText, error.driverText())); + // 非重复错误,回滚整个事务 + db.rollback(); + return false; + } + } + + successCount++; + } + + if (successCount > 0) { + if (!db.commit()) { + LOG_ERROR("DB", "Commit transaction failed."); + db.rollback(); + return false; + } + + if (successCount < totalCount) { + LOG_WARN("DB", QString("Batch insert partially completed. Success: %1, Failed: %2") + .arg(successCount).arg(totalCount - successCount)); + } + + LOG_INFO("DB", QString("Insert completed: %1/%2 images").arg(successCount).arg(totalCount)); + return true; + } else { + db.rollback(); + LOG_WARN("DB", "No records were inserted."); + return false; + } } catch (const std::exception& e) { - LOG_ERROR("DB", QString("Batch insert HMI images failed: %1").arg(e.what())); + db.rollback(); + LOG_ERROR("DB", QString("Insert failed with exception: %1").arg(e.what())); return false; } } @@ -3489,7 +3541,7 @@ QVector DataBase::batchCheckHMIimagesExists(const QVector& has for (const auto& hash : hashList) { placeholders.append("?"); - params.append(QString(hash.toHex())); // 转换为十六进制字符串 + params.append(hash); // 转换为十六进制字符串 } QString sql = QString("SELECT svg_hash FROM diagramui_hmi_image WHERE svg_hash IN (%1)") @@ -3506,7 +3558,7 @@ QVector DataBase::batchCheckHMIimagesExists(const QVector& has // 更新存在性结果 for (int i = 0; i < hashList.size(); ++i) { - QString currentHash = QString(hashList[i].toHex()); + QString currentHash = QString(hashList[i]); if (existingHashes.contains(currentHash)) { existsResults[i] = true; } @@ -3704,6 +3756,164 @@ bool DataBase::removeHMIRefAll(QUuid hmiId) return false; } } + +bool DataBase::inserHMICustomRef(QUuid hmiId,QString model,QUuid itemId,QByteArray hash256) +{ + QString strSQL = "INSERT INTO diagramui_hmi_custom_config(hmi_uuid, model_name, item_id, svg_hash) VALUES ( ?, ?, ?, ?)"; + + QVariantList params; + params.append(hmiId); + params.append(model); + params.append(itemId); + params.append(hash256); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_hmi_custom_config fail")); + return false; + } +} + +bool DataBase::insertHMICustomRefBatch(const QList& refList) +{ + if (refList.isEmpty()) { + return true; + } + + // 准备批量参数 + QStringList sqlStatements; + QList paramsList; + + // 注意:id 是自增主键,不需要插入 + QString strSQL = "INSERT INTO diagramui_hmi_custom_config(hmi_uuid, model_name, item_id, svg_hash) VALUES ( ?, ?, ?, ?)"; + + for (const auto& ref : refList) { + sqlStatements.append(strSQL); + + QVariantList params; + params.append(ref.hmiId); + params.append(ref.model); + params.append(ref.itemId); + params.append(ref.hash256); + + paramsList.append(params); + } + + try { + // 使用现有批量执行函数,强制使用事务 + executeBatchSQL(sqlStatements, false, paramsList, true); + return true; + } + catch (const std::exception& e) { + LOG_ERROR("DB", QString("Batch insert HMI custom refs failed: %1").arg(e.what())); + return false; + } +} + +QList DataBase::getHMICustomRefAll(QUuid hmiId) +{ + QList lst; + QString strSQL = "SELECT id, hmi_uuid, model_name, item_id, svg_hash FROM diagramui_hmi_custom_config WHERE hmi_uuid = ?"; + QVariantList params; + params.append(hmiId); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + HMICustomImageRef info; + info.id = query.value(0).toInt(); + info.hmiId = QUuid(query.value(1).toString()); + info.model = query.value(2).toString(); + info.itemId = QUuid(query.value(3).toString()); + info.hash256 = query.value(4).toByteArray(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getHMICustomRef(QUuid hmiId,QUuid itemId) +{ + QList lst; + QString strSQL = "SELECT id, hmi_uuid, model_name, item_id, svg_hash FROM diagramui_hmi_custom_config WHERE hmi_uuid = ? AND item_id = ?"; + QVariantList params; + params.append(hmiId); + params.append(itemId); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + HMICustomImageRef info; + info.id = query.value(0).toInt(); + info.hmiId = QUuid(query.value(1).toString()); + info.model = query.value(2).toString(); + info.itemId = QUuid(query.value(3).toString()); + info.hash256 = query.value(4).toByteArray(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::removeHMICustomRef(QUuid hmiId,QUuid itemId) +{ + QString strSQL = "DELETE FROM diagramui_hmi_custom_config WHERE hmi_uuid = ? AND item_id = ?"; + QVariantList params; + params.append(hmiId); + params.append(itemId); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete diagramui_hmi_custom_config success")); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete diagramui_hmi_custom_config failed")); + return false; + } +} + +bool DataBase::removeHMICustomRefAll(QUuid hmiId) +{ + QString strSQL = "DELETE FROM diagramui_hmi_custom_config WHERE hmi_uuid = ?"; + QVariantList params; + params.append(hmiId); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete diagramui_hmi_custom_config success")); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete diagramui_hmi_custom_config failed")); + return false; + } +} //=========================================================================== bool DataBase::createDynamicTable(const QString &tableName, const QStringList &fields) { diff --git a/include/configToolBar.h b/include/configToolBar.h index 7441e89..8297948 100644 --- a/include/configToolBar.h +++ b/include/configToolBar.h @@ -9,12 +9,10 @@ class ConfigToolBar : public EnhancedToolBar Q_OBJECT public: - explicit ConfigToolBar(const QString &title, QWidget *parent = nullptr); explicit ConfigToolBar(QWidget *parent = nullptr); // 从配置加载工具 bool loadToolsFromConfig(const QString &configFile); - private: void loadDefaultTools(); diff --git a/include/draggableToolButton.h b/include/draggableToolButton.h new file mode 100644 index 0000000..ac328f2 --- /dev/null +++ b/include/draggableToolButton.h @@ -0,0 +1,35 @@ +// draggabletoolbutton.h +#ifndef DRAGGABLETOOLBUTTON_H +#define DRAGGABLETOOLBUTTON_H + +#include + +class DraggableToolButton : public QToolButton +{ + Q_OBJECT +public: + explicit DraggableToolButton(QWidget *parent = nullptr); + + void setDragEnabled(bool enabled); + bool dragEnabled() const; + + void setToolData(const QString &data); + QString toolData() const; + +signals: + void dragStarted(const QString &toolData, const QPoint &globalPos); + void clicked(); // 自定义点击信号 + +protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + +private: + bool m_dragEnabled = true; + bool m_isDragging = false; + QPoint m_pressPos; + QString m_toolData; +}; + +#endif // DRAGGABLETOOLBUTTON_H diff --git a/include/enhancedToolBar.h b/include/enhancedToolBar.h index 5f1bb2d..8264b85 100644 --- a/include/enhancedToolBar.h +++ b/include/enhancedToolBar.h @@ -1,54 +1,45 @@ +// enhancedtoolbar.h #ifndef ENHANCEDTOOLBAR_H #define ENHANCEDTOOLBAR_H #include -#include #include +class DraggableToolButton; + class EnhancedToolBar : public QToolBar { Q_OBJECT - public: - explicit EnhancedToolBar(const QString &title, QWidget *parent = nullptr); explicit EnhancedToolBar(QWidget *parent = nullptr); - // 工具管理接口 + // 添加工具 void addTool(const QString &toolType, const QString &toolName, const QIcon &icon = QIcon()); - void removeTool(const QString &toolType); - void setCurrentTool(const QString &toolType); + + // 获取当前选中的工具 QString currentTool() const; - // 获取工具信息 - QString toolName(const QString &toolType) const; - QIcon toolIcon(const QString &toolType) const; - QStringList availableTools() const; + // 设置当前工具 + void setCurrentTool(const QString &toolType); - // 批量操作 - void clearTools(); - void addTools(const QList> &tools); + // 启用/禁用拖拽 + void setDragEnabled(bool enabled); + bool dragEnabled() const; signals: void toolSelected(const QString &toolType); - void toolAdded(const QString &toolType); - void toolRemoved(const QString &toolType); - -protected: - virtual QAction* createAction(const QString &toolType, - const QString &toolName, - const QIcon &icon); + void toolDragged(const QString &toolType, const QPoint &globalPos); private slots: - void onActionTriggered(QAction *action); + void onToolButtonClicked(); + void onToolButtonDragStarted(const QString &toolData, const QPoint &globalPos); + void startDrag(const QString &toolType, const QPoint &globalPos); private: - void init(); - - QActionGroup *m_actionGroup; - QMap m_actions; - QMap m_actionToType; // 反向映射 + QMap m_buttons; QString m_currentTool; + bool m_dragEnabled = true; }; -#endif +#endif // ENHANCEDTOOLBAR_H diff --git a/pluginManager/CMakeLists.txt b/pluginManager/CMakeLists.txt new file mode 100644 index 0000000..47b19bc --- /dev/null +++ b/pluginManager/CMakeLists.txt @@ -0,0 +1,54 @@ +project(pluginManager) + +set(PLUGINMANAGER_HEADER_FILES + + include/pluginManager.h + include/genericPluginAdapter.h + ../common/include/compiler.hpp + ../common/include/export.hpp + ../common/include/operatingSystem.hpp + + ../common/include/pluginCommon/iPluginAdapter.h + ../common/include/pluginCommon/iCanvasItem.h +) + +set(PLUGINMANAGER_SOURCE_FILES + source/pluginManager.cpp + source/genericPluginAdapter.cpp +) + + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(pluginManager SHARED + MANUAL_FINALIZATION + ${PLUGINMANAGER_HEADER_FILES} + ${PLUGINMANAGER_SOURCE_FILES} + ) +else() + add_library(pluginManager SHARED + ${PLUGINMANAGER_HEADER_FILES} + ${PLUGINMANAGER_SOURCE_FILES} + ) +endif() + +target_link_libraries(pluginManager PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) + +target_link_libraries(pluginManager PRIVATE Qt6::Xml) +target_link_libraries(pluginManager PRIVATE Qt6::Network) +target_link_libraries(pluginManager PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) + +option(BUILD_SHARED_LIBS "Build as shared library" ON) + + + +target_include_directories(pluginManager PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_compile_definitions(pluginManager + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) diff --git a/pluginManager/include/genericPluginAdapter.h b/pluginManager/include/genericPluginAdapter.h new file mode 100644 index 0000000..d0ff8e9 --- /dev/null +++ b/pluginManager/include/genericPluginAdapter.h @@ -0,0 +1,60 @@ +// genericPluginAdapter.h +#pragma once + +#include "pluginCommon/iPluginAdapter.h" +#include + +class ICanvasItem; + +// 通用适配器实现 +class GenericPluginAdapter :public IPluginAdapter +{ + Q_OBJECT + +public: + explicit GenericPluginAdapter(ICanvasItem *pluginItem, QObject *parent = nullptr); + virtual ~GenericPluginAdapter(); + + // IPluginAdapter接口 + QString adapterType() const override { return "generic"; } + QString pluginType() const override; + + QRectF bounds() const override; + void setBounds(const QRectF &bounds) override; + + void paint(QPainter *painter, const QRectF &bounds) override; + QPainterPath shape() const override; + + QVariant property(const QString &key) const override; + void setProperty(const QString &key, const QVariant &value) override; + + void move(const QPointF &delta) override; + void resize(const QRectF &newBounds) override; + + bool isSelected() const override; + void setSelected(bool selected) override; + + QVariantMap saveState() const override; + bool loadState(const QVariantMap &state) override; + + // 获取原始插件项 + ICanvasItem* pluginItem() const; + +signals: + void boundsChanged(const QRectF &newBounds); + void propertyChanged(const QString &key, const QVariant &value); + void selectionChanged(bool selected); + void adapterChanged(); + +private: + ICanvasItem *m_pluginItem = nullptr; + bool m_selected = false; + QRectF m_cachedBounds; + + void updateCache(); + void connectSignals(); + +private slots: + void onPluginBoundsChanged(const QRectF &newBounds); + void onPluginPropertyChanged(const QString &key, const QVariant &value); +}; diff --git a/pluginManager/include/pluginManager.h b/pluginManager/include/pluginManager.h new file mode 100644 index 0000000..266aa13 --- /dev/null +++ b/pluginManager/include/pluginManager.h @@ -0,0 +1,57 @@ +// pluginManager.h +#pragma once + +#include "export.hpp" +#include "../common/include/pluginCommon/iCanvasItem.h" +#include "../common/include/pluginCommon/iPlugin.h" +#include + +class DIAGRAM_DESIGNER_PUBLIC PluginManager : public QObject +{ + Q_OBJECT + +public: + explicit PluginManager(QObject *parent = nullptr); + virtual ~PluginManager(); + + // 单例模式 + static PluginManager* instance(); + + // 插件管理 + bool loadPlugin(const QString &filePath); + bool unloadPlugin(const QString &pluginId); + void loadAllPlugins(const QString &directory); + + // 插件目录管理 + void addPluginDirectory(const QString &dir); + void setPluginDirectories(const QStringList &dirs); + QStringList pluginDirectories() const; + + // 创建原始插件项 + ICanvasItem* createItem(const QString &shapeId); + + // 查询接口 + QStringList availableShapes() const; + bool contains(const QString &shapeId) const; + ShapeDescriptor shapeDescriptor(const QString &shapeId) const; + QIcon shapeIcon(const QString &shapeId) const; + + // 插件信息 + PluginDescriptor pluginDescriptor(const QString &pluginId) const; + QStringList loadedPlugins() const; + + // 内置形状注册 + void registerBuiltinShape(const QString &shapeId, + const QString &name, + std::function creator, + const QString &iconPath = QString()); + +signals: + void pluginLoaded(const PluginDescriptor &descriptor); + void pluginUnloaded(const QString &pluginId); + void shapesChanged(); + +private: + class Private; + QScopedPointer d; +}; diff --git a/pluginManager/source/genericPluginAdapter.cpp b/pluginManager/source/genericPluginAdapter.cpp new file mode 100644 index 0000000..abd440c --- /dev/null +++ b/pluginManager/source/genericPluginAdapter.cpp @@ -0,0 +1,200 @@ +// genericPluginAdapter.cpp +#include "genericPluginAdapter.h" +#include "pluginCommon/ICanvasItem.h" +#include +#include +#include + +GenericPluginAdapter::GenericPluginAdapter(ICanvasItem *pluginItem, QObject *parent) + : IPluginAdapter(parent) + , m_pluginItem(pluginItem) +{ + if (!m_pluginItem) { + qWarning() << "GenericPluginAdapter: pluginItem is null!"; + return; + } + + connectSignals(); + updateCache(); + + qDebug() << "GenericPluginAdapter created for plugin:" << m_pluginItem->typeId(); +} + +GenericPluginAdapter::~GenericPluginAdapter() +{ + if (m_pluginItem) { + m_pluginItem->deleteLater(); + } +} + +QString GenericPluginAdapter::pluginType() const +{ + return m_pluginItem ? m_pluginItem->typeId() : QString(); +} + +QRectF GenericPluginAdapter::bounds() const +{ + return m_cachedBounds; +} + +void GenericPluginAdapter::setBounds(const QRectF &bounds) +{ + if (m_pluginItem && bounds.isValid()) { + m_pluginItem->setBounds(bounds); + } +} + +void GenericPluginAdapter::paint(QPainter *painter, const QRectF &bounds) +{ + if (m_pluginItem && painter) { + m_pluginItem->draw(painter, bounds); + } +} + +QPainterPath GenericPluginAdapter::shape() const +{ + // 返回简单的矩形路径 + QPainterPath path; + path.addRect(m_cachedBounds); + return path; +} + +QVariant GenericPluginAdapter::property(const QString &key) const +{ + return m_pluginItem ? m_pluginItem->property(key) : QVariant(); +} + +void GenericPluginAdapter::setProperty(const QString &key, const QVariant &value) +{ + if (m_pluginItem) { + m_pluginItem->setProperty(key, value); + } +} + +void GenericPluginAdapter::move(const QPointF &delta) +{ + if (m_pluginItem && !delta.isNull()) { + QRectF bounds = m_pluginItem->bounds(); + bounds.translate(delta); + m_pluginItem->setBounds(bounds); + } +} + +void GenericPluginAdapter::resize(const QRectF &newBounds) +{ + setBounds(newBounds); +} + +bool GenericPluginAdapter::isSelected() const +{ + return m_selected; +} + +void GenericPluginAdapter::setSelected(bool selected) +{ + if (m_selected != selected) { + m_selected = selected; + emit selectionChanged(selected); + } +} + +QVariantMap GenericPluginAdapter::saveState() const +{ + QVariantMap state; + + if (!m_pluginItem) { + return state; + } + + // 保存基本信息 + state["type"] = m_pluginItem->typeId(); + state["selected"] = m_selected; + + // 保存几何信息 + QRectF bounds = m_pluginItem->bounds(); + state["x"] = bounds.x(); + state["y"] = bounds.y(); + state["width"] = bounds.width(); + state["height"] = bounds.height(); + + // 保存属性 + QVariantMap properties; + // 这里可以保存插件的关键属性 + // 例如:properties["fillColor"] = m_pluginItem->property("fillColor"); + + state["properties"] = properties; + + return state; +} + +bool GenericPluginAdapter::loadState(const QVariantMap &state) +{ + if (!m_pluginItem) { + return false; + } + + // 加载几何信息 + if (state.contains("x") && state.contains("y") && + state.contains("width") && state.contains("height")) { + QRectF bounds( + state["x"].toReal(), + state["y"].toReal(), + state["width"].toReal(), + state["height"].toReal() + ); + m_pluginItem->setBounds(bounds); + } + + // 加载选择状态 + if (state.contains("selected")) { + setSelected(state["selected"].toBool()); + } + + // 加载属性 + if (state.contains("properties")) { + QVariantMap properties = state["properties"].toMap(); + for (auto it = properties.begin(); it != properties.end(); ++it) { + m_pluginItem->setProperty(it.key(), it.value()); + } + } + + return true; +} + +ICanvasItem* GenericPluginAdapter::pluginItem() const +{ + return m_pluginItem; +} + +void GenericPluginAdapter::updateCache() +{ + if (m_pluginItem) { + m_cachedBounds = m_pluginItem->bounds(); + } +} + +void GenericPluginAdapter::connectSignals() +{ + if (!m_pluginItem) { + return; + } + + connect(m_pluginItem, &ICanvasItem::boundsChanged, + this, &GenericPluginAdapter::onPluginBoundsChanged); + + connect(m_pluginItem, &ICanvasItem::propertyChanged, + this, &GenericPluginAdapter::onPluginPropertyChanged); +} + +void GenericPluginAdapter::onPluginBoundsChanged(const QRectF &newBounds) +{ + m_cachedBounds = newBounds; + emit boundsChanged(newBounds); + emit adapterChanged(); +} + +void GenericPluginAdapter::onPluginPropertyChanged(const QString &key, const QVariant &value) +{ + emit propertyChanged(key, value); + emit adapterChanged(); +} diff --git a/pluginManager/source/pluginManager.cpp b/pluginManager/source/pluginManager.cpp new file mode 100644 index 0000000..128741f --- /dev/null +++ b/pluginManager/source/pluginManager.cpp @@ -0,0 +1,388 @@ +//pluginManager.cpp +#include "pluginManager/include/pluginManager.h" +//#include "common/include/pluginCommon/iPlugin.h" +#include +#include +#include +#include +#include +#include + +// 私有实现 +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 plugins; // pluginId -> PluginInfo* + QMap shapeToPlugin; // shapeId -> pluginId + QMap shapeDescriptors; // shapeId -> descriptor + + // 内置形状 + QMap> builtinCreators; + QMap 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(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 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 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(); +} diff --git a/source/configToolBar.cpp b/source/configToolBar.cpp index bc1977d..03e97f1 100644 --- a/source/configToolBar.cpp +++ b/source/configToolBar.cpp @@ -2,12 +2,6 @@ #include "configToolBar.h" #include -ConfigToolBar::ConfigToolBar(const QString &title, QWidget *parent) - : EnhancedToolBar(title, parent) -{ - loadDefaultTools(); -} - ConfigToolBar::ConfigToolBar(QWidget *parent) : EnhancedToolBar(parent) { @@ -23,7 +17,7 @@ bool ConfigToolBar::loadToolsFromConfig(const QString &configFile) } // 清空现有工具 - clearTools(); + //clearTools(); // 从配置添加工具 QList tools = m_config.getAllTools(); @@ -37,8 +31,6 @@ bool ConfigToolBar::loadToolsFromConfig(const QString &configFile) void ConfigToolBar::loadDefaultTools() { // 添加一些基本工具 - addTool("select", "选择", QIcon(":/icons/select.png")); - addTool("line", "直线", QIcon(":/icons/line.png")); - addTool("rect", "矩形", QIcon(":/icons/rect.png")); - addTool("circle", "圆形", QIcon(":/icons/circle.png")); + addTool("image", "图像", QIcon(":/images/element/icon_image.png")); + addTool("text", "文本", QIcon(":/images/element/icon_text.png")); } diff --git a/source/draggableToolButton.cpp b/source/draggableToolButton.cpp new file mode 100644 index 0000000..636157c --- /dev/null +++ b/source/draggableToolButton.cpp @@ -0,0 +1,76 @@ +// draggabletoolbutton.cpp +#include "draggabletoolbutton.h" +#include +#include +#include + +DraggableToolButton::DraggableToolButton(QWidget *parent) + : QToolButton(parent) + , m_dragEnabled(true) + , m_isDragging(false) +{ + setMouseTracking(true); +} + +void DraggableToolButton::setDragEnabled(bool enabled) +{ + m_dragEnabled = enabled; +} + +bool DraggableToolButton::dragEnabled() const +{ + return m_dragEnabled; +} + +void DraggableToolButton::setToolData(const QString &data) +{ + m_toolData = data; +} + +QString DraggableToolButton::toolData() const +{ + return m_toolData; +} + +void DraggableToolButton::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton && m_dragEnabled) { + m_pressPos = event->pos(); + m_isDragging = false; + } + + // 不调用基类,完全自己处理 + event->accept(); +} + +void DraggableToolButton::mouseMoveEvent(QMouseEvent *event) +{ + if (!m_dragEnabled || !(event->buttons() & Qt::LeftButton)) { + event->ignore(); + return; + } + + // 检查是否达到拖拽阈值 + int moveDist = (event->pos() - m_pressPos).manhattanLength(); + if (moveDist >= QApplication::startDragDistance()) { + if (!m_isDragging) { + m_isDragging = true; + emit dragStarted(m_toolData, event->globalPosition().toPoint()); + } + } + + event->accept(); +} + +void DraggableToolButton::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + if (!m_isDragging) { + // 没有拖拽,是点击 + emit clicked(); + } + m_isDragging = false; + } + + event->accept(); +} diff --git a/source/enhancedToolBar.cpp b/source/enhancedToolBar.cpp index d7f69d3..83881d8 100644 --- a/source/enhancedToolBar.cpp +++ b/source/enhancedToolBar.cpp @@ -1,108 +1,106 @@ -// EnhancedToolBar.cpp -#include "enhancedToolBar.h" -#include +#include "enhancedtoolbar.h" +#include "draggabletoolbutton.h" +#include +#include +#include +#include +#include #include -EnhancedToolBar::EnhancedToolBar(const QString &title, QWidget *parent) - : QToolBar(title, parent) - , m_currentTool("") -{ - init(); -} - EnhancedToolBar::EnhancedToolBar(QWidget *parent) : QToolBar(parent) - , m_currentTool("") + , m_dragEnabled(true) { - init(); -} - -void EnhancedToolBar::init() -{ - // 设置工具栏属性 setIconSize(QSize(32, 32)); setToolButtonStyle(Qt::ToolButtonIconOnly); setMovable(true); setFloatable(true); - setAllowedAreas(Qt::AllToolBarAreas); - - // 创建互斥的动作组 - m_actionGroup = new QActionGroup(this); - m_actionGroup->setExclusive(true); - - // 连接信号 - connect(this, &QToolBar::actionTriggered, - this, &EnhancedToolBar::onActionTriggered); -} - -QAction* EnhancedToolBar::createAction(const QString &toolType, - const QString &toolName, - const QIcon &icon) -{ - QAction *action = new QAction(icon, toolName, this); - action->setCheckable(true); - action->setData(toolType); - action->setToolTip(toolName); - return action; } void EnhancedToolBar::addTool(const QString &toolType, const QString &toolName, const QIcon &icon) { - if (m_actions.contains(toolType)) { - return; // 已存在 + if (m_buttons.contains(toolType)) { + return; } - QAction *action = createAction(toolType, toolName, icon); + // 创建自定义按钮 + DraggableToolButton *button = new DraggableToolButton(this); + button->setIcon(icon); + button->setIconSize(iconSize()); + button->setText(toolName); + button->setToolTip(toolName); + button->setCheckable(true); + button->setToolData(toolType); + button->setDragEnabled(m_dragEnabled); - m_actionGroup->addAction(action); - addAction(action); - m_actions[toolType] = action; - m_actionToType[action] = toolType; + // 连接信号 + connect(button, &DraggableToolButton::clicked, + this, &EnhancedToolBar::onToolButtonClicked); + connect(button, &DraggableToolButton::dragStarted, + this, &EnhancedToolBar::onToolButtonDragStarted); - // 如果没有当前工具,自动选择第一个工具 + // 添加到工具栏 + addWidget(button); + m_buttons[toolType] = button; + + // 如果没有当前工具,设置第一个为当前 if (m_currentTool.isEmpty()) { setCurrentTool(toolType); } - - emit toolAdded(toolType); } -void EnhancedToolBar::removeTool(const QString &toolType) +void EnhancedToolBar::onToolButtonClicked() { - if (m_actions.contains(toolType)) { - QAction *action = m_actions[toolType]; - - removeAction(action); - m_actionGroup->removeAction(action); - m_actions.remove(toolType); - m_actionToType.remove(action); - - action->deleteLater(); - - if (m_currentTool == toolType) { - m_currentTool = ""; - } - - emit toolRemoved(toolType); + DraggableToolButton *button = qobject_cast(sender()); + if (button) { + QString toolType = button->toolData(); + setCurrentTool(toolType); } } -void EnhancedToolBar::setCurrentTool(const QString &toolType) +void EnhancedToolBar::onToolButtonDragStarted(const QString &toolData, const QPoint &globalPos) { - if (toolType.isEmpty()) { - // 清除当前选择 - if (m_actionGroup->checkedAction()) { - m_actionGroup->checkedAction()->setChecked(false); - } - m_currentTool = ""; - emit toolSelected(""); + if (m_dragEnabled) { + startDrag(toolData, globalPos); } - else if (m_actions.contains(toolType)) { - m_actions[toolType]->setChecked(true); - m_currentTool = toolType; - emit toolSelected(toolType); +} + +void EnhancedToolBar::startDrag(const QString &toolType, const QPoint &globalPos) +{ + DraggableToolButton *button = m_buttons.value(toolType); + if (!button) { + return; } + + QString toolName = button->text(); + QIcon icon = button->icon(); + + qDebug() << "开始拖拽:" << toolType << toolName; + + // 创建MIME数据 + QMimeData *mimeData = new QMimeData(); + mimeData->setText(toolName); + mimeData->setData("application/x-tooltype", toolType.toUtf8()); + + // 创建拖拽对象 + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + + // 创建预览图像 + if (!icon.isNull()) { + QPixmap pixmap = icon.pixmap(32, 32); + drag->setPixmap(pixmap); + drag->setHotSpot(QPoint(16, 16)); + } + + // 发出信号 + emit toolDragged(toolType, globalPos); + + // 执行拖拽 + Qt::DropAction result = drag->exec(Qt::CopyAction); + + qDebug() << "拖拽完成,结果:" << result; } QString EnhancedToolBar::currentTool() const @@ -110,52 +108,38 @@ QString EnhancedToolBar::currentTool() const return m_currentTool; } -QString EnhancedToolBar::toolName(const QString &toolType) const +void EnhancedToolBar::setCurrentTool(const QString &toolType) { - if (m_actions.contains(toolType)) { - return m_actions[toolType]->text(); - } - return QString(); -} - -QIcon EnhancedToolBar::toolIcon(const QString &toolType) const -{ - if (m_actions.contains(toolType)) { - return m_actions[toolType]->icon(); - } - return QIcon(); -} - -QStringList EnhancedToolBar::availableTools() const -{ - return m_actions.keys(); -} - -void EnhancedToolBar::clearTools() -{ - for (auto it = m_actions.begin(); it != m_actions.end(); ++it) { - removeAction(it.value()); - m_actionGroup->removeAction(it.value()); - it.value()->deleteLater(); + if (m_currentTool == toolType) { + return; } - m_actions.clear(); - m_actionToType.clear(); - m_currentTool = ""; -} - -void EnhancedToolBar::addTools(const QList> &tools) -{ - for (const auto &tool : tools) { - addTool(tool.first, tool.second); + // 取消之前的选择 + if (!m_currentTool.isEmpty() && m_buttons.contains(m_currentTool)) { + m_buttons[m_currentTool]->setChecked(false); } -} -void EnhancedToolBar::onActionTriggered(QAction *action) -{ - if (m_actionToType.contains(action)) { - QString toolType = m_actionToType[action]; + // 设置新的选择 + if (m_buttons.contains(toolType)) { + m_buttons[toolType]->setChecked(true); m_currentTool = toolType; emit toolSelected(toolType); + } else { + m_currentTool = ""; } } + +void EnhancedToolBar::setDragEnabled(bool enabled) +{ + m_dragEnabled = enabled; + + // 更新所有按钮 + for (DraggableToolButton *button : m_buttons.values()) { + button->setDragEnabled(enabled); + } +} + +bool EnhancedToolBar::dragEnabled() const +{ + return m_dragEnabled; +} diff --git a/source/mainwindow.cpp b/source/mainwindow.cpp index 8f2131f..cd8f045 100644 --- a/source/mainwindow.cpp +++ b/source/mainwindow.cpp @@ -25,7 +25,7 @@ #include "monitorPagesDlg.h" #include "QDetailsView.h" #include "baseDockWidget.h" -#include "enhancedToolBar.h" +#include "configToolBar.h" #include "QDetailsView.h" @@ -83,12 +83,10 @@ void CMainWindow::moveEvent(QMoveEvent *event) void CMainWindow::initializeDockUi() { - EnhancedToolBar *toolBar = new EnhancedToolBar("绘图工具", this); + ConfigToolBar* toolBar = new ConfigToolBar(this); addToolBar(Qt::TopToolBarArea, toolBar); - toolBar->addTool("image", "图像", QIcon(":/images/element/icon_image.png")); - toolBar->addTool("text", "文本", QIcon(":/images/element/icon_text.png")); - toolBar->setIconSize(QSize(24, 24)); - toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + //toolBar->setIconSize(QSize(24, 24)); + //toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); m_pElectricElementsBox = new ElectricElementsBox(); /*m_pElectricElementsBox->initial(); @@ -114,21 +112,21 @@ void CMainWindow::initializeDockUi() diagramDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); this->addDockWidget(Qt::LeftDockWidgetArea,diagramDock); - m_pMonitorItemsDlg = new MonitorItemsDlg(this); - m_pMonitorItemsDock = new BaseDockWidget(QString::fromWCharArray(L"当前设备列表"),this); - m_pMonitorItemsDock->setWidget(m_pMonitorItemsDlg); - m_pMonitorItemsDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); - this->addDockWidget(Qt::RightDockWidgetArea,m_pMonitorItemsDock); + //m_pMonitorItemsDlg = new MonitorItemsDlg(this); + //m_pMonitorItemsDock = new BaseDockWidget(QString::fromWCharArray(L"当前设备列表"),this); + //m_pMonitorItemsDock->setWidget(m_pMonitorItemsDlg); + //m_pMonitorItemsDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + //this->addDockWidget(Qt::RightDockWidgetArea,m_pMonitorItemsDock); - m_pMonitorPagesDlg = new MonitorPagesDlg(this); - m_pMonitorPagesDock = new BaseDockWidget(QString::fromWCharArray(L"监控列表"),this); - m_pMonitorPagesDock->setWidget(m_pMonitorPagesDlg); - m_pMonitorPagesDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); - m_pMonitorPagesDock->setMinimumSize(120,550); - this->addDockWidget(Qt::RightDockWidgetArea,m_pMonitorPagesDock); + //m_pMonitorPagesDlg = new MonitorPagesDlg(this); + //m_pMonitorPagesDock = new BaseDockWidget(QString::fromWCharArray(L"监控列表"),this); + //m_pMonitorPagesDock->setWidget(m_pMonitorPagesDlg); + //m_pMonitorPagesDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + //m_pMonitorPagesDock->setMinimumSize(120,550); + //this->addDockWidget(Qt::RightDockWidgetArea,m_pMonitorPagesDock); - m_pMonitorItemsDock->hide(); - m_pMonitorPagesDock->hide(); + //m_pMonitorItemsDock->hide(); + //m_pMonitorPagesDock->hide(); m_pDiagramCavas = new DiagramCavas(this); m_pDiagramCavas->initial(); @@ -186,13 +184,13 @@ void CMainWindow::initializeAction() connect(m_pDiagramCavas,&DiagramCavas::createHMI,m_pDiagramView,&DiagramView::onNewHMICreated); connect(m_pDiagramCavas,&DiagramCavas::updateHMI,m_pDiagramView,&DiagramView::onHMIUpdated); - connect(m_pDiagramCavas,&DiagramCavas::prepareUpdateItems,m_pMonitorItemsDlg,&MonitorItemsDlg::onUpdateItems); - connect(m_pDiagramCavas,&DiagramCavas::prepareSelectItems,m_pMonitorItemsDlg,&MonitorItemsDlg::onSelectItems); - connect(m_pDiagramCavas,&DiagramCavas::updateMonitorList,m_pMonitorPagesDlg,&MonitorPagesDlg::onMonitorCreated); + //connect(m_pDiagramCavas,&DiagramCavas::prepareUpdateItems,m_pMonitorItemsDlg,&MonitorItemsDlg::onUpdateItems); + //connect(m_pDiagramCavas,&DiagramCavas::prepareSelectItems,m_pMonitorItemsDlg,&MonitorItemsDlg::onSelectItems); + //connect(m_pDiagramCavas,&DiagramCavas::updateMonitorList,m_pMonitorPagesDlg,&MonitorPagesDlg::onMonitorCreated); connect(m_pDiagramCavas,&DiagramCavas::createdMonitorItems,m_pMonitorItemsDlg,&MonitorItemsDlg::onMonitorCreated); - connect(m_pDiagramCavas,&DiagramCavas::updateMonitorTopology,m_pTopologyView,&TopologyView::onMonitorUpdate); + //connect(m_pDiagramCavas,&DiagramCavas::updateMonitorTopology,m_pTopologyView,&TopologyView::onMonitorUpdate); - connect(m_pMonitorPagesDlg,&MonitorPagesDlg::monitorSelected,m_pDiagramCavas,&DiagramCavas::onSignal_monitorSelected); + //connect(m_pMonitorPagesDlg,&MonitorPagesDlg::monitorSelected,m_pDiagramCavas,&DiagramCavas::onSignal_monitorSelected); //connect(m_pMonitorPagesDlg,&MonitorPagesDlg::prepareSaveMonitor,m_pDiagramCavas,&DiagramCavas::onSignal_saveMonitor); connect(ui->actionNew,&QAction::triggered,m_pDiagramCavas,&DiagramCavas::onSignal_addPage); @@ -217,13 +215,13 @@ void CMainWindow::initializeAction() connect(_pActMonitor,&QAction::triggered,this,[&](){ if(!_pActMonitor->isChecked()){ _pActMonitor->setChecked(false); - m_pMonitorItemsDock->hide(); - m_pMonitorPagesDock->hide(); + //m_pMonitorItemsDock->hide(); + //m_pMonitorPagesDock->hide(); } else{ _pActMonitor->setChecked(true); - m_pMonitorItemsDock->show(); - m_pMonitorPagesDock->show(); + //m_pMonitorItemsDock->show(); + //m_pMonitorPagesDock->show(); if(m_pDiagramCavas) m_pDiagramCavas->updateMonitorListFromDB();