From 07022c71efacf5d261a733c3b7ec8467d1993380 Mon Sep 17 00:00:00 2001 From: baiYue Date: Mon, 23 Mar 2026 11:06:17 +0800 Subject: [PATCH] exchange commit --- .drone.yml | 20 + .gitignore | 106 + CMakeLists.txt | 185 + PropertyEditor/CMakeLists.txt | 72 + PropertyEditor/README.md | 162 + PropertyEditor/README_zh.md | 159 + PropertyEditor/example/CMakeLists.txt | 29 + PropertyEditor/example/CommonInclude.h | 18 + PropertyEditor/example/CustomGadget.h | 20 + PropertyEditor/example/CustomObject.h | 48 + PropertyEditor/example/CustomType.h | 18 + .../PropertyTypeCustomization_CustomType.cpp | 73 + .../PropertyTypeCustomization_CustomType.h | 12 + PropertyEditor/example/main.cpp | 20 + PropertyEditor/resources.qrc | 31 + .../resources/Icon/arrow-left-l.png | Bin 0 -> 2500 bytes .../resources/Icon/arrow-right-l.png | Bin 0 -> 2515 bytes PropertyEditor/resources/Icon/close.png | Bin 0 -> 5692 bytes PropertyEditor/resources/Icon/delete.png | Bin 0 -> 1960 bytes PropertyEditor/resources/Icon/down.png | Bin 0 -> 2399 bytes PropertyEditor/resources/Icon/expand.png | Bin 0 -> 3220 bytes PropertyEditor/resources/Icon/plus.png | Bin 0 -> 1927 bytes PropertyEditor/resources/Icon/reset.png | Bin 0 -> 4433 bytes PropertyEditor/resources/Icon/unexpand.png | Bin 0 -> 2863 bytes PropertyEditor/resources/Icon/up.png | Bin 0 -> 2400 bytes .../Qml/ColorPalette/ColorPalette.qml | 9 + .../Qml/ColorPalette/ColorPalette_Dark.qml | 27 + .../Qml/ColorPalette/ColorPalette_Light.qml | 27 + .../resources/Qml/ValueEditor/BoolBox.qml | 184 + .../resources/Qml/ValueEditor/ColorBox.qml | 96 + .../Qml/ValueEditor/DirectorySelector.qml | 63 + .../Qml/ValueEditor/FileSelector.qml | 102 + .../resources/Qml/ValueEditor/LineTextBox.qml | 72 + .../Qml/ValueEditor/MultiLineTextBox.qml | 83 + .../resources/Qml/ValueEditor/NumberBox.qml | 159 + .../resources/Qml/ValueEditor/PointFBox.qml | 184 + .../resources/Qml/ValueEditor/RectFBox.qml | 136 + .../resources/Qml/ValueEditor/SizeFBox.qml | 372 ++ .../Qml/ValueEditor/TextComboBox.qml | 103 + .../resources/Qml/ValueEditor/Vec2Box.qml | 44 + .../resources/Qml/ValueEditor/Vec3Box.qml | 55 + .../resources/Qml/ValueEditor/Vec4Box.qml | 66 + .../resources/image-20250826114654194.png | Bin 0 -> 125086 bytes .../include/IPropertyTypeCustomization.h | 18 + .../PropertyHandleImpl/IPropertyHandleImpl.h | 33 + .../QPropertyHandleImpl_Associative.h | 27 + .../QPropertyHandleImpl_Enum.h | 20 + .../QPropertyHandleImpl_Object.h | 27 + .../QPropertyHandleImpl_RawType.h | 14 + .../QPropertyHandleImpl_Sequential.h | 26 + PropertyEditor/source/include/QDetailsView.h | 20 + .../source/include/QDetailsViewAPI.h | 21 + .../source/include/QPropertyHandle.h | 108 + .../source/include/QQuickDetailsView.h | 28 + .../include/QQuickDetailsViewLayoutBuilder.h | 36 + .../include/QQuickDetailsViewMananger.h | 58 + .../source/include/QQuickDetailsViewModel.h | 44 + .../source/include/QQuickDetailsViewRow.h | 66 + .../source/include/QQuickFunctionLibrary.h | 64 + .../source/include/QQuickTreeViewEx.h | 53 + .../PropertyTypeCustomization_Associative.cpp | 35 + .../PropertyTypeCustomization_Associative.h | 11 + .../PropertyTypeCustomization_Matrix4x4.cpp | 94 + .../PropertyTypeCustomization_Matrix4x4.h | 11 + ...ropertyTypeCustomization_ObjectDefault.cpp | 52 + .../PropertyTypeCustomization_ObjectDefault.h | 11 + .../PropertyTypeCustomization_Sequential.cpp | 40 + .../PropertyTypeCustomization_Sequential.h | 11 + .../source/src/IPropertyTypeCustomization.cpp | 13 + .../IPropertyHandleImpl.cpp | 38 + .../QPropertyHandleImpl_Associative.cpp | 72 + .../QPropertyHandleImpl_Enum.cpp | 46 + .../QPropertyHandleImpl_Object.cpp | 98 + .../QPropertyHandleImpl_RawType.cpp | 14 + .../QPropertyHandleImpl_Sequential.cpp | 69 + PropertyEditor/source/src/QDetailsView.cpp | 86 + PropertyEditor/source/src/QPropertyHandle.cpp | 322 ++ .../source/src/QQuickDetailsView.cpp | 86 + .../src/QQuickDetailsViewBasicTypeEditor.cpp | 468 ++ .../src/QQuickDetailsViewLayoutBuilder.cpp | 216 + .../source/src/QQuickDetailsViewMananger.cpp | 138 + .../source/src/QQuickDetailsViewModel.cpp | 137 + .../source/src/QQuickDetailsViewPrivate.h | 41 + .../source/src/QQuickDetailsViewRow.cpp | 118 + .../source/src/QQuickFunctionLibrary.cpp | 58 + .../source/src/QQuickTreeViewEx.cpp | 480 ++ .../source/src/QQuickTreeViewExPrivate.h | 34 + README.md | 6 + common/backend/meta_model.h | 72 + common/backend/project_model.h | 284 ++ common/core_model/constants.h | 15 + common/core_model/data_transmission.h | 14 + common/core_model/diagram.h | 372 ++ common/core_model/topology.h | 413 ++ common/core_model/types.cpp | 9 + common/core_model/types.h | 172 + common/frontend/graphics_items.h | 28 + common/frontend/monitor_item.h | 272 ++ common/include/baseProperty.h | 224 + common/include/compiler.hpp | 40 + common/include/export.hpp | 48 + common/include/extraPropertyManager.h | 40 + common/include/httpInterface.h | 33 + common/include/operatingSystem.hpp | 49 + common/include/structDataSource.h | 84 + common/include/tools.h | 84 + common/source/baseProperty.cpp | 96 + common/source/extraPropertyManager.cpp | 77 + common/source/httpInterface.cpp | 150 + common/source/structDataSource.cpp | 270 ++ common/source/tools.cpp | 137 + diagramCavas/CMakeLists.txt | 302 ++ diagramCavas/include/baseContentDlg.h | 38 + diagramCavas/include/baseDrawingPanel.h | 76 + diagramCavas/include/baseInfoDlg.h | 35 + .../baseModelItem/electricBaseModelLineItem.h | 34 + .../baseModelItem/electricBaseModelPortItem.h | 23 + .../baseModelItem/electricBaseModelSvgBus.h | 22 + .../baseModelItem/electricBaseModelSvgItem.h | 31 + .../include/basePannelPropertyProxy.h | 31 + diagramCavas/include/basePropertyProxy.h | 14 + diagramCavas/include/baseScene.h | 42 + diagramCavas/include/bayInfoDlg.h | 51 + diagramCavas/include/bayManagerContentDlg.h | 33 + diagramCavas/include/bayManagerDlg.h | 41 + diagramCavas/include/bayMeasureDlg.h | 48 + diagramCavas/include/cornerMonitorLauncher.h | 29 + diagramCavas/include/createHMIdlg.h | 28 + diagramCavas/include/ctExtraInfoDlg.h | 45 + diagramCavas/include/dataSourceDlg.h | 67 + diagramCavas/include/designerScene.h | 55 + diagramCavas/include/designerView.h | 41 + diagramCavas/include/diagramCavas.h | 131 + diagramCavas/include/diagramConnectSetting.h | 38 + .../include/graphicsDataModel/baseModel.h | 35 + .../graphicsDataModel/fixedPortsModel.h | 199 + .../include/graphicsItem/electricBayItem.h | 27 + .../electricFunctionModelConnectLineItem.h | 95 + .../electricFunctionModelPortItem.h | 24 + .../electricFunctionModelSvgGroup.h | 32 + .../electricFunctionModelSvgGroupCT.h | 26 + .../electricFunctionModelSvgGroupPT.h | 25 + .../electricFunctionModelSvgItem.h | 30 + ...lectricFunctionModelSvgItem2wTransformer.h | 20 + ...lectricFunctionModelSvgItem3wTransformer.h | 20 + .../electricFunctionModelSvgItemBus.h | 23 + .../electricFunctionModelSvgItemCB.h | 22 + .../electricFunctionModelSvgItemCT.h | 21 + .../electricFunctionModelSvgItemCableEnd.h | 20 + .../electricFunctionModelSvgItemCableTer.h | 20 + .../electricFunctionModelSvgItemDS.h | 19 + .../electricFunctionModelSvgItemDTEDS.h | 20 + .../electricFunctionModelSvgItemES.h | 19 + .../electricFunctionModelSvgItemFES.h | 19 + .../electricFunctionModelSvgItemLA.h | 20 + .../electricFunctionModelSvgItemPI.h | 20 + .../electricFunctionModelSvgItemPT.h | 21 + .../graphicsFunctionModelItem.h | 41 + .../include/graphicsItem/graphicsBaseItem.h | 705 +++ .../include/graphicsItem/graphicsItemGroup.h | 38 + .../graphicsItem/graphicsPolygonItem.h | 33 + .../include/graphicsItem/handleRect.h | 19 + .../include/graphicsItem/handleText.h | 47 + .../include/graphicsItem/itemControlHandle.h | 54 + diagramCavas/include/graphicsItem/itemPort.h | 46 + diagramCavas/include/instance/dataAccessor.h | 31 + diagramCavas/include/itemPropertyDlg.h | 58 + diagramCavas/include/loadMonitorPageDlg.h | 36 + diagramCavas/include/measureSettingDlg.h | 67 + diagramCavas/include/monitorAttributeDlg.h | 32 + .../include/monitorAttributeGroupDlg.h | 42 + diagramCavas/include/monitorConfigDlg.h | 58 + .../include/monitorDetailAttributeDlg.h | 42 + .../include/monitorDisplaySettingDlg.h | 58 + diagramCavas/include/monitorItemPreviewDlg.h | 31 + diagramCavas/include/monitorPanel.h | 73 + .../include/monitorSelectedItemsDlg.h | 28 + diagramCavas/include/monitorSideBarDlg.h | 27 + diagramCavas/include/monitorToolBox.h | 22 + diagramCavas/include/monitorToolPage.h | 30 + diagramCavas/include/powerConnection.h | 41 + diagramCavas/include/powerEntity.h | 138 + diagramCavas/include/powerTerminal.h | 71 + .../include/projectDiagramNameInput.h | 32 + .../include/projectIconSelectionDlg.h | 21 + diagramCavas/include/projectIconSetting.h | 37 + diagramCavas/include/propertyContentDlg.h | 32 + .../include/propertyType/CustomGadget.h | 20 + .../include/propertyType/CustomType.h | 18 + .../PropertyTypeCustomization_CustomType.h | 12 + .../include/propertyType/dataSourceType.h | 18 + .../include/propertyType/pannelColorGadget.h | 27 + ...propertyTypeCustomization_DataSourceType.h | 16 + diagramCavas/include/ptExtraInfoDlg.h | 42 + diagramCavas/include/serializable.h | 13 + diagramCavas/include/statusBar.h | 28 + .../include/structDataActionParaDlg.h | 29 + diagramCavas/include/structDataCauseEditDlg.h | 30 + .../include/structDataMeasurementDelegate.h | 60 + .../include/structDataMeasurementModel.h | 81 + diagramCavas/include/structDataPreviewDlg.h | 110 + .../include/structDataPropertyDelegate.h | 49 + .../include/structDataPropertyModel.h | 77 + diagramCavas/include/titleBar.h | 47 + diagramCavas/include/topologyManager.h | 121 + diagramCavas/include/topologyTree.h | 17 + diagramCavas/include/util/baseSelector.h | 91 + .../include/util/connectingSelector.h | 38 + diagramCavas/include/util/creatingSelector.h | 46 + diagramCavas/include/util/editingSelector.h | 34 + .../include/util/linkMovingSelector.h | 30 + diagramCavas/include/util/movingSelector.h | 28 + diagramCavas/include/util/rotationSelector.h | 28 + diagramCavas/include/util/scalingSelector.h | 30 + diagramCavas/include/util/selectorManager.h | 42 + diagramCavas/include/util/subMovingSelector.h | 28 + diagramCavas/source/baseContentDlg.cpp | 30 + diagramCavas/source/baseDrawingPanel.cpp | 92 + diagramCavas/source/baseInfoDlg.cpp | 170 + .../electricBaseModelLineItem.cpp | 137 + .../electricBaseModelPortItem.cpp | 76 + .../baseModelItem/electricBaseModelSvgBus.cpp | 60 + .../electricBaseModelSvgItem.cpp | 104 + .../source/basePannelPropertyProxy.cpp | 38 + diagramCavas/source/basePropertyProxy.cpp | 12 + diagramCavas/source/baseScene.cpp | 41 + diagramCavas/source/bayInfoDlg.cpp | 564 +++ diagramCavas/source/bayManagerContentDlg.cpp | 289 ++ diagramCavas/source/bayManagerDlg.cpp | 98 + diagramCavas/source/bayMeasureDlg.cpp | 448 ++ diagramCavas/source/cornerMonitorLauncher.cpp | 77 + diagramCavas/source/createHMIdlg.cpp | 48 + diagramCavas/source/ctExtraInfoDlg.cpp | 329 ++ diagramCavas/source/dataSourceDlg.cpp | 648 +++ diagramCavas/source/designerScene.cpp | 259 + diagramCavas/source/designerView.cpp | 194 + diagramCavas/source/diagramCavas.cpp | 619 +++ diagramCavas/source/diagramConnectSetting.cpp | 115 + .../source/graphicsDataModel/baseModel.cpp | 454 ++ .../graphicsDataModel/fixedPortsModel.cpp | 2406 ++++++++++ .../source/graphicsItem/electricBayItem.cpp | 57 + .../electricFunctionModelConnectLineItem.cpp | 974 ++++ .../electricFunctionModelPortItem.cpp | 65 + .../electricFunctionModelSvgGroup.cpp | 149 + .../electricFunctionModelSvgGroupCT.cpp | 116 + .../electricFunctionModelSvgGroupPT.cpp | 182 + .../electricFunctionModelSvgItem.cpp | 190 + ...ctricFunctionModelSvgItem2wTransformer.cpp | 69 + ...ctricFunctionModelSvgItem3wTransformer.cpp | 69 + .../electricFunctionModelSvgItemBus.cpp | 106 + .../electricFunctionModelSvgItemCB.cpp | 76 + .../electricFunctionModelSvgItemCT.cpp | 107 + .../electricFunctionModelSvgItemCableEnd.cpp | 70 + .../electricFunctionModelSvgItemCableTer.cpp | 69 + .../electricFunctionModelSvgItemDS.cpp | 70 + .../electricFunctionModelSvgItemDTEDS.cpp | 70 + .../electricFunctionModelSvgItemES.cpp | 71 + .../electricFunctionModelSvgItemFES.cpp | 69 + .../electricFunctionModelSvgItemLA.cpp | 70 + .../electricFunctionModelSvgItemPI.cpp | 71 + .../electricFunctionModelSvgItemPT.cpp | 68 + .../graphicsFunctionModelItem.cpp | 97 + .../source/graphicsItem/graphicsBaseItem.cpp | 920 ++++ .../source/graphicsItem/graphicsItemGroup.cpp | 278 ++ .../graphicsItem/graphicsPolygonItem.cpp | 155 + .../source/graphicsItem/handleRect.cpp | 69 + .../source/graphicsItem/handleText.cpp | 82 + .../source/graphicsItem/itemControlHandle.cpp | 53 + diagramCavas/source/graphicsItem/itemPort.cpp | 68 + diagramCavas/source/instance/dataAccessor.cpp | 226 + diagramCavas/source/itemPropertyDlg.cpp | 281 ++ diagramCavas/source/loadMonitorPageDlg.cpp | 105 + diagramCavas/source/measureSettingDlg.cpp | 680 +++ diagramCavas/source/monitorAttributeDlg.cpp | 74 + .../source/monitorAttributeGroupDlg.cpp | 340 ++ diagramCavas/source/monitorConfigDlg.cpp | 265 ++ .../source/monitorDetailAttributeDlg.cpp | 88 + .../source/monitorDisplaySettingDlg.cpp | 420 ++ diagramCavas/source/monitorItemPreviewDlg.cpp | 84 + diagramCavas/source/monitorPanel.cpp | 936 ++++ .../source/monitorSelectedItemsDlg.cpp | 60 + diagramCavas/source/monitorSideBarDlg.cpp | 44 + diagramCavas/source/monitorToolBox.cpp | 73 + diagramCavas/source/monitorToolPage.cpp | 77 + diagramCavas/source/powerConnection.cpp | 20 + diagramCavas/source/powerEntity.cpp | 43 + diagramCavas/source/powerTerminal.cpp | 53 + .../source/projectIconSelectionDlg.cpp | 54 + diagramCavas/source/projectIconSetting.cpp | 139 + diagramCavas/source/propertyContentDlg.cpp | 366 ++ .../PropertyTypeCustomization_CustomType.cpp | 73 + .../source/propertyType/pannelColorGadget.cpp | 28 + ...opertyTypeCustomization_DataSourceType.cpp | 120 + diagramCavas/source/ptExtraInfoDlg.cpp | 312 ++ diagramCavas/source/statusBar.cpp | 41 + .../source/structDataActionParaDlg.cpp | 80 + .../source/structDataCauseEditDlg.cpp | 125 + .../source/structDataMeasurementDelegate.cpp | 427 ++ .../source/structDataMeasurementModel.cpp | 334 ++ diagramCavas/source/structDataPreviewDlg.cpp | 921 ++++ .../source/structDataPropertyDelegate.cpp | 210 + .../source/structDataPropertyModel.cpp | 223 + diagramCavas/source/titleBar.cpp | 113 + diagramCavas/source/topologyManager.cpp | 1093 +++++ diagramCavas/source/util/baseSelector.cpp | 675 +++ .../source/util/connectingSelector.cpp | 246 + diagramCavas/source/util/creatingSelector.cpp | 188 + diagramCavas/source/util/editingSelector.cpp | 54 + .../source/util/linkMovingSelector.cpp | 53 + diagramCavas/source/util/movingSelector.cpp | 89 + diagramCavas/source/util/rotationSelector.cpp | 69 + diagramCavas/source/util/scalingSelector.cpp | 70 + diagramCavas/source/util/selectorManager.cpp | 93 + .../source/util/subMovingSelector.cpp | 73 + diagramCavas/ui/baseInfoDlg.ui | 404 ++ diagramCavas/ui/bayInfoDlg.ui | 382 ++ diagramCavas/ui/bayManagerContentDlg.ui | 318 ++ diagramCavas/ui/bayManagerDlg.ui | 241 + diagramCavas/ui/bayMeasureDlg.ui | 534 +++ diagramCavas/ui/createHMIdlg.ui | 306 ++ diagramCavas/ui/ctExtraInfoDlg.ui | 407 ++ diagramCavas/ui/dataSourceDlg.ui | 280 ++ diagramCavas/ui/diagramConnectSetting.ui | 675 +++ diagramCavas/ui/drawingPanel.ui | 39 + diagramCavas/ui/editorSettingDlg.ui | 83 + diagramCavas/ui/itemPropertyDlg.ui | 221 + diagramCavas/ui/loadMonitorPageDlg.ui | 176 + diagramCavas/ui/measureSettingDlg.ui | 1447 ++++++ diagramCavas/ui/monitorConfigDlg.ui | 532 +++ diagramCavas/ui/monitorDetailAttributeDlg.ui | 211 + diagramCavas/ui/monitorDisplaySettingDlg.ui | 540 +++ diagramCavas/ui/projectDiagramNameInput.ui | 292 ++ diagramCavas/ui/projectIconSetting.ui | 179 + diagramCavas/ui/ptExtraInfoDlg.ui | 393 ++ diagramCavas/ui/structDataPreviewDlg.ui | 539 +++ diagramCommunication/CMakeLists.txt | 58 + diagramCommunication/CMakeLists.txt.NaNAUP | 58 + diagramCommunication/CMakeLists.txt.ozECFc | 58 + diagramCommunication/include/baseChannel.h | 73 + diagramCommunication/include/channelConfig.h | 72 + .../include/communicationManager.h | 76 + diagramCommunication/include/configManager.h | 49 + diagramCommunication/include/dataProcessor.h | 40 + diagramCommunication/include/httpChannel.h | 32 + .../include/uiCommunicationBus.h | 123 + .../include/webSocketChannel.h | 38 + diagramCommunication/source/baseChannel.cpp | 76 + .../source/communicationManager.cpp | 370 ++ diagramCommunication/source/configManager.cpp | 217 + diagramCommunication/source/dataProcessor.cpp | 77 + diagramCommunication/source/httpAdapter.cpp | 260 + diagramCommunication/source/httpChannel.cpp | 168 + .../source/uiCommunicationBus.cpp | 191 + .../source/webSocketChannel.cpp | 123 + diagramUtils/CMakeLists.txt | 70 + diagramUtils/include/basePropertyManager.h | 68 + diagramUtils/include/componentIconManager.h | 27 + diagramUtils/include/dataBase.h | 223 + diagramUtils/include/dataManager.h | 38 + diagramUtils/include/logger.h | 70 + diagramUtils/include/projectManager.h | 44 + diagramUtils/include/projectModelManager.h | 64 + diagramUtils/source/basePropertyManager.cpp | 176 + diagramUtils/source/componentIconManager.cpp | 45 + diagramUtils/source/dataBase.cpp | 4229 +++++++++++++++++ diagramUtils/source/dataManager.cpp | 292 ++ diagramUtils/source/logger.cpp | 155 + diagramUtils/source/projectManager.cpp | 89 + diagramUtils/source/projectModelManager.cpp | 924 ++++ include/CommonInclude.h | 18 + include/baseDockWidget.h | 13 + include/configToolBar.h | 24 + include/diagramView.h | 55 + include/electricElementsBox.h | 38 + include/electricElementsListwidget.h | 18 + include/electricElementsPanel.h | 32 + include/enhancedToolBar.h | 54 + include/graphicElementsPanel.h | 31 + include/mainwindow.h | 78 + include/monitorItemsDlg.h | 50 + include/monitorPagesDlg.h | 37 + include/operationCommand.h | 81 + include/projectTableDelegate.h | 28 + include/renameModel.h | 44 + include/selectorDialog.h | 38 + include/toolBarConfig.h | 54 + include/toolBox.h | 20 + include/toolPage.h | 30 + include/topologyTree.h | 17 + include/topologyView.h | 72 + resource/DiagramDesigner.qrc | 19 + resource/images/checkerboard.png | Bin 0 -> 137 bytes resource/images/element/icon_image.png | Bin 0 -> 650 bytes resource/images/element/icon_text.png | Bin 0 -> 532 bytes resource/images/element/icons_triangle.png | Bin 0 -> 657 bytes resource/images/element/svg_bus.svg | 7 + resource/images/element/svg_rect.svg | 9 + resource/images/element/svg_triangle.svg | 9 + resource/images/icon_down_arrow.png | Bin 0 -> 272 bytes resource/images/icon_left_arrow.png | Bin 0 -> 248 bytes resource/images/icon_rotate.png | Bin 0 -> 373 bytes resource/images/icon_rotate_lb.png | Bin 0 -> 865 bytes resource/images/icon_rotate_lt.png | Bin 0 -> 864 bytes resource/images/icon_rotate_rb.png | Bin 0 -> 872 bytes resource/images/icon_rotate_rt.png | Bin 0 -> 280 bytes resource/images/icon_up_arrow.png | Bin 0 -> 268 bytes setting.xml | 12 + source/baseDockWidget.cpp | 72 + source/configToolBar.cpp | 44 + source/diagramView.cpp | 246 + source/electricElementsBox.cpp | 84 + source/electricElementsListwidget.cpp | 23 + source/electricElementsPanel.cpp | 52 + source/enhancedToolBar.cpp | 161 + source/graphicElementsPanel.cpp | 28 + source/main.cpp | 11 + source/mainwindow.cpp | 329 ++ source/monitorItemsDlg.cpp | 268 ++ source/monitorPagesDlg.cpp | 126 + source/operationCommand.cpp | 141 + source/projectTableDelegate.cpp | 155 + source/renameModel.cpp | 151 + source/selectorDialog.cpp | 107 + source/toolBarConfig.cpp | 129 + source/toolBox.cpp | 58 + source/toolPage.cpp | 77 + source/topologyTree.cpp | 46 + source/topologyView.cpp | 659 +++ ui/diagramView.ui | 143 + ui/drawingPanel.ui | 41 + ui/graphicElementsPanel.ui | 80 + ui/itemPropertyDlg.ui | 331 ++ ui/mainwindow.ui | 330 ++ ui/monitorItemsDlg.ui | 108 + ui/monitorPagesDlg.ui | 24 + ui/renameModel.ui | 177 + ui/topologyView.ui | 26 + 437 files changed, 58381 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 PropertyEditor/CMakeLists.txt create mode 100644 PropertyEditor/README.md create mode 100644 PropertyEditor/README_zh.md create mode 100644 PropertyEditor/example/CMakeLists.txt create mode 100644 PropertyEditor/example/CommonInclude.h create mode 100644 PropertyEditor/example/CustomGadget.h create mode 100644 PropertyEditor/example/CustomObject.h create mode 100644 PropertyEditor/example/CustomType.h create mode 100644 PropertyEditor/example/PropertyTypeCustomization_CustomType.cpp create mode 100644 PropertyEditor/example/PropertyTypeCustomization_CustomType.h create mode 100644 PropertyEditor/example/main.cpp create mode 100644 PropertyEditor/resources.qrc create mode 100644 PropertyEditor/resources/Icon/arrow-left-l.png create mode 100644 PropertyEditor/resources/Icon/arrow-right-l.png create mode 100644 PropertyEditor/resources/Icon/close.png create mode 100644 PropertyEditor/resources/Icon/delete.png create mode 100644 PropertyEditor/resources/Icon/down.png create mode 100644 PropertyEditor/resources/Icon/expand.png create mode 100644 PropertyEditor/resources/Icon/plus.png create mode 100644 PropertyEditor/resources/Icon/reset.png create mode 100644 PropertyEditor/resources/Icon/unexpand.png create mode 100644 PropertyEditor/resources/Icon/up.png create mode 100644 PropertyEditor/resources/Qml/ColorPalette/ColorPalette.qml create mode 100644 PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Dark.qml create mode 100644 PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Light.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/BoolBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/ColorBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/DirectorySelector.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/FileSelector.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/LineTextBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/MultiLineTextBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/NumberBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/PointFBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/RectFBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/SizeFBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/TextComboBox.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/Vec2Box.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/Vec3Box.qml create mode 100644 PropertyEditor/resources/Qml/ValueEditor/Vec4Box.qml create mode 100644 PropertyEditor/resources/image-20250826114654194.png create mode 100644 PropertyEditor/source/include/IPropertyTypeCustomization.h create mode 100644 PropertyEditor/source/include/PropertyHandleImpl/IPropertyHandleImpl.h create mode 100644 PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Associative.h create mode 100644 PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Enum.h create mode 100644 PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Object.h create mode 100644 PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_RawType.h create mode 100644 PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Sequential.h create mode 100644 PropertyEditor/source/include/QDetailsView.h create mode 100644 PropertyEditor/source/include/QDetailsViewAPI.h create mode 100644 PropertyEditor/source/include/QPropertyHandle.h create mode 100644 PropertyEditor/source/include/QQuickDetailsView.h create mode 100644 PropertyEditor/source/include/QQuickDetailsViewLayoutBuilder.h create mode 100644 PropertyEditor/source/include/QQuickDetailsViewMananger.h create mode 100644 PropertyEditor/source/include/QQuickDetailsViewModel.h create mode 100644 PropertyEditor/source/include/QQuickDetailsViewRow.h create mode 100644 PropertyEditor/source/include/QQuickFunctionLibrary.h create mode 100644 PropertyEditor/source/include/QQuickTreeViewEx.h create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.cpp create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.h create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.cpp create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.h create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.cpp create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.h create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.cpp create mode 100644 PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.h create mode 100644 PropertyEditor/source/src/IPropertyTypeCustomization.cpp create mode 100644 PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp create mode 100644 PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Associative.cpp create mode 100644 PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Enum.cpp create mode 100644 PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Object.cpp create mode 100644 PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_RawType.cpp create mode 100644 PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Sequential.cpp create mode 100644 PropertyEditor/source/src/QDetailsView.cpp create mode 100644 PropertyEditor/source/src/QPropertyHandle.cpp create mode 100644 PropertyEditor/source/src/QQuickDetailsView.cpp create mode 100644 PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp create mode 100644 PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp create mode 100644 PropertyEditor/source/src/QQuickDetailsViewMananger.cpp create mode 100644 PropertyEditor/source/src/QQuickDetailsViewModel.cpp create mode 100644 PropertyEditor/source/src/QQuickDetailsViewPrivate.h create mode 100644 PropertyEditor/source/src/QQuickDetailsViewRow.cpp create mode 100644 PropertyEditor/source/src/QQuickFunctionLibrary.cpp create mode 100644 PropertyEditor/source/src/QQuickTreeViewEx.cpp create mode 100644 PropertyEditor/source/src/QQuickTreeViewExPrivate.h create mode 100644 README.md create mode 100644 common/backend/meta_model.h create mode 100644 common/backend/project_model.h create mode 100644 common/core_model/constants.h create mode 100644 common/core_model/data_transmission.h create mode 100644 common/core_model/diagram.h create mode 100644 common/core_model/topology.h create mode 100644 common/core_model/types.cpp create mode 100644 common/core_model/types.h create mode 100644 common/frontend/graphics_items.h create mode 100644 common/frontend/monitor_item.h create mode 100644 common/include/baseProperty.h create mode 100644 common/include/compiler.hpp create mode 100644 common/include/export.hpp create mode 100644 common/include/extraPropertyManager.h create mode 100644 common/include/httpInterface.h create mode 100644 common/include/operatingSystem.hpp create mode 100644 common/include/structDataSource.h create mode 100644 common/include/tools.h create mode 100644 common/source/baseProperty.cpp create mode 100644 common/source/extraPropertyManager.cpp create mode 100644 common/source/httpInterface.cpp create mode 100644 common/source/structDataSource.cpp create mode 100644 common/source/tools.cpp create mode 100644 diagramCavas/CMakeLists.txt create mode 100644 diagramCavas/include/baseContentDlg.h create mode 100644 diagramCavas/include/baseDrawingPanel.h create mode 100644 diagramCavas/include/baseInfoDlg.h create mode 100644 diagramCavas/include/baseModelItem/electricBaseModelLineItem.h create mode 100644 diagramCavas/include/baseModelItem/electricBaseModelPortItem.h create mode 100644 diagramCavas/include/baseModelItem/electricBaseModelSvgBus.h create mode 100644 diagramCavas/include/baseModelItem/electricBaseModelSvgItem.h create mode 100644 diagramCavas/include/basePannelPropertyProxy.h create mode 100644 diagramCavas/include/basePropertyProxy.h create mode 100644 diagramCavas/include/baseScene.h create mode 100644 diagramCavas/include/bayInfoDlg.h create mode 100644 diagramCavas/include/bayManagerContentDlg.h create mode 100644 diagramCavas/include/bayManagerDlg.h create mode 100644 diagramCavas/include/bayMeasureDlg.h create mode 100644 diagramCavas/include/cornerMonitorLauncher.h create mode 100644 diagramCavas/include/createHMIdlg.h create mode 100644 diagramCavas/include/ctExtraInfoDlg.h create mode 100644 diagramCavas/include/dataSourceDlg.h create mode 100644 diagramCavas/include/designerScene.h create mode 100644 diagramCavas/include/designerView.h create mode 100644 diagramCavas/include/diagramCavas.h create mode 100644 diagramCavas/include/diagramConnectSetting.h create mode 100644 diagramCavas/include/graphicsDataModel/baseModel.h create mode 100644 diagramCavas/include/graphicsDataModel/fixedPortsModel.h create mode 100644 diagramCavas/include/graphicsItem/electricBayItem.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelPortItem.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h create mode 100644 diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h create mode 100644 diagramCavas/include/graphicsItem/graphicsBaseItem.h create mode 100644 diagramCavas/include/graphicsItem/graphicsItemGroup.h create mode 100644 diagramCavas/include/graphicsItem/graphicsPolygonItem.h create mode 100644 diagramCavas/include/graphicsItem/handleRect.h create mode 100644 diagramCavas/include/graphicsItem/handleText.h create mode 100644 diagramCavas/include/graphicsItem/itemControlHandle.h create mode 100644 diagramCavas/include/graphicsItem/itemPort.h create mode 100644 diagramCavas/include/instance/dataAccessor.h create mode 100644 diagramCavas/include/itemPropertyDlg.h create mode 100644 diagramCavas/include/loadMonitorPageDlg.h create mode 100644 diagramCavas/include/measureSettingDlg.h create mode 100644 diagramCavas/include/monitorAttributeDlg.h create mode 100644 diagramCavas/include/monitorAttributeGroupDlg.h create mode 100644 diagramCavas/include/monitorConfigDlg.h create mode 100644 diagramCavas/include/monitorDetailAttributeDlg.h create mode 100644 diagramCavas/include/monitorDisplaySettingDlg.h create mode 100644 diagramCavas/include/monitorItemPreviewDlg.h create mode 100644 diagramCavas/include/monitorPanel.h create mode 100644 diagramCavas/include/monitorSelectedItemsDlg.h create mode 100644 diagramCavas/include/monitorSideBarDlg.h create mode 100644 diagramCavas/include/monitorToolBox.h create mode 100644 diagramCavas/include/monitorToolPage.h create mode 100644 diagramCavas/include/powerConnection.h create mode 100644 diagramCavas/include/powerEntity.h create mode 100644 diagramCavas/include/powerTerminal.h create mode 100644 diagramCavas/include/projectDiagramNameInput.h create mode 100644 diagramCavas/include/projectIconSelectionDlg.h create mode 100644 diagramCavas/include/projectIconSetting.h create mode 100644 diagramCavas/include/propertyContentDlg.h create mode 100644 diagramCavas/include/propertyType/CustomGadget.h create mode 100644 diagramCavas/include/propertyType/CustomType.h create mode 100644 diagramCavas/include/propertyType/PropertyTypeCustomization_CustomType.h create mode 100644 diagramCavas/include/propertyType/dataSourceType.h create mode 100644 diagramCavas/include/propertyType/pannelColorGadget.h create mode 100644 diagramCavas/include/propertyType/propertyTypeCustomization_DataSourceType.h create mode 100644 diagramCavas/include/ptExtraInfoDlg.h create mode 100644 diagramCavas/include/serializable.h create mode 100644 diagramCavas/include/statusBar.h create mode 100644 diagramCavas/include/structDataActionParaDlg.h create mode 100644 diagramCavas/include/structDataCauseEditDlg.h create mode 100644 diagramCavas/include/structDataMeasurementDelegate.h create mode 100644 diagramCavas/include/structDataMeasurementModel.h create mode 100644 diagramCavas/include/structDataPreviewDlg.h create mode 100644 diagramCavas/include/structDataPropertyDelegate.h create mode 100644 diagramCavas/include/structDataPropertyModel.h create mode 100644 diagramCavas/include/titleBar.h create mode 100644 diagramCavas/include/topologyManager.h create mode 100644 diagramCavas/include/topologyTree.h create mode 100644 diagramCavas/include/util/baseSelector.h create mode 100644 diagramCavas/include/util/connectingSelector.h create mode 100644 diagramCavas/include/util/creatingSelector.h create mode 100644 diagramCavas/include/util/editingSelector.h create mode 100644 diagramCavas/include/util/linkMovingSelector.h create mode 100644 diagramCavas/include/util/movingSelector.h create mode 100644 diagramCavas/include/util/rotationSelector.h create mode 100644 diagramCavas/include/util/scalingSelector.h create mode 100644 diagramCavas/include/util/selectorManager.h create mode 100644 diagramCavas/include/util/subMovingSelector.h create mode 100644 diagramCavas/source/baseContentDlg.cpp create mode 100644 diagramCavas/source/baseDrawingPanel.cpp create mode 100644 diagramCavas/source/baseInfoDlg.cpp create mode 100644 diagramCavas/source/baseModelItem/electricBaseModelLineItem.cpp create mode 100644 diagramCavas/source/baseModelItem/electricBaseModelPortItem.cpp create mode 100644 diagramCavas/source/baseModelItem/electricBaseModelSvgBus.cpp create mode 100644 diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp create mode 100644 diagramCavas/source/basePannelPropertyProxy.cpp create mode 100644 diagramCavas/source/basePropertyProxy.cpp create mode 100644 diagramCavas/source/baseScene.cpp create mode 100644 diagramCavas/source/bayInfoDlg.cpp create mode 100644 diagramCavas/source/bayManagerContentDlg.cpp create mode 100644 diagramCavas/source/bayManagerDlg.cpp create mode 100644 diagramCavas/source/bayMeasureDlg.cpp create mode 100644 diagramCavas/source/cornerMonitorLauncher.cpp create mode 100644 diagramCavas/source/createHMIdlg.cpp create mode 100644 diagramCavas/source/ctExtraInfoDlg.cpp create mode 100644 diagramCavas/source/dataSourceDlg.cpp create mode 100644 diagramCavas/source/designerScene.cpp create mode 100644 diagramCavas/source/designerView.cpp create mode 100644 diagramCavas/source/diagramCavas.cpp create mode 100644 diagramCavas/source/diagramConnectSetting.cpp create mode 100644 diagramCavas/source/graphicsDataModel/baseModel.cpp create mode 100644 diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp create mode 100644 diagramCavas/source/graphicsItem/electricBayItem.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelPortItem.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.cpp create mode 100644 diagramCavas/source/graphicsItem/functionModelItem/graphicsFunctionModelItem.cpp create mode 100644 diagramCavas/source/graphicsItem/graphicsBaseItem.cpp create mode 100644 diagramCavas/source/graphicsItem/graphicsItemGroup.cpp create mode 100644 diagramCavas/source/graphicsItem/graphicsPolygonItem.cpp create mode 100644 diagramCavas/source/graphicsItem/handleRect.cpp create mode 100644 diagramCavas/source/graphicsItem/handleText.cpp create mode 100644 diagramCavas/source/graphicsItem/itemControlHandle.cpp create mode 100644 diagramCavas/source/graphicsItem/itemPort.cpp create mode 100644 diagramCavas/source/instance/dataAccessor.cpp create mode 100644 diagramCavas/source/itemPropertyDlg.cpp create mode 100644 diagramCavas/source/loadMonitorPageDlg.cpp create mode 100644 diagramCavas/source/measureSettingDlg.cpp create mode 100644 diagramCavas/source/monitorAttributeDlg.cpp create mode 100644 diagramCavas/source/monitorAttributeGroupDlg.cpp create mode 100644 diagramCavas/source/monitorConfigDlg.cpp create mode 100644 diagramCavas/source/monitorDetailAttributeDlg.cpp create mode 100644 diagramCavas/source/monitorDisplaySettingDlg.cpp create mode 100644 diagramCavas/source/monitorItemPreviewDlg.cpp create mode 100644 diagramCavas/source/monitorPanel.cpp create mode 100644 diagramCavas/source/monitorSelectedItemsDlg.cpp create mode 100644 diagramCavas/source/monitorSideBarDlg.cpp create mode 100644 diagramCavas/source/monitorToolBox.cpp create mode 100644 diagramCavas/source/monitorToolPage.cpp create mode 100644 diagramCavas/source/powerConnection.cpp create mode 100644 diagramCavas/source/powerEntity.cpp create mode 100644 diagramCavas/source/powerTerminal.cpp create mode 100644 diagramCavas/source/projectIconSelectionDlg.cpp create mode 100644 diagramCavas/source/projectIconSetting.cpp create mode 100644 diagramCavas/source/propertyContentDlg.cpp create mode 100644 diagramCavas/source/propertyType/PropertyTypeCustomization_CustomType.cpp create mode 100644 diagramCavas/source/propertyType/pannelColorGadget.cpp create mode 100644 diagramCavas/source/propertyType/propertyTypeCustomization_DataSourceType.cpp create mode 100644 diagramCavas/source/ptExtraInfoDlg.cpp create mode 100644 diagramCavas/source/statusBar.cpp create mode 100644 diagramCavas/source/structDataActionParaDlg.cpp create mode 100644 diagramCavas/source/structDataCauseEditDlg.cpp create mode 100644 diagramCavas/source/structDataMeasurementDelegate.cpp create mode 100644 diagramCavas/source/structDataMeasurementModel.cpp create mode 100644 diagramCavas/source/structDataPreviewDlg.cpp create mode 100644 diagramCavas/source/structDataPropertyDelegate.cpp create mode 100644 diagramCavas/source/structDataPropertyModel.cpp create mode 100644 diagramCavas/source/titleBar.cpp create mode 100644 diagramCavas/source/topologyManager.cpp create mode 100644 diagramCavas/source/util/baseSelector.cpp create mode 100644 diagramCavas/source/util/connectingSelector.cpp create mode 100644 diagramCavas/source/util/creatingSelector.cpp create mode 100644 diagramCavas/source/util/editingSelector.cpp create mode 100644 diagramCavas/source/util/linkMovingSelector.cpp create mode 100644 diagramCavas/source/util/movingSelector.cpp create mode 100644 diagramCavas/source/util/rotationSelector.cpp create mode 100644 diagramCavas/source/util/scalingSelector.cpp create mode 100644 diagramCavas/source/util/selectorManager.cpp create mode 100644 diagramCavas/source/util/subMovingSelector.cpp create mode 100644 diagramCavas/ui/baseInfoDlg.ui create mode 100644 diagramCavas/ui/bayInfoDlg.ui create mode 100644 diagramCavas/ui/bayManagerContentDlg.ui create mode 100644 diagramCavas/ui/bayManagerDlg.ui create mode 100644 diagramCavas/ui/bayMeasureDlg.ui create mode 100644 diagramCavas/ui/createHMIdlg.ui create mode 100644 diagramCavas/ui/ctExtraInfoDlg.ui create mode 100644 diagramCavas/ui/dataSourceDlg.ui create mode 100644 diagramCavas/ui/diagramConnectSetting.ui create mode 100644 diagramCavas/ui/drawingPanel.ui create mode 100644 diagramCavas/ui/editorSettingDlg.ui create mode 100644 diagramCavas/ui/itemPropertyDlg.ui create mode 100644 diagramCavas/ui/loadMonitorPageDlg.ui create mode 100644 diagramCavas/ui/measureSettingDlg.ui create mode 100644 diagramCavas/ui/monitorConfigDlg.ui create mode 100644 diagramCavas/ui/monitorDetailAttributeDlg.ui create mode 100644 diagramCavas/ui/monitorDisplaySettingDlg.ui create mode 100644 diagramCavas/ui/projectDiagramNameInput.ui create mode 100644 diagramCavas/ui/projectIconSetting.ui create mode 100644 diagramCavas/ui/ptExtraInfoDlg.ui create mode 100644 diagramCavas/ui/structDataPreviewDlg.ui create mode 100644 diagramCommunication/CMakeLists.txt create mode 100644 diagramCommunication/CMakeLists.txt.NaNAUP create mode 100644 diagramCommunication/CMakeLists.txt.ozECFc create mode 100644 diagramCommunication/include/baseChannel.h create mode 100644 diagramCommunication/include/channelConfig.h create mode 100644 diagramCommunication/include/communicationManager.h create mode 100644 diagramCommunication/include/configManager.h create mode 100644 diagramCommunication/include/dataProcessor.h create mode 100644 diagramCommunication/include/httpChannel.h create mode 100644 diagramCommunication/include/uiCommunicationBus.h create mode 100644 diagramCommunication/include/webSocketChannel.h create mode 100644 diagramCommunication/source/baseChannel.cpp create mode 100644 diagramCommunication/source/communicationManager.cpp create mode 100644 diagramCommunication/source/configManager.cpp create mode 100644 diagramCommunication/source/dataProcessor.cpp create mode 100644 diagramCommunication/source/httpAdapter.cpp create mode 100644 diagramCommunication/source/httpChannel.cpp create mode 100644 diagramCommunication/source/uiCommunicationBus.cpp create mode 100644 diagramCommunication/source/webSocketChannel.cpp create mode 100644 diagramUtils/CMakeLists.txt create mode 100644 diagramUtils/include/basePropertyManager.h create mode 100644 diagramUtils/include/componentIconManager.h create mode 100644 diagramUtils/include/dataBase.h create mode 100644 diagramUtils/include/dataManager.h create mode 100644 diagramUtils/include/logger.h create mode 100644 diagramUtils/include/projectManager.h create mode 100644 diagramUtils/include/projectModelManager.h create mode 100644 diagramUtils/source/basePropertyManager.cpp create mode 100644 diagramUtils/source/componentIconManager.cpp create mode 100644 diagramUtils/source/dataBase.cpp create mode 100644 diagramUtils/source/dataManager.cpp create mode 100644 diagramUtils/source/logger.cpp create mode 100644 diagramUtils/source/projectManager.cpp create mode 100644 diagramUtils/source/projectModelManager.cpp create mode 100644 include/CommonInclude.h create mode 100644 include/baseDockWidget.h create mode 100644 include/configToolBar.h create mode 100644 include/diagramView.h create mode 100644 include/electricElementsBox.h create mode 100644 include/electricElementsListwidget.h create mode 100644 include/electricElementsPanel.h create mode 100644 include/enhancedToolBar.h create mode 100644 include/graphicElementsPanel.h create mode 100644 include/mainwindow.h create mode 100644 include/monitorItemsDlg.h create mode 100644 include/monitorPagesDlg.h create mode 100644 include/operationCommand.h create mode 100644 include/projectTableDelegate.h create mode 100644 include/renameModel.h create mode 100644 include/selectorDialog.h create mode 100644 include/toolBarConfig.h create mode 100644 include/toolBox.h create mode 100644 include/toolPage.h create mode 100644 include/topologyTree.h create mode 100644 include/topologyView.h create mode 100644 resource/DiagramDesigner.qrc create mode 100644 resource/images/checkerboard.png create mode 100644 resource/images/element/icon_image.png create mode 100644 resource/images/element/icon_text.png create mode 100644 resource/images/element/icons_triangle.png create mode 100644 resource/images/element/svg_bus.svg create mode 100644 resource/images/element/svg_rect.svg create mode 100644 resource/images/element/svg_triangle.svg create mode 100644 resource/images/icon_down_arrow.png create mode 100644 resource/images/icon_left_arrow.png create mode 100644 resource/images/icon_rotate.png create mode 100644 resource/images/icon_rotate_lb.png create mode 100644 resource/images/icon_rotate_lt.png create mode 100644 resource/images/icon_rotate_rb.png create mode 100644 resource/images/icon_rotate_rt.png create mode 100644 resource/images/icon_up_arrow.png create mode 100644 setting.xml create mode 100644 source/baseDockWidget.cpp create mode 100644 source/configToolBar.cpp create mode 100644 source/diagramView.cpp create mode 100644 source/electricElementsBox.cpp create mode 100644 source/electricElementsListwidget.cpp create mode 100644 source/electricElementsPanel.cpp create mode 100644 source/enhancedToolBar.cpp create mode 100644 source/graphicElementsPanel.cpp create mode 100644 source/main.cpp create mode 100644 source/mainwindow.cpp create mode 100644 source/monitorItemsDlg.cpp create mode 100644 source/monitorPagesDlg.cpp create mode 100644 source/operationCommand.cpp create mode 100644 source/projectTableDelegate.cpp create mode 100644 source/renameModel.cpp create mode 100644 source/selectorDialog.cpp create mode 100644 source/toolBarConfig.cpp create mode 100644 source/toolBox.cpp create mode 100644 source/toolPage.cpp create mode 100644 source/topologyTree.cpp create mode 100644 source/topologyView.cpp create mode 100644 ui/diagramView.ui create mode 100644 ui/drawingPanel.ui create mode 100644 ui/graphicElementsPanel.ui create mode 100644 ui/itemPropertyDlg.ui create mode 100644 ui/mainwindow.ui create mode 100644 ui/monitorItemsDlg.ui create mode 100644 ui/monitorPagesDlg.ui create mode 100644 ui/renameModel.ui create mode 100644 ui/topologyView.ui diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..c0c71a7 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,20 @@ +kind: pipeline +type: ssh +name: default + +server: + host: 192.168.46.100:2223 + user: jessequ + password: + from_secret: password + +workspace: + path: /home/tmp + +steps: +- name: build + commands: + - mkdir -p build/debug + - cd build/debug + - /home/jessequ/Qt/Tools/CMake/bin/cmake -S ../.. -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_GENERATOR:STRING=Ninja -DCMAKE_MAKE_PROGRAM:FILEPATH=/home/jessequ/Qt/Tools/Ninja/ninja -DCMAKE_PREFIX_PATH:PATH=/home/jessequ/Qt/6.7.2/gcc_64 -DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=/home/jessequ/Qt/Platforms/package-manager/auto-setup.cmake -DPostgreSQL_INCLUDE_DIR:FILEPATH=/usr/pgsql-17/include -DPostgreSQL_LIBRARY_DIR:FILEPATH=/usr/pgsql-17/lib -DPostgreSQL_LIBRARY:FILEPATH=/usr/pgsql-17/lib/libpq.so + - /home/jessequ/Qt/Tools/Ninja/ninja diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..183556c --- /dev/null +++ b/.gitignore @@ -0,0 +1,106 @@ +build/ +.vscode/ +.qtcreator/ + +# ---> CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ---> C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# ---> C +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9f1f1b9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,185 @@ +cmake_minimum_required(VERSION 3.5) +cmake_policy(SET CMP0079 NEW) +if(POLICY CMP0079) + cmake_policy(GET CMP0079 policy_status) + message(STATUS "CMP0079 策略状态: ${policy_status}") +else() + message(STATUS "CMP0079 策略不可用") +endif() + +project(DiagramDesigner LANGUAGES CXX VERSION 1.0) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets Sql Xml REQUIRED Charts) +find_package(Qt6 REQUIRED COMPONENTS SvgWidgets) +find_package(Qt6 COMPONENTS Network WebSockets REQUIRED) +find_package(PostgreSQL REQUIRED) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# 默认 ui 文件要和 .h 头文件在一个目录,若不在一个目录,需要指定其所在目录 +set(CMAKE_AUTOUIC_SEARCH_PATHS "ui") + +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + set(dd_PlatformDir "x86") +else() + if(DEFINED CMAKE_SYSTEM_PROCESSOR) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") + set(dd_PlatformDir "x64") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64") + set(dd_PlatformDir "aarch64") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64") + set(dd_PlatformDir "arm64") + else() + set(dd_PlatformDir "x64") + endif() + else() + set(dd_PlatformDir "x64") + endif() +endif() + +set(H_HEADER_FILES + include/mainwindow.h + include/graphicElementsPanel.h + include/electricElementsPanel.h + include/electricElementsBox.h + include/electricElementsListwidget.h + include/operationCommand.h + include/toolPage.h + include/toolBox.h + include/projectTableDelegate.h + include/selectorDialog.h + include/topologyView.h + include/diagramView.h + include/topologyTree.h + include/monitorItemsDlg.h + include/monitorPagesDlg.h + include/baseDockWidget.h + include/enhancedToolBar.h + include/toolBarConfig.h + include/configToolBar.h + + common/include/tools.h + common/include/httpInterface.h + common/include/baseProperty.h + common/include/compiler.hpp + common/include/export.hpp + common/include/operatingSystem.hpp + common/include/structDataSource.h + common/include/extraPropertyManager.h + + common/core_model/types.h + common/core_model/topology.h + common/core_model/diagram.h + common/backend/project_model.h + common/backend/meta_model.h +) +set(CPP_SOURCE_FILES + source/main.cpp + source/mainwindow.cpp + source/graphicElementsPanel.cpp + source/electricElementsPanel.cpp + source/electricElementsBox.cpp + source/electricElementsListwidget.cpp + source/operationCommand.cpp + source/toolPage.cpp + source/toolBox.cpp + source/projectTableDelegate.cpp + source/selectorDialog.cpp + source/topologyView.cpp + source/diagramView.cpp + source/topologyTree.cpp + source/monitorItemsDlg.cpp + source/monitorPagesDlg.cpp + source/baseDockWidget.cpp + source/enhancedToolBar.cpp + source/toolBarConfig.cpp + source/configToolBar.cpp + + common/source/httpInterface.cpp + common/source/tools.cpp + common/source/baseProperty.cpp + common/source/structDataSource.cpp + common/source/extraPropertyManager.cpp + + common/core_model/types.cpp +) +set(UI_FILES + ui/mainwindow.ui + ui/graphicElementsPanel.ui + ui/topologyView.ui + ui/diagramView.ui + ui/monitorItemsDlg.ui + ui/monitorPagesDlg.ui +) + +# 包含源文件目录 +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(DiagramDesigner + MANUAL_FINALIZATION + ${H_HEADER_FILES} + ${CPP_SOURCE_FILES} + ${UI_FILES} + resource/DiagramDesigner.qrc + ) +else() + if(ANDROID) + add_library(DiagramDesigner SHARED + ${H_HEADER_FILES} + ${CPP_SOURCE_FILES} + ${UI_FILES} + resource/DiagramDesigner.qrc + ) + else() + add_executable(DiagramDesigner WIN32 + ${H_HEADER_FILES} + ${CPP_SOURCE_FILES} + ${UI_FILES} + resource/DiagramDesigner.qrc + ) + endif() +endif() + +include_directories(include) +include_directories(common/include) +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_link_libraries(DiagramDesigner PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) +target_link_libraries(DiagramDesigner PRIVATE Qt6::SvgWidgets) +target_link_libraries(DiagramDesigner PRIVATE Qt6::Xml) +target_link_libraries(DiagramDesigner PRIVATE Qt6::Network) +target_link_libraries(DiagramDesigner PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) +message(STATUS "POSTGRESQL_LIBRARIES: ${PostgreSQL_LIBRARIES}") +set_target_properties(DiagramDesigner PROPERTIES + AUTOMOC ON + AUTORCC ON + AUTOUIC ON + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + VERSION 1.0 + EXPORT_NAME "DiagramDesigner with Qt Advanced Docking System" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin" +) + +add_subdirectory(PropertyEditor) +add_subdirectory(diagramCavas) +add_subdirectory(diagramUtils) +add_subdirectory(diagramCommunication) + +target_link_libraries(DiagramDesigner PRIVATE PropertyEditor) +target_link_libraries(DiagramDesigner PRIVATE diagramCavas diagramUtils diagramCommunication) + +file(COPY setting.xml DESTINATION "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin") diff --git a/PropertyEditor/CMakeLists.txt b/PropertyEditor/CMakeLists.txt new file mode 100644 index 0000000..fb4c221 --- /dev/null +++ b/PropertyEditor/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.12) + +project(PropertyEditor CXX) + +find_package(Qt6 COMPONENTS Core Widgets Gui QuickWidgets QuickTemplates2 QuickControls2 REQUIRED) + +qt6_add_resources(QRC_FILE resources.qrc) + +file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/*.h ${CMAKE_CURRENT_SOURCE_DIR}/source/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/resources/*) + +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE}) + +#if(BUILD_SHARED_LIBS) +# add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE}) +# target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_SHARED_LIBRARY) +# message(STATUS "[CMake] Building as SHARED library") +# message(STATUS "[CMake] Defined: PROPERTY_EDITOR_SHARED_LIBRARY") +#else() +# add_library(PropertyEditor STATIC ${PROJECT_SOURCE} ${QRC_FILE}) +# target_compile_definitions(PropertyEditor PUBLIC PROPERTY_EDITOR_STATIC_LIBRARY) +# message(STATUS "[CMake] Building as STATIC library") +# message(STATUS "[CMake] Defined: PROPERTY_EDITOR_STATIC_LIBRARY") +#endif() + +add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE}) + +target_compile_definitions(PropertyEditor + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) + + +set_property(TARGET PropertyEditor PROPERTY AUTOMOC ON) +set_property(TARGET PropertyEditor PROPERTY USE_FOLDERS ON) +set_property(TARGET PropertyEditor PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files") + +target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_LIBRARY) + +target_link_libraries(PropertyEditor PUBLIC + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + Qt::WidgetsPrivate + Qt::QuickWidgets + Qt::QuickPrivate + Qt::QuickTemplates2 + Qt::QuickTemplates2Private + Qt::QuickControls2 +) + +target_include_directories(PropertyEditor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/source/include) +file(GLOB_RECURSE PUBLIC_FILES LIST_DIRECTORIES TRUE ${CMAKE_CURRENT_SOURCE_DIR}/source/include/*) +foreach(PUBLIC_FILE ${PUBLIC_FILES}) + if(IS_DIRECTORY ${PUBLIC_FILE}) + target_include_directories(PropertyEditor PRIVATE ${PUBLIC_FILE}) + endif() +endforeach() + +set_target_properties( + PropertyEditor + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib" +) + +if(PROJECT_IS_TOP_LEVEL) + add_subdirectory(example) +endif() diff --git a/PropertyEditor/README.md b/PropertyEditor/README.md new file mode 100644 index 0000000..dc94be6 --- /dev/null +++ b/PropertyEditor/README.md @@ -0,0 +1,162 @@ +# QDetailsView + +[中文用户?点我查看中文介绍](README_zh.md) + +Inspired by the details view of Unreal Engine, QDetailsView leverages Qt's reflection system to easily build property editors for qobject. + +Its core features are: + +- Create type-based control editors that automatically organize the editor layout according to the reflection structure of the object. +- Utilize QML GPU rendering and control management based on the preview view. + +## Usage + +It is extremely easy to use—simply declare the meta-properties of a `QObject` using **`Q_PROPERTY(...)`**: + +```c++ +class QCustomObject : public QObject { + Q_OBJECT + Q_PROPERTY(int Int READ getInt WRITE setInt) + Q_PROPERTY(float Float READ getFloat WRITE setFloat) + ... +}; +``` + +```c++ +QCustomObject obj; +QDetailsView view; +view.setObject(&obj); +view.show(); +``` + +You will get the following result: + +![image-20250826114654194](resources/image-20250826114654194.png) + +## Customization + +### About QPropertyHandle + +`QPropertyHandle` serves as the unified entry point for QDetailsView to manipulate properties. It is typically constructed via the following interface: + +```c++ +static QPropertyHandle* QPropertyHandle::FindOrCreate( + QObject* inParent, // Parent object that manages the lifecycle of the PropertyHandle + QMetaType inType, // Meta-type of the property + QString inPropertyPath, // Path field of the property + Getter inGetter, // Getter function for the property + Setter inSetter // Setter function for the property +); +``` + +To ensure that changes to property values are detected by DetailsView, all value modifications must use the interface provided by PropertyHandle: + +```c++ +QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName"); +if (handle) { + handle->setVar(QVariant::fromValue(newValue)); +} +``` + +When creating a `QPropertyHandle`, you must specify a `parent`—the handle will be attached to the parent as a child object. Thus, its lifecycle is tied to the parent object. To clean it up, call: + +```c++ +static void QPropertyHandle::Cleanup(QObject* inParent); +``` + +### Custom Enum + +For enum types, they must be defined within a class and declared using **`Q_ENUM(...)`**: + +```c++ +class QCustomObject : public QObject { + Q_OBJECT +public: + enum QCustomEnum { + One, + Two, + Three + }; + Q_ENUM(QCustomEnum); +}; +``` + +### Custom Type Editor + +For custom types that do not inherit from `QObject`, you first need to declare the type using the macro **`Q_DECLARE_METATYPE(...)`** during definition. + +For specific types that require only a single editor control, you can directly register the type editor using the following interface: + +```c++ +QQuickDetailsViewManager::Get()->registerTypeEditor( + metaType, + [](QPropertyHandle* handle, QQuickItem* parent) -> QQuickItem* { + // Implementation of the editor creation logic + } +); +``` + +The source code directory `QQuickDetailsViewBasicTypeEditor.cpp` contains many reference examples, such as the editor for `QDir`: + +```c++ +registerTypeEditor( + QMetaType::fromType(), + [](QPropertyHandle* handle, QQuickItem* parent) -> QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + DirectorySelector { + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + + return valueEditor; + } +); +``` + +For editor controls that span multiple rows or have complex behaviors, you can extend the property editor by deriving from `IPropertyTypeCustomization`. This class provides two virtual functions: + +```c++ +class IPropertyTypeCustomization : public QEnableSharedFromThis +{ +public: + // Used to assemble the editor for the current property row + virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder); + + // Used to extend child items for the current property row + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder); +}; +``` + +After implementing the derived class, register it using the following interface: + +```c++ +QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization(); +``` + +## TODO + +- Undo/redo +- Container operations +- Extended meta-data support diff --git a/PropertyEditor/README_zh.md b/PropertyEditor/README_zh.md new file mode 100644 index 0000000..5027f82 --- /dev/null +++ b/PropertyEditor/README_zh.md @@ -0,0 +1,159 @@ +# QDetailsView + +QDetailsView 受虚幻引擎的属性面板所启发,借助Qt的反射系统,可以轻易地搭建对象的属性编辑器。 + +其核心在于: + +- 编写基于类型的控件编辑器,自动根据对象的反射结构来组织编辑器布局。 +- 使用QML GPU渲染,基于预览视图的控件管理。 + +## 使用 + +它的使用非常简单,只需要使用 **Q_PROPERTY(...) ** 声明 `QObject` 的元属性: + +``` c++ +class QCustomObject :public QObject { + Q_OBJECT + Q_PROPERTY(int Int READ getInt WRITE setInt) + Q_PROPERTY(float Float READ getFloat WRITE setFloat) + ... +}; +``` + +然后创建 `QDetailsView`,并指定Object: + +``` c++ +QCustomObject obj; +QDetailsView view; +view.setObject(&obj); +view.show(); +``` + +你就能得到: + +![image-20250826114654194](resources/image-20250826114654194.png) + +## 定制化 + +### 关于 QPropertyHandle + +`QPropertyHandle` 是QDetailsView操作属性的统一入口,它通常通过如下接口构建: + +``` c++ +static QPropertyHandle* QPropertyHandle::FindOrCreate( + QObject* inParent, // 父对象,它会持有PropertyHandle周期 + QMetaType inType, // 该属性的元类型 + QString inPropertyPath, // 该属性的路径字段 + Getter inGetter, // 该属性的获取器 + Setter inSetter // 该属性的设置器 +); +``` + +为了保证属性值的变动可以被DetailsView响应,所有对值的修改请统一使用PropertyHandle的接口: + +``` C++ +QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName"); +if(handle){ + handle->setVar(QVariant::fromValue(newValue)); +} +``` + +`QPropertyHandle`创建时要求指定`parent`,它将会作为子对象挂载上去,因此它的生命周期将跟随父对象,如果要清理,请调用: + +``` c++ +static void QPropertyHandle::Cleanup(QObject* inParent); +``` + +### 自定义枚举 + +对于枚举类型,需要使用类内定义的方式,并通过**Q_ENUM(...)**声明: + +``` c++ +class QCustomObject: public QObject { + Q_OBJECT +public: + enum QCustomEnum { + One, + Two, + Three + }; + Q_ENUM(QCustomEnum); +}; +``` + +### 自定义类型编辑器 + +非`QObject`的自定义类型,首先需要在定义类型时使用宏**Q_DECLARE_METATYPE(...)**进行声明。 + +对于只有单个编辑器控件的特定类型,可以使用如下接口直接注册类型编辑器: + +``` c++ +QQuickDetailsViewManager::Get()->registerTypeEditor( + metaType, + [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + + } +); +``` + +在源代码的`QQuickDetailsViewBasicTypeEditor.cpp`目录下有许多参考示例,比如QDir: + +``` c++ +registerTypeEditor( + QMetaType::fromType(), + [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + DirectorySelector{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + } +); +``` + +对于具有多行或者行为较为复杂的编辑器控件,可以通过派生`IPropertyTypeCustomization`来扩展属性编辑器,它提供两个虚函数: + +``` c++ +class IPropertyTypeCustomization :public QEnableSharedFromThis +{ +public: + // 用于装配当前属性行的编辑器 + virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder); + + // 用于扩展当前属性行的子项 + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder); +}; +``` + +之后使用如下接口进行注册: + +``` c++ +QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization(); +``` + +## TODO + +- 撤销重做 +- 容器操作 +- 扩展元信息 + + + diff --git a/PropertyEditor/example/CMakeLists.txt b/PropertyEditor/example/CMakeLists.txt new file mode 100644 index 0000000..b70b81b --- /dev/null +++ b/PropertyEditor/example/CMakeLists.txt @@ -0,0 +1,29 @@ + +file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) + +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE}) + +add_executable(PropertyEditorExample + ${PROJECT_SOURCE} +) + +set_property(TARGET PropertyEditorExample PROPERTY AUTOMOC ON) + +find_package(Qt6 REQUIRED COMPONENTS QuickControls2) + +target_link_libraries(PropertyEditorExample PUBLIC PropertyEditor + Qt::Widgets + Qt::QuickWidgets + Qt::QuickPrivate + Qt::QuickTemplates2 + Qt::QuickTemplates2Private + Qt::QuickControls2 +) + +set_target_properties( + PropertyEditorExample + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib" +) diff --git a/PropertyEditor/example/CommonInclude.h b/PropertyEditor/example/CommonInclude.h new file mode 100644 index 0000000..6424340 --- /dev/null +++ b/PropertyEditor/example/CommonInclude.h @@ -0,0 +1,18 @@ +#ifndef CommonInclude_h__ +#define CommonInclude_h__ + +#include +#include +#include +#include + +#define Q_PROPERTY_VAR(Type,Name)\ + Q_PROPERTY(Type Name READ get##Name WRITE set##Name) \ + Type get##Name(){ return Name; } \ + void set##Name(Type var){ \ + Name = var; \ + qDebug() <<"Set" <); + +#endif // CustomGadget_h__ \ No newline at end of file diff --git a/PropertyEditor/example/CustomObject.h b/PropertyEditor/example/CustomObject.h new file mode 100644 index 0000000..abe60ff --- /dev/null +++ b/PropertyEditor/example/CustomObject.h @@ -0,0 +1,48 @@ +#ifndef CustomObject_h__ +#define CustomObject_h__ + +#include +#include +#include +#include +#include +#include +#include "CommonInclude.h" +#include "CustomGadget.h" +#include "CustomType.h" + +class QCustomObject :public QObject { + Q_OBJECT + Q_CLASSINFO("LimitedDouble", "Min=0,Max=10") +public: + enum QCustomEnum { + One, + Two, + Three + }; + Q_ENUM(QCustomEnum); + + Q_PROPERTY_VAR(int, Int) = 0; + Q_PROPERTY_VAR(float, Float) = 1.23f; + Q_PROPERTY_VAR(double, LimitedDouble) = 5; + Q_PROPERTY_VAR(QString, String); + Q_PROPERTY_VAR(QDir, Directory) = QDir("."); + Q_PROPERTY_VAR(QVector2D, Vec2) = QVector2D(1, 2); + Q_PROPERTY_VAR(QVector3D, Vec3) = QVector3D(1, 2, 3); + Q_PROPERTY_VAR(QVector4D, Vec4) = QVector4D(1, 2, 3, 4); + Q_PROPERTY_VAR(QMatrix4x4, Mat4); + Q_PROPERTY_VAR(QColor, Color); + Q_PROPERTY_VAR(QList, ColorList) = { Qt::red,Qt::green,Qt::blue }; + + typedef QMap StringColorMap; + Q_PROPERTY_VAR(StringColorMap, ColorMap) = { {"Red",Qt::red},{"Green",Qt::green},{"Blue",Qt::blue} }; + + Q_PROPERTY_VAR(QCustomEnum, CustomEnum) = QCustomEnum::One; + Q_PROPERTY_VAR(QCustomType, CustomType); + Q_PROPERTY_VAR(QCustomGadget, CustomGadget); + Q_PROPERTY_VAR(QCustomGadget*, CustomGadgetPtr) = new QCustomGadget; + Q_PROPERTY_VAR(QSharedPointer, CustomGadgetSharedPtr) = QSharedPointer::create(); + Q_PROPERTY_VAR(QCustomObject*, SubCustomObject) = nullptr; +}; + +#endif // CustomObject_h__ diff --git a/PropertyEditor/example/CustomType.h b/PropertyEditor/example/CustomType.h new file mode 100644 index 0000000..7674a7e --- /dev/null +++ b/PropertyEditor/example/CustomType.h @@ -0,0 +1,18 @@ +#ifndef CustomType_h__ +#define CustomType_h__ + +#include +#include + +struct QCustomType { + unsigned int ArraySize = 0; + QVector Array; +}; + +static QDebug operator<<(QDebug debug, const QCustomType& it) { + return debug << it.Array; +} + +Q_DECLARE_METATYPE(QCustomType) + +#endif // CustomType_h__ diff --git a/PropertyEditor/example/PropertyTypeCustomization_CustomType.cpp b/PropertyEditor/example/PropertyTypeCustomization_CustomType.cpp new file mode 100644 index 0000000..660db32 --- /dev/null +++ b/PropertyEditor/example/PropertyTypeCustomization_CustomType.cpp @@ -0,0 +1,73 @@ +#include "PropertyTypeCustomization_CustomType.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" +#include +#include +#include +#include "QQuickDetailsViewModel.h" +#include "CustomType.h" +#include "QQuickFunctionLibrary.h" + +void PropertyTypeCustomization_CustomType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) +{ + auto editorSlot = inBuilder->makeNameValueSlot(); + inPropertyHandle->setupNameEditor(editorSlot.first); + auto buttonItem = inBuilder->setupItem(editorSlot.second, R"( + import QtQuick; + import QtQuick.Controls; + Button{ + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + width: 80 + height: 20 + text: "Sort" + } + )"); + + + QQuickFunctionLibrary::connect(buttonItem, SIGNAL(clicked()), inPropertyHandle, [inPropertyHandle]() { + QCustomType customType = inPropertyHandle->getVar().value(); + std::sort(customType.Array.begin(), customType.Array.end()); + inPropertyHandle->setVar(QVariant::fromValue(customType)); + if (auto arrayHandle = inPropertyHandle->findChild("Array")) { + } + }); +} + +void PropertyTypeCustomization_CustomType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + auto arrayHandle = inPropertyHandle->findOrCreateChild( + QMetaType::fromType>(), + "Array", + [inPropertyHandle]() { + return QVariant::fromValue(inPropertyHandle->getVar().value().Array); + }, + [inPropertyHandle](QVariant var) { + QCustomType customType = inPropertyHandle->getVar().value(); + customType.Array = var.value>(); + inPropertyHandle->setVar(QVariant::fromValue(customType)); + } + ); + + auto arraySizeHandle = inPropertyHandle->findOrCreateChild( + QMetaType::fromType(), + "ArraySize", + [inPropertyHandle]() { + return inPropertyHandle->getVar().value().ArraySize; + }, + [inPropertyHandle, arrayHandle](QVariant var) { + QCustomType customType = inPropertyHandle->getVar().value(); + customType.ArraySize = var.toUInt(); + customType.Array.resize(customType.ArraySize); + for (int i = 0; i < customType.ArraySize; ++i) { + customType.Array[i] = QRandomGenerator::global()->bounded(-100000, 100000); + } + inPropertyHandle->setVar(QVariant::fromValue(customType)); + arrayHandle->invalidateStructure(); + } + ); + + inBuilder->addProperty(arraySizeHandle); + inBuilder->addProperty(arrayHandle); +} + diff --git a/PropertyEditor/example/PropertyTypeCustomization_CustomType.h b/PropertyEditor/example/PropertyTypeCustomization_CustomType.h new file mode 100644 index 0000000..3d847cf --- /dev/null +++ b/PropertyEditor/example/PropertyTypeCustomization_CustomType.h @@ -0,0 +1,12 @@ +#ifndef PropertyTypeCustomization_CustomType_h__ +#define PropertyTypeCustomization_CustomType_h__ + +#include "IPropertyTypeCustomization.h" + +class PropertyTypeCustomization_CustomType : public IPropertyTypeCustomization { +protected: + virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override; + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +}; + +#endif // PropertyTypeCustomization_CustomType_h__ diff --git a/PropertyEditor/example/main.cpp b/PropertyEditor/example/main.cpp new file mode 100644 index 0000000..5f0297f --- /dev/null +++ b/PropertyEditor/example/main.cpp @@ -0,0 +1,20 @@ +#include +#include "QDetailsView.h" +#include "CustomObject.h" +#include "CustomType.h" +#include "PropertyTypeCustomization_CustomType.h" +#include "QQuickDetailsViewMananger.h" + +int main(int argc, char** argv) { + QApplication app(argc, argv); + + QCustomObject obj; + obj.setSubCustomObject(new QCustomObject); + obj.getSubCustomObject()->setSubCustomObject(new QCustomObject); + QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization(); + QDetailsView view; + view.setObject(&obj); + view.show(); + + return app.exec(); +} \ No newline at end of file diff --git a/PropertyEditor/resources.qrc b/PropertyEditor/resources.qrc new file mode 100644 index 0000000..0a0e6cc --- /dev/null +++ b/PropertyEditor/resources.qrc @@ -0,0 +1,31 @@ + + + resources/Icon/arrow-left-l.png + resources/Icon/arrow-right-l.png + resources/Icon/close.png + resources/Icon/delete.png + resources/Icon/down.png + resources/Icon/expand.png + resources/Icon/plus.png + resources/Icon/reset.png + resources/Icon/unexpand.png + resources/Icon/up.png + resources/Qml/ValueEditor/ColorBox.qml + resources/Qml/ValueEditor/NumberBox.qml + resources/Qml/ValueEditor/TextComboBox.qml + resources/Qml/ValueEditor/Vec2Box.qml + resources/Qml/ValueEditor/Vec3Box.qml + resources/Qml/ValueEditor/Vec4Box.qml + resources/Qml/ColorPalette/ColorPalette.qml + resources/Qml/ColorPalette/ColorPalette_Dark.qml + resources/Qml/ColorPalette/ColorPalette_Light.qml + resources/Qml/ValueEditor/DirectorySelector.qml + resources/Qml/ValueEditor/FileSelector.qml + resources/Qml/ValueEditor/LineTextBox.qml + resources/Qml/ValueEditor/MultiLineTextBox.qml + resources/Qml/ValueEditor/PointFBox.qml + resources/Qml/ValueEditor/RectFBox.qml + resources/Qml/ValueEditor/SizeFBox.qml + resources/Qml/ValueEditor/BoolBox.qml + + diff --git a/PropertyEditor/resources/Icon/arrow-left-l.png b/PropertyEditor/resources/Icon/arrow-left-l.png new file mode 100644 index 0000000000000000000000000000000000000000..5d98b3c0a258c84b0caf5f330f45ef8fe0400ac6 GIT binary patch literal 2500 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 literal 0 HcmV?d00001 diff --git a/PropertyEditor/resources/Icon/close.png b/PropertyEditor/resources/Icon/close.png new file mode 100644 index 0000000000000000000000000000000000000000..d7ff2d74b41748c6ace7c21b9848fb710721e46b GIT binary patch literal 5692 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 literal 0 HcmV?d00001 diff --git a/PropertyEditor/resources/Icon/expand.png b/PropertyEditor/resources/Icon/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..a1dbcf702a24fde595a06a35a232f115becd3da7 GIT binary patch literal 3220 zcmeHK`#TeCAD`JWhcc8A*$#7GY#hrH{3|AhCK_lNg~`}?`>&vjp)&-eP?-}}10cg9&etAj#Eg#ZA+ zK@8f$frov+7vumh*1FV{^MEhh!O9F!$CO*vKUwfy9? z6ubLn3yb@SI?^W{`<&EMkrW4Mt#fHM9uF-ZSxal{NS#dVv+2Ij18;%)s5w2Vsp8Ih zPHlERr@bq`_wHA`${#Y2!%jl~@@vtS*M zd4A_|=hku|s4;_b33;tOW_utd>6f@gqr5O z6ZUHJ>2Cc$+@O7L$jvschdbRRV(wFDj$)71h%oeaXtmFGem=}<(K^csc~$reLnR9X z2AghV`owOlVkm_IdsNd2Cb$^A%U{pPPyPJ%7$F>KbcSWA@<)gz!g**1$w2zyx56xE zo{;U(9(XLUL0Pn!-H!VvzjoS?V6MnGX;q*m*z;||la-+_^lG6V#1z3&--bDjmY`~S zTd?B>Rt;OS7gS}ZrzGk( zx0;zAmQ-LvyJ&s{Dj2Y}O72qp+&L%EMhkI8ICt#98upb$AT@U0o8M`usFStAJuBwR z2L@KKQqWUCZt@Ri;GHR0-O}iKQ3#t_F=Y`}4y?gR5-0 zyVxy3YB7j;2v_^da0rJF#{t`7d3VyG=LZZ{T>K)Z86%#)v4BzQ)Eu8n)jqy(+B$A8 z<)^Lfh7vS+KgV6b_|M_7KgmDSD6gAH*6xyW;4nr#I}R!HU(Zdtv|@? z0Wt1*PM~d+Zz;u7|K(1oC)lUxv#HV3Y?+TFhJgQsR-}oca8HSUpl^IY4k@Rrx@-Me zg!)qK4|U?L)E&~sQj>RU-@Bsq5BAkR)S*jrR7J+M{50pfZm!Lzrm#bpZUrJWG{KYA zjCr#i&ZX7Uw>jua9@ic_UC(Y`L=URDzs*xDbJ4rZ4TXG3Oy?-b83^QRmL-*$?UpQF z=C~n1g09L0)4T{xWMj--2YB^fL6W_H9mAp@XiMnLG{+nC9*6pygVjgYMsTZFg7=jqB zn0g=pbZuuCV7AxaPvvSbqp}+I8@ok8jVqGRJ6^9o*7juMBkE9FCZ)XOHXr zWBD;_M@h8pji4w&6&D?d@p4Zn$&$8}3(Lzh1iIc{aYHhq8lH4B8*v4kS9a(5392J2 zfvJ@v3b4DzlQpX;RnCd74p0&YO_SxD)ZnTA{U=vJpc6QZ8 zEVgC(ErPgM)P60gc|L-su4X3`pAlJnJ(D8V7m+C{gsqjaInC*xjlLMLF;v%RSE>rk z;BT*@bkEv!G&JJ;IT9B>@)LSm_G2*Ij;{3XKRxOPVUwaD@kGH%ZP~RbY7ETwLb*ht zGfcPG<1+=?XE@U=qVH3l^|gjGGbB;+w%WlA;%mH>yAkpAlh(YPe8B2YbnjXc>g(nY zJ6>a#Ep$|W`+U2e-u~>WWH#6e5pcATM!Cdlt)88qHkGY9pYbX5EQnmE4=1kAMyzDU zja#wYvd%0crGS`^kKIx#M;NR&)!8>u1(Tg<%AsTgk#0+~6^V-jM&Mm8>><_C@XwAD z*BNwW)A-8hyRYVg$y}q_;t@_)Ucytj2ndo*cc|kHEr9(b_j=rmyE5S#|Uz~>_uQ@8Q z$@ZSgZ<;lHYO)o^?;WoDSCkF8|!;*yjrQH?sAr08db5a7l(AlI8 z+Ob1~P=bU_6c+>h%sqbs=dZvckC>;UG9Pqmy`OsNuoRX0@<-rLoA(f3r7lQ#Jfk}Y z+GoTR#!}C<@n*iB-w9s8&tKO(1F1A9?YVXZ; zd7|$LWrN1MksSdzBc>3Righ&L{w+#ibGGA=9FiW`z1Vzi7PhkvP(a`P9F-h6Ufxr5z zE12_dy0?kjXg2QEiFtYgT9Sc&cK>v^#n z$cOq%`B!2GCU$kbCyK|%mSwQ*7Gy@u6&cfd{`dzYYj6tm%FA^OfUrJxx>4N%KsXGv zy<;qhAR6zS8A;`NncWv0Qsx7({HOccp?n|*h|a}rWD;t7S;iG6pM(-3DB0ZOS>L;% zuw=%=IzF2oYmy7S`ZVgYXNWFHchp8%IL879vbAs2|#f2)7dp@o^J^9z$$MQ@Zf3zF# zZ*XL2tXFpsh-YWIujn8!pPh;2Bd|7*WL zfByMr*trjXCNMbiIsRR6W4c29)XjD39-amjeiIn3@plM4VRx7#<2b?d)_i`)w94zZ zIf9gX*aTD#eYNwMaFdV2==AP;ZWE*uO&Syg*L_cHI#cE_A@bf=v!*K?iy2uu3-mT>ulg>XvYh4q?pX>fiAT#h1X>hW6i?hVe^9^ioJFqJPu@r7Czidp-8WT% zrPF?vFF%LV1mFABHQJ6GLiHxI<$%g(@B98EtVuxSUxt~j0*9kn?fZ{wSrk3~T+FF* z5NOKzb?cNZ)f2s;_%8*XKZ@&=I+(u d%WK@v7*N4;{E*^Pv;l|3|=>>>M-2(O~~AyHx|#n`5?8(G4Tt*M9# zjV*f=WoblZ7ruGF>;2=L=Q_`GuJhdIKIc5=y6#k4n+sg*BJ2(V2n7^*! z;7L5|V$x}O9p{s9%xaQCll`1ho?udV!&nNU4t;*Xs4e#jpoCWm-4fw}3HGMA`fIDF z;h1^f_Pa|N5w*a+W9YG3HQnUCe%RWYtXkcE*yFpHUaD?)?tc*KeOI?`O!Nr2i|8v^a1JPDc;~^ z7u8=l9qJ|JxLYl7D{~?Syv2gjS|=3H&V@3!PW1za7l|gIC=*>QJTsoDV%AF?^oNzg z7p&$IGoQt>Vb#=u7Y_?GOE zLQHFY)ICS^fiM?h(h8{no(4#&>6Yppu^sI^JqPBMs2sQkaGk@%TT}@8!3P{8KM1op)i)<{RB$o!62U2vrA9~K(*D}`#H))i&Ow{#z!hSqo+ z&b0owb715r(%O9Yny!#|2y7Xi#BLJDwW)JG=TpgA9)l4A+65*7d#CgEX`pORQ%F*V zHs}gWvP&->zbh*mFXv<|HU=26HS+XMSeNT8oKxRDH`H}txC7E(fO~oC(QNNP0DJvO#zPD)c_9*#f_wAsl-$SA9gx|$Q z9Uo~n zBuJL1s_9TX=qThYYu9d$>|weoSX9X;ss)=3N+QL+?0m% zvSd!}RLVLdO7GZd@yTaWKN7Mi1?fdrh1D%S4J^_^#3}IZb>`@4PZ$S;gNeg>Pgqpt z;ryEiW@o2LZnSP9Z$sRRDl?0ixIgeuT^Z)ZLTP@x-^=5jJT$*iW+a4z7I7DoP?XKw z0Mjqgc{24O-dhoWb93*xZbyWQ^Ib&$+17$+1?sml-3Sqxi+B3@aDXCvfS&u4n?<94 zxuM=2HU5)LX)wj-61u3nRd;iIWKZ*YVVD-UG`;q2hhSY?exen1W59AcAUL&H?omw_ zDSq|G^`__Ij0v?YKYO!*I@9w!zE)FD{gCU=%Q~M2Dv{T+hBfZ~DAlo{)uJOqJHFUT zu-+KIK7LsEJB#TD)w`3p9A+UO_CBY-f%>;{zM02tZCYe>#M_B`xb;!SH#T|>liYg# z_x@q%i^EB#?Naw0AxT80V~?BIj%eA9{RsqepD4tgU({^U+gxg)$c*(pKw8S+Kfi0z9dS3z?fV%Ct=pyF-A#E9 z_pkXZ0iRlzuY(f{a$H;jL3z*(yyW6HRP^SVwOH+8`j%Z2@AwItdv+zA;G1;xivo>c zGtp^3LG$=4x+T3{)i?Y*VqrCBl`Z&K{^-Efx5Y0%jo^X=)SjRpby-laQaN0%jvT!r zn8yYj-QDkHhqnuwU8Z0%j^rA@;4dNH8~+J!ylj)cI>mqVkg5ahx*$8w2Y~mhdKT-3)!q(d)_POq@V{+HXJ5H&k5)#wowYU@IdOV) z-XdgDHTvq@A){i}9*4yqkjJR?Wokx>avH^4Mo0^@;xdt$ktaG$0ciix5ks+Bc^IMT zsla{JEo_8P?l0vDB0x&+V6$b_?TMmQwTDM=>g@|!qb-9SWd?3! z4c%PUybt&VpPe4DK@`G4kw7KfRs-LBpGtX~{ z>~4Sd(07l??H)RknZi6^6RB`IK(<1LeNp8OFSbN@<5H2WiU>3!O+ecqA*m?!=Xd5y2)SG*fS$T2Ss~iJvOjbSn(rD^> z;?Id{t;Xao3|G$Ba+nR`hyuysXBh$~Sb) zYU>p-<&~>d^7`4!^%ka!=eBtMY*x%_Q!2ikE7)`fhT0-Urfe78#1x-t+Gz^JNpo-O ztQ;luP|(BUi|2vCPE?rannxa5ax!#`M}l}Zq^evnkYGh#N%Qsvle`)|DgjKAO}2n> z$m`x#X5M>)@uj@BP~(1@n}&+vZq1Ue8B(v!!4Q4_cO&Y|2u5hHIlha%+wbKNyy9%} z`7z_wOC3*{4ZQA1HhL@rjYY9b$$GJTl%_RgsfX91kTKF%@vm`*7risF%N4f&*veCV zj+6rQ>`4fk0fudiLl@8!hc<4yq^!d8{lO$+!$oDFCPRKs)$zj^tn!<^?neYnb(6aW z@~HuBR5w*F2y=lLa9TMwjXr*khAmG#V~0)JZKtPLEn<;88+7M$&>J*|Nx6~F!C(@p z!9yAN34P3A6=lgGjnAK{k|0_?E_3nMc9Gg@Li-vo9Mu-!1cz67$5W(~@#yPOMQ5a@Ti~LI z5n84g!!6%+n(|*PwwvtKA+g0s4CKWHN)n$ut}1B8rnNs} zg3h+ae39Gqje&D(2>Y+g<0ZLwi!lm;Vo+87u{FN~y)wGxHjLtm)>%3bmY5 z`S6lS&YEH!A z5s_P!Ae!E}`~&KQMQXYRE*>}XJTupVxG<;SnSP4_JR#~-A)}Vh698{nuQ!^i6p)rQM48F{sD1vMyj%{x=4Zr&UsoYG1AO^Ng_P;350OtgtrhR?;}4o8h^ZfON<|U2z3a_XcAsK$sP)J zNm6c)0s1WYkLOAVkJWyVuW(311MV)R;U3dg23KBW`rSH?_)lyq#MPrMH+-F;p63Za}4=nxUaF+86}u#ICs8X&~X~>ZbI`SqEqNVD2t;-q~L>p zHaaTHnX^(_-}p!o%~Qy7_YSXmHfz-@;k6oX1wR2aAQe4NuksdP96oD94@YGSnTL`e zXRO}CeRnL#t}w32KN?Cg=BRScWgcRGa_lFL>i7WV<;Y@V6QMhokDpe?BLcVHnIfYc z<}m*#d^dawcR@Z~pg+mN$(}qpO1_(?vhM2nBPT}CjPWs)A&H8TyggnD2@mc+T`g)3 zo>C~vPh`&8sY1EV0*#st_mqNXn=c%(9gZAIW z<4N)+4i4k9!&6*}tiBx5tfPY|FBIzx8|=#Bqe50v$m3JvjsY+FVDC($V!ocHDPe}l zmz1%wLF!)*hM-!%R?7U1f4sXJ5^+)xR068m+D~FB%7A!=Nu3HZeQ#plEdkrAqAv$^ zR@;vh`iCnKJtB+|&Y@YKXq%%)bn(%?6L^!$UPb#haV!An;^&kvve&Liaj&{hRgNkl zMtvMY9H-WKX)7xxQhJXIil^{N&r30Z$3&F$rC63^dax(kviR27w zN57kK_A%DbCU%qI93f$po*bTlGkJPJF|~w^Y=@le-`J8|(n;k94u{X3{*k%+l^=TP zZ5`TvD6(1$_!*k?)Qi2~Wx~j2{K#d|`m^nEM#p z{NvyD!B6b^VRn{!qa`z&1K}H+{rca=AiU=zWimz0liyamAp@J`P9?h^M8!Mbekzvx zQ*=91KG_2CFq4x(o$STvuBE6m*(|j+eY@A-!Gc+$KDdrIUxw9P2Y(~Y?JT= zBi~H;RX)No+b`dVn4DqA0 z5QrT#D%k~IyZ#+$S$MwVAu53vWP}UlFhbO;CP5(NB{VYR#`d1S`fP!86yMo$`^0_l zyNn{v!SwQv1{;XT?u+-fIqXqZs#3&7u@g0?OjSTxqV4vbS$5jj+O!^^+Pq$y9s;08 zkPQ)+c)%sbFMqOmu)e8j&F!kQPKzJEqVquMKxyRiJf|`nw-o@Z@>;{H5)i;EoDHT| z0nGyA!D4j`Y$MS**f;VnPqMrrG)Uzg7MxK*0($Rk?}7k^Nd1FWlZoKwgq6`TD72#R zFQKO`0(#o;!T^l~dkbnQ{mcH6fo-TF zxmrS(GwJF*&0H_iJbwwXCDq0eWrY4xCw#Y+7`LAm{!o}LTU#y%h=B*&zNpep_MHif zOibnLS_Y3~zsKxPiDyEr?#SM>LWWVdNZTY+IZ^aTj*Xu_%l|ufDf#m9ISYoPNcn)m zM55wn{jLJOsM13));~Did(qe24T19BQhg)Ws5(ePiFjOLP(6QfmH5V;w_txTlrZf~ zq=n=MoKKlgvmryj%+#c2MB_HyD9gm$rvPD_kM5)+|AbqCL0i;eunDX*|J!-x(AvQY zgFrVzJV;6WPE(w@V|QNVW*7<`&O(+t^G}SigXLOKNWgRF;t(xI2EX5eTn1qLBHnJ~ z604c!n}M}YlB~E0?)Jo(lZrF-ZG{83lu&3fgA}84+gH#eSlE7NKQw+o%q96@)QcZ2{F4+N`4!o z6V+(EZ#UN=al?fs9m9?N#w|OILYp%d=L`&@L&Y&oddla5;Df!VyVZ>gbypmI##_A9 zjtB3hi!}O()h#jai}(dk6m59bK>epp2Yynhd}wEI^((Y^`)ZLucg4iQszw3M^ayP* zlsTzTN4gR>y6nCFR;5}N2~MCg{r&{Wy_t{?I zBk!Z7p_f$Tz^=?HijzMbi|Y{St69KZTwp^lPVuK7&2RhzL!@-gZr02dRv4qraUare zo*vnY9UiPgo148UNMXX80H6RphiVhg6(SCD9S~7(H9e!<4G~BzUYfz{Qrdzp5dQrLLd3_(cz;q#l!qcC1 zY=Gt-wv(&Zazr};Nz(&?Hna4%0-`jXUcZ$a=`}i{Si{tgzVPJ(%c6Hbmirui|6Rpv zXS(1k=NT4?t^6SVHvH*s@W{Q;5z~)7m%wEY^Zre_6M5usoKMFv!e#l@tW62Ad-?Yx z&nYS3Ion!gN-I$9e?@L!JOS*8+zKF{>g}@6UzwE zq!!~|MBMyY5SiDBz_$CkPjxJn2nD3(k490z;xCsQ7K=3{wxJTOwEBzfS+n=mHHZZ2 zIIiF8{Um=nWGfz6j4p)FBWW#P^Q^h-O6qfViM^#ngJZ7&J#Z(UIW$XKP3V02uV z-)-1nR!AT9TkZL3n*k&PAn=AuaOXL?Vp-#SJVrQ=>*Fj*gX#xGpI`eCXP~< zQxwE|()X&Mtwpap?9}^nN<|9Lt|OB#+v~>CjLM~Z@KMR9utHROZ|ZQ-^#0*2i+ zta#3?xxzU-=3I;DOa&uwE0(3fYH`{E#{pRvRR}j!4`s~0kOXkC1x$CA>BjhBO zkhTyzYJbMz(tn|1xsXs+nef}xfcHSW1#`9})|iwsm` zl+3s>N;0xjz$m-c@LLL$m@=@dfnQa;yR$DY6woulKC1|i?pY%9=I#COYS-L}`bU3N zI#>890UXPH-d?pLZEj&${#tK`fKuFhy=iBBJ_m_UKWGX}28-+i$3EpNOIx$GK$!4O zE~yICvbP*Ep&oz{+1EKak(Sw9OoHV@SzkgWU+r#KkWDKZ>?)sqlt7f_j8AnO2QcK( z-06RK5i+P!9gHV$OrX4WzzD3m1e37>$&4MU;qCV?(W~-rvXk?Jzq9)Gs`v5#Kmux$ z!V8}L7I=dt7sWPjRyQ)?5%9npGDLE0V!pg_cx5?$b(-D$*ke036BZ8-b-Ja08d<5^ z`HZ_9M682vJb5_cgr>w`i@$hn``X%{*Gw4TZ%Ob?!!AL;YHo_1*d#qnX>W?n4%F){ zp0qDN(k91wpH=zBmm>pr=w>1l@CmyB&yLuO!U57%Z3&rnGG~dkx}TjbwA3>If^Y#e z-e{wRIxJ`~#EdlA%W@!f`lEo8hv52QUx4OFApJyKcpuEJhJ@W@m~}5#I{)p7Li3N3 z6T9Fi;mh4t_6SIv7Jw;0aLFR;MkefG7j_2$X8*21ofShQ&`r1w`N1Nuado-`=3!6r mo;0|-2=vnNn*Sr{n-;r_zlNz%G8(V~M$nErkwu43asLBhkqlS> literal 0 HcmV?d00001 diff --git a/PropertyEditor/resources/Icon/up.png b/PropertyEditor/resources/Icon/up.png new file mode 100644 index 0000000000000000000000000000000000000000..ff302d53952bb32a3d692a0bb3952f74f6952042 GIT binary patch literal 2400 zcmeHJ`!`$J9*@Y}gd&M&J+3)PPXw*0>CBR5sv{vGXDFpq+Za`}9;L2OLi&j4B{8JQ z5sHb&RdlqAsb;FGMLTpvt+J|*cns}GQZ=n=l;TqN9{<6;zt3K4@9$pc^ZoAcXMfh& z`@1Fkg9FS>9ZXRulsUvQhE#+E~^HHb0F2n+BOshwZU56XGc!fr0^PHY1B1|lJa4nrg9EhVF)@IK5o*m21tA!e z)N|Ixshad*L5->=p`~m2NKXO}JFn8HH1j>@=9ar=l*^OUD1z{wKYA_3*a(;6A_Ri4 zn2&%ED8^z47wm;XDT*qVI$nbb3>nCL(j-NRUU#&)n;2mr%db=a!~uVS|Covaomz2YhTaFoxuuSf0n2gxcLOz!>s)qB6w^|5-Id#vlO_udddZG!S z7B{yg(9pQ)f+}nY2gURcD%pF7$RN<3dMC2bxLvWyZ^uedqK<#PVs1ltc83Y-P9-d* z9laCS;fR@~E|MR7I=u&9go86>=Aul3Z`m zz&N_%09h6(NNx;`>+=Egv3W8U{@Sd_s_*k|e+$qsEU{`Z`atIaQ2xE$%=NmR zBrLq5e_%4L&V}f*zAtwN69l8SJV0{xloGHg_Rupd3sH3DTR@=B1G84sMO$wyDU2br zd>!Vn^QkR#S^TOKooQClzWEWY#~iju#`AyyUJg&RLw$pa;P+$b%vZf{LoL9{+U-;| z+DuSx4B6r;tXTMT3|+>m-APrOWo;YC1HTfz!i@cWN{qz6qi2X*D@t}?VOR7Frqbe( zpS0)CENMlz$Fu77c7T&Jc5j6&dU4DR`~yGf{8gv< zl-h~>Y()<~x_k1Qr0GxN6SDXcC(*i{xX|fEIDKj|y=doESb105`F&ad>?bCvdpwbJ zS05TSS`TpNd#<0mbGnH4>FIG!7(`P000`#GYPFT)uLRE)tPTAPE4@@h?Rw6yY1!GO zT9tc$|3njSS~J`NE59_Hn@gRg44f13l}@$c^lzUbA*T}}p`jxk?{aTHh2w4e{G=DJ zI@LOw8!E{QBC#?q{CtF03wZc5xAXYxxY(St`}Hd$3nVfxw@l})ooP^jmjP$q-AMh) zI6pB`A7Uf7W9!h`nZ{D^M+8u4Xswp=w35QpU1xI6>L^6F|JbDDG{Ru%CK&nK4^A(* z!LOZf4sM@g!wdEG`Z9lbhlsBZojMSqTY1deMSc3k{FpnexOL@Fv>P}8AR_4}JJ`}% z4=S&4y#ELFAUSO6bz;sxFM*w#OAe=7)6d;hveD zH#WrEzw&$k03|M)1o8tl_V}uZ)rEHJlWl&GB}Bl}Yl!kUElrx{cPa}}p&Tqi$aqu8 zwB6=yOK~+>guNF)A_n<4Oo+D!a+9my__&d24n7%t$lnf;yq~SJ)iRSKx)-xlhTg*R z_;@5YKZteeE9biB`oN2~9MWPBLH;&~YXDnkp}jxbcxHAfOZ&*oZ9u%a2eJ6~k91-Q**n$+({=G7X6i_zVl9U6XVFK5># zRVPp9qQ?*CX;beBaPg+dzo{6j=9tb)4cScDwr=kCfg>tFO*|@%D4hIm1i3%u0n%2= zY$!perUu-C7hP~XUD@S8DAy7>sh9UaxkSXqRWs~-I+g((59oJ6KcXagY|gyU`zT3n zI9`gQdH2+M0(?*eV0(ME=eba!ElgRQ=JCso6I979Kz;xEFy&<~%r_M|Wd!6ey`xGl z0nX<=OAQ0;dwS4d2}WjSIu@Wr7bLL%4x}J{7Li;)5(3BS8;D!bxL9Uc)iE5vsc=o| z6QM+h_BWo`ipE*GeCN=HR9V~bX8XTF|38`5&7&;LMpOM%T;$1wg4n^VR^J%We*rZA B`vm|1 literal 0 HcmV?d00001 diff --git a/PropertyEditor/resources/Qml/ColorPalette/ColorPalette.qml b/PropertyEditor/resources/Qml/ColorPalette/ColorPalette.qml new file mode 100644 index 0000000..c445a74 --- /dev/null +++ b/PropertyEditor/resources/Qml/ColorPalette/ColorPalette.qml @@ -0,0 +1,9 @@ +import QtQuick + +pragma Singleton + +QtObject { + id: colorPalette + property var theme : ColorPalette_Light +} + \ No newline at end of file diff --git a/PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Dark.qml b/PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Dark.qml new file mode 100644 index 0000000..a946b7a --- /dev/null +++ b/PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Dark.qml @@ -0,0 +1,27 @@ +import QtQuick + +pragma Singleton + +QtObject { + property color labelPrimary : "#FFFFFF" + property color textPrimary: "#EEEEEE" + + property color textBoxBackground: "#444444" + + property color rowBackground: "#333333" + property color rowBackgroundHover: "#888888" + property color rowBorder: "#666666" + property color rowIndicator: "#CCCCCC" + property color rowSplitter: "#666666" + property color rowShadowStart: "#00000000" + property color rowShadowEnd: "#66000000" + + property color boxHover: "#A478DB" + + property color comboBoxBackground: "#444444" + property color comboBoxItemBackground: "#666666" + property color comboBoxItemBackgroundHover: "#888888" + + property color buttonBackground: "#111111" +} + \ No newline at end of file diff --git a/PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Light.qml b/PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Light.qml new file mode 100644 index 0000000..e6550e8 --- /dev/null +++ b/PropertyEditor/resources/Qml/ColorPalette/ColorPalette_Light.qml @@ -0,0 +1,27 @@ +import QtQuick + +pragma Singleton + +QtObject { + property color labelPrimary : "#444444" + property color textPrimary: "#666666" + + property color textBoxBackground: "#f8fef9" + + property color rowBackground: "white" + property color rowBackgroundHover: "#F3F3F3" + property color rowBorder: "#EEEEEE" + property color rowIndicator: "#cef9ce" + property color rowSplitter: "#EEEEEE" + property color rowShadowStart: "#00000000" + property color rowShadowEnd: "#14000000" + + property color boxHover: "#cef9ce" + + property color comboBoxBackground: "#f8fef9" + property color comboBoxItemBackground: "#FFFFFF" + property color comboBoxItemBackgroundHover: "#cef9ce" + + property color buttonBackground: "#cef9ce" +} + \ No newline at end of file diff --git a/PropertyEditor/resources/Qml/ValueEditor/BoolBox.qml b/PropertyEditor/resources/Qml/ValueEditor/BoolBox.qml new file mode 100644 index 0000000..828f24a --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/BoolBox.qml @@ -0,0 +1,184 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Shapes + +Item { + id: control + + // 保持与框架一致的接口 + property var value: false + property int decimals: 0 + property string text: "Enabled" + property color textColor: "black" + property color checkedColor: "#2196F3" + property color uncheckedColor: "#cccccc" + property color checkedBoxColor: "white" + property color uncheckedBoxColor: "white" + property int boxSize: 16 + property int spacing: 8 + property int textSize: 12 + property alias font: label.font + + // 与框架一致的信号 + signal asValueChanged(value: var) + signal boolValueChanged(bool checked) // 辅助信号,避免与内置信号冲突 + + implicitHeight: Math.max(boxSize, label.implicitHeight) + implicitWidth: boxSize + spacing + label.implicitWidth + + function toggle() { + var newValue = !getBoolValue() + setValue(newValue) + } + + function setValue(newValue) { + var boolValue = getBoolValue() + var newBoolValue = toBool(newValue) + + if (boolValue !== newBoolValue) { + value = newBoolValue + boolValueChanged(newBoolValue) + asValueChanged(newBoolValue) + } + } + + function getBoolValue() { + return toBool(value) + } + + function toBool(value) { + if (typeof value === 'boolean') { + return value + } else if (typeof value === 'number') { + return value !== 0 + } else if (typeof value === 'string') { + var str = value.toString().toLowerCase() + return str === 'true' || str === '1' || str === 'on' + } + return false + } + + // 整个区域的点击事件 + MouseArea { + anchors.fill: parent + + onClicked: function(mouse) { + control.toggle() + mouse.accepted = true + } + } + + RowLayout { + anchors.fill: parent + spacing: control.spacing + + // 复选框 + Rectangle { + id: checkBox + width: control.boxSize + height: control.boxSize + radius: 3 + border.color: getBoolValue() ? control.checkedColor : control.uncheckedColor + border.width: 1 + color: getBoolValue() ? control.checkedColor : control.uncheckedBoxColor + + Layout.alignment: Qt.AlignVCenter + + // 勾选标记 + Shape { + anchors.centerIn: parent + width: 10 + height: 8 + visible: getBoolValue() + + ShapePath { + strokeColor: "transparent" + fillColor: control.checkedBoxColor + strokeWidth: 0 + + PathMove { x: 0; y: 4 } + PathLine { x: 4; y: 8 } + PathLine { x: 10; y: 0 } + PathLine { x: 9; y: 0 } + PathLine { x: 4; y: 7 } + PathLine { x: 1; y: 4 } + } + } + + // 悬停效果 + Rectangle { + anchors.fill: parent + radius: parent.radius + color: "transparent" + border.color: "#888888" + border.width: boxMouseArea.containsMouse ? 1 : 0 + opacity: boxMouseArea.containsMouse ? 0.3 : 0 + } + + // 复选框自身的点击事件 + MouseArea { + id: boxMouseArea + anchors.fill: parent + hoverEnabled: true + + onClicked: function(mouse) { + control.toggle() + mouse.accepted = true + } + } + } + + // 标签文本 + Label { + id: label + text: control.text + color: control.textColor + font.pixelSize: control.textSize + verticalAlignment: Text.AlignVCenter + + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + + // 鼠标悬停在文本上时的点击事件 + MouseArea { + anchors.fill: parent + + onClicked: function(mouse) { + control.toggle() + mouse.accepted = true + } + } + } + } + + // 键盘支持 + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Space || event.key === Qt.Key_Return) { + control.toggle() + event.accepted = true + } + } + + // 聚焦支持 + activeFocusOnTab: true + + // 聚焦时的边框 + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: control.activeFocus ? "#2196F3" : "transparent" + border.width: 2 + radius: 2 + visible: control.activeFocus + } + + // 当value从外部改变时,更新显示 + onValueChanged: { + var boolValue = getBoolValue() + if (checkBox.border.color !== (boolValue ? control.checkedColor : control.uncheckedColor)) { + checkBox.border.color = boolValue ? control.checkedColor : control.uncheckedColor + checkBox.color = boolValue ? control.checkedColor : control.uncheckedBoxColor + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/ColorBox.qml b/PropertyEditor/resources/Qml/ValueEditor/ColorBox.qml new file mode 100644 index 0000000..5fc7ca0 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/ColorBox.qml @@ -0,0 +1,96 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +Item { + id: control + property color value + implicitHeight: 25 + implicitWidth: 150 + signal asValueChanged(text: var) + + // 计算颜色代码(hex格式) + function getColorCode(color) { + if (color.a === 1) { + // 不透明颜色 + return color.toString().toUpperCase() + } else { + // 透明颜色 + return Qt.rgba(color.r, color.g, color.b, color.a).toString() + } + } + + function setValue(newValue: var) { + if (newValue !== value) { + value = newValue + asValueChanged(value) + } + } + + Row { + anchors.fill: parent + spacing: 5 + + // 左边颜色显示矩形 + Rectangle { + id: colorRect + width: 25 + height: parent.height + radius: 2 + border.width: 1 + border.color: Qt.darker(colorRect.color, 1.5) + color: value + + MouseArea { + anchors.fill: parent + onClicked: { + colorDialog.open() + } + } + } + + // 右边颜色代码显示(不可编辑) + Rectangle { + width: parent.width - colorRect.width - parent.spacing + height: parent.height + radius: 2 + border.width: 1 + border.color: "#cccccc" + + Text { + id: colorText + anchors.fill: parent + anchors.margins: 2 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + text: getColorCode(value) + font.pixelSize: 12 + elide: Text.ElideRight + + // 颜色值改变时更新文本 + Connections { + target: control + function onValueChanged() { + colorText.text = getColorCode(control.value) + } + } + } + + // 点击整个右侧区域也可以打开颜色选择 + MouseArea { + anchors.fill: parent + onClicked: { + colorDialog.open() + } + } + } + } + + ColorDialog { + id: colorDialog + selectedColor: value + onAccepted: { + control.setValue(selectedColor) + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/DirectorySelector.qml b/PropertyEditor/resources/Qml/ValueEditor/DirectorySelector.qml new file mode 100644 index 0000000..9f48888 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/DirectorySelector.qml @@ -0,0 +1,63 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts +import QtCore +import ColorPalette + +Item{ + id: control + property var value + implicitHeight: dirBox.implicitHeight + signal asValueChanged(text: var) + + function setValue(newValue: var){ + if(newValue !== value){ + value = newValue + dirBox.value = value + asValueChanged(value) + } + } + + LineTextBox { + id: dirBox + value: control.value + anchors.left: parent.left + anchors.right: button.left + anchors.verticalCenter: parent.verticalCenter + onValueChanged: { + control.setValue(value) + } + } + + Button { + id: button + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + width: 30 + height: 25 + text: "..." + palette.buttonText: ColorPalette.theme.textPrimary + background: Rectangle { + color: ColorPalette.theme.buttonBackground + } + + onClicked: { + folderDialog.open() + } + } + FolderDialog { + id: folderDialog + title: "选择目录" + onAccepted: { + var filePath = currentFolder.toString(); + + if (filePath.startsWith("file:///")) { + filePath = filePath.substring(8); + } + + control.setValue(filePath); + } + } +} + diff --git a/PropertyEditor/resources/Qml/ValueEditor/FileSelector.qml b/PropertyEditor/resources/Qml/ValueEditor/FileSelector.qml new file mode 100644 index 0000000..9e5584d --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/FileSelector.qml @@ -0,0 +1,102 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts +import QtCore +import ColorPalette + +Item { + id: control + + // 属性接口 + property var value + property string fileFilter: "svg(*.svg)" // 文件过滤器 + property bool selectMultiple: false // 是否多选 + + implicitHeight: fileBox.implicitHeight + signal asValueChanged(text: var) + + function setValue(newValue: var) { + if (newValue !== value) { + value = newValue + fileBox.value = value + asValueChanged(value) + } + } + + LineTextBox { + id: fileBox + value: control.value + anchors.left: parent.left + anchors.right: button.left + anchors.verticalCenter: parent.verticalCenter + + onValueChanged: { + control.setValue(value) + } + } + + Button { + id: button + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + width: 30 + height: 25 + text: "..." + palette.buttonText: ColorPalette.theme.textPrimary + background: Rectangle { + color: ColorPalette.theme.buttonBackground + } + + onClicked: { + fileDialog.open() + } + } + + FileDialog { + id: fileDialog + modality: Qt.WindowModal // 改为窗口模态 + options: FolderDialog.DontUseNativeDialog // 尝试禁用原生对话框 + title: control.selectMultiple ? "选择多个文件" : "选择文件" + fileMode: control.selectMultiple ? FileDialog.OpenFiles : FileDialog.OpenFile + + // 设置文件过滤器 + nameFilters: { + if (control.fileFilter) { + return control.fileFilter.split(";;") + } + return ["所有文件 (*.*)"] + } + + onAccepted: { + if (control.selectMultiple) { + // 多选模式 + var filePaths = [] + + for (var i = 0; i < selectedFiles.length; i++) { + var filePath = selectedFiles[i].toString() + filePath = removeFileProtocol(filePath) + filePaths.push(filePath) + } + + // 多个文件用分号分隔 + control.setValue(filePaths.join(";")) + } else { + // 单选模式 + var filePath = selectedFile.toString() + filePath = removeFileProtocol(filePath) + control.setValue(filePath) + } + } + } + + // 辅助函数:移除文件协议前缀 + function removeFileProtocol(path) { + if (path.startsWith("file:///")) { + return path.substring(8) + } else if (path.startsWith("file://")) { + return path.substring(7) + } + return path + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/LineTextBox.qml b/PropertyEditor/resources/Qml/ValueEditor/LineTextBox.qml new file mode 100644 index 0000000..6617d79 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/LineTextBox.qml @@ -0,0 +1,72 @@ +import QtQuick; +import QtQuick.Controls; +import ColorPalette + +Item{ + id: control + property var value + implicitHeight: lineEditor.implicitHeight + 2 + signal asValueChanged(text:var) + function setValue(newText:var){ + if(newText !== value){ + value = newText + asValueChanged(value) + } + } + Rectangle { + anchors.fill: parent + border.color: ColorPalette.theme.textBoxBackground + color: ColorPalette.theme.textBoxBackground + border.width: 1 + clip: true + TextInput{ + id: lineEditor + enabled: true + clip: true + padding : 3 + anchors.fill: parent + anchors.leftMargin: 2 + anchors.rightMargin: 2 + text: control.value + color: ColorPalette.theme.textPrimary + wrapMode: TextInput.WordWrap + verticalAlignment: Text.AlignVCenter + onEditingFinished:{ + setValue(lineEditor.text) + } + } + MouseArea{ + id: hoverArea + hoverEnabled: true + propagateComposedEvents: true + anchors.fill: parent + onEntered:{ + exitAnimation.stop() + enterAnimation.start() + hoverArea.cursorShape = Qt.IBeamCursor + } + onExited:{ + enterAnimation.stop() + exitAnimation.start() + hoverArea.cursorShape = Qt.ArrowCursor + } + onPressed: (mouse)=> mouse.accepted = false + onReleased:(mouse)=> mouse.accepted = false + onClicked:(mouse)=> mouse.accepted = false + onDoubleClicked:(mouse)=> mouse.accepted = false + } + ColorAnimation on border.color{ + id: enterAnimation + to: ColorPalette.theme.boxHover + duration: 100 + running: false + } + ColorAnimation on border.color{ + id: exitAnimation + to: ColorPalette.theme.textBoxBackground + duration: 100 + running: false + } + } +} + diff --git a/PropertyEditor/resources/Qml/ValueEditor/MultiLineTextBox.qml b/PropertyEditor/resources/Qml/ValueEditor/MultiLineTextBox.qml new file mode 100644 index 0000000..df1ff01 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/MultiLineTextBox.qml @@ -0,0 +1,83 @@ +import QtQuick; +import QtQuick.Controls; +import ColorPalette + +Item{ + id: control + property var value + implicitHeight: lineEditor.implicitHeight + 2 + signal asValueChanged(text:var) + function setValue(newText:var){ + if(newText !== value){ + value = newText + asValueChanged(value) + } + } + Rectangle { + anchors.fill: parent + border.color: ColorPalette.theme.textBoxBackground + color: ColorPalette.theme.textBoxBackground + border.width: 1 + clip: true + TextArea{ + id: lineEditor + enabled: true + clip: true + padding: 3 + anchors.fill: parent + anchors.leftMargin: 2 + anchors.rightMargin: 2 + text: control.value + color: ColorPalette.theme.textPrimary + wrapMode: TextInput.WordWrap + verticalAlignment: Text.AlignVCenter + onEditingFinished:{ + setValue(lineEditor.text) + } + Keys.onPressed: { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + if (event.modifiers & Qt.ShiftModifier) { + // Shift+Enter:保留换行,不处理事件 + } else { + // 单独按Enter:触发编辑完成,不换行 + event.accepted = true; + lineEditor.editingFinished(); + } + } + } + } + MouseArea{ + id: hoverArea + hoverEnabled: true + propagateComposedEvents: true + anchors.fill: parent + onEntered:{ + exitAnimation.stop() + enterAnimation.start() + hoverArea.cursorShape = Qt.IBeamCursor + } + onExited:{ + enterAnimation.stop() + exitAnimation.start() + hoverArea.cursorShape = Qt.ArrowCursor + } + onPressed: (mouse)=> mouse.accepted = false + onReleased:(mouse)=> mouse.accepted = false + onClicked:(mouse)=> mouse.accepted = false + onDoubleClicked:(mouse)=> mouse.accepted = false + } + ColorAnimation on border.color{ + id: enterAnimation + to: ColorPalette.theme.boxHover + duration: 100 + running: false + } + ColorAnimation on border.color{ + id: exitAnimation + to: ColorPalette.theme.textBoxBackground + duration: 100 + running: false + } + } +} + diff --git a/PropertyEditor/resources/Qml/ValueEditor/NumberBox.qml b/PropertyEditor/resources/Qml/ValueEditor/NumberBox.qml new file mode 100644 index 0000000..2178bb6 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/NumberBox.qml @@ -0,0 +1,159 @@ +import QtQuick; +import QtQuick.Controls; +import ColorPalette + +Item{ + id: control + implicitHeight: 25 + implicitWidth: 100 + property bool isLimited: false + property bool isHovered: false + property var min:0 + property var max:100 + property var number: 0 + property real step : 1 + property int precision: 3 + property bool isMousePressed: false + + signal valueChanged(number:var) + function setNumber(value:var){ + if(value !== number && !isNaN(value)){ + number = value + if(min < max){ + if(number>max){ + number = max + } + if(number{ + if(mouse.button === Qt.LeftButton){ + control.isMousePressed = true + lastPressX = mouse.x + lastPressY = mouse.y + cursorShape = Qt.BlankCursor + } + } + onReleased: + (mouse)=>{ + control.isMousePressed = false + lastPressX = -1 + lastPressY = -1 + cursorShape = Qt.SplitHCursor + if(!isHovered){ + enterAnimation.stop() + exitAnimation.start() + } + } + onPositionChanged: + (mouse)=>{ + if(!input.enabled && mouse.buttons&Qt.LeftButton){ + if(!isLimited){ + var offset = mouse.x - lastPressX + setNumber(number + offset * step) + var global = dragArea.mapToGlobal(lastPressX, lastPressY) + var local = dragArea.mapFromGlobal(global.x,global.y) + helper.setCursorPos(global.x,global.y) + } + else{ + var xPercent = Math.max(0, Math.min(1, mouse.x / dragArea.width)) + var range = max - min + var newValue = min + xPercent * range + control.setNumber(newValue) + const validMouseX = Math.max (0, Math.min (dragArea.width, mouse.x)); + const validMouseY = Math.max (0, Math.min (dragArea.height, mouse.y)); + if (mouse.x !== validMouseX || mouse.y !== validMouseY) { + const validGlobalPos = dragArea.mapToGlobal (validMouseX, validMouseY); + helper.setCursorPos (validGlobalPos.x, validGlobalPos.y); + } + } + } + } + } + ColorAnimation on border.color{ + id: enterAnimation + to: ColorPalette.theme.boxHover + duration: 100 + running: false + } + ColorAnimation on border.color{ + id: exitAnimation + to: ColorPalette.theme.textBoxBackground + duration: 100 + running: false + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/PointFBox.qml b/PropertyEditor/resources/Qml/ValueEditor/PointFBox.qml new file mode 100644 index 0000000..c323fbe --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/PointFBox.qml @@ -0,0 +1,184 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: control + + property var value: Qt.point(0, 0) + property int decimals: 2 + implicitHeight: 25 + implicitWidth: 120 // 设置默认宽度 + + signal asValueChanged(value: var) + + function setValue(newValue: var) { + if (!control.value || + Math.abs(control.value.x - newValue.x) > 0.000001 || + Math.abs(control.value.y - newValue.y) > 0.000001) { + value = newValue + asValueChanged(value) + } + } + + RowLayout { + anchors.fill: parent + spacing: 4 // 减小间距 + + Label { + text: "X:" + Layout.alignment: Qt.AlignVCenter + font.pixelSize: 10 + } + + TextField { + id: xBox + Layout.preferredWidth: 60 // 减小宽度 + Layout.preferredHeight: 22 + Layout.alignment: Qt.AlignVCenter + text: control.value ? control.value.x.toFixed(control.decimals) : "0.00" + font.pixelSize: 10 + leftPadding: 4 + rightPadding: 4 + selectByMouse: true + + // 双击全选的处理 + MouseArea { + id: xMouseArea + anchors.fill: parent + propagateComposedEvents: true + acceptedButtons: Qt.LeftButton + cursorShape: Qt.IBeamCursor + + onDoubleClicked: function(mouse) { + parent.selectAll() + parent.forceActiveFocus() + mouse.accepted = true + } + + onClicked: function(mouse) { + // 单击时设置焦点但不全选 + parent.forceActiveFocus() + mouse.accepted = false + } + } + + onActiveFocusChanged: { + if (activeFocus) { + // 如果不是双击触发的焦点变化,就全选文本 + if (!xMouseArea.containsPress) { + selectAll() + } + } + } + + validator: DoubleValidator { + bottom: -999999 + top: 999999 + decimals: 2 + } + + background: Rectangle { + color: xBox.enabled ? "white" : "#f0f0f0" + border.color: xBox.activeFocus ? "#2196F3" : "#cccccc" + border.width: 1 + radius: 2 + } + + onEditingFinished: { + var xValue = parseFloat(text) + if (!isNaN(xValue) && control.value) { + control.setValue(Qt.point(xValue, control.value.y)) + } else { + xBox.text = control.value ? control.value.x.toFixed(control.decimals) : "0.00" + } + } + } + + Label { + text: "Y:" + Layout.alignment: Qt.AlignVCenter + font.pixelSize: 10 + } + + TextField { + id: yBox + Layout.preferredWidth: 60 + Layout.preferredHeight: 22 + Layout.alignment: Qt.AlignVCenter + text: control.value ? control.value.y.toFixed(control.decimals) : "0.00" + font.pixelSize: 10 + leftPadding: 4 + rightPadding: 4 + selectByMouse: true + + // 双击全选的处理 + MouseArea { + id: yMouseArea + anchors.fill: parent + propagateComposedEvents: true + acceptedButtons: Qt.LeftButton + cursorShape: Qt.IBeamCursor + + onDoubleClicked: function(mouse) { + parent.selectAll() + parent.forceActiveFocus() + mouse.accepted = true + } + + onClicked: function(mouse) { + // 单击时设置焦点但不全选 + parent.forceActiveFocus() + mouse.accepted = false + } + } + + onActiveFocusChanged: { + if (activeFocus) { + // 如果不是双击触发的焦点变化,就全选文本 + if (!yMouseArea.containsPress) { + selectAll() + } + } + } + + validator: DoubleValidator { + bottom: -999999 + top: 999999 + decimals: 2 + } + + background: Rectangle { + color: yBox.enabled ? "white" : "#f0f0f0" + border.color: yBox.activeFocus ? "#2196F3" : "#cccccc" + border.width: 1 + radius: 2 + } + + onEditingFinished: { + var yValue = parseFloat(text) + if (!isNaN(yValue) && control.value) { + control.setValue(Qt.point(control.value.x, yValue)) + } else { + yBox.text = control.value ? control.value.y.toFixed(control.decimals) : "0.00" + } + } + } + + Item { + Layout.fillWidth: true + } + } + + // 当value从外部改变时,更新输入框显示 + onValueChanged: { + if (value) { + if (!xBox.activeFocus) { + xBox.text = value.x.toFixed(decimals) + } + if (!yBox.activeFocus) { + yBox.text = value.y.toFixed(decimals) + } + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/RectFBox.qml b/PropertyEditor/resources/Qml/ValueEditor/RectFBox.qml new file mode 100644 index 0000000..e194763 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/RectFBox.qml @@ -0,0 +1,136 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: control + + property var value: Qt.rect(0, 0, 0, 0) + property int decimals: 1 + implicitHeight: 25 + implicitWidth: 240 + + signal asValueChanged(value: var) + + function setValue(newValue) { + if (!control.value || + Math.abs(control.value.x - newValue.x) > 0.000001 || + Math.abs(control.value.y - newValue.y) > 0.000001 || + Math.abs(control.value.width - newValue.width) > 0.000001 || + Math.abs(control.value.height - newValue.height) > 0.000001) { + value = newValue + asValueChanged(value) + } + } + + RowLayout { + anchors.fill: parent + spacing: 2 + + Repeater { + id: repeater + model: [ + { label: "X", prop: "x", validator: [-999999, 999999] }, + { label: "Y", prop: "y", validator: [-999999, 999999] }, + { label: "W", prop: "width", validator: [0, 999999] }, + { label: "H", prop: "height", validator: [0, 999999] } + ] + + RowLayout { + id: inputRow + spacing: 2 + Layout.alignment: Qt.AlignVCenter + + property string propertyName: modelData.prop + property var textField: inputField + + Label { + text: modelData.label + ":" + font.pixelSize: 10 + } + + TextField { + id: inputField + Layout.preferredWidth: 60 + Layout.preferredHeight: 22 + text: control.value ? control.value[modelData.prop].toFixed(control.decimals) : "0.0" + font.pixelSize: 9 + padding: 2 + selectByMouse: true + + // 双击全选的处理 + MouseArea { + id: fieldMouseArea + anchors.fill: parent + propagateComposedEvents: true + acceptedButtons: Qt.LeftButton + cursorShape: Qt.IBeamCursor + + onDoubleClicked: function(mouse) { + parent.selectAll() + parent.forceActiveFocus() + mouse.accepted = true + } + + onClicked: function(mouse) { + // 单击时设置焦点但不全选 + parent.forceActiveFocus() + mouse.accepted = false + } + } + + onActiveFocusChanged: { + if (activeFocus) { + // 如果不是双击触发的焦点变化,就全选文本 + if (!fieldMouseArea.containsPress) { + selectAll() + } + } + } + + validator: DoubleValidator { + bottom: modelData.validator[0] + top: modelData.validator[1] + decimals: 2 + } + + background: Rectangle { + border.color: parent.activeFocus ? "#0066cc" : "#999999" + border.width: 1 + radius: 1 + } + + onEditingFinished: { + var val = parseFloat(text) + if (!isNaN(val) && control.value) { + var newRect = Qt.rect( + modelData.prop === "x" ? val : control.value.x, + modelData.prop === "y" ? val : control.value.y, + modelData.prop === "width" ? val : control.value.width, + modelData.prop === "height" ? val : control.value.height + ) + control.setValue(newRect) + } else { + text = control.value ? control.value[modelData.prop].toFixed(control.decimals) : "0.0" + } + } + } + } + } + + Item { Layout.fillWidth: true } + } + + onValueChanged: { + if (!value) return + + // 通过 Repeater 的 itemAt 方法安全访问 + var props = ["x", "y", "width", "height"] + for (var i = 0; i < props.length; i++) { + var item = repeater.itemAt(i) + if (item && item.textField && !item.textField.activeFocus) { + item.textField.text = value[props[i]].toFixed(decimals) + } + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/SizeFBox.qml b/PropertyEditor/resources/Qml/ValueEditor/SizeFBox.qml new file mode 100644 index 0000000..ce347cc --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/SizeFBox.qml @@ -0,0 +1,372 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: control + + // 属性定义 + property var value: Qt.size(0, 0) // QSizeF + property int decimals: 1 // 默认小数位为1 + property int precision: 2 // 精度,允许的小数位数 + + // 尺寸相关属性 + property real minWidth: 0 + property real maxWidth: 999999 + property real minHeight: 0 + property real maxHeight: 999999 + + // 步进增量 + property real widthStep: 1.0 + property real heightStep: 1.0 + + // 是否显示步进按钮 + property bool showSpinButtons: true + + implicitHeight: 25 + implicitWidth: showSpinButtons ? 140 : 120 + + // 信号 - 重命名避免与内置信号冲突 + signal asValueChanged(value: var) + signal sizeWidthChanged(width: real) // 改为 sizeWidthChanged + signal sizeHeightChanged(height: real) // 改为 sizeHeightChanged + + // 获取宽高 + function getWidth() { + return value ? value.width : 0 + } + + function getHeight() { + return value ? value.height : 0 + } + + // 设置值 + function setValue(newValue: var) { + if (!control.value || + Math.abs(control.value.width - newValue.width) > 0.000001 || + Math.abs(control.value.height - newValue.height) > 0.000001) { + + // 应用边界限制 + var limitedWidth = Math.max(minWidth, Math.min(maxWidth, newValue.width)) + var limitedHeight = Math.max(minHeight, Math.min(maxHeight, newValue.height)) + + // 保留精度 + limitedWidth = parseFloat(limitedWidth.toFixed(precision)) + limitedHeight = parseFloat(limitedHeight.toFixed(precision)) + + value = Qt.size(limitedWidth, limitedHeight) + asValueChanged(value) + } + } + + // 设置宽度 + function setWidth(width: real) { + if (control.value && Math.abs(control.value.width - width) > 0.000001) { + var limitedWidth = Math.max(minWidth, Math.min(maxWidth, width)) + limitedWidth = parseFloat(limitedWidth.toFixed(precision)) + control.setValue(Qt.size(limitedWidth, control.value.height)) + } + } + + // 设置高度 + function setHeight(height: real) { + if (control.value && Math.abs(control.value.height - height) > 0.000001) { + var limitedHeight = Math.max(minHeight, Math.min(maxHeight, height)) + limitedHeight = parseFloat(limitedHeight.toFixed(precision)) + control.setValue(Qt.size(control.value.width, limitedHeight)) + } + } + + // 步进增加/减少 + function stepWidth(positive: bool) { + if (!control.value) return + var step = positive ? widthStep : -widthStep + setWidth(getWidth() + step) + } + + function stepHeight(positive: bool) { + if (!control.value) return + var step = positive ? heightStep : -heightStep + setHeight(getHeight() + step) + } + + // UI布局 + RowLayout { + anchors.fill: parent + spacing: 4 + + // 宽度部分 + Label { + text: "W:" + Layout.alignment: Qt.AlignVCenter + font.pixelSize: 10 + } + + TextField { + id: widthBox + Layout.preferredWidth: 60 + Layout.preferredHeight: 22 + Layout.alignment: Qt.AlignVCenter + selectByMouse: true // 允许鼠标选择文本 + + text: control.value ? control.value.width.toFixed(control.decimals) : "0.0" + font.pixelSize: 10 + leftPadding: 4 + rightPadding: 4 + + // 双击全选的处理 + MouseArea { + id: widthMouseArea + anchors.fill: parent + propagateComposedEvents: true + acceptedButtons: Qt.LeftButton + cursorShape: Qt.IBeamCursor + + onDoubleClicked: function(mouse) { + parent.selectAll() + parent.forceActiveFocus() + mouse.accepted = true + } + + onClicked: function(mouse) { + // 单击时设置焦点但不全选 + parent.forceActiveFocus() + mouse.accepted = false + } + } + + onActiveFocusChanged: { + if (activeFocus) { + // 如果不是双击触发的焦点变化,就全选文本 + if (!widthMouseArea.containsPress) { + selectAll() + } + } + } + + // 验证器 + validator: DoubleValidator { + bottom: control.minWidth + top: control.maxWidth + decimals: control.precision + notation: DoubleValidator.StandardNotation + } + + background: Rectangle { + color: widthBox.enabled ? "white" : "#f0f0f0" + border.color: widthBox.activeFocus ? "#2196F3" : "#cccccc" + border.width: 1 + radius: 2 + } + + onEditingFinished: { + var num = parseFloat(text) + if (!isNaN(num) && control.value) { + control.setWidth(num) + } else { + // 恢复原值 + widthBox.text = control.value ? + control.value.width.toFixed(control.decimals) : + "0.0" + } + } + + // 键盘上下键调整 + Keys.onUpPressed: function(event) { + control.stepWidth(true) + event.accepted = true + } + + Keys.onDownPressed: function(event) { + control.stepWidth(false) + event.accepted = true + } + } + + // 宽度步进按钮 + Row { + visible: control.showSpinButtons + spacing: 0 + Layout.alignment: Qt.AlignVCenter + + Button { + width: 12 + height: 11 + text: "▲" + font.pixelSize: 6 + padding: 0 + onClicked: control.stepWidth(true) + + background: Rectangle { + color: parent.hovered ? "#e0e0e0" : "transparent" + border.color: "#cccccc" + border.width: 1 + radius: 2 + } + } + + Button { + width: 12 + height: 11 + text: "▼" + font.pixelSize: 6 + padding: 0 + onClicked: control.stepWidth(false) + + background: Rectangle { + color: parent.hovered ? "#e0e0e0" : "transparent" + border.color: "#cccccc" + border.width: 1 + radius: 2 + } + } + } + + // 高度部分 + Label { + text: "H:" + Layout.alignment: Qt.AlignVCenter + font.pixelSize: 10 + } + + TextField { + id: heightBox + Layout.preferredWidth: 60 + Layout.preferredHeight: 22 + Layout.alignment: Qt.AlignVCenter + selectByMouse: true // 允许鼠标选择文本 + + text: control.value ? control.value.height.toFixed(control.decimals) : "0.0" + font.pixelSize: 10 + leftPadding: 4 + rightPadding: 4 + + // 双击全选的处理 + MouseArea { + id: heightMouseArea + anchors.fill: parent + propagateComposedEvents: true + acceptedButtons: Qt.LeftButton + cursorShape: Qt.IBeamCursor + + onDoubleClicked: function(mouse) { + parent.selectAll() + parent.forceActiveFocus() + mouse.accepted = true + } + + onClicked: function(mouse) { + // 单击时设置焦点但不全选 + parent.forceActiveFocus() + mouse.accepted = false + } + } + + onActiveFocusChanged: { + if (activeFocus) { + // 如果不是双击触发的焦点变化,就全选文本 + if (!heightMouseArea.containsPress) { + selectAll() + } + } + } + + // 验证器 + validator: DoubleValidator { + bottom: control.minHeight + top: control.maxHeight + decimals: control.precision + notation: DoubleValidator.StandardNotation + } + + background: Rectangle { + color: heightBox.enabled ? "white" : "#f0f0f0" + border.color: heightBox.activeFocus ? "#2196F3" : "#cccccc" + border.width: 1 + radius: 2 + } + + onEditingFinished: { + var num = parseFloat(text) + if (!isNaN(num) && control.value) { + control.setHeight(num) + } else { + // 恢复原值 + heightBox.text = control.value ? + control.value.height.toFixed(control.decimals) : + "0.0" + } + } + + // 键盘上下键调整 + Keys.onUpPressed: function(event) { + control.stepHeight(true) + event.accepted = true + } + + Keys.onDownPressed: function(event) { + control.stepHeight(false) + event.accepted = true + } + } + + // 高度步进按钮 + Row { + visible: control.showSpinButtons + spacing: 0 + Layout.alignment: Qt.AlignVCenter + + Button { + width: 12 + height: 11 + text: "▲" + font.pixelSize: 6 + padding: 0 + onClicked: control.stepHeight(true) + + background: Rectangle { + color: parent.hovered ? "#e0e0e0" : "transparent" + border.color: "#cccccc" + border.width: 1 + radius: 2 + } + } + + Button { + width: 12 + height: 11 + text: "▼" + font.pixelSize: 6 + padding: 0 + onClicked: control.stepHeight(false) + + background: Rectangle { + color: parent.hovered ? "#e0e0e0" : "transparent" + border.color: "#cccccc" + border.width: 1 + radius: 2 + } + } + } + + Item { + Layout.fillWidth: true + } + } + + // 当value从外部改变时,更新输入框显示 + onValueChanged: { + if (value) { + if (!widthBox.activeFocus) { + widthBox.text = value.width.toFixed(decimals) + } + if (!heightBox.activeFocus) { + heightBox.text = value.height.toFixed(decimals) + } + + // 发出单独的宽高变化信号 + sizeWidthChanged(value.width) + sizeHeightChanged(value.height) + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/TextComboBox.qml b/PropertyEditor/resources/Qml/ValueEditor/TextComboBox.qml new file mode 100644 index 0000000..34ddf79 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/TextComboBox.qml @@ -0,0 +1,103 @@ +import QtQuick; +import QtQuick.Controls; +import Qt5Compat.GraphicalEffects +import ColorPalette + +Item{ + id: editor + property var value + property var model + implicitHeight: 25 + signal asValueChanged(value:var) + function setValue(newValue:var){ + if(newValue !== value){ + value = newValue + asValueChanged(value) + } + } + ComboBox { + id: control + anchors.margins: 2 + anchors.fill: parent + model: editor.model + onCurrentTextChanged: { + setValue(currentText) + } + delegate: ItemDelegate { + id: itemDelegate + width: control.width + height: 25 + padding: 5 + background: Rectangle { + color: itemDelegate.highlighted ? ColorPalette.theme.comboBoxItemBackgroundHover + : itemDelegate.hovered ? ColorPalette.theme.comboBoxItemBackgroundHover + : ColorPalette.theme.comboBoxItemBackground + Behavior on color { + ColorAnimation { duration: 100 } + } + } + contentItem: Text { + text: modelData + color: ColorPalette.theme.textPrimary + font: control.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter // 文字垂直居中 + horizontalAlignment: Text.AlignLeft // 文字左对齐 + padding: 0 + } + highlighted: control.highlightedIndex === index + required property int index + required property var modelData + } + + indicator: Image { + id: indicator + x: control.width - width/2 - control.rightPadding + y: control.topPadding + (control.availableHeight - height) / 2 + width: 13 + height: 13 + mipmap: true + source: "qrc:/resources/Icon/expand.png" + ColorOverlay { + anchors.fill: parent + source: parent + color: ColorPalette.theme.rowIndicator + opacity: 1.0 + } + } + + contentItem: Text { + leftPadding: 3 + rightPadding: control.indicator.width + control.spacing + + text: control.displayText + font: control.font + color: ColorPalette.theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: ColorPalette.theme.comboBoxBackground + } + + popup: Popup { + y: control.height + width: control.width + implicitHeight: contentItem.implicitHeight + 5 + padding : 2 + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: control.popup.visible ? control.delegateModel : null + currentIndex: control.highlightedIndex + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: ColorPalette.theme.comboBoxBackground + } + } + } +} + diff --git a/PropertyEditor/resources/Qml/ValueEditor/Vec2Box.qml b/PropertyEditor/resources/Qml/ValueEditor/Vec2Box.qml new file mode 100644 index 0000000..8c26d38 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/Vec2Box.qml @@ -0,0 +1,44 @@ +import QtQuick; +import QtQuick.Controls; +import QtQuick.Layouts; + +Item{ + id: control + property vector2d value + implicitHeight: 25 + signal asValueChanged(value:var) + function setValue(newValue:var){ + if(value !== newValue){ + value = newValue + asValueChanged(value) + } + } + RowLayout{ + anchors.fill: parent + NumberBox{ + id: xBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.x + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector2d(number, control.value.y)) + } + } + } + NumberBox{ + id: yBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.y + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector2d(control.value.x, number)) + } + } + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/Vec3Box.qml b/PropertyEditor/resources/Qml/ValueEditor/Vec3Box.qml new file mode 100644 index 0000000..935bbf0 --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/Vec3Box.qml @@ -0,0 +1,55 @@ +import QtQuick; +import QtQuick.Controls; +import QtQuick.Layouts; + +Item{ + id: control + property vector3d value + implicitHeight: 25 + signal asValueChanged(value:var) + function setValue(newValue:var){ + if(value !== newValue){ + value = newValue + asValueChanged(value) + } + } + RowLayout{ + anchors.fill: parent + NumberBox{ + id: xBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.x + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector3d(number, control.value.y, control.value.z)) + } + } + } + NumberBox{ + id: yBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.y + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector3d(control.value.x, number, control.value.z)) + } + } + } + NumberBox{ + id: zBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.z + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector3d(control.value.x, control.value.y, number)) + } + } + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/PropertyEditor/resources/Qml/ValueEditor/Vec4Box.qml b/PropertyEditor/resources/Qml/ValueEditor/Vec4Box.qml new file mode 100644 index 0000000..b962a2a --- /dev/null +++ b/PropertyEditor/resources/Qml/ValueEditor/Vec4Box.qml @@ -0,0 +1,66 @@ +import QtQuick; +import QtQuick.Controls; +import QtQuick.Layouts; + +Item{ + id: control + property vector4d value + implicitHeight: 25 + signal asValueChanged(value:var) + function setValue(newValue:var){ + if(value !== newValue){ + value = newValue + asValueChanged(value) + } + } + RowLayout{ + anchors.fill: parent + NumberBox{ + id: xBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.x + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector4d(number, control.value.y, control.value.z, control.value.w)) + } + } + } + NumberBox{ + id: yBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.y + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector4d(control.value.x, number, control.value.z, control.value.w)) + } + } + } + NumberBox{ + id: zBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.z + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector4d(control.value.x, control.value.y, number, control.value.w)) + } + } + } + NumberBox{ + id: wBox + width: parent.width/4 + Layout.alignment: Qt.AlignLeft + number: value.w + onNumberChanged: { + if (control.value) { + control.setValue(Qt.vector4d(control.value.x, control.value.y, control.value.z, number)) + } + } + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/PropertyEditor/resources/image-20250826114654194.png b/PropertyEditor/resources/image-20250826114654194.png new file mode 100644 index 0000000000000000000000000000000000000000..8c95aa14c475ae5258d5d123438a62af5e94a7c5 GIT binary patch literal 125086 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 IQtv+hH=5DSA^-pY literal 0 HcmV?d00001 diff --git a/PropertyEditor/source/include/IPropertyTypeCustomization.h b/PropertyEditor/source/include/IPropertyTypeCustomization.h new file mode 100644 index 0000000..0b09267 --- /dev/null +++ b/PropertyEditor/source/include/IPropertyTypeCustomization.h @@ -0,0 +1,18 @@ +#ifndef IPropertyTypeCustomization_h__ +#define IPropertyTypeCustomization_h__ + +#include +#include "export.hpp" + +class QPropertyHandle; +class QQuickDetailsViewRowBuilder; +class QQuickDetailsViewLayoutBuilder; + +class DIAGRAM_DESIGNER_PUBLIC IPropertyTypeCustomization :public QEnableSharedFromThis +{ +public: + virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder); + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder); +}; + +#endif // IPropertyTypeCustomization_h__ diff --git a/PropertyEditor/source/include/PropertyHandleImpl/IPropertyHandleImpl.h b/PropertyEditor/source/include/PropertyHandleImpl/IPropertyHandleImpl.h new file mode 100644 index 0000000..d08d78d --- /dev/null +++ b/PropertyEditor/source/include/PropertyHandleImpl/IPropertyHandleImpl.h @@ -0,0 +1,33 @@ +#ifndef IPropertyHandleImpl_h__ +#define IPropertyHandleImpl_h__ + +#include +#include +#include +#include +#include "export.hpp" + +class QPropertyHandle; + +class IPropertyHandleImpl{ + friend class QPropertyHandle; +public: + enum Type { + Null, + RawType, + Associative, + Sequential, + Enum, + Object, + }; +protected: + IPropertyHandleImpl(QPropertyHandle* inHandle); + virtual QQuickItem* createNameEditor(QQuickItem* inParent); + virtual QQuickItem* createValueEditor(QQuickItem* inParent)= 0; + virtual Type type() { return Type::Null; }; + +protected: + QPropertyHandle* mHandle; +}; + +#endif // IPropertyHandleImpl_h__ diff --git a/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Associative.h b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Associative.h new file mode 100644 index 0000000..3ae61c9 --- /dev/null +++ b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Associative.h @@ -0,0 +1,27 @@ +#ifndef QAssociativePropertyHandle_h__ +#define QAssociativePropertyHandle_h__ + +#include "QMetaContainer" +#include "IPropertyHandleImpl.h" + +class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Associative: public IPropertyHandleImpl { +public: + QPropertyHandleImpl_Associative(QPropertyHandle* inHandle); + + const QMetaAssociation& metaAssociation() const; + + void appendItem(QString inKey, QVariant inValue); + bool renameItem(QString inSrc, QString inDst); + void removeItem(QString inKey); + +protected: + Type type() override { return Type::Associative; }; + QQuickItem* createValueEditor(QQuickItem* inParent)override; + +private: + QMetaAssociation mMetaAssociation; +}; + + +#endif // QAssociativePropertyHandle_h__ + diff --git a/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Enum.h b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Enum.h new file mode 100644 index 0000000..c3a50c2 --- /dev/null +++ b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Enum.h @@ -0,0 +1,20 @@ +#ifndef QPropertyHandleImpl_Enum_h__ +#define QPropertyHandleImpl_Enum_h__ + +#include "IPropertyHandleImpl.h" + +class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Enum: public IPropertyHandleImpl { +public: + QPropertyHandleImpl_Enum(QPropertyHandle* inHandle); + +protected: + QQuickItem* createValueEditor(QQuickItem* inParent) override; + Type type() override { return Type::Enum; }; + +private: + QHash mNameToValueMap; + QList mKeys; +}; + + +#endif // QPropertyHandleImpl_Enum_h__ diff --git a/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Object.h b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Object.h new file mode 100644 index 0000000..1a6cb47 --- /dev/null +++ b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Object.h @@ -0,0 +1,27 @@ +#ifndef QPropertyHandleImpl_Object_h__ +#define QPropertyHandleImpl_Object_h__ + +#include "IPropertyHandleImpl.h" + +class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Object : public IPropertyHandleImpl { +public: + QPropertyHandleImpl_Object(QPropertyHandle* inHandle); + QObject* getObject(); + void* getGadget(); + bool isGadget() const; + QObject* getOwnerObject(); + const QMetaObject* getMetaObject() const; + QQuickItem* createValueEditor(QQuickItem* inParent)override; + Type type() override { return Type::Object; }; + void refreshObjectPtr(); + QVariant& getObjectHolder(); +private: + QVariant mObjectHolder; + void* mObjectPtr = nullptr; + QObject* mOwnerObject = nullptr; + const QMetaObject* mMetaObject = nullptr; + bool bIsSharedPointer = false; + bool bIsPointer = false; +}; + +#endif // QPropertyHandleImpl_Object_h__ diff --git a/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_RawType.h b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_RawType.h new file mode 100644 index 0000000..385bb2e --- /dev/null +++ b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_RawType.h @@ -0,0 +1,14 @@ +#ifndef QPropertyHandleImpl_RawType_h__ +#define QPropertyHandleImpl_RawType_h__ + +#include "IPropertyHandleImpl.h" + +class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_RawType : public IPropertyHandleImpl { +public: + QPropertyHandleImpl_RawType(QPropertyHandle* inHandle); +protected: + QQuickItem* createValueEditor(QQuickItem* inParent) override; + Type type() override { return Type::RawType; }; +}; + +#endif // QPropertyHandleImpl_RawType_h__ diff --git a/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Sequential.h b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Sequential.h new file mode 100644 index 0000000..7f79a96 --- /dev/null +++ b/PropertyEditor/source/include/PropertyHandleImpl/QPropertyHandleImpl_Sequential.h @@ -0,0 +1,26 @@ +#ifndef QSequentialPropertyHandle_h__ +#define QSequentialPropertyHandle_h__ + +#include "QMetaContainer" +#include "IPropertyHandleImpl.h" + +class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Sequential: public IPropertyHandleImpl +{ +public: + QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle); + + const QMetaSequence& metaSequence() const; + int itemCount(); + void appendItem(QVariant InVar); + void moveItem(int InSrcIndex, int InDstIndex); + void removeItem(int InIndex); + +protected: + QQuickItem* createValueEditor(QQuickItem* inParent)override; + Type type() override { return Type::Sequential; }; + +private: + QMetaSequence mMetaSequence; +}; + +#endif // QSequentialPropertyHandle_h__ diff --git a/PropertyEditor/source/include/QDetailsView.h b/PropertyEditor/source/include/QDetailsView.h new file mode 100644 index 0000000..5118634 --- /dev/null +++ b/PropertyEditor/source/include/QDetailsView.h @@ -0,0 +1,20 @@ +#ifndef QDetailsView_h__ +#define QDetailsView_h__ + +#include +#include +#include "QQuickDetailsView.h" + +class DIAGRAM_DESIGNER_PUBLIC QDetailsView : public QWidget { + Q_OBJECT +public: + explicit QDetailsView(QWidget* parent = nullptr); + QQuickDetailsView* getQuickDetailsView() const; + void setObject(QObject* inObject); + QObject* getObject() const; +private: + QQuickWidget* mQuickWidget; + QQuickDetailsView* mQuickDetailsView; +}; + +#endif // QDetailsView_h__ diff --git a/PropertyEditor/source/include/QDetailsViewAPI.h b/PropertyEditor/source/include/QDetailsViewAPI.h new file mode 100644 index 0000000..38e8d06 --- /dev/null +++ b/PropertyEditor/source/include/QDetailsViewAPI.h @@ -0,0 +1,21 @@ +#ifndef QDETAILS_VIEW_API_H +#define QDETAILS_VIEW_API_H + +#include + +#ifdef _WIN32 +# ifdef PROPERTY_EDITOR_STATIC_LIBRARY +# define QDETAILS_VIEW_API +# else +# ifdef QDETAILS_VIEW_SHARED_LIBRARY +# define QDETAILS_VIEW_API __declspec(dllexport) +# else +# define QDETAILS_VIEW_API __declspec(dllimport) +# endif +# endif +#else +# define QDETAILS_VIEW_API __attribute__((visibility("default"))) +#endif + + +#endif // QDETAILS_VIEW_API_H diff --git a/PropertyEditor/source/include/QPropertyHandle.h b/PropertyEditor/source/include/QPropertyHandle.h new file mode 100644 index 0000000..bfb05fc --- /dev/null +++ b/PropertyEditor/source/include/QPropertyHandle.h @@ -0,0 +1,108 @@ +#ifndef QPropertyHandle_h__ +#define QPropertyHandle_h__ + +#include +#include +#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h" + +class IPropertyHandleImpl; + +class DIAGRAM_DESIGNER_PUBLIC QPropertyHandle : public QObject { + Q_OBJECT + Q_PROPERTY(QVariant Var READ getVar WRITE setVar NOTIFY asVarChanged) +public: + using Getter = std::function; + using Setter = std::function; + + enum PropertyType { + Unknown, + RawType, + Enum, + Sequential, + Associative, + Object + }; + + static QPropertyHandle* Find(const QObject* inParent, const QString& inPropertyPath); + static QPropertyHandle* FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter); + static QPropertyHandle* FindOrCreate(QObject* inObject); + static QPropertyHandle* Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter); + static void Cleanup(QObject* inParent); + + QMetaType getType(); + PropertyType getPropertyType() const; + QString getName(); + QString getPropertyPath(); + QString createSubPath(const QString& inSubName); + + void invalidateStructure(); + + Q_INVOKABLE QVariant getVar(); + Q_INVOKABLE void setVar(QVariant var); + + bool hasMetaData(const QString& inName) const; + QVariant getMetaData(const QString& inName) const; + const QVariantHash& getMetaDataMap() const; + + QPropertyHandle* findChild(QString inPropertyName); + QPropertyHandle* findOrCreateChild(QMetaType inType, QString inPropertyName, QPropertyHandle::Getter inGetter, QPropertyHandle::Setter inSetter); + + QQuickItem* setupNameEditor(QQuickItem* inParent); + QQuickItem* steupValueEditor(QQuickItem* inParent); + + IPropertyHandleImpl::Type type(); + QPropertyHandleImpl_Enum* asEnum(); + QPropertyHandleImpl_Object* asObject(); + QPropertyHandleImpl_Associative* asAssociative(); + QPropertyHandleImpl_Sequential* asSequential(); + + static PropertyType parserType(QMetaType inType); + static QVariant createNewVariant(QMetaType inOutputType); +Q_SIGNALS: + void asVarChanged(QVariant); + void asStructureChanged(); + void asRequestRollback(QVariant); +protected: + QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter); + void resloveMetaData(); +private: + QMetaType mType; + PropertyType mPropertyType; + QString mPropertyPath; + Getter mGetter; + Setter mSetter; + QVariant mInitialValue; + QVariantHash mMetaData; + QSharedPointer mImpl; +}; + +struct DIAGRAM_DESIGNER_PUBLIC ExternalRefCountWithMetaType : public QtSharedPointer::ExternalRefCountData { + typedef ExternalRefCountData Parent; + QMetaType mMetaType; + void* mData; + + static void deleter(ExternalRefCountData* self) { + ExternalRefCountWithMetaType* that = + static_cast(self); + that->mMetaType.destroy(that->mData); + Q_UNUSED(that); // MSVC warns if T has a trivial destructor + } + + static inline ExternalRefCountData* create(QMetaType inMetaType, void* inPtr) + { + ExternalRefCountWithMetaType* d = static_cast(::operator new(sizeof(ExternalRefCountWithMetaType))); + + // initialize the d-pointer sub-object + // leave d->data uninitialized + new (d) Parent(ExternalRefCountWithMetaType::deleter); // can't throw + d->mData = inPtr; + d->mMetaType = inMetaType; + return d; + } +}; + + +#endif // QPropertyHandle_h__ diff --git a/PropertyEditor/source/include/QQuickDetailsView.h b/PropertyEditor/source/include/QQuickDetailsView.h new file mode 100644 index 0000000..aeea44c --- /dev/null +++ b/PropertyEditor/source/include/QQuickDetailsView.h @@ -0,0 +1,28 @@ +#ifndef QQuickDetailsView_h__ +#define QQuickDetailsView_h__ + +#include "QQuickTreeViewEx.h" + +class QQuickDetailsViewPrivate; + +class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsView: public QQuickTreeViewEx { + Q_OBJECT + QML_NAMED_ELEMENT(DetailsView) + Q_DISABLE_COPY(QQuickDetailsView) + Q_DECLARE_PRIVATE(QQuickDetailsView) + Q_PROPERTY(qreal SpliterPencent READ getSpliterPencent WRITE setSpliterPencent NOTIFY asSpliterPencentChanged FINAL) + Q_PROPERTY(QObject* Object READ getObject WRITE setObject NOTIFY asObjectChanged FINAL) +public: + QQuickDetailsView(QQuickItem* parent = nullptr); + qreal getSpliterPencent() const; + void setSpliterPencent(qreal val); + Q_INVOKABLE void setObject(QObject* inObject); + Q_INVOKABLE QObject* getObject() const; +Q_SIGNALS: + void asSpliterPencentChanged(qreal); + void asObjectChanged(QObject*); +protected: + void componentComplete() override; +}; + +#endif // QQuickDetailsView_h__ diff --git a/PropertyEditor/source/include/QQuickDetailsViewLayoutBuilder.h b/PropertyEditor/source/include/QQuickDetailsViewLayoutBuilder.h new file mode 100644 index 0000000..81bc2b4 --- /dev/null +++ b/PropertyEditor/source/include/QQuickDetailsViewLayoutBuilder.h @@ -0,0 +1,36 @@ +#ifndef QQuickDetailsViewLayoutBuilder_h__ +#define QQuickDetailsViewLayoutBuilder_h__ + +#include "QQuickDetailsViewRow.h" + +class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewRowBuilder { +public: + QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem); + QPair makeNameValueSlot(); + + IDetailsViewRow* row() const; + QQuickItem* rootItem() const; + + void makePropertyRow(QPropertyHandle* inHandle); + QQuickItem* setupItem(QQuickItem* inParent, QString inQmlCode); + void setupLabel(QQuickItem* inParent, QString inText); + void setHeightProxy(QQuickItem* inProxyItem); +private: + IDetailsViewRow* mRow = nullptr; + QQuickItem* mRootItem = nullptr; +}; + +class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewLayoutBuilder { +public: + QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRootRow); + + IDetailsViewRow* row() const; + + void addCustomRow(std::function inCustomRowCreator, QString inOverrideName = ""); + void addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName = ""); + void addObject(QObject* inObject); +private: + IDetailsViewRow* mRootRow = nullptr; +}; + +#endif // QQuickDetailsViewLayoutBuilder_h__ diff --git a/PropertyEditor/source/include/QQuickDetailsViewMananger.h b/PropertyEditor/source/include/QQuickDetailsViewMananger.h new file mode 100644 index 0000000..de81849 --- /dev/null +++ b/PropertyEditor/source/include/QQuickDetailsViewMananger.h @@ -0,0 +1,58 @@ +#ifndef QQuickDetailsViewManager_h__ +#define QQuickDetailsViewManager_h__ + +#include +#include +#include +#include +#include +#include +#include "IPropertyTypeCustomization.h" +#include "export.hpp" + +class QPropertyHandle; + +class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewManager : public QObject{ +public: + using PropertyTypeCustomizationCreator = std::function()>; + using TypeEditorCreator = std::function; + + static QQuickDetailsViewManager* Get(); + + void initialize(); + bool isInitialized() const; + + template + void registerPropertyTypeCustomization() { + QMetaType metaType = QMetaType::fromType(); + if (metaType.metaObject()) { + mClassCustomizationMap.insert(metaType.metaObject(), []() { + return QSharedPointer::create(); + }); + } + else { + mMetaTypeCustomizationMap.insert(metaType, []() { + return QSharedPointer::create(); + }); + } + + } + void unregisterPropertyTypeCustomization(const QMetaType& inMetaType); + + void registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator); + void unregisterTypeEditor(const QMetaType& inMetaType); + + QQuickItem* createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent); + QSharedPointer getCustomPropertyType(QPropertyHandle* inHandle); +protected: + QQuickDetailsViewManager(); + void RegisterBasicTypeEditor(); +private: + bool mInitialized = false; + + QHash mClassCustomizationMap; + QHash mMetaTypeCustomizationMap; + QHash mTypeEditorCreatorMap; +}; + +#endif // QQuickDetailsViewManager_h__ diff --git a/PropertyEditor/source/include/QQuickDetailsViewModel.h b/PropertyEditor/source/include/QQuickDetailsViewModel.h new file mode 100644 index 0000000..577d249 --- /dev/null +++ b/PropertyEditor/source/include/QQuickDetailsViewModel.h @@ -0,0 +1,44 @@ +#ifndef QQuickDetailsViewModel_h__ +#define QQuickDetailsViewModel_h__ + +#include +#include +#include +#include +#include +#include "export.hpp" + +class IDetailsViewRow; +class QDetailsViewRow_Property; + +class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewModel : public QAbstractItemModel { + Q_OBJECT + enum Roles { + name = 0, + }; + friend class IDetailsViewRow; +public: + QQuickDetailsViewModel(QObject* parent = 0); + ~QQuickDetailsViewModel() {} + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& index) const override; + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QHash roleNames() const override; + + void setObject(QObject* inObject); + QObject* getObject() const; + + QModelIndex indexForRow(IDetailsViewRow* row) const; + void updateRowIndex(IDetailsViewRow* row); + +private: + QSharedPointer mRoot; + QObject* mObject; +}; + +#endif // QQuickDetailsViewModel_h__ diff --git a/PropertyEditor/source/include/QQuickDetailsViewRow.h b/PropertyEditor/source/include/QQuickDetailsViewRow.h new file mode 100644 index 0000000..b40b8f7 --- /dev/null +++ b/PropertyEditor/source/include/QQuickDetailsViewRow.h @@ -0,0 +1,66 @@ +#ifndef QQuickDetailsViewRow_h__ +#define QQuickDetailsViewRow_h__ + +#include +#include +#include +#include +#include +#include "QPropertyHandle.h" +#include "IPropertyTypeCustomization.h" +#include "QQuickDetailsViewModel.h" +#include "export.hpp" + +class DIAGRAM_DESIGNER_PUBLIC IDetailsViewRow { + friend class QQuickDetailsViewModel; +public: + ~IDetailsViewRow() {}; + + void setName(QString inName); + virtual QString name(); + + virtual void setupItem(QQuickItem* inParent){} + virtual void attachChildren() {} + virtual void addChild(QSharedPointer inChild); + + void clear(); + QQuickDetailsViewModel* model(); + QModelIndex modelIndex() const { return mModelIndex; } + void setModelIndex(const QModelIndex& index) { mModelIndex = index; } + int rowNumber() const { + if (!mParent) return -1; + return mParent->mChildren.indexOf(const_cast(this)); + } + void invalidateChildren(); +protected: + QString mName; + QQuickDetailsViewModel* mModel = nullptr; + QModelIndex mModelIndex; + IDetailsViewRow* mParent = nullptr; + QList> mChildren; +}; + +class DIAGRAM_DESIGNER_PUBLIC QDetailsViewRow_Property : public IDetailsViewRow { +public: + QDetailsViewRow_Property(QPropertyHandle* inHandle); + ~QDetailsViewRow_Property(); + void setHandle(QPropertyHandle* inHandle); + void setupItem(QQuickItem* inParent) override; + void attachChildren() override; +protected: + QPropertyHandle* mHandle = nullptr; + QMetaObject::Connection mStructureChangedConnection; + QSharedPointer mPropertyTypeCustomization; +}; + +class DIAGRAM_DESIGNER_PUBLIC QDetailsViewRow_Custom : public IDetailsViewRow { +public: + QDetailsViewRow_Custom(std::function inRowCreator); +protected: + QString name() override { return "Custom"; } + void setupItem(QQuickItem* inParent) override; +private: + std::function mRowCreator; +}; + +#endif // QQuickDetailsViewRow_h__ diff --git a/PropertyEditor/source/include/QQuickFunctionLibrary.h b/PropertyEditor/source/include/QQuickFunctionLibrary.h new file mode 100644 index 0000000..de355ed --- /dev/null +++ b/PropertyEditor/source/include/QQuickFunctionLibrary.h @@ -0,0 +1,64 @@ +#ifndef QQuickFunctionLibrary_h__ +#define QQuickFunctionLibrary_h__ + +#include +#include +#include +#include "export.hpp" + +class QQuickLambdaHolder; +class QQuickLambdaHolder_OneParam; + +class DIAGRAM_DESIGNER_PUBLIC QQuickFunctionLibrary : public QObject { + Q_OBJECT +public: + static QQuickFunctionLibrary* Get(); + + Q_INVOKABLE QString numberToString(QVariant var,int precision); + Q_INVOKABLE void setCursorPos(qreal x, qreal y); + Q_INVOKABLE void setOverrideCursorShape(Qt::CursorShape shape); + Q_INVOKABLE void restoreOverrideCursorShape(); + Q_INVOKABLE void setCursorPosTest(QQuickItem* item, qreal x, qreal y); + + + static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function callback); + static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function callback); + static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function callback); +}; + +class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder : public QObject { + Q_OBJECT + std::function mCallback; +public: + QQuickLambdaHolder(std::function callback, QObject* parent) + : QObject(parent) + , mCallback(std::move(callback)) {} + + Q_SLOT void call() { mCallback(); } +}; + +class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder_OneParam : public QObject { + Q_OBJECT + std::function mCallback; +public: + QQuickLambdaHolder_OneParam(std::function callback, QObject* parent) + : QObject(parent) + , mCallback(std::move(callback)) { + } + + Q_SLOT void call(QVariant inParam0) { mCallback(inParam0); } +}; + +class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder_TwoParams : public QObject { + Q_OBJECT + std::function mCallback; +public: + QQuickLambdaHolder_TwoParams(std::function callback, QObject* parent) + : QObject(parent) + , mCallback(std::move(callback)) { + } + + Q_SLOT void call(QVariant inParam0, QVariant inParam1) { mCallback(inParam0, inParam1); } +}; + +#endif // QQuickFunctionLibrary_h__ diff --git a/PropertyEditor/source/include/QQuickTreeViewEx.h b/PropertyEditor/source/include/QQuickTreeViewEx.h new file mode 100644 index 0000000..ef9c136 --- /dev/null +++ b/PropertyEditor/source/include/QQuickTreeViewEx.h @@ -0,0 +1,53 @@ +#ifndef QQuickTreeViewEx_h__ +#define QQuickTreeViewEx_h__ + +#include "export.hpp" +#include "private/qquicktableview_p.h" + +class QQuickTreeViewExPrivate; + +class DIAGRAM_DESIGNER_PUBLIC QQuickTreeViewEx: public QQuickTableView { + Q_OBJECT +public: + QQuickTreeViewEx(QQuickItem* parent = nullptr); + ~QQuickTreeViewEx() override; + + QModelIndex rootIndex() const; + void setRootIndex(const QModelIndex& index); + void resetRootIndex(); + + Q_INVOKABLE int depth(int row) const; + + Q_INVOKABLE bool isExpanded(int row) const; + Q_INVOKABLE void expand(int row); + Q_INVOKABLE void collapse(int row); + Q_INVOKABLE void toggleExpanded(int row); + Q_INVOKABLE void invalidateLayout(); + + Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1); + Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1); + Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex& index); + + Q_INVOKABLE QModelIndex modelIndex(const QPoint& cell) const override; + Q_INVOKABLE QPoint cellAtIndex(const QModelIndex& index) const override; + +#if QT_DEPRECATED_SINCE(6, 4) + QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead") + Q_REVISION(6, 4) Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override; +#endif + +Q_SIGNALS: + void expanded(int row, int depth); + void collapsed(int row, bool recursively); + Q_REVISION(6, 6) void rootIndexChanged(); +protected: + QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent); + void keyPressEvent(QKeyEvent* event) override; +private: + Q_DISABLE_COPY(QQuickTreeViewEx) + Q_DECLARE_PRIVATE(QQuickTreeViewEx) +}; + +QML_DECLARE_TYPE(QQuickTreeViewEx) + +#endif // QQuickTreeViewEx_h__ diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.cpp b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.cpp new file mode 100644 index 0000000..6cf62b1 --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.cpp @@ -0,0 +1,35 @@ +#include "PropertyTypeCustomization_Associative.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" +#include "qsequentialiterable.h" +#include "qassociativeiterable.h" + +void PropertyTypeCustomization_Associative::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + auto associativelHandle = inPropertyHandle->asAssociative(); + QMetaAssociation metaAssociation = associativelHandle->metaAssociation(); + QVariant varMap = inPropertyHandle->getVar(); + QAssociativeIterable iterable = varMap.value(); + for (auto iter = iterable.begin(); iter != iterable.end(); ++iter) { + QString key = iter.key().toString(); + inBuilder->addProperty(inPropertyHandle->findOrCreateChild( + metaAssociation.mappedMetaType(), + key, + [inPropertyHandle, key]() { + QVariant varMap = inPropertyHandle->getVar(); + QAssociativeIterable iterable = varMap.value(); + return iterable.value(key); + }, + [inPropertyHandle, key, metaAssociation](QVariant var) { + QVariant varMap = inPropertyHandle->getVar(); + QAssociativeIterable iterable = varMap.value(); + QtPrivate::QVariantTypeCoercer keyCoercer; + QtPrivate::QVariantTypeCoercer mappedCoercer; + void* containterPtr = const_cast(iterable.constIterable()); + const void* dataPtr = mappedCoercer.coerce(var, metaAssociation.mappedMetaType()); + metaAssociation.setMappedAtKey(containterPtr, keyCoercer.coerce(key, metaAssociation.keyMetaType()), dataPtr); + inPropertyHandle->setVar(varMap); + } + )); + } +} \ No newline at end of file diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.h b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.h new file mode 100644 index 0000000..3e2be28 --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Associative.h @@ -0,0 +1,11 @@ +#ifndef PropertyTypeCustomization_Associative_h__ +#define PropertyTypeCustomization_Associative_h__ + +#include "IPropertyTypeCustomization.h" + +class PropertyTypeCustomization_Associative : public IPropertyTypeCustomization { +protected: + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +}; + +#endif // PropertyTypeCustomization_Associative_h__ diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.cpp b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.cpp new file mode 100644 index 0000000..c2e31b1 --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.cpp @@ -0,0 +1,94 @@ +#include "PropertyTypeCustomization_Matrix4x4.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" +#include +#include +#include +#include "QQuickFunctionLibrary.h" + +void PropertyTypeCustomization_Matrix4x4::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + //inBuilder->addCustomRow([inPropertyHandle](QQuickDetailsViewRowBuilder* builder) { + // auto editorSlot = builder->makeNameValueSlot(); + // builder->setupLabel(editorSlot.first, "0"); + // auto valueItem = builder->setupItem(editorSlot.second, R"( + // import QtQuick; + // import QtQuick.Controls; + // import "qrc:/resources/Qml/ValueEditor" + // Vec4Box{ + // anchors.verticalCenter: parent.verticalCenter + // width: parent.width + // } + // )"); + // builder->setHeightProxy(valueItem); + // QMatrix4x4 mat = inPropertyHandle->getVar().value(); + // valueItem->setProperty("value", mat.row(0)); + + // QQuickFunctionLibrary::connect(valueItem, SIGNAL(asValueChanged(QVariant)), inPropertyHandle, [inPropertyHandle](QVariant var) { + // QMatrix4x4 mat = inPropertyHandle->getVar().value(); + // mat.setRow(0, var.value()); + // inPropertyHandle->setVar(mat); + // }); + // QQuickFunctionLibrary::connect(inPropertyHandle, SIGNAL(asRequestRollback(QVariant)), valueItem, [inPropertyHandle, valueItem](QVariant var) { + // QMatrix4x4 mat = var.value(); + // valueItem->setProperty("value", mat.row(0)); + // }); + //}); + + inBuilder->addProperty(QPropertyHandle::FindOrCreate( + inPropertyHandle->parent(), + QMetaType::fromType(), + inPropertyHandle->createSubPath("Row 0"), + [inPropertyHandle]() { + return inPropertyHandle->getVar().value().row(0); + }, + [inPropertyHandle](QVariant var) { + QMatrix4x4 mat = inPropertyHandle->getVar().value(); + mat.setRow(0, var.value()); + inPropertyHandle->setVar(mat); + } + )); + + inBuilder->addProperty(QPropertyHandle::FindOrCreate( + inPropertyHandle->parent(), + QMetaType::fromType(), + inPropertyHandle->createSubPath("Row 1"), + [inPropertyHandle]() { + return inPropertyHandle->getVar().value().row(1); + }, + [inPropertyHandle](QVariant var) { + QMatrix4x4 mat = inPropertyHandle->getVar().value(); + mat.setRow(1, var.value()); + inPropertyHandle->setVar(mat); + } + )); + + inBuilder->addProperty(QPropertyHandle::FindOrCreate( + inPropertyHandle->parent(), + QMetaType::fromType(), + inPropertyHandle->createSubPath("Row 2"), + [inPropertyHandle]() { + return inPropertyHandle->getVar().value().row(2); + }, + [inPropertyHandle](QVariant var) { + QMatrix4x4 mat = inPropertyHandle->getVar().value(); + mat.setRow(2, var.value()); + inPropertyHandle->setVar(mat); + } + )); + + inBuilder->addProperty(QPropertyHandle::FindOrCreate( + inPropertyHandle->parent(), + QMetaType::fromType(), + inPropertyHandle->createSubPath("Row 3"), + [inPropertyHandle]() { + return inPropertyHandle->getVar().value().row(3); + }, + [inPropertyHandle](QVariant var) { + QMatrix4x4 mat = inPropertyHandle->getVar().value(); + mat.setRow(3, var.value()); + inPropertyHandle->setVar(mat); + } + )); +} + diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.h b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.h new file mode 100644 index 0000000..5ecd67b --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Matrix4x4.h @@ -0,0 +1,11 @@ +#ifndef PropertyTypeCustomization_Matrix4x4_h__ +#define PropertyTypeCustomization_Matrix4x4_h__ + +#include "IPropertyTypeCustomization.h" + +class PropertyTypeCustomization_Matrix4x4 : public IPropertyTypeCustomization { +protected: + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +}; + +#endif // PropertyTypeCustomization_Matrix4x4_h__ diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.cpp b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.cpp new file mode 100644 index 0000000..57ad671 --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.cpp @@ -0,0 +1,52 @@ +#include "PropertyTypeCustomization_ObjectDefault.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" + +void PropertyTypeCustomization_ObjectDefault::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + auto objectHandle = inPropertyHandle->asObject(); + const QMetaObject* metaObject = objectHandle->getMetaObject(); + if (objectHandle->isGadget()) { + for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) { + QMetaProperty prop = metaObject->property(i); + QString propName = prop.name(); + inBuilder->addProperty( + inPropertyHandle->findOrCreateChild( + prop.metaType(), + propName, + [this, prop, objectHandle]() { + return prop.readOnGadget(objectHandle->getGadget()); + }, + [this, prop, objectHandle, inPropertyHandle](QVariant var) { + prop.writeOnGadget(objectHandle->getGadget(), var); + inPropertyHandle->setVar(objectHandle->getObjectHolder()); + objectHandle->refreshObjectPtr(); + } + ), + propName + ); + } + } + else { + if (objectHandle->getObject() != nullptr) { + for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) { + QMetaProperty prop = metaObject->property(i); + QString propName = prop.name(); + inBuilder->addProperty( + inPropertyHandle->findOrCreateChild( + prop.metaType(), + propName, + [this, prop, objectHandle]() { + return prop.read(objectHandle->getObject()); + }, + [this, prop, objectHandle, inPropertyHandle](QVariant var) { + prop.write(objectHandle->getObject(), var); + } + ), + propName + ); + } + } + } +} + diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.h b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.h new file mode 100644 index 0000000..11a8488 --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.h @@ -0,0 +1,11 @@ +#ifndef PropertyTypeCustomization_ObjectDefault_h__ +#define PropertyTypeCustomization_ObjectDefault_h__ + +#include "IPropertyTypeCustomization.h" + +class PropertyTypeCustomization_ObjectDefault : public IPropertyTypeCustomization { +protected: + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +}; + +#endif // PropertyTypeCustomization_ObjectDefault_h__ diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.cpp b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.cpp new file mode 100644 index 0000000..b24bc93 --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.cpp @@ -0,0 +1,40 @@ +#include "PropertyTypeCustomization_Sequential.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" +#include "qsequentialiterable.h" +#include "qassociativeiterable.h" + +void PropertyTypeCustomization_Sequential::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + auto sequentialHandle = inPropertyHandle->asSequential(); + QVariant varList = inPropertyHandle->getVar(); + QSequentialIterable iterable = varList.value(); + for (int index = 0; index < iterable.size(); index++) { + QString name = QString::number(index); + inBuilder->addProperty(inPropertyHandle->findOrCreateChild( + sequentialHandle->metaSequence().valueMetaType(), + name, + [inPropertyHandle, index]() { + QVariant varList = inPropertyHandle->getVar(); + QSequentialIterable iterable = varList.value(); + if (index < 0 || index >= iterable.size()) { + return QVariant(); + } + return iterable.at(index); + }, + [inPropertyHandle, index](QVariant var) { + QVariant varList = inPropertyHandle->getVar(); + QSequentialIterable iterable = varList.value(); + if (index >= 0 && index < iterable.size()) { + const QMetaSequence metaSequence = iterable.metaContainer(); + void* containterPtr = const_cast(iterable.constIterable()); + QtPrivate::QVariantTypeCoercer coercer; + const void* dataPtr = coercer.coerce(var, iterable.valueMetaType()); + metaSequence.setValueAtIndex(containterPtr, index, dataPtr); + inPropertyHandle->setVar(varList); + } + } + )); + } +} + diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.h b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.h new file mode 100644 index 0000000..b07e41a --- /dev/null +++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Sequential.h @@ -0,0 +1,11 @@ +#ifndef PropertyTypeCustomization_Sequential_h__ +#define PropertyTypeCustomization_Sequential_h__ + +#include "IPropertyTypeCustomization.h" + +class PropertyTypeCustomization_Sequential : public IPropertyTypeCustomization { +protected: + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +}; + +#endif // PropertyTypeCustomization_Sequential_h__ diff --git a/PropertyEditor/source/src/IPropertyTypeCustomization.cpp b/PropertyEditor/source/src/IPropertyTypeCustomization.cpp new file mode 100644 index 0000000..43669c3 --- /dev/null +++ b/PropertyEditor/source/src/IPropertyTypeCustomization.cpp @@ -0,0 +1,13 @@ +#include "IPropertyTypeCustomization.h" +#include "QQuickDetailsViewLayoutBuilder.h" + +void IPropertyTypeCustomization::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) +{ + inBuilder->makePropertyRow(inPropertyHandle); +} + +void IPropertyTypeCustomization::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + +} + diff --git a/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp b/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp new file mode 100644 index 0000000..c95c6ff --- /dev/null +++ b/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp @@ -0,0 +1,38 @@ +#include "PropertyHandleImpl/IPropertyHandleImpl.h" +#include "QPropertyHandle.h" + +IPropertyHandleImpl::IPropertyHandleImpl(QPropertyHandle* inHandle): + mHandle(inHandle) +{ +} + +QQuickItem* IPropertyHandleImpl::createNameEditor(QQuickItem* inParent) +{ + QQmlEngine* engine = qmlEngine(inParent); + QQmlContext* context = qmlContext(inParent); + QQmlComponent nameComp(engine); + nameComp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import ColorPalette; + Item{ + implicitHeight: 25 + width: parent.width + anchors.verticalCenter: parent.verticalCenter + Text { + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + clip: true + elide: Text.ElideRight + text: model.name + color: ColorPalette.theme.labelPrimary + } + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(inParent); + auto nameEditor = qobject_cast(nameComp.createWithInitialProperties(initialProperties, context)); + nameEditor->setParentItem(inParent); + return nameEditor; +} + diff --git a/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Associative.cpp b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Associative.cpp new file mode 100644 index 0000000..44e46c3 --- /dev/null +++ b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Associative.cpp @@ -0,0 +1,72 @@ +#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h" +#include +#include "QBoxLayout" +#include "QPropertyHandle.h" + +QPropertyHandleImpl_Associative::QPropertyHandleImpl_Associative(QPropertyHandle* inHandle) + :IPropertyHandleImpl(inHandle) { + QVariant varMap = mHandle->getVar(); + QAssociativeIterable iterable = varMap.value(); + mMetaAssociation = iterable.metaContainer(); +} + +const QMetaAssociation& QPropertyHandleImpl_Associative::metaAssociation() const +{ + return mMetaAssociation; +} + +QQuickItem* QPropertyHandleImpl_Associative::createValueEditor(QQuickItem* inParent) +{ + return nullptr; +} + +bool QPropertyHandleImpl_Associative::renameItem(QString inSrc, QString inDst) { + bool canRename = false; + QVariant varMap = mHandle->getVar(); + QAssociativeIterable iterable = varMap.value(); + if (iterable.containsKey(inSrc) && !iterable.containsKey(inDst)) { + canRename = true; + QVariant var = iterable.value(inSrc); + QtPrivate::QVariantTypeCoercer keyCoercer; + QtPrivate::QVariantTypeCoercer mappedCoercer; + void* containterPtr = const_cast(iterable.constIterable()); + QMetaAssociation metaAssociation = iterable.metaContainer(); + metaAssociation.removeKey(containterPtr, keyCoercer.coerce(inSrc, QMetaType::fromType())); + metaAssociation.setMappedAtKey( + containterPtr, + keyCoercer.coerce(inDst, QMetaType::fromType()), + mappedCoercer.coerce(var, var.metaType()) + ); + //mHandle->setVar(varMap, QString("Rename: %1 -> %2").arg(inSrc).arg(inDst)); + //Q_EMIT mHandle->asRequestRebuildRow(); + } + return canRename; +} + +void QPropertyHandleImpl_Associative::appendItem(QString inKey, QVariant inValue) { + QVariant varList = mHandle->getVar(); + QAssociativeIterable iterable = varList.value(); + void* containterPtr = const_cast(iterable.constIterable()); + QtPrivate::QVariantTypeCoercer coercer; + QVariant key(inKey); + const void* keyDataPtr = coercer.coerce(key, key.metaType()); + const void* valueDataPtr = coercer.coerce(inValue, inValue.metaType()); + //metaAssociation.insertKey(containterPtr, keyDataPtr); + mMetaAssociation.setMappedAtKey(containterPtr, keyDataPtr, valueDataPtr); + //mHandle->setVar(varList, QString("%1 Insert: %2").arg(mHandle->getPath()).arg(inKey)); + //Q_EMIT mHandle->asRequestRebuildRow(); +} + +void QPropertyHandleImpl_Associative::removeItem(QString inKey) { + QVariant varList = mHandle->getVar(); + QAssociativeIterable iterable = varList.value(); + const QMetaAssociation metaAssociation = iterable.metaContainer(); + void* containterPtr = const_cast(iterable.constIterable()); + QtPrivate::QVariantTypeCoercer coercer; + QVariant key(inKey); + const void* keyDataPtr = coercer.coerce(key, key.metaType()); + metaAssociation.removeKey(containterPtr, keyDataPtr); + //mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(inKey)); + //Q_EMIT mHandle->asRequestRebuildRow(); +} + diff --git a/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Enum.cpp b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Enum.cpp new file mode 100644 index 0000000..a927f96 --- /dev/null +++ b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Enum.cpp @@ -0,0 +1,46 @@ +#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h" +#include +#include +#include +#include +#include "QPropertyHandle.h" + + +QPropertyHandleImpl_Enum::QPropertyHandleImpl_Enum(QPropertyHandle* inHandle) + :IPropertyHandleImpl(inHandle) { + const QMetaObject* metaObj = mHandle->getType().metaObject(); + if (metaObj){ + const QMetaEnum& metaEnum = metaObj->enumerator(metaObj->indexOfEnumerator(QString(mHandle->getType().name()).split("::").last().toLocal8Bit())); + for (int i = 0; i < metaEnum.keyCount(); i++) { + mNameToValueMap[metaEnum.key(i)] = metaEnum.value(i); + mKeys << metaEnum.key(i); + } + } +} + +QQuickItem* QPropertyHandleImpl_Enum::createValueEditor(QQuickItem* inParent) +{ + QQmlEngine* engine = qmlEngine(inParent); + QQmlContext* context = qmlContext(inParent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + TextComboBox{ + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(inParent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(inParent); + valueEditor->setProperty("value", mHandle->getVar()); + valueEditor->setProperty("model", mKeys); + QObject::connect(valueEditor, SIGNAL(asValueChanged(QVariant)), mHandle, SLOT(setVar(QVariant))); + QObject::connect(mHandle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; +} \ No newline at end of file diff --git a/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Object.cpp b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Object.cpp new file mode 100644 index 0000000..34b8454 --- /dev/null +++ b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Object.cpp @@ -0,0 +1,98 @@ +#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h" +#include +#include +#include "QPropertyHandle.h" +#include + +QPropertyHandleImpl_Object::QPropertyHandleImpl_Object(QPropertyHandle* inHandle) + :IPropertyHandleImpl(inHandle) { + mObjectHolder = mHandle->getVar(); + QMetaType metaType = mHandle->getType(); + QRegularExpression reg("QSharedPointer\\<(.+)\\>"); + QRegularExpressionMatch match = reg.match(metaType.name()); + QStringList matchTexts = match.capturedTexts(); + if (!matchTexts.isEmpty()) { + QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit()); + mMetaObject = innerMetaType.metaObject(); + const void* ptr = *(const void**)mObjectHolder.data(); + bIsSharedPointer = true; + bIsPointer = true; + if (ptr) { + mObjectHolder = QVariant(innerMetaType, mObjectHolder.data()); + } + else { + mObjectHolder = QVariant(); + } + } + else{ + bIsPointer = metaType.flags().testFlag(QMetaType::IsPointer); + mMetaObject = metaType.metaObject(); + } + mOwnerObject = mHandle->parent(); + refreshObjectPtr(); +} + +QObject* QPropertyHandleImpl_Object::getObject() +{ + if(mMetaObject->inherits(&QObject::staticMetaObject)) + return (QObject*)mObjectPtr; + return nullptr; +} + +void* QPropertyHandleImpl_Object::getGadget() +{ + return mObjectPtr; +} + +bool QPropertyHandleImpl_Object::isGadget() const +{ + if (mMetaObject->inherits(&QObject::staticMetaObject)) + return false; + return mObjectPtr != nullptr; +} + +QObject* QPropertyHandleImpl_Object::getOwnerObject() +{ + return mOwnerObject; +} + +const QMetaObject* QPropertyHandleImpl_Object::getMetaObject() const +{ + return mMetaObject; +} + +void QPropertyHandleImpl_Object::refreshObjectPtr() { + mObjectHolder = mHandle->getVar(); + if (mObjectHolder.isValid()) { + if (mMetaObject->inherits(&QObject::staticMetaObject)) { + QObject* objectPtr = mObjectHolder.value(); + if (objectPtr) { + mMetaObject = objectPtr->metaObject(); + } + mObjectPtr = objectPtr; + mOwnerObject = objectPtr; + if (mOwnerObject) { + //QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::moveToThread, mOwnerObject, mHandle->thread())); + //QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::installEventFilter, mOwnerObject, mHandle)); + //mOwnerObject->installEventFilter(mHandle); + } + } + else { + void* ptr = mObjectHolder.data(); + if (bIsPointer) + ptr = *(void**)mObjectHolder.data(); + mObjectPtr = ptr; + } + } +} + +QVariant& QPropertyHandleImpl_Object::getObjectHolder() +{ + return mObjectHolder; +} + +QQuickItem* QPropertyHandleImpl_Object::createValueEditor(QQuickItem* inParent) +{ + return nullptr; +} + diff --git a/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_RawType.cpp b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_RawType.cpp new file mode 100644 index 0000000..4182b5b --- /dev/null +++ b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_RawType.cpp @@ -0,0 +1,14 @@ +#include "PropertyHandleImpl/IPropertyHandleImpl.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h" +#include "QQuickDetailsViewMananger.h" + +QPropertyHandleImpl_RawType::QPropertyHandleImpl_RawType(QPropertyHandle* inHandle) + : IPropertyHandleImpl(inHandle) +{ + +} + +QQuickItem* QPropertyHandleImpl_RawType::createValueEditor(QQuickItem* inParent) +{ + return QQuickDetailsViewManager::Get()->createValueEditor(mHandle, inParent); +} diff --git a/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Sequential.cpp b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Sequential.cpp new file mode 100644 index 0000000..4195d8e --- /dev/null +++ b/PropertyEditor/source/src/PropertyHandleImpl/QPropertyHandleImpl_Sequential.cpp @@ -0,0 +1,69 @@ +#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h" +#include +#include "QBoxLayout" +#include "QPropertyHandle.h" + + +QPropertyHandleImpl_Sequential::QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle) + :IPropertyHandleImpl(inHandle) { + QVariant varList = mHandle->getVar(); + QSequentialIterable iterable = varList.value(); + mMetaSequence = iterable.metaContainer(); +} + +const QMetaSequence& QPropertyHandleImpl_Sequential::metaSequence() const +{ + return mMetaSequence; +} + +QQuickItem* QPropertyHandleImpl_Sequential::createValueEditor(QQuickItem* inParent) +{ + return nullptr; +} + +int QPropertyHandleImpl_Sequential::itemCount() { + QVariant varList = mHandle->getVar(); + QSequentialIterable iterable = varList.value(); + return iterable.size(); +} + +void QPropertyHandleImpl_Sequential::appendItem( QVariant InVar) { + QVariant varList = mHandle->getVar(); + QSequentialIterable iterable = varList.value(); + const QMetaSequence metaSequence = iterable.metaContainer(); + void* containterPtr = const_cast(iterable.constIterable()); + QtPrivate::QVariantTypeCoercer coercer; + const void* dataPtr = coercer.coerce(InVar, InVar.metaType()); + metaSequence.addValue(containterPtr, dataPtr); + //mHandle->setVar(varList, QString("%1 Append: %2").arg(mHandle->getPath()).arg(metaSequence.size(containterPtr) - 1)); + //Q_EMIT mHandle->asRequestRebuildRow(); +} + +void QPropertyHandleImpl_Sequential::moveItem(int InSrcIndex, int InDstIndex) { + QVariant varList = mHandle->getVar(); + QSequentialIterable iterable = varList.value(); + const QMetaSequence metaSequence = iterable.metaContainer(); + void* containterPtr = const_cast(iterable.constIterable()); + QtPrivate::QVariantTypeCoercer coercer; + QVariant srcVar = iterable.at(InSrcIndex); + QVariant dstVar = iterable.at(InDstIndex); + metaSequence.setValueAtIndex(containterPtr, InDstIndex, coercer.coerce(srcVar, srcVar.metaType())); + metaSequence.setValueAtIndex(containterPtr, InSrcIndex, coercer.coerce(dstVar, dstVar.metaType())); + //mHandle->setVar(varList, QString("%1 Move: %2->%3").arg(mHandle->getPath()).arg(InSrcIndex).arg(InDstIndex)); + //Q_EMIT mHandle->asRequestRebuildRow(); +} + +void QPropertyHandleImpl_Sequential::removeItem(int InIndex) { + QVariant varList = mHandle->getVar(); + QSequentialIterable iterable = varList.value(); + const QMetaSequence metaSequence = iterable.metaContainer(); + void* containterPtr = const_cast(iterable.constIterable()); + QtPrivate::QVariantTypeCoercer coercer; + for (int i = InIndex; i < iterable.size() - 1; i++) { + QVariant nextVar = iterable.at(i + 1); + metaSequence.setValueAtIndex(containterPtr, InIndex, coercer.coerce(nextVar, nextVar.metaType())); + } + metaSequence.removeValueAtEnd(containterPtr); + //mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(InIndex)); + //Q_EMIT mHandle->asRequestRebuildRow(); +} diff --git a/PropertyEditor/source/src/QDetailsView.cpp b/PropertyEditor/source/src/QDetailsView.cpp new file mode 100644 index 0000000..ea68b04 --- /dev/null +++ b/PropertyEditor/source/src/QDetailsView.cpp @@ -0,0 +1,86 @@ +#include "QDetailsView.h" +#include +#include +#include +#include +#include +#include "QQuickDetailsViewMananger.h" + +QDetailsView::QDetailsView(QWidget* parent) + : QWidget(parent) + , mQuickWidget(nullptr) + , mQuickDetailsView(nullptr) +{ + setMinimumSize(200, 200); + + if (!QQuickDetailsViewManager::Get()->isInitialized()) { + QQuickDetailsViewManager::Get()->initialize(); + } + + mQuickWidget = new QQuickWidget(this); + mQuickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + + const QString qmlContent = R"( + import QtQuick + import QtQuick.Controls + import QtQuick.DetailsView + + + DetailsView { + id: detailsView + anchors.fill: parent + boundsBehavior: Flickable.OvershootBounds + ScrollBar.vertical: ScrollBar { + parent: detailsView.parent + width : 10 + anchors.top: detailsView.top + anchors.right: detailsView.right + anchors.bottom: detailsView.bottom + } + } + )"; + + QQmlComponent component(mQuickWidget->engine()); + component.setData(qmlContent.toUtf8(), QUrl("")); + QObject* rootObject = component.create(); + if (!component.errors().isEmpty()) { + qDebug() << component.errorString(); + } + if (rootObject) { + mQuickWidget->setSource(QUrl()); + mQuickWidget->engine()->setObjectOwnership(rootObject, QQmlEngine::CppOwnership); + + QQuickItem* rootItem = qobject_cast(rootObject); + if (rootItem) { + mQuickWidget->setContent(QUrl(), &component, rootItem); + mQuickDetailsView = qobject_cast(rootItem); + } + } + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(mQuickWidget); + setLayout(layout); +} + +QQuickDetailsView* QDetailsView::getQuickDetailsView() const +{ + return mQuickDetailsView; +} + +void QDetailsView::setObject(QObject* inObject) +{ + QQuickItem* rootObject = mQuickWidget->rootObject(); + if (rootObject) { + rootObject->setProperty("Object", QVariant::fromValue(inObject)); + } +} + +QObject* QDetailsView::getObject() const +{ + QQuickItem* rootObject = mQuickWidget->rootObject(); + if (rootObject) { + return rootObject->property("Object").value(); + } + return nullptr; +} diff --git a/PropertyEditor/source/src/QPropertyHandle.cpp b/PropertyEditor/source/src/QPropertyHandle.cpp new file mode 100644 index 0000000..ac8e165 --- /dev/null +++ b/PropertyEditor/source/src/QPropertyHandle.cpp @@ -0,0 +1,322 @@ +#include "QPropertyHandle.h" +#include +#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h" +#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h" + +QPropertyHandle::QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter) + : mType(inType) + , mPropertyPath(inPropertyPath) + , mGetter(inGetter) + , mSetter(inSetter) +{ + setParent(inParent); + setObjectName(inPropertyPath.split(".").back()); + resloveMetaData(); + mInitialValue = inGetter(); + mPropertyType = parserType(inType); + switch (mPropertyType) + { + default: + break; + case PropertyType::RawType: + mImpl.reset(new QPropertyHandleImpl_RawType(this)); + break; + case PropertyType::Enum: + mImpl.reset(new QPropertyHandleImpl_Enum(this)); + break; + case PropertyType::Sequential: + mImpl.reset(new QPropertyHandleImpl_Sequential(this)); + break; + case PropertyType::Associative: + mImpl.reset(new QPropertyHandleImpl_Associative(this)); + break; + case PropertyType::Object: + mImpl.reset(new QPropertyHandleImpl_Object(this)); + break; + } +} + +void QPropertyHandle::resloveMetaData() +{ + auto metaObj = parent()->metaObject(); + auto firstField = getPropertyPath().split(".").first(); + for (int i = 0; i < metaObj->classInfoCount(); i++) { + auto metaClassInfo = metaObj->classInfo(i); + if (metaClassInfo.name() == firstField) { + QStringList fields = QString(metaClassInfo.value()).split(",", Qt::SplitBehaviorFlags::SkipEmptyParts); + for (auto field : fields) { + QStringList pair = field.split("="); + QString key, value; + if (pair.size() > 0) { + key = pair.first().trimmed(); + } + if (pair.size() > 1) { + value = pair[1].trimmed(); + } + mMetaData[key] = value; + } + return; + } + } +} + +QPropertyHandle* QPropertyHandle::Find(const QObject* inParent, const QString& inPropertyPath) +{ + for (QObject* child : inParent->children()) { + QPropertyHandle* handler = qobject_cast(child); + if (handler && handler->getPropertyPath() == inPropertyPath) { + return handler; + } + } + return nullptr; +} + +QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter) +{ + QPropertyHandle* handle = Find(inParent, inPropertyPath); + if (handle) + return handle; + return new QPropertyHandle( + inParent, + inType, + inPropertyPath, + inGetter, + inSetter + ); +} + +QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inObject) +{ + QPropertyHandle* handle = Find(inObject, ""); + if (handle) + return handle; + return new QPropertyHandle( + inObject, + inObject->metaObject()->metaType(), + "", + [inObject]() {return QVariant::fromValue(inObject); }, + [inObject](QVariant var) {} + ); +} + +QPropertyHandle* QPropertyHandle::Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter) +{ + return new QPropertyHandle(inParent, inType, inPropertyPath, inGetter, inSetter); +} + +void QPropertyHandle::Cleanup(QObject* inParent) +{ + for (QObject* child : inParent->children()) { + QPropertyHandle* handler = qobject_cast(child); + if (handler) { + handler->deleteLater(); + } + } +} + +QMetaType QPropertyHandle::getType() +{ + return mType; +} + +QPropertyHandle::PropertyType QPropertyHandle::getPropertyType() const +{ + return mPropertyType; +} + +QString QPropertyHandle::getName() +{ + return objectName(); +} + +QString QPropertyHandle::getPropertyPath() +{ + return mPropertyPath; +} + +QString QPropertyHandle::createSubPath(const QString& inSubName) +{ + return getPropertyPath() + "." + inSubName; +} + +void QPropertyHandle::invalidateStructure() +{ + Q_EMIT asStructureChanged(); +} + +QVariant QPropertyHandle::getVar() +{ + return mGetter(); +} + +void QPropertyHandle::setVar(QVariant var) +{ + QVariant lastVar = mGetter(); + if (lastVar != var) { + mSetter(var); + Q_EMIT asVarChanged(var); + QVariant currVar = mGetter(); + if (currVar != var) { + Q_EMIT asRequestRollback(currVar); + } + } +} + +bool QPropertyHandle::hasMetaData(const QString& inName) const +{ + return mMetaData.contains(inName); +} + +QVariant QPropertyHandle::getMetaData(const QString& inName) const +{ + return mMetaData.value(inName); +} + +const QVariantHash& QPropertyHandle::getMetaDataMap() const +{ + return mMetaData; +} + +QPropertyHandle* QPropertyHandle::findChild(QString inPropertyName) +{ + return Find(this->parent(), this->createSubPath(inPropertyName)); +} + +QPropertyHandle* QPropertyHandle::findOrCreateChild(QMetaType inType, QString inPropertyName, Getter inGetter, Setter inSetter) +{ + QVariant var = inGetter(); + QObject* object = var.value(); + QPropertyHandle* childHandle = nullptr; + if (object) { + childHandle = QPropertyHandle::FindOrCreate( + object + ); + childHandle->setObjectName(inPropertyName); + } + else { + childHandle = QPropertyHandle::FindOrCreate( + this->parent(), + inType, + this->createSubPath(inPropertyName), + inGetter, + inSetter + ); + } + + childHandle->disconnect(this); + connect(this, &QPropertyHandle::asVarChanged, childHandle, [childHandle](QVariant) { + QVariant var = childHandle->getVar(); + Q_EMIT childHandle->asRequestRollback(var); + }); + connect(this, &QPropertyHandle::asRequestRollback, childHandle, [childHandle](QVariant) { + QVariant var = childHandle->getVar(); + Q_EMIT childHandle->asRequestRollback(var); + }); + return childHandle; +} + +QQuickItem* QPropertyHandle::setupNameEditor(QQuickItem* inParent) +{ + return mImpl->createNameEditor(inParent); +} + +QQuickItem* QPropertyHandle::steupValueEditor(QQuickItem* inParent) +{ + return mImpl->createValueEditor(inParent); +} + +IPropertyHandleImpl::Type QPropertyHandle::type() +{ + return mImpl->type(); +} + +QPropertyHandleImpl_Enum* QPropertyHandle::asEnum() +{ + return static_cast(mImpl.get()); +} + +QPropertyHandleImpl_Object* QPropertyHandle::asObject() +{ + return static_cast(mImpl.get()); +} + +QPropertyHandleImpl_Associative* QPropertyHandle::asAssociative() +{ + return static_cast(mImpl.get()); +} + +QPropertyHandleImpl_Sequential* QPropertyHandle::asSequential() +{ + return static_cast(mImpl.get()); +} + +QPropertyHandle::PropertyType QPropertyHandle::parserType(QMetaType inType) +{ + if (QMetaType::canConvert(inType, QMetaType::fromType()) + && !QMetaType::canConvert(inType, QMetaType::fromType()) + ) { + return Sequential; + } + else if (QMetaType::canConvert(inType, QMetaType::fromType())) { + return Associative; + } + else if (inType.flags() & QMetaType::IsEnumeration) { + return Enum; + } + else { + QRegularExpression reg("QSharedPointer\\<(.+)\\>"); + QRegularExpressionMatch match = reg.match(inType.name(), 0, QRegularExpression::MatchType::PartialPreferCompleteMatch, QRegularExpression::AnchorAtOffsetMatchOption); + QStringList matchTexts = match.capturedTexts(); + QMetaType innerMetaType; + if (!matchTexts.isEmpty()) { + QString metaTypeName = matchTexts.back(); + innerMetaType = QMetaType::fromName(metaTypeName.toLocal8Bit()); + } + if (innerMetaType.metaObject() || inType.metaObject()) { + return Object; + } + else { + return RawType; + } + } +} + +QVariant QPropertyHandle::createNewVariant(QMetaType inOutputType) +{ + QRegularExpression reg("QSharedPointer\\<(.+)\\>"); + QRegularExpressionMatch match = reg.match(inOutputType.name()); + QStringList matchTexts = match.capturedTexts(); + if (!matchTexts.isEmpty()) { + QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit()); + if (innerMetaType.isValid()) { + void* ptr = innerMetaType.create(); + QVariant sharedPtr(inOutputType); + memcpy(sharedPtr.data(), &ptr, sizeof(ptr)); + QtSharedPointer::ExternalRefCountData* data = ExternalRefCountWithMetaType::create(innerMetaType, ptr); + memcpy((char*)sharedPtr.data() + sizeof(ptr), &data, sizeof(data)); + return sharedPtr; + } + } + else if (inOutputType.flags().testFlag(QMetaType::IsPointer)) { + const QMetaObject* metaObject = inOutputType.metaObject(); + if (metaObject && metaObject->inherits(&QObject::staticMetaObject)) { + QObject* obj = metaObject->newInstance(); + if (obj) + return QVariant::fromValue(obj); + } + QMetaType innerMetaType = QMetaType::fromName(QString(inOutputType.name()).remove("*").toLocal8Bit()); + if (innerMetaType.isValid()) { + void* ptr = innerMetaType.create(); + QVariant var(inOutputType, ptr); + memcpy(var.data(), &ptr, sizeof(ptr)); + return var; + } + else { + return QVariant(); + } + } + return QVariant(inOutputType); +} diff --git a/PropertyEditor/source/src/QQuickDetailsView.cpp b/PropertyEditor/source/src/QQuickDetailsView.cpp new file mode 100644 index 0000000..2e3e517 --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsView.cpp @@ -0,0 +1,86 @@ +#include "QQuickDetailsView.h" +#include "private/qqmldata_p.h" +#include + +#include "qqmlcontext.h" +#include "QQuickDetailsViewPrivate.h" +#include "QQuickDetailsViewRow.h" +#include "QQuickFunctionLibrary.h" + +void QQuickDetailsViewPrivate::initItemCallback(int serializedModelIndex, QObject* object) +{ + QQuickTreeViewExPrivate::initItemCallback(serializedModelIndex, object); + auto item = qobject_cast(object); + if (!item) + return; + const QModelIndex& index = m_treeModelToTableModel.mapToModel(serializedModelIndex);; + IDetailsViewRow* node = static_cast(index.internalPointer()); + node->setupItem(item); +} + +QQuickDetailsView::QQuickDetailsView(QQuickItem* parent /*= nullptr*/) + : QQuickTreeViewEx(*(new QQuickDetailsViewPrivate()),parent) +{ + setModel(QVariant::fromValue(d_func()->mModel)); + setReuseItems(false); + setEditTriggers(QQuickTableView::EditTrigger::DoubleTapped); +} + +qreal QQuickDetailsView::getSpliterPencent() const +{ + return d_func()->mSpliterPencent; +} + +void QQuickDetailsView::setSpliterPencent(qreal val) +{ + if(val != d_func()->mSpliterPencent){ + d_func()->mSpliterPencent = val; + Q_EMIT asSpliterPencentChanged(val); + } +} + +void QQuickDetailsView::setObject(QObject* inObject) +{ + if (inObject != d_func()->mModel->getObject()) { + d_func()->mModel->setObject(inObject); + Q_EMIT asObjectChanged(inObject); + } +} + +QObject* QQuickDetailsView::getObject() const +{ + return d_func()->mModel->getObject(); +} + +void QQuickDetailsView::componentComplete() +{ + QQmlEngine* engine = qmlEngine(this); + QQmlComponent* delegate = new QQmlComponent(engine, this); + engine->rootContext()->setContextProperty("helper", QQuickFunctionLibrary::Get()); + delegate->setData(R"( + import QtQuick; + import QtQuick.Controls; + import QtQuick.Layouts; + import QtQuick.DetailsView; + Item { + id: detailsDelegate + readonly property real indent: 20 + readonly property real padding: 5 + required property DetailsView detailsView + required property int row + required property bool isTreeNode + required property bool expanded + required property int hasChildren + required property int depth + implicitWidth: detailsView.width + implicitHeight: heightProxy ? heightProxy.height + 5 : 30 + TapHandler { + onTapped: detailsView.toggleExpanded(row) + } + onImplicitHeightChanged: { + detailsView.invalidateLayout(); + } + })", QUrl("QQuickDetailsView.componentComplete"));; + setDelegate(delegate); + QQuickTreeViewEx::componentComplete(); +} diff --git a/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp b/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp new file mode 100644 index 0000000..d27f91a --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp @@ -0,0 +1,468 @@ +#include "QQuickDetailsViewMananger.h" +#include "QPropertyHandle.h" +#include "QQuickFunctionLibrary.h" +#include +#include +#include + +#define REGINTER_NUMBER_EDITOR_CREATOR(TypeName, DefaultPrecision) \ + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { \ + QQmlEngine* engine = qmlEngine(parent); \ + QQmlContext* context = qmlContext(parent); \ + QQmlComponent nameComp(engine); \ + const char* qmlData = \ + "import QtQuick;\n" \ + "import QtQuick.Controls;\n" \ + "import \"qrc:/resources/Qml/ValueEditor\"\n" \ + "NumberBox{\n" \ + " anchors.verticalCenter: parent.verticalCenter\n" \ + " width: parent.width\n" \ + "}"; \ + nameComp.setData(qmlData, QUrl()); \ + QVariantMap initialProperties; \ + initialProperties["parent"] = QVariant::fromValue(parent); \ + auto valueEditor = qobject_cast(nameComp.createWithInitialProperties(initialProperties, context)); \ + if (!nameComp.errors().isEmpty()) { \ + qDebug() << nameComp.errorString(); \ + } \ + if (valueEditor) { \ + valueEditor->setParentItem(parent); \ + TypeName min = handle->getMetaData("Min").toDouble(); \ + TypeName max = handle->getMetaData("Max").toDouble(); \ + double step = handle->getMetaData("Step").toDouble(); \ + int precision = handle->getMetaData("Precision").toInt(); \ + valueEditor->setProperty("step", step <= 0.0001 ? 1.0 / pow(10.0, DefaultPrecision) : step); \ + valueEditor->setProperty("number", handle->getVar()); \ + valueEditor->setProperty("precision", precision == 0 ? DefaultPrecision : precision); \ + if (min >= max) { \ + min = std::numeric_limits::min(); \ + max = std::numeric_limits::max(); \ + } else { \ + valueEditor->setProperty("isLimited", true); \ + } \ + valueEditor->setProperty("min", QVariant::fromValue(min)); \ + valueEditor->setProperty("max", QVariant::fromValue(max)); \ + qDebug() << min << max; \ + QObject::connect(valueEditor, SIGNAL(valueChanged(QVariant)), handle, SLOT(setVar(QVariant))); \ + QObject::connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setNumber(QVariant))); \ + } \ + return valueEditor; \ + }); + + +void QQuickDetailsViewManager::RegisterBasicTypeEditor() { + REGINTER_NUMBER_EDITOR_CREATOR(int, 0); + REGINTER_NUMBER_EDITOR_CREATOR(unsigned int, 0); + REGINTER_NUMBER_EDITOR_CREATOR(size_t, 0); + REGINTER_NUMBER_EDITOR_CREATOR(float, 2); + REGINTER_NUMBER_EDITOR_CREATOR(double, 3); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + MultiLineTextBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + Vec4Box{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + Vec3Box{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + Vec2Box{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + BoolBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + PointFBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + PointFBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + RectFBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + RectFBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + SizeFBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + SizeFBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + ColorBox{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + QMetaType::registerConverterFunction( + [](const void* src, void* target) -> bool { + const QDir& dir = *static_cast(src); + QString& str = *static_cast(target); + str = dir.absolutePath(); + return true; + }, + QMetaType::fromType(), + QMetaType::fromType() + ); + + QMetaType::registerConverterFunction( + [](const void* src, void* target) -> bool { + const QString& str = *static_cast(src); + QDir& dir = *static_cast(target); + dir = QDir(str); + return true; + }, + QMetaType::fromType(), + QMetaType::fromType() + ); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + DirectorySelector{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); + + QMetaType::registerConverterFunction( + [](const void* src, void* target) -> bool { + const QFileInfo& file = *static_cast(src); + QString& str = *static_cast(target); + str = file.fileName(); + return true; + }, + QMetaType::fromType(), + QMetaType::fromType() + ); + + QMetaType::registerConverterFunction( + [](const void* src, void* target) -> bool { + const QString& str = *static_cast(src); + QFileInfo& file = *static_cast(target); + file = QFileInfo(str); + return true; + }, + QMetaType::fromType(), + QMetaType::fromType() + ); + + registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { + QQmlEngine* engine = qmlEngine(parent); + QQmlContext* context = qmlContext(parent); + QQmlComponent comp(engine); + comp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import "qrc:/resources/Qml/ValueEditor" + FileSelector{ + anchors.verticalCenter: parent.verticalCenter + width: parent.width + } + )", QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(parent); + auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + valueEditor->setParentItem(parent); + valueEditor->setProperty("value", handle->getVar()); + connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); + connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); + return valueEditor; + }); +} diff --git a/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp b/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp new file mode 100644 index 0000000..56a4169 --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp @@ -0,0 +1,216 @@ +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QQuickDetailsViewMananger.h" + +QQuickDetailsViewRowBuilder::QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem) + : mRow(inRow) + , mRootItem(inRootItem) +{ + setHeightProxy(nullptr); +} + +QPair QQuickDetailsViewRowBuilder::makeNameValueSlot() +{ + QQmlEngine* engine = qmlEngine(mRootItem); + QQmlContext* context = qmlContext(mRootItem); + QQmlContext* newContext = new QQmlContext(context, mRootItem); + QQmlComponent rootComp(newContext->engine()); + rootComp.setData(R"( + import QtQuick; + import QtQuick.Controls; + import Qt5Compat.GraphicalEffects + import ColorPalette + Rectangle{ + id: topLevelRect + anchors.fill: parent + color: hoverHandler.hovered ? ColorPalette.theme.rowBackgroundHover : ColorPalette.theme.rowBackground + border.color: ColorPalette.theme.rowBorder + border.width: 0.5 + Behavior on color { + ColorAnimation { duration: 100 } + } + HoverHandler { id: hoverHandler } + Image { + id: indicator + visible: detailsDelegate.isTreeNode && detailsDelegate.hasChildren + x: padding + (detailsDelegate.depth * detailsDelegate.indent) + anchors.verticalCenter: parent.verticalCenter + mipmap: true + source: detailsDelegate.expanded + ? "qrc:/resources/Icon/expand.png" + : "qrc:/resources/Icon/unexpand.png" + + width: 12 + height: 12 + ColorOverlay { + anchors.fill: parent + source: parent + color: ColorPalette.theme.rowIndicator + opacity: 1.0 + } + } + Item{ + id: nameEditorContent + height: parent.height + anchors.left: parent.left + anchors.leftMargin: padding + (detailsDelegate.isTreeNode ? (detailsDelegate.depth + 1) * detailsDelegate.indent : 0) + anchors.right: splitter.left + anchors.verticalCenter: parent.verticalCenter + } + Item{ + id: valueEditorContent + implicitHeight: 25 + anchors.left: splitter.left + anchors.leftMargin: 10 + anchors.rightMargin: 10 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + Rectangle{ + id: splitter + width: 3 + height: parent.height + color : ColorPalette.theme.rowBackground + x: detailsView.SpliterPencent * detailsView.width - 1 + + Rectangle{ + id: visibleSplitter + width: 1 + height: parent.height + color: ColorPalette.theme.rowSplitter + anchors.horizontalCenter: parent.horizontalCenter + } + + MouseArea { + id: dragArea + hoverEnabled: true + cursorShape: containsMouse ? Qt.SplitHCursor : Qt.ArrowCursor + anchors.fill: parent + drag.target: splitter + drag.axis: Drag.XAxis + drag.minimumX: 10 + 1 + drag.maximumX: detailsView.width - 10 - 1 + onPositionChanged: { + detailsView.SpliterPencent = (splitter.x + 1) / detailsView.width + } + } + } + Rectangle { + id: gradientBox + visible: detailsDelegate.depth > 0 + width: 5 + x: padding + (detailsDelegate.depth * detailsDelegate.indent) - 10 + height: parent.height + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: ColorPalette.theme.rowShadowStart } + GradientStop { position: 1.0; color: ColorPalette.theme.rowShadowEnd } + } + } + } + )", QUrl()); + QQuickItem* slotItem = qobject_cast(rootComp.create(newContext)); + if (!rootComp.errors().isEmpty()) { + qDebug() << rootComp.errorString(); + } + slotItem->setParentItem(mRootItem); + return { slotItem->childItems()[1] ,slotItem->childItems()[2] }; +} + +QQuickItem* QQuickDetailsViewRowBuilder::rootItem() const +{ + return mRootItem; +} + +IDetailsViewRow* QQuickDetailsViewRowBuilder::row() const +{ + return mRow; +} + +QQuickItem* QQuickDetailsViewRowBuilder::setupItem(QQuickItem* inParent, QString inQmlCode) +{ + QQmlEngine* engine = qmlEngine(inParent); + QQmlContext* context = qmlContext(inParent); + QQmlComponent comp(engine); + QQmlComponent nameComp(engine); + comp.setData(inQmlCode.toLocal8Bit(), QUrl()); + QVariantMap initialProperties; + initialProperties["parent"] = QVariant::fromValue(inParent); + auto item = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); + if (!comp.errors().isEmpty()) { + qDebug() << comp.errorString(); + } + item->setParentItem(inParent); + return item; +} + +void QQuickDetailsViewRowBuilder::setupLabel(QQuickItem* inParent, QString inText) +{ + QQuickItem* lableItem = setupItem(inParent, R"( + import QtQuick; + import QtQuick.Controls; + import ColorPalette; + Item{ + property string lableText + implicitHeight: 25 + width: parent.width + anchors.verticalCenter: parent.verticalCenter + Text { + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + clip: true + elide: Text.ElideRight + text: lableText + color: ColorPalette.theme.labelPrimary + } + } + )"); + lableItem->setProperty("lableText", inText); +} + +void QQuickDetailsViewRowBuilder::setHeightProxy(QQuickItem* inProxyItem) +{ + QQmlEngine* engine = qmlEngine(mRootItem); + QQmlContext* context = qmlContext(mRootItem); + context->parentContext()->setContextProperty("heightProxy", inProxyItem); +} + +void QQuickDetailsViewRowBuilder::makePropertyRow(QPropertyHandle* inHandle) +{ + QQmlEngine* engine = qmlEngine(mRootItem); + QQmlContext* context = qmlContext(mRootItem); + QPair slotItem = makeNameValueSlot(); + QQuickItem* nameEditor = inHandle->setupNameEditor(slotItem.first); + QQuickItem* valueEditor = inHandle->steupValueEditor(slotItem.second); + context->parentContext()->setContextProperty("heightProxy", valueEditor ? valueEditor : nameEditor); +} + +QQuickDetailsViewLayoutBuilder::QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRow) + : mRootRow(inRow) +{ +} + +IDetailsViewRow* QQuickDetailsViewLayoutBuilder::row() const +{ + return mRootRow; +} + +void QQuickDetailsViewLayoutBuilder::addCustomRow(std::function creator, QString inOverrideName) +{ + QSharedPointer child(new QDetailsViewRow_Custom(creator)); + child->setName(inOverrideName); + mRootRow->addChild(child); + child->attachChildren(); +} + +void QQuickDetailsViewLayoutBuilder::addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName) +{ + QSharedPointer child(new QDetailsViewRow_Property(inPropertyHandle)); + child->setName(inOverrideName.isEmpty() ? inPropertyHandle->getName() : inOverrideName); + mRootRow->addChild(child); + child->attachChildren(); +} + +void QQuickDetailsViewLayoutBuilder::addObject(QObject* inObject) +{ + addProperty(QPropertyHandle::FindOrCreate(inObject)); +} diff --git a/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp b/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp new file mode 100644 index 0000000..91e7779 --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp @@ -0,0 +1,138 @@ +#include "QQuickDetailsViewMananger.h" +#include "QPropertyHandle.h" +#include "QQuickFunctionLibrary.h" +#include +#include "QQuickDetailsView.h" +#include "Customization/PropertyTypeCustomization_Sequential.h" +#include "Customization/PropertyTypeCustomization_Associative.h" +#include "Customization/PropertyTypeCustomization_ObjectDefault.h" +#include "Customization/PropertyTypeCustomization_Matrix4x4.h" +#include +#include + +QQuickDetailsViewManager* QQuickDetailsViewManager::Get() +{ + static QQuickDetailsViewManager ins; + return &ins; +} + +void QQuickDetailsViewManager::initialize() +{ + mInitialized = true; + +#ifdef PROPERTY_EDITOR_STATIC_LIBRARY + Q_INIT_RESOURCE(resources); +#endif + + QQuickStyle::setStyle("Basic"); + + qmlRegisterType("QtQuick.DetailsView", 1, 0, "DetailsView"); + + qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette.qml"), + "ColorPalette", + 1, 0, + "ColorPalette"); + + qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette_Light.qml"), + "ColorPalette", + 1, 0, + "ColorPalette_Light"); + + qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette_Dark.qml"), + "ColorPalette", + 1, 0, + "ColorPalette_Dark"); +} + +bool QQuickDetailsViewManager::isInitialized() const +{ + return mInitialized; +} + +void QQuickDetailsViewManager::unregisterPropertyTypeCustomization(const QMetaType& InMetaType) +{ + if(InMetaType.metaObject()) + mMetaTypeCustomizationMap.remove(InMetaType); +} + +void QQuickDetailsViewManager::registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator) +{ + mTypeEditorCreatorMap.insert(inMetaType, Creator); +} + +void QQuickDetailsViewManager::unregisterTypeEditor(const QMetaType& inMetaType) +{ + mTypeEditorCreatorMap.remove(inMetaType); +} + +QQuickItem* QQuickDetailsViewManager::createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent) +{ + if (mTypeEditorCreatorMap.contains(inHandle->getType())) { + QQuickItem* item = mTypeEditorCreatorMap[inHandle->getType()](inHandle, parent); + if (parent) { + item->setProperty("anchors.verticalCenter", QVariant::fromValue(parent->property("anchors.verticalCenter"))); + //item->update(); + } + return item; + } + return nullptr; +} + +QSharedPointer QQuickDetailsViewManager::getCustomPropertyType(QPropertyHandle* inHandle) +{ + if (inHandle->getPropertyType() == QPropertyHandle::Sequential) { + return QSharedPointer::create(); + } + else if (inHandle->getPropertyType() == QPropertyHandle::Associative) { + return QSharedPointer::create(); + } + else if (inHandle->getPropertyType() == QPropertyHandle::Object) { + const QMetaObject* metaObject = inHandle->metaObject(); + for (const auto& It : mClassCustomizationMap.asKeyValueRange()) { + if (It.first == metaObject) { + return It.second(); + } + } + for (const auto& It : mClassCustomizationMap.asKeyValueRange()) { + if (metaObject->inherits(It.first)) { + return It.second(); + } + } + return QSharedPointer::create(); + } + else if (inHandle->getPropertyType() == QPropertyHandle::RawType) { + const QMetaType& metaType = inHandle->getType(); + for (const auto& It : mMetaTypeCustomizationMap.asKeyValueRange()) { + if (It.first == metaType) { + return It.second(); + } + } + const QMetaObject* Child = nullptr; + QRegularExpression reg("QSharedPointer\\<(.+)\\>"); + QRegularExpressionMatch match = reg.match(metaType.name()); + QStringList matchTexts = match.capturedTexts(); + QMetaType innerMetaType; + if (!matchTexts.isEmpty()) { + innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit()); + Child = innerMetaType.metaObject(); + } + else { + Child = metaType.metaObject(); + } + for (const auto& It : mMetaTypeCustomizationMap.asKeyValueRange()) { + const QMetaObject* Parent = It.first.metaObject(); + if (Parent && Child && Child->inherits(Parent)) { + return It.second(); + } + } + } + return nullptr; +} + +QQuickDetailsViewManager::QQuickDetailsViewManager() +{ + RegisterBasicTypeEditor(); + + registerPropertyTypeCustomization(); +} + diff --git a/PropertyEditor/source/src/QQuickDetailsViewModel.cpp b/PropertyEditor/source/src/QQuickDetailsViewModel.cpp new file mode 100644 index 0000000..022c399 --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsViewModel.cpp @@ -0,0 +1,137 @@ +#include "QQuickDetailsViewModel.h" +#include "QQuickDetailsViewRow.h" +#include "QQuickDetailsViewLayoutBuilder.h" + +QQuickDetailsViewModel::QQuickDetailsViewModel(QObject* parent) + : QAbstractItemModel(parent) + , mRoot(new QDetailsViewRow_Property(nullptr)) +{ + mRoot->mModel = this; +} + +QVariant QQuickDetailsViewModel::data(const QModelIndex& index, int role) const { + if (!index.isValid()) + return QVariant(); + IDetailsViewRow* node = static_cast(index.internalPointer()); + return node->name(); +} + +Qt::ItemFlags QQuickDetailsViewModel::flags(const QModelIndex& index) const { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QModelIndex QQuickDetailsViewModel::index(int row, int column, const QModelIndex& parent) const { + if (column < 0 || column >= columnCount(parent)) { + return QModelIndex(); + } + + IDetailsViewRow* parentNode = nullptr; + if (!parent.isValid()) { + parentNode = mRoot.get(); + } + else { + parentNode = static_cast(parent.internalPointer()); + if (!parentNode) { + return QModelIndex(); + } + } + + if (row < 0 || row >= parentNode->mChildren.size()) { + return QModelIndex(); + } + + IDetailsViewRow* childNode = parentNode->mChildren[row].get(); + QModelIndex childIndex = createIndex(row, column, childNode); + childNode->setModelIndex(childIndex); + return childIndex; +} + +QModelIndex QQuickDetailsViewModel::parent(const QModelIndex& index) const { + if (!index.isValid()) + return QModelIndex(); + + IDetailsViewRow* node = static_cast(index.internalPointer()); + IDetailsViewRow* parentNode = node->mParent; + + if (!parentNode || parentNode == mRoot.get()) { + return QModelIndex(); + } + + IDetailsViewRow* grandparent = parentNode->mParent; + int parentRow = grandparent ? grandparent->mChildren.indexOf(parentNode) : -1; + + if (parentRow == -1) { + return QModelIndex(); + } + + QModelIndex parentIndex = createIndex(parentRow, 0, parentNode); + parentNode->setModelIndex(parentIndex); + return parentIndex; +} + +int QQuickDetailsViewModel::rowCount(const QModelIndex& parent) const { + if (!parent.isValid()) + return mRoot->mChildren.size(); + IDetailsViewRow* node = static_cast(parent.internalPointer()); + return node->mChildren.count(); +} + +void QQuickDetailsViewModel::setObject(QObject* inObject) +{ + mObject = inObject; + mRoot->setHandle(QPropertyHandle::FindOrCreate(mObject)); + mRoot->invalidateChildren(); +} + +QObject* QQuickDetailsViewModel::getObject() const +{ + return mObject; +} + +QModelIndex QQuickDetailsViewModel::indexForRow(IDetailsViewRow* row) const +{ + if (!row || row == mRoot.get()) { + return QModelIndex(); + } + + IDetailsViewRow* parentRow = row->mParent; + if (!parentRow) { + return QModelIndex(); + } + + int rowNum = -1; + for (int i = 0; i < parentRow->mChildren.size(); ++i) { + if (parentRow->mChildren[i].data() == row) { + rowNum = i; + break; + } + } + if (rowNum == -1) { + return QModelIndex(); + } + QModelIndex parentIndex = indexForRow(parentRow); + return createIndex(rowNum, 0, row); +} + +void QQuickDetailsViewModel::updateRowIndex(IDetailsViewRow* row) +{ + if (!row) return; + + QModelIndex oldIndex = row->modelIndex(); + QModelIndex newIndex = indexForRow(row); + + if (oldIndex != newIndex) { + row->setModelIndex(newIndex); + dataChanged(newIndex, newIndex); + } +} + +int QQuickDetailsViewModel::columnCount(const QModelIndex& parent) const { + return 1; +} + +QHash QQuickDetailsViewModel::roleNames() const { + return { + { Roles::name,"name" }, + }; +} diff --git a/PropertyEditor/source/src/QQuickDetailsViewPrivate.h b/PropertyEditor/source/src/QQuickDetailsViewPrivate.h new file mode 100644 index 0000000..a0efd06 --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsViewPrivate.h @@ -0,0 +1,41 @@ +#ifndef QQuickDetailsViewPrivate_h__ +#define QQuickDetailsViewPrivate_h__ + +#include "private/qquicktreeview_p_p.h" +#include "QQuickTreeViewExPrivate.h" +#include "QQuickDetailsView.h" +#include "QQuickDetailsViewModel.h" + +class QQuickDetailsViewPrivate : public QQuickTreeViewExPrivate +{ + Q_DECLARE_PUBLIC(QQuickDetailsView) +public: + QQuickDetailsViewPrivate(); + void initItemCallback(int serializedModelIndex, QObject* object) override; + void updateRequiredProperties(int serializedModelIndex, QObject* object, bool init) override; +private: + qreal mSpliterPencent = 0.3; + QList mObjects; + QQuickDetailsViewModel* mModel; +}; + +QQuickDetailsViewPrivate::QQuickDetailsViewPrivate() + :mModel(new QQuickDetailsViewModel) +{ +} + +void QQuickDetailsViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject* object, bool init) +{ + Q_Q(QQuickDetailsView); + const QPoint cell = cellAtModelIndex(serializedModelIndex); + const int row = cell.y(); + const int column = cell.x(); + setRequiredProperty("row", QVariant::fromValue(serializedModelIndex), serializedModelIndex, object, init); + setRequiredProperty("detailsView", QVariant::fromValue(q), serializedModelIndex, object, init); + setRequiredProperty("isTreeNode", column == 0, serializedModelIndex, object, init); + setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init); + setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init); + setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init); +} + +#endif // QQuickDetailsViewPrivate_h__ \ No newline at end of file diff --git a/PropertyEditor/source/src/QQuickDetailsViewRow.cpp b/PropertyEditor/source/src/QQuickDetailsViewRow.cpp new file mode 100644 index 0000000..a537b42 --- /dev/null +++ b/PropertyEditor/source/src/QQuickDetailsViewRow.cpp @@ -0,0 +1,118 @@ +#include "QQuickDetailsViewRow.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QQuickDetailsViewMananger.h" +#include "QQuickDetailsViewModel.h" + +void IDetailsViewRow::setName(QString inName) +{ + mName = inName; +} + +QString IDetailsViewRow::name() +{ + return mName; +} + +void IDetailsViewRow::addChild(QSharedPointer inChild) +{ + mChildren << inChild; + inChild->mModel = mModel; + inChild->mParent = this; +} + +void IDetailsViewRow::clear() +{ + mChildren.clear(); +} + +QQuickDetailsViewModel* IDetailsViewRow::model() +{ + return mModel; +} + +void IDetailsViewRow::invalidateChildren() +{ + if (!mModel) { + mChildren.clear(); + return; + } + + QModelIndex parentIndex = mModel->indexForRow(this); + if (!parentIndex.isValid()) { + parentIndex = QModelIndex(); + } + + const int oldChildCount = mChildren.size(); + if (oldChildCount > 0) { + mModel->beginRemoveRows(parentIndex, 0, oldChildCount - 1); + mChildren.clear(); + mModel->endRemoveRows(); + } + + attachChildren(); + + const int newChildCount = mChildren.size(); + if (newChildCount > 0) { + mModel->beginInsertRows(parentIndex, 0, newChildCount - 1); + for (auto& child : mChildren) { + child->mParent = this; + child->mModel = mModel; + } + mModel->endInsertRows(); + } +} + +QDetailsViewRow_Property::QDetailsViewRow_Property(QPropertyHandle* inHandle) +{ + setHandle(inHandle); +} + +QDetailsViewRow_Property::~QDetailsViewRow_Property() +{ + if (mStructureChangedConnection) { + QObject::disconnect(mStructureChangedConnection); + } +} + +void QDetailsViewRow_Property::setupItem(QQuickItem* inParent) +{ + QQuickDetailsViewRowBuilder builder(this, inParent); + if (mPropertyTypeCustomization) { + mPropertyTypeCustomization->customizeHeaderRow(mHandle, &builder); + return; + } + builder.makePropertyRow(mHandle); +} + +void QDetailsViewRow_Property::attachChildren() +{ + if (mPropertyTypeCustomization){ + QQuickDetailsViewLayoutBuilder builder(this); + mPropertyTypeCustomization->customizeChildren(mHandle, &builder); + } +} + +void QDetailsViewRow_Property::setHandle(QPropertyHandle* inHandle) +{ + mHandle = inHandle; + if (!inHandle) + return; + mPropertyTypeCustomization = QQuickDetailsViewManager::Get()->getCustomPropertyType(inHandle); + mStructureChangedConnection = QObject::connect(inHandle, &QPropertyHandle::asStructureChanged, [this]() { + invalidateChildren(); + }); +} + +QDetailsViewRow_Custom::QDetailsViewRow_Custom(std::function inRowCreator) + : mRowCreator(inRowCreator) +{ + +} + +void QDetailsViewRow_Custom::setupItem(QQuickItem* inParent) +{ + if (mRowCreator) { + QQuickDetailsViewRowBuilder builder(this, inParent); + mRowCreator(&builder); + } +} diff --git a/PropertyEditor/source/src/QQuickFunctionLibrary.cpp b/PropertyEditor/source/src/QQuickFunctionLibrary.cpp new file mode 100644 index 0000000..bfdcc95 --- /dev/null +++ b/PropertyEditor/source/src/QQuickFunctionLibrary.cpp @@ -0,0 +1,58 @@ +#include "QQuickFunctionLibrary.h" +#include +#include + +QQuickFunctionLibrary* QQuickFunctionLibrary::Get() +{ + static QQuickFunctionLibrary Ins; + return &Ins; +} + +QString QQuickFunctionLibrary::numberToString(QVariant var, int precision) +{ + double value = var.toDouble(); + return QString::number(value, 'f', precision); +} + +void QQuickFunctionLibrary::setCursorPos(qreal x, qreal y) +{ + QCursor::setPos(x, y); +} + +void QQuickFunctionLibrary::setOverrideCursorShape(Qt::CursorShape shape) +{ + qApp->setOverrideCursor(shape); +} + +void QQuickFunctionLibrary::restoreOverrideCursorShape() +{ + qApp->restoreOverrideCursor(); +} + +void QQuickFunctionLibrary::setCursorPosTest(QQuickItem* item, qreal x, qreal y) +{ + QPointF global = item->mapToGlobal(x, y); + QCursor::setPos(global.x(), global.y()); + qDebug() << x << y << global << QCursor::pos(); +} + +QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function callback) +{ + if (!sender) + return {}; + return QObject::connect(sender, signal, new QQuickLambdaHolder(callback, receiver ? receiver : sender), SLOT(call())); +} + +QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function callback) +{ + if (!sender) + return {}; + return QObject::connect(sender, signal, new QQuickLambdaHolder_OneParam(callback, receiver ? receiver : sender), SLOT(call(QVariant))); +} + +QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function callback) +{ + if (!sender) + return {}; + return QObject::connect(sender, signal, new QQuickLambdaHolder_TwoParams(callback, receiver ? receiver : sender), SLOT(call(QVariant, QVariant))); +} diff --git a/PropertyEditor/source/src/QQuickTreeViewEx.cpp b/PropertyEditor/source/src/QQuickTreeViewEx.cpp new file mode 100644 index 0000000..0df2f01 --- /dev/null +++ b/PropertyEditor/source/src/QQuickTreeViewEx.cpp @@ -0,0 +1,480 @@ +#include "QQuickTreeViewEx.h" +#include "QQuickTreeViewExPrivate.h" +#include +#include +#include +#include + +// Hard-code the tree column to be 0 for now +static const int kTreeColumn = 0; + + +QQuickTreeViewExPrivate::QQuickTreeViewExPrivate() + : QQuickTableViewPrivate() +{ +} + +QQuickTreeViewExPrivate::~QQuickTreeViewExPrivate() +{ +} + +QVariant QQuickTreeViewExPrivate::modelImpl() const +{ + return m_assignedModel; +} + +void QQuickTreeViewExPrivate::setModelImpl(const QVariant& newModel) +{ + Q_Q(QQuickTreeViewEx); + + if (newModel == m_assignedModel) + return; + + m_assignedModel = newModel; + QVariant effectiveModel = m_assignedModel; + if (effectiveModel.userType() == qMetaTypeId()) + effectiveModel = effectiveModel.value().toVariant(); + + if (effectiveModel.isNull()) + m_treeModelToTableModel.setModel(nullptr); + else if (const auto qaim = qvariant_cast(effectiveModel)) + m_treeModelToTableModel.setModel(qaim); + else + qmlWarning(q) << "TreeView only accepts a model of type QAbstractItemModel"; + + + scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All); + emit q->modelChanged(); +} + +void QQuickTreeViewExPrivate::initItemCallback(int serializedModelIndex, QObject* object) +{ + Q_Q(QQuickTreeViewEx); + updateRequiredProperties(serializedModelIndex, object, true); + QQuickTableViewPrivate::initItemCallback(serializedModelIndex, object); +} + +void QQuickTreeViewExPrivate::itemReusedCallback(int serializedModelIndex, QObject* object) +{ + updateRequiredProperties(serializedModelIndex, object, false); + QQuickTableViewPrivate::itemReusedCallback(serializedModelIndex, object); +} + +void QQuickTreeViewExPrivate::dataChangedCallback( + const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) +{ + Q_Q(QQuickTreeViewEx); + Q_UNUSED(roles); + + for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { + for (int column = topLeft.column(); column <= bottomRight.column(); ++column) { + const QPoint cell(column, row); + auto item = q->itemAtCell(cell); + if (!item) + continue; + + const int serializedModelIndex = modelIndexAtCell(QPoint(column, row)); + updateRequiredProperties(serializedModelIndex, item, false); + } + } +} + +void QQuickTreeViewExPrivate::updateRequiredProperties(int serializedModelIndex, QObject* object, bool init) +{ + Q_Q(QQuickTreeViewEx); + const QPoint cell = cellAtModelIndex(serializedModelIndex); + const int row = cell.y(); + const int column = cell.x(); + + setRequiredProperty("treeView", QVariant::fromValue(q), serializedModelIndex, object, init); + setRequiredProperty("isTreeNode", column == kTreeColumn, serializedModelIndex, object, init); + setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init); + setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init); + setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init); +} + +void QQuickTreeViewExPrivate::updateSelection(const QRect& oldSelection, const QRect& newSelection) +{ + Q_Q(QQuickTreeViewEx); + + const QRect oldRect = oldSelection.normalized(); + const QRect newRect = newSelection.normalized(); + + if (oldSelection == newSelection) + return; + + // Select the rows inside newRect that doesn't overlap with oldRect + for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) { + if (oldRect.y() != -1 && oldRect.y() <= row && row <= oldRect.y() + oldRect.height()) + continue; + const QModelIndex startIndex = q->index(row, newRect.x()); + const QModelIndex endIndex = q->index(row, newRect.x() + newRect.width()); + selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select); + } + + if (oldRect.x() != -1) { + // Since oldRect is valid, this update is a continuation of an already existing selection! + + // Select the columns inside newRect that don't overlap with oldRect + for (int column = newRect.x(); column <= newRect.x() + newRect.width(); ++column) { + if (oldRect.x() <= column && column <= oldRect.x() + oldRect.width()) + continue; + for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) + selectionModel->select(q->index(row, column), QItemSelectionModel::Select); + } + + // Unselect the rows inside oldRect that don't overlap with newRect + for (int row = oldRect.y(); row <= oldRect.y() + oldRect.height(); ++row) { + if (newRect.y() <= row && row <= newRect.y() + newRect.height()) + continue; + const QModelIndex startIndex = q->index(row, oldRect.x()); + const QModelIndex endIndex = q->index(row, oldRect.x() + oldRect.width()); + selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect); + } + + // Unselect the columns inside oldRect that don't overlap with newRect + for (int column = oldRect.x(); column <= oldRect.x() + oldRect.width(); ++column) { + if (newRect.x() <= column && column <= newRect.x() + newRect.width()) + continue; + // Since we're not allowed to call select/unselect on the selectionModel with + // indices from different parents, and since indicies from different parents are + // expected when working with trees, we need to unselect the indices in the column + // one by one, rather than the whole column in one go. This, however, can cause a + // lot of selection fragments in the selectionModel, which eventually can hurt + // performance. But large selections containing a lot of columns is not normally + // the case for a treeview, so accept this potential corner case for now. + for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) + selectionModel->select(q->index(row, column), QItemSelectionModel::Deselect); + } + } +} + +QQuickTreeViewEx::QQuickTreeViewEx(QQuickItem* parent) + : QQuickTableView(*(new QQuickTreeViewExPrivate), parent) +{ + Q_D(QQuickTreeViewEx); + + setSelectionBehavior(SelectRows); + setEditTriggers(EditKeyPressed); + + // Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and + // never the actual tree model that is assigned to us by the application. + const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel)); + d->QQuickTableViewPrivate::setModelImpl(modelAsVariant); + QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged, + d, &QQuickTreeViewExPrivate::dataChangedCallback); + QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged, + this, &QQuickTreeViewEx::rootIndexChanged); + + auto tapHandler = new QQuickTapHandler(this); + tapHandler->setAcceptedModifiers(Qt::NoModifier); + connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler] { + if (!pointerNavigationEnabled()) + return; + if (editTriggers() & DoubleTapped) + return; + + const int row = cellAtPosition(tapHandler->point().pressPosition()).y(); + toggleExpanded(row); + }); +} + +QQuickTreeViewEx::QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent) + : QQuickTableView(dd, parent) +{ + Q_D(QQuickTreeViewEx); + + setSelectionBehavior(SelectRows); + setEditTriggers(EditKeyPressed); + + // Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and + // never the actual tree model that is assigned to us by the application. + const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel)); + d->QQuickTableViewPrivate::setModelImpl(modelAsVariant); + QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged, + d, &QQuickTreeViewExPrivate::dataChangedCallback); + QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged, + this, &QQuickTreeViewEx::rootIndexChanged); + + auto tapHandler = new QQuickTapHandler(this); + tapHandler->setAcceptedModifiers(Qt::NoModifier); + connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler] { + if (!pointerNavigationEnabled()) + return; + if (editTriggers() & DoubleTapped) + return; + + const int row = cellAtPosition(tapHandler->point().pressPosition()).y(); + toggleExpanded(row); + }); + d_func()->init(); +} + +QQuickTreeViewEx::~QQuickTreeViewEx() +{ +} + +QModelIndex QQuickTreeViewEx::rootIndex() const +{ + return d_func()->m_treeModelToTableModel.rootIndex(); +} + +void QQuickTreeViewEx::setRootIndex(const QModelIndex& index) +{ + Q_D(QQuickTreeViewEx); + d->m_treeModelToTableModel.setRootIndex(index); + positionViewAtCell({ 0, 0 }, QQuickTableView::AlignTop | QQuickTableView::AlignLeft); +} + +void QQuickTreeViewEx::resetRootIndex() +{ + Q_D(QQuickTreeViewEx); + d->m_treeModelToTableModel.resetRootIndex(); + positionViewAtCell({ 0, 0 }, QQuickTableView::AlignTop | QQuickTableView::AlignLeft); +} + +int QQuickTreeViewEx::depth(int row) const +{ + Q_D(const QQuickTreeViewEx); + if (row < 0 || row >= d->m_treeModelToTableModel.rowCount()) + return -1; + + return d->m_treeModelToTableModel.depthAtRow(row); +} + +bool QQuickTreeViewEx::isExpanded(int row) const +{ + Q_D(const QQuickTreeViewEx); + if (row < 0 || row >= d->m_treeModelToTableModel.rowCount()) + return false; + + return d->m_treeModelToTableModel.isExpanded(row); +} + +void QQuickTreeViewEx::expand(int row) +{ + if (row >= 0) + expandRecursively(row, 1); +} + +void QQuickTreeViewEx::expandRecursively(int row, int depth) +{ + Q_D(QQuickTreeViewEx); + if (row >= d->m_treeModelToTableModel.rowCount()) + return; + if (row < 0 && row != -1) + return; + if (depth == 0 || depth < -1) + return; + + auto expandRowRecursively = [this, d, depth](int startRow) { + d->m_treeModelToTableModel.expandRecursively(startRow, depth); + // Update the expanded state of the startRow. The descendant rows that gets + // expanded will get the correct state set from initItem/itemReused instead. + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, startRow); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false); + } + }; + + if (row >= 0) { + // Expand only one row recursively + const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row); + if (isExpanded && depth == 1) + return; + expandRowRecursively(row); + } + else { + // Expand all root nodes recursively + const auto model = d->m_treeModelToTableModel.model(); + for (int r = 0; r < model->rowCount(); ++r) { + const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0)); + if (rootRow != -1) + expandRowRecursively(rootRow); + } + } + + emit expanded(row, depth); +} + +void QQuickTreeViewEx::expandToIndex(const QModelIndex& index) +{ + Q_D(QQuickTreeViewEx); + + if (!index.isValid()) { + qmlWarning(this) << "index is not valid: " << index; + return; + } + + if (index.model() != d->m_treeModelToTableModel.model()) { + qmlWarning(this) << "index doesn't belong to correct model: " << index; + return; + } + + if (rowAtIndex(index) != -1) { + // index is already visible + return; + } + + int depth = 1; + QModelIndex parent = index.parent(); + int row = rowAtIndex(parent); + + while (parent.isValid()) { + if (row != -1) { + // The node is already visible, since it maps to a row in the table! + d->m_treeModelToTableModel.expandRow(row); + + // Update the state of the already existing delegate item + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, row); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false); + } + + // When we hit a node that is visible, we know that all other nodes + // up to the parent have to be visible as well, so we can stop. + break; + } + else { + d->m_treeModelToTableModel.expand(parent); + parent = parent.parent(); + row = rowAtIndex(parent); + depth++; + } + } + + emit expanded(row, depth); +} + +void QQuickTreeViewEx::collapse(int row) +{ + Q_D(QQuickTreeViewEx); + if (row < 0 || row >= d->m_treeModelToTableModel.rowCount()) + return; + + if (!d->m_treeModelToTableModel.isExpanded(row)) + return; + + d_func()->m_treeModelToTableModel.collapseRow(row); + + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, row); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false); + } + + emit collapsed(row, false); +} + +void QQuickTreeViewEx::collapseRecursively(int row) +{ + Q_D(QQuickTreeViewEx); + if (row >= d->m_treeModelToTableModel.rowCount()) + return; + if (row < 0 && row != -1) + return; + + auto collapseRowRecursive = [this, d](int startRow) { + // Always collapse descendants recursively, + // even if the top row itself is already collapsed. + d->m_treeModelToTableModel.collapseRecursively(startRow); + // Update the expanded state of the (still visible) startRow + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, startRow); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false); + } + }; + + if (row >= 0) { + collapseRowRecursive(row); + } + else { + // Collapse all root nodes recursively + const auto model = d->m_treeModelToTableModel.model(); + for (int r = 0; r < model->rowCount(); ++r) { + const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0)); + if (rootRow != -1) + collapseRowRecursive(rootRow); + } + } + + emit collapsed(row, true); +} + +void QQuickTreeViewEx::toggleExpanded(int row) +{ + if (isExpanded(row)) + collapse(row); + else + expand(row); +} + +void QQuickTreeViewEx::invalidateLayout() +{ + Q_D(QQuickTreeViewEx); + d->forceLayout(false); +} + +QModelIndex QQuickTreeViewEx::modelIndex(const QPoint& cell) const +{ + Q_D(const QQuickTreeViewEx); + const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x()); + return d->m_treeModelToTableModel.mapToModel(tableIndex); +} + +QPoint QQuickTreeViewEx::cellAtIndex(const QModelIndex& index) const +{ + const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index); + return QPoint(tableIndex.column(), tableIndex.row()); +} + +#if QT_DEPRECATED_SINCE(6, 4) +QModelIndex QQuickTreeViewEx::modelIndex(int row, int column) const +{ + static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4"); + if (compat6_4) { + // XXX Qt 7: Remove this compatibility path here and in QQuickTableView. + // In Qt 6.4.0 and 6.4.1, a source incompatible change led to row and column + // being documented to be specified in the opposite order. + // QT_QUICK_TABLEVIEW_COMPAT_VERSION can therefore be set to force tableview + // to continue accepting calls to modelIndex(column, row). + return modelIndex({ row, column }); + } + else { + qmlWarning(this) << "modelIndex(row, column) is deprecated. " + "Use index(row, column) instead. For more information, see " + "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html"; + return modelIndex({ column, row }); + } +} +#endif + +void QQuickTreeViewEx::keyPressEvent(QKeyEvent* event) +{ + event->ignore(); + + if (!keyNavigationEnabled()) + return; + if (!selectionModel()) + return; + + const int row = cellAtIndex(selectionModel()->currentIndex()).y(); + switch (event->key()) { + case Qt::Key_Left: + collapse(row); + event->accept(); + break; + case Qt::Key_Right: + expand(row); + event->accept(); + break; + default: + break; + } + + if (!event->isAccepted()) + QQuickTableView::keyPressEvent(event); +} \ No newline at end of file diff --git a/PropertyEditor/source/src/QQuickTreeViewExPrivate.h b/PropertyEditor/source/src/QQuickTreeViewExPrivate.h new file mode 100644 index 0000000..b25e548 --- /dev/null +++ b/PropertyEditor/source/src/QQuickTreeViewExPrivate.h @@ -0,0 +1,34 @@ +#ifndef QQuickTreeViewExPrivate_h__ +#define QQuickTreeViewExPrivate_h__ + +#include "private/qquicktreeview_p_p.h" +#include "QQuickTreeViewEx.h" + +class QQuickTreeViewExPrivate : public QQuickTableViewPrivate +{ +public: + Q_DECLARE_PUBLIC(QQuickTreeViewEx) + + QQuickTreeViewExPrivate(); + ~QQuickTreeViewExPrivate() override; + + static inline QQuickTreeViewExPrivate* get(QQuickTreeViewEx* q) { return q->d_func(); } + + QVariant modelImpl() const override; + void setModelImpl(const QVariant& newModel) override; + + void initItemCallback(int serializedModelIndex, QObject* object) override; + void itemReusedCallback(int serializedModelIndex, QObject* object) override; + void dataChangedCallback(const QModelIndex& topLeft, + const QModelIndex& bottomRight, + const QVector& roles); + + virtual void updateRequiredProperties(int serializedModelIndex, QObject* object, bool init); + void updateSelection(const QRect& oldSelection, const QRect& newSelection) override; +public: + QQmlTreeModelToTableModel m_treeModelToTableModel; + QVariant m_assignedModel; +}; + + +#endif // QQuickTreeViewExPrivate_h__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee6168f --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +[![Build Status](http://192.168.46.100:4080/api/badges/CL-Softwares/DiagramDesigner/status.svg)](http://192.168.46.100:4080/CL-Softwares/DiagramDesigner) + +# DiagramDesigner + +桌面端人机界面(Desktop one-line HMI DesignTime) + diff --git a/common/backend/meta_model.h b/common/backend/meta_model.h new file mode 100644 index 0000000..1b2550f --- /dev/null +++ b/common/backend/meta_model.h @@ -0,0 +1,72 @@ +#ifndef META_MODEL_H +#define META_MODEL_H +/*********元模型********/ +#include +#include + +struct attributeGroup { + int id = 0; + QString groupType; + QString groupName; + int isPublic = -1; + QString remark; +}; + +struct dataType //数据类型(元模) +{ + int id = 0; + QString dataType; + QString databaseType; +}; + +struct modelType { + int id = 0; + QString modelType; + QString modelName; + int graphicElement; // 图形元素类型 + QByteArray icon; //图片 + QString remark; +}; + +struct modelGroup +{ + int id = 0; + qint64 modelTypeId = 0; + qint64 attributeGroupId = 0; +}; + +struct attribute { + int id = 0; + QString attribute; // 属性名 + QString attributeName; // 别名 + qint64 dataTypeId = 0; //数据类型id + int lengthPrecision = 0; //长度限制(varchar) + int scale = 0; //小数点位数 + int isNotNull = 0; //是否非空 + QString defaultValue; //默认值 + QString valueRange; //数值范围 + int isVisible = 1; +}; + +struct modelAttribute //模型属性表(所有模型属性的索引) +{ + int id = 0; + qint64 modelTypeId = 0; + qint64 attributeGroupId = 0; + qint64 attributeId = 0; +}; + +struct modelAttributePublic //公共属性表 +{ + int id = 0; + qint64 attributeGroupId = 0; + qint64 attributeId = 0; +}; + +struct modelConnectivity { //模型连接性表(元模是否可以连接) + int id = 0; + QString fromModel; //属性名 + QString toModel; + int connectivity = 0; //是否可连 +}; +#endif diff --git a/common/backend/project_model.h b/common/backend/project_model.h new file mode 100644 index 0000000..e5a4cf3 --- /dev/null +++ b/common/backend/project_model.h @@ -0,0 +1,284 @@ +#ifndef PROJECT_MODEL_H +#define PROJECT_MODEL_H + +#include +#include +#include +#include +#include +#include "tools.h" + +struct ProjectManagerStruct { + int id = 0; + QString name; // 工程模表名 + QString tag; // 工程模名称 + QString metaModel; // 元模名 + QString groupName; // 属性组名 + int linkType; // 图元链接类型 + QJsonObject checkState; // 属性选择状态 +}; + +struct ProjectModelSettingStruct //工程模设定类,如图标 +{ + QString modelName; //工程模名 + QMap mapSvg; //存放选择的svg图片 + QMap mapUsedSvg; //存放使用的svg +}; + +struct PropertyState { + bool checkState = false; + QString dataType; + bool editable = true; +}; + +struct PropertyPage { + QMap checkState; + bool isPublic = false; +}; + +struct FormerName //曾用名,记录修改前名称 +{ + QString sName; + bool bChanged = false; //是否改变过 +}; + +typedef QMap MapProperty; + +struct PropertyModel { + MapProperty mapProperty; + int nType = 0; //工程模类型,选择图标后确定 + QStandardItemModel* pBase = nullptr; //基础属性 + QStandardItemModel* pSelect = nullptr; //已选择属性 + FormerName formerMeta; //曾用元模名 + FormerName formerProject; //曾用工程模名 + QMap dataInfo; //存放数据库内容 + ProjectModelSettingStruct modelSetting; + PropertyModel deepCopy() //深拷贝 + { + PropertyModel copy; + copy.mapProperty = mapProperty; + copy.nType = nType; + copy.pBase = deepCloneModel(pBase); + copy.pSelect = deepCloneModel(pSelect); + copy.formerMeta = formerMeta; + copy.formerProject = formerProject; + copy.dataInfo = dataInfo; + copy.modelSetting = modelSetting; + return copy; + } + void release() + { + delete pBase; + delete pSelect; + pBase = nullptr; + pSelect = nullptr; + } +}; + +typedef QMap MapProject; //str为工程名,PropertyModel为工程属性 +typedef QMap MapMeta; //str为元模名,PropertyModel为工程模集 + +struct PropertyStateInfo { + QString name; // 属性名 + QString tagName; // 别名 + QString type; // 属性类型 + QVariant defaultValue; // 默认值 + int lengthPrecision = 999; // 长度限制 + int isVisible = 1; // 是否可见(0不可见,1可见,2特殊项) + bool lock = false; // 值手动改变时置true,下次保存时恢复 +}; + +Q_DECLARE_METATYPE(PropertyStateInfo) + +// 属性值信息映射 +typedef QMap PropertyValueInfo; + +// 属性组状态信息 +struct GroupStateInfo { + QString groupName; // 组名 + QString tableName; // 表名 + bool isPublic = false; // 是否公共 + PropertyValueInfo info; // 属性信息 +}; + +// 属性组实时数据 +struct GroupStateValue { + QString groupName; // 组名 + QString tableName; // 表名 + QMap mapInfo; // 实时信息映射 +}; + +struct ModelStateInfo //模型结构信息 +{ + QString modelName; + int modelType; + QWidget* _PropertyDlg = NULL; //属性设置界面,每个模型维护一种界面 + QMap groupInfo; //属性组信息 + void release() + { + if(_PropertyDlg){ + delete _PropertyDlg; + _PropertyDlg = nullptr; + } + } +}; + +struct ModelDataInfo //模型数据信息 +{ + QString modelName; + int modelType; + QMap groupInfo; //属性组实时信息 +}; + +struct PropertyGroupState { + QString groupName; // 属性组名称 + QString tableName; // 属性组表名 + QJsonObject propertyState; // 属性状态信息 +}; + +struct MeasureAttributeType //量测可用属性类型(从基模获取的占位符变量) +{ + QString tag; + QString name; +}; + +// 属性内容信息 +struct PropertyContentInfo { + QString proTag; // 属性标签 + QString proName; // 属性名称 + QString proType; // 属性类型 + QWidget* proEditor = nullptr; // 编辑窗口对象 + + bool isValid() const { + return !proTag.isEmpty() && !proName.isEmpty(); + } +}; + +struct PtExtraInfo +{ + int index = 0; + QString scope; //变比标签 + QString accuracy; //精度等级标签 + QString volume; //二次负载容量标签 + QString star; //线圈接法 + double ratio; //变比 + int polarity = 1; //极性 +}; + +struct CtExtraInfo +{ + int index = 0; + QString scope; //变比标签 + QString accuracy; //精度等级标签 + QString volume; //二次负载容量标签 + double ratio; //变比 + int polarity = 1; //极性 +}; + +struct MeasurementInfo //量测 +{ + QString tag; //量测tag + QString name; //量测名称 + int type; //量测类型 0:遥测 1:遥信 2:遥控 + int size; //量测size + QUuid bayUuid; //所属间隔 + QUuid componentUuid; //所属设备 + + //通讯 + int nSource; //数据来源 1:cl3611 2:104 + QString sStation; //子站名称 + QString sDevice; //设备名称 + QString sChannel; //通道名(cl3611) 遥测:TMx(1-8); P; Q; S; PF; F; deltaF; UAB; UBC; UCA; 遥信: TSxx(01-10); 遥控: TCx; + int nPacket; //包号(104) + int nOffset; //偏移量(104) + //事件 + bool bEnable = false; //"enable"开启标志 + QMap mapTE; //遥测"cause" key:upup,up,down,downdown可选,val:0-100 + QString sEdge; //遥信"cause:edge" raising, falling 字符串单选 + QString sCommand; //"action:command" info, warning, error, critical, exception 字符串单选 + QStringList lstParameter; //"action:parameters" 字符串数组 + + QString sWindType; //绕组类型 ctpt + int nRatio; //变比 + int nPolarity; //极性 + int nIndex; //对应绕组序号 + + QString sSymmetry; //对称量测的name +}; + +// 定义比较键的结构 +struct MeasurementKey { + int nSource; + QString sStation; + QString sDevice; + QString sChannel; + int nPacket; + int nOffset; + + MeasurementKey(const MeasurementInfo& info) + : nSource(info.nSource) + , sStation(info.sStation) + , sDevice(info.sDevice) + , sChannel(info.sChannel) + , nPacket(info.nPacket) + , nOffset(info.nOffset) {} + + // 用于QMap排序 + bool operator<(const MeasurementKey& other) const { + if (nSource != other.nSource) return nSource < other.nSource; + if (sStation != other.sStation) return sStation < other.sStation; + if (sDevice != other.sDevice) return sDevice < other.sDevice; + if (sChannel != other.sChannel) return sChannel < other.sChannel; + if (nPacket != other.nPacket) return nPacket < other.nPacket; + return nOffset < other.nOffset; + } +}; + +struct ComponentInfo //工程模图元数据 +{ + QUuid uuid; + QString modelName; + QString nspath; + QString tag; + QString name; + QString description; + QString grid; + QString zone; + QString station; + int type = 0; + bool inService = true; + int state = 0; + int status = 0; + QJsonObject connected_bus; + QJsonObject label; + QJsonObject context; + int op = 0; +}; + +struct ComponentTypeInfo //元件类型 +{ + int id = 0; + QString type; + QString name; + QJsonObject config; +}; + +struct PageInfo +{ + int id = -1; + QString tag; + QString name; + QJsonObject label; + QJsonObject context; + QString description; + int op; +}; + +// 页面中保存的项信息 +struct ItemPageInfo { + QPointF pos; + double dWidth = 0.0; + double dHeight = 0.0; + double dRotate = 0.0; +}; +#endif diff --git a/common/core_model/constants.h b/common/core_model/constants.h new file mode 100644 index 0000000..9f763fe --- /dev/null +++ b/common/core_model/constants.h @@ -0,0 +1,15 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H +/*********常量定义********/ +namespace Constants { +const double SCENE_WIDTH = 800; +const double SCENE_HEIGHT = 600; +const int EDITOR_ITEM_WIDTH = 150; +const int EDITOR_ITEM_HEIGHT = 80; +const int EDITOR_BUS_HEIGHT = 10; +const int V_DIAGRAM_SPACING = 80; +const int H_DIAGRAM_SPACING = 80; +const int TRANSFORMER_LEVEL = 999; // 层级结构中变压器所处层级 +} + +#endif diff --git a/common/core_model/data_transmission.h b/common/core_model/data_transmission.h new file mode 100644 index 0000000..c64da6f --- /dev/null +++ b/common/core_model/data_transmission.h @@ -0,0 +1,14 @@ +#ifndef DATA_TRANSMISSION_H +#define DATA_TRANSMISSION_H +/*********数据传输********/ +#include +#include + +/*******************传递的数据************************/ +struct HttpRecommandInfo{ //推荐数据服务 + QString sInput; //输入的前缀 + int nOffset = 0; //正确位置计数 + QStringList lstRecommand; //推荐列表 +}; + +#endif diff --git a/common/core_model/diagram.h b/common/core_model/diagram.h new file mode 100644 index 0000000..4c66a3e --- /dev/null +++ b/common/core_model/diagram.h @@ -0,0 +1,372 @@ +#ifndef DIAGRAM_H +#define DIAGRAM_H +/*********组态图********/ +#include +#include +#include +#include +#include +#include +#include + +// 组态图信息 +struct DiagramInfo { + QVariant id; //临时id使用uuid,load数据使用数据库自增id + QString sName; + QString sTag; + QVariant parentId; + QString sBasePageName; //父组态图名称 +}; + +struct DiagramEditorWizardBusInfo //组态编辑母线信息 +{ + int nIndex = 0; + double dVoltage = 0; //电压等级 + int nLineType = 1; //1单母线,2双母线 + int nNum1 = 0; //1母 + int nNum2 = 0; //2母 + int connectType = 0; //接线方式,1为分段连接 + int nState = 0; //母线状态位 0已保存1新建2修改 + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorWizardBusInfo &data) { + out << data.nIndex << data.dVoltage << data.nLineType << data.nNum1 << data.nNum2 << data.connectType << data.nState; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorWizardBusInfo &data) { + in >> data.nIndex >> data.dVoltage >> data.nLineType >> data.nNum1 >> data.nNum2 >> data.connectType >> data.nState; + return in; + } +}; + +enum class TransformerType //变压器类型 +{ + twoWinding = 0, //两绕组 + threeWinding //三绕组 +}; + +enum class BayType //间隔类型 +{ + busSectionBay = 0, //分段 + busCouplerBay, //母联 + ptBay, //pt + incomingBay, //进线 + outcomingBay, //出线 + compensationBay, //无功补偿 + bypassBay, //旁路 + mainTransformerBay //主变 +}; + +enum class EditorItemType //组态编辑中的图形项类型 +{ + bus = 1, //母线 + bay, //间隔 + trans, //变压器 + line, //连接线 + None //空白占位图 +}; + +struct EditorTransConnection //组态变压器连接信息 +{ + QString sName; + int nPara = 0; //012,高中低侧 +}; + +struct DiagramEditorWizardTransformerInfo //组态变压器信息 +{ + QString sName; + TransformerType nType; + double dVoltageLevel; + QList lstBindObj; //连接的对象 +}; + +struct DiagramEditorWizardBayInfo //组态间隔信息 +{ + QString sName; + BayType nType; + double dVoltageLevel; + QList lstBindObj; //连接的对象 +}; + +struct DiagramEditorComponentInfo //组态设备信息 +{ + int nCategory = 0; //分类 0电气设备1连接关系 + QString sName; + int nType = 0; //类型 1母线2异步电动机3断路器4电缆5电流互感器6电压互感器7隔离开关8接地开关9快速接地开关10双掷接地隔离开关11带电指示器12避雷器13电缆出线套筒14电缆端 + QString sBindObj; //所关联的实体名 (母线block,间隔block,变压器高中低端子) + int nBindType = 0; //关联实体的类型 1母线2间隔3变压器 + int nBindPara = 0; //关联额外参数,关联变压器时为(0高1中2低) + QString sBindParent; //关联父item名,关联变压器时为变压器名 + QStringList sUsedRoute; //使用设备的线路名 + int nUsedDirection = 0; //被占用的方向 8421 上下左右 + QPoint deltaPos = QPoint(0,0); //相对坐标(相对间隔) + QUuid uid; + int nFlag = 0; //标志0未使用1新建2修改 + int nRotate = 0; //旋转角 + + bool operator ==(const DiagramEditorComponentInfo& obj) const { + if(nCategory == obj.nCategory && sName == obj.sName && nType == obj.nType && sBindObj == obj.sBindObj && nBindType == obj.nBindType && + sUsedRoute == obj.sUsedRoute && nUsedDirection == obj.nUsedDirection && deltaPos == obj.deltaPos && uid == obj.uid && nFlag == obj.nFlag && nRotate == obj.nRotate) + return true; + else + return false; + } + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorComponentInfo &data) { + out << data.nCategory << data.sName << data.nType << data.sBindObj << data.nBindType << data.nBindPara << data.sBindParent << data.sUsedRoute << data.nUsedDirection << data.deltaPos << data.uid << data.nFlag << data.nRotate; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorComponentInfo &data) { + in >> data.nCategory >> data.sName >> data.nType >> data.sBindObj >> data.nBindType >> data.nBindPara >> data.sBindParent >> data.sUsedRoute >> data.nUsedDirection >> data.deltaPos >> data.uid >> data.nFlag >> data.nRotate; + return in; + } +}; + +inline uint qHash(const DiagramEditorComponentInfo &key, uint seed = 0) { + return qHash(key.uid, seed); +} + +struct DiagramEditorRouteInfo //间隔中单条线路信息 +{ + QString sRouteName; + bool bMainRoute = false; //主线路(包含设备最多的线路,决定整体布局) + QList lstCompo; + QList lstOrder; //线路顺序容器(计算用) + QList lstReverse; //逆序容器(计算用) + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorRouteInfo &data) { + out << data.sRouteName << data.bMainRoute << data.lstCompo << data.lstOrder << data.lstReverse; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorRouteInfo &data) { + in >> data.sRouteName >> data.bMainRoute >> data.lstCompo >> data.lstOrder >> data.lstReverse; + return in; + } +}; + +struct DiagramEditorBayInfo //组态编辑间隔信息 +{ + QString name; //间隔名 + int nLayout; //布局 0纵向1横向 + QList lstFrom; //起始 + QList lstTo; //结束 + QMap mapRoute; //线路信息 + QMap mapComponent; //设备信息 + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBayInfo &data) { + out << data.name << data.nLayout << data.lstFrom << data.lstTo << data.mapRoute << data.mapComponent; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorBayInfo &data) { + in >> data.name >> data.nLayout >> data.lstFrom >> data.lstTo >> data.mapRoute >> data.mapComponent; + return in; + } +}; + +struct DiagramEditorTransNeutralInfo //组态编辑变压器中性点信息 +{ + QString name; //中性点名 + int nType = 0; //中性点类型 0高1中2低 + QPointF delPoint; //相对变压器偏移量 + QMap mapRoute; //中性点对应的线路结构 + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorTransNeutralInfo &data) { + out << data.name << data.nType << data.delPoint << data.mapRoute; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorTransNeutralInfo &data) { + in >> data.name >> data.nType >> data.delPoint >> data.mapRoute; + return in; + } +}; + +struct DiagramEditorTransInfo //组态编辑变压器信息 +{ + QString name; //变压器名 + QMap mapNeutral; //中性点结构 + QMap mapComponent; //设备信息 + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorTransInfo &data) { + out << data.name << data.mapNeutral << data.mapComponent; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorTransInfo &data) { + in >> data.name >> data.mapNeutral >> data.mapComponent; + return in; + } +}; + +enum class DiagramEditorStructType +{ + block = 0, //模块(母线段、间隔、变压器) + blockContainer, //模块容器(包含若干模块) + rowData //行数据(包含若干容器) +}; + +struct DiagramEditorConnectType //组态编辑连接信息 +{ + QString sName; + int nType = 0; //1母线,2间隔,3变压器 + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorConnectType &data) { + out << data.sName << data.nType; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorConnectType &data) { + in >> data.sName >> data.nType; + return in; + } +}; + +struct DiagramEditorBriefConnect //组态编辑时连接信息 +{ + QUuid uid; + DiagramEditorConnectType con1; + DiagramEditorConnectType con2; + int nPara = 0; //万用参数(变压器中表示连接位置,0高压侧,1中压侧,2低压侧) + + bool operator==(const DiagramEditorBriefConnect& obj) + { + if(this == &obj) + return false; + if((con1.sName == obj.con1.sName && con2.sName == obj.con2.sName)||(con1.sName == obj.con2.sName && con2.sName == obj.con1.sName)) + return true; + else + return false; + } + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBriefConnect &data) { + out << data.uid << data.con1 << data.con2 << data.nPara; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorBriefConnect &data) { + in >> data.uid >> data.con1 >> data.con2 >> data.nPara; + return in; + } + + DiagramEditorConnectType getOpposite(const QString& s){ //获取另一端名称 + if(con1.sName == s) + return con2; + else if(con2.sName == s) + return con1; + return DiagramEditorConnectType(); + } +}; + +struct DiagramEditorBlockInfo //组态编辑block信息 +{ + QString sName; + QString sContainerId; //所属的容器id + int nType; //1母线,2间隔,3变压器 + int nContainerLevel; //所处容器的层级 0,1,2,3 + QUuid uid; + QList _lstCon; //连接信息 + QList> _lstSub; //子对象列表(非拓扑计算使用) 如母线子对象为间隔,间隔子对象为item,电动机子对象为item <类型,uid> 类型:0设备1间隔 + QRectF recSize; //当前大小(根据内容确定) + QPointF sceneDelta; //block中心相对位移(计算布局位置 + bool bEditState; //详细编辑状态 + + //bus + float fVoltage; //母线电压 + int nBusType; //0无前缀,1:Ⅰ母,2Ⅱ母 + int nIndex; //第几段,1A,2B,3C,4D,5E,6F,7G,8H + //bay + BayType nBayType; + DiagramEditorBayInfo bayInfo; //间隔信息 + //transformer + TransformerType nTransType; + DiagramEditorTransInfo transInfo; + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBlockInfo &data) { + out << data.sName << data.sContainerId << data.nType << data.nContainerLevel << data.uid << data._lstCon << data.recSize << data.sceneDelta + << data.bEditState << data.fVoltage << data.nBusType << data.nIndex << data.nBayType << data.bayInfo << data.nTransType << data.transInfo; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorBlockInfo &data) { + in >> data.sName >> data.sContainerId >> data.nType >> data.nContainerLevel >> data.uid >> data._lstCon >> data.recSize >> data.sceneDelta + >> data.bEditState >> data.fVoltage >> data.nBusType >> data.nIndex >> data.nBayType >> data.bayInfo >> data.nTransType >> data.transInfo; + return in; + } +}; + +struct DiagramEditorContainerInfo //组态编辑容器信息 +{ + QString sId; + double dMidUpY; //1母上边界 + double dMidDownY; //2母下边界 + double dStartX; //起始x + double dStartY; //起始y + double dWidth; //宽度 + double dHeight; + double dMaxUpH; //上方最大高度(1母线到上边界) + double dMaxDownH; //下方最大高度(2母线到下边界) + QMap> mapBlockInfo; + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorContainerInfo &data) { + out << data.sId << data.dMidUpY << data.dMidDownY << data.dStartX << data.dStartY << data.dWidth << data.dHeight << data.dMaxUpH << data.dMaxDownH << data.mapBlockInfo; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorContainerInfo &data) { + in >> data.sId >> data.dMidUpY >> data.dMidDownY >> data.dStartX >> data.dStartY >> data.dWidth >> data.dHeight >> data.dMaxUpH >> data.dMaxDownH >> data.mapBlockInfo; + return in; + } +}; + +struct DiagramEditorProjectInfo //editor工程信息 +{ + QString sName; + QUuid uid; + QMap mapBus; + QMap> mapSturctContainer; + QMap mapConnect; + + friend QDataStream &operator<<(QDataStream &out, const DiagramEditorProjectInfo &data) { + out << data.sName << data.uid << data.mapBus << data.mapSturctContainer << data.mapConnect; + return out; + } + friend QDataStream &operator>>(QDataStream &in, DiagramEditorProjectInfo &data) { + in >> data.sName >> data.uid >> data.mapBus >> data.mapSturctContainer >> data.mapConnect; + return in; + } +}; + +struct DiagramContent { + QString diagramId; // 对应的PowerEntity ID + + // 元素位置信息 <元素ID, 位置> + QHash elementPositions; + QHash> connectionPaths; // 自定义路径点 +}; + +struct BaseComponentInfo //基模图元数据 +{ + int id = 0; + QUuid uuid; + QString name; + QString tag; + int typeId = 0; + QJsonObject context; + QString metaMmodel; + QString projectModel; +}; + +struct EditorProjectInfo //editor工程数据 +{ + int id = 0; + QUuid uuid; + QString name; + QString tag; +}; + +struct EditorBaseSettingInfo //editor基础设置 +{ + int id = 0; + QUuid uuid; + QString projectName; //所属工程名 + QString autor; //作者 + QByteArray context; //内容 + QUuid generateUid; //生成的图uuid + QString ts; //时间戳 +}; + + +#endif // DIAGRAM_H diff --git a/common/core_model/topology.h b/common/core_model/topology.h new file mode 100644 index 0000000..dcdba36 --- /dev/null +++ b/common/core_model/topology.h @@ -0,0 +1,413 @@ +#ifndef TOPOLOGY_H +#define TOPOLOGY_H +/*********拓扑相关结构********/ +#include +#include +#include +#include +#include +#include + +// 数据状态 +enum class DataState { + Unchanged = 0, + Changed, + PrepareDelete +}; + +// 端口位置 +enum PortPos { + P_top = 0, + P_down, + P_left, + P_right +}; + +enum HandleType +{ + T_none = 0, + T_resize, //调整大小 + T_rotate, //旋转 + T_editShape, //编辑形状 + T_text, //文本 + T_lineIn, //入线口 + T_lineOut, //出线口 + T_lineInOut, //双端线 + T_newTral //中性点 +}; + +struct Connection +{ + QUuid nSrcNodeId; + QUuid nSrcPortId; + HandleType srcType; + PortPos srcPos; + QUuid nDestNodeId; + QUuid nDestPortId; + HandleType destType; + PortPos destPos; + + Connection() + { + srcType = T_none; + srcPos = P_top; + destType = T_none; + destPos = P_top; + nSrcNodeId = QUuid(); + nSrcPortId = QUuid(); + nDestNodeId = QUuid(); + nDestPortId = QUuid(); + } + + Connection(const Connection& obj) + { + nSrcNodeId = obj.nSrcNodeId; + nSrcPortId = obj.nSrcPortId; + srcType = obj.srcType; + srcPos = obj.srcPos; + nDestNodeId = obj.nDestNodeId; + nDestPortId = obj.nDestPortId; + destType = obj.destType; + destPos = obj.destPos; + } + + Connection(QUuid nSNI,QUuid nSPI,HandleType sT,PortPos sPOS,QUuid nDNI,QUuid nDPI,HandleType dT,PortPos dPOS) + { + nSrcNodeId = nSNI; + nSrcPortId = nSPI; + srcType = sT; + srcPos = sPOS; + nDestNodeId = nDNI; + nDestPortId = nDPI; + destType = dT; + destPos = dPOS; + } + bool operator==(const Connection& obj) + { + return ((nSrcNodeId == obj.nSrcNodeId)&&(nSrcPortId == obj.nSrcPortId)&&(srcType == obj.srcType)&&(nDestNodeId == obj.nDestNodeId)&&(nDestPortId == obj.nDestPortId)&&(destType == obj.destType)); + } + + Connection& operator=(const Connection& obj) + { + if(this == &obj) + return *this; + nSrcNodeId = obj.nSrcNodeId; + nSrcPortId = obj.nSrcPortId; + srcType = obj.srcType; + srcPos = obj.srcPos; + nDestNodeId = obj.nDestNodeId; + nDestPortId = obj.nDestPortId; + destType = obj.destType; + destPos = obj.destPos; + return *this; + } +}; + +// 拓扑连接信息 +struct TopologyInfo +{ + int id = -1; + QUuid uuid_from; + QUuid uuid_to; + QJsonObject context; + int flag; + QString description; + int op; +}; + +// 电网信息 +struct GridInfo +{ + int id = -1; + QString tagname; + QString name; + QString description; +}; + +// 区域信息 +struct ZoneInfo +{ + int id = -1; + int grid_id = -1; + QString tagname; + QString name; + QString description; +}; + +// 电站信息 +struct StationInfo +{ + int id = -1; + int zone_id = -1; + QString tagname; + QString name; + QString description; + bool is_local = true; +}; + +// 间隔信息 +struct BayInfo +{ + QUuid uuid; + QString name; + QString tag; + QString type; + double unom; + double fla; + double capacity; + QString description; + bool inService; + int nState; + QString grid; + QString zone; + QString station; + QJsonObject business; + QJsonObject fromUuid; + QJsonObject toUuid; + QJsonObject protect; + QJsonObject faultRec; + QJsonObject status; + QJsonObject dynSense; + QJsonObject instruct; + QJsonObject etc; + QJsonObject context; + QList components; +}; + +// 层级关系结构项 +struct HierarchyStructItem { + QString sVoltageLevel; + int nCategory; //类型 0设备1间隔 + int nEquipType; //设备类别 + QString sName; //名称 + QUuid uid; //id + QString grid; //电网 + QString zone; //区域 + QString station; //电站 + + QJsonObject toJson() const { + QJsonObject obj; + obj["sVoltageLevel"] = sVoltageLevel; + obj["nCategory"] = nCategory; + obj["nEquipType"] = nEquipType; + obj["sName"] = sName; + obj["uid"] = uid.toString(); + obj["grid"] = grid; + obj["zone"] = zone; + obj["station"] = station; + return obj; + } + + // 从JSON对象解析 + void fromJson(const QJsonObject& json) { + sVoltageLevel = json["sVoltageLevel"].toString(); + nCategory = json["nCategory"].toInt(); + nEquipType = json["nEquipType"].toInt(); + sName = json["sName"].toString(); + uid = QUuid::fromString(json["uid"].toString()); + grid = json["grid"].toString(); + zone = json["zone"].toString(); + station = json["station"].toString(); + } + + // 检查有效性 + bool isValid() const { + return !uid.isNull() && !sName.isEmpty(); + } + + // 重载相等运算符 + bool operator==(const HierarchyStructItem& other) const { + return uid == other.uid && + sName == other.sName && + nCategory == other.nCategory; + } +}; + +// 层级关系项 +struct HierarchyItem { + HierarchyStructItem parent; + HierarchyStructItem item; + QList subList; + + QJsonObject toJson() const { + QJsonObject obj; + obj["parent"] = parent.toJson(); + obj["item"] = item.toJson(); + + // 序列化子列表 + QJsonArray subArray; + for (const auto& subItem : subList) { + subArray.append(subItem.toJson()); + } + obj["subList"] = subArray; + obj["subCount"] = subList.size(); + + return obj; + } + + // 从JSON对象解析 + void fromJson(const QJsonObject& json) { + parent.fromJson(json["parent"].toObject()); + item.fromJson(json["item"].toObject()); + + // 解析子列表 + subList.clear(); + QJsonArray subArray = json["subList"].toArray(); + for (const QJsonValue& subValue : subArray) { + HierarchyStructItem sub; + sub.fromJson(subValue.toObject()); + subList.append(sub); + } + } + + // 检查有效性 + bool isValid() const { + return parent.isValid() && item.isValid(); + } + + // 添加子项 + void addSubItem(const HierarchyStructItem& subItem) { + subList.append(subItem); + } + + // 移除子项 + bool removeSubItem(const QUuid& subUid) { + for (int i = 0; i < subList.size(); ++i) { + if (subList[i].uid == subUid) { + subList.removeAt(i); + return true; + } + } + return false; + } + + // 查找子项 + HierarchyStructItem* findSubItem(const QUuid& subUid) { + for (auto& sub : subList) { + if (sub.uid == subUid) { + return ⊂ + } + } + return nullptr; + } +}; + +//属性其他参数与层级关系 +struct ExtraProperty +{ + int id = 0; + QString code; // 唯一编码(拼接字符串) + QString tag; // 属性tag + QString name; // 显示名称 + QString connect_para; // 连接参数 + + // 层级名称 + QString grid_name; + QString zone_name; + QString station_name; + QString currentLevel; + QString bay_name; + QString component_name; + QString group_name; + QString type_name; + + //层级索引 + QString grid_tag; + QString zone_tag; + QString station_tag; + QString page_tag; + QString bay_tag; + QUuid component_uuid; + QString component_tag; + QString group_tag; + QString type_tag; + + // 数据源配置 + QString sourceType; // "property", "measurement" + QVariantMap sourceConfig; + + bool bDataChanged = false; //数据改变标志(临时) + // 获取完整路径 + QString getFullName() const { + QStringList parts = {grid_name, zone_name, station_name, currentLevel, bay_name, component_name, group_name, name}; + parts.removeAll(""); + return parts.join("."); + } + + //获取完整索引 + QString getFullTag() const { + QStringList parts = {grid_tag, zone_tag, station_tag, page_tag, currentLevel, bay_tag, component_uuid.toString(), group_tag, tag}; + parts.removeAll(""); + return parts.join("."); + } + + // 检查是否匹配过滤条件 + bool matches(const QVariantMap& filter,const QString& type) const { + for (auto it = filter.begin(); it != filter.end(); ++it) { + QString field = it.key(); + QString filterValue = it.value().toString(); + QString fieldValue; + if(type == "name"){ + fieldValue = getFieldName(field); + } + else if(type == "tag"){ + fieldValue = getFieldTag(field); + } + + if (!filterValue.isEmpty() && fieldValue != filterValue) { + return false; + } + } + return true; + } + + QString getFieldName(const QString& field) const { + if (field == "grid") return grid_name; + if (field == "zone") return zone_name; + if (field == "station") return station_name; + if (field == "currentLevel") return currentLevel; + if (field == "bay") return bay_name; + if (field == "component") return component_name; + if (field == "group") return group_name; + if (field == "property") return name; + return ""; + } + + QString getFieldTag(const QString& field) const { + if (field == "grid") return grid_tag; + if (field == "zone") return zone_tag; + if (field == "station") return station_tag; + if (field == "page") return page_tag; + if (field == "currentLevel") return currentLevel; + if (field == "bay") return bay_tag; + if (field == "component") return component_name; + if (field == "group") return component_uuid.toString(); + if (field == "tag") return name; + return ""; + } +}; + +struct ExtraPropertyLevelInfo { //层级关系item信息 + QString displayText; // 显示文本 + QString nameValue; // 名称值 + QString tagValue; // 标签值 + bool isRequired; // 是否必填 + QString placeholder; // 缺省占位符 +}; + +// 基础实体类型 +enum class EntityType { + Grid, + Zone, + Station, + ConfigurationDiagram, + Component +}; + +struct EntityInfo { + QString sName; + QString sUuid; + QString sParentId; + EntityType eType; +}; +#endif // TOPOLOGY_H diff --git a/common/core_model/types.cpp b/common/core_model/types.cpp new file mode 100644 index 0000000..3f53f31 --- /dev/null +++ b/common/core_model/types.cpp @@ -0,0 +1,9 @@ +#include "types.h" + +const QMap linkType = { + {AIT_motor,GIT_itemRect},{AIT_bus,GIT_bus}, + }; +//类型转换 +const QMap typeToProGraphic = { + {0,GIT_node},{1,GIT_bus},{3,GIT_itemRect},{4,GIT_ctGroup},{5,GIT_ptGroup},{6,GIT_ES},{7,GIT_FES},{8,GIT_link},{9,GIT_DS},{10,GIT_DTEDS},{11,GIT_PI},{12,GIT_LA},{13,GIT_cableTer},{14,GIT_cableEnd},{15,GIT_2wTransformer},{16,GIT_3wTransformer} +}; diff --git a/common/core_model/types.h b/common/core_model/types.h new file mode 100644 index 0000000..a986511 --- /dev/null +++ b/common/core_model/types.h @@ -0,0 +1,172 @@ +#ifndef TYPES_H +#define TYPES_H +/*********枚举类型********/ +#include +#include +#include + +// 图形项类型 +enum GraphicsItemType +{ + GIT_base = QGraphicsItem::UserType + 1, + GIT_line = QGraphicsItem::UserType + 2, + GIT_rect = QGraphicsItem::UserType + 3, + GIT_roundRect = QGraphicsItem::UserType + 4, + GIT_ellipse = QGraphicsItem::UserType + 5, + GIT_polygon = QGraphicsItem::UserType + 6, + //====================================== + GIT_bus = QGraphicsItem::UserType + 50, + GIT_itemRect = QGraphicsItem::UserType + 51, + GIT_itemTri = QGraphicsItem::UserType + 52, + GIT_link= QGraphicsItem::UserType + 53, + GIT_ctItem= QGraphicsItem::UserType + 54, + GIT_ctGroup= QGraphicsItem::UserType + 55, + GIT_ptItem= QGraphicsItem::UserType + 56, + GIT_ptGroup= QGraphicsItem::UserType + 57, + GIT_ES= QGraphicsItem::UserType + 58, + GIT_DS= QGraphicsItem::UserType + 59, + GIT_FES= QGraphicsItem::UserType + 60, + GIT_DTEDS= QGraphicsItem::UserType + 61, + GIT_PI= QGraphicsItem::UserType + 62, + GIT_LA= QGraphicsItem::UserType + 63, + GIT_cableTer= QGraphicsItem::UserType + 64, + GIT_cableEnd= QGraphicsItem::UserType + 65, + GIT_2wTransformer= QGraphicsItem::UserType + 66, + GIT_3wTransformer= QGraphicsItem::UserType + 67, + GIT_node= QGraphicsItem::UserType + 79, + GIT_bay= QGraphicsItem::UserType + 80, //间隔 + //====================================== + GIT_baseNode = QGraphicsItem::UserType + 199, + GIT_baseBus = QGraphicsItem::UserType + 200, + GIT_baseLine = QGraphicsItem::UserType + 201, + GIT_baseBreaker = QGraphicsItem::UserType + 202, + GIT_baseCT = QGraphicsItem::UserType + 203, + GIT_basePT = QGraphicsItem::UserType + 204, + GIT_baseDS = QGraphicsItem::UserType + 205, //隔离开关 + GIT_baseES = QGraphicsItem::UserType + 206, //接地开关 + GIT_baseFES = QGraphicsItem::UserType + 207, //快速接地 + GIT_baseDTEDS = QGraphicsItem::UserType + 208, //双掷接地隔离开关 + GIT_basePI = QGraphicsItem::UserType + 209, //带电指示器 + GIT_baseLightningArrester = QGraphicsItem::UserType + 210, //避雷器 + GIT_baseCableTer = QGraphicsItem::UserType + 211, //电缆出线套筒 + GIT_baseCableEnd = QGraphicsItem::UserType + 212, + GIT_base2wTransformer = QGraphicsItem::UserType + 213, //两绕阻变压器 + GIT_base3wTransformer = QGraphicsItem::UserType + 214, //三绕组变压器 +}; + +enum AttributeField //元模属性字段对照 +{ + Id = Qt::UserRole + 1, + Attribute = Qt::UserRole + 2, + AttributeName = Qt::UserRole + 3, + DataType = Qt::UserRole + 4, + LengthPrecision = Qt::UserRole + 5, + Scale = Qt::UserRole + 6, + IsNotNull = Qt::UserRole + 7, + DefaultValue = Qt::UserRole + 8, + ValueRange = Qt::UserRole + 9, + IsVisible = Qt::UserRole + 10 +}; + +// 抽象项类型 +enum AbstractItemType { + AIT_motor = 1, + AIT_bus +}; + +// 模型功能类型 +enum class ModelFunctionType { + ProjectModel = 0, + BaseModel, + EditorModel, + BlockEditorModel, + RuntimeModel +}; + +// 组态图模式 +enum DiagramMode { + DM_edit = 0, + DM_run, + DM_academic, + DM_baseModel +}; + +//端口类型 +enum PortState +{ + P_const = 0, //固定端口 + p_movable, //移动端口 +}; + +// 项目状态 +enum ProjectState { + Error = -1, // 错误 + NotExist = 0, // 不存在 + Exist, // 已存在 + Changed // 已改变 +}; + +// 表单项状态 +enum TableItemState { + TS_Create = 1, // 创建 + TS_Select = 2, // 选择 + TS_Edit = 4 // 编辑 +}; + +// 警告提示信息 +enum class AlertInfo { + Success = 1, // 成功 + Fail = 2, // 失败 + Exist = 4 // 已存在 +}; + +// 选择对话框类型 +enum SelectorDialogType { + ST_MetaModel = 0, // 元模对话框 + ST_ComponentType // 元件选择 +}; + +// 表格代理内容 +enum TableDelegateContent { + TD_ProjectModel = 0, // 工程模 + TD_MetaModel, // 基模 + TD_ComponentType // 元件类型 +}; + +// 变种图标 +enum VariantIcon { + VI_Thumbnail = 0, // 缩略图 + VI_Normal_1, // 常规1 + VI_Normal_2, // 常规2 + VI_Normal_3, // 常规3 + VI_Normal_4, // 常规4 + VI_Normal_5 // 常规5 +}; + +// 组件类型枚举 +enum class ComponentCategory { + ElectricalEquipment = 0, // 电气设备 + ConnectionRelation = 1 // 连接关系 +}; + +enum class ComponentType { + Bus = 1, // 母线 + AsynchronousMotor = 2, // 异步电动机 + CircuitBreaker = 3, // 断路器 + Cable = 4, // 电缆 + CurrentTransformer = 5, // 电流互感器 + VoltageTransformer = 6, // 电压互感器 + Disconnector = 7, // 隔离开关 + EarthingSwitch = 8, // 接地开关 + FastEarthingSwitch = 9, // 快速接地开关 + DoubleThrowEarthingSwitch = 10, // 双掷接地隔离开关 + VoltageIndicator = 11, // 带电指示器 + LightningArrester = 12, // 避雷器 + CableTerminal = 13, // 电缆出线套筒 + CableEnd = 14 // 电缆端 +}; + +extern const QMap linkType; +//类型转换 +extern const QMap typeToProGraphic; +#endif diff --git a/common/frontend/graphics_items.h b/common/frontend/graphics_items.h new file mode 100644 index 0000000..5a9a7a1 --- /dev/null +++ b/common/frontend/graphics_items.h @@ -0,0 +1,28 @@ +#ifndef GRAPHICS_ITEMS_H +#define GRAPHICS_ITEMS_H +/*********图元********/ +#include + +// 句柄标签 +enum HandleTag { + H_none = 0, + H_leftTop, + H_top, + H_rightTop, + H_right, + H_rightBottom, + H_bottom, + H_leftBottom, + H_left, + H_rotate_leftTop, + H_rotate_rightTop, + H_rotate_rightBottom, + H_rotate_leftBottom, + H_edit, + H_textCaption = 40, // 标题文本 + H_textCurrent, // 电流 + H_textVoltage, // 电压 + H_connect = 50 // 连接操作点 +}; + +#endif diff --git a/common/frontend/monitor_item.h b/common/frontend/monitor_item.h new file mode 100644 index 0000000..7bb4095 --- /dev/null +++ b/common/frontend/monitor_item.h @@ -0,0 +1,272 @@ +#ifndef MONITOR_ITEM_H +#define MONITOR_ITEM_H +/*********人机界面********/ +#include +#include +#include + +enum class MonitorItemState { //监控item的状态 + Normal = 0, //正常 + Closing, //合闸 + Opening, //分闸 + AccidentTrip, //事故跳闸 + StatusFault, //状态故障 + Energized, //带电 + DeEnergized, //失电 + Grounding, //接地 + Running, //运行 + ShutDown, //停运 + Alarm, //告警 + LineBreak, //断线 + MeasureMentOutLimit //量测越限 +}; + +struct MonitorItemAttributeInfo //单个监控item属性 +{ + QString sGroup; //所属组别 + QString sTag; //索引名 + QString sName; //显示名 + int nConnectType = 0; //关联数据类别 0字段 1量测 + QString sConnectPara; //查询参数(参数服务使用) + int nShowType = 0; //显示类别 0字符 1图表 + bool bShowDiagram = false; //显示到组态中 + int nGraphType = 0; //图表类型 0折线1柱状 + QString sTimeRange; //时间范围(分) + QMap mapValue; //属性值 + bool bSelected = false; + + // 转换为JSON对象 + QJsonObject toJson() const { + QJsonObject obj; + obj["sGroup"] = sGroup; + obj["sTag"] = sTag; + obj["sName"] = sName; + obj["nConnectType"] = nConnectType; + obj["sConnectPara"] = sConnectPara; + obj["nShowType"] = nShowType; + obj["bShowDiagram"] = bShowDiagram; + obj["nGraphType"] = nGraphType; + obj["sTimeRange"] = sTimeRange; + obj["sValue"] = mapToJson(mapValue); + obj["bSelected"] = bSelected; + return obj; + } + + // 从JSON对象解析 + void fromJson(const QJsonObject& json) { + sGroup = json["sGroup"].toString(); + sTag = json["sTag"].toString(); + sName = json["sName"].toString(); + nConnectType = json["nConnectType"].toInt(); + sConnectPara = json["sConnectPara"].toString(); + nShowType = json["nShowType"].toInt(); + bShowDiagram = json["bShowDiagram"].toBool(); + nGraphType = json["nGraphType"].toInt(); + sTimeRange = json["sTimeRange"].toString(); + mapValue = jsonToMap(json["sValue"].toObject()); + bSelected = json["bSelected"].toBool(); + } + + // 检查有效性 + bool isValid() const { + return !sTag.isEmpty() && !sName.isEmpty(); + } + + // 重载相等运算符 + bool operator==(const MonitorItemAttributeInfo& other) const { + return sTag == other.sTag && + sName == other.sName && + sGroup == other.sGroup; + } + + QJsonObject mapToJson(const QMap& map) const{ + QJsonObject jsonObj; + + for (auto it = map.constBegin(); it != map.constEnd(); ++it) { + // 将quint64的key转换为QString + QString key = QString::number(it.key()); + jsonObj[key] = it.value(); + } + + return jsonObj; + } + + // 从JSON转换回来 + QMap jsonToMap(const QJsonObject& jsonObj) const{ + QMap map; + + for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it) { + bool ok; + quint64 key = it.key().toULongLong(&ok); + if (ok) { + double value = it.value().toDouble(); + map[key] = value; + } + } + + return map; + } +}; + +struct MonitorItemTypeStruct //监控设备类型 +{ + QString sTag; + QString sName; + + bool isValid() const { + return !sTag.isEmpty() && !sName.isEmpty(); + } + + bool operator==(const MonitorItemTypeStruct& other) const { + return sTag == other.sTag && sName == other.sName; + } + + bool operator<(const MonitorItemTypeStruct& other) const { + return sTag < other.sTag || (sTag == other.sTag && sName < other.sName); + } + + // 转换为JSON对象 - 成员函数 + QJsonObject toJson() const { + QJsonObject obj; + obj["sTag"] = sTag; + obj["sName"] = sName; + return obj; + } + + // 从JSON对象解析 - 成员函数 + void fromJson(const QJsonObject& json) { + sTag = json["sTag"].toString(); + sName = json["sName"].toString(); + } +}; + +struct MonitorItemStateStruct //监控设备状态 +{ + QString sState; + MonitorItemState eState; + + bool isValid() const { + return !sState.isEmpty(); + } + + bool operator==(const MonitorItemStateStruct& other) const { + return sState == other.sState && eState == other.eState; + } + + bool operator<(const MonitorItemStateStruct& other) const { + return sState < other.sState || (sState == other.sState && eState < other.eState); + } + + // 转换为JSON对象 - 成员函数 + QJsonObject toJson() const { + QJsonObject obj; + obj["sState"] = sState; + obj["eState"] = static_cast(eState); + return obj; + } + + // 从JSON对象解析 - 成员函数 + void fromJson(const QJsonObject& json) { + sState = json["sState"].toString(); + eState = static_cast(json["eState"].toInt()); + } +}; + +struct ModelTypeInfo{ //类型临时信息 + int nType; + QString sType; + QString sName; + QString sMeta; //该类型元模名 + QString sModel; //该类型工程模名 +}; + +struct MonitorPageInfo //运行时page +{ + int id = -1; + QUuid uid; + QString tag; + QString name; + QString parent; + QJsonObject context; + QString ts; +}; + +struct HMIPageInfo //人机界面组态page +{ + int id = -1; + QUuid uid; + QString tag; + QString name; + QJsonObject context; + QString ts; +}; + +struct HMIImageInfo //人机界面中的图片资源 +{ + int id = -1; + int baseType; + QString imageName; + QByteArray hash256; + QByteArray svgData; +}; + +struct HMIImageRef //人机界面中的图片引用 +{ + int id = -1; + QUuid hmiId; + QString model; + QByteArray hash256; + int slot; + + bool operator==(const HMIImageRef& other) const + { + return hmiId == other.hmiId && + model == other.model && + hash256 == other.hash256 && + slot == other.slot; + } +}; + +struct MonitorItemDisplayInfo //监控模式显示信息 +{ + int nItemType; //设备类型 + QString sStateName; //状态名 + bool bEnable = false; + QString sColor; + QByteArray bytPicture; //存储二进制数据 + int nWidth; + int nHeight; + QString sMeta; //元模型名 + QString sModel; //工程模名 + + QJsonObject toJson() const { + QJsonObject obj; + obj["nItemType"] = nItemType; + obj["sStateName"] = sStateName; + obj["bEnable"] = bEnable; + obj["sColor"] = sColor; + obj["bytPicture"] = QString(bytPicture.toBase64()); + obj["nWidth"] = nWidth; + obj["nHeight"] = nHeight; + obj["sMeta"] = sMeta; + obj["sModel"] = sModel; + return obj; + } + + // 从JSON对象解析 - 成员函数 + void fromJson(const QJsonObject& json) { + nItemType = json["nItemType"].toInt(); + sStateName = json["sStateName"].toString(); + bEnable = json["bEnable"].toBool(); + sColor = json["sColor"].toString(); + + QString pictureBase64 = json["bytPicture"].toString(); + bytPicture = QByteArray::fromBase64(pictureBase64.toUtf8()); + + nWidth = json["nWidth"].toInt(); + nHeight = json["nHeight"].toInt(); + sMeta = json["sMeta"].toString(); + sModel = json["sModel"].toString(); + } +}; +#endif diff --git a/common/include/baseProperty.h b/common/include/baseProperty.h new file mode 100644 index 0000000..695be24 --- /dev/null +++ b/common/include/baseProperty.h @@ -0,0 +1,224 @@ +#ifndef BASEPROPERTY_H +#define BASEPROPERTY_H + +#include +#include +//#include "global.h" +#include "common/backend/project_model.h" +#include "common/core_model/topology.h" + +class AbstractProperty:public QObject //抽象属性类 +{ + Q_OBJECT +public: + AbstractProperty(QObject* parent); + virtual ~AbstractProperty(); + + virtual void setUuid(QUuid id) {uUid = id;} + virtual QUuid uuid() const {return uUid;} + virtual void setTag(QString s){sTag = s;} + virtual QString tag() const {return sTag;} + virtual void setName(QString s){sName = s;} + virtual QString name() const {return sName;} + virtual void setContext(QJsonObject j){jContext = j;} + virtual QJsonObject context() const {return jContext;} + virtual void setSubList(QList> lst) {subList = lst;} + virtual QList>& getSubList() {return subList;} + virtual void setVoltageLevel(double d){dVoltageLevel = d;} + virtual double getVoltageLevel() const {return dVoltageLevel;} + + virtual QJsonArray saveSubToJsonArr(); +protected: + QUuid uUid; + QString sTag; + QString sName; + QJsonObject jContext; //存放port信息 + QList> subList; //可能存在的子对象(不用来拓朴计算) <类型,uid> //类型0:设备 1:间隔 + double dVoltageLevel; //所属电压等级 +}; + +class BayProperty:public AbstractProperty //间隔属性(待扩展) +{ + Q_OBJECT +public: + BayProperty(QObject* parent = nullptr) + :AbstractProperty(parent){ + dVoltage = 0.0; + dFla = 0.0; //电流 + dCapacity = 0.0; //容量 + sType = "normal"; + } + virtual ~BayProperty(){}; +public: + virtual void setType(QString s) {sType = s;} + virtual QString getType(){return sType;} + virtual void setLstComponent(QList lst) {lstComponent = lst;} + virtual QList& getLstComponent() {return lstComponent;} + virtual void setVoltage(double d) {dVoltage = d;} + virtual double getVoltage() {return dVoltage;} + virtual void setFla(double d) {dFla = d;} + virtual double getFla() {return dFla;} + virtual void setCapacity(double d) {dCapacity = d;} + virtual double getCapacity() {return dCapacity;} + virtual void setInService(bool b) {bInService = b;} + virtual bool getInService() {return bInService;} + virtual void setLstFrom(QList lst) {lstFrom = lst;} + virtual QList& getLstFrom() {return lstFrom;} + virtual void setLstTo(QList lst) {lstTo = lst;} + virtual QList& getLstTo() {return lstTo;} + virtual void setLstProtect(QList lst) {lstProtect = lst;} + virtual QList& getLstProtect() {return lstProtect;} + virtual void setLstFaultRecord(QList lst) {lstFaultRecord = lst;} + virtual QList& getLstFaultRecord() {return lstFaultRecord;} + virtual void setLstDynSense(QList lst) {lstDynSense = lst;} + virtual QList& getLstDynSense() {return lstDynSense;} + virtual void setLstStatus(QList lst) {lstStatus = lst;} + virtual QList& getLstStatus() {return lstStatus;} + virtual void setLstInstruct(QList lst) {lstInstruct = lst;} + virtual QList& getLstInstruct() {return lstInstruct;} + virtual void setLstEtc(QList lst) {lstEtc = lst;} + virtual QList& getLstEtc() {return lstEtc;} + void setMeasurement(QMap map) {mMeasurement = map;} + auto getMeasurement() {return mMeasurement;} +protected: + QString sType; + QList lstComponent; //包含的设备 + double dVoltage; //电压 + double dFla; //电流 + double dCapacity; //容量 + bool bInService; //服役状态 + QList lstFrom; //联结 从 + QList lstTo; //联结到 + + QList lstProtect; //综合保护 + QList lstFaultRecord; //故障录播 + QList lstDynSense; //动态感知 + QList lstStatus; //状态检测 + QList lstInstruct; //监控 + QList lstEtc; //其他设备 + QMap mMeasurement; //量测 +}; + +class ModelProperty:public AbstractProperty //模型基类 +{ + Q_OBJECT +public: + ModelProperty(QObject* parent); + virtual ~ModelProperty(); + virtual void setType(int n) {nType = n;} //设置实际类型 + virtual int type() const {return nType;} + virtual void setGraphicsType(int n) {nGraphicsType = n;} //设置显示类型 + virtual int graphicsType() const {return nGraphicsType;} + virtual void setModelName(QString sName) {sModelName = sName;} + virtual QString modelName() const {return sModelName;} + virtual void setMetaModelName(QString sName) {sMetaName = sName;} + virtual QString metaModelName() const {return sMetaName;} + virtual void notifyUpdate(){emit updateData();} + virtual void setBay(QString s){sBay = s;} + virtual QString getBay(){return sBay;} + + void setPrepareDelete(bool b) {_prepareDelete = b;} + bool prepareDelete() const {return _prepareDelete;} + void setDataChanged(bool b) {_dataChanged = b;} //数据变换标签 + bool dataChanged() const {return _dataChanged;} + void setConnection(Connection con){m_connectState = con;} //保留,用以获取当前图中的连接 + Connection getConnection() const {return m_connectState;} +signals: + void updateData(); //通知数据拥有者更新 +protected: + Connection m_connectState; + int nType; //设备类型 + int nGraphicsType; + QString sModelName; //模型名 + QString sMetaName; //元模型名 + QString sBay; //所属间隔 + + bool _dataChanged; //数据状态,为真则写入库 + bool _prepareDelete; //状态,为真准备删除 +}; + +class DiagramEditorItemProperty:public ModelProperty //基模编辑中预览元件的属性 +{ +public: + DiagramEditorItemProperty(QObject* parent); + virtual ~DiagramEditorItemProperty(); + void setBlock(const QString& s) {sBlock = s;} + QString getBlock(){return sBlock;} +private: + QString sBlock; //所属的block(跨间隔连线等可能无此值) +}; + +class BaseProperty; +class BaseModelProperty:public ModelProperty //图像基模属性 +{ + Q_OBJECT +public: + BaseModelProperty(QObject* parent); + virtual ~BaseModelProperty(); + + virtual void addProData(QString sPage,BaseProperty* pData) {_generatedData.insert(sPage,pData);} + virtual PropertyModel& getModelProperty() {return pm;} + virtual void setModelProperty(PropertyModel pro) {pm = pro;} +private: + QMap _generatedData; //该数据生成过的工程模数据 + PropertyModel pm; //工程模的选择状态 +}; + +class BaseProperty:public ModelProperty //图像工程模模属性类,存放电路元件属性 +{ + Q_OBJECT +public: + BaseProperty(QObject* parent); + virtual ~BaseProperty(); + + void setPath(QString s){sPath = s;} + QString path() const {return sPath;} + void setDescription(QString s) {sDescription = s;} + QString description() const {return sDescription;} + void setInService(bool b) {bInService = b;} + bool inService() {return bInService;} + void setState(int n) {nState = n;} + int state() const {return nState;} + void setStatus(int n) {nStatus = n;} + int status() const {return nStatus;} + void setConnectedBus(QJsonObject j) {jConnectedBus = j;} + QJsonObject connectedBus() const {return jConnectedBus;} + void setLabel(QJsonObject j){jLabel = j;} + QJsonObject label() const {return jLabel;} + void setGrid(const QString& s) {sGrid = s;} + QString grid() const {return sGrid;} + void setZone(const QString& s) {sZone = s;} + QString zone() const {return sZone;} + void setStation(const QString& s) {sStation = s;} + QString station() const {return sStation;} + void setSourceItemId(const QString& s) {sSourceItemId = s;} + QString getSourceItemId() {return sSourceItemId;} + void setMeasurement(QMap map) {mMeasurement = map;} + auto getMeasurement() {return mMeasurement;} +protected: + QString sPath; + QString sDescription; + QString sGrid; + QString sZone; + QString sStation; + bool bInService; + int nState; + int nStatus; + QJsonObject jConnectedBus; + QJsonObject jLabel; + QString sSourceItemId; //被哪个对象生成 + QMap mMeasurement; //量测 +}; + +typedef QMap VariableMap; //属性名,值 + +class VariableProperty:public BaseProperty //收到的变量数据 +{ + Q_OBJECT +public: + VariableProperty(QObject* parent = nullptr); + ~VariableProperty(); + + ModelDataInfo& getPropertyValue() const; +}; +#endif // DATABASE_H diff --git a/common/include/compiler.hpp b/common/include/compiler.hpp new file mode 100644 index 0000000..fb90a53 --- /dev/null +++ b/common/include/compiler.hpp @@ -0,0 +1,40 @@ +#pragma once + +#if defined(__MINGW32__) || defined(__MINGW64__) +#define DIAGRAM_DESIGNER_COMPILER "MinGW" +#define DIAGRAM_DESIGNER_COMPILER_MINGW +#elif defined(__clang__) +#define DIAGRAM_DESIGNER_COMPILER "Clang" +#define DIAGRAM_DESIGNER_COMPILER_CLANG +#elif defined(_MSC_VER) +#define DIAGRAM_DESIGNER_COMPILER "Microsoft Visual C++" +#define DIAGRAM_DESIGNER_COMPILER_MICROSOFT +#elif defined(__GNUC__) +#define DIAGRAM_DESIGNER_COMPILER "GNU" +#define DIAGRAM_DESIGNER_COMPILER_GNU +#define DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_MAJOR __GNUC__ +#define DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_MINOR __GNUC_MINOR__ +#define DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_PATCH __GNUC_PATCHLEVEL__ +#elif defined(__BORLANDC__) +#define DIAGRAM_DESIGNER_COMPILER "Borland C++ Builder" +#define DIAGRAM_DESIGNER_COMPILER_BORLAND +#elif defined(__CODEGEARC__) +#define DIAGRAM_DESIGNER_COMPILER "CodeGear C++ Builder" +#define DIAGRAM_DESIGNER_COMPILER_CODEGEAR +#elif defined(__INTEL_COMPILER) || defined(__ICL) +#define DIAGRAM_DESIGNER_COMPILER "Intel C++" +#define DIAGRAM_DESIGNER_COMPILER_INTEL +#elif defined(__xlC__) || defined(__IBMCPP__) +#define DIAGRAM_DESIGNER_COMPILER "IBM XL C++" +#define DIAGRAM_DESIGNER_COMPILER_IBM +#elif defined(__HP_aCC) +#define DIAGRAM_DESIGNER_COMPILER "HP aC++" +#define DIAGRAM_DESIGNER_COMPILER_HP +#elif defined(__WATCOMC__) +#define DIAGRAM_DESIGNER_COMPILER "Watcom C++" +#define DIAGRAM_DESIGNER_COMPILER_WATCOM +#endif + +#ifndef DIAGRAM_DESIGNER_COMPILER +#error "Current compiler is not supported." +#endif diff --git a/common/include/export.hpp b/common/include/export.hpp new file mode 100644 index 0000000..16b138a --- /dev/null +++ b/common/include/export.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "compiler.hpp" +#include "operatingSystem.hpp" + +#ifdef DIAGRAM_DESIGNER_PLATFORM_WINDOWS +#define DIAGRAM_DESIGNER_EXPORT __declspec(dllexport) +#define DIAGRAM_DESIGNER_IMPORT __declspec(dllimport) +#define DIAGRAM_DESIGNER_LOCAL +#elif DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_MAJOR >= 4 || defined(DIAGRAM_DESIGNER_COMPILER_CLANG) +#define DIAGRAM_DESIGNER_EXPORT __attribute__((visibility("default"))) +#define DIAGRAM_DESIGNER_IMPORT __attribute__((visibility("default"))) +#define DIAGRAM_DESIGNER_LOCAL __attribute__((visibility("hidden"))) +#else +#define DIAGRAM_DESIGNER_EXPORT +#define DIAGRAM_DESIGNER_IMPORT +#define DIAGRAM_DESIGNER_LOCAL +#endif + +#ifdef __cplusplus +#define DIAGRAM_DESIGNER_DEMANGLED extern "C" +#else +#define DIAGRAM_DESIGNER_DEMANGLED +#endif + +#if defined(DIAGRAM_DESIGNER_SHARED) && !defined(DIAGRAM_DESIGNER_STATIC) +#ifdef DIAGRAM_DESIGNER_EXPORTS +#define DIAGRAM_DESIGNER_PUBLIC DIAGRAM_DESIGNER_EXPORT +#else +#define DIAGRAM_DESIGNER_PUBLIC DIAGRAM_DESIGNER_IMPORT +#endif +#define DIAGRAM_DESIGNER_PRIVATE DIAGRAM_DESIGNER_LOCAL +#elif !defined(DIAGRAM_DESIGNER_SHARED) && defined(DIAGRAM_DESIGNER_STATIC) +#define DIAGRAM_DESIGNER_PUBLIC +#define DIAGRAM_DESIGNER_PRIVATE +#elif defined(DIAGRAM_DESIGNER_SHARED) && defined(DIAGRAM_DESIGNER_STATIC) +#ifdef DIAGRAM_DESIGNER_EXPORTS +#error "Cannot build as shared and static simultaneously." +#else +#error "Cannot link against shared and static simultaneously." +#endif +#else +#ifdef DIAGRAM_DESIGNER_EXPORTS +#error "Choose whether to build as shared or static." +#else +#error "Choose whether to link against shared or static." +#endif +#endif diff --git a/common/include/extraPropertyManager.h b/common/include/extraPropertyManager.h new file mode 100644 index 0000000..b0ce816 --- /dev/null +++ b/common/include/extraPropertyManager.h @@ -0,0 +1,40 @@ +// extraPropertyManager.h +#pragma once +#include +#include +#include +//#include "global.h" +#include "common/core_model/topology.h" + +/** + * 属性层级信息管理 + * */ + +class ExtraPropertyManager : public QObject { + Q_OBJECT + +public: + explicit ExtraPropertyManager(QObject* parent = nullptr); + + // 加载所有属性 + bool loadAll(); + void initial(); + // 查询方法 + QVector getByFilter(const QVariantMap& filter,const QString& filterType) const; //filterType:name,tag + ExtraProperty getByCode(const QString& code) const; + QMap geAlltProperty() {return m_props;} + + // CRUD操作 + int add(const ExtraProperty& prop); + bool update(const ExtraProperty& prop); + bool remove(int id); + + // 层级选项 + QStringList getGrids() const; + QStringList getZones(const QString& grid = "") const; + QStringList getStations(const QString& grid = "", const QString& zone = "") const; +private: + QString removeSuffix(const QString& str); //移除最后一个下划线后的内容 (处理各种tag后缀) +private: + QMap m_props; // 内存缓存 +}; diff --git a/common/include/httpInterface.h b/common/include/httpInterface.h new file mode 100644 index 0000000..86f79c1 --- /dev/null +++ b/common/include/httpInterface.h @@ -0,0 +1,33 @@ +#ifndef HTTPINTERFACE_H +#define HTTPINTERFACE_H + +#include +#include + +class QNetworkAccessManager; +class QNetworkReply; + +class HttpInterface:public QObject +{ + Q_OBJECT +public: + HttpInterface(QObject* parent = nullptr); + ~HttpInterface(); + //static HttpInterface* GetInstance(); + + void getPointData(QString type,QString station = QString("0"),QString component = QString("0"),QString begin = "",QString end = ""); +signals: + void sendPointData(QString type,QMap map); +public slots: + void replyFinished(QNetworkReply *reply); +private: + void initial(); + void readXML(); + static HttpInterface* instance; + QString m_sFileName; + QString _Host; + QString _Port; + QString _Interface; + QNetworkAccessManager* _manager; +}; +#endif // DATABASE_H diff --git a/common/include/operatingSystem.hpp b/common/include/operatingSystem.hpp new file mode 100644 index 0000000..a653fee --- /dev/null +++ b/common/include/operatingSystem.hpp @@ -0,0 +1,49 @@ +#pragma once + +#if defined(__CYGWIN__) || defined(__CYGWIN32__) +#define DIAGRAM_DESIGNER_PLATFORM "Cygwin" +#define DIAGRAM_DESIGNER_PLATFORM_CYGWIN +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#define DIAGRAM_DESIGNER_PLATFORM_WINDOWS +#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) \ + || defined(__TOS_WIN__) || defined(__WINDOWS__) +#define DIAGRAM_DESIGNER_PLATFORM "Windows" +#define DIAGRAM_DESIGNER_PLATFORM_WINDOWS +#elif defined(macintosh) || defined(Macintosh) || defined(__TOS_MACOS__) \ + || (defined(__APPLE__) && defined(__MACH__)) +#define DIAGRAM_DESIGNER_PLATFORM "Mac" +#define DIAGRAM_DESIGNER_PLATFORM_MAC +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(linux) || defined(__linux) || defined(__linux__) || defined(__TOS_LINUX__) +#define DIAGRAM_DESIGNER_PLATFORM "Linux" +#define DIAGRAM_DESIGNER_PLATFORM_LINUX +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) \ + || defined(__DragonFly__) +#define DIAGRAM_DESIGNER_PLATFORM "BSD" +#define DIAGRAM_DESIGNER_PLATFORM_BSD +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(sun) || defined(__sun) +#define DIAGRAM_DESIGNER_PLATFORM "Solaris" +#define DIAGRAM_DESIGNER_PLATFORM_SOLARIS +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(_AIX) || defined(__TOS_AIX__) +#define DIAGRAM_DESIGNER_PLATFORM "AIX" +#define DIAGRAM_DESIGNER_PLATFORM_AIX +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(hpux) || defined(_hpux) || defined(__hpux) +#define DIAGRAM_DESIGNER_PLATFORM "HPUX" +#define DIAGRAM_DESIGNER_PLATFORM_HPUX +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(__QNX__) +#define DIAGRAM_DESIGNER_PLATFORM "QNX" +#define DIAGRAM_DESIGNER_PLATFORM_QNX +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#elif defined(unix) || defined(__unix) || defined(__unix__) +#define DIAGRAM_DESIGNER_PLATFORM "Unix" +#define DIAGRAM_DESIGNER_PLATFORM_UNIX +#endif + +#ifndef DIAGRAM_DESIGNER_PLATFORM +#error "Current platform is not supported." +#endif diff --git a/common/include/structDataSource.h b/common/include/structDataSource.h new file mode 100644 index 0000000..f8df64d --- /dev/null +++ b/common/include/structDataSource.h @@ -0,0 +1,84 @@ +#ifndef STRUCTDATASOURCE_H +#define STRUCTDATASOURCE_H +/** + * *******结构化数据源接口****** + **/ +#include +struct ExtraProperty; +struct PropertyStateInfo; +struct MeasurementInfo; +struct ModelDataInfo; + +class StructDataSource : public QObject +{ + Q_OBJECT + +public: + StructDataSource(QObject* parent = nullptr); + + // 根据code获取属性 + ExtraProperty* getPropertyByCode(const QString& code); + + // 根据tag获取属性 + ExtraProperty* getPropertyByTag(const QString& tag); + + // 根据group获取属性列表 + QVector getPropertiesByGroup(const QString& groupTag, const QString& modelName = ""); + + // 根据sourceType获取属性列表 + QVector getPropertiesBySourceType(const QString& sourceType); + + // 添加或更新属性 + bool addOrUpdateProperty(const ExtraProperty& prop); + + // 更新单个属性 + bool updateProperty(const ExtraProperty& updatedProp); + + // 更新connect_para + bool updateConnectPara(const QString& code, const QString& newConnectPara); + + // 批量更新 + void updateProperties(const QVector& updatedProps); + + // 删除属性 + bool removeProperty(const QString& code); + + // 批量删除 + int removeProperties(const QVector& codes); + + // 保存到文件 + void saveAll(); + + // 加载数据 + void loadExtrapro(QMap vec); + + void loadPropertyData(QMap map); + + void loadMeasurementData(QMap map); + + // 获取所有属性的code列表 + QStringList getAllCodes() const; + + // 获取所有属性 + QVector getAllProperties() const; + + // 获取property数据 + PropertyStateInfo* getPropertyData(const ExtraProperty& prop); + + // 获取measurement数据 + MeasurementInfo* getMeasurementData(const ExtraProperty& prop); + + // 验证数据 + bool validateProperty(const ExtraProperty& prop); + +signals: + void propertyUpdated(const ExtraProperty& prop, bool isNew); + void propertyRemoved(const ExtraProperty& prop); + void dataChanged(); +public: + QMap allProperties; +private: + QMap m_propertyData; //参量 + QMap m_measurementData; //量测 +}; +#endif diff --git a/common/include/tools.h b/common/include/tools.h new file mode 100644 index 0000000..8c56442 --- /dev/null +++ b/common/include/tools.h @@ -0,0 +1,84 @@ +#ifndef TOOLS_H +#define TOOLS_H + +#include +#include + +template //双向map工具类,实现key->value,value->key的映射 +class BiDirectionalMap { +public: + // 插入键值对,确保双向唯一性 + void insert(const Key& key, const Value& value) { + // 删除旧键和旧值的关联(如果存在) + if (m_keyToValue.contains(key)) { + Value oldValue = m_keyToValue[key]; + m_valueToKey.remove(oldValue); + } + if (m_valueToKey.contains(value)) { + Key oldKey = m_valueToKey[value]; + m_keyToValue.remove(oldKey); + } + // 插入新键值对 + m_keyToValue[key] = value; + m_valueToKey[value] = key; + } + + // 根据键获取值 + Value value(const Key& key) const { + return m_keyToValue.value(key); + } + + // 根据值获取键 + Key key(const Value& value) const { + return m_valueToKey.value(value); + } + + // 检查键是否存在 + bool containsKey(const Key& key) const { + return m_keyToValue.contains(key); + } + + // 检查值是否存在 + bool containsValue(const Value& value) const { + return m_valueToKey.contains(value); + } + + // 通过键删除项 + void removeByKey(const Key& key) { + if (m_keyToValue.contains(key)) { + Value value = m_keyToValue[key]; + m_keyToValue.remove(key); + m_valueToKey.remove(value); + } + } + + // 通过值删除项 + void removeByValue(const Value& value) { + if (m_valueToKey.contains(value)) { + Key key = m_valueToKey[value]; + m_valueToKey.remove(value); + m_keyToValue.remove(key); + } + } + +private: + QHash m_keyToValue; // 键 → 值 + QHash m_valueToKey; // 值 → 键 +}; + +int getLevel(QStandardItem *item); +QStandardItem* findStandardItemAtLevel(QStandardItemModel *model, int targetLevel, + const QString &targetText, + QStandardItem *parent = nullptr, + int currentLevel = 0); +QList getAllChildren(QStandardItem* parentItem); +QModelIndex findIndex(const QAbstractItemModel* model, const QVariant& target, + int role = Qt::DisplayRole, const QModelIndex& parent = QModelIndex()); + +// 使用 clone() 方法深拷贝item +QStandardItem* deepCloneItem(const QStandardItem* source); + +// 深拷贝整个模型(不复制表头) +QStandardItemModel* deepCloneModel(const QStandardItemModel* source); + +#endif // DATABASE_H diff --git a/common/source/baseProperty.cpp b/common/source/baseProperty.cpp new file mode 100644 index 0000000..dac202b --- /dev/null +++ b/common/source/baseProperty.cpp @@ -0,0 +1,96 @@ +#include "baseProperty.h" +#include "dataManager.h" +#include + + +AbstractProperty::AbstractProperty(QObject* parent) + : QObject(parent) +{ + +} +AbstractProperty::~AbstractProperty() +{ + +} + +QJsonArray AbstractProperty::saveSubToJsonArr() +{ + QJsonArray jsonArray; + + for (const auto& pair : subList) { + QJsonObject itemObject; + itemObject["category"] = pair.first; + itemObject["uuid"] = pair.second.toString(); // 将QUuid转换为字符串 + + jsonArray.append(itemObject); + } + return jsonArray; +} + +/******************************模型基类*******************************/ +ModelProperty::ModelProperty(QObject* parent) + :AbstractProperty(parent) +{ + _dataChanged = false; + _prepareDelete = false; + +} +ModelProperty::~ModelProperty() +{ + +} + +/*****************************组态编辑预览item*********************************/ +DiagramEditorItemProperty::DiagramEditorItemProperty(QObject* parent) + :ModelProperty(parent) +{ + +} +DiagramEditorItemProperty::~DiagramEditorItemProperty() +{ + +} + +/*****************************基模*********************************/ +BaseModelProperty::BaseModelProperty(QObject* parent) + : ModelProperty(parent) +{ + +} +BaseModelProperty::~BaseModelProperty() +{ + +} +/****************************工程模****************************/ + +BaseProperty::BaseProperty(QObject* parent) + : ModelProperty(parent) +{ + nType = 0; //设备类型 + bInService = true; + nState = 1; + nStatus = 1; +} + +BaseProperty::~BaseProperty() +{ + //qDebug()<<"release by "< + +ExtraPropertyManager::ExtraPropertyManager(QObject* parent) : QObject(parent) { +} + +bool ExtraPropertyManager::loadAll() { + m_props.clear(); + int count = 0; + + QList lstPro = DataBase::GetInstance()->getAllExtraProperty(); + for(auto& pro:lstPro){ + pro.bay_tag = removeSuffix(pro.bay_tag); + m_props[pro.code] = pro; + count++; + } + + qInfo() << "加载了" << count << "个属性"; + return true; +} + +void ExtraPropertyManager::initial() +{ + loadAll(); +} + +QVector ExtraPropertyManager::getByFilter(const QVariantMap& filter,const QString& filterType) const { + QVector result; + + for (const ExtraProperty& prop : m_props) { + if (prop.matches(filter,filterType)) { + result.append(prop); + } + } + + return result; +} + +ExtraProperty ExtraPropertyManager::getByCode(const QString& code) const { + return m_props.value(code); +} + +int ExtraPropertyManager::add(const ExtraProperty& prop) { + if (prop.code.isEmpty()) { + qWarning() << "属性编码不能为空"; + return -1; + } + + if (m_props.contains(prop.code)) { + qWarning() << "属性编码已存在:" << prop.code; + return -1; + } + + bool res = DataBase::GetInstance()->insertExtraProperty(prop); + + return res; +} + +QStringList ExtraPropertyManager::getGrids() const { + QSet grids; + for (const ExtraProperty& prop : m_props) { + if (!prop.grid_tag.isEmpty()) { + grids.insert(prop.grid_tag); + } + } + return QStringList(grids.begin(), grids.end()); +} + +QString ExtraPropertyManager::removeSuffix(const QString& str) +{ + int lastUnderscore = str.lastIndexOf('_'); + if (lastUnderscore == -1) return str; // 没有下划线 + + return str.left(lastUnderscore); +} diff --git a/common/source/httpInterface.cpp b/common/source/httpInterface.cpp new file mode 100644 index 0000000..4431982 --- /dev/null +++ b/common/source/httpInterface.cpp @@ -0,0 +1,150 @@ +#include "httpInterface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//HttpInterface* HttpInterface::instance = nullptr; + +HttpInterface::HttpInterface(QObject* parent) + :QObject(parent) +{ + m_sFileName = QString("setting.xml"); + _manager = new QNetworkAccessManager(this); + initial(); +} + +HttpInterface::~HttpInterface() +{ + if(_manager) + _manager->deleteLater(); +} + +void HttpInterface::initial() +{ + readXML(); + connect(_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*))); +} + +void HttpInterface::readXML() +{ + if (m_sFileName.isEmpty()) + return; + + QFile *pFile = new QFile(m_sFileName); + if (!pFile->open(QIODevice::ReadOnly | QFile::Text)) + { + QMessageBox::information(NULL, QString("title"), QString::fromWCharArray(L"配置文件打开错误")); + return; + } + + QXmlStreamReader* m_pReader = new QXmlStreamReader(pFile); + while (!m_pReader->atEnd() && !m_pReader->hasError()) + { + m_pReader->lineNumber(); + QXmlStreamReader::TokenType token = m_pReader->readNext(); + if (token == QXmlStreamReader::StartDocument) + continue; + + //qDebug() << m_pReader->name(); + if (m_pReader->isStartElement()) + { + if(m_pReader->name() == QString("HttpInterface")) + { + QXmlStreamAttributes attributes = m_pReader->attributes(); + _Host = attributes.value("Ip").toString(); + _Port = attributes.value("Port").toString(); + } + else if(m_pReader->name() == QString("GetPointData")) + { + _Interface = m_pReader->readElementText(); + } + } + m_pReader->readNext(); + } + if (m_pReader->hasError()) + { + qDebug() << m_pReader->errorString(); + } + m_pReader->clear(); + delete m_pReader; + m_pReader = NULL; + pFile->close(); + delete pFile; + pFile = NULL; +} + +/*HttpInterface* HttpInterface::GetInstance() +{ + if(instance == nullptr) + { + instance = new HttpInterface(); + } + return instance; +}*/ + +void HttpInterface::getPointData(QString type,QString station,QString component,QString begin,QString end) +{ + QNetworkRequest request; + QString scheme = "http"; + QString requestHeader = scheme + QString("://") + _Host + QString(":") + _Port + QString("/") + _Interface; + QString fullRequest; + if(begin.isEmpty() && end.isEmpty()) + fullRequest = requestHeader + QString("?station=%1&component=%2&point=%3").arg(station,component,type); + else + fullRequest = requestHeader + QString("?station=%1&component=%2&point=%3&begin=%4&end=%5").arg(station,component,type,begin,end); + request.setUrl(QUrl(fullRequest)); + _manager->get(request); +} + +void HttpInterface::replyFinished(QNetworkReply *reply) +{ + QString allinfo = reply->readAll(); + + QJsonParseError err; + QJsonDocument json_recv = QJsonDocument::fromJson(allinfo.toUtf8(),&err);//解析json对象 + if(!json_recv.isNull()) + { + QJsonObject object = json_recv.object(); + if(object.contains("data")) + { + /*QJsonObject dataObj = object["data"].toObject(); + QString type = dataObj["point"].toString(); + + QJsonArray nodesJsonArray = dataObj["values"].toArray(); + + QMap map; + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QString time = node["time"].toString(); + double value = node["value"].toDouble(); + qint64 tval = time.toLongLong(); + map.insert(tval,value); + }*/ + QJsonArray nodesJsonArray = object["data"].toArray(); + + QMap map; + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QString time = node["time"].toString(); + double value = node["value"].toDouble(); + qint64 tval = time.toLongLong(); + map.insert(tval,value); + } + emit sendPointData("v",map); + } + + }else + { + qDebug()<<"json_recv is NULL or is not a object !!"; + } + reply->deleteLater(); //销毁请求对象 +} diff --git a/common/source/structDataSource.cpp b/common/source/structDataSource.cpp new file mode 100644 index 0000000..26a7829 --- /dev/null +++ b/common/source/structDataSource.cpp @@ -0,0 +1,270 @@ +#include "structDataSource.h" +#include "dataBase.h" +//#include "global.h" + +StructDataSource::StructDataSource(QObject* parent) : QObject(parent) { + +} + +// 根据code获取属性 +ExtraProperty* StructDataSource::getPropertyByCode(const QString& code) { + auto it = allProperties.find(code); + if (it != allProperties.end()) { + return &it.value(); + } + return nullptr; +} + +// 根据tag获取属性 +ExtraProperty* StructDataSource::getPropertyByTag(const QString& tag) { + for (auto it = allProperties.begin(); it != allProperties.end(); ++it) { + if (it.value().tag == tag) { + return &it.value(); + } + } + return nullptr; +} + +// 根据group获取属性列表 +QVector StructDataSource::getPropertiesByGroup(const QString& groupTag, const QString& modelName) { + QVector result; + for (auto it = allProperties.begin(); it != allProperties.end(); ++it) { + const ExtraProperty& prop = it.value(); + if (prop.group_tag == groupTag && + (modelName.isEmpty() || prop.sourceConfig.value("modelName").toString() == modelName)) { + result.append(prop); + } + } + return result; +} + +// 根据sourceType获取属性列表 +QVector StructDataSource::getPropertiesBySourceType(const QString& sourceType) { + QVector result; + for (auto it = allProperties.begin(); it != allProperties.end(); ++it) { + if (it.value().sourceType == sourceType) { + result.append(it.value()); + } + } + return result; +} + +// 添加或更新属性 +bool StructDataSource::addOrUpdateProperty(const ExtraProperty& prop) { + if (prop.code.isEmpty()) { + qWarning() << "Property code is empty"; + return false; + } + + bool isNew = !allProperties.contains(prop.code); + allProperties[prop.code] = prop; + + emit propertyUpdated(prop, isNew); + return true; +} + +// 更新单个属性 +bool StructDataSource::updateProperty(const ExtraProperty& updatedProp) { + if (updatedProp.code.isEmpty()) { + qWarning() << "Property code is empty"; + return false; + } + + if (!allProperties.contains(updatedProp.code)) { + qWarning() << "Property not found:" << updatedProp.code; + return false; + } + + allProperties[updatedProp.code] = updatedProp; + emit propertyUpdated(updatedProp, false); + return true; +} + +// 更新connect_para +bool StructDataSource::updateConnectPara(const QString& code, const QString& newConnectPara) { + auto it = allProperties.find(code); + if (it != allProperties.end()) { + it.value().connect_para = newConnectPara; + emit propertyUpdated(it.value(), false); + return true; + } + return false; +} + +// 批量更新 +void StructDataSource::updateProperties(const QVector& updatedProps) { + for (const auto& prop : updatedProps) { + updateProperty(prop); + } +} + +// 删除属性 +bool StructDataSource::removeProperty(const QString& code) { + if (allProperties.contains(code)) { + ExtraProperty removedProp = allProperties[code]; + allProperties.remove(code); + emit propertyRemoved(removedProp); + return true; + } + return false; +} + +// 批量删除 +int StructDataSource::removeProperties(const QVector& codes) { + int count = 0; + for (const QString& code : codes) { + if (removeProperty(code)) { + count++; + } + } + return count; +} + +// 保存到文件 +void StructDataSource::saveAll() { + for(auto& pro:allProperties){ + if(pro.bDataChanged){ //已修改,需要保存 + if(pro.sourceType == "measurement"){ //量测 + auto pMeasure = getMeasurementData(pro); + if(pMeasure){ + QJsonObject objDataSource; + QJsonObject objIoAddress; + if(pMeasure->nSource == 1){ //3611 + objDataSource["type"] = 1; + objIoAddress["station"] = pMeasure->sStation; + objIoAddress["device"] = pMeasure->sDevice; + objIoAddress["channel"] = pMeasure->sChannel; + } + else if(pMeasure->nSource == 2){ //104 + objDataSource["type"] = 2; + objIoAddress["station"] = pMeasure->sStation; + objIoAddress["packet"] = pMeasure->nPacket; + objIoAddress["offset"] = pMeasure->nOffset; + } + objDataSource["io_address"] = objIoAddress; + + QJsonObject objEventPlan; + QJsonObject objCause; + QJsonObject objAction; + QJsonArray arrPara; + objEventPlan["enable"] = pMeasure->bEnable; + if(pMeasure->type == 0){ //遥测 + for(auto iter = pMeasure->mapTE.begin();iter != pMeasure->mapTE.end();++iter){ + objCause[iter.key()] = iter.value(); + } + } + else if(pMeasure->type == 1){ //遥信 + objCause["edge"] = pMeasure->sEdge; + } + objEventPlan["cause"] = objCause; + + objAction["command"] = pMeasure->sCommand; + for(auto ¶:pMeasure->lstParameter){ + arrPara.append(para); + } + objAction["parameters"] = arrPara; + objEventPlan["action"] = objAction; + + QJsonObject objBinding; + if(!pMeasure->sWindType.isEmpty()){ + QJsonObject objWind; + objWind["ratio"] = pMeasure->nRatio; + objWind["polarity"] = pMeasure->nPolarity; + objWind["index"] = pMeasure->nIndex; + objBinding[pMeasure->sWindType] = objWind; + } + + DataBase::GetInstance()->updateMeasurement(pMeasure->name,pMeasure->type,objDataSource,objEventPlan,objBinding,pMeasure->size,pMeasure->componentUuid); + } + } + } + } +} + +// 加载数据 + +// 获取所有属性的code列表 +QStringList StructDataSource::getAllCodes() const { + return allProperties.keys(); +} + +// 获取所有属性 +QVector StructDataSource::getAllProperties() const { + return allProperties.values(); +} + +// 获取property数据 +PropertyStateInfo* StructDataSource::getPropertyData(const ExtraProperty& prop) { + QString modelName = prop.sourceConfig.value("modelName").toString(); + QString groupTag = prop.group_tag; + QUuid componentUuid = prop.component_uuid; + QString propertyTag = prop.tag; + + auto itModel = m_propertyData.find(modelName); + if (itModel == m_propertyData.end()) return nullptr; + + auto itGroup = itModel->groupInfo.find(groupTag); + if (itGroup == itModel->groupInfo.end()) return nullptr; + + auto itComponent = itGroup->mapInfo.find(componentUuid); + if (itComponent == itGroup->mapInfo.end()) return nullptr; + + auto itProperty = itComponent->find(propertyTag); + if (itProperty == itComponent->end()) return nullptr; + + return &itProperty.value(); +} + +// 获取measurement数据 +MeasurementInfo* StructDataSource::getMeasurementData(const ExtraProperty& prop) { + auto it = m_measurementData.find(prop.tag); + if (it != m_measurementData.end()) { + return &it.value(); + } + return nullptr; +} + +// 验证数据 +bool StructDataSource::validateProperty(const ExtraProperty& prop) { + if (prop.code.isEmpty()) { + qWarning() << "Property code is empty"; + return false; + } + + if (prop.name.isEmpty()) { + qWarning() << "Property name is empty:" << prop.code; + return false; + } + + if (prop.tag.isEmpty()) { + qWarning() << "Property tag is empty:" << prop.code; + return false; + } + + if (prop.sourceType.isEmpty()) { + qWarning() << "Property sourceType is empty:" << prop.code; + return false; + } + + if (prop.sourceType != "property" && prop.sourceType != "measurement") { + qWarning() << "Invalid sourceType:" << prop.sourceType << "for property:" << prop.code; + return false; + } + + return true; +} + +void StructDataSource::loadExtrapro(QMap vec) +{ + allProperties = vec; +} + +void StructDataSource::loadPropertyData(QMap map) +{ + m_propertyData = map; +} + +void StructDataSource::loadMeasurementData(QMap map) +{ + m_measurementData = map; +} diff --git a/common/source/tools.cpp b/common/source/tools.cpp new file mode 100644 index 0000000..c732ce5 --- /dev/null +++ b/common/source/tools.cpp @@ -0,0 +1,137 @@ +#include "tools.h" + +int getLevel(QStandardItem *item) { + int level = 0; + QStandardItem *parent = item->parent(); + if(parent) + { + while (parent) { + level++; + parent = parent->parent(); + } + return level; + } + else{ + return -1; + } +} + +QStandardItem* findStandardItemAtLevel(QStandardItemModel *model, int targetLevel, + const QString &targetText, + QStandardItem *parent, + int currentLevel) { + if (!model) return nullptr; + + if (currentLevel == targetLevel) { + if (parent) { + // 在父项的子树中查找 + for (int row = 0; row < parent->rowCount(); ++row) { + QStandardItem *item = parent->child(row, 0); + if (item && item->text() == targetText) { + return item; + } + } + } else { + // 在根层级查找 + for (int row = 0; row < model->rowCount(); ++row) { + QStandardItem *item = model->item(row, 0); + if (item && item->text() == targetText) { + return item; + } + } + } + return nullptr; + } + + if (currentLevel > targetLevel) return nullptr; + + // 递归查找子项 + if (parent) { + for (int row = 0; row < parent->rowCount(); ++row) { + QStandardItem *child = parent->child(row, 0); + QStandardItem *result = findStandardItemAtLevel(model, targetLevel, targetText, child, currentLevel + 1); + if (result) return result; + } + } else { + for (int row = 0; row < model->rowCount(); ++row) { + QStandardItem *item = model->item(row, 0); + QStandardItem *result = findStandardItemAtLevel(model, targetLevel, targetText, item, currentLevel + 1); + if (result) return result; + } + } + + return nullptr; +} + +QList getAllChildren(QStandardItem* parentItem) { + QList children; + + if (!parentItem) return children; + + for (int row = 0; row < parentItem->rowCount(); ++row) { + for (int column = 0; column < parentItem->columnCount(); ++column) { + QStandardItem* childItem = parentItem->child(row, column); + if (childItem) { + children.append(childItem); + } + } + } + + return children; +} + +QModelIndex findIndex(const QAbstractItemModel* model, const QVariant& target, + int role, const QModelIndex& parent) { + for (int row = 0; row < model->rowCount(parent); ++row) { + QModelIndex idx = model->index(row, 0, parent); // 假设查找第0列 + if (model->data(idx, role) == target) { + return idx; // 找到匹配项 + } + // 递归查找子项 + if (model->hasChildren(idx)) { + QModelIndex childIdx = findIndex(model, target, role, idx); + if (childIdx.isValid()) { + return childIdx; + } + } + } + return QModelIndex(); // 未找到 +} + +QStandardItem* deepCloneItem(const QStandardItem* source) { + if (!source) return nullptr; + + // 使用 clone() 方法创建副本 + QStandardItem* clone = source->clone(); + + // 递归复制所有子项 + for (int row = 0; row < source->rowCount(); ++row) { + for (int col = 0; col < source->columnCount(); ++col) { + if (QStandardItem* child = source->child(row, col)) { + clone->setChild(row, col, deepCloneItem(child)); + } + } + } + + return clone; +} + +QStandardItemModel* deepCloneModel(const QStandardItemModel* source) { + if (source == nullptr) return nullptr; + + // 设置相同的行列数 + QStandardItemModel* dest = new QStandardItemModel(source->parent()); + + dest->setRowCount(source->rowCount()); + dest->setColumnCount(source->columnCount()); + + // 复制所有数据项 + for (int row = 0; row < source->rowCount(); ++row) { + for (int col = 0; col < source->columnCount(); ++col) { + if (QStandardItem* item = source->item(row, col)) { + dest->setItem(row, col, deepCloneItem(item)); + } + } + } + return dest; +} diff --git a/diagramCavas/CMakeLists.txt b/diagramCavas/CMakeLists.txt new file mode 100644 index 0000000..5a03eff --- /dev/null +++ b/diagramCavas/CMakeLists.txt @@ -0,0 +1,302 @@ +project(diagramCavas) + +set(DIAGRAMCAVAS_HEADER_FILES + include/baseScene.h + include/designerScene.h + include/designerView.h + include/diagramCavas.h + include/baseDrawingPanel.h + include/monitorPanel.h + include/monitorSideBarDlg.h + include/monitorSelectedItemsDlg.h + include/monitorToolPage.h + include/monitorToolBox.h + include/monitorAttributeDlg.h + include/monitorAttributeGroupDlg.h + include/monitorConfigDlg.h + include/monitorDetailAttributeDlg.h + include/monitorDisplaySettingDlg.h + include/monitorItemPreviewDlg.h + include/loadMonitorPageDlg.h + include/cornerMonitorLauncher.h + include/itemPropertyDlg.h + include/propertyContentDlg.h + include/serializable.h + include/statusBar.h + include/powerEntity.h + include/powerConnection.h + include/powerTerminal.h + include/topologyManager.h + include/baseInfoDlg.h + include/baseContentDlg.h + include/ptExtraInfoDlg.h + include/ctExtraInfoDlg.h + include/bayInfoDlg.h + include/bayManagerDlg.h + include/bayManagerContentDlg.h + include/measureSettingDlg.h + include/projectIconSetting.h + include/projectIconSelectionDlg.h + include/diagramConnectSetting.h + include/structDataPreviewDlg.h + include/titleBar.h + include/structDataMeasurementModel.h + include/structDataPropertyModel.h + include/structDataMeasurementDelegate.h + include/structDataPropertyDelegate.h + include/structDataCauseEditDlg.h + include/structDataActionParaDlg.h + include/bayMeasureDlg.h + include/basePropertyProxy.h + include/basePannelPropertyProxy.h + include/dataSourceDlg.h + 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/functionModelItem/electricFunctionModelConnectLineItem.h + include/graphicsItem/functionModelItem/electricFunctionModelPortItem.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItem.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h + include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h + include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h + + include/baseModelItem/electricBaseModelSvgItem.h + include/baseModelItem/electricBaseModelLineItem.h + include/baseModelItem/electricBaseModelSvgBus.h + include/baseModelItem/electricBaseModelPortItem.h + include/util/baseSelector.h + include/util/connectingSelector.h + include/util/creatingSelector.h + include/util/editingSelector.h + include/util/linkMovingSelector.h + include/util/movingSelector.h + include/util/rotationSelector.h + include/util/scalingSelector.h + include/util/selectorManager.h + include/util/subMovingSelector.h + include/instance/dataAccessor.h + + include/propertyType/CustomGadget.h + include/propertyType/CustomType.h + include/propertyType/dataSourceType.h + include/propertyType/PropertyTypeCustomization_CustomType.h + include/propertyType/propertyTypeCustomization_DataSourceType.h + include/propertyType/pannelColorGadget.h + ../common/include/httpInterface.h + ../common/include/tools.h + ../common/include/baseProperty.h + ../common/include/compiler.hpp + ../common/include/export.hpp + ../common/include/operatingSystem.hpp + ../common/include/structDataSource.h + ../common/include/extraPropertyManager.h + + ../common/core_model/types.h + ../common/core_model/diagram.h + ../common/core_model/topology.h + ../common/core_model/data_transmission.h + ../common/core_model/constants.h + ../common/frontend/monitor_item.h + ../common/backend/project_model.h + ../common/frontend/graphics_items.h +) + +set(DIAGRAMCAVAS_SOURCE_FILES + source/baseScene.cpp + source/designerScene.cpp + source/designerView.cpp + source/diagramCavas.cpp + source/baseDrawingPanel.cpp + source/monitorPanel.cpp + source/monitorSideBarDlg.cpp + source/monitorSelectedItemsDlg.cpp + source/monitorToolPage.cpp + source/monitorToolBox.cpp + source/monitorAttributeDlg.cpp + source/monitorAttributeGroupDlg.cpp + source/monitorConfigDlg.cpp + source/monitorDetailAttributeDlg.cpp + source/monitorDisplaySettingDlg.cpp + source/monitorItemPreviewDlg.cpp + source/loadMonitorPageDlg.cpp + source/cornerMonitorLauncher.cpp + source/itemPropertyDlg.cpp + source/propertyContentDlg.cpp + source/statusBar.cpp + source/powerEntity.cpp + source/powerConnection.cpp + source/powerTerminal.cpp + source/topologyManager.cpp + source/baseInfoDlg.cpp + source/baseContentDlg.cpp + source/ptExtraInfoDlg.cpp + source/ctExtraInfoDlg.cpp + source/bayInfoDlg.cpp + source/bayManagerDlg.cpp + source/bayManagerContentDlg.cpp + source/measureSettingDlg.cpp + source/projectIconSetting.cpp + source/projectIconSelectionDlg.cpp + source/diagramConnectSetting.cpp + source/structDataPreviewDlg.cpp + source/titleBar.cpp + source/structDataMeasurementModel.cpp + source/structDataPropertyModel.cpp + source/structDataMeasurementDelegate.cpp + source/structDataPropertyDelegate.cpp + source/structDataCauseEditDlg.cpp + source/structDataActionParaDlg.cpp + source/bayMeasureDlg.cpp + source/basePropertyProxy.cpp + source/basePannelPropertyProxy.cpp + source/dataSourceDlg.cpp + 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/functionModelItem/electricFunctionModelConnectLineItem.cpp + source/graphicsItem/functionModelItem/electricFunctionModelPortItem.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.cpp + source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.cpp + source/graphicsItem/functionModelItem/graphicsFunctionModelItem.cpp + + source/baseModelItem/electricBaseModelSvgItem.cpp + source/baseModelItem/electricBaseModelLineItem.cpp + source/baseModelItem/electricBaseModelSvgBus.cpp + source/baseModelItem/electricBaseModelPortItem.cpp + source/util/baseSelector.cpp + source/util/connectingSelector.cpp + source/util/creatingSelector.cpp + source/util/editingSelector.cpp + source/util/linkMovingSelector.cpp + source/util/movingSelector.cpp + source/util/rotationSelector.cpp + source/util/scalingSelector.cpp + source/util/selectorManager.cpp + source/util/subMovingSelector.cpp + source/instance/dataAccessor.cpp + + source/propertyType/PropertyTypeCustomization_CustomType.cpp + source/propertyType/propertyTypeCustomization_DataSourceType.cpp + source/propertyType/pannelColorGadget.cpp + ../common/source/httpInterface.cpp + ../common/source/baseProperty.cpp + ../common/source/tools.cpp + ../common/source/structDataSource.cpp + ../common/source/extraPropertyManager.cpp + + ../common/core_model/types.cpp +) + +set(UI_FILES + ui/itemPropertyDlg.ui + ui/baseInfoDlg.ui + ui/ptExtraInfoDlg.ui + ui/ctExtraInfoDlg.ui + ui/bayInfoDlg.ui + ui/measureSettingDlg.ui + ui/bayManagerDlg.ui + ui/bayManagerContentDlg.ui + ui/projectIconSetting.ui + ui/monitorConfigDlg.ui + ui/monitorDetailAttributeDlg.ui + ui/monitorDisplaySettingDlg.ui + ui/loadMonitorPageDlg.ui + ui/diagramConnectSetting.ui + ui/structDataPreviewDlg.ui + ui/bayMeasureDlg.ui + ui/dataSourceDlg.ui + ui/createHMIdlg.ui +) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(diagramCavas SHARED + MANUAL_FINALIZATION + ${DIAGRAMCAVAS_HEADER_FILES} + ${DIAGRAMCAVAS_SOURCE_FILES} + ${UI_FILES} + ../resource/DiagramDesigner.qrc + ) +else() + add_library(diagramCavas SHARED + ${DIAGRAMCAVAS_HEADER_FILES} + ${DIAGRAMCAVAS_SOURCE_FILES} + ${UI_FILES} + ../resource/DiagramDesigner.qrc + ) +endif() + +target_link_libraries(diagramCavas PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) +target_link_libraries(diagramCavas PRIVATE Qt6::SvgWidgets) +target_link_libraries(diagramCavas PRIVATE Qt6::Xml) +target_link_libraries(diagramCavas PRIVATE Qt6::Network) +target_link_libraries(diagramCavas PRIVATE Qt6::WebSockets) +target_link_libraries(diagramCavas PRIVATE Qt6::Charts) +target_link_libraries(diagramCavas PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) + +option(BUILD_SHARED_LIBS "Build as shared library" ON) + + +target_include_directories(diagramCavas PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_link_libraries(diagramCavas PRIVATE diagramUtils) +target_link_libraries(diagramCavas PRIVATE diagramCommunication) +target_link_libraries(diagramCavas PUBLIC PropertyEditor) + +target_compile_definitions(diagramCavas + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) diff --git a/diagramCavas/include/baseContentDlg.h b/diagramCavas/include/baseContentDlg.h new file mode 100644 index 0000000..7bcd742 --- /dev/null +++ b/diagramCavas/include/baseContentDlg.h @@ -0,0 +1,38 @@ +#ifndef BASECONTENTDLG_H +#define BASECONTENTDLG_H + +#include +#include +#include +//#include "global.h" +#include "common/backend/project_model.h" +/******************************************************* + 属性组界面基类 +********************************************************/ + +class BaseProperty; +class FixedPortsModel; + +class BaseContentDlg : public QDialog +{ + Q_OBJECT + +public: + BaseContentDlg(QWidget *parent = nullptr); + virtual ~BaseContentDlg(); + virtual void createGroupView(GroupStateInfo) = 0; //创建页面 + virtual QMap getPropertyValue(BaseProperty* = nullptr) = 0; //返回当前页面的属性值 + //void setPropertyValue(QMap); + //void setPropertyValue(BaseProperty*); + virtual void setPropertyValue(QVariant) = 0; + void setModelController(FixedPortsModel* p){_curModelController = p;} + auto getModelController() {return _curModelController;} + void setExtendProperty(PropertyStateInfo info) {_extendInfo = info;} //设置跨组别使用的公共变量(ct,pt使用extend绕组信息) +protected: + QMap _mapPro; + QFormLayout* createFormLayout(QWidget* parent); + FixedPortsModel* _curModelController; + PropertyStateInfo _extendInfo; +}; + +#endif diff --git a/diagramCavas/include/baseDrawingPanel.h b/diagramCavas/include/baseDrawingPanel.h new file mode 100644 index 0000000..94de1c2 --- /dev/null +++ b/diagramCavas/include/baseDrawingPanel.h @@ -0,0 +1,76 @@ +#ifndef BASEDRAWINGPANEL_H +#define BASEDRAWINGPANEL_H + +/****************工程模和运行时panel的基类*****************/ + +#include +#include +#include +//#include "global.h" +#include "designerScene.h" + +class DesignerView; +class DesignerScene; +class SelectorManager; +class GraphicsItemGroup; +class StatusBar; +class PowerEntity; +class ProjectDiagramNameInput; +class BayManagerDlg; +class BasePannelPropertyProxy; + +class BaseDrawingPanel : public QWidget +{ + Q_OBJECT +public: + BaseDrawingPanel(PowerEntity* pEntity,QWidget *parent = nullptr,DiagramMode mode = DM_edit); + ~BaseDrawingPanel(); + + QGraphicsScene* getQGraphicsScene(); + DesignerScene* getDesignerScene(); + + SelectorManager* selectorManager() const; //返回manager指针 + + void setPageName(QString s){_name = s;_pModel->setPageName(_name);} //设置当前page名称 + QString pageName(){return _name;} + + FixedPortsModel* getModelController() const {return _pModel;} + DiagramMode getMode(){return _mode;} + + virtual void loadNodes(QJsonObject obj) {}; //加载图元信息 + virtual void saveNodes(int pageId) {}; //保存到数据库 + virtual QJsonObject getDiagramInfo() {return QJsonObject();} + + DesignerScene* getScene() {return m_pGraphicsScene;} + DesignerView* getView() {return m_pGraphicsView;} + + void setGeneratePanelLst(QStringList lst){_lstGeneratePanel = lst;} + QStringList& getGeneratePanelLst(){return _lstGeneratePanel;} + void setGenerateByPanel(QString s) {_sGenerateByPanel = s;} + QString getGenerateByPanel() {return _sGenerateByPanel;} + + PowerEntity* getEntity() {return _pEntity;} + BasePannelPropertyProxy* getPropertyProxy(); +signals: + void panelDelete(const QString&,int); +protected: + virtual void closeEvent(QCloseEvent *closeEvent) {}; +protected: + QPointer _pPropertyProxy; //属性页代理 +protected: + DesignerView* m_pGraphicsView; + DesignerScene* m_pGraphicsScene; + SelectorManager* m_pSelectorManager; + StatusBar* m_pStatusBar; + FixedPortsModel* _pModel; + DiagramMode _mode; + QString _name; + PowerEntity* _pEntity; //组态图拓扑对象 + QVBoxLayout* _verticalLayout; + QHBoxLayout* _horizontalLayout; + QSplitter* _hSplitter; + QStringList _lstGeneratePanel; //生成的panel列表 + QString _sGenerateByPanel; //被哪个panel生成 +}; + +#endif diff --git a/diagramCavas/include/baseInfoDlg.h b/diagramCavas/include/baseInfoDlg.h new file mode 100644 index 0000000..00f6824 --- /dev/null +++ b/diagramCavas/include/baseInfoDlg.h @@ -0,0 +1,35 @@ +#ifndef BASEINFODLG_H +#define BASEINFODLG_H + +#include +#include "baseContentDlg.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class baseInfoDlg; } +QT_END_NAMESPACE + +class QButtonGroup; +class ItemPropertyDlg; + +class BaseInfoDlg : public BaseContentDlg +{ + Q_OBJECT +public: + BaseInfoDlg(QWidget *parent = nullptr); + ~BaseInfoDlg(); + + void initial(); + + virtual void createGroupView(GroupStateInfo); + virtual QMap getPropertyValue(BaseProperty* = nullptr); + virtual void setPropertyValue(QVariant); + void setParentDlg(ItemPropertyDlg* p) {_parentDlg = p;} +public slots: + void onIconManagerClicked(); +private: + Ui::baseInfoDlg *ui; + QButtonGroup* _stateGroup; + ItemPropertyDlg* _parentDlg; +}; + +#endif diff --git a/diagramCavas/include/baseModelItem/electricBaseModelLineItem.h b/diagramCavas/include/baseModelItem/electricBaseModelLineItem.h new file mode 100644 index 0000000..a0230b9 --- /dev/null +++ b/diagramCavas/include/baseModelItem/electricBaseModelLineItem.h @@ -0,0 +1,34 @@ +#ifndef ELECTRIBASEMODELLINEITEM_H +#define ELECTRIBASEMODELLINEITEM_H + +#include +#include +#include "graphicsItem/graphicsBaseItem.h" + +//基模导线 + +class ElectricBaseModelLineItem : public GraphicsBaseModelItem +{ +public: + ElectricBaseModelLineItem(QGraphicsItem *parent = 0); + ElectricBaseModelLineItem(const ElectricBaseModelLineItem&); + virtual ~ElectricBaseModelLineItem(); + virtual ElectricBaseModelLineItem* clone() const override; + void setStartPoint(const QPointF& p); + void setEndPoint(const QPointF& p); + + void calculatePath(); + void resetCurLine(){_curLine = QPoint();} +protected: + virtual QPainterPath shape() const override; + virtual QRectF boundingRect() const override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + QPainterPath m_points; + QPainterPath m_pointsBoundingRect; //包裹点的矩形集合 + QList m_lstPoints; + QPoint _curLine; //参数1用点序号表示的当前线段起始点,参数2表示线段方向 + +}; + +#endif diff --git a/diagramCavas/include/baseModelItem/electricBaseModelPortItem.h b/diagramCavas/include/baseModelItem/electricBaseModelPortItem.h new file mode 100644 index 0000000..01492a4 --- /dev/null +++ b/diagramCavas/include/baseModelItem/electricBaseModelPortItem.h @@ -0,0 +1,23 @@ +#ifndef ELECTRICBASEMODELPORTITEM_H +#define ELECTRICBASEMODELPORTITEM_H + +#include "graphicsItem/graphicsBaseItem.h" + +class ElectricBaseModelPortItem :public GraphicsBaseModelItem +{ + Q_OBJECT +public: + ElectricBaseModelPortItem(QGraphicsItem *parent = 0); + ElectricBaseModelPortItem(const ElectricBaseModelPortItem&); + virtual ElectricBaseModelPortItem* clone() const override; + virtual ~ElectricBaseModelPortItem(); + + void addPort(); +public: + virtual void updateConnectData(); +protected: + virtual QRectF boundingRect() const override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); +}; + +#endif diff --git a/diagramCavas/include/baseModelItem/electricBaseModelSvgBus.h b/diagramCavas/include/baseModelItem/electricBaseModelSvgBus.h new file mode 100644 index 0000000..3f4d338 --- /dev/null +++ b/diagramCavas/include/baseModelItem/electricBaseModelSvgBus.h @@ -0,0 +1,22 @@ +#ifndef ELECTRICBASEMODELSVGBUS_H +#define ELECTRICBASEMODELSVGBUS_H + +#include "baseModelItem/electricBaseModelSvgItem.h" + +class ElectricBaseModelSvgBus :public ElectricBaseModelSvgItem +{ + Q_OBJECT +public: + ElectricBaseModelSvgBus(const QRect &rect, QGraphicsItem *parent = 0); + virtual ~ElectricBaseModelSvgBus(); + + void addPort(); +public: + virtual void updateConnectData(); +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); +private: + virtual void updateHandles(); +}; + +#endif diff --git a/diagramCavas/include/baseModelItem/electricBaseModelSvgItem.h b/diagramCavas/include/baseModelItem/electricBaseModelSvgItem.h new file mode 100644 index 0000000..6c2f52b --- /dev/null +++ b/diagramCavas/include/baseModelItem/electricBaseModelSvgItem.h @@ -0,0 +1,31 @@ +#ifndef ELECTRICBASEMODELSVGITEM_H +#define ELECTRICBASEMODELSVGITEM_H + +#include "graphicsItem/graphicsBaseItem.h" +#include + +class ElectricBaseModelSvgItem :public GraphicsBaseModelItem +{ + Q_OBJECT +public: + ElectricBaseModelSvgItem(const QRect &rect, QGraphicsItem *parent = 0); //genNewPort生成新接线点 + ElectricBaseModelSvgItem(const ElectricBaseModelSvgItem&); + virtual ElectricBaseModelSvgItem* clone() const override; + virtual ~ElectricBaseModelSvgItem() override; + void updateCoordinate() override; + void move(const QPointF&) override; + void loadSvg(const QByteArray&); + +protected: + virtual QPainterPath shape() override; + virtual void editShape(int, const QPointF&) override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + +protected: + QRectF m_lastBoudingRect; //记录上一时刻的boundingRect + QSvgRenderer* m_pRender; + QByteArray m_icon; + +}; + +#endif diff --git a/diagramCavas/include/basePannelPropertyProxy.h b/diagramCavas/include/basePannelPropertyProxy.h new file mode 100644 index 0000000..9961740 --- /dev/null +++ b/diagramCavas/include/basePannelPropertyProxy.h @@ -0,0 +1,31 @@ +#ifndef BASEPANNELPROPERTYPROXY_H +#define BASEPANNELPROPERTYPROXY_H +/**************************** + * pannel属性代理基类 + * *************************/ +#include "basePropertyProxy.h" +#include "propertyType/pannelColorGadget.h" + +class BaseDrawingPanel; + +class BasePannelPropertyProxy : public BasePropertyProxy { + Q_OBJECT +public: + Q_PROPERTY(QString Name READ getName WRITE setName) + Q_PROPERTY(QSize Size READ getSize WRITE setSize) + Q_PROPERTY(PannelColorGadget* Color READ getColorGadgetPtr WRITE setColorGadgetPtr) +public: + BasePannelPropertyProxy(BaseDrawingPanel*); + ~BasePannelPropertyProxy(); +public: + virtual QString getName() const; + virtual void setName(QString); + virtual QSize getSize() const; + virtual void setSize(QSize); + PannelColorGadget* getColorGadgetPtr(){return _pColorGadget;} + void setColorGadgetPtr(PannelColorGadget* p){_pColorGadget = p;} +protected: + BaseDrawingPanel* _pPanel; + PannelColorGadget* _pColorGadget; +}; +#endif //BASEPANNELPROPERTYPROXY_H diff --git a/diagramCavas/include/basePropertyProxy.h b/diagramCavas/include/basePropertyProxy.h new file mode 100644 index 0000000..1985f89 --- /dev/null +++ b/diagramCavas/include/basePropertyProxy.h @@ -0,0 +1,14 @@ +#ifndef BASEPROPERTYPROXY_H +#define BASEPROPERTYPROXY_H +/**************************** + * 属性页代理基类 + * *************************/ +#include + +class BasePropertyProxy : public QObject { + Q_OBJECT +public: + BasePropertyProxy(QObject* parnet = nullptr); + ~BasePropertyProxy(); +}; +#endif //BASEPROPERTYPROXY_H diff --git a/diagramCavas/include/baseScene.h b/diagramCavas/include/baseScene.h new file mode 100644 index 0000000..ba686de --- /dev/null +++ b/diagramCavas/include/baseScene.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +#include "graphicsDataModel/baseModel.h" +//#include "global.h" + +class QUndoStack; + +class BaseModel; + +/// An instance of QGraphicsScene, holds connections and nodes. +class BaseScene : public QGraphicsScene +{ + Q_OBJECT +public: + BaseScene(BaseModel* graphModel, QObject *parent = nullptr); + + // Scenes without models are not supported + BaseScene() = delete; + + ~BaseScene(); + +public: + /// @returns associated BaseModel. + BaseModel const *graphModel() const; + + BaseModel *graphModel(); + + QUndoStack &undoStack(); + + +private: + BaseModel* _graphModel; + + + QUndoStack *_undoStack; + +}; + diff --git a/diagramCavas/include/bayInfoDlg.h b/diagramCavas/include/bayInfoDlg.h new file mode 100644 index 0000000..7d8a939 --- /dev/null +++ b/diagramCavas/include/bayInfoDlg.h @@ -0,0 +1,51 @@ +#ifndef BAYINFODLG_H +#define BAYINFODLG_H + +#include +#include "baseContentDlg.h" +//#include "global.h" +#include "common/core_model/data_transmission.h" +/******************************************************* + 间隔信息 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class bayInfoDlg; } +QT_END_NAMESPACE + +class MeasureSettingDlg; +class BayProperty; + +class BayInfoDlg : public BaseContentDlg +{ + Q_OBJECT +public: + BayInfoDlg(QWidget *parent = nullptr); + virtual ~BayInfoDlg(); + virtual void createGroupView(GroupStateInfo); + virtual QMap getPropertyValue(BaseProperty* = nullptr); //返回当前页面的属性值 + virtual void setPropertyValue(QVariant); + auto& getValidType() {return _validType;} //获取可用的量测属性 + void setUi(); + void addMeasure(MeasurementInfo,int mode = 0); //mode:0新建1修改 + void addOtherMeasure(QStringList); //本间隔的其他量测 + BaseProperty* getProperty() {return _itemProperty;} +public slots: + void onAddClicked(); + void onDeleteClicked(); + void onModifyClicked(); + void onIndexRbtnClicked(const QPoint &pos); //索引列表右键菜单 + + void onHttpDataUpdated(HttpRecommandInfo); +private: + void initial(); +private: + Ui::bayInfoDlg *ui; + BayProperty* _bayProperty; //当前间隔属性 + BaseProperty* _itemProperty; //当前对象属性 + MeasureSettingDlg* _measureDlg; + QList _validType; //可用的属性列表 + QMap _mapMeasure; //量测列表 + bool bShowDouble = false; //显示double界面 +}; + +#endif diff --git a/diagramCavas/include/bayManagerContentDlg.h b/diagramCavas/include/bayManagerContentDlg.h new file mode 100644 index 0000000..e64c748 --- /dev/null +++ b/diagramCavas/include/bayManagerContentDlg.h @@ -0,0 +1,33 @@ +#ifndef BAYMANAGERCONTENTDLG_H +#define BAYMANAGERCONTENTDLG_H + +#include +/******************************************************* + 间隔内容 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class bayManagerContentDlg; } +QT_END_NAMESPACE + +class BayProperty; +class QButtonGroup; + +class BayManagerContentDlg : public QDialog +{ + Q_OBJECT +public: + BayManagerContentDlg(QWidget *parent = nullptr); + ~BayManagerContentDlg(); + + void initial(); + void setProperty(BayProperty* p) {_pData = p;} + BayProperty* getProperty() {return _pData;} + void updateByProperty(); //根据数据更新显示 + void saveSetting(); //保存修改 +private: + Ui::bayManagerContentDlg *ui; + BayProperty* _pData; + QButtonGroup* _stateGroup; +}; + +#endif diff --git a/diagramCavas/include/bayManagerDlg.h b/diagramCavas/include/bayManagerDlg.h new file mode 100644 index 0000000..4997d17 --- /dev/null +++ b/diagramCavas/include/bayManagerDlg.h @@ -0,0 +1,41 @@ +#ifndef BAYMANAGERDLG_H +#define BAYMANAGERDLG_H + +#include +#include +/******************************************************* + 间隔管理 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class bayManagerDlg; } +QT_END_NAMESPACE + +class BayManagerContentDlg; +class FixedPortsModel; +class BayProperty; + +class BayManagerDlg : public QDialog +{ + Q_OBJECT +public: + BayManagerDlg(QWidget *parent = nullptr); + ~BayManagerDlg(); + + void initial(); + void showDlg(); + void initData(); + void clearData(); //切换打开文件时调用 + void setModelController(FixedPortsModel* p) {_modelController = p;} +public slots: + void onOkClicked(); + void onCancelClicked(); + void onListItemClicked(QListWidgetItem *item); +private: + void generatePage(QList); //生成间隔页 <间隔列表> +private: + Ui::bayManagerDlg *ui; + FixedPortsModel* _modelController; + QMap _contentData; // +}; + +#endif diff --git a/diagramCavas/include/bayMeasureDlg.h b/diagramCavas/include/bayMeasureDlg.h new file mode 100644 index 0000000..4800a75 --- /dev/null +++ b/diagramCavas/include/bayMeasureDlg.h @@ -0,0 +1,48 @@ +#ifndef BAYMEASUREDLG_H +#define BAYMEASUREDLG_H + +#include +//#include "global.h" +#include "common/backend/project_model.h" +/******************************************************* + 间隔量测界面 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class bayMeasureDlg; } +QT_END_NAMESPACE + +class MeasureSettingDlg; +class BayProperty; + +class BayMeasureDlg : public QDialog +{ + Q_OBJECT +public: + BayMeasureDlg(QWidget *parent = nullptr); + ~BayMeasureDlg(); + void getPropertyValue(BayProperty* pBay); + void setPropertyValue(BayProperty* pBay); + auto& getValidType() {return _validType;} //获取可用的量测属性 + void setUi(); + void addMeasure(MeasurementInfo,int mode = 0); //mode:0新建1修改 + void addOtherMeasure(QStringList); //本间隔的其他量测 + BayProperty* getBayProperty(){return _bayProperty;} + void showDlg(BayProperty* pBay); +public slots: + void onAddClicked(); + void onDeleteClicked(); + void onModifyClicked(); + void onIndexRbtnClicked(const QPoint &pos); //索引列表右键菜单 + void onOkClicked(); + void onCancelClicked(); +private: + void initial(); +private: + Ui::bayMeasureDlg *ui; + BayProperty* _bayProperty; //当前间隔属性 + MeasureSettingDlg* _measureDlg; + QList _validType; //可用的属性列表 + QMap _mapMeasure; //量测列表 +}; + +#endif diff --git a/diagramCavas/include/cornerMonitorLauncher.h b/diagramCavas/include/cornerMonitorLauncher.h new file mode 100644 index 0000000..323b480 --- /dev/null +++ b/diagramCavas/include/cornerMonitorLauncher.h @@ -0,0 +1,29 @@ +#ifndef CORNERMONITORLAUNCHER_H +#define CORNERMONITORLAUNCHER_H + +/***************cavas中的monitor临时加载菜单*****************/ +#include + +class QMdiArea; + +class CornerMonitorLauncher : public QWidget +{ + Q_OBJECT +public: + CornerMonitorLauncher(QMdiArea *parent = nullptr); + ~CornerMonitorLauncher(); + + void showDlg(); + void positionAtCorner(); +signals: + void openLoadMonitorDlg(); +protected: + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; +private: + void showQuickMenu(); +private: + QMdiArea* m_mdiArea; +}; + +#endif diff --git a/diagramCavas/include/createHMIdlg.h b/diagramCavas/include/createHMIdlg.h new file mode 100644 index 0000000..27dd829 --- /dev/null +++ b/diagramCavas/include/createHMIdlg.h @@ -0,0 +1,28 @@ +#ifndef CREATEHMIDLG_H +#define CREATEHMIDLG_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class createHMIdlg; } +QT_END_NAMESPACE + +class CreateHMIdlg : public QDialog +{ + Q_OBJECT +public: + CreateHMIdlg(QWidget *parent = nullptr); + ~CreateHMIdlg(); + + void initial(); + void showDlg(); +signals: + void createHMI(QString,QString,int type = 0); //HMI名称,系统图名称,模板类型 +public slots: + void onSaveClicked(); + void onCancelClicked(); +private: + Ui::createHMIdlg *ui; +}; + +#endif diff --git a/diagramCavas/include/ctExtraInfoDlg.h b/diagramCavas/include/ctExtraInfoDlg.h new file mode 100644 index 0000000..205d490 --- /dev/null +++ b/diagramCavas/include/ctExtraInfoDlg.h @@ -0,0 +1,45 @@ +#ifndef CTEXTRAINFODLG_H +#define CTEXTRAINFODLG_H + +#include +#include "baseContentDlg.h" +//#include "global.h" +/******************************************************* + ct扩展信息界面 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class ctExtraInfoDlg; } +QT_END_NAMESPACE + +class BaseProperty; +class QButtonGroup; + +class CtExtraInfoDlg : public BaseContentDlg +{ + Q_OBJECT + +public: + CtExtraInfoDlg(QWidget *parent = nullptr); + virtual ~CtExtraInfoDlg(); + virtual void createGroupView(GroupStateInfo); + virtual QMap getPropertyValue(BaseProperty* = nullptr); //返回当前页面的属性值 + virtual void setPropertyValue(QVariant); +public slots: + void onAddClicked(); + void onTableCustomContextMenuRequested(const QPoint &pos); +protected: + void addTableRow(QString sRatioRange,QString sAccuracy,QString sVolume,double dRatio,bool bPolarity,int index = -1); +private: + void updateShowLabel(QStringList lst); + void updateLables(); + void deleteRowWithReindex(int row); + void reorderMapAndUpdateIndices(int startRow); +private: + Ui::ctExtraInfoDlg *ui; + QMap _mapCT; + QButtonGroup* _stateGroup_ct; + int _count; + QStringList _curLabels; +}; + +#endif diff --git a/diagramCavas/include/dataSourceDlg.h b/diagramCavas/include/dataSourceDlg.h new file mode 100644 index 0000000..d6a089c --- /dev/null +++ b/diagramCavas/include/dataSourceDlg.h @@ -0,0 +1,67 @@ +#ifndef DATASOURCEDLG_H +#define DATASOURCEDLG_H + +/*********运行时数据源选则*********/ +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class dataSourceDlg; } +QT_END_NAMESPACE + +class QStandardItemModel; +class QStandardItem; +struct ExtraProperty; +class StructDataSource; +class ExtraPropertyManager; +class QPropertyHandle; +struct DataSourceType; +class QListWidgetItem; + +class DataSourceDlg : public QDialog +{ + Q_OBJECT +public: + DataSourceDlg(QWidget *parent = nullptr); + ~DataSourceDlg(); + + void initial(); + void loadData(); + + void showDlg(DataSourceType); + DataSourceType getCurData(); +signals: + void listWidgetUpdated(); +public slots: + void onOkClicked(); + void onCancelClicked(); + + void onTreeSelectionChanged(const QModelIndex& current, const QModelIndex& previous); + void onListWidgetClicked(QListWidgetItem*); + void addItemToView(const ExtraProperty& property, + const QString& displayMode, // "name" 或 "tag" + QStandardItem* root, + QStandardItem* pItem); +private: + QString getLevelType(int index); + void clearItems(); + void clearPropertyList(); + void loadCategoryProperties(QStandardItem* categoryItem); + QVector getCategoryPropertiesFromDataManager(const QVariantMap& categoryData); + void updateCategoryProperties(QStandardItem* categoryItem,const ExtraProperty& property); // 更新category节点的属性列表 + QStandardItem* processGroupLevel(QStandardItem* componentItem,const ExtraProperty& property); // 处理group层级 + void processCategoryLevel(QStandardItem* groupItem,const ExtraProperty& property); // 处理category层级 + void updatePropertyList(QVector); + void expandToNodeByCode(const QString& code,QStandardItemModel* model); //根据属性code展开树到指定节点 + void selectListItemByCode(const QString& code); //在listWidget中选中指定code的项 + QStandardItem* findCategoryNodeByPropertyCode(QStandardItem* item, const QString& code); +private: + Ui::dataSourceDlg *ui; + QStandardItemModel* _treeModel; + QStandardItem* m_currentCategoryItem; //当前操作对象 + ExtraPropertyManager* _pExtraProManager; + StructDataSource* m_dataSource; + QListWidgetItem* _curProperty; //当前属性 + QString m_targetPropertyCode; //打开的目标code +}; + +#endif diff --git a/diagramCavas/include/designerScene.h b/diagramCavas/include/designerScene.h new file mode 100644 index 0000000..60871eb --- /dev/null +++ b/diagramCavas/include/designerScene.h @@ -0,0 +1,55 @@ +#ifndef DESIGNER_SCENE_H +#define DESIGNER_SCENE_H + +#include +#include "baseScene.h" +#include "graphicsDataModel/fixedPortsModel.h" + +class GraphicsItemGroup; +class BaseDrawingPanel; + + +class DesignerScene : public BaseScene +{ + Q_OBJECT + +public: + DesignerScene(FixedPortsModel* graphModel,QObject *parent = 0); + virtual ~DesignerScene(); + + void setGridVisible(bool); + void setView(QGraphicsView* view) { m_pView = view; } + QGraphicsView* getView() { return m_pView; } + void callParentEvent(QGraphicsSceneMouseEvent*); + + GraphicsItemGroup* createGroup(); + void destroyGroup(); + void setBackGoundColor(QColor color) {m_backGroundColor = color;} + QColor getBackGoundColor() {return m_backGroundColor;} + void setGridColor(QColor color) {m_gridColor = color;} + QColor getGridColor() {return m_gridColor;} +signals: + void signalAddItem(QGraphicsItem*); +protected: + void drawBackground(QPainter*, const QRectF&) override; + void mousePressEvent(QGraphicsSceneMouseEvent*) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override; + void keyPressEvent(QKeyEvent*) override; + void keyReleaseEvent(QKeyEvent*) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent) override; + void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override; + void dragMoveEvent(QGraphicsSceneDragDropEvent *event) override; + void dropEvent(QGraphicsSceneDragDropEvent *event) override; +private: + bool m_bGridVisible; + QGraphicsView* m_pView; + BaseDrawingPanel* m_pDrawingPanel; //保存父指针 + QColor m_backGroundColor; + QColor m_gridColor; +private: + FixedPortsModel* _graphModel; +}; + +#endif diff --git a/diagramCavas/include/designerView.h b/diagramCavas/include/designerView.h new file mode 100644 index 0000000..8adcbb9 --- /dev/null +++ b/diagramCavas/include/designerView.h @@ -0,0 +1,41 @@ +#ifndef DESIGNER_VIEW_H +#define DESIGNER_VIEW_H + +#include + +class DesignerView : public QGraphicsView +{ + Q_OBJECT + +public: + explicit DesignerView(QWidget *parent = 0); + virtual ~DesignerView(); + + //视图操作-外部调用 + void zoomIn(); + void zoomOut(); + void zoomFit(); + +protected: + //virtual void contextMenuEvent(QContextMenuEvent*) override; + virtual void mousePressEvent(QMouseEvent*) override; + virtual void mouseMoveEvent(QMouseEvent*) override; + virtual void mouseReleaseEvent(QMouseEvent*) override; + virtual void wheelEvent(QWheelEvent*) override; +private: + void initialize(); + //视图操作相关 + void zoom(const QPointF&, double); + bool zoomLimit(double&); + double getScaleFactor(); + void translate(const QPointF&); +signals: + void onScaleChanged(double d); +private: + bool m_bMousePress; + double m_dScale; + QPointF m_ptLatstMouse_view; //鼠标最后按下在view中的位置 + int m_nLevel; +}; + +#endif diff --git a/diagramCavas/include/diagramCavas.h b/diagramCavas/include/diagramCavas.h new file mode 100644 index 0000000..582b577 --- /dev/null +++ b/diagramCavas/include/diagramCavas.h @@ -0,0 +1,131 @@ +#ifndef DIAGRAMCAVAS_H +#define DIAGRAMCAVAS_H + +#include +//#include "global.h" +#include "common/backend/project_model.h" +#include "common/core_model/topology.h" +#include "common/core_model/types.h" +#include "common/core_model/diagram.h" +#include "export.hpp" + +QT_BEGIN_NAMESPACE +namespace Ui { class diagramCavas; } +QT_END_NAMESPACE + +class DrawingPanel; +class PowerEntity; +class GraphicsBaseModelItem; +class EditBaseItem; +class MonitorPanel; +class CornerMonitorLauncher; +class LoadMonitorPageDlg; +class DiagramConnectSetting; +class DataAccessor; +struct HttpRecommandInfo; +class StructDataPreviewDlg; +class ExtraPropertyManager; +class DataSourceDlg; +class CreateHMIdlg; + +class DIAGRAM_DESIGNER_PUBLIC DiagramCavas : public QMdiArea +{ + Q_OBJECT + +public: + DiagramCavas(QWidget *parent = nullptr); + ~DiagramCavas(); + + MonitorPanel* getMonitorPanel(QString); + DataAccessor* getDataAccessor() const {return _dataAccessor;} + + void updateSubPos(); + void passRecommmandHttpData(HttpRecommandInfo); //传递推荐列表数据 + DiagramConnectSetting* getConnectSettingDlg() {return _connectSetting;} + ExtraPropertyManager* getExtraPropertyManager() {return _extraPropertyManager;} +public: + void initial(); +signals: + void prepareUpdateItems(QList,bool refresh); + void prepareSelectItems(QList); + void updateMonitorList(QString,QPair,int nMode = 0); //0新增1删除 + void createdMonitorItems(QList); //创建的监控中item个数 + void selectTarget(QObject*); + void prepareUpdateTopology(QList,bool refresh,bool showFull); //更新层级数 refresh:刷新标志,showFull:显示全部层级 + + void updateMonitorTopology(QList); + + void createHMI(QString,QUuid); + void updateHMI(QString,QUuid,int,QString); +public slots: + void onSignal_addDrawingPanel(PowerEntity* p,DiagramMode = DM_edit,QString parent = QString()); //parent:派生运行时的page + void onSignal_addGraphicsItem(ModelStateInfo&); + void onSignal_addPage(); + void onSignal_savePage(); + void onSignal_loadMonitor(PowerEntity* p); + void runPage(const QString); //运行时 + void onSignal_runPage(); + void onSignal_deletePage(); + + void onSignal_activatePage(const QString& name); + void onSignal_panelDelete(const QString& name,int nType); //type:0editorPanel,1drawPanel + + void onSignal_createEntity(EntityInfo); + void onSignal_changeEntity(EntityInfo); + void onSignal_deleteEntity(EntityInfo); + void onSignal_selectEntity(EntityInfo); + + void onSignal_createDiagram(DiagramInfo,DiagramMode mode = DM_edit); + void onSignal_changeDiagram(DiagramInfo); + void onSignal_deleteDiagram(DiagramInfo); + void onSignal_selectDiagram(DiagramInfo); + + void onSignal_openNetSetting(); //打开网络设置 + void onSignal_openStructDataPreview(); //打开结构数据界面 + + void onSignal_createHMI(QString,QUuid); //HMI创建 + void onSignal_updateHMI(QString,QUuid,int,QString); //HMI更新 + + /****************************拓扑关系(层级关系)******************************/ + void onSignal_updateTopology(QList,bool,bool); //更新拓扑列表 + /*********************************间隔**************************************/ + void onSignl_openCurrentBay(); + /********************************运行时**********************************/ + void onCreateHMIClicked(); //创建HMI组态按下 + void onSignal_createHMIClicked(QString,QString,int); //收到设置信号 + + void onSignal_updateCurItems(QList,bool); //更新当前设备列表 + void onSignal_selectedItems(QList); //当前选中设备 + void onSignal_monitorCreated(QString,QPair); //监控已创建 + void onSignal_monitorItemCreated(QList); //监控中创建的对象 + + void onSignal_monitorSelected(DiagramInfo); //监控选中 + void onSignal_saveMonitor(QList>); //保存选中的监控 + + void updateMonitorListFromDB(int dest = 0); //从数据库更新监控列表 0更新外部lst 1更新内部lst + void onSignal_updateMonitorTopology(QList); //通知tree更新monitor拓扑 + + void updateHMIlstFromDB(); //从数据库更新HMI列表 + + QMap> getMapMonitor() {return m_mapMonitorPanel;} + + void onTargetSelected(QObject*); //选中事件(属性显示) +protected: + void resizeEvent(QResizeEvent* event) override; +private: + void removePanel(PowerEntity*); + void calculateLauncherVisible(); +private: + QMap> m_mapMonitorPanel; //监控时panel + int _pageIndex; + QString _curPage; + CornerMonitorLauncher* _cornerButton; //简略菜单呼出按钮 + LoadMonitorPageDlg* _loadMonitorPageDlg; + DiagramConnectSetting* _connectSetting; + DataAccessor* _dataAccessor; + StructDataPreviewDlg* _structDataPreviewDlg; + ExtraPropertyManager* _extraPropertyManager; + CreateHMIdlg* _createHMIDlg; +}; + +#endif diff --git a/diagramCavas/include/diagramConnectSetting.h b/diagramCavas/include/diagramConnectSetting.h new file mode 100644 index 0000000..28432fd --- /dev/null +++ b/diagramCavas/include/diagramConnectSetting.h @@ -0,0 +1,38 @@ +#ifndef DIAGRAMCONNECTSETTING_H +#define DIAGRAMCONNECTSETTING_H + +#include +//#include "global.h" +/******************************************************* + 网络连接设置 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class diagramConnectSetting; } +QT_END_NAMESPACE + +struct ChannelConfig; + +class DiagramConnectSetting : public QDialog +{ + Q_OBJECT +public: + DiagramConnectSetting(QWidget *parent = nullptr); + ~DiagramConnectSetting(); + + void showDlg(); + void updateHttpLog(QString sType,QString data); + void updateWebsocketLog(QString); +public slots: + void onTestHttpRecommandClicked(); + void onTestHttpDataClicked(); + void onTestWebsocketClicked(); + void onOkClicked(); + void onCancelClicked(); +private: + void initial(); + void updateByConfig(ChannelConfig,int nType = 0); //0http 1websocket +private: + Ui::diagramConnectSetting *ui; +}; + +#endif diff --git a/diagramCavas/include/graphicsDataModel/baseModel.h b/diagramCavas/include/graphicsDataModel/baseModel.h new file mode 100644 index 0000000..39b9316 --- /dev/null +++ b/diagramCavas/include/graphicsDataModel/baseModel.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +//#include "global.h" +#include "common/core_model/types.h" + +class GraphicsBaseItem; +class ItemPort; + +class BaseModel : public QObject +{ + Q_OBJECT +public: + void createTopoTerminalsByItem(GraphicsBaseItem*,ModelFunctionType funType = ModelFunctionType::ProjectModel); //通过图形对象创建port接线点(新建) + QPointF calculateBusPortPos(GraphicsBaseItem* pBus,GraphicsBaseItem* item); //计算母线上接线点位置 + template void establishConnection(GraphicsBaseItem*,GraphicsBaseItem*,TypeLine*,ModelFunctionType,int nMode=0,int nParam = 0); //在两个item之间建立连接 nMode:0正常1变压器中性点2变压器 nParam附加参数:中性点中表示需连接的位置(012高中低) + double distanceBetweenItems(QGraphicsItem* item1, QGraphicsItem* item2) { + QPointF center1 = item1->mapToScene(item1->boundingRect().center()); + QPointF center2 = item2->mapToScene(item2->boundingRect().center()); + + QPointF diff = center1 - center2; + return qSqrt(diff.x() * diff.x() + diff.y() * diff.y()); + } + ItemPort* getClosestUnusedPort(QMap,GraphicsBaseItem* item,ModelFunctionType); //返回距离item最近未使用端点 +}; + + diff --git a/diagramCavas/include/graphicsDataModel/fixedPortsModel.h b/diagramCavas/include/graphicsDataModel/fixedPortsModel.h new file mode 100644 index 0000000..5dfbdbd --- /dev/null +++ b/diagramCavas/include/graphicsDataModel/fixedPortsModel.h @@ -0,0 +1,199 @@ +#pragma once + +#include "graphicsDataModel/baseModel.h" +#include "serializable.h" +#include "powerEntity.h" + +#include +#include +#include +#include + +#include "common/frontend/monitor_item.h" +#include "common/backend/project_model.h" + +class BaseDrawingPanel; +class DrawingPanel; +class GraphicsBaseItem; +class GraphicsFunctionModelItem; +class GraphicsBaseModelItem; +class BaseProperty; +class BaseModelProperty; +class DesignerScene; +class HttpInterface; +class DiagramCavas; +struct Connection; + +class PowerEntity; +class ElectricFunctionModelConnectLineItem; +class ProjectIconSetting; +class ElectricBayItem; +class GraphicsNonStandardItem; +class BayProperty; +class BayManagerDlg; +class ModelProperty; +struct ItemPageInfo; +class EditBaseItem; +class MonitorPanel; +class ItemPropertyDlg; +class BayMeasureDlg; +struct MeasurementInfo; + +class FixedPortsModel : public BaseModel, public Serializable +{ + Q_OBJECT + +public: + FixedPortsModel(PowerEntity*); + ~FixedPortsModel(); +public: + QMap allNodePos() const; + QVector allConnectionProperty(); + QMap& allItems(); + bool addNodeItem(QUuid uuid,GraphicsFunctionModelItem*); + QString addNodeItem(QUuid id,QPointF pos,double width = 0,double height = 0,double rotate = 0); + 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*); + //QJsonObject saveNode(QUuid const) const; + void saveNode(int nPageId); + void setScene(DesignerScene* p){_scene = p;} + DesignerScene* getScene() {return _scene;} + void setTopWidget(BaseDrawingPanel* p) {_widget = p;} + BaseDrawingPanel* getParent(){return _widget;} + QWidget* getTopWidget(); + QPointF getTerminalPos(const QString& sTerminalId); //获取拓扑接线点在当前diagram中的位置 + ElectricFunctionModelConnectLineItem* getLineItemById(const QString& terminalId); + void updateItemLinePort(QUuid,ModelFunctionType); //刷新连接item的线端点位置 + QMap& getModelState() {return _modelStateInfo;} + + void showModelDlg(const QString&,QUuid,GraphicsFunctionModelItem*); //点击时显示指定模型的dlg、指定item的数据(模型名,对象Uuid,触发事件的item) + void initialPropertyDlg(); //初始化属性设置dlg,每个模型拥各自的dlg + void generatePropertyDlg(const QString&); + ConfigurationDiagram* getTopologyDiagram(); //返回当前组态图的拓扑实体 + void createTopoTerminalsByData(PowerEntity* pParent,QJsonObject componentCon,ModelFunctionType funType = ModelFunctionType::ProjectModel); //通过componet数据创建port接线点(加载) + bool isItemValid(GraphicsFunctionModelItem*); //判断item是否可以连接 + + 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 updateModelIcon(QString sMeta,QString sModel,QMap,QString sIndex = ""); //更新某类模型的所有图标 + /*************************间隔*****************************/ + void addBayItem(QUuid,ModelFunctionType = ModelFunctionType::ProjectModel); + bool addBayItem(QUuid,ElectricBayItem*,ModelFunctionType = ModelFunctionType::ProjectModel); + void addItemsToBay(QList,ElectricBayItem*); //将对象添加到间隔 + BayProperty* addBayData(QUuid uuid,ModelFunctionType = ModelFunctionType::ProjectModel); + QMap& allBayItem(); //返回所有间隔对象 + + BayProperty* generateBayData(BayProperty*,QList); //生成新间隔数据(间隔数据结构相同) + QList getCorrespondId(QList,QList); //获取基模id对应的工程模id + QRectF calculateItemsBoundingRect(QList items); //返回包含所有item的rect + void addBayByData(BayProperty*,ModelFunctionType = ModelFunctionType::ProjectModel); //data生成bay + + void showBayMeasureDlg(BayProperty*); //显示间隔量测 + QJsonObject turnListToJson(QList lst,QString sInerTag,QString sOutTag); //将list转换为QJsonObject, + QList turnJsonArrToList(QJsonObject obj,QString sInner,QString sOut); + /*************************监控(运行时)**************************/ + void generateMonitorConfig(MonitorPanel*); //生成监控配置参数结构 + void setMonitorPara(QMap> map){m_monitorPara = map;} + QMap>& getMonitorPara() {return m_monitorPara;} + void setMonitorRelation(QList lst){m_lstMonitorRelation = lst;} + QList getMonitorRelation() {return m_lstMonitorRelation;} + + void monitorItemSelected(QUuid); //运行时item选中事件 + void monitorItemDetailAttr(QUuid); //显示属性详情 + + void monitorItemSet(QUuid); //运行时item设置完成 + void updateMonitorDisplay(); //更新监控图元显示 + + void startAcceptData(); //开始接收实时数据 + void stopAcceptData(QString); //停止接收实时数据 + + QMap>>& getMonitorStateMap(){return m_monitorStateMap;} + void setMonitorDisplaySetting(QMap> map){m_monitorDisplaySetting = map;} + QMap>& getMonitorDisplaySetting(){return m_monitorDisplaySetting;} + + QList& getHMIimageRef(){return _HMIimageRef;} + int imageRefExist(QString model,QByteArray hash256); + void updateHMIRef(QUuid hmiId,QString model,QByteArray hash256,int slot); //更新img引用 + /************************数据显示*************************/ + void setCurItemPropertyDlg(ItemPropertyDlg* p) {m_curPropertyDlg = p;} + ItemPropertyDlg* getCurItemPropertyDlg() {return m_curPropertyDlg;} +Q_SIGNALS: + void activatePage(const QString&); //激活当前model所在page + void updateCurrentItems(QList,bool); //更新当前组态元件列表 <连接关系,是否刷新> + void itemSelected(QList); //发送选中的元件 + void monitorCreated(QString,QPair); //监控创建信号 <工程page,<监控page,page_uid>> + void monitorItems(QList); //发送创建成功的Items + void dataUpdated(); //数据更新通知 + void updateTopologyItems(QList,bool,bool); //更新当前拓扑列表 <连接关系,是否刷新,显示全部层级> + void notifyUpdateMonitorTopology(QList); //使用列表中的item id更新总拓扑 + + void HMIUpdated(QString,QUuid,int,QString); //HMI更新信号,页名、页id、页状态、保存时间 +public: + void setPageName(QString s) {_pageName = s;} //设置表名称 + QString pageName() const {return _pageName;} + void activateModel() {Q_EMIT activatePage(_pageName);} //发送激活信号(点击) + void startHttpRequest(); //开始请求数据(运行时) + void setCavas(QPointer p) {_cavas = p;} //设置所属顶层容器 + DiagramCavas* getCavas(); + + QMap getHMIItems(){return _nodeItem;} + QMap getProjectBayItems(){return _bayItem;} + + void setPageState(int n) {_pageState = n;} + int getPageState() {return _pageState;} + void setLastSave(QString sTime) {_lastSaveTime = sTime;} + QString getLastSave() {return _lastSaveTime;} +public Q_SLOTS: + void onSignal_ifExits(QUuid id,const QString&,int type,GraphicsFunctionModelItem*); //判断用户输入的名称是否已存在 + void onTimeOut(); + void onSignal_GetPointData(QString type,QMap map); + void onSignal_openBayManager(); + void onDataTimerOut(); + void onSelectionChanged(); +private: + void addPortsToItem_json(PortState,QJsonArray,GraphicsBaseItem*); //将json格式的port添加到item + void autoSetModelName(GraphicsBaseModelItem*); //如果此页的工程模已被设置,将projectName更新到item + QString removeSuffix(const QString& str); //移除最后一个下划线后的内容 (处理各种tag后缀) + ModelProperty* getItemByUid(QList,QUuid); //返回uid对应的data + void updateMonitor(QMap>); //使用当前数据更新运行时 + void assignMeasureSymmetry(QMap& measurementMap); //设置量测中互为double的name +private: + + QMap _nodeItem; //工程模对象 + QMap _bayItem; //间隔对象 + + QString _pageName; + QPointer _cavas; + DesignerScene* _scene; + BaseDrawingPanel* _widget; //顶层widget + HttpInterface* _Interface; + QTimer* _timer; + PowerEntity* _pEntity; //拓扑实体 + QMap _projectModelName; //该图中所有元件对应的工程模类型(todo:扩展为每张图独立的结构体) uuid,工程模名称 + + QMap _modelStateInfo; //模型结构信息 + QMap _modelDataInfo; //模型数据信息 + ProjectIconSetting* m_projectIconSettingDlg; + BayManagerDlg* m_pBayManager; + QMap> m_monitorPara; //监控参数 + QMap>> m_monitorStateMap; //元件状态对照表 > + QMap> m_monitorDisplaySetting; //元件设置 + QList m_lstMonitorRelation; //监控item层级关系 + + ItemPropertyDlg* m_curPropertyDlg; + QTimer* m_dataTimer; //获取数据的定时器 + QStringList _curRequestLst; //当前请求对象列表 + QMap> _curData; //当前数据 + BayMeasureDlg* m_bayMeasureDlg; //间隔量测 + int _pageState = 0; //当前页面的状态 0未保存1已保存 + QString _lastSaveTime; //页面上次保存时间 + + QList _HMIimageRef; //当前HMI中图片的引用关系 +public: + static bool _dataInitialised; +}; + diff --git a/diagramCavas/include/graphicsItem/electricBayItem.h b/diagramCavas/include/graphicsItem/electricBayItem.h new file mode 100644 index 0000000..1ef1c22 --- /dev/null +++ b/diagramCavas/include/graphicsItem/electricBayItem.h @@ -0,0 +1,27 @@ +#ifndef ELECBAYITEM_H +#define ELECBAYITEM_H + +#include "graphicsBaseItem.h" +#include "baseProperty.h" + +class ElectricBayItem :public GraphicsNonStandardItem +{ + Q_OBJECT +public: + ElectricBayItem(const QRectF &rect, QGraphicsItem *parent = 0); //genNewPort生成新接线点 + virtual ~ElectricBayItem(); + + void setText(const QString& s); +protected: + virtual QPainterPath shape(); + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); +private: + void updateTextShape(); +protected: + QRectF _recLabel; + QString m_text; + QFont m_font; + QRectF m_showRect; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h new file mode 100644 index 0000000..39ec691 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h @@ -0,0 +1,95 @@ +#ifndef ELECTRICFUNCTIONMODELCONNECTLINEITEM_H +#define ELECTRICFUNCTIONMODELCONNECTLINEITEM_H + +#include +#include +#include "graphicsFunctionModelItem.h" + +class ElectricFunctionModelConnectLineItem : public GraphicsFunctionModelItem +{ +public: + enum UShapeType { + UShape_Unknown, + UShape_TopOpen, // 开口向上 + UShape_BottomOpen, // 开口向下 + UShape_LeftOpen, // 开口向左 + UShape_RightOpen // 开口向右 + }; +public: + ElectricFunctionModelConnectLineItem(QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelConnectLineItem(); + + void setStartPoint(const QPointF& p); + void setEndPoint(const QPointF& p); + QPainterPath getPoints(void) const { return m_points; } + + void startDrag(const QPointF& scenePos); + void updateDrag(const QPointF& scenePos); + void endDrag(); + void setLastPoint(const QPointF& point); + void setPath(const QPainterPath& path); + QPainterPath path() const; + + void resetCurLine(){_curLine = QPoint();} + void calculatePath(); + + virtual QPainterPath shape() const override; + virtual QRectF boundingRect() const override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + +protected: + void initial(); +private: + //路径计算相关 + void generateAvoidancePath(const QPointF& start, const QPointF& end,const QList& obstacleShapes); // 使用形状进行避障 + double calculatePathLength(const QList& path); + bool lineIntersectsRect(const QLineF& line, const QRectF& rect) const; + bool isSegmentSafe(const QPointF& p1, const QPointF& p2,const QList& components); + bool isPathSafe(const QList& path,const QList& components); + bool segmentPenetratesComponent(const QLineF& segment,const QRectF& component); + QRectF getTotalComponentsBounds(const QList& components); + void collectRectilinearPaths(const QPointF& start, const QPointF& end,const QList& components,QMultiMap>& paths); + void addPathIfRectilinear(const QList& path,QMultiMap>& paths); + void collectBypassPaths(const QPointF& start, const QPointF& end,const QList& components,QMultiMap>& paths); + void generateForcedRectilinearBypass(const QPointF& start, const QPointF& end,const QList& components); + QRectF findFirstBlockingComponent(const QPointF& start, const QPointF& end,const QList& components); + + //线段拖拽相关 + double distancePointToLine(const QLineF& line, const QPointF& point) const; + void ensureEnoughPointsForDrag(); + void debugPathState(const QString& context) const; + + QVector extractSegmentsFromPainterPath() const; + void collectBoundaryBypassPaths(const QPointF& start, const QPointF& end,const QList& obstacles,QMultiMap>& paths); + void collectBoundaryPath(const QPointF& start, const QPointF& end,const QList& obstacles,QMultiMap>& paths,const QRectF& bounds, bool useTop); + QRectF getSceneBounds() const; + QList generateForcedBypass(const QPointF& start, const QPointF& end,const QList& components); + + // 拖拽实现 + int findSegmentAt(const QList& points,const QPointF& itemPos) const; + double distanceToSegment(const QLineF& segment, const QPointF& point) const; + void fixConnections(int segmentIndex, bool isVertical); + QList extractPointsFromPath() const; + QList extractPointsFromPath(const QPainterPath& path) const; + void applyPointsToPath(const QList& points); + void fixConnections(QList& points, int segmentIndex, bool isVertical); + void validateAndFixPath(); + void updateBoundingRect(); + +private: + QPainterPath m_points; + QList m_lstPoints; + QPoint _curLine; //参数1用点序号表示的当前线段起始点,参数2表示线段方向 + + struct DragState { + bool isActive = false; + int segmentIndex = -1; + bool isVertical = false; + QPointF startScenePos; + QPainterPath originalPath; + }; + + DragState m_dragData; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelPortItem.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelPortItem.h new file mode 100644 index 0000000..368dce6 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelPortItem.h @@ -0,0 +1,24 @@ +#ifndef ELECTRICFUNCTIONMODELPORTITEM_H +#define ELECTRICFUNCTIONMODELPORTITEM_H + +#include "graphicsItem/functionModelItem/graphicsFunctionModelItem.h" + +//node节点 +class ElectricFunctionModelPortItem :public GraphicsFunctionModelItem +{ + Q_OBJECT +public: + ElectricFunctionModelPortItem(QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelPortItem(); + + void addPort(); +public: + virtual void updateConnectData() override; +protected: + virtual QRectF boundingRect() const override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + void initial(); +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h new file mode 100644 index 0000000..f9227a3 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h @@ -0,0 +1,32 @@ +#ifndef ELECTRICFUNCTIONMODELSVGGROUP_H +#define ELECTRICFUNCTIONMODELSVGGROUP_H + +#include "graphicsFunctionModelItem.h" +#include + +class ElectricFunctionModelSvgItem; + +class ElectricFunctionModelSvgGroup :public GraphicsFunctionModelGroup +{ + Q_OBJECT +public: + ElectricFunctionModelSvgGroup(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgGroup(); + void resize(int,double, double, const QPointF&) override; + void updateCoordinate() override; + void move(const QPointF&) override; + virtual void addSvgItem(ElectricFunctionModelSvgItem* item); + virtual void updateMapSvg(QMap map,QString sIndex = ""); //工程模property不含图片,额外存储 + virtual void setMonitorDisplayInfo(QMap info) override; //将显示数据更新到子item中 +protected: + virtual QPainterPath shape() override; + virtual void editShape(int, const QPointF&) override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +protected: + virtual void updateCurState(MonitorItemState e) override; +protected: + QRectF m_lastBoudingRect; //记录上一时刻的boundingRect + QMap m_mapSvg; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h new file mode 100644 index 0000000..ea7e085 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h @@ -0,0 +1,26 @@ +#ifndef ELECTRICFUNCTIONMODELSVGGROUPCT_H +#define ELECTRICFUNCTIONMODELSVGGROUPCT_H + +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h" + +class ElectricFunctionModelSvgGroupCT :public ElectricFunctionModelSvgGroup +{ + Q_OBJECT +public: + ElectricFunctionModelSvgGroupCT(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgGroupCT(); + virtual void setupFinish(QVariant) override; + virtual void updateItem() override; + void setCtType(int n){_nType = n;} + void setCtSize(int n){_nSize = n;} + virtual void setImage_1(QFileInfo) override; +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + void initial(); +protected: + int _nType = 0; //Ct类型 1三相0零相 + int _nSize = 0; //ct个数 +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h new file mode 100644 index 0000000..9c3162f --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h @@ -0,0 +1,25 @@ +#ifndef ELECTRICFUNCTIONMODELSVGGROUPPT_H +#define ELECTRICFUNCTIONMODELSVGGROUPPT_H + +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h" + +class ElectricFunctionModelSvgGroupPT :public ElectricFunctionModelSvgGroup +{ + Q_OBJECT +public: + ElectricFunctionModelSvgGroupPT(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgGroupPT(); + virtual void setupFinish(QVariant) override; + virtual void updateItem() override; + virtual void updateLayout() override; + QList& getLstType() {return m_lstType;} + virtual void setImage_1(QFileInfo) override; +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + void initial(); +protected: + QList m_lstType; //绕组类型 1星型 0三角 +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem.h new file mode 100644 index 0000000..6778619 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem.h @@ -0,0 +1,30 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEM_H +#define ELECTRICFUNCTIONMODELSVGITEM_H + +#include "graphicsFunctionModelItem.h" + +class ElectricFunctionModelSvgItem :public GraphicsFunctionModelItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItem(const QRect &rect, bool genNewPort = true,QGraphicsItem *parent = 0); //genNewPort生成新接线点 + virtual ~ElectricFunctionModelSvgItem(); + void resize(int,double, double, const QPointF&) override; + void updateCoordinate() override; + void move(const QPointF&) override; + virtual void loadSvg(){}; + virtual void loadSvg(QByteArray); //第二种load直接加载图片 + virtual void updateMapSvg(QMap map,QString sIndex = ""); //index:空全部更新 + virtual void updateCurState(MonitorItemState e) override; +protected: + virtual QPainterPath shape() override; + virtual void editShape(int, const QPointF&) override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +protected: + QRectF m_lastBoudingRect; //记录上一时刻的boundingRect + QSvgRenderer* m_pRender; //默认 + QSvgRenderer* m_pCustomRender; //定制 + QMap m_mapSvg; + QByteArray _tempSvg; //保存直接加载的svg数据 +}; +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h new file mode 100644 index 0000000..df6f7b6 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEM2WTRANSFORMER_H +#define ELECTRICFUNCTIONMODELSVGITEM2WTRANSFORMER_H + +/*************两绕组变压器***********/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItem2wTransformer :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItem2wTransformer(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItem2wTransformer(); + 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/electricFunctionModelSvgItem3wTransformer.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h new file mode 100644 index 0000000..3b7bdad --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEM3WTRANSFORMER_H +#define ELECTRICFUNCTIONMODELSVGITEM3WTRANSFORMER_H + +/*************三绕组变压器***********/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItem3wTransformer :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItem3wTransformer(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItem3wTransformer(); + 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/electricFunctionModelSvgItemBus.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h new file mode 100644 index 0000000..728c32e --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h @@ -0,0 +1,23 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMBUS_H +#define ELECTRICFUNCTIONMODELSVGITEMBUS_H + +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemBus :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemBus(const QRect &rect, QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemBus(); + virtual void setImage_1(QFileInfo) override; + void addPort(); +public: + virtual void updateConnectData() override; +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + virtual void updateHandles() override; +private: + void initial(); +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h new file mode 100644 index 0000000..1911038 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h @@ -0,0 +1,22 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMCB_H +#define ELECTRICFUNCTIONMODELSVGITEMCB_H + +/*****************断路器*******************/ +#include "electricFunctionModelSvgItem.h" + + +class ElectricFunctionModelSvgItemCB :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemCB(const QRect &rect, bool genNewPort = true,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemCB(); + virtual void setImage_1(QFileInfo) override; +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + virtual void updateHandles() override; +private: + void initial(); +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h new file mode 100644 index 0000000..67cd8f2 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h @@ -0,0 +1,21 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMCT_H +#define ELECTRICFUNCTIONMODELSVGITEMCT_H + +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemCT :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemCT(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemCT(); + void setItemType(int n){_itemType = n;} +private: + void initial(); +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + + int _itemType = 0; //1三相0零相 +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h new file mode 100644 index 0000000..75d252c --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMCABLEEND_H +#define ELECTRICFUNCTIONMODELSVGITEMCABLEEND_H + +/*****************电缆端*******************/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemCableEnd :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemCableEnd(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemCableEnd(); + 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/electricFunctionModelSvgItemCableTer.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.h new file mode 100644 index 0000000..d20fb52 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMCABLETER_H +#define ELECTRICFUNCTIONMODELSVGITEMCABLETER_H + +/*****************电缆出线套筒*******************/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemCableTer :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemCableTer(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemCableTer(); + 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/electricFunctionModelSvgItemDS.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.h new file mode 100644 index 0000000..cbe248b --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.h @@ -0,0 +1,19 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMDS_H +#define ELECTRICFUNCTIONMODELSVGITEMDS_H + +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemDS :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemDS(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemDS(); + 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/electricFunctionModelSvgItemDTEDS.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.h new file mode 100644 index 0000000..bdf6f2c --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMDTEDS_H +#define ELECTRICFUNCTIONMODELSVGITEMDTEDS_H + +/**********双掷接地隔离开关*********/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemDTEDS:public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemDTEDS(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemDTEDS(); + virtual void setImage_1(QFileInfo) override; +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + void inital(); +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h new file mode 100644 index 0000000..44ead31 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h @@ -0,0 +1,19 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMES_H +#define ELECTRICFUNCTIONMODELSVGITEMES_H + +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemES :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemES(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemES(); + 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/electricFunctionModelSvgItemFES.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.h new file mode 100644 index 0000000..1e77f9e --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.h @@ -0,0 +1,19 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMFES_H +#define ELECTRICFUNCTIONMODELSVGITEMFES_H + +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemFES :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemFES(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemFES(); + 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/electricFunctionModelSvgItemLA.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.h new file mode 100644 index 0000000..443e4eb --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMLA_H +#define ELECTRICFUNCTIONMODELSVGITEMLA_H + +/***********避雷器************/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemLA :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemLA(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemLA(); + 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/electricFunctionModelSvgItemPI.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h new file mode 100644 index 0000000..4a3ed29 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h @@ -0,0 +1,20 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMPI_H +#define ELECTRICFUNCTIONMODELSVGITEMPI_H + +/*************带点指示器***************/ +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemPI :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemPI(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemPI(); + 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/electricFunctionModelSvgItemPT.h b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h new file mode 100644 index 0000000..63d5ce2 --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h @@ -0,0 +1,21 @@ +#ifndef ELECTRICFUNCTIONMODELSVGITEMPT_H +#define ELECTRICFUNCTIONMODELSVGITEMPT_H + +#include "electricFunctionModelSvgItem.h" + +class ElectricFunctionModelSvgItemPT :public ElectricFunctionModelSvgItem +{ + Q_OBJECT +public: + ElectricFunctionModelSvgItemPT(const QRect &rect,QGraphicsItem *parent = 0); + virtual ~ElectricFunctionModelSvgItemPT(); + void setItemType(int n){_itemType = n;} +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +private: + void initial(); + + int _itemType = 0; //1星型 0三角 +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h b/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h new file mode 100644 index 0000000..384d3cd --- /dev/null +++ b/diagramCavas/include/graphicsItem/functionModelItem/graphicsFunctionModelItem.h @@ -0,0 +1,41 @@ +#ifndef GRAPHICSFUNCTIONMODELITEM_H +#define GRAPHICSFUNCTIONMODELITEM_H + +#include "../graphicsBaseItem.h" + +class GraphicsFunctionModelItem : public GraphicsProjectModelItem //功能模item +{ + Q_OBJECT +public: + GraphicsFunctionModelItem(QGraphicsItem *parent); + virtual ~GraphicsFunctionModelItem(); +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +}; + + +class GraphicsFunctionModelGroup : public GraphicsFunctionModelItem //功能模group +{ + Q_OBJECT +public: + GraphicsFunctionModelGroup(QGraphicsItem *parent); + virtual ~GraphicsFunctionModelGroup(); + virtual void addItem(GraphicsFunctionModelItem* item); + virtual void updateLayout(); + virtual void setLayout(int n) {m_direction = n;} + virtual void setGroupType(int n) {_groupType = n;} + virtual int getGroupType() {return _groupType;} + virtual void setSpacing(qreal spacing) { + if (m_spacing != spacing) { + m_spacing = spacing; + updateLayout(); + } + } + QRectF updateBoundRect(); +protected: + QList m_childItems; + int m_direction = 1; //组内布局,0横1纵 + int m_spacing = 0; //间距 + int _groupType = 0; //组类型,0联合(子item独立连接),1聚合(子item仅作展示) +}; +#endif diff --git a/diagramCavas/include/graphicsItem/graphicsBaseItem.h b/diagramCavas/include/graphicsItem/graphicsBaseItem.h new file mode 100644 index 0000000..68dfa44 --- /dev/null +++ b/diagramCavas/include/graphicsItem/graphicsBaseItem.h @@ -0,0 +1,705 @@ +#ifndef GRAPHICSBASEITEM_H +#define GRAPHICSBASEITEM_H + +#include "itemControlHandle.h" +//#include "global.h" +#include "common/core_model/types.h" +#include "common/core_model/topology.h" +#include "common/frontend/graphics_items.h" +#include "common/frontend/monitor_item.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "propertyType/dataSourceType.h" +//#include "graphicsItem/itemPort.h" + +class FixedPortsModel; + +enum ShapeType +{ + T_undefined, + T_item, + T_group +}; + +enum ItemState +{ + S_normal = 0, + S_prepareConnect, +}; + + +//基类采用模板形式,QGraphicsItem是默认值,也可以是别的类型,比如QGraphicsItemGroup,这样不同的基类继承可以共用一些高层的行为定义 +template +class AbstractShapeType : public BaseType +{ +public: + explicit AbstractShapeType(QGraphicsItem *parent = 0) + : BaseType(parent) + { + m_type = T_undefined; + m_pen = QPen(Qt::NoPen); + m_brush = QBrush(QColor(rand() % 32 * 8, rand() % 32 * 8, rand() % 32 * 8)); + m_dWidth = m_dHeight = 0; + m_pOperationCopy = nullptr; + m_dSyncRotationByParent = 0.0; + } + + AbstractShapeType(const AbstractShapeType& obj) + { + m_type = T_undefined; + m_pen = QPen(Qt::NoPen); + m_brush = QBrush(QColor(rand() % 32 * 8, rand() % 32 * 8, rand() % 32 * 8)); + m_dWidth = m_dHeight = 0; + m_pOperationCopy = nullptr; + m_dSyncRotationByParent = 0.0; + + m_itemId = obj.m_itemId; + m_type = obj.m_type; + m_pen = obj.m_pen; + m_brush = obj.m_brush; + m_dWidth = obj.m_dWidth; + m_dHeight = obj.m_dHeight; + m_boundingRect = obj.m_boundingRect; + m_Itemtype = obj.m_Itemtype; + m_movingIniPos = obj.m_movingIniPos; + } + + virtual ~AbstractShapeType() + { + + } + +public: + virtual ShapeType getType() {return m_type;} + virtual QUuid itemId() const {return m_itemId;} + virtual void setItemId(QUuid n){m_itemId = n;} + + QPen pen() { return m_pen; } + void setPen(const QPen &pen) { m_pen = pen; } + QColor penColor() { return m_pen.color(); } + void setPenColor(const QColor &color) { m_pen.setColor(color); } + + QBrush brush() { return m_brush; } + void setBrush(const QBrush &brush) { m_brush = brush; } + QColor brushColor() { return m_brush.color(); } + void setBrushColor(const QColor &color) { m_brush.setColor(color); } + + double width() { return m_dWidth; } + void setWidth(double width) + { + m_dWidth = width; + updateCoordinate(); + } + + double height() { return m_dHeight; } + void setHeight(double height) + { + m_dHeight = height; + updateCoordinate(); + } + + //操作副本相关 + virtual void createOperationCopy() {} + virtual void removeOperationCopy() {} + virtual void moveOperationCopy(const QPointF&) {} + virtual void rotateOperationCopy(const double&) {} + + virtual void resize(int,double, double, const QPointF&) {} + virtual void move(const QPointF&) {} + virtual void editShape(int, const QPointF&) {} + + virtual void updateCoordinate() {} + + virtual void setBoundingRect(QRectF rec){m_boundingRect = rec;} + virtual QRectF boundingRect() const { return m_boundingRect; } + virtual QPainterPath shape() + { + QPainterPath path; + return path; + } + + virtual void syncRotationDataFromParent(const double&) {} + virtual double getSyncRotationDataFromParent() {return m_dSyncRotationByParent;} + virtual void setItemType(GraphicsItemType type){m_Itemtype = type;} + virtual GraphicsItemType getItemType() const {return m_Itemtype;} +protected: + QUuid m_itemId; + ShapeType m_type; + QPen m_pen; + QBrush m_brush; + double m_dWidth; + double m_dHeight; + QRectF m_boundingRect; + GraphicsItemType m_Itemtype; //显示类型 + + double m_dSyncRotationByParent; //父项(被加入到某一组)的旋转数据,因为加入某一组后,对该组进行旋转,自身的旋转数据不会同步更新 + QGraphicsPathItem* m_pOperationCopy; //图元移动和旋转时的操作副本 + QPointF m_movingIniPos; //移动副本开始移动初始 +}; + +class ItemPort; +class ModelProperty; +class PowerEntity; +class AbstractProperty; +class HandleText; + +typedef AbstractShapeType AbstractShape; + +class GraphicsNonStandardItem:public QObject, public AbstractShapeType //非标准图元,如间隔 +{ + Q_OBJECT +public: + GraphicsNonStandardItem(QGraphicsItem *parent = 0) + :AbstractShapeType(parent) + { + setFlag(QGraphicsItem::ItemIsSelectable, true); + } + virtual ~GraphicsNonStandardItem(){}; + virtual void setProperty(AbstractProperty* p){_property = p;} + virtual AbstractProperty* getProperty() {return _property;} + + bool containsPoint(const QPointF& pos) + { + QPointF localPos = mapFromScene(pos); + QPainterPath path = shape(); // 或更精确的计算 + return path.contains(localPos); + } +protected: + AbstractProperty* _property; +}; + +class GraphicsBaseItem :public QObject, public AbstractShapeType +{ + Q_OBJECT +public: + enum RotateAngle { + Angle_0 = 0, + Angle_90 = 90, + Angle_180 = 180, + Angle_270 = 270 + }; + Q_ENUM(RotateAngle); + + Q_PROPERTY(QString Name READ getName WRITE setName) + Q_PROPERTY(QPointF Position READ getPosition WRITE setPosition NOTIFY posChanged) + Q_PROPERTY(QRectF Size READ getSize WRITE setSize) + Q_PROPERTY(RotateAngle Rotation READ getRotateAngle WRITE setRotateAngle) + Q_PROPERTY(QFileInfo Image READ getImage_1 WRITE setImage_1) +public: + GraphicsBaseItem(QGraphicsItem *parent); + GraphicsBaseItem(const GraphicsBaseItem&); + virtual ~GraphicsBaseItem(); + virtual GraphicsBaseItem* clone() const = 0; +signals: + void itemRotated(GraphicsBaseItem*); + void posChanged(); +public: + virtual QString getName() const; + virtual void setName(QString); + virtual QPointF getPosition() const; + virtual void setPosition(QPointF); + virtual QRectF getSize() const; + virtual void setSize(QRectF); + virtual RotateAngle getRotateAngle() const; + virtual void setRotateAngle(RotateAngle); + virtual QFileInfo getImage_1() const; + virtual void setImage_1(QFileInfo); +public: + int addPort(PortState typ,QPointF vec,QString id = "",HandleType hType = T_lineInOut,PortPos pos = P_top,double dXPercent = 0,double dYPercent = 0); //新建,返回-1失败 + virtual void movePort(QString id,QPointF vec); //移动可动点 + virtual void setEntity(PowerEntity*); //设置当前图元的拓扑数据 + virtual PowerEntity* entity(); + virtual void setProperty(ModelProperty* p); + virtual ModelProperty* getProperty() {return _property;} + virtual void updateByProperty(){}; //使用data对象更新自己 + virtual void setItemChanged(bool b){_itemChanged = b;} + virtual bool itemChanged() const {return _itemChanged;} + virtual void renderSelectBackground(QPainter*); + virtual ItemPort* getPortById(QString) const; + virtual ItemPort* getPortPtr(int) const; + virtual ItemControlHandle* getHandlePtr(int) const; + virtual QMap getPorts() {return m_mapPort;} + //virtual GraphicsBaseItem* + virtual void initialPortsByDatabase(int nComponentTypeId); //从数据库初始化port信息,component_type中的id + virtual void setLastPoint(QPointF p) {m_lastPoint = p;} + virtual void setTouched(bool b){m_touched = b;} + virtual void setPosChanged(bool b){_posChanged = b;} + virtual bool getPosChanged() {return _posChanged;} + virtual void setMoveable(bool b){_bMove = b;} + virtual bool getMoveable(){return _bMove;} + virtual void addDynamicText(QString tag,QString para); //动态显示数据 + virtual void removeDynamicText(QString tag); //移除动态显示数据 + virtual void removeAllDynamicText(); + virtual bool hasDynamicText(const QString& tag); + virtual void setDynamicLayoutRadius(qreal radius); + virtual QMap getDynamicText() {return m_mapDynamicText;} + void setHandle(FixedPortsModel* p){_pHandle = p;} + virtual void img_1_selected(QString sMeta,QString sModel,QByteArray img){}; + virtual void img_2_selected(QString sMeta,QString sModel,QByteArray img){}; + virtual void img_3_selected(QString sMeta,QString sModel,QByteArray img){}; + + int collidesWithHandle(const QPointF& point) + { + if(m_vecHanle.isEmpty()) + return HandleTag::H_none; + for (auto& pHandle: m_vecHanle) + { + if (pHandle) + { + QPointF pt = pHandle->mapFromScene(point); + if(pHandle->contains(pt)) + { + if(pHandle->enable()) + return pHandle->getTag(); + } + } + } + return HandleTag::H_none; + } + + //handle相关 + virtual int handleCount() { return m_vecHanle.count(); } + virtual ItemControlHandle* getHandle(int nHandle) + { + ItemControlHandle* handle = nullptr; + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if (pHandle) + { + if(pHandle->getTag() == nHandle) + { + handle = pHandle; + return handle; + } + } + } + return handle; + } + virtual void setHandleVisible(bool bVisible) + { + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + if(pHandle->ifShow()) + { + if(bVisible) + pHandle->show(); + else + pHandle->hide(); + } + } + } + } + virtual void setHandleVisible(int tag,bool bVisible) + { + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + if(pHandle->getTag() == tag) + { + if(bVisible) + pHandle->show(); + else + pHandle->hide(); + break; + } + } + } + } + virtual void setHandleIfShow(int tag,bool bVisible) //是否参与显示判断 + { + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + if(pHandle->getTag() == tag) + { + pHandle->setIfShow(bVisible); + break; + } + } + } + } + virtual void setFunctionHandleIfShow(bool bVisible) //是否参与显示判断 + { + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + if(pHandle->getTag() < H_connect) + { + pHandle->setIfShow(bVisible); + } + } + } + } + virtual void setFunctionHandleEnaable(bool val) + { + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + if(pHandle->getTag() < H_connect) //设置port以外端点是否交互 + pHandle->setEnable(val); + } + } + } + virtual void setHandleEnaable(HandleTag tag,bool val) + { + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + if(pHandle->getTag() == tag) //设置端点是否交互 + { + pHandle->setEnable(val); + break; + } + } + } + } + virtual QPointF getSymmetricPointPos(int nHandle) //获取对称点的坐标位置,缩放的时候需要以对称点为锚点 + { + QPointF pt; + //handle的位置相对boundingRect会有一个向外的错位,因此直接采用bounddingRect的相应位置会更精准 + switch (nHandle) + { + case H_leftTop: + //pt = m_vecHanle.at(H_rightBottom - 1)->pos(); + pt = m_boundingRect.bottomRight(); + break; + case H_top: + //pt = m_vecHanle.at(H_bottom - 1)->pos(); + pt = QPointF(m_boundingRect.width() * 0.5, m_boundingRect.bottom()); + break; + case H_rightTop: + //pt = m_vecHanle.at(H_leftBottom - 1)->pos(); + pt = m_boundingRect.bottomLeft(); + break; + case H_right: + //pt = m_vecHanle.at(H_left - 1)->pos(); + pt = QPointF(m_boundingRect.left(), m_boundingRect.height() * 0.5); + break; + case H_rightBottom: + //pt = m_vecHanle.at(H_leftTop - 1)->pos(); + pt = m_boundingRect.topLeft(); + break; + case H_bottom: + //pt = m_vecHanle.at(H_top - 1)->pos(); + pt = QPointF(m_boundingRect.width() * 0.5, m_boundingRect.top()); + break; + case H_leftBottom: + //pt = m_vecHanle.at(H_rightTop - 1)->pos(); + pt = m_boundingRect.topRight(); + break; + case H_left: + //pt = m_vecHanle.at(H_right - 1)->pos(); + pt = QPointF(m_boundingRect.right(), m_boundingRect.height() * 0.5); + break; + default: + break; + } + + return pt; + } + virtual void updateHandles() + { + int nMargin = 5; + const QRectF& boundRect = this->boundingRect(); + + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if(pHandle) + { + switch (pHandle->getTag()) { + case H_leftTop: + pHandle->move(boundRect.x() - nMargin, boundRect.y() - nMargin); + break; + case H_top: + pHandle->move(boundRect.x() + boundRect.width() * 0.5, boundRect.y() - nMargin); + break; + case H_rightTop: + pHandle->move(boundRect.x() + boundRect.width() + nMargin, boundRect.y() - nMargin); + break; + case H_right: + pHandle->move(boundRect.x() + boundRect.width() + nMargin, boundRect.y() + boundRect.height() * 0.5 + nMargin); + break; + case H_rightBottom: + pHandle->move(boundRect.x() + boundRect.width() + nMargin, boundRect.y() + boundRect.height() + nMargin); + break; + case H_bottom: + pHandle->move(boundRect.x() + boundRect.width() * 0.5, boundRect.y() + boundRect.height()+ nMargin); + break; + case H_leftBottom: + pHandle->move(boundRect.x() - nMargin, boundRect.y() + boundRect.height() + nMargin); + break; + case H_left: + pHandle->move(boundRect.x() - nMargin, boundRect.y() + boundRect.height() * 0.5); + break; + case H_rotate_leftTop: + { + ItemControlHandle* handle = getHandle(H_leftTop); + if(handle) + { + int nSize = handle->getSize(); + QPointF pt = handle->pos(); + pHandle->move(pt.x() - nSize - 1, pt.y() - nSize - 1); + } + else + pHandle->setVisible(false); + + break; + } + case H_rotate_rightTop: + { + ItemControlHandle* handle = getHandle(H_rightTop); + if(handle) + { + int nSize = handle->getSize(); + QPointF pt = handle->pos(); + pHandle->move(pt.x() + nSize + 1, pt.y() - nSize - 1); + } + else + pHandle->setVisible(false); + + break; + } + case H_rotate_rightBottom: + { + ItemControlHandle* handle = getHandle(H_rightBottom); + if(handle) + { + int nSize = handle->getSize(); + QPointF pt = handle->pos(); + pHandle->move(pt.x() + nSize + 1, pt.y() + nSize + 1); + } + else + pHandle->setVisible(false); + + break; + } + case H_rotate_leftBottom: + { + ItemControlHandle* handle = getHandle(H_leftBottom); + if(handle) + { + int nSize = handle->getSize(); + QPointF pt = handle->pos(); + pHandle->move(pt.x() - nSize - 1, pt.y() + nSize + 1); + } + else + pHandle->setVisible(false); + + break; + } + default: + break; + } + } + } + + double dWidth = boundRect.width(); + double dHeight = boundRect.height(); + double dDiagonal = sqrt((dWidth* 0.5)*(dWidth * 0.5)+(dHeight*0.5)*(dHeight*0.5)); + double dParam = dWidth > dHeight ? (dDiagonal-dWidth*0.5):(dDiagonal-dHeight*0.5); + m_boundingRect_selected = boundRect.adjusted(-dParam-nMargin, -dParam-nMargin, dParam+nMargin, dParam+nMargin); + } + + void rearrangeDynamicItems(); +public slots: + void onUpdateData(); //data发送的更新通知 +protected: + void rearrangeDynamicText(); //重新设置动态数据的布局 + QList getAllComponentRects() const; //获取图所有碰撞矩形 + QList getObstacleShapes() const; //获取所有碰撞shape +protected: + FixedPortsModel* _pHandle; + ModelProperty* _property; + PowerEntity* _pEntity; //图元拓扑 + bool _itemChanged; //图元变化标志,判断是否需要保存 + QMap m_mapPort; //单独存放port + QMap m_mapDynamicText; //单独存放动态数据 + QMap m_vecHanle; + QPointF m_lastPoint; //鼠标上次点击位置 + bool m_touched; //被触碰状态 + QRectF m_boundingRect_selected; //选中矩形框 + bool _posChanged = false; //位置移动标志 + bool _bMove = true; //是否允许移动 + QString m_bgImagePath; //选定的背景图路径 + QStringList _itemImgIndex; //item图形索引列表 + +private: + qreal m_layoutRadius; // 布局半径 (动态数据使用) +}; + +class GraphicsBaseModelItem : public GraphicsBaseItem //基模item +{ + Q_OBJECT +public: + GraphicsBaseModelItem(QGraphicsItem *parent); + GraphicsBaseModelItem(const GraphicsBaseModelItem&); + virtual ~GraphicsBaseModelItem(); + virtual GraphicsBaseModelItem* clone() const override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + virtual void setMask(bool b){_stateMask = b;} + virtual bool getMask(){return _stateMask;} +protected: + virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange, const QVariant&) override; + bool _stateMask = true; //状态遮罩 +}; + +class GraphicsBaseModelGroup : public GraphicsBaseModelItem //基模group +{ + Q_OBJECT +public: + GraphicsBaseModelGroup(QGraphicsItem *parent); + GraphicsBaseModelGroup(const GraphicsBaseModelGroup&); + virtual ~GraphicsBaseModelGroup(); + virtual GraphicsBaseModelGroup* clone() const override; + virtual void addItem(GraphicsBaseModelItem* item); + virtual void updateLayout(); + virtual QRectF boundingRect() const override; + virtual void setLayout(int n) {_layout = n;} +protected: + QList m_childItems; + int _layout = 0; //组内布局,0横1纵 +}; + +class GraphicsProjectModelItem : public GraphicsBaseItem //工程模item +{ + Q_OBJECT +public: + Q_PROPERTY(DataSourceType DataSourceType READ getDataSourceType WRITE setDataSourceType) + +public: + GraphicsProjectModelItem(QGraphicsItem *parent); + GraphicsProjectModelItem(const GraphicsProjectModelItem&); + virtual ~GraphicsProjectModelItem(); + virtual GraphicsProjectModelItem* clone() const override; + + virtual void createOperationCopy() override; + virtual void removeOperationCopy() override; + virtual void moveOperationCopy(const QPointF&) override; + virtual void rotateOperationCopy(const double&) override; + virtual void syncRotationDataFromParent(const double&) override; + //多边形、线段等点选创建的对象需要的函数 + virtual void addPoint(const QPointF&) {} + virtual bool endDrawing() { return true; } + + + virtual void setLabelTag(const QString& name); //设置名字牌 + virtual QString getLabelTag() const; + virtual void setState(ItemState s){m_state = s;} + virtual void setBeginConnectPos(QPointF p){m_beginConnectPoint = p;} + virtual void setEndConnectPos(QPointF p){m_endConnectPoint = p;} + virtual void setLastPort(int n){_lastPort = n;} + virtual int getLastPort() const {return _lastPort;} + virtual void unbindProperty(); //断开图元与数据的绑定 + virtual void updateByProperty() override; //使用data对象更新自己 + virtual void updateConnectData(); //更新连接关系数据 + virtual void setModelName(QString sName){_modelName = sName;} + virtual QString getModelName() const {return _modelName;} + virtual void setupFinish(QVariant){} //设置完成后调用(如ct,pt) + virtual void updateItem(){}; //更新自身(如ct,pt) + virtual void updateTerPos(); //ct,pt等item大小变动后重新计算端点位置 + + virtual void setCurMonitorState(MonitorItemState sta) {_curMonitorState = sta;} //设置当前运行时模式 + virtual void setMonitorDisplayInfo(QMap info){_displaySetting = info;} + virtual void updateCurState(MonitorItemState e){ //更新当前显示模式(运行时) + if(ifStateExist(e)){ + _curMonitorState = e; + auto info = getInfoByState(e); + _curMonitorStateColor = QColor(info.sColor); + _curMonitorStateSvg = info.bytPicture; + _curMonitorStateEnable = info.bEnable; + } + } +public: + DataSourceType getDataSourceType() const; + void setDataSourceType(DataSourceType); +protected: + virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange, const QVariant&) override; + virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +signals: + void ifExist(QUuid id,const QString&,int type,GraphicsProjectModelItem*); +public slots: + void onEditNameFinish(const QString&); +protected: + bool ifStateExist(MonitorItemState sta){ //判断指定状态存在 + for(auto iter = _displaySetting.begin();iter != _displaySetting.end();++iter){ + if(iter.key().eState == sta){ + return true; + } + } + return false; + } + MonitorItemDisplayInfo getInfoByState(MonitorItemState sta){ //返回指定状态设置值 + for(auto iter = _displaySetting.begin();iter != _displaySetting.end();++iter){ + if(iter.key().eState == sta){ + return iter.value(); + } + } + return MonitorItemDisplayInfo(); + } +protected: + ItemState m_state; + QPointF m_beginConnectPoint; + QPointF m_endConnectPoint; + int _lastPort; //最后触碰的port + QString _modelName; //当前图元使用的模型名,用来在model中检索属性信息 + + MonitorItemState _curMonitorState; //当前运行时模式 + QColor _curMonitorStateColor; + QByteArray _curMonitorStateSvg; + bool _curMonitorStateEnable; + QMap _displaySetting; //显示设置 + + DataSourceType _sourceType; +}; + +class GraphicsProjectModelGroup : public GraphicsProjectModelItem //工程模group +{ + Q_OBJECT +public: + GraphicsProjectModelGroup(QGraphicsItem *parent); + GraphicsProjectModelGroup(const GraphicsProjectModelGroup&); + virtual ~GraphicsProjectModelGroup(); + virtual GraphicsProjectModelGroup* clone() const override; + virtual void addItem(GraphicsProjectModelItem* item); + virtual void updateLayout(); + //virtual QRectF boundingRect() const override; + virtual void setLayout(int n) {m_direction = n;} + virtual void setGroupType(int n) {_groupType = n;} + virtual int getGroupType() {return _groupType;} + virtual void setSpacing(qreal spacing) { + if (m_spacing != spacing) { + m_spacing = spacing; + updateLayout(); + } + } + QRectF updateBoundRect(); +protected: + QList m_childItems; + int m_direction = 1; //组内布局,0横1纵 + int m_spacing = 0; //间距 + int _groupType = 0; //组类型,0联合(子item独立连接),1聚合(子item仅作展示) +}; +#endif diff --git a/diagramCavas/include/graphicsItem/graphicsItemGroup.h b/diagramCavas/include/graphicsItem/graphicsItemGroup.h new file mode 100644 index 0000000..85ecdfd --- /dev/null +++ b/diagramCavas/include/graphicsItem/graphicsItemGroup.h @@ -0,0 +1,38 @@ +#ifndef GRAPHICSITEMGROUP_H +#define GRAPHICSITEMGROUP_H + +#include "graphicsBaseItem.h" + + +class GraphicsItemGroup : public QObject, public AbstractShapeType +{ + Q_OBJECT + +public: + GraphicsItemGroup(QGraphicsItem *parent = 0); + virtual ~GraphicsItemGroup(); + + void resize(int,double, double, const QPointF&); + void updateCoordinate(); + + void createOperationCopy(); + void removeOperationCopy(); + void moveOperationCopy(const QPointF&); + void rotateOperationCopy(const double&); + + void addItems(const QList&); + QList getItems() {return m_listItem;} + +protected: + virtual QPainterPath shape(); + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent*); + virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange, const QVariant&); + virtual void syncRotationDataFromParent(const double&); + +private: + QRectF m_lastBoudingRect; //记录上一时刻的boundingRect + QList m_listItem; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/graphicsPolygonItem.h b/diagramCavas/include/graphicsItem/graphicsPolygonItem.h new file mode 100644 index 0000000..1fc9ab7 --- /dev/null +++ b/diagramCavas/include/graphicsItem/graphicsPolygonItem.h @@ -0,0 +1,33 @@ +#ifndef GRAPHICSPOLYGONITEM_H +#define GRAPHICSPOLYGONITEM_H + +#include "graphicsBaseItem.h" + +class GraphicPolygonItem : public GraphicsProjectModelItem +{ +public: + GraphicPolygonItem(QGraphicsItem *parent = 0); + virtual ~GraphicPolygonItem(); + + void resize(int,double, double, const QPointF&); + void updateCoordinate(); + void move(const QPointF&); + void editShape(int, const QPointF&); + + void addPoint(const QPointF&); + bool endDrawing(); + QPolygonF getPoints(void) { return m_points; } + +protected: + virtual QPainterPath shape(); + virtual QRectF boundingRect(); + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + +private: + virtual void updateHandles(); + + QPolygonF m_lastPoints; //记录上一时刻的点集数据,用于缩放等操作 + QPolygonF m_points; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/handleRect.h b/diagramCavas/include/graphicsItem/handleRect.h new file mode 100644 index 0000000..0bc6787 --- /dev/null +++ b/diagramCavas/include/graphicsItem/handleRect.h @@ -0,0 +1,19 @@ +#ifndef HANDLERECT_H +#define HANDLERECT_H + +#include "graphicsItem/itemControlHandle.h" + +class HandleRect : public ItemControlHandle +{ + Q_OBJECT +public: + HandleRect(QGraphicsItem *parent); + virtual ~HandleRect(); +protected: + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + virtual QRectF boundingRect() const override; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/handleText.h b/diagramCavas/include/graphicsItem/handleText.h new file mode 100644 index 0000000..e7fcf06 --- /dev/null +++ b/diagramCavas/include/graphicsItem/handleText.h @@ -0,0 +1,47 @@ +#ifndef HANDLETEXT_H +#define HANDLETEXT_H + +#include +#include "graphicsItem/itemControlHandle.h" + +int const TEXT_WIDTH = 100; +int const TEXT_HEIGHT = 40; + +class QGraphicsProxyWidget; + +class HandleText : public ItemControlHandle +{ + Q_OBJECT +public: + HandleText(QGraphicsItem *parent); + virtual ~HandleText(); + + virtual void setText(QString) override; + virtual QString getText() const override; + void creatEditor(); //创建editor编辑文本 + void setEditable(bool b){_editable = b;} + void setIndex(int n) {_nIndex = n;} + int getIndex(){return _nIndex;} + void setTagName(QString s){_sTagName = s;} + QString getTagName(){return _sTagName;} + void setPara(QString s){_sPara = s;} + QString getPara(){return _sPara;} + void setType(int n) {_type = n;} +signals: + void editFinish(const QString&); +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + virtual QRectF boundingRect() const override; +private: + QString _text; + QFont _font; + QRectF _boundingRect; + QGraphicsProxyWidget* _proxy; + bool _editable; + int _nIndex; + QString _sTagName; //监控数据的tag + QString _sPara; //监控数据的查询参数 + int _type; //0normal 1dynamic +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/itemControlHandle.h b/diagramCavas/include/graphicsItem/itemControlHandle.h new file mode 100644 index 0000000..ff30d7e --- /dev/null +++ b/diagramCavas/include/graphicsItem/itemControlHandle.h @@ -0,0 +1,54 @@ +#ifndef ITEMCONTROLHANDLE_H +#define ITEMCONTROLHANDLE_H + +#include +//#include "global.h" +#include "common/core_model/topology.h" + +const int HNDLE_SIZE = 8; + +class GraphicsBaseItem; + +class ItemControlHandle : public QObject,public QGraphicsItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsItem) +public: + ItemControlHandle(QGraphicsItem *parent); + virtual ~ItemControlHandle(); + +public: + virtual int getSize(); + virtual void move(double, double); + + virtual void setText(QString); + virtual QString getText() const; + + void setType(HandleType ht) { m_type = ht; } + HandleType getType() { return m_type; } + + void setTag(int ht) { m_tag = ht; } + int getTag() { return m_tag; } + + void setEnable(bool b){m_enable = b;} + bool enable(){return m_enable;} + + void setIfShow(bool b){m_ifShow = b;} + bool ifShow(){return m_ifShow;} + + GraphicsBaseItem* getParentPtr() const {return _parent;} + void setParent(GraphicsBaseItem* p) {_parent = p;} +protected: + + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; +protected: + HandleType m_type; + int m_tag; + bool m_enable; + bool m_ifShow; + GraphicsBaseItem* _parent; +}; + +#endif diff --git a/diagramCavas/include/graphicsItem/itemPort.h b/diagramCavas/include/graphicsItem/itemPort.h new file mode 100644 index 0000000..a07397f --- /dev/null +++ b/diagramCavas/include/graphicsItem/itemPort.h @@ -0,0 +1,46 @@ +#ifndef ITEMPORT_H +#define ITEMPORT_H + +#include "graphicsItem/handleRect.h" + +class ElectricFunctionModelConnectLineItem; + +class ItemPort : public HandleRect +{ + Q_OBJECT + +public: + ItemPort(QGraphicsItem *parent,QString uuid = ""); + virtual ~ItemPort(); + +public: + void setPortPos(PortPos p){_pos = p;} + PortPos portPos() const {return _pos;} + void setConnect(ElectricFunctionModelConnectLineItem* ptr){_ptr = ptr;} + void disConnect(){_ptr = nullptr;} + bool connected() const {return _ptr == nullptr?false:true;} + ElectricFunctionModelConnectLineItem* getConnectPtr() const {return _ptr;} + void setId(const QString& id){_uuid = id;} + QString getId() {return _uuid;} + void setName(const QString& str) {_name = str;} + QString getName() const {return _name;} + void setSourcePortId(const QString& id) {_sourcePortId = id;} + QString getSourcePortId() {return _sourcePortId;} + void setXPercent(double d) {_dXPercent = d;} + void setYPercent(double d) {_dYPercent = d;} + double getXPercent(){return _dXPercent;} + double getYPercent(){return _dYPercent;} +protected: + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; + +private: + QString _uuid; + QString _name; + PortPos _pos; + ElectricFunctionModelConnectLineItem* _ptr; + QString _sourcePortId; //被哪个port生成 + double _dXPercent = 0.0; //横向相对位置(0-1)固定端点 + double _dYPercent = 0.0; //纵向相对位置(0-1)固定端点 +}; + +#endif diff --git a/diagramCavas/include/instance/dataAccessor.h b/diagramCavas/include/instance/dataAccessor.h new file mode 100644 index 0000000..6f71f2d --- /dev/null +++ b/diagramCavas/include/instance/dataAccessor.h @@ -0,0 +1,31 @@ +#ifndef DATAACCESSOR_H +#define DATAACCESSOR_H + +/*********中转、处理网络数据*********/ +#include +#include +#include + +class DiagramCavas; + +class DataAccessor : public QObject +{ + Q_OBJECT +public: + DataAccessor(QObject *parent = nullptr); + ~DataAccessor(); + + void setParent(DiagramCavas* p) {_parentCavas = p;} + QMap> getTargetData(QStringList); //获取指定名称的值 +public slots: + void onReceiveHttpData(const QString& sType,const QVariant& data); + void onReceiveWebsocketData(const QVariant& data); +private: + QString removeAfterStreamBySplit(const QString& url); //手动处理websocket的config +private: + QMap> _realTimeData; //实时数据缓存 todo:自动清理 + mutable QMutex m_mutex; + DiagramCavas* _parentCavas; +}; + +#endif diff --git a/diagramCavas/include/itemPropertyDlg.h b/diagramCavas/include/itemPropertyDlg.h new file mode 100644 index 0000000..4e0de5d --- /dev/null +++ b/diagramCavas/include/itemPropertyDlg.h @@ -0,0 +1,58 @@ +#ifndef ITEMPROPERTYDLG_H +#define ITEMPROPERTYDLG_H + +#include +#include +#include +#include +//#include "global.h" +#include "common/backend/project_model.h" +#include "common/core_model/data_transmission.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class itemPropertyDlg; } +QT_END_NAMESPACE + +class GraphicsFunctionModelItem; +class FixedPortsModel; + +class ItemPropertyDlg : public QDialog +{ + Q_OBJECT + +public: + ItemPropertyDlg(QWidget *parent = nullptr); + ~ItemPropertyDlg(); + + void initial(); + void loadGroupButton(QMap); //加载属性组列表 + void createGroupView(const QString&); //创建属性页 + void showDlg(ModelDataInfo,QUuid,GraphicsFunctionModelItem*); //显示属性页面 + void setModelController(FixedPortsModel* p) {_curModelController = p;} + auto getModelController() {return _curModelController;} + auto getCurItem() {return _curItem;} + +public slots: + void onOkClicked(); + void onCancelClicked(); + void onGroupSelected(const QString&); + + void onHttpDataUpdated(HttpRecommandInfo); //更新推荐列表 +private: + Ui::itemPropertyDlg *ui; + + QVBoxLayout* layout_; + QButtonGroup* btnGroup_; + QMap btnMap_; + + QMap groupViews_; //stack中存储的属性页 + QMap groupInfo_; //属性组结构信息 + QMap groupValue_; //属性数据 + QUuid curUuid_; //当前显示对象的uuid + QString _curModel; //当前模型名 + GraphicsFunctionModelItem* _curItem; + FixedPortsModel* _curModelController; + QString _curGroup; //当前属性组 +}; + +#endif diff --git a/diagramCavas/include/loadMonitorPageDlg.h b/diagramCavas/include/loadMonitorPageDlg.h new file mode 100644 index 0000000..0d55cb9 --- /dev/null +++ b/diagramCavas/include/loadMonitorPageDlg.h @@ -0,0 +1,36 @@ +#ifndef LOADMONITORPAGEDLG_H +#define LOADMONITORPAGEDLG_H + +/*******************加载运行时*********************/ +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class loadMonitorPageDlg; } +QT_END_NAMESPACE + +struct DiagramInfo; + +class LoadMonitorPageDlg : public QDialog +{ + Q_OBJECT +public: + LoadMonitorPageDlg(QWidget *parent = nullptr); + ~LoadMonitorPageDlg(); + + void initial(); + void updateItems(QString,QPair); + void clearItems(); +signals: + void monitorSelected(DiagramInfo); +public slots: + void onOkClicked(); + void onCancelClicked(); +private: + QStandardItem* findTopLevelItem(const QString& name); //查找顶层项 +private: + Ui::loadMonitorPageDlg *ui; + QStandardItemModel* _pModel; +}; + +#endif diff --git a/diagramCavas/include/measureSettingDlg.h b/diagramCavas/include/measureSettingDlg.h new file mode 100644 index 0000000..d1b21e5 --- /dev/null +++ b/diagramCavas/include/measureSettingDlg.h @@ -0,0 +1,67 @@ +#ifndef MEASURESETTINGDLG_H +#define MEASURESETTINGDLG_H + +#include +#include +//#include "global.h" +#include "common/backend/project_model.h" +#include "common/core_model/data_transmission.h" +/******************************************************* + 间隔信息 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class measureSettingDlg; } +QT_END_NAMESPACE + +class BayInfoDlg; +class BayMeasureDlg; + +class MeasureSettingDlg : public QDialog +{ + Q_OBJECT +public: + MeasureSettingDlg(QWidget *parent = nullptr); + ~MeasureSettingDlg(); + + void initial(); + void setParentType(int n) {_nParentType = n;} + void setBayComponent(BayInfoDlg* p) {_pBayComponent = p;} + void setBayMeasure(BayMeasureDlg* p) {_pBayMeasure = p;} + void showDlg(int type,PropertyStateInfo,bool isDouble = false); + void showDlg(MeasurementInfo,PropertyStateInfo,bool isDouble = false,MeasurementInfo symmetryInfo = MeasurementInfo()); //修改 +public slots: + void onOkClicked(); + void onCancelClicked(); + void onTagChanged(const QString&); + void onNameChanged(const QString&); + + void onRuleIndexChanged(int); //通信方式改变时 + void onTypeIndexChanged(int); //量测类型改变时 + + void onAddParaClicked(); + void onDelParaClicked(); + void onEventStrategyChange(int); //事件策略改变 + + void onHttpDataUpdated(HttpRecommandInfo); //更新推荐对象列表(若有) +private: + void clearData(); + //void setDbCheckVisible(bool); //设置double勾选可见性 + void setDbTagVisible(bool); //设置double tag可见性 + QJsonObject safeToJsonObject(const QVariant& var); //var转json +private: + Ui::measureSettingDlg *ui; + BayInfoDlg* _pBayComponent; //component中的bay(元件父) + BayMeasureDlg* _pBayMeasure; //间隔父 + + QButtonGroup* _pEventStrategy; //事件策略组 + QButtonGroup* _pEventYXGroup; //遥信事件组 + bool _curMode; //0新增1修改 + + QMap _tempCtMap; + QMap _tempPtMap; + int _curComponentType; + bool _isDouble = false; + int _nParentType = 0; //所属父类型 0元件 1间隔 +}; + +#endif diff --git a/diagramCavas/include/monitorAttributeDlg.h b/diagramCavas/include/monitorAttributeDlg.h new file mode 100644 index 0000000..7b7717c --- /dev/null +++ b/diagramCavas/include/monitorAttributeDlg.h @@ -0,0 +1,32 @@ +#ifndef MONITORATTRIBUTEDLG_H +#define MONITORATTRIBUTEDLG_H + +#include +#include +#include + +class MonitorToolBox; +class MonitorAttributeGroupDlg; +class MonitorSideBarDlg; +struct MonitorItemAttributeInfo; + +class MonitorAttributeDlg : public QDialog +{ + Q_OBJECT +public: + MonitorAttributeDlg(QWidget *parent = nullptr); + ~MonitorAttributeDlg(); + + void initial(); + void generateAttributeGroups(QUuid); + MonitorSideBarDlg* getParent(){return _pParent;} + QUuid getCurId() {return _curId;} + void clearAllGroup(); +private: + QVBoxLayout* _pLayout; + MonitorToolBox* _pBox; + QUuid _curId; + MonitorSideBarDlg* _pParent; +}; + +#endif diff --git a/diagramCavas/include/monitorAttributeGroupDlg.h b/diagramCavas/include/monitorAttributeGroupDlg.h new file mode 100644 index 0000000..70aed02 --- /dev/null +++ b/diagramCavas/include/monitorAttributeGroupDlg.h @@ -0,0 +1,42 @@ +#ifndef MONITORATTRIBUTEGROUPDLG_H +#define MONITORATTRIBUTEGROUPDLG_H + +/**********************监控属性页中单组的具体内容*************************/ +#include +#include + +class MonitorAttributeDlg; +struct MonitorItemAttributeInfo; +class QChartView; +class QLabel; +class FixedPortsModel; +class MonitorDetailAttributeDlg; + +class MonitorAttributeGroupDlg : public QScrollArea +{ + Q_OBJECT +public: + MonitorAttributeGroupDlg(QWidget *parent = nullptr); + ~MonitorAttributeGroupDlg(); + + void initial(); + void createGroupView(QList,int nType = 0); //0小型 1中型 + void setParent(MonitorAttributeDlg* p) {_pParent = p;} + void setDetailParent(MonitorDetailAttributeDlg* p){_pDetailParent = p;} + void setCurMode(int n) {_curMode = n;} +public slots: + void updateData(); //使用数据更新当前界面 +private: + QWidget* createEditor(MonitorItemAttributeInfo,int nType = 0); //nType:0小 1中 + void updateLineChartData(QChartView* chartView, const QVector& data); + FixedPortsModel* getModelController(); + QUuid getCurUid(); +private: + QVBoxLayout* _layout; + MonitorAttributeDlg* _pParent; + MonitorDetailAttributeDlg* _pDetailParent; + QMap _curWidget; //当前控件 + int _curMode; //0简略模式 1详细模式 +}; + +#endif diff --git a/diagramCavas/include/monitorConfigDlg.h b/diagramCavas/include/monitorConfigDlg.h new file mode 100644 index 0000000..4897edd --- /dev/null +++ b/diagramCavas/include/monitorConfigDlg.h @@ -0,0 +1,58 @@ +#ifndef MONITORCONFIGDLG_H +#define MONITORCONFIGDLG_H + +/*******************监控配置界面**********************/ +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class monitorConfigDlg; } +QT_END_NAMESPACE + +class MonitorPanel; +class QItemSelection; +class QStandardItemModel; +class QStandardItem; +struct MonitorItemAttributeInfo; +class QCompleter; +class QStringListModel; + +class MonitorConfigDlg : public QDialog +{ + Q_OBJECT +public: + MonitorConfigDlg(QWidget *parent = nullptr); + ~MonitorConfigDlg(); + + void initial(); + void updateSelectedItems(); + void updateRecommandLst(QStringList); //更新当前推荐列表 +public slots: + void onOkClicked(); + void onCancelClicked(); + + void onTypeChanged(int); //展现类型改变 + void onItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); //选中设备事件 + void onPropertyCheckChanged(QStandardItem *item); //属性勾选改变信号 + void onPropertySelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); //属性选中事件 + + void onConnectParamChanged(const QString&); //连接参数变化事件(发送推荐查询) +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +private: + void savePropertyData(const QModelIndex ¤t,QUuid uid); //保存属性到uid的属性 + void loadPropertyData(const QModelIndex ¤t,QUuid uid); + void clearProperty(); +private: + Ui::monitorConfigDlg *ui; + MonitorPanel* _parent; + QMap> _tempConfig; + QStandardItemModel* _curItemModel; + QUuid _curUuid; + QStringList _curRecommandLst; //当前推荐列表 + QCompleter* _recommandCompleter; //自动填充器 + QStringListModel* _strLstModel; //自动填充模型 + +}; + +#endif diff --git a/diagramCavas/include/monitorDetailAttributeDlg.h b/diagramCavas/include/monitorDetailAttributeDlg.h new file mode 100644 index 0000000..b0afb08 --- /dev/null +++ b/diagramCavas/include/monitorDetailAttributeDlg.h @@ -0,0 +1,42 @@ +#ifndef MONITORDETAILATTRIBUTEDLG_H +#define MONITORDETAILATTRIBUTEDLG_H + +/******************监控属性详情页*********************/ + +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class monitorDetailAttributeDlg; } +QT_END_NAMESPACE + +class QGridLayout; +class MonitorAttributeGroupDlg; +class MonitorPanel; + +class MonitorDetailAttributeDlg : public QDialog +{ + Q_OBJECT +public: + MonitorDetailAttributeDlg(QWidget *parent = nullptr); + ~MonitorDetailAttributeDlg(); + + void initial(); + void generateAttributeGroups(QUuid uid); + void updateLayout(int columns); + MonitorPanel* getParent() {return _pParent;} + QUuid getCurId() {return _curId;} + void clearAllGroup(); +public slots: + void onCloseClicked(); + void onColChanged(const QString&); +private: + Ui::monitorDetailAttributeDlg *ui; + int _curColNum; //当前每行的列数 + QGridLayout* m_gridLayout; + QMap _curGroups; + MonitorPanel* _pParent; + QUuid _curId; +}; + +#endif diff --git a/diagramCavas/include/monitorDisplaySettingDlg.h b/diagramCavas/include/monitorDisplaySettingDlg.h new file mode 100644 index 0000000..809c30a --- /dev/null +++ b/diagramCavas/include/monitorDisplaySettingDlg.h @@ -0,0 +1,58 @@ +#ifndef MONITORDISPLAYSETTINGDLG_H +#define MONITORDISPLAYSETTINGDLG_H + +/*******************监控图元设置界面**********************/ +#include +//#include "global.h" +#include "common/frontend/monitor_item.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class monitorDisplaySettingDlg; } +QT_END_NAMESPACE + +class MonitorPanel; + +class MonitorDisplaySettingDlg : public QDialog +{ + Q_OBJECT +public: + MonitorDisplaySettingDlg(QWidget *parent = nullptr); + ~MonitorDisplaySettingDlg(); + + void initial(); + void showDlg(); +public slots: + void onSaveClicked(); + void onCancelClicked(); + void onIconSelectClicked(); + + void onCheckboxToggled(bool); + void onDeviceComboBoxChanged(const QString&); + void onStateComboBoxChanged(const QString&); + +private: + void loadSetting(MonitorItemTypeStruct type, MonitorItemStateStruct state); + bool saveCurrentSettingsWithIcon(); + void loadFirstStateSafely(); + + // 图片处理 + void clearStateDisplay(); + void updateIconDisplay(const QByteArray& svgData); + void clearIconDisplay(); + bool validateSvgData(const QByteArray& svgData) const; + + // 辅助方法 + bool validateCurrentDeviceState() const; +private: + Ui::monitorDisplaySettingDlg *ui; + MonitorPanel* _parent; + MonitorItemTypeStruct _curType; //当前类型 + MonitorItemStateStruct _curState; //当前状态 + QColor _curColor; + QString _curMeta; + QString _curModel; + QMap> _tempSetting; + +}; + +#endif diff --git a/diagramCavas/include/monitorItemPreviewDlg.h b/diagramCavas/include/monitorItemPreviewDlg.h new file mode 100644 index 0000000..d7f3a4c --- /dev/null +++ b/diagramCavas/include/monitorItemPreviewDlg.h @@ -0,0 +1,31 @@ +#ifndef MONITORITEMPREVIEWDLG_H +#define MONITORITEMPREVIEWDLG_H + +/*************运行时item显示预览*************/ + +#include +#include + +class MonitorItemPreviewDlg : public QWidget +{ + Q_OBJECT +public: + MonitorItemPreviewDlg(QWidget *parent = nullptr); + ~MonitorItemPreviewDlg(); + + void initial(); + void setSvgFile(const QByteArray &bytSvg); + void setColors(const QColor &color); + void setCurType(const QString& str) {m_curDeviceType = str;} + void clearSvg(); + QByteArray getCurSvg() {return _curSvg;} +protected: + void paintEvent(QPaintEvent *) override; +private: + QSvgRenderer m_renderer; + QByteArray _curSvg; + QColor m_Color; + QString m_curDeviceType; //当前设备类型,对应设备tag +}; + +#endif diff --git a/diagramCavas/include/monitorPanel.h b/diagramCavas/include/monitorPanel.h new file mode 100644 index 0000000..3ca5f47 --- /dev/null +++ b/diagramCavas/include/monitorPanel.h @@ -0,0 +1,73 @@ +#ifndef MONITORPANEL_H +#define MONITORPANEL_H + +#include +#include +#include "baseDrawingPanel.h" + +class PowerEntity; +class MonitorSideBarDlg; +class MonitorConfigDlg; +class MonitorDetailAttributeDlg; +class MonitorDisplaySettingDlg; + +class MonitorPanel : public BaseDrawingPanel +{ + Q_OBJECT +public: + MonitorPanel(PowerEntity* pEntity,QWidget *parent = nullptr,DiagramMode mode = DM_edit); + ~MonitorPanel(); + + void initial(); + virtual QJsonObject getDiagramInfo() override; //返回运行时的item信息 + virtual void loadNodes(QJsonObject obj) override; //加载图元信息 + virtual void saveNodes(int pageId) override; //保存到数据库 + + void setParentPage(const QString& str) {_sParentPage = str;} + QString getParentPage() {return _sParentPage;} + + void updateSelectedItems(QList,bool); + + QStandardItemModel* getLstModel() {return _itemListmodel;} + void initMonitorConfig(); //初始化参数设置(每个运行时可能不同) + void itemSelected(QUuid); //item选中事件 + void detailItemSelected(QUuid); //显示详细属性页 + + void initDisplayState(); //初始化显示状态参照表 + void initDisplaySetting(); //初始化显示状态设置 + + MonitorConfigDlg* getMonitorConfigDlg() {return _pConfigDlg;} +public: + //对层级关系的序列化与反序列化 + QJsonArray serializeRelationToJsonArray(const QList& data) const; + bool deserializeRelationFromJsonArray(const QJsonArray& jsonArray, QList& result); + //对para的序列化与反序列化 + QJsonArray serializeParaToJsonArray(const QMap>& data) const; + bool deserializeParaFromJsonArray(const QJsonArray& jsonArray,QMap>& result); + //对displaySetiing的序列化与反序列化 + QJsonArray serializeDisplayToJsonArray(const QMap>& data) const; + void deserializeDisplayFromJsonArray(const QJsonArray& jsonArray,QMap>& result); +public slots: + void onRunClicked(); + void onStopClicked(); + void onSaveClicked(); + void onConfigClicked(); + void onItemConfigClicked(); + void onConncecClicked(); +protected: + void closeEvent(QCloseEvent *closeEvent) override; +private: + void createToolBar(); +private: + QString _sParentPage; //派生自哪个工程 + QToolBar* _toolBar; + MonitorSideBarDlg* _sideBar; + MonitorConfigDlg* _pConfigDlg; + QStandardItemModel* _itemListmodel; //通用的序列模型(暂时) + MonitorDetailAttributeDlg* _detailAttributeDlg; + MonitorDisplaySettingDlg* _displaySettingDlg; + QMenu* _menuSetting; + QMenu* _menuFile; +}; + +#endif diff --git a/diagramCavas/include/monitorSelectedItemsDlg.h b/diagramCavas/include/monitorSelectedItemsDlg.h new file mode 100644 index 0000000..1e67fbc --- /dev/null +++ b/diagramCavas/include/monitorSelectedItemsDlg.h @@ -0,0 +1,28 @@ +#ifndef MONITORSELECTEDITEMS_H +#define MONITORSELECTEDITEMS_H + +#include +#include +#include + +class MonitorSideBarDlg; +class QVBoxLayout; + +class MonitorSelectedItemsDlg : public QDialog +{ + Q_OBJECT +public: + MonitorSelectedItemsDlg(QWidget *parent = nullptr); + ~MonitorSelectedItemsDlg(); + + void initial(); + void updateItems(); +public slots: + void onSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); //属性选中事件 +private: + QTreeView* _treeView; + MonitorSideBarDlg* _parent; + QVBoxLayout* _pLayout; +}; + +#endif diff --git a/diagramCavas/include/monitorSideBarDlg.h b/diagramCavas/include/monitorSideBarDlg.h new file mode 100644 index 0000000..62c6591 --- /dev/null +++ b/diagramCavas/include/monitorSideBarDlg.h @@ -0,0 +1,27 @@ +#ifndef MONITORSIDEBARDLG_H +#define MONITORSIDEBARDLG_H + +#include + +class MonitorSelectedItemsDlg; +class MonitorAttributeDlg; +class MonitorPanel; + +class MonitorSideBarDlg : public QDialog +{ + Q_OBJECT +public: + MonitorSideBarDlg(QWidget *parent = nullptr); + ~MonitorSideBarDlg(); + + void initial(); + MonitorPanel* getParent() {return _parent;} + MonitorSelectedItemsDlg* getItemsDlg() {return _itemsDlg;} + MonitorAttributeDlg* getAttributeDlg() {return _attributeDlg;} +private: + MonitorSelectedItemsDlg* _itemsDlg; + MonitorAttributeDlg* _attributeDlg; + MonitorPanel* _parent; +}; + +#endif diff --git a/diagramCavas/include/monitorToolBox.h b/diagramCavas/include/monitorToolBox.h new file mode 100644 index 0000000..53eb51f --- /dev/null +++ b/diagramCavas/include/monitorToolBox.h @@ -0,0 +1,22 @@ +#ifndef MONITORTOOLBOX_H +#define MONITORTOOLBOX_H + +#include + +class QVBoxLayout; +class MonitorToolBox : public QScrollArea +{ + Q_OBJECT + +public: + explicit MonitorToolBox(QWidget *parent = nullptr); + ~MonitorToolBox(); + void addWidget(const QString &title, QWidget *widget); + void removeWidget(const QString &title); + void removeAllWidget(); +private: + QWidget* _container; + QVBoxLayout *m_pContentVBoxLayout; + QMap m_mapWidget; +}; +#endif // MONITORTOOLBOX_H diff --git a/diagramCavas/include/monitorToolPage.h b/diagramCavas/include/monitorToolPage.h new file mode 100644 index 0000000..7978daf --- /dev/null +++ b/diagramCavas/include/monitorToolPage.h @@ -0,0 +1,30 @@ +#ifndef MONITORTOOLPAGE_H +#define MONITORTOOLPAGE_H + +#include + +class QFormLayout; +class QLabel; +class QPushButton; + + +class MonitorToolPage : public QWidget +{ + Q_OBJECT +public: + explicit MonitorToolPage(QWidget *parent = nullptr); + ~MonitorToolPage(); +public slots: + void addWidget(const QString &title, QWidget *widget); + void expand(); + void collapse(); +private slots: + void onPushButtonFoldClicked(); +private: + + bool m_bIsExpanded; + QLabel *m_pLabel; + QPushButton *m_pPushButtonFold; + QWidget *m_pContent; +}; +#endif // MONITORTOOLPAGE_HTOOLPAGE_H diff --git a/diagramCavas/include/powerConnection.h b/diagramCavas/include/powerConnection.h new file mode 100644 index 0000000..2008cab --- /dev/null +++ b/diagramCavas/include/powerConnection.h @@ -0,0 +1,41 @@ +#ifndef POWETCONNECTION_H +#define POWETCONNECTION_H +/**************************** + * 拓扑单元的连接线,表示连接关系 + * *************************/ +#include +#include +//#include "global.h" +#include "common/core_model/topology.h" + +// 连接线元数据(抽象连接关系) +class PowerConnection : public QObject { + Q_OBJECT +public: + PowerConnection(const QString& uuid,const QString& fromTerminalId,const QString& toTerminalId,const QString& fromId,const QString& toId,QObject* parent = nullptr); + + PowerConnection* clone(); + QString id() const {return m_uuid;} + QString fromTerminalId() const { return m_fromTerminal; } + QString toTerminalId() const { return m_toTerminal; } + QString fromComponent() const {return m_fromComponent;} + QString toComponent() const {return m_toComponent;} + QVariantMap properties() const { return m_properties; } + DataState state() {return m_state;} + void setState(DataState s) {m_state = s;} + void setId(QString sId){m_uuid = sId;} + + void setProperty(const QString& key, const QVariant& value); + + QJsonObject toJson() const; + +private: + QString m_uuid; + QString m_fromTerminal; + QString m_toTerminal; + QString m_fromComponent; + QString m_toComponent; + DataState m_state; + QVariantMap m_properties; +}; +#endif //POWETCONNECTION_H diff --git a/diagramCavas/include/powerEntity.h b/diagramCavas/include/powerEntity.h new file mode 100644 index 0000000..f1e5e18 --- /dev/null +++ b/diagramCavas/include/powerEntity.h @@ -0,0 +1,138 @@ +#ifndef POWERENTITY_H +#define POWERENTITY_H +/**************************** + * 电力实体类,实现拓扑层级的建立 grid-zone-station-diagram + * *************************/ +#include +#include +#include "powerTerminal.h" +#include "topologyManager.h" +//#include "global.h" + +class PowerTerminal; + +// 所有实体的基类(组合模式核心) +class PowerEntity :public QObject{ + Q_OBJECT +public: + PowerEntity(EntityType type, const QString& id, const QString& name) + : m_type(type), m_id(id), m_name(name) {} + + virtual ~PowerEntity() { + //qDeleteAll(m_children); + } + + virtual PowerEntity* clone(){ + // 1. 检查是否已经拷贝过 + if (TopologyManager::instance().findEntity(this->m_id,ModelFunctionType::BaseModel)) { //拷贝的子项也加入到clonedMap + return TopologyManager::instance().findEntity(this->m_id,ModelFunctionType::BaseModel); + } + + // 2. 创建当前对象的新副本 + PowerEntity* newEntity = TopologyManager::instance().createEntity(m_type,m_id,m_name,ModelFunctionType::BaseModel); + + // 3. 清空子列表(避免浅拷贝) + newEntity->m_children.clear(); + newEntity->m_terminals.clear(); + + // 4. 递归克隆子项 + for (PowerEntity* child : m_children) { + newEntity->addChild(child->clone()); + } + + // 5. 深拷贝所有 PowerTerminal + for (PowerTerminal* terminal : m_terminals) { + auto pTer = TopologyManager::instance().getTerminal(terminal->id(),ModelFunctionType::BaseModel); + if(pTer == nullptr){ //BaseModel中不存在则拷贝 + double dX = terminal->getPerX(); + double dY = terminal->getPerY(); + auto newTer = TopologyManager::instance().createTerminal(m_id,terminal->type(),terminal->name(),terminal->relativePosition(),terminal->id(),ModelFunctionType::BaseModel,dX,dY); + if(newTer){ + newTer->setPortLocate(terminal->getPortLocate()); + } + } + } + + return newEntity; + } + + // 添加/删除子元素 + void addChild(PowerEntity* child) { + if (child == this || getAllDescendants().contains(this)) { + qCritical() << "Detected cyclic parent-child relationship!"; + return; + } + if (child && !m_children.contains(child)) { + m_children.append(child); + child->m_parent = this; + } + } + + void removeChild(PowerEntity* child) { + if (child && m_children.removeOne(child)) { + child->m_parent = nullptr; + } + } + + // 获取属性 + EntityType type() const { return m_type; } + QString id() const { return m_id; } + QString name() const { return m_name; } + QList children() const { return m_children; } + PowerEntity* parent() const { return m_parent; } + virtual void setBlock(const QString& s){m_bBlock = s;} + virtual QString block(){return m_bBlock;} + // 递归查找 + QList getAllDescendants() const { + QList descendants; + for (PowerEntity* child : m_children) { + descendants.append(child); + descendants.append(child->getAllDescendants()); + } + return descendants; + } + + virtual QJsonObject toJson() const; +public: + // 接线点管理 + void addTerminal(PowerTerminal* terminal); + void removeTerminal(const QString& terminalId); + QList terminals() const { return m_terminals; } + PowerTerminal* findTerminal(const QString& terminalId) const; +signals: + void terminalAdded(PowerTerminal* terminal); + void terminalRemoved(const QString& terminalId); +protected: + QList m_terminals; +private: + EntityType m_type; + QString m_id; // 唯一标识符(可用UUID生成) + QString m_name; + QString m_bBlock; //所属区块(编辑时) + PowerEntity* m_parent = nullptr; + QList m_children; +}; + +// 组态图特殊属性(继承自PowerEntity) +class ConfigurationDiagram : public PowerEntity { +public: + ConfigurationDiagram(const QString& id, const QString& name) + : PowerEntity(EntityType::ConfigurationDiagram, id, name) {} + + // 可扩展图特有属性(如版本、创建时间等) +}; + +//电网区域划分 grid-zone-station +class PowerDivision : public PowerEntity { +public: + PowerDivision(EntityType type,const QString& id, const QString& name) + : PowerEntity(type, id, name) {} +}; + +//电力元件(拓扑) +class PowerComponent : public PowerEntity { +public: + PowerComponent(EntityType type,const QString& id, const QString& name) + : PowerEntity(type, id, name) {} +}; +#endif diff --git a/diagramCavas/include/powerTerminal.h b/diagramCavas/include/powerTerminal.h new file mode 100644 index 0000000..5c77586 --- /dev/null +++ b/diagramCavas/include/powerTerminal.h @@ -0,0 +1,71 @@ +#ifndef POWERTERMINAL_H +#define POWERTERMINAL_H +/**************************** + * 拓扑元件的接线口,拓扑关系的精确表示 + * *************************/ +#include +#include +#include +#include + +enum class TerminalType { + PowerInput, + PowerOutput, + PowerConnect, + ControlSignal, + ProtectiveGround, + NewTral +}; + +class PowerTerminal : public QObject { + Q_OBJECT +public: + explicit PowerTerminal(const QString& parentEntityId, + TerminalType type, + const QString& name, + const QPointF& relativePos = QPointF(), + const QString& uuid = "", + const double dPerX = 0.0, + const double dPerY = 0.0, + QObject* parent = nullptr); + + // 属性访问 + void setId(const QString& sId){m_id = sId;} + QString id() const { return m_id; } + QString parentEntityId() const { return m_parentEntityId; } + TerminalType type() const { return m_type; } + QString name() const { return m_name; } + QPointF relativePosition() const { return m_relativePosition; } + + // 设置相对位置 + void setRelativePosition(const QPointF& newPos); + + // 序列化 + QJsonObject toJson() const; + static PowerTerminal* fromJson(const QJsonObject& json, QObject* parent = nullptr); + + void setGenerateBy(const QString& sName) {m_generateBy = sName;} + QString getGenerateBy() {return m_generateBy;} + + void setPerX(double d) {m_dPerX = d;} + double getPerX() {return m_dPerX;} + void setPerY(double d) {m_dPerY = d;} + double getPerY() {return m_dPerY;} + void setPortLocate(int n) {m_portLocate = n;} + int getPortLocate() {return m_portLocate;} +signals: + void positionChanged(const QPointF& newPosition); + +private: + QString m_id; + const QString m_parentEntityId; + const TerminalType m_type; + QString m_name; + QPointF m_relativePosition; + QString m_generateBy; //被哪个Terminal生成 + double m_dPerX = 0.0; //横向百分比位置 + double m_dPerY = 0.0; //纵向百分比位置 + int m_portLocate = 0; //所处位置(上下左右) + +}; +#endif //POWERTERMINAL_H diff --git a/diagramCavas/include/projectDiagramNameInput.h b/diagramCavas/include/projectDiagramNameInput.h new file mode 100644 index 0000000..abc44c3 --- /dev/null +++ b/diagramCavas/include/projectDiagramNameInput.h @@ -0,0 +1,32 @@ +#ifndef PROJECTDIAGRAMNAMEINPUT_H +#define PROJECTDIAGRAMNAMEINPUT_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class projectDiagramNameInput; } +QT_END_NAMESPACE + +class FixedPortsModel; + +class ProjectDiagramNameInput : public QDialog +{ + Q_OBJECT +public: + ProjectDiagramNameInput(QWidget *parent = nullptr); + ~ProjectDiagramNameInput(); + + void initial(); + void setModel(FixedPortsModel* p) {_model = p;} +signals: + void onGenerateClicked(const QString&); +public slots: + void onOkClicked(); + void onCancelClicked(); + void onNameEdited(const QString&); +private: + Ui::projectDiagramNameInput *ui; + FixedPortsModel* _model; +}; + +#endif diff --git a/diagramCavas/include/projectIconSelectionDlg.h b/diagramCavas/include/projectIconSelectionDlg.h new file mode 100644 index 0000000..4cd8977 --- /dev/null +++ b/diagramCavas/include/projectIconSelectionDlg.h @@ -0,0 +1,21 @@ +#ifndef PROJECTICONSELECTIONDLG_H +#define PROJECTICONSELECTIONDLG_H + +#include +#include +#include +#include + +class ProjectIconSelectionDlg : public QDialog { + Q_OBJECT +public: + ProjectIconSelectionDlg(const QMap,QWidget* parent = nullptr); + QByteArray selectedSVG() const; + +private: + QListWidget* listWidget; + QMap svgMap; +}; + + +#endif diff --git a/diagramCavas/include/projectIconSetting.h b/diagramCavas/include/projectIconSetting.h new file mode 100644 index 0000000..453150d --- /dev/null +++ b/diagramCavas/include/projectIconSetting.h @@ -0,0 +1,37 @@ +#ifndef PROJECTICONSETTING_H +#define PROJECTICONSETTING_H + +/*********工程模使用的图标设置*********/ +#include + +class FixedPortsModel; +class GraphicsProjectModelItem; + +QT_BEGIN_NAMESPACE +namespace Ui { class projectIconSetting; } +QT_END_NAMESPACE + +class ProjectIconSetting : public QDialog +{ + Q_OBJECT +public: + ProjectIconSetting(QWidget *parent = nullptr); + ~ProjectIconSetting(); + + void showDlg(GraphicsProjectModelItem*); + void initial(); + void addItems(QMap); + void selectImage(int row); + void setController(FixedPortsModel* p){_controller = p;} +public slots: + void onOkClicked(); + void onCellClicked(int row,int col); +private: + Ui::projectIconSetting *ui; + QSize _iconSize; + FixedPortsModel* _controller; + QString _sMetaModel; + QString _sModel; +}; + +#endif diff --git a/diagramCavas/include/propertyContentDlg.h b/diagramCavas/include/propertyContentDlg.h new file mode 100644 index 0000000..85571ba --- /dev/null +++ b/diagramCavas/include/propertyContentDlg.h @@ -0,0 +1,32 @@ +#ifndef PROPERTYCONTENTDLG_H +#define PROPERTYCONTENTDLG_H + +#include +#include +#include +//#include "global.h" +#include "baseContentDlg.h" +/******************************************************* + 每个属性组单独的界面信息,动态生成后加入到itemPropertyDlg +********************************************************/ + +class PropertyContentDlg : public BaseContentDlg +{ + Q_OBJECT + +public: + PropertyContentDlg(QWidget *parent = nullptr); + ~PropertyContentDlg(); + + virtual void createGroupView(GroupStateInfo); //创建页面 + virtual QMap getPropertyValue(BaseProperty* = nullptr); //返回当前页面的属性值 + //void setPropertyValue(QMap); + virtual void setPropertyValue(QVariant); +protected: + QVBoxLayout* _layout; + QWidget* createEditor(PropertyStateInfo); //创建属性 +private: + QMap _curValue; +}; + +#endif diff --git a/diagramCavas/include/propertyType/CustomGadget.h b/diagramCavas/include/propertyType/CustomGadget.h new file mode 100644 index 0000000..2862458 --- /dev/null +++ b/diagramCavas/include/propertyType/CustomGadget.h @@ -0,0 +1,20 @@ +#ifndef CustomGadget_h__ +#define CustomGadget_h__ + +#include "CommonInclude.h" + +class QCustomGadget { + Q_GADGET + Q_CLASSINFO("LimitedDouble", "Min=0,Max=10") +public: + Q_PROPERTY_VAR(double, LimitedDouble) = 1; + Q_PROPERTY_VAR(QString, Desc) = "This is inline Gadget"; +}; + +static QDebug operator<<(QDebug debug, const QCustomGadget& gadget) { + return debug << gadget.LimitedDouble << gadget.Desc; +} + +Q_DECLARE_METATYPE(QSharedPointer); + +#endif // CustomGadget_h__ \ No newline at end of file diff --git a/diagramCavas/include/propertyType/CustomType.h b/diagramCavas/include/propertyType/CustomType.h new file mode 100644 index 0000000..7674a7e --- /dev/null +++ b/diagramCavas/include/propertyType/CustomType.h @@ -0,0 +1,18 @@ +#ifndef CustomType_h__ +#define CustomType_h__ + +#include +#include + +struct QCustomType { + unsigned int ArraySize = 0; + QVector Array; +}; + +static QDebug operator<<(QDebug debug, const QCustomType& it) { + return debug << it.Array; +} + +Q_DECLARE_METATYPE(QCustomType) + +#endif // CustomType_h__ diff --git a/diagramCavas/include/propertyType/PropertyTypeCustomization_CustomType.h b/diagramCavas/include/propertyType/PropertyTypeCustomization_CustomType.h new file mode 100644 index 0000000..3d847cf --- /dev/null +++ b/diagramCavas/include/propertyType/PropertyTypeCustomization_CustomType.h @@ -0,0 +1,12 @@ +#ifndef PropertyTypeCustomization_CustomType_h__ +#define PropertyTypeCustomization_CustomType_h__ + +#include "IPropertyTypeCustomization.h" + +class PropertyTypeCustomization_CustomType : public IPropertyTypeCustomization { +protected: + virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override; + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +}; + +#endif // PropertyTypeCustomization_CustomType_h__ diff --git a/diagramCavas/include/propertyType/dataSourceType.h b/diagramCavas/include/propertyType/dataSourceType.h new file mode 100644 index 0000000..6aa0bba --- /dev/null +++ b/diagramCavas/include/propertyType/dataSourceType.h @@ -0,0 +1,18 @@ +#ifndef DATASOURCETYPE_H +#define DATASOURCETYPE_H + +#include +#include + +struct DataSourceType { + QString sCode; + QString sPara; +}; + +static QDebug operator<<(QDebug debug, const DataSourceType& it) { + return debug << it.sCode <<":"< +#include + +class BaseDrawingPanel; + +class PannelColorGadget { + Q_GADGET +public: + Q_PROPERTY(QColor BackColor READ getBackColor WRITE setBackColor) + Q_PROPERTY(QColor GridColor READ getGridColor WRITE setGridColor) +public: + PannelColorGadget(BaseDrawingPanel*); + + QColor getBackColor() const; + void setBackColor(QColor); + QColor getGridColor() const; + void setGridColor(QColor); +private: + BaseDrawingPanel* _pPanel; +}; + +Q_DECLARE_METATYPE(QSharedPointer); + +#endif // PANNELCOLORGADGET_H diff --git a/diagramCavas/include/propertyType/propertyTypeCustomization_DataSourceType.h b/diagramCavas/include/propertyType/propertyTypeCustomization_DataSourceType.h new file mode 100644 index 0000000..c3d3885 --- /dev/null +++ b/diagramCavas/include/propertyType/propertyTypeCustomization_DataSourceType.h @@ -0,0 +1,16 @@ +#ifndef PROPERTYTYPECUSTOMIZATION_DATASOURCETYPE_H +#define PROPERTYTYPECUSTOMIZATION_DATASOURCETYPE_H + +#include "IPropertyTypeCustomization.h" + +class DataSourceDlg; + +class PropertyTypeCustomization_DataSourceType : public IPropertyTypeCustomization { +protected: + virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override; + virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override; +private: + DataSourceDlg* _pDlg = nullptr; +}; + +#endif // PROPERTYTYPECUSTOMIZATION_DATASOURCETYPE_H diff --git a/diagramCavas/include/ptExtraInfoDlg.h b/diagramCavas/include/ptExtraInfoDlg.h new file mode 100644 index 0000000..c1edd75 --- /dev/null +++ b/diagramCavas/include/ptExtraInfoDlg.h @@ -0,0 +1,42 @@ +#ifndef PTEXTRAINFODLG_H +#define PTEXTRAINFODLG_H + +#include +#include "baseContentDlg.h" +//#include "global.h" +/******************************************************* + 扩展信息界面 +********************************************************/ +QT_BEGIN_NAMESPACE +namespace Ui { class ptExtraInfoDlg; } +QT_END_NAMESPACE + +class BaseProperty; +class QButtonGroup; + +class PtExtraInfoDlg : public BaseContentDlg +{ + Q_OBJECT + +public: + PtExtraInfoDlg(QWidget *parent = nullptr); + virtual ~PtExtraInfoDlg(); + virtual void createGroupView(GroupStateInfo); + virtual QMap getPropertyValue(BaseProperty* = nullptr); //返回当前页面的属性值 + virtual void setPropertyValue(QVariant); +public slots: + void onAddClicked(); + void onTableCustomContextMenuRequested(const QPoint &pos); +protected: + void addTableRow(QString sRatioRange,QString sAccuracy,QString sVolume,QString sStar,double dRatio,bool bPolarity,int index = -1); +private: + void deleteRowWithReindex(int row); + void reorderMapAndUpdateIndices(int startRow); +private: + Ui::ptExtraInfoDlg *ui; + QMap _mapPT; + QButtonGroup* _stateGroup_pt; + int _count; +}; + +#endif diff --git a/diagramCavas/include/serializable.h b/diagramCavas/include/serializable.h new file mode 100644 index 0000000..935c8e0 --- /dev/null +++ b/diagramCavas/include/serializable.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class Serializable +{ +public: + virtual ~Serializable() = default; + + virtual QJsonObject save() const { return {}; } + + virtual void load(QJsonObject const & /*p*/) {} +}; diff --git a/diagramCavas/include/statusBar.h b/diagramCavas/include/statusBar.h new file mode 100644 index 0000000..ca3afc1 --- /dev/null +++ b/diagramCavas/include/statusBar.h @@ -0,0 +1,28 @@ +#ifndef STATUSBAR_H +#define STATUSBAR_H + +#include + +class QLabel; +class QPushButton; + +class StatusBar : public QStatusBar +{ + Q_OBJECT + +public: + explicit StatusBar(QWidget *parent = nullptr); + ~StatusBar(); + + void initial(); + void setButtonVisible(bool); +signals: + void generateDiagram(); +public slots: + void onScaleLevelChanged(double f); + void onGenerateClicked(); +private: + QLabel *m_pScaleLevel; + QPushButton *m_pButtonGenerate; +}; +#endif // STATUSBAR_H diff --git a/diagramCavas/include/structDataActionParaDlg.h b/diagramCavas/include/structDataActionParaDlg.h new file mode 100644 index 0000000..97107bb --- /dev/null +++ b/diagramCavas/include/structDataActionParaDlg.h @@ -0,0 +1,29 @@ +#ifndef STRUCTDATAACTIONPARADLG_H +#define STRUCTDATAACTIONPARADLG_H +/** + * 结构化数据展示中的量测事件action_para配置界面 + * */ +#include +#include + +class StructDataActionParaDlg : public QDialog { + Q_OBJECT + +public: + StructDataActionParaDlg(QWidget* parent = nullptr); + + void setAlarms(const QStringList &alarms); + QStringList alarms() const; +private slots: + void onAddClicked(); + void onDeleteClicked(); +private: + QListWidget *m_listWidget; + QLineEdit *m_editLine; + QPushButton *m_btnAdd; + QPushButton *m_btnDelete; + QPushButton *m_btnClear; + QPushButton *m_btnOk; + QPushButton *m_btnCancel; +}; +#endif diff --git a/diagramCavas/include/structDataCauseEditDlg.h b/diagramCavas/include/structDataCauseEditDlg.h new file mode 100644 index 0000000..f1f5813 --- /dev/null +++ b/diagramCavas/include/structDataCauseEditDlg.h @@ -0,0 +1,30 @@ +#ifndef STRUCTDATACAUSEEDITDLG_H +#define STRUCTDATACAUSEEDITDLG_H +/** + * 结构化数据展示中的量测事件cause配置界面 + * */ +#include +#include +#include +#include + +class StructDataCauseEditDlg : public QDialog { + Q_OBJECT + +public: + explicit StructDataCauseEditDlg(const QMap& initialData,QWidget* parent = nullptr); + + QMap getData() const; + void setData(const QMap& data); + + void setupUI(); + void updateUIFromData(); + void updateTotal(); +private: + QStringList m_availableKeys; + QMap m_checkBoxes; + QMap m_spinBoxes; + QDialogButtonBox* m_buttonBox; + QMap m_data; +}; +#endif diff --git a/diagramCavas/include/structDataMeasurementDelegate.h b/diagramCavas/include/structDataMeasurementDelegate.h new file mode 100644 index 0000000..478f328 --- /dev/null +++ b/diagramCavas/include/structDataMeasurementDelegate.h @@ -0,0 +1,60 @@ +#ifndef STRUCTDATAMEASUREMENTDELEGATE_H +#define STRUCTDATAMEASUREMENTDELEGATE_H +/** + * 结构预览ui的量测类型model + * */ +#include + +class StructDataSource; +struct MeasurementInfo; +struct ExtraProperty; +class QCompleter; + +class StructDataMeasurementDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + StructDataMeasurementDelegate(QObject* parent = nullptr); + + QWidget* createEditor(QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const override; + + void setEditorData(QWidget* editor, const QModelIndex& index) const override; + + void setModelData(QWidget* editor, QAbstractItemModel* model,const QModelIndex& index) const override; + + void updateEditorGeometry(QWidget* editor,const QStyleOptionViewItem& option,const QModelIndex& index) const override; + + QString displayText(const QVariant& value, const QLocale& locale) const override; + + // 提供工具提示 + QString displayText(const QVariant& value, const QLocale& locale,const QModelIndex& index) const; + + void setConnectParaCompleter(QCompleter* p) {_connectCompleter = p;} //设置连接参数输入的completer +public slots: + void onConnectParamChanged(const QString& str) const; +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +private: + QWidget* createConnectParaEditor(QWidget* parent) const; + + QWidget* createTextEditor(QWidget* parent) const; + + QWidget* createMeasurementTypeEditor(QWidget* parent) const; + QWidget* createSizeEditor(QWidget* parent) const; + QWidget* createSourceEditor(QWidget* parent) const; + QWidget* createNumberEditor(QWidget* parent) const; + + QWidget* createEnableEditor(QWidget* parent) const; + QWidget* createCauseYCEditor(QWidget* parent, const QModelIndex& index) const; //创建遥测cause输入对话框 + QWidget* createCauseYXEditor(QWidget* parent) const; //遥信cause + QWidget* createCommandEditor(QWidget* parent) const; //事件command + QWidget* createActionParaEditor(QWidget* parent) const; //事件actionpara + QString getParaType(const QModelIndex& index) const; //获取当前的协议方式 +private: + QCompleter* _connectCompleter = nullptr; //自动补全的completer +}; + +#endif diff --git a/diagramCavas/include/structDataMeasurementModel.h b/diagramCavas/include/structDataMeasurementModel.h new file mode 100644 index 0000000..33de5d6 --- /dev/null +++ b/diagramCavas/include/structDataMeasurementModel.h @@ -0,0 +1,81 @@ +#ifndef STRUCTDATAMEASUREMENTMODEL_H +#define STRUCTDATAMEASUREMENTMODEL_H +/** + * 结构预览ui的量测类型model + * */ +#include +#include "common/core_model/topology.h" + +class StructDataSource; +struct MeasurementInfo; + +class StructDataMeasurementModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum Columns { + ColName = 0, + ColTag = 1, + ColCode = 2, + ColSourceType = 3, + ColConnectPara = 4, + ColType = 5, + ColSize = 6, + ColSource = 7, + ColStation = 8, + ColEquipment = 9, + ColChannel = 10, + ColPacket = 11, + ColOffset = 12, + ColEnable = 13, //事件 + ColCause = 14, //原因 + ColCommand = 15, //动作 + ColParameters = 16, //参数 + ColumnCount + }; + + StructDataMeasurementModel(StructDataSource* dataManager, QObject* parent = nullptr); + + void setPropertyCodes(const QStringList& codes); + + void setProperties(const QVector& properties); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + QVector getDisplayedProperties() const; + + QStringList getDisplayedCodes() const; + + int findRowByCode(const QString& code) const; + + void refreshRow(const QString& code); + +private slots: + void onPropertyUpdated(const ExtraProperty& updatedProp, bool isNew); +signals: + void propertiesLoaded(int count); + void propertyModified(int row, const ExtraProperty& prop); +private: + ExtraProperty* getProperty(int displayRow) const; + QVariant getMeasurementData(const ExtraProperty& prop, int col, int role) const; + bool updateMeasurementData(MeasurementInfo* data, int col, const QVariant& value,int role); + QString getTypeText(int type) const; + QString getSourceText(int source) const; + int getSourceInt(QString) const; +private: + StructDataSource* m_dataManager = nullptr; + QStringList m_propertyCodes; +}; + +#endif diff --git a/diagramCavas/include/structDataPreviewDlg.h b/diagramCavas/include/structDataPreviewDlg.h new file mode 100644 index 0000000..a998bbd --- /dev/null +++ b/diagramCavas/include/structDataPreviewDlg.h @@ -0,0 +1,110 @@ +#ifndef STRUCTDATAPREVIEWDLG_H +#define STRUCTDATAPREVIEWDLG_H +/** + * *******结构化数据展示界面******* + **/ + +#include +#include "dataManager.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class structDataPreviewDlg; } +QT_END_NAMESPACE + +class QStandardItemModel; +class TitleBar; +class ExtraPropertyManager; +struct ExtraProperty; +class QStandardItem; +class QTreeView; +class QAbstractItemModel; +class StructDataSource; +class StructDataMeasurementModel; +class StructDataPropertyModel; +class StructDataPropertyDelegate; +class StructDataMeasurementDelegate; +class QStatusBar; +class QCompleter; +class QStringListModel; + +class StructDataPreviewDlg : public QDialog +{ + Q_OBJECT +public: + StructDataPreviewDlg(QWidget *parent = nullptr); + ~StructDataPreviewDlg(); + + void initial(); + void loadData(); + + void showMaximized(); + void showNormal(); + void setExtraPropertyManager(ExtraPropertyManager* p) {_pExtraProManager = p;} + void showDlg(); + QVector getGroupProperties(QStandardItem* groupItem); //获取属性组的所有属性 + QString getGroupSourceType(QStandardItem* groupItem); + void addItemToView(const ExtraProperty& property, + const QString& displayMode, // "name" 或 "tag" + QStandardItem* root, + QStandardItem* pItem); + void updateRecommandLst(QStringList); //更新当前推荐列表 + + void addLog(const QString &message); +public slots: + void onExitClicked(); + void onSaveClicked(); + + void onLevelButtonClicked(int nLevel); + void onTreeSelectionChanged(const QModelIndex& current, const QModelIndex& previous); + void onPropertyModified(int row, const ExtraProperty& prop); +protected: + void showEvent(QShowEvent *event) override; + void resizeEvent(QResizeEvent *event) override; +private slots: + void toggleMaximize(); +private: + void clearItems(); + QString getLevelType(int index); + void expandToLevel(QTreeView* treeView, int targetLevel); // 展开到指定层级 + void expandToLevelRecursive(QTreeView* treeView, + QAbstractItemModel* model, + const QModelIndex& parent, + int currentDepth, + int targetLevel); + int getNodeLevel(QAbstractItemModel* model, const QModelIndex& index); // 获取节点的层级(从存储的数据中读取) + int getDepthFromRoot(QAbstractItemModel* model, const QModelIndex& index); // 获取节点从根开始的深度 + QStandardItem* processGroupLevel(QStandardItem* componentItem,const ExtraProperty& property); // 处理group层级 + void processCategoryLevel(QStandardItem* groupItem,const ExtraProperty& property); // 处理category层级 + void updateCategoryProperties(QStandardItem* categoryItem,const ExtraProperty& property); // 更新category节点的属性列表 + void loadCategoryProperties(QStandardItem* categoryItem); + QVector getCategoryPropertiesFromDataManager(const QVariantMap& categoryData); + void setupPropertyTable(const QVector& properties,const QString& categoryName,const QString& groupName); //设置参量的model + void setupMeasurementTable(const QVector& properties,const QString& categoryName,const QString& groupName); //设置量测的model + void updateCategoryAfterPropertyModified(QStandardItem* categoryItem, const ExtraProperty& updatedProp); //回调更新节点 + void saveCurrentIfModified(); + void saveAll(); + void clearTableView(); + void setupPropertyColumns(); + void setupMeasurementColumns(); + void updateTableTitle(const QString& dataType, const QString& categoryName,const QString& groupName, int count); +private: + Ui::structDataPreviewDlg *ui; + QStandardItemModel* _treeModel; + TitleBar* m_titleBar; + QRect m_normalGeometry; // 记录正常状态的位置和大小 + ExtraPropertyManager* _pExtraProManager; //使用外部的manager + StructDataSource* m_dataSource; + bool m_currentModified = false; + QStandardItem* m_currentCategoryItem; //当前操作对象 + StructDataMeasurementModel* m_measurementTableModel; + StructDataPropertyModel* m_propertyTableModel; + StructDataPropertyDelegate* m_propertyDelegate; + StructDataMeasurementDelegate* m_measurementDelegate; + QStatusBar* m_statusBar; + + QStringList _curRecommandLst; //当前推荐列表 + QCompleter* _recommandCompleter; //自动填充器 + QStringListModel* _strLstModel; //自动填充模型 +}; + +#endif diff --git a/diagramCavas/include/structDataPropertyDelegate.h b/diagramCavas/include/structDataPropertyDelegate.h new file mode 100644 index 0000000..1c0b5fc --- /dev/null +++ b/diagramCavas/include/structDataPropertyDelegate.h @@ -0,0 +1,49 @@ +#ifndef STRUCTDATAPROPERTYDELEGATE_H +#define STRUCTDATAPROPERTYDELEGATE_H +/** + * 结构预览ui的参量类型代理 + * */ +#include + +class StructDataSource; +struct MeasurementInfo; +struct ExtraProperty; +class QCompleter; + +class StructDataPropertyDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + StructDataPropertyDelegate(QObject* parent = nullptr); + + QWidget* createEditor(QWidget* parent,const QStyleOptionViewItem& option,const QModelIndex& index) const override; + + void setEditorData(QWidget* editor, const QModelIndex& index) const override; + + void setModelData(QWidget* editor, QAbstractItemModel* model,const QModelIndex& index) const override; + + void updateEditorGeometry(QWidget* editor,const QStyleOptionViewItem& option,const QModelIndex& index) const override; + QString displayText(const QVariant& value, const QLocale& locale) const override; + + void setConnectParaCompleter(QCompleter* p) {_connectCompleter = p;} //设置连接参数输入的completer +private: + QWidget* createConnectParaEditor(QWidget* parent) const; + + QWidget* createDefaultValueEditor(QWidget* parent, const QModelIndex& index) const; + + QWidget* createLengthPrecisionEditor(QWidget* parent) const; + + void setDefaultValueEditorData(QWidget* editor, const QVariant& value, const QModelIndex& index) const; + + QVariant getDefaultValueEditorData(QWidget* editor, const QModelIndex& index) const; +public slots: + void onConnectParamChanged(const QString& str) const; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +private: + QCompleter* _connectCompleter = nullptr; //自动补全的completer +}; + +#endif diff --git a/diagramCavas/include/structDataPropertyModel.h b/diagramCavas/include/structDataPropertyModel.h new file mode 100644 index 0000000..9486b52 --- /dev/null +++ b/diagramCavas/include/structDataPropertyModel.h @@ -0,0 +1,77 @@ +#ifndef STRUCTDATAPROPERTYMODEL_H +#define STRUCTDATAPROPERTYMODEL_H +/** + * 结构预览ui的参量类型model + * */ + +#include +#include "common/core_model/topology.h" + +class StructDataSource; +struct PropertyStateInfo; + +class StructDataPropertyModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum Columns { + ColName = 0, + ColTag = 1, + ColCode = 2, + ColSourceType = 3, + ColConnectPara = 4, + ColModelName = 5, + ColDataType = 6, + ColDefaultValue = 7, + ColLengthPrecision = 8, + ColumnCount + }; + + StructDataPropertyModel(StructDataSource* dataManager, QObject* parent = nullptr); + + // 设置要显示的code列表 + void setPropertyCodes(const QStringList& codes); + + // 设置要显示的属性 + void setProperties(const QVector& properties); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + // 获取当前显示的属性 + QVector getDisplayedProperties() const; + + // 获取当前显示的code列表 + QStringList getDisplayedCodes() const; + + // 根据code查找行 + int findRowByCode(const QString& code) const; + + // 刷新指定code的行 + void refreshRow(const QString& code); +private slots: + void onPropertyUpdated(const ExtraProperty& updatedProp, bool isNew); +signals: + void propertyModified(int row, const ExtraProperty& prop); +private: + ExtraProperty* getProperty(int displayRow) const; + + QVariant getPropertyData(const ExtraProperty& prop, int col) const; + + bool updatePropertyData(PropertyStateInfo* data, int col, const QVariant& value); + +private: + StructDataSource* m_dataManager = nullptr; + QStringList m_propertyCodes; // 存储当前显示的属性code列表 +}; +#endif diff --git a/diagramCavas/include/titleBar.h b/diagramCavas/include/titleBar.h new file mode 100644 index 0000000..e3ee77a --- /dev/null +++ b/diagramCavas/include/titleBar.h @@ -0,0 +1,47 @@ +#ifndef TITLEBAR_H +#define TITLEBAR_H +/** + * 自定义标题栏 + * */ +#include +#include +#include +#include +#include +#include +#include +#include + +class TitleBar : public QWidget +{ + Q_OBJECT +public: + explicit TitleBar(QWidget *parent = nullptr); + + void setTitle(const QString &title); + void updateMaximizeButton(); // 根据窗口状态更新按钮文本 + +signals: + void maximizeClicked(); + void closeClicked(); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + +private: + void setupUI(); + +private: + QPoint m_dragStartPosition; + QWidget *m_parentWindow; + + QLabel *m_titleLabel; + QPushButton *m_maximizeButton; + QPushButton *m_closeButton; + + bool m_isMaximized = false; +}; + +#endif diff --git a/diagramCavas/include/topologyManager.h b/diagramCavas/include/topologyManager.h new file mode 100644 index 0000000..325c996 --- /dev/null +++ b/diagramCavas/include/topologyManager.h @@ -0,0 +1,121 @@ +#ifndef TOPOLOGYMANAGER_H +#define TOPOLOGYMANAGER_H +/**************************** + * 拓扑关系管理类 + * *************************/ +#include +#include +#include "powerConnection.h" +#include "powerTerminal.h" +//#include "global.h" +#include "common/core_model/types.h" + +class PowerEntity; +class BaseProperty; + +class TopologyManager : public QObject { + Q_OBJECT +public: + static TopologyManager& instance(); + + // 实体管理 + PowerEntity* createEntity(EntityType type,const QString& uuid,const QString& name,ModelFunctionType tpe = ModelFunctionType::ProjectModel,const QString& block = QString()); + PowerEntity* findEntity(const QString& id,ModelFunctionType = ModelFunctionType::ProjectModel) const; + bool deleteEntity(const QString& id,ModelFunctionType = ModelFunctionType::ProjectModel); + + QList getEntitiesByBlock(const QString&); //通过名称返回entity中的实体(blockEditor中) + void moveTempBlockData(); //blockEditor中将临时数据转为全局数据 + void clearGlobalBlockData(const QString&); //清除全局数据中的blockdata(block修改时) + + void cloneEditorToBase(); //将editor中的数据深拷贝到base中 + + // 连接管理 + PowerConnection* createConnection(const QString& uuid,const QString& fromTerId, const QString& toTerId,const QString& fromId,const QString& toId,ModelFunctionType tpe = ModelFunctionType::ProjectModel); + QList getConnectionsForTerminal(const QString& terminalId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + void removeConnection(const QString& connId,ModelFunctionType = ModelFunctionType::ProjectModel); + bool validateConnection(const QString& fromTermId, const QString& toTermId,ModelFunctionType = ModelFunctionType::ProjectModel) const; + + // 连接查询接口 + QList getConnectionsFor(const QString& entityId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + PowerConnection* connection(const QString& conId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + PowerConnection* connection(const QString& fromPin,const QString& toPin,ModelFunctionType tpe = ModelFunctionType::ProjectModel); + PowerConnection* ifConnection(const QString& entityId1,const QString& entityId2,ModelFunctionType tpe = ModelFunctionType::ProjectModel); //返回两个item之间的连接 + QHash getAllConnections(ModelFunctionType tpe = ModelFunctionType::ProjectModel); + + PowerEntity* getEntity(const QString& id,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + QList findEntitiesByName(const QString& name,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + + //==========================组态图拓扑相关=================================== + PowerEntity* createDiagram(const QString& id,const QString& name,ModelFunctionType tpe = ModelFunctionType::ProjectModel); //单独创建组态图 + PowerEntity* findDiagram(const QString& id,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + bool deleteDiagram(const QString& id,ModelFunctionType tpe = ModelFunctionType::ProjectModel); + +public: + // 接线点管理 + PowerTerminal* createTerminal(const QString& parentEntityId, + TerminalType type, + const QString& name, + const QPointF& relPos = QPointF(), + const QString& uuid = "", + ModelFunctionType tpe = ModelFunctionType::ProjectModel,double dPerX = 0.0,double dPerY = 0.0); + bool deleteTerminal(const QString& terminalId,ModelFunctionType tpe = ModelFunctionType::ProjectModel); + PowerTerminal* getTerminal(const QString& terminalId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + QList getTerminalsForEntity(const QString& entityId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; + PowerEntity* getEntityByTerminal(const QString& terminalId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; //返回terminal所在的entity实体 + PowerConnection* getConnectionContainsTerminal(const QString& terminalId,ModelFunctionType tpe = ModelFunctionType::ProjectModel) const; //返回包含terminal的connection +signals: + void entityCreated(const QString&); + void entityDeleted(const QString&); + void connectionCreated(const QString&); + void connectionRemoved(const QString&); + +private: + explicit TopologyManager(QObject* parent = nullptr); + ~TopologyManager(); + void clearAllData(); + QHash m_entities; // ID到实体映射 + + QHash m_diagrams; // 组态图拓扑结构 + QHash m_monitorDiagrams; //运行时组态 + // 连接存储 + QHash m_connections; + QMultiHash m_connectionIndex; // 接线点ID到连接的映射 + + QHash m_allTerminals; // ID到接线点映射 + QHash> m_terminalsByEntity; // 实体ID到接线点列表 + + QHash> m_entityConnections; // > +private: + QHash m_baseEntities; // ID到实体映射(基模) + QHash m_baseDiagrams; // 组态图拓扑结构(基模) + // 连接存储 + QHash m_baseConnections; + QMultiHash m_baseConnectionIndex; // 接线点ID到连接的映射(基模) + + QHash m_baseAllTerminals; // ID到接线点映射(基模) + QHash> m_baseTerminalsByEntity; // 实体ID到接线点列表(基模) + + QHash> m_baseEntityConnections; // >(基模) +private: + QHash m_editorEntities; // ID到实体映射(编辑器) + // 连接存储 + QHash m_editorConnections; + QMultiHash m_editorConnectionIndex; // 接线点ID到连接的映射(编辑器) + + QHash m_editorAllTerminals; // ID到接线点映射(编辑器) + QHash> m_editorTerminalsByEntity; // 实体ID到接线点列表(编辑器) + + QHash> m_editorEntityConnections; // >(编辑器) +private: + QHash m_blockEntities; // ID到实体映射(模块编辑) + // 连接存储 + QHash m_blockConnections; + QMultiHash m_blockConnectionIndex; // 接线点ID到连接的映射(模块编辑) + + QHash m_blockAllTerminals; // ID到接线点映射(模块编辑) + QHash> m_blockTerminalsByEntity; // 实体ID到接线点列表(模块编辑) + + QHash> m_blockEntityConnections; // >(模块编辑) +}; + +#endif diff --git a/diagramCavas/include/topologyTree.h b/diagramCavas/include/topologyTree.h new file mode 100644 index 0000000..b5c3149 --- /dev/null +++ b/diagramCavas/include/topologyTree.h @@ -0,0 +1,17 @@ +#ifndef TOPOLOGYTREE_H +#define TOPOLOGYTREE_H + +#include + +/***************拖拽实现*************/ +class TopologyTree : public QTreeView +{ + Q_OBJECT +public: + TopologyTree(QWidget *parent = nullptr); + ~TopologyTree(); +protected: + void mouseMoveEvent(QMouseEvent *event) override; +}; + +#endif diff --git a/diagramCavas/include/util/baseSelector.h b/diagramCavas/include/util/baseSelector.h new file mode 100644 index 0000000..3d6003a --- /dev/null +++ b/diagramCavas/include/util/baseSelector.h @@ -0,0 +1,91 @@ +/** + *\file baseSelector.h + * + *\brief selector是用来实现对图元进行具体操作的类,例如创建、编辑、旋转、移动等,通过对鼠标的行为动作进行具体的逻辑编写实现操作表达 + * + *\author dsc + */ + +#ifndef BASESELECTOR_H +#define BASESELECTOR_H + +#include +#include "designerScene.h" + +enum SelectorType +{ + ST_base = 0, //基本 + ST_cerating, //创建 + ST_moving, //移动 + ST_editing, //顶点编辑 + ST_scaling, //缩放 + ST_rotation, //旋转 + ST_connecting, //连接 + ST_subMoving, //子对象移动 + ST_linkMoving, //连接线移动 +}; + +enum OperationMode +{ + OM_none = 0, + OM_move, //移动 + OM_create, //创建 + OM_edit, //顶点编辑 + OM_scale, //缩放 + OM_rotate, //旋转 + OM_connect, //连接 + OM_subMove,//子对象移动 + OM_linkMove, //连接线移动 +}; + +class GraphicsProjectModelItem; + +using ItemMap = QMap>; + +class BaseSelector : public QObject +{ + Q_OBJECT + +public: + explicit BaseSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~BaseSelector(); + +public: + virtual void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode); + virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode); + virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode); + virtual void dropEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode); + virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event,DesignerScene*,DiagramMode); + + SelectorType getSelectorType() { return m_type; } + //void setOperationMode(OperationMode m) { m_opMode = m; } + OperationMode getOperationMode() { return ms_opMode; } + + void setCursor(DesignerScene*, const QCursor&); + void setSceneName(const QString& str) {m_sceneName = str;} + QString sceneName() const {return m_sceneName;} + + FixedPortsModel* getModel() {return _model;} + void updateConnectLineByTopology(QList); //通过拓扑关系更新位置 +signals: + void setWorkingSelector(SelectorType); +protected: + //静态变量,用于不同类型selector间的成员共享 + static OperationMode ms_opMode; + static QPointF ms_ptMouseDown; + static QPointF ms_ptMouseLast; + static double ms_dAngleMouseDownToItem; //鼠标按下时其位置和item中心点形成的夹角 + static int ms_nDragHandle; //当前抓取的控制点 + SelectorType m_type; + + QString m_sceneName; + FixedPortsModel* _model; +private: + bool m_bHoverOnHandel; //鼠标是否悬停在handel + OperationMode m_opMode; +}; + +#endif diff --git a/diagramCavas/include/util/connectingSelector.h b/diagramCavas/include/util/connectingSelector.h new file mode 100644 index 0000000..052c28b --- /dev/null +++ b/diagramCavas/include/util/connectingSelector.h @@ -0,0 +1,38 @@ +/** + *\file connectingSelector.h + * + *\brief 用来实现图元连接的selector + * + *\author by + */ + +#ifndef CONNECTINGSELECTOR_H +#define CONNECTINGSELECTOR_H + +#include "baseSelector.h" + +class GraphicsFunctionModelItem; + + +class ConnectingSelector : public BaseSelector +{ + Q_OBJECT + +public: + explicit ConnectingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~ConnectingSelector(); +public: + bool targetCouldConnect(GraphicsFunctionModelItem* p,QPointF pos); + void setTargetHighLight(bool val); //设置目标高亮 + void createConnectLline(GraphicsFunctionModelItem* connecting,GraphicsFunctionModelItem* touched,DesignerScene* scene); +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); +private: + GraphicsFunctionModelItem* m_pConnectingItem; + GraphicsFunctionModelItem* m_pTouchedItem; //连线时接触的对象 + bool m_bReadyConnect; //准备连接 +}; + +#endif diff --git a/diagramCavas/include/util/creatingSelector.h b/diagramCavas/include/util/creatingSelector.h new file mode 100644 index 0000000..ec1a574 --- /dev/null +++ b/diagramCavas/include/util/creatingSelector.h @@ -0,0 +1,46 @@ +/** + *\file creatingSelector.h + * + *\brief 用来实现图元创建的selector + * + *\author dsc + */ + +#ifndef CREATINGSELECTOR_H +#define CREATINGSELECTOR_H + +#include "baseSelector.h" +//#include "global.h" + + +enum CreatingMethod +{ + CM_drag, //多拽,默认的创建方式 + CM_click //单击点选,如多边形、线段等 +}; + +class GraphicsFunctionModelItem; +class DesignerScene; + +class CreatingSelector : public BaseSelector +{ + Q_OBJECT + +public: + explicit CreatingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~CreatingSelector(); + +public: + virtual void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + + void setCreatingItem(ModelStateInfo& info) { m_creatingItemInfo=info; } +private: + CreatingMethod m_creatingMethod; + ModelStateInfo m_creatingItemInfo; + GraphicsFunctionModelItem* m_pCreatingItem; + QPointF m_scalBasePoint; +}; + +#endif diff --git a/diagramCavas/include/util/editingSelector.h b/diagramCavas/include/util/editingSelector.h new file mode 100644 index 0000000..2f69313 --- /dev/null +++ b/diagramCavas/include/util/editingSelector.h @@ -0,0 +1,34 @@ +/** + *\file editingSelector.h + * + *\brief 用来实现图元编辑的selector + * + *\author dsc + */ + +#ifndef EDITINGSELECTOR_H +#define EDITINGSELECTOR_H + +#include "baseSelector.h" +//#include "global.h" +#include + + +class EditingSelector : public BaseSelector +{ + Q_OBJECT + +public: + explicit EditingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~EditingSelector(); + +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + +private: + GraphicsProjectModelItem* m_pEditingItem; +}; + +#endif diff --git a/diagramCavas/include/util/linkMovingSelector.h b/diagramCavas/include/util/linkMovingSelector.h new file mode 100644 index 0000000..c430807 --- /dev/null +++ b/diagramCavas/include/util/linkMovingSelector.h @@ -0,0 +1,30 @@ +/** + *\file linkMovingSelector.h + * + *\brief 实现连接线移动的selector,与移动movingSelector作区分 + * + *\author by + */ + +#ifndef LINKMOVINGSELECTOR_H +#define LINKMOVINGSELECTOR_H + +#include "baseSelector.h" + +class ElectricFunctionModelConnectLineItem; + +class LinkMovingSelector : public BaseSelector +{ + Q_OBJECT +public: + explicit LinkMovingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~LinkMovingSelector(); +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); +private: + ElectricFunctionModelConnectLineItem* m_pMovingLine; +}; + +#endif diff --git a/diagramCavas/include/util/movingSelector.h b/diagramCavas/include/util/movingSelector.h new file mode 100644 index 0000000..d22878a --- /dev/null +++ b/diagramCavas/include/util/movingSelector.h @@ -0,0 +1,28 @@ +/** + *\file movingSelector.h + * + *\brief 用来实现图元移动的selector + * + *\author dsc + */ + +#ifndef MOVINGSELECTOR_H +#define MOVINGSELECTOR_H + +#include "baseSelector.h" + +class MovingSelector : public BaseSelector +{ + Q_OBJECT + +public: + explicit MovingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~MovingSelector(); +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + +}; + +#endif diff --git a/diagramCavas/include/util/rotationSelector.h b/diagramCavas/include/util/rotationSelector.h new file mode 100644 index 0000000..97bdabd --- /dev/null +++ b/diagramCavas/include/util/rotationSelector.h @@ -0,0 +1,28 @@ +/** + *\file rotationSelector.h + * + *\brief 用来实现图元旋转的selector + * + *\author dsc + */ + +#ifndef ROTATIONSELECTOR_H +#define ROTATIONSELECTOR_H + +#include "baseSelector.h" + +class RotationSelector : public BaseSelector +{ + Q_OBJECT + +public: + explicit RotationSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~RotationSelector(); + +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); +}; + +#endif diff --git a/diagramCavas/include/util/scalingSelector.h b/diagramCavas/include/util/scalingSelector.h new file mode 100644 index 0000000..654d206 --- /dev/null +++ b/diagramCavas/include/util/scalingSelector.h @@ -0,0 +1,30 @@ +/** + *\file scalingSelector.h + * + *\brief 用来实现图元缩放的selector + *\author dsc + */ + +#ifndef SCALINGSELECTOR_H +#define SCALINGSELECTOR_H + +#include "baseSelector.h" + +class ScalingSelector : public BaseSelector +{ + Q_OBJECT + +public: + explicit ScalingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~ScalingSelector(); + +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + +private: + QPointF m_scalBasePoint; +}; + +#endif diff --git a/diagramCavas/include/util/selectorManager.h b/diagramCavas/include/util/selectorManager.h new file mode 100644 index 0000000..a40bb2e --- /dev/null +++ b/diagramCavas/include/util/selectorManager.h @@ -0,0 +1,42 @@ +/** + *\file selectorManager.h + * + *\brief 所有的selector管理类,采用单例模式 + * 每个cavas实例一个selector,根据具体要实现的内容进行创建和选择 + *\author by 20241113 + */ + +#ifndef SELECTORMANAGER_H +#define SELECTORMANAGER_H + +#include +#include "baseSelector.h" +//#include "global.h" +#include "graphicsDataModel/fixedPortsModel.h" + + +class SelectorManager : public QObject +{ + Q_OBJECT + +public: + SelectorManager(FixedPortsModel*,QObject *parent = 0); + SelectorManager() = delete; + ~SelectorManager(); + +public: + void setWorkingSelector(SelectorType s) { m_curSelector=s; } + BaseSelector* getWorkingSelector(); //根据操作方式获取selector + + void setDrawGraphicsItem(ModelStateInfo&); + void setName(const QString&); +public slots: + void onSignal_setWorkingSelector(SelectorType); + +private: + SelectorType m_curSelector; + QVector m_vecSelectors; + FixedPortsModel *_graphModel; +}; + +#endif diff --git a/diagramCavas/include/util/subMovingSelector.h b/diagramCavas/include/util/subMovingSelector.h new file mode 100644 index 0000000..f87e179 --- /dev/null +++ b/diagramCavas/include/util/subMovingSelector.h @@ -0,0 +1,28 @@ +/** + *\file subMovingSelector.h + * + *\brief 实现子类图元移动的selector,与移动movingSelector作区分 + * + *\author by + */ + +#ifndef SUBMOVINGSELECTOR_H +#define SUBMOVINGSELECTOR_H + +#include "baseSelector.h" + +class SubMovingSelector : public BaseSelector +{ + Q_OBJECT +public: + explicit SubMovingSelector(FixedPortsModel* model,QObject *parent = 0); + virtual ~SubMovingSelector(); +public: + void mousePressEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseMoveEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*, DesignerScene*,DiagramMode sceneMode); +private: + GraphicsProjectModelItem* m_pParentItem; +}; + +#endif diff --git a/diagramCavas/source/baseContentDlg.cpp b/diagramCavas/source/baseContentDlg.cpp new file mode 100644 index 0000000..e80fb15 --- /dev/null +++ b/diagramCavas/source/baseContentDlg.cpp @@ -0,0 +1,30 @@ +#include "baseContentDlg.h" +#include +#include +#include +#include +#include +#include + +BaseContentDlg::BaseContentDlg(QWidget *parent) + : QDialog(parent) + ,_curModelController(nullptr) +{ + setWindowFlags(Qt::Widget); + //this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); +} + +BaseContentDlg::~BaseContentDlg() +{ + +} + +QFormLayout* BaseContentDlg::createFormLayout(QWidget* parent) +{ + QFormLayout* layout = new QFormLayout(parent); + layout->setHorizontalSpacing(20); // 标签与控件间距 + layout->setVerticalSpacing(12); // 行间距 + layout->setLabelAlignment(Qt::AlignRight); // 标签右对齐 + layout->setContentsMargins(12, 12, 12, 12); // 内边距 + return layout; +} diff --git a/diagramCavas/source/baseDrawingPanel.cpp b/diagramCavas/source/baseDrawingPanel.cpp new file mode 100644 index 0000000..648cbf4 --- /dev/null +++ b/diagramCavas/source/baseDrawingPanel.cpp @@ -0,0 +1,92 @@ +#include +#include "baseDrawingPanel.h" +#include +#include +#include "designerView.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "graphicsItem/graphicsBaseItem.h" +#include "util/selectorManager.h" +#include "statusBar.h" +#include "powerEntity.h" +#include "diagramCavas.h" +#include "topologyManager.h" +#include "basePannelPropertyProxy.h" +#include "common/core_model/constants.h" + +BaseDrawingPanel::BaseDrawingPanel(PowerEntity* pEntity,QWidget *parent,DiagramMode mode) + : QWidget(parent) + ,_pModel(nullptr) + ,_mode(mode) + ,_pEntity(nullptr) + ,_verticalLayout(nullptr) + ,_horizontalLayout(nullptr) + ,_hSplitter(nullptr) +{ + _pEntity = pEntity; + _pModel = new FixedPortsModel(pEntity); + _pModel->setTopWidget(this); + m_pSelectorManager = new SelectorManager(_pModel,this); + m_pGraphicsScene = new DesignerScene(_pModel,this); + //设置场景大小.前两个参数为scene的坐标远点,设置到view的中心点后,无论view如何缩放,secne的坐标原点都不会动,方便后续的位置计算 + m_pGraphicsScene->setSceneRect(0,0, Constants::SCENE_WIDTH*4, Constants::SCENE_HEIGHT*4); + m_pGraphicsScene->setGridVisible(true); + + m_pGraphicsView = new DesignerView(this); + m_pGraphicsView->setScene(m_pGraphicsScene); + m_pGraphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + m_pGraphicsScene->setView(m_pGraphicsView); + _pModel->setScene(m_pGraphicsScene); + connect(m_pGraphicsScene, &DesignerScene::selectionChanged, _pModel, &FixedPortsModel::onSelectionChanged); + + m_pStatusBar = new StatusBar(this); + m_pStatusBar->setMaximumHeight(25); + connect(m_pGraphicsView,&DesignerView::onScaleChanged,m_pStatusBar,&StatusBar::onScaleLevelChanged); + + _horizontalLayout = new QHBoxLayout(); + //_horizontalLayout->addWidget(m_pGraphicsView); + _horizontalLayout->setContentsMargins(0, 0, 0, 0); + _horizontalLayout->setSpacing(0); + + _hSplitter = new QSplitter(Qt::Horizontal); + _hSplitter->setHandleWidth(2); // 设置分割条宽度 + + _hSplitter->addWidget(m_pGraphicsView); + _horizontalLayout->addWidget(_hSplitter); + + _verticalLayout = new QVBoxLayout(this); + _verticalLayout->addLayout(_horizontalLayout); + _verticalLayout->addWidget(m_pStatusBar); + _verticalLayout->setContentsMargins(0, 0, 0, 0); + _verticalLayout->setSpacing(0); + + _pPropertyProxy = new BasePannelPropertyProxy(this); +} + +BaseDrawingPanel::~BaseDrawingPanel() +{ + //if(_pModel) + // delete _pModel; +} + +QGraphicsScene* BaseDrawingPanel::getQGraphicsScene() +{ + return m_pGraphicsView->scene(); +} + +DesignerScene* BaseDrawingPanel::getDesignerScene() +{ + return m_pGraphicsScene; +} + +BasePannelPropertyProxy* BaseDrawingPanel::getPropertyProxy() +{ + return _pPropertyProxy.data(); +} + +SelectorManager* BaseDrawingPanel::selectorManager() const +{ + if(m_pSelectorManager) + return m_pSelectorManager; + else + return NULL; +} diff --git a/diagramCavas/source/baseInfoDlg.cpp b/diagramCavas/source/baseInfoDlg.cpp new file mode 100644 index 0000000..3dab594 --- /dev/null +++ b/diagramCavas/source/baseInfoDlg.cpp @@ -0,0 +1,170 @@ +#include "baseInfoDlg.h" +#include "graphicsItem/electricBayItem.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "ui_baseInfoDlg.h" +#include "dataBase.h" +//#include "global.h" +#include "baseProperty.h" +#include "basePropertyManager.h" +#include "itemPropertyDlg.h" +#include + +BaseInfoDlg::BaseInfoDlg(QWidget *parent) + : BaseContentDlg(parent) + , ui(new Ui::baseInfoDlg) + ,_parentDlg(nullptr) +{ + ui->setupUi(this); + _stateGroup = new QButtonGroup(this); + _stateGroup->addButton(ui->rb_inService,1); + _stateGroup->addButton(ui->rb_outService,0); + _stateGroup->setExclusive(true); + initial(); +} + +BaseInfoDlg::~BaseInfoDlg() +{ + delete ui; +} + +void BaseInfoDlg::initial() +{ + connect(ui->btn_icon,&QPushButton::clicked,this,&BaseInfoDlg::onIconManagerClicked); +} + +void BaseInfoDlg::createGroupView(GroupStateInfo infos) +{ + QList lstGrid = DataBase::GetInstance()->getAllGrid(); + QList lstZone = DataBase::GetInstance()->getAllZone(); + QList lstStation = DataBase::GetInstance()->getAllStation(); + + for(auto &info:lstGrid) + { + ui->cb_grid->addItem(info.tagname,info.id); + } + + for(auto &info:lstZone) + { + ui->cb_zone->addItem(info.tagname,info.id); + } + + for(auto &info:lstStation) + { + ui->cb_station->addItem(info.tagname,info.id); + } +} + +QMap BaseInfoDlg::getPropertyValue(BaseProperty* pPro) +{ + QMap map; + + if(pPro) + { + pPro->setTag(ui->le_tag->text()); + if(!ui->le_nameSpace->text().isEmpty()) + pPro->setPath(ui->le_nameSpace->text()); + pPro->setName(ui->le_name->text()); + pPro->setLabel(QJsonObject()); + pPro->setDescription(ui->le_description->text()); + pPro->setGrid(ui->cb_grid->currentText()); + pPro->setZone(ui->cb_zone->currentText()); + pPro->setStation(ui->cb_station->currentText()); + + QJsonObject connectBus; + connectBus["bay_name"] = ui->le_bayName->text(); + connectBus["bay_offset_outer"] = ui->le_idx_1->text(); + connectBus["bay_offset_inner"] = ui->le_idx_2->text(); + connectBus["from_uuid"] = ui->le_from->text(); + connectBus["to_uuid"] = ui->le_to->text(); + pPro->setConnectedBus(connectBus); + pPro->setInService(_stateGroup->checkedId()); + pPro->setState(ui->le_status->text().toInt()); + + emit BasePropertyManager::instance().dataChanged(pPro->uuid().toString()); + } + return map; +} + +void BaseInfoDlg::setPropertyValue(QVariant var) +{ + BaseProperty* pPro = static_cast(var.value()); + if(pPro) + { + ui->le_uuid->setText(pPro->uuid().toString()); + ui->le_tag->setText(pPro->tag()); + ui->le_nameSpace->setText(pPro->getBay()); + ui->le_name->setText(pPro->name()); + ui->le_label->setText(""); //label josn格式暂不解析 + ui->le_description->setText(pPro->description()); + ui->cb_grid->setCurrentText(pPro->grid()); + ui->cb_zone->setCurrentText(pPro->zone()); + ui->cb_station->setCurrentText(pPro->station()); + + ui->le_status->setText(QString::number(pPro->state())); //page status字段将移动到component + + QJsonObject connectBus = pPro->connectedBus(); + QString bay_name = connectBus["bay_name"].toString(); + QString bay_offset_outer = connectBus["bay_offset_outer"].toString(); + QString bay_offset_inner = connectBus["bay_offset_inner"].toString(); + QString from_uuid = connectBus["from_uuid"].toString(); + QString to_uuid = connectBus["to_uuid"].toString(); + //ui->le_bayName->setText(bay_name); + ui->le_idx_1->setText(bay_offset_outer); + ui->le_idx_2->setText(bay_offset_inner); + //ui->le_from->setText(from_uuid); + //ui->le_to->setText(to_uuid); + if(pPro->inService() == 1) + ui->rb_inService->setChecked(true); + else + ui->rb_outService->setChecked(true); + + //间隔处理 + BayProperty* pBay = nullptr; + QMap mapBay = _curModelController->allBayItem(); + for(auto& item:mapBay){ + AbstractProperty* p = item->getProperty(); + BayProperty* pBayPro = dynamic_cast(p); + if(pBayPro){ + QList lstCompo = pBayPro->getLstComponent(); //获取间隔下的component,找到本component对应的间隔 + for(auto& id:lstCompo){ + if(id == pPro->uuid()){ + pBay = pBayPro; + break; + } + } + } + } + + if(pBay){ + auto lstFrom = pBay->getLstFrom(); + auto lstTo = pBay->getLstTo(); + + QStringList sLstFrom; + for(auto& fromId:lstFrom){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(fromId); + if(pPro){ + sLstFrom.append(pPro->tag()); + } + } + + QStringList sLstTo; + for(auto& toId:lstTo){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(toId); + if(pPro){ + sLstTo.append(pPro->tag()); + } + } + + ui->le_bayName->setText(pBay->tag()); + ui->le_from->setText(sLstFrom.join("、")); + ui->le_to->setText(sLstTo.join("、")); + } + } +} + +void BaseInfoDlg::onIconManagerClicked() +{ + if(_parentDlg){ + _parentDlg->getModelController()->showProjectIconSettingDlg(_parentDlg->getCurItem()); + } +} diff --git a/diagramCavas/source/baseModelItem/electricBaseModelLineItem.cpp b/diagramCavas/source/baseModelItem/electricBaseModelLineItem.cpp new file mode 100644 index 0000000..e490f1b --- /dev/null +++ b/diagramCavas/source/baseModelItem/electricBaseModelLineItem.cpp @@ -0,0 +1,137 @@ +#include "baseModelItem/electricBaseModelLineItem.h" +#include +#include +#include + +ElectricBaseModelLineItem::ElectricBaseModelLineItem(QGraphicsItem *parent) + : GraphicsBaseModelItem(parent) +{ + m_boundingRect = QRectF(); + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::NoBrush); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + m_lstPoints.push_back(QPointF()); //起点 + m_lstPoints.push_back(QPointF()); //终点 + _curLine = QPoint(); +} + +ElectricBaseModelLineItem::~ElectricBaseModelLineItem() +{ +} + +ElectricBaseModelLineItem::ElectricBaseModelLineItem(const ElectricBaseModelLineItem& obj) + :GraphicsBaseModelItem(obj) +{ + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::NoBrush); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + + m_points = obj.m_points; + m_pointsBoundingRect = obj.m_pointsBoundingRect; //包裹点的矩形集合 + m_lstPoints = obj.m_lstPoints; + _curLine = obj._curLine; +} + +ElectricBaseModelLineItem* ElectricBaseModelLineItem::clone() const +{ + ElectricBaseModelLineItem* newItem = new ElectricBaseModelLineItem(*this); + return newItem; +} + +void ElectricBaseModelLineItem::setStartPoint(const QPointF& p) +{ + int n = m_lstPoints.size(); + if(n) + { + if(n >2) + { + if(m_lstPoints[0].x() == m_lstPoints[1].x()) //相邻点在垂直方向,水平移动,否则垂直移动 + { + m_lstPoints[1].setX(p.x()); + } + else + { + m_lstPoints[1].setY(p.y()); + } + } + m_lstPoints[0] = p; + } +} +void ElectricBaseModelLineItem::setEndPoint(const QPointF& p) +{ + int n = m_lstPoints.size(); + if(n) + { + if(n >2) + { + if(m_lstPoints[n-1].x() == m_lstPoints[n-2].x()) //相邻点在垂直方向,水平移动,否则垂直移动 + { + m_lstPoints[n-2].setX(p.x()); + } + else + { + m_lstPoints[n-2].setY(p.y()); + } + } + m_lstPoints[n-1] = p; + } +} + +QPainterPath ElectricBaseModelLineItem::shape() const +{ + QPainterPath path; + //path.addPath(m_points); + path.addPath(m_pointsBoundingRect); + return path; +} + +QRectF ElectricBaseModelLineItem::boundingRect() const +{ + return m_boundingRect; +} + +void ElectricBaseModelLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if (option->state & QStyle::State_Selected) + { + painter->setPen(QColor(135,206,235,180)); + } + else + { + painter->setPen(m_pen); + GraphicsBaseModelItem::paint(painter,option,widget); + } + + painter->setBrush(m_brush); + + painter->drawPath(m_points); +} + +void ElectricBaseModelLineItem::calculatePath() +{ + int n = m_lstPoints.size(); + prepareGeometryChange(); + m_points.clear(); + m_pointsBoundingRect.clear(); + + if(m_lstPoints.size() == 2 && (m_lstPoints.first().x() != m_lstPoints.last().x()) && (m_lstPoints.first().y() != m_lstPoints.last().y())) + { + if(m_lstPoints.first().y() < m_lstPoints.last().y()) + m_lstPoints.insert(1,QPointF(m_lstPoints.first().x(),m_lstPoints.last().y())); + else + m_lstPoints.insert(1,QPointF(m_lstPoints.last().x(),m_lstPoints.first().y())); + } + m_points.moveTo(m_lstPoints.first()); + QPointF pLast = m_lstPoints.first(); + for(int i = 1;i +#include +#include + +ElectricBaseModelPortItem::ElectricBaseModelPortItem(QGraphicsItem *parent) + : GraphicsBaseModelItem(parent) +{ + m_boundingRect = QRectF(-1,-1,2,2); + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::black); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); +} + +ElectricBaseModelPortItem::ElectricBaseModelPortItem(const ElectricBaseModelPortItem& obj) + :GraphicsBaseModelItem(obj) +{ + m_boundingRect = QRectF(-1,-1,2,2); + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::black); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + setPos(obj.pos()); +} + +ElectricBaseModelPortItem* ElectricBaseModelPortItem::clone() const +{ + return new ElectricBaseModelPortItem(*this); +} + +ElectricBaseModelPortItem::~ElectricBaseModelPortItem() +{ + +} + +QRectF ElectricBaseModelPortItem::boundingRect() const +{ + return m_boundingRect; +} + +void ElectricBaseModelPortItem::updateConnectData() +{ + QJsonObject obj; + QJsonArray arr; + if(_property) + { + for(auto &ptr:m_mapPort) + { + //if(ptr->connected()) + { + QJsonObject port; + port["portId"] = ptr->getId(); + //auto pLine = ptr->getConnectPtr(); + port["x"] = ptr->pos().x(); + port["y"] = ptr->pos().y(); + port["portType"] = ptr->getType(); + arr.push_back(port); + } + } + + obj["port"] = arr; + obj["metaModel"] = _property->metaModelName(); + _property->setContext(obj); + } +} + +void ElectricBaseModelPortItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->setBrush(m_brush); + painter->drawEllipse(m_boundingRect); +} diff --git a/diagramCavas/source/baseModelItem/electricBaseModelSvgBus.cpp b/diagramCavas/source/baseModelItem/electricBaseModelSvgBus.cpp new file mode 100644 index 0000000..1210ef9 --- /dev/null +++ b/diagramCavas/source/baseModelItem/electricBaseModelSvgBus.cpp @@ -0,0 +1,60 @@ +#include "baseModelItem/electricBaseModelSvgBus.h" +#include "graphicsItem/itemPort.h" +#include "baseProperty.h" +#include +#include +#include + +ElectricBaseModelSvgBus::ElectricBaseModelSvgBus(const QRect &rect, QGraphicsItem *parent) + : ElectricBaseModelSvgItem(rect,parent) +{ + //loadSvg(":/images/element/svg_bus.svg"); + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + setHandleEnaable(H_right,true); + setHandleEnaable(H_left,true); +} + +ElectricBaseModelSvgBus::~ElectricBaseModelSvgBus() +{ + +} + +void ElectricBaseModelSvgBus::updateHandles() +{ + ElectricBaseModelSvgItem::updateHandles(); +} + +void ElectricBaseModelSvgBus::updateConnectData() +{ + QJsonObject obj; + QJsonArray arr; + if(_property) + { + for(auto &ptr:m_mapPort) + { + //if(ptr->connected()) + { + QJsonObject port; + port["portId"] = ptr->getId(); + //auto pLine = ptr->getConnectPtr(); + port["x"] = ptr->pos().x(); + port["y"] = ptr->pos().y(); + port["portType"] = ptr->getType(); + arr.push_back(port); + } + } + + obj["port"] = arr; + obj["metaModel"] = _property->metaModelName(); + obj["subList"] = _property->saveSubToJsonArr(); + _property->setContext(obj); + } +} + +void ElectricBaseModelSvgBus::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricBaseModelSvgItem::paint(painter,option,widget); +} diff --git a/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp b/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp new file mode 100644 index 0000000..74bddce --- /dev/null +++ b/diagramCavas/source/baseModelItem/electricBaseModelSvgItem.cpp @@ -0,0 +1,104 @@ +#include "baseModelItem/electricBaseModelSvgItem.h" +#include "graphicsItem/itemControlHandle.h" + +#include +#include +#include +#include + +ElectricBaseModelSvgItem::ElectricBaseModelSvgItem(const QRect &rect, QGraphicsItem *parent) + : GraphicsBaseModelItem(parent),m_pRender(nullptr) +{ + m_lastBoudingRect = rect; + m_boundingRect = rect; + m_dWidth = rect.width(); + m_dHeight = rect.height(); +} + +ElectricBaseModelSvgItem::ElectricBaseModelSvgItem(const ElectricBaseModelSvgItem& obj) + :GraphicsBaseModelItem(obj) +{ + m_lastBoudingRect = obj.m_lastBoudingRect; + m_icon = obj.m_icon; + m_pRender = new QSvgRenderer(m_icon); + setRotation(obj.rotation()); +} + +ElectricBaseModelSvgItem* ElectricBaseModelSvgItem::clone() const +{ + return new ElectricBaseModelSvgItem(*this); +} + +ElectricBaseModelSvgItem::~ElectricBaseModelSvgItem() +{ + +} + +QPainterPath ElectricBaseModelSvgItem::shape() +{ + QPainterPath path; + double dHandleX = 0.0; + double dHandleY = 0.0; + path.addRect(m_boundingRect); + return path; +} + +void ElectricBaseModelSvgItem::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() +{ + if (!parentItem()) + { + QPointF pt1, pt2, delta; + pt1 = mapToScene(QPointF(0, 0)); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + + prepareGeometryChange(); + //将boundingRect设置成中心点和原点(也是默认变换原点),这样三点重合,有助于简化计算 + m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight); + //setTransformOriginPoint(m_boundingRect.center()); //变换中心默认为item的(0,0)点,所以不执行这句话也没有问题 + //更新bouondingRect后重回会显示位置会有变化,需要做对应的移动 + moveBy(-delta.x(), -delta.y()); + updateHandles(); + } + + m_lastBoudingRect = m_boundingRect; +} + +void ElectricBaseModelSvgItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if(m_pRender) + { + m_pRender->render(painter,m_boundingRect); + } + + painter->setPen(m_pen); + painter->setBrush(m_brush); + + if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 + { + renderSelectBackground(painter); + } + else + { + GraphicsBaseModelItem::paint(painter,option,widget); + } +} + +void ElectricBaseModelSvgItem::loadSvg(const QByteArray& data) +{ + if(m_pRender == nullptr){ + m_icon = data; + m_pRender = new QSvgRenderer(data); + } +} + +void ElectricBaseModelSvgItem::move(const QPointF& point) +{ + moveBy(point.x(), point.y()); +} + +void ElectricBaseModelSvgItem::editShape(int nHandle,const QPointF& ptMouse) +{ + prepareGeometryChange(); + updateHandles(); +} diff --git a/diagramCavas/source/basePannelPropertyProxy.cpp b/diagramCavas/source/basePannelPropertyProxy.cpp new file mode 100644 index 0000000..82639a4 --- /dev/null +++ b/diagramCavas/source/basePannelPropertyProxy.cpp @@ -0,0 +1,38 @@ +#include "basePannelPropertyProxy.h" +#include "baseDrawingPanel.h" +#include "propertyType/pannelColorGadget.h" + +BasePannelPropertyProxy::BasePannelPropertyProxy(BaseDrawingPanel* pPanel) + : BasePropertyProxy(pPanel) + ,_pPanel(pPanel) +{ + _pColorGadget = new PannelColorGadget(_pPanel); +} + +BasePannelPropertyProxy::~BasePannelPropertyProxy() +{ + if(_pColorGadget) + delete _pColorGadget; +} + + +QString BasePannelPropertyProxy::getName() const +{ + + return _pPanel->pageName(); +} + +void BasePannelPropertyProxy::setName(QString str) +{ + _pPanel->setPageName(str); +} + +QSize BasePannelPropertyProxy::getSize() const +{ + return _pPanel->size(); +} + +void BasePannelPropertyProxy::setSize(QSize size) +{ + _pPanel->resize(size); +} diff --git a/diagramCavas/source/basePropertyProxy.cpp b/diagramCavas/source/basePropertyProxy.cpp new file mode 100644 index 0000000..38d0b04 --- /dev/null +++ b/diagramCavas/source/basePropertyProxy.cpp @@ -0,0 +1,12 @@ +#include "basePropertyProxy.h" + +BasePropertyProxy::BasePropertyProxy(QObject *parent) + : QObject(parent) +{ + +} + +BasePropertyProxy::~BasePropertyProxy() +{ + +} diff --git a/diagramCavas/source/baseScene.cpp b/diagramCavas/source/baseScene.cpp new file mode 100644 index 0000000..1e6cab3 --- /dev/null +++ b/diagramCavas/source/baseScene.cpp @@ -0,0 +1,41 @@ +#include "baseScene.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BaseScene::BaseScene(BaseModel* graphModel, QObject *parent) + : QGraphicsScene(parent) + , _graphModel(graphModel) + , _undoStack(new QUndoStack(this)) +{ + setItemIndexMethod(QGraphicsScene::NoIndex); +} + +BaseScene::~BaseScene() = default; + +BaseModel const *BaseScene::graphModel() const +{ + return _graphModel; +} + +BaseModel *BaseScene::graphModel() +{ + return _graphModel; +} + + +QUndoStack &BaseScene::undoStack() +{ + return *_undoStack; +} diff --git a/diagramCavas/source/bayInfoDlg.cpp b/diagramCavas/source/bayInfoDlg.cpp new file mode 100644 index 0000000..1e67203 --- /dev/null +++ b/diagramCavas/source/bayInfoDlg.cpp @@ -0,0 +1,564 @@ +#include +#include +#include +#include +#include "bayInfoDlg.h" +#include "ui_bayInfoDlg.h" +#include "baseProperty.h" +#include "basePropertyManager.h" +#include "measureSettingDlg.h" +#include "graphicsItem/electricBayItem.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "dataBase.h" + +BayInfoDlg::BayInfoDlg(QWidget *parent) + : BaseContentDlg(parent) + , ui(new Ui::bayInfoDlg) + ,_measureDlg(nullptr) + ,_bayProperty(nullptr) + ,_itemProperty(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +BayInfoDlg::~BayInfoDlg() +{ + delete ui; +} + +void BayInfoDlg::initial() +{ + setUi(); + connect(ui->btn_add,&QPushButton::clicked,this,&BayInfoDlg::onAddClicked); + connect(ui->tableWidget_local, &QTableWidget::customContextMenuRequested, this, &BayInfoDlg::onIndexRbtnClicked); +} + +void BayInfoDlg::createGroupView(GroupStateInfo infos) +{ + for(auto& info:infos.info) { + PropertyContentInfo inf; + inf.proTag = info.tagName; + inf.proName = info.name; + inf.proType = info.type; + _mapPro.insert(info.tagName,inf); + } +} + +QMap BayInfoDlg::getPropertyValue(BaseProperty* pPro) +{ + QMap map; + + pPro->setDataChanged(true); + pPro->setMeasurement(_mapMeasure); + return map; +} + +void BayInfoDlg::setPropertyValue(QVariant var) +{ + _mapMeasure.clear(); + ui->tableWidget_other->setRowCount(0); + ui->tableWidget_local->setRowCount(0); //清空列表 + ui->le_zhbh->clear(); + ui->le_jk->clear(); + ui->le_dtgz->clear(); + ui->le_gzlb->clear(); + ui->le_ztjc->clear(); + ui->le_qt->clear(); + + BaseProperty* property = static_cast(var.value()); + if(property) + { + _itemProperty = property; + QList lstType = DataBase::GetInstance()->getMeasureAttributeTypes(); + + _validType = lstType; + + //间隔处理 + _bayProperty = nullptr; + QMap mapBay = _curModelController->allBayItem(); + for(auto& item:mapBay){ + AbstractProperty* pPro = item->getProperty(); + BayProperty* pBayPro = dynamic_cast(pPro); + if(pBayPro){ + QList lstCompo = pBayPro->getLstComponent(); //获取间隔下的component,找到本component对应的间隔 + for(auto& id:lstCompo){ + if(id == property->uuid()){ + _bayProperty = pBayPro; + break; + } + } + } + } + + auto map = property->getMeasurement(); + + for(auto& info:map){ + addMeasure(info); + } + + if(_bayProperty){ + auto lstFrom = _bayProperty->getLstFrom(); + auto lstTo = _bayProperty->getLstTo(); + auto lstProtect = _bayProperty->getLstProtect(); + auto lstFaultRecord = _bayProperty->getLstFaultRecord(); + auto lstDynSense = _bayProperty->getLstDynSense(); + auto lstStatus = _bayProperty->getLstStatus(); + auto lstInstruct = _bayProperty->getLstInstruct(); + auto lstEtc = _bayProperty->getLstEtc(); + + QStringList sLstFrom; + for(auto& fromId:lstFrom){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(fromId); + if(pPro){ + sLstFrom.append(pPro->tag()); + } + } + + QStringList sLstTo; + for(auto& toId:lstTo){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(toId); + if(pPro){ + sLstTo.append(pPro->tag()); + } + } + + QStringList sLstProtect; + for(auto& protectId:lstProtect){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(protectId); + if(pPro){ + sLstProtect.append(pPro->tag()); + } + } + + QStringList sLstFaultRecord; + for(auto& faultRecordId:lstFaultRecord){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(faultRecordId); + if(pPro){ + sLstFaultRecord.append(pPro->tag()); + } + } + + QStringList sLstDynSense; + for(auto& dynSenseId:lstDynSense){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(dynSenseId); + if(pPro){ + sLstDynSense.append(pPro->tag()); + } + } + + QStringList sLstStatus; + for(auto& statusId:lstStatus){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(statusId); + if(pPro){ + sLstStatus.append(pPro->tag()); + } + } + + QStringList sLstInstruct; + for(auto& instructId:lstInstruct){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(instructId); + if(pPro){ + sLstInstruct.append(pPro->tag()); + } + } + + QStringList sLstEtc; + for(auto& etcId:lstEtc){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(etcId); + if(pPro){ + sLstEtc.append(pPro->tag()); + } + } + + QList lstOther; + QList lstCompo = _bayProperty->getLstComponent(); + for(auto& compoId:lstCompo){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(compoId); + if(pPro){ + if(pPro->uuid() == property->uuid()){ //取本间隔内的其他元件量测 + continue; + } + else{ + lstOther.append(pPro); + } + } + } + + QStringList lstOtherMeasure; + for(auto& compo:lstOther){ + auto map = compo->getMeasurement(); + for(auto& measure:map){ + lstOtherMeasure.append(compo->tag()+"-"+measure.tag); + } + } + + ui->le_zhbh->setText(sLstProtect.join("、")); + ui->le_jk->setText(sLstInstruct.join("、")); + ui->le_dtgz->setText(sLstDynSense.join("、")); + ui->le_gzlb->setText(sLstFaultRecord.join("、")); + ui->le_ztjc->setText(sLstStatus.join("、")); + ui->le_qt->setText(sLstEtc.join("、")); + + ui->le_bayName->setText(_bayProperty->name()); + addOtherMeasure(lstOtherMeasure); + } + } +} + + +void BayInfoDlg::setUi() +{ + QStringList headerText; + headerText<<"TAG"<<"名称"<<"设备"<<"端子"<<"类型"<<"SIZE"<<"事件"<<"double"; + ui->tableWidget_local->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tableWidget_local->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tableWidget_local->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableWidget_local->setColumnCount(headerText.count()); + ui->tableWidget_local->setHorizontalHeaderLabels(headerText); + ui->tableWidget_local->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + ui->tableWidget_local->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->tableWidget_local->setStyleSheet( + "QTableWidget::item {" + " padding: 5px;" // 可选:增加内边距 + " white-space: pre-wrap;" // 关键:保留空白符并允许自动换行 + "}" + ); + + headerText.clear(); + headerText<<"TAG"; + ui->tableWidget_other->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tableWidget_other->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tableWidget_other->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableWidget_other->setColumnCount(headerText.count()); + ui->tableWidget_other->setHorizontalHeaderLabels(headerText); + ui->tableWidget_other->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + ui->tableWidget_other->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->tableWidget_other->setStyleSheet( + "QTableWidget::item {" + " padding: 5px;" // 可选:增加内边距 + " white-space: pre-wrap;" // 关键:保留空白符并允许自动换行 + "}" + ); +} + +void BayInfoDlg::addMeasure(MeasurementInfo info,int mode) +{ + if(mode == 0){ //新建 + if(_mapMeasure.contains(info.name)) + return; + int row = ui->tableWidget_local->rowCount(); + ui->tableWidget_local->insertRow(row); + + QTableWidgetItem* tagItem = new QTableWidgetItem(info.tag); + ui->tableWidget_local->setItem(row, 0, tagItem); + + QTableWidgetItem* nameItem = new QTableWidgetItem(info.name); + ui->tableWidget_local->setItem(row, 1, nameItem); + + QTableWidgetItem* deviceItem = new QTableWidgetItem(info.sDevice); + ui->tableWidget_local->setItem(row, 2, deviceItem); + + QTableWidgetItem* channelItem = new QTableWidgetItem(info.sChannel); + ui->tableWidget_local->setItem(row, 3, channelItem); + + QString sType; + if(info.type == 0){ + sType = "遥测"; + } + else if(info.type == 1){ + sType = "遥信"; + } + else if(info.type == 2){ + sType = "遥控"; + } + else if(info.type == 3){ + sType = "遥调"; + } + else if(info.type == 4){ + sType = "整定值"; + } + QTableWidgetItem* typeItem = new QTableWidgetItem(sType); + ui->tableWidget_local->setItem(row, 4, typeItem); + + QTableWidgetItem* sizeItem = new QTableWidgetItem(QString::number(info.size)); + ui->tableWidget_local->setItem(row, 5, sizeItem); + + QTableWidgetItem* enableItem = new QTableWidgetItem(info.bEnable?"启用":"关闭"); + ui->tableWidget_local->setItem(row, 6, enableItem); + + QTableWidgetItem* doubleItem = new QTableWidgetItem(info.sSymmetry.isEmpty()?"":"*"); + ui->tableWidget_local->setItem(row, 7, doubleItem); + + if(_bayProperty && _itemProperty){ + QUuid bayId = _bayProperty->uuid(); + QUuid itemId = _itemProperty->uuid(); + info.bayUuid = bayId; + info.componentUuid = itemId; + _mapMeasure.insert(info.name,info); + } + } + else if(mode == 1){ //修改 + QAbstractItemModel* model = ui->tableWidget_local->model(); + int rowCount = model->rowCount(); + + QModelIndex index; + for (int row = 0; row < rowCount; ++row) { + QModelIndex col1Index = model->index(row, 1); + QVariant data = model->data(col1Index, Qt::DisplayRole); + + if (data.toString() == info.name) { + // 返回本行第0列的QModelIndex + index = model->index(row, 1); + } + } + + /*QModelIndexList selectedIndexes = ui->tableWidget_local->selectionModel()->selectedRows(); + if (selectedIndexes.isEmpty()) { + return; // 没有选中任何行 + } + + // 获取当前选中的第一项索引 + QModelIndex index = selectedIndexes.first(); + if (!index.isValid()) { + return; + } + QModelIndex indexName = index.sibling(index.row(),1);*/ + + QString sName = index.data().toString(); + if(_mapMeasure.contains(sName)){ + + auto itemDevice = ui->tableWidget_local->item(index.row(),2); + if(itemDevice){ + itemDevice->setText(info.sDevice); + } + + auto itemChannel = ui->tableWidget_local->item(index.row(),3); + if(itemChannel){ + itemChannel->setText(info.sChannel); + } + + auto itemType = ui->tableWidget_local->item(index.row(),4); + if(itemType){ + QString sType; + if(info.type == 0){ + sType = "遥测"; + } + else if(info.type == 1){ + sType = "遥信"; + } + else if(info.type == 2){ + sType = "遥控"; + } + else if(info.type == 3){ + sType = "遥调"; + } + else if(info.type == 4){ + sType = "整定值"; + } + + itemType->setText(sType); + } + + auto itemSize = ui->tableWidget_local->item(index.row(),5); + if(itemSize){ + itemSize->setText(QString::number(info.size)); + } + + auto itemEnable = ui->tableWidget_local->item(index.row(),6); + if(itemEnable){ + itemEnable->setText(info.bEnable?"启用":"关闭"); + } + + if(_bayProperty && _itemProperty){ + QUuid bayId = _bayProperty->uuid(); + QUuid itemId = _itemProperty->uuid(); + info.bayUuid = bayId; + info.componentUuid = itemId; + _mapMeasure[info.name] = info; + } + } + } +} + +void BayInfoDlg::addOtherMeasure(QStringList lst) +{ + for(auto& str:lst){ + int row = ui->tableWidget_other->rowCount(); + ui->tableWidget_other->insertRow(row); + + QTableWidgetItem* tagItem = new QTableWidgetItem(str); + ui->tableWidget_local->setItem(row, 0, tagItem); + } +} + +void BayInfoDlg::onAddClicked() +{ + if(_measureDlg == nullptr){ + _measureDlg = new MeasureSettingDlg(this); + _measureDlg->setBayComponent(this); + } + int curType = 0; //当前对象类型 + if(_itemProperty) + curType = _itemProperty->type(); + + bool isDouble = false; + if(_bayProperty){ //判断double + QString sBayType = _bayProperty->getType(); + if(sBayType == "分段间隔" || sBayType == "母联间隔"){ + if(_curModelController){ + int ctCount = 0; + auto lstUuid = _bayProperty->getLstComponent(); //获取间隔下的所有设备 + QUuid ctUid; + for(auto& uid:lstUuid){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(uid); + if(pPro){ + if(pPro->type() == 4){ //该设备是ct + ctCount += 1; + ctUid = pPro->uuid(); + } + } + } + + if(ctCount == 1){ //只有1个ct时置为double + if(ctUid == _itemProperty->uuid()) //选择ct时才判断 + isDouble = true; + } + } + } + } + _measureDlg->showDlg(curType,_extendInfo,isDouble); +} + +void BayInfoDlg::onDeleteClicked() +{ + // 获取当前选中的索引 + QModelIndexList selectedIndexes = ui->tableWidget_local->selectionModel()->selectedRows(); + if (selectedIndexes.isEmpty()) { + return; // 没有选中任何行 + } + + // 获取当前选中的第一项索引 + QModelIndex index = selectedIndexes.first(); + if (!index.isValid()) { + return; + } + + QModelIndex indexName = index.sibling(index.row(),1); + QString sName = indexName.data().toString(); + if(_mapMeasure.contains(sName)){ + MeasurementInfo info = _mapMeasure.take(sName); + if(!info.sSymmetry.isEmpty()){ //是double的情况 + if(_mapMeasure.contains(info.sSymmetry)){ + MeasurementInfo dbInfo = _mapMeasure.take(info.sSymmetry); + + QAbstractItemModel* model = ui->tableWidget_local->model(); + int rowCount = model->rowCount(); + + for (int row = 0; row < rowCount; ++row) { //删除double所在行 + QModelIndex col1Index = model->index(row, 1); + QVariant data = model->data(col1Index, Qt::DisplayRole); + + if (data.toString() == info.sSymmetry) { + ui->tableWidget_local->removeRow(col1Index.row()); + break; + } + } + } + } + } + + int currentRow = ui->tableWidget_local->currentRow(); + if (currentRow == -1) { + return; // 没有选中行 + } + + ui->tableWidget_local->removeRow(currentRow); +} + +void BayInfoDlg::onModifyClicked() +{ + // 获取当前选中的索引 + QModelIndexList selectedIndexes = ui->tableWidget_local->selectionModel()->selectedRows(); + if (selectedIndexes.isEmpty()) { + return; // 没有选中任何行 + } + + // 获取当前选中的第一项索引 + QModelIndex index = selectedIndexes.first(); + if (!index.isValid()) { + return; + } + + QModelIndex indexName = index.sibling(index.row(),1); + QString sName = indexName.data().toString(); + if(_mapMeasure.contains(sName)){ + auto info = _mapMeasure.value(sName); + if(_measureDlg == nullptr){ + _measureDlg = new MeasureSettingDlg(this); + _measureDlg->setBayComponent(this); + } + bool isDouble = false; + if(_bayProperty){ //判断double + QString sBayType = _bayProperty->getType(); + if(sBayType == "分段间隔" || sBayType == "母联间隔"){ + if(_curModelController){ + int ctCount = 0; + auto lstUuid = _bayProperty->getLstComponent(); //获取间隔下的所有设备 + for(auto& uid:lstUuid){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(uid); + if(pPro){ + if(pPro->type() == 4){ //该设备是ct + ctCount += 1; + } + } + } + + if(ctCount == 1) //只有1个ct时置为double + isDouble = true; + } + } + } + + MeasurementInfo symmetryInfo; + if(isDouble){ + QString sDoubleName = info.sSymmetry; + if(_mapMeasure.contains(sDoubleName)) + symmetryInfo = _mapMeasure.value(sDoubleName); + } + + _measureDlg->showDlg(info,_extendInfo,isDouble,symmetryInfo); + } +} + +void BayInfoDlg::onIndexRbtnClicked(const QPoint &pos) +{ + // 获取当前点击的位置对应的索引 + QModelIndex index = ui->tableWidget_local->indexAt(pos); + if (!index.isValid()) { + return; // 如果点击的是空白区域,直接返回 + } + + QMenu menu; + QAction *modifyAction = new QAction("修改量测", this); + QAction *deleteAction = new QAction("移除量测", this); + menu.addAction(modifyAction); + menu.addAction(deleteAction); + + // 连接删除菜单项的触发信号与槽函数 + connect(modifyAction, &QAction::triggered, this, &BayInfoDlg::onModifyClicked); + connect(deleteAction, &QAction::triggered, this, &BayInfoDlg::onDeleteClicked); + + // 在点击位置显示菜单 + menu.exec(ui->tableWidget_local->mapToGlobal(pos)); +} + +void BayInfoDlg::onHttpDataUpdated(HttpRecommandInfo info) +{ + if(_measureDlg){ + if(_measureDlg->isVisible()){ + + } + } +} diff --git a/diagramCavas/source/bayManagerContentDlg.cpp b/diagramCavas/source/bayManagerContentDlg.cpp new file mode 100644 index 0000000..6b4ca0c --- /dev/null +++ b/diagramCavas/source/bayManagerContentDlg.cpp @@ -0,0 +1,289 @@ +#include +#include "bayManagerContentDlg.h" +#include "baseProperty.h" +#include "ui_bayManagerContentDlg.h" +#include "basePropertyManager.h" + +BayManagerContentDlg::BayManagerContentDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::bayManagerContentDlg) + ,_pData(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +BayManagerContentDlg::~BayManagerContentDlg() +{ + delete ui; +} + +void BayManagerContentDlg::initial() +{ + _stateGroup = new QButtonGroup(this); + _stateGroup->addButton(ui->rbtn_in,1); + _stateGroup->addButton(ui->rbtn_out,0); + _stateGroup->setExclusive(true); + + QStringList headerText; + headerText<<"选择"<<"名称"; + ui->tw_zongHe->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tw_zongHe->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tw_zongHe->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tw_zongHe->setColumnCount(headerText.count()); + ui->tw_zongHe->setHorizontalHeaderLabels(headerText); + ui->tw_zongHe->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + ui->tw_jianKong->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tw_jianKong->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tw_jianKong->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tw_jianKong->setColumnCount(headerText.count()); + ui->tw_jianKong->setHorizontalHeaderLabels(headerText); + ui->tw_jianKong->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + ui->tw_dongTai->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tw_dongTai->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tw_dongTai->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tw_dongTai->setColumnCount(headerText.count()); + ui->tw_dongTai->setHorizontalHeaderLabels(headerText); + ui->tw_dongTai->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + ui->tw_guZhang->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tw_guZhang->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tw_guZhang->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tw_guZhang->setColumnCount(headerText.count()); + ui->tw_guZhang->setHorizontalHeaderLabels(headerText); + ui->tw_guZhang->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + ui->tw_zhuangTai->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tw_zhuangTai->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tw_zhuangTai->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tw_zhuangTai->setColumnCount(headerText.count()); + ui->tw_zhuangTai->setHorizontalHeaderLabels(headerText); + ui->tw_zhuangTai->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + ui->tw_qiTa->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tw_qiTa->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tw_qiTa->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tw_qiTa->setColumnCount(headerText.count()); + ui->tw_qiTa->setHorizontalHeaderLabels(headerText); + ui->tw_qiTa->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); +} + +void BayManagerContentDlg::updateByProperty() +{ + if(_pData){ + ui->le_name->setText(_pData->name()); + ui->tw_zongHe->setRowCount(0); + ui->tw_jianKong->setRowCount(0); + ui->tw_dongTai->setRowCount(0); + ui->tw_guZhang->setRowCount(0); + ui->tw_zhuangTai->setRowCount(0); + ui->tw_qiTa->setRowCount(0); + + ui->le_index->setText(_pData->uuid().toString()); + ui->le_type->setText(_pData->getType()); + + QStringList lstFrom; + for(auto uid:_pData->getLstFrom()){ + lstFrom.append(uid.toString()); + } + ui->le_from->setText(lstFrom.join(" ")); + + QStringList lstTo; + for(auto uid:_pData->getLstTo()){ + lstTo.append(uid.toString()); + } + ui->le_to->setText(lstTo.join(" ")); + + ui->le_voltage->setText(QString::number(_pData->getVoltage())); + ui->le_fla->setText(QString::number(_pData->getFla())); + ui->le_capacity->setText(QString::number(_pData->getCapacity())); + + QList lstPro; + QList lstId = _pData->getLstComponent(); + QList lstProtectId = _pData->getLstProtect(); //综合保护 + QList lstInsId = _pData->getLstInstruct(); //监控 + QList lstDynSenId = _pData->getLstDynSense(); //动态感知 + QList lstFauRecId = _pData->getLstFaultRecord(); //故障录播 + QList lstStaId= _pData->getLstStatus(); //状态监测 + QList lstEtcId= _pData->getLstEtc(); //其他设备 + for(auto& id:lstId){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(id); //根据id找到对应的设备 + if(pPro){ + lstPro.append(pPro); + } + } + + for(auto& pro:lstPro){ //初始化tablewidget + int row = ui->tw_zongHe->rowCount(); //综合保护 + ui->tw_zongHe->insertRow(row); + QTableWidgetItem *checkItemProtec = new QTableWidgetItem(); + checkItemProtec->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + checkItemProtec->setCheckState(Qt::Unchecked); // 默认未勾选 + checkItemProtec->setData(Qt::UserRole+1,pro->uuid()); + ui->tw_zongHe->setItem(row, 0, checkItemProtec); // 设置到第一列 + QTableWidgetItem* tagItemProtec = new QTableWidgetItem(pro->tag()); + ui->tw_zongHe->setItem(row, 1, tagItemProtec); + + row = ui->tw_jianKong->rowCount(); //监控 + ui->tw_jianKong->insertRow(row); + QTableWidgetItem *checkItemIns = new QTableWidgetItem(); + checkItemIns->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + checkItemIns->setCheckState(Qt::Unchecked); + checkItemIns->setData(Qt::UserRole+1,pro->uuid()); + ui->tw_jianKong->setItem(row, 0, checkItemIns); + QTableWidgetItem* tagItemIns = new QTableWidgetItem(pro->tag()); + ui->tw_jianKong->setItem(row, 1, tagItemIns); + + row = ui->tw_dongTai->rowCount(); //动态感知 + ui->tw_dongTai->insertRow(row); + QTableWidgetItem *checkItemDynSen = new QTableWidgetItem(); + checkItemDynSen->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + checkItemDynSen->setCheckState(Qt::Unchecked); + checkItemDynSen->setData(Qt::UserRole+1,pro->uuid()); + ui->tw_dongTai->setItem(row, 0, checkItemDynSen); + QTableWidgetItem* tagItemDynSen = new QTableWidgetItem(pro->tag()); + ui->tw_dongTai->setItem(row, 1, tagItemDynSen); + + row = ui->tw_guZhang->rowCount(); //故障录播 + ui->tw_guZhang->insertRow(row); + QTableWidgetItem *checkItemFauRec = new QTableWidgetItem(); + checkItemFauRec->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + checkItemFauRec->setCheckState(Qt::Unchecked); + checkItemFauRec->setData(Qt::UserRole+1,pro->uuid()); + ui->tw_guZhang->setItem(row, 0, checkItemFauRec); + QTableWidgetItem* tagItemFauRec = new QTableWidgetItem(pro->tag()); + ui->tw_guZhang->setItem(row, 1, tagItemFauRec); + + row = ui->tw_zhuangTai->rowCount(); //状态监测 + ui->tw_zhuangTai->insertRow(row); + QTableWidgetItem *checkItemSta = new QTableWidgetItem(); + checkItemSta->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + checkItemSta->setCheckState(Qt::Unchecked); + checkItemSta->setData(Qt::UserRole+1,pro->uuid()); + ui->tw_zhuangTai->setItem(row, 0, checkItemSta); + QTableWidgetItem* tagItemSta = new QTableWidgetItem(pro->tag()); + ui->tw_zhuangTai->setItem(row, 1, tagItemSta); + + row = ui->tw_qiTa->rowCount(); //其他设备 + ui->tw_qiTa->insertRow(row); + QTableWidgetItem *checkItemEtc = new QTableWidgetItem(); + checkItemEtc->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + checkItemEtc->setCheckState(Qt::Unchecked); + checkItemEtc->setData(Qt::UserRole+1,pro->uuid()); + ui->tw_qiTa->setItem(row, 0, checkItemEtc); + QTableWidgetItem* tagItemEtc = new QTableWidgetItem(pro->tag()); + ui->tw_qiTa->setItem(row, 1, tagItemEtc); + } + + for(auto& id:lstProtectId){ //设置勾选状态 + for(int i = 0;i < ui->tw_zongHe->rowCount();++i){ + if(ui->tw_zongHe->item(i,0)->data(Qt::UserRole+1).toUuid() == id){ + ui->tw_zongHe->item(i,0)->setCheckState(Qt::Checked); + break; + } + } + } + + for(auto& id:lstInsId){ + for(int i = 0;i < ui->tw_jianKong->rowCount();++i){ + if(ui->tw_jianKong->item(i,0)->data(Qt::UserRole+1).toUuid() == id){ + ui->tw_jianKong->item(i,0)->setCheckState(Qt::Checked); + break; + } + } + } + + for(auto& id:lstDynSenId){ + for(int i = 0;i < ui->tw_dongTai->rowCount();++i){ + if(ui->tw_dongTai->item(i,0)->data(Qt::UserRole+1).toUuid() == id){ + ui->tw_dongTai->item(i,0)->setCheckState(Qt::Checked); + break; + } + } + } + + for(auto& id:lstFauRecId){ + for(int i = 0;i < ui->tw_guZhang->rowCount();++i){ + if(ui->tw_guZhang->item(i,0)->data(Qt::UserRole+1).toUuid() == id){ + ui->tw_guZhang->item(i,0)->setCheckState(Qt::Checked); + break; + } + } + } + + for(auto& id:lstStaId){ + for(int i = 0;i < ui->tw_zhuangTai->rowCount();++i){ + if(ui->tw_zhuangTai->item(i,0)->data(Qt::UserRole+1).toUuid() == id){ + ui->tw_zhuangTai->item(i,0)->setCheckState(Qt::Checked); + break; + } + } + } + + for(auto& id:lstEtcId){ + for(int i = 0;i < ui->tw_qiTa->rowCount();++i){ + if(ui->tw_qiTa->item(i,0)->data(Qt::UserRole+1).toUuid() == id){ + ui->tw_qiTa->item(i,0)->setCheckState(Qt::Checked); + break; + } + } + } + } +} + +void BayManagerContentDlg::saveSetting() +{ + if(_pData){ + QList lstProtec; + for(int i = 0;i < ui->tw_zongHe->rowCount();++i){ + if(Qt::Checked == ui->tw_zongHe->item(i,0)->checkState()){ + lstProtec.append(ui->tw_zongHe->item(i,0)->data(Qt::UserRole+1).toUuid()); + } + } + + QList lstIns; + for(int i = 0;i < ui->tw_jianKong->rowCount();++i){ + if(Qt::Checked == ui->tw_jianKong->item(i,0)->checkState()){ + lstIns.append(ui->tw_jianKong->item(i,0)->data(Qt::UserRole+1).toUuid()); + } + } + + QList lstDynSen; + for(int i = 0;i < ui->tw_dongTai->rowCount();++i){ + if(Qt::Checked == ui->tw_dongTai->item(i,0)->checkState()){ + lstDynSen.append(ui->tw_dongTai->item(i,0)->data(Qt::UserRole+1).toUuid()); + } + } + + QList lstFauRec; + for(int i = 0;i < ui->tw_guZhang->rowCount();++i){ + if(Qt::Checked == ui->tw_guZhang->item(i,0)->checkState()){ + lstFauRec.append(ui->tw_guZhang->item(i,0)->data(Qt::UserRole+1).toUuid()); + } + } + + QList lstSta; + for(int i = 0;i < ui->tw_zhuangTai->rowCount();++i){ + if(Qt::Checked == ui->tw_zhuangTai->item(i,0)->checkState()){ + lstSta.append(ui->tw_zhuangTai->item(i,0)->data(Qt::UserRole+1).toUuid()); + } + } + + QList lstEtc; + for(int i = 0;i < ui->tw_qiTa->rowCount();++i){ + if(Qt::Checked == ui->tw_qiTa->item(i,0)->checkState()){ + lstEtc.append(ui->tw_qiTa->item(i,0)->data(Qt::UserRole+1).toUuid()); + } + } + + _pData->setLstProtect(lstProtec); + _pData->setLstInstruct(lstIns); + _pData->setLstDynSense(lstDynSen); + _pData->setLstFaultRecord(lstFauRec); + _pData->setLstStatus(lstSta); + _pData->setLstEtc(lstEtc); + } +} diff --git a/diagramCavas/source/bayManagerDlg.cpp b/diagramCavas/source/bayManagerDlg.cpp new file mode 100644 index 0000000..f8aed81 --- /dev/null +++ b/diagramCavas/source/bayManagerDlg.cpp @@ -0,0 +1,98 @@ +#include "bayManagerDlg.h" +#include "bayManagerContentDlg.h" +#include "graphicsItem/electricBayItem.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "ui_bayManagerDlg.h" + +BayManagerDlg::BayManagerDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::bayManagerDlg) + ,_modelController(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +BayManagerDlg::~BayManagerDlg() +{ + delete ui; +} + +void BayManagerDlg::initial() +{ + connect(ui->btn_ok,&QPushButton::clicked,this,&BayManagerDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&BayManagerDlg::onCancelClicked); + connect(ui->listWidget,&QListWidget::itemClicked,this,&BayManagerDlg::onListItemClicked); +} + +void BayManagerDlg::showDlg() +{ + show(); + clearData(); + initData(); +} + +void BayManagerDlg::initData() +{ + QList lstBay; + QMap mapBay = _modelController->allBayItem(); //获取当前图中所有bay + for(auto& item:mapBay){ + AbstractProperty* p = item->getProperty(); + BayProperty* pBayPro = dynamic_cast(p); + if(pBayPro){ + lstBay.append(pBayPro); + } + } + + generatePage(lstBay); +} + +void BayManagerDlg::clearData() +{ + ui->listWidget->clear(); + for(auto& page:_contentData) + { + ui->stackedWidget->removeWidget(page); + } + qDeleteAll(_contentData); + _contentData.clear(); +} + +void BayManagerDlg::onOkClicked() +{ + hide(); + for(auto& pDlg:_contentData){ + if(pDlg){ + pDlg->saveSetting(); + } + } +} + +void BayManagerDlg::onCancelClicked() +{ + hide(); +} + +void BayManagerDlg::onListItemClicked(QListWidgetItem *item) +{ + int index = item->data(Qt::UserRole+1).toInt(); + ui->stackedWidget->setCurrentIndex(index); +} + +void BayManagerDlg::generatePage(QList lstBay) +{ + for(auto& pData:lstBay) + { + BayManagerContentDlg* pBay = new BayManagerContentDlg(this); + pBay->setProperty(pData); + pBay->updateByProperty(); + int index = ui->stackedWidget->addWidget(pBay); + _contentData.insert(index,pBay); + QListWidgetItem* pItem = new QListWidgetItem(pData->name()); + pItem->setData(Qt::UserRole+1,index); + ui->listWidget->addItem(pItem); + } + if(ui->stackedWidget->count() != 0) + ui->stackedWidget->setCurrentIndex(0); +} diff --git a/diagramCavas/source/bayMeasureDlg.cpp b/diagramCavas/source/bayMeasureDlg.cpp new file mode 100644 index 0000000..619e74b --- /dev/null +++ b/diagramCavas/source/bayMeasureDlg.cpp @@ -0,0 +1,448 @@ +#include +#include +#include +#include +#include "bayMeasureDlg.h" +#include "ui_bayMeasureDlg.h" +#include "baseProperty.h" +#include "basePropertyManager.h" +#include "measureSettingDlg.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "dataBase.h" + +BayMeasureDlg::BayMeasureDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::bayMeasureDlg) + ,_measureDlg(nullptr) + ,_bayProperty(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +BayMeasureDlg::~BayMeasureDlg() +{ + delete ui; +} + +void BayMeasureDlg::initial() +{ + setUi(); + connect(ui->btn_add,&QPushButton::clicked,this,&BayMeasureDlg::onAddClicked); + connect(ui->tableWidget_local, &QTableWidget::customContextMenuRequested, this, &BayMeasureDlg::onIndexRbtnClicked); + + connect(ui->btn_ok,&QPushButton::clicked,this,&BayMeasureDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&BayMeasureDlg::onCancelClicked); +} + +void BayMeasureDlg::getPropertyValue(BayProperty* pBay) +{ + pBay->setMeasurement(_mapMeasure); +} + +void BayMeasureDlg::setPropertyValue(BayProperty* pBay) +{ + _mapMeasure.clear(); + ui->tableWidget_other->setRowCount(0); + ui->tableWidget_local->setRowCount(0); //清空列表 + ui->le_zhbh->clear(); + ui->le_jk->clear(); + ui->le_dtgz->clear(); + ui->le_gzlb->clear(); + ui->le_ztjc->clear(); + ui->le_qt->clear(); + + if(pBay) + { + QList lstType = DataBase::GetInstance()->getMeasureAttributeTypes(); + + _validType = lstType; + + //间隔处理 + _bayProperty = pBay; + + auto map = pBay->getMeasurement(); + + for(auto& info:map){ + addMeasure(info); + } + + if(_bayProperty){ + auto lstFrom = _bayProperty->getLstFrom(); + auto lstTo = _bayProperty->getLstTo(); + auto lstProtect = _bayProperty->getLstProtect(); + auto lstFaultRecord = _bayProperty->getLstFaultRecord(); + auto lstDynSense = _bayProperty->getLstDynSense(); + auto lstStatus = _bayProperty->getLstStatus(); + auto lstInstruct = _bayProperty->getLstInstruct(); + auto lstEtc = _bayProperty->getLstEtc(); + + QStringList sLstFrom; + for(auto& fromId:lstFrom){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(fromId); + if(pPro){ + sLstFrom.append(pPro->tag()); + } + } + + QStringList sLstTo; + for(auto& toId:lstTo){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(toId); + if(pPro){ + sLstTo.append(pPro->tag()); + } + } + + QStringList sLstProtect; + for(auto& protectId:lstProtect){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(protectId); + if(pPro){ + sLstProtect.append(pPro->tag()); + } + } + + QStringList sLstFaultRecord; + for(auto& faultRecordId:lstFaultRecord){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(faultRecordId); + if(pPro){ + sLstFaultRecord.append(pPro->tag()); + } + } + + QStringList sLstDynSense; + for(auto& dynSenseId:lstDynSense){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(dynSenseId); + if(pPro){ + sLstDynSense.append(pPro->tag()); + } + } + + QStringList sLstStatus; + for(auto& statusId:lstStatus){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(statusId); + if(pPro){ + sLstStatus.append(pPro->tag()); + } + } + + QStringList sLstInstruct; + for(auto& instructId:lstInstruct){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(instructId); + if(pPro){ + sLstInstruct.append(pPro->tag()); + } + } + + QStringList sLstEtc; + for(auto& etcId:lstEtc){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(etcId); + if(pPro){ + sLstEtc.append(pPro->tag()); + } + } + + QList lstOther; + QList lstCompo = _bayProperty->getLstComponent(); + for(auto& compoId:lstCompo){ + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(compoId); + if(pPro){ + lstOther.append(pPro); + } + } + + QStringList lstOtherMeasure; + for(auto& compo:lstOther){ + auto map = compo->getMeasurement(); + for(auto& measure:map){ + lstOtherMeasure.append(compo->tag()+"-"+measure.tag); + } + } + + ui->le_zhbh->setText(sLstProtect.join("、")); + ui->le_jk->setText(sLstInstruct.join("、")); + ui->le_dtgz->setText(sLstDynSense.join("、")); + ui->le_gzlb->setText(sLstFaultRecord.join("、")); + ui->le_ztjc->setText(sLstStatus.join("、")); + ui->le_qt->setText(sLstEtc.join("、")); + + ui->le_bayName->setText(_bayProperty->name()); + addOtherMeasure(lstOtherMeasure); + } + } +} + + +void BayMeasureDlg::setUi() +{ + QStringList headerText; + headerText<<"TAG"<<"名称"<<"设备"<<"端子"<<"类型"<<"SIZE"<<"事件"; + ui->tableWidget_local->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tableWidget_local->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tableWidget_local->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableWidget_local->setColumnCount(headerText.count()); + ui->tableWidget_local->setHorizontalHeaderLabels(headerText); + ui->tableWidget_local->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + ui->tableWidget_local->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->tableWidget_local->setStyleSheet( + "QTableWidget::item {" + " padding: 5px;" // 可选:增加内边距 + " white-space: pre-wrap;" // 关键:保留空白符并允许自动换行 + "}" + ); + + headerText.clear(); + headerText<<"TAG"; + ui->tableWidget_other->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tableWidget_other->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tableWidget_other->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableWidget_other->setColumnCount(headerText.count()); + ui->tableWidget_other->setHorizontalHeaderLabels(headerText); + ui->tableWidget_other->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + ui->tableWidget_other->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->tableWidget_other->setStyleSheet( + "QTableWidget::item {" + " padding: 5px;" // 可选:增加内边距 + " white-space: pre-wrap;" // 关键:保留空白符并允许自动换行 + "}" + ); +} + +void BayMeasureDlg::addMeasure(MeasurementInfo info,int mode) +{ + if(mode == 0){ //新建 + if(_mapMeasure.contains(info.name)) + return; + int row = ui->tableWidget_local->rowCount(); + ui->tableWidget_local->insertRow(row); + + QTableWidgetItem* tagItem = new QTableWidgetItem(info.tag); + ui->tableWidget_local->setItem(row, 0, tagItem); + + QTableWidgetItem* nameItem = new QTableWidgetItem(info.name); + ui->tableWidget_local->setItem(row, 1, nameItem); + + QTableWidgetItem* deviceItem = new QTableWidgetItem(info.sDevice); + ui->tableWidget_local->setItem(row, 2, deviceItem); + + QTableWidgetItem* channelItem = new QTableWidgetItem(info.sChannel); + ui->tableWidget_local->setItem(row, 3, channelItem); + + QString sType; + if(info.type == 0){ + sType = "遥测"; + } + else if(info.type == 1){ + sType = "遥信"; + } + else if(info.type == 2){ + sType = "遥控"; + } + else if(info.type == 3){ + sType = "遥调"; + } + else if(info.type == 4){ + sType = "整定值"; + } + QTableWidgetItem* typeItem = new QTableWidgetItem(sType); + ui->tableWidget_local->setItem(row, 4, typeItem); + + QTableWidgetItem* sizeItem = new QTableWidgetItem(QString::number(info.size)); + ui->tableWidget_local->setItem(row, 5, sizeItem); + + QTableWidgetItem* enableItem = new QTableWidgetItem(info.bEnable?"启用":"关闭"); + ui->tableWidget_local->setItem(row, 6, enableItem); + + if(_bayProperty){ + QUuid bayId = _bayProperty->uuid(); + QUuid itemId = QUuid("11111111-1111-1111-1111-111111111111"); + info.bayUuid = bayId; + info.componentUuid = itemId; + _mapMeasure.insert(info.name,info); + } + } + else if(mode == 1){ //修改 + QAbstractItemModel* model = ui->tableWidget_local->model(); + int rowCount = model->rowCount(); + + QModelIndex index; + for (int row = 0; row < rowCount; ++row) { + QModelIndex col1Index = model->index(row, 1); + QVariant data = model->data(col1Index, Qt::DisplayRole); + + if (data.toString() == info.name) { + // 返回本行第0列的QModelIndex + index = model->index(row, 1); + } + } + + QString sName = index.data().toString(); + if(_mapMeasure.contains(sName)){ + + auto itemDevice = ui->tableWidget_local->item(index.row(),2); + if(itemDevice){ + itemDevice->setText(info.sDevice); + } + + auto itemChannel = ui->tableWidget_local->item(index.row(),3); + if(itemChannel){ + itemChannel->setText(info.sChannel); + } + + auto itemType = ui->tableWidget_local->item(index.row(),4); + if(itemType){ + QString sType; + if(info.type == 0){ + sType = "遥测"; + } + else if(info.type == 1){ + sType = "遥信"; + } + else if(info.type == 2){ + sType = "遥控"; + } + else if(info.type == 3){ + sType = "遥调"; + } + else if(info.type == 4){ + sType = "整定值"; + } + + itemType->setText(sType); + } + + auto itemSize = ui->tableWidget_local->item(index.row(),5); + if(itemSize){ + itemSize->setText(QString::number(info.size)); + } + + auto itemEnable = ui->tableWidget_local->item(index.row(),6); + if(itemEnable){ + itemEnable->setText(info.bEnable?"启用":"关闭"); + } + + if(_bayProperty){ + QUuid bayId = _bayProperty->uuid(); + info.bayUuid = bayId; + _mapMeasure[info.name] = info; + } + } + } +} + +void BayMeasureDlg::addOtherMeasure(QStringList lst) +{ + for(auto& str:lst){ + int row = ui->tableWidget_other->rowCount(); + ui->tableWidget_other->insertRow(row); + + QTableWidgetItem* tagItem = new QTableWidgetItem(str); + ui->tableWidget_local->setItem(row, 0, tagItem); + } +} + +void BayMeasureDlg::showDlg(BayProperty* pBay) +{ + show(); + setPropertyValue(pBay); +} + +void BayMeasureDlg::onAddClicked() +{ + if(_measureDlg == nullptr){ + _measureDlg = new MeasureSettingDlg(this); + _measureDlg->setParentType(1); + _measureDlg->setBayMeasure(this); + } + int curType = 0; //当前对象类型 + _measureDlg->showDlg(curType,PropertyStateInfo()); +} + +void BayMeasureDlg::onDeleteClicked() +{ + // 获取当前选中的索引 + QModelIndexList selectedIndexes = ui->tableWidget_local->selectionModel()->selectedRows(); + if (selectedIndexes.isEmpty()) { + return; // 没有选中任何行 + } + + // 获取当前选中的第一项索引 + QModelIndex index = selectedIndexes.first(); + if (!index.isValid()) { + return; + } + + QModelIndex indexName = index.sibling(index.row(),1); + QString sName = indexName.data().toString(); + if(_mapMeasure.contains(sName)){ + MeasurementInfo info = _mapMeasure.take(sName); + } + + int currentRow = ui->tableWidget_local->currentRow(); + if (currentRow == -1) { + return; // 没有选中行 + } + + ui->tableWidget_local->removeRow(currentRow); +} + +void BayMeasureDlg::onModifyClicked() +{ + // 获取当前选中的索引 + QModelIndexList selectedIndexes = ui->tableWidget_local->selectionModel()->selectedRows(); + if (selectedIndexes.isEmpty()) { + return; // 没有选中任何行 + } + + // 获取当前选中的第一项索引 + QModelIndex index = selectedIndexes.first(); + if (!index.isValid()) { + return; + } + + QModelIndex indexName = index.sibling(index.row(),1); + QString sName = indexName.data().toString(); + if(_mapMeasure.contains(sName)){ + auto info = _mapMeasure.value(sName); + if(_measureDlg == nullptr){ + _measureDlg = new MeasureSettingDlg(this); + _measureDlg->setParentType(1); + _measureDlg->setBayMeasure(this); + } + + _measureDlg->showDlg(info,PropertyStateInfo()); + } +} + +void BayMeasureDlg::onIndexRbtnClicked(const QPoint &pos) +{ + // 获取当前点击的位置对应的索引 + QModelIndex index = ui->tableWidget_local->indexAt(pos); + if (!index.isValid()) { + return; // 如果点击的是空白区域,直接返回 + } + + QMenu menu; + QAction *modifyAction = new QAction("修改量测", this); + QAction *deleteAction = new QAction("移除量测", this); + menu.addAction(modifyAction); + menu.addAction(deleteAction); + + // 连接删除菜单项的触发信号与槽函数 + connect(modifyAction, &QAction::triggered, this, &BayMeasureDlg::onModifyClicked); + connect(deleteAction, &QAction::triggered, this, &BayMeasureDlg::onDeleteClicked); + + // 在点击位置显示菜单 + menu.exec(ui->tableWidget_local->mapToGlobal(pos)); +} + +void BayMeasureDlg::onOkClicked() +{ + getPropertyValue(_bayProperty); + hide(); +} + +void BayMeasureDlg::onCancelClicked() +{ + hide(); +} diff --git a/diagramCavas/source/cornerMonitorLauncher.cpp b/diagramCavas/source/cornerMonitorLauncher.cpp new file mode 100644 index 0000000..569a38f --- /dev/null +++ b/diagramCavas/source/cornerMonitorLauncher.cpp @@ -0,0 +1,77 @@ +#include "cornerMonitorLauncher.h" +#include +#include +#include +#include + +CornerMonitorLauncher::CornerMonitorLauncher(QMdiArea* parent) + : QWidget(parent) + ,m_mdiArea(parent) +{ + setFixedSize(48, 48); + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + setAttribute(Qt::WA_TranslucentBackground); + + positionAtCorner(); +} + +CornerMonitorLauncher::~CornerMonitorLauncher() +{ + +} + +void CornerMonitorLauncher::showDlg() +{ + show(); + positionAtCorner(); +} + +void CornerMonitorLauncher::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // 绘制圆形按钮 + painter.setBrush(QColor(255, 255, 255, 240)); + painter.setPen(QPen(QColor(200, 200, 200), 1)); + painter.drawEllipse(rect().adjusted(1, 1, -1, -1)); + + // 绘制+号 + painter.setPen(QPen(QColor(100, 100, 100), 2)); + painter.drawLine(width()/2 - 8, height()/2, width()/2 + 8, height()/2); + painter.drawLine(width()/2, height()/2 - 8, width()/2, height()/2 + 8); +} + +void CornerMonitorLauncher::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + showQuickMenu(); + } +} + +void CornerMonitorLauncher::showQuickMenu() { + QMenu menu; + menu.setStyleSheet(R"( + QMenu { + background: white; + border: 1px solid #dee2e6; + border-radius: 4px; + } + QMenu::item { + padding: 6px 20px 6px 10px; + } + QMenu::item:selected { + background: #f8f9fa; + } + )"); + + menu.addAction("加载运行时监控", [&]{ emit openLoadMonitorDlg();}); + menu.exec(mapToGlobal(QPoint(0, height()))); +} + +void CornerMonitorLauncher::positionAtCorner() { + QPoint topRight = m_mdiArea->mapToGlobal(m_mdiArea->rect().topRight()); + int x = topRight.x() - width() - 20; + int y = topRight.y() + 20; + move(x, y); +} diff --git a/diagramCavas/source/createHMIdlg.cpp b/diagramCavas/source/createHMIdlg.cpp new file mode 100644 index 0000000..5a44eee --- /dev/null +++ b/diagramCavas/source/createHMIdlg.cpp @@ -0,0 +1,48 @@ +#include "createHMIdlg.h" +#include "common/frontend/monitor_item.h" +#include "dataBase.h" +#include "ui_createHMIdlg.h" + +CreateHMIdlg::CreateHMIdlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::createHMIdlg) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +CreateHMIdlg::~CreateHMIdlg() +{ + delete ui; +} + +void CreateHMIdlg::initial() +{ + connect(ui->btn_cancel,&QPushButton::clicked,this,&CreateHMIdlg::onCancelClicked); + connect(ui->btn_ok,&QPushButton::clicked,this,&CreateHMIdlg::onSaveClicked); +} + +void CreateHMIdlg::showDlg() +{ + show(); + ui->cb_structure->clear(); + QList lstMonitor = DataBase::GetInstance()->getAllMonitor(); + for(auto& info:lstMonitor){ + ui->cb_structure->addItem(info.name); + } + ui->cb_structure->addItem("无"); +} + +void CreateHMIdlg::onCancelClicked() +{ + hide(); +} + +void CreateHMIdlg::onSaveClicked() +{ + QString sName = ui->cb_structure->currentText(); + if(sName != "无") + emit createHMI(ui->lineEdit->text(),sName); + hide(); +} diff --git a/diagramCavas/source/ctExtraInfoDlg.cpp b/diagramCavas/source/ctExtraInfoDlg.cpp new file mode 100644 index 0000000..5fef9b6 --- /dev/null +++ b/diagramCavas/source/ctExtraInfoDlg.cpp @@ -0,0 +1,329 @@ +#include "ctExtraInfoDlg.h" +#include "ui_ctExtraInfoDlg.h" +#include "baseProperty.h" +#include "basePropertyManager.h" +#include +#include +#include +#include + +CtExtraInfoDlg::CtExtraInfoDlg(QWidget *parent) + : BaseContentDlg(parent) + , ui(new Ui::ctExtraInfoDlg) +{ + ui->setupUi(this); + _stateGroup_ct = new QButtonGroup(this); + _stateGroup_ct->addButton(ui->rb_tpt_ct,1); + _stateGroup_ct->addButton(ui->rb_zst_ct,0); + + connect(ui->btn_add_ct,&QPushButton::clicked,this,&CtExtraInfoDlg::onAddClicked); + + ui->tb_ct->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + _count = 1; + + ui->tb_ct->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->tb_ct, &QTableWidget::customContextMenuRequested, this, &CtExtraInfoDlg::onTableCustomContextMenuRequested); +} + +CtExtraInfoDlg::~CtExtraInfoDlg() +{ + delete ui; +} + +void CtExtraInfoDlg::createGroupView(GroupStateInfo infos) +{ + for(auto& info:infos.info) { + PropertyContentInfo inf; + inf.proTag = info.tagName; + inf.proName = info.name; + inf.proType = info.type; + _mapPro.insert(info.tagName,inf); + } +} + +QMap CtExtraInfoDlg::getPropertyValue(BaseProperty* pPro) +{ + QMap map; + + for(auto &pro:_mapPro) + { + PropertyStateInfo info; + info.tagName = pro.proTag; + info.type = pro.proType; + info.name = pro.proName; + if(info.name == "额定电流(A)" || info.tagName == "in_a") //此处应为类型名 + { + info.defaultValue = ui->le_ratedCurrent->text(); + } + else if(info.name == "工频耐压(V/1min)" || info.tagName == "uac_v_1min") + { + info.defaultValue = ui->le_pfwv_ct->text(); + } + else if(info.name == "冲击耐压(V)" || info.tagName == "uimp_v") + { + info.defaultValue = ui->le_iwv_ct->text(); + } + else if(info.name == "动稳定电流(A)" || info.tagName == "dsc_a") + { + info.defaultValue = ui->le_dsc_ct->text(); + } + else if(info.name == "仪表保安系数" || info.tagName == "fs") + { + info.defaultValue = ui->le_isf->text(); + } + else if(info.name == "热稳定电流(A)" || info.tagName == "ith_a") + { + info.defaultValue = ui->le_sttc->text(); + } + else if(info.name == "额定频率(Hz)" || info.tagName == "fn_hz") + { + info.defaultValue = ui->le_rf_ct->text(); + } + else if(info.name == "相数" || info.tagName == "phase_num") + { + if(ui->rb_tpt_ct->isChecked()) + info.defaultValue = 1; + else + info.defaultValue = 0; + } + else if(info.name == "CT绕组" || info.tagName == "ct_winding") + { + QJsonObject object; + QJsonArray arr; + for(auto &info:_mapCT) + { + QJsonObject obj; + obj["index"] = info.index; + obj["scope"] = info.scope; + obj["accuracy"] = info.accuracy; + obj["volume"] = info.volume; + obj["ratio"] = info.ratio; + obj["polarity"] = info.polarity; + arr.push_back(obj); + } + object["winding"] = arr; + info.defaultValue = object; + } + map.insert(pro.proTag,info); + } + pPro->setDataChanged(true); + return map; +} + +void CtExtraInfoDlg::setPropertyValue(QVariant var) +{ + QMap map = var.value>(); + for(auto &info:map) + { + if(info.name == "额定电流(A)" || info.tagName == "in_a") //此处应为类型名 + { + ui->le_ratedCurrent->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "工频耐压(V/1min)" || info.tagName == "uac_v_1min") + { + ui->le_pfwv_ct->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "冲击耐压(V)" || info.tagName == "uimp_v") + { + ui->le_iwv_ct->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "动稳定电流(A)" || info.tagName == "dsc_a") + { + ui->le_dsc_ct->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "仪表保安系数" || info.tagName == "fs") + { + if(info.defaultValue.toString() == "null") + ui->le_isf->setText(0); + ui->le_isf->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "热稳定电流(A)" || info.tagName == "ith_a") + { + ui->le_sttc->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "额定频率(Hz)" || info.tagName == "fn_hz") + { + ui->le_rf_ct->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "相数" || info.tagName == "phase_num") + { + if(info.defaultValue.toInt() == 1) + ui->rb_tpt_ct->setChecked(true); + else + ui->rb_zst_ct->setChecked(true); + } + else if(info.name == "CT绕组" || info.tagName == "ct_winding") + { + QString jsonString = info.defaultValue.toString(); + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8().data()); + QJsonObject jsonObject = jsonDocument.object(); + + QJsonObject object = jsonObject; + QJsonArray arr = object["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + QJsonObject node = jsonObj.toObject(); + int index = node["index"].toInt(); + QString sRatioRange = node["scope"].toString(); + QString sAccuracy = node["accuracy"].toString(); + QString sVolume = node["volume"].toString(); + double dRatio = node["ratio"].toDouble(); + bool bPolarity = node["polarity"].toBool(); + + addTableRow(sRatioRange,sAccuracy,sVolume,dRatio,bPolarity,index); + } + } + } +} + +void CtExtraInfoDlg::onAddClicked() +{ + QString sRatioRange = ui->le_tr_range_ct->text(); + QString sAccuracy= ui->le_ac_ct->text(); + QString sVolume = ui->le_slc_ct->text(); + double dRatio = ui->le_tr_ct->text().toDouble(); + bool bPolarity = ui->cb_polarity->checkState(); + int index = -1; + if(ui->rb_zst_ct->isChecked()) + index = 0; + addTableRow(sRatioRange,sAccuracy,sVolume,dRatio,bPolarity,index); +} + +void CtExtraInfoDlg::onTableCustomContextMenuRequested(const QPoint &pos) { + QModelIndex index = ui->tb_ct->indexAt(pos); + if (!index.isValid()) { + return; + } + + int row = index.row(); + + // 创建右键菜单 + QMenu menu(this); + QAction *deleteAction = menu.addAction("删除此行"); + + // 连接删除操作 + connect(deleteAction, &QAction::triggered, this, [this, row]() { + deleteRowWithReindex(row); + updateLables(); + }); + + menu.exec(ui->tb_ct->viewport()->mapToGlobal(pos)); +} +void CtExtraInfoDlg::addTableRow(QString sRatioRange,QString sAccuracy,QString sVolume,double dRatio,bool bPolarity,int index) +{ + if(_mapCT.contains(QString::number(index))) + { + return; + } + + CtExtraInfo info; + if(index == -1){ //缺省id时新建,否则加载 + info.index = _count; + _count += 1; + } + else{ + info.index = index; + } + + int row = ui->tb_ct->rowCount(); + ui->tb_ct->insertRow(row); + + //index + QTableWidgetItem *item = new QTableWidgetItem(QString::number(info.index)); + item->setData(Qt::UserRole,info.index); + ui->tb_ct->setItem(row, 0, item); + + //变比范围 + ui->tb_ct->setItem(row, 1, new QTableWidgetItem(sRatioRange)); + + //精度等级 + ui->tb_ct->setItem(row, 2, new QTableWidgetItem(sAccuracy)); + + //二次负载容量 + ui->tb_ct->setItem(row, 3, new QTableWidgetItem(sVolume)); + + //变比 + ui->tb_ct->setItem(row, 4, new QTableWidgetItem(QString::number(dRatio))); + + //极性 + ui->tb_ct->setItem(row, 5, new QTableWidgetItem(QString::number(bPolarity? 1 : -1))); + + info.scope = sRatioRange; + info.accuracy = sAccuracy; + info.volume = sVolume; + info.ratio = dRatio; + info.polarity = bPolarity? 1 : -1; + _mapCT.insert(QString::number(info.index),info); + + updateLables(); +} + +void CtExtraInfoDlg::updateShowLabel(QStringList lst) +{ + _curLabels = lst; + ui->label_title_ct->setText(_curLabels.join(" ")); +} + +void CtExtraInfoDlg::updateLables() +{ + QStringList lst; //更新显示标签 + for(auto& info:_mapCT){ + QString sLabel = info.scope+" "+info.accuracy+" "+info.volume; + lst.append(sLabel); + } + updateShowLabel(lst); +} + +void CtExtraInfoDlg::deleteRowWithReindex(int row) { + // 1. 获取要删除的ID + QTableWidgetItem* pFirstItem = ui->tb_ct->item(row, 0); + if (!pFirstItem) return; + + int deletedId = pFirstItem->data(Qt::UserRole).toInt(); + QString deletedKey = QString::number(deletedId); + + // 2. 从表格中删除行 + ui->tb_ct->removeRow(row); + + // 3. 从_mapCT中删除对应项 + if (_mapCT.contains(deletedKey)) { + _mapCT.remove(deletedKey); + } + + // 4. 重新排序和更新index + reorderMapAndUpdateIndices(row); +} + +void CtExtraInfoDlg::reorderMapAndUpdateIndices(int startRow) { + int totalRows = ui->tb_ct->rowCount(); + + // 遍历从startRow开始的所有行 + for (int row = startRow; row < totalRows; ++row) { + QTableWidgetItem* idItem = ui->tb_ct->item(row, 0); + if (!idItem) continue; + + int currentId = idItem->data(Qt::UserRole).toInt(); + QString currentKey = QString::number(currentId); + + // 计算新的ID和索引 + int newId = row + 1; // 新的ID + int newIndex = row + 1; // 新的索引 + + if (_mapCT.contains(currentKey)) { + // 获取并更新数据 + CtExtraInfo info = _mapCT[currentKey]; + info.index = newIndex; + + // 从旧位置移除 + _mapCT.remove(currentKey); + + // 添加到新位置 + QString newKey = QString::number(newId); + _mapCT[newKey] = info; + + // 更新表格显示 + idItem->setText(QString::number(newId)); + idItem->setData(Qt::UserRole, newId); + } + } +} diff --git a/diagramCavas/source/dataSourceDlg.cpp b/diagramCavas/source/dataSourceDlg.cpp new file mode 100644 index 0000000..beceb1d --- /dev/null +++ b/diagramCavas/source/dataSourceDlg.cpp @@ -0,0 +1,648 @@ +#include "dataSourceDlg.h" +#include "ui_dataSourceDlg.h" +#include +#include +#include "structDataSource.h" +#include "extraPropertyManager.h" +#include "propertyType/dataSourceType.h" +#include +//#include "global.h" + +DataSourceDlg::DataSourceDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::dataSourceDlg) + ,_treeModel(nullptr) + ,m_currentCategoryItem(nullptr) + ,_pExtraProManager(nullptr) + ,_curProperty(nullptr) +{ + ui->setupUi(this); + setModal(true); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + //setAttribute(Qt::WA_DeleteOnClose); + initial(); +} + +DataSourceDlg::~DataSourceDlg() +{ + delete ui; +} + +void DataSourceDlg::initial() +{ + connect(ui->btn_ok,&QPushButton::clicked,this,&DataSourceDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&DataSourceDlg::onCancelClicked); + + m_dataSource = new StructDataSource(this); + _treeModel = new QStandardItemModel(this); + _treeModel->setHorizontalHeaderLabels(QStringList() << "属性层级结构"); + ui->treeView->setModel(_treeModel); + connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &DataSourceDlg::onTreeSelectionChanged); + + connect(ui->listWidget, &QListWidget::itemClicked, this,&DataSourceDlg::onListWidgetClicked); + + _pExtraProManager = new ExtraPropertyManager(this); + _pExtraProManager->initial(); + + loadData(); + + connect(this, &DataSourceDlg::listWidgetUpdated, this, [this]() { + if (!m_targetPropertyCode.isEmpty()) { + for (int i = 0; i < ui->listWidget->count(); ++i) { + QListWidgetItem* listItem = ui->listWidget->item(i); + if (listItem && listItem->text() == m_targetPropertyCode) { + ui->listWidget->setCurrentRow(i); + ui->listWidget->scrollToItem(listItem, QAbstractItemView::EnsureVisible); + emit ui->listWidget->itemClicked(listItem); + m_targetPropertyCode.clear(); + break; + } + } + } + }); + +} + +void DataSourceDlg::loadData() +{ + if(_pExtraProManager) + m_dataSource->loadExtrapro(_pExtraProManager->geAlltProperty()); +} + +void DataSourceDlg::showDlg(DataSourceType tpe) +{ + if(_pExtraProManager) + { + //show(); + clearItems(); + auto& mapExtra = m_dataSource->allProperties; + QStandardItem* root = _treeModel->invisibleRootItem(); + for(auto& pro:mapExtra){ + QStandardItem* propertyItem = new QStandardItem(); + addItemToView(pro,"name",root,propertyItem); + } + + ui->treeView->expandAll(); + + if(!tpe.sCode.isEmpty()){ + expandToNodeByCode(tpe.sCode,_treeModel); + } + } +} + +DataSourceType DataSourceDlg::getCurData() +{ + DataSourceType type; + if(_curProperty){ + type.sPara = _curProperty->data(Qt::UserRole).toString(); + type.sCode = _curProperty->data(Qt::UserRole+1).toString(); + } + return type; +} + +void DataSourceDlg::addItemToView(const ExtraProperty& property, + const QString& displayMode, // "name" 或 "tag" + QStandardItem* root, + QStandardItem* pItem) +{ + // 设置叶子节点的显示文本(使用name或tag) + if (displayMode == "name") { + pItem->setText(property.name.isEmpty() ? "未命名属性" : property.name); + } else { + pItem->setText(property.tag.isEmpty() ? "unknown_property" : property.tag); + } + + // 在叶子节点存储完整的属性信息 + QVariantMap propertyData; + propertyData["property"] = QVariant::fromValue(property); + propertyData["displayMode"] = displayMode; + propertyData["code"] = property.code; + pItem->setData(propertyData, Qt::UserRole + 1); + + QVector levels = { + {(displayMode == "name") ? property.grid_name : property.grid_tag, + property.grid_name, property.grid_tag, true, + (displayMode == "name") ? "未命名电网" : "unknown_grid"}, + + {(displayMode == "name") ? property.zone_name : property.zone_tag, + property.zone_name, property.zone_tag, true, + (displayMode == "name") ? "未命名区域" : "unknown_zone"}, + + {(displayMode == "name") ? property.station_name : property.station_tag, + property.station_name, property.station_tag, true, + (displayMode == "name") ? "未命名站点" : "unknown_station"}, + + {property.currentLevel, + property.currentLevel, property.currentLevel, false, + (displayMode == "name") ? "通用层级" : "common_level"}, + + {(displayMode == "name") ? property.bay_name : property.bay_tag, + property.bay_name, property.bay_tag, false, + (displayMode == "name") ? "间隔" : "bay"}, + + {property.component_name.isEmpty() ? + (displayMode == "name") ? "未命名设备" : property.component_uuid.toString() : + (displayMode == "name") ? property.component_name : property.component_uuid.toString(), + property.component_name, property.component_uuid.toString(), true, + (displayMode == "name") ? "未命名设备" : "unknown_component"} + }; + + QStandardItem* currentParent = root; + + for (size_t i = 0; i < levels.size(); ++i) { + const auto& level = levels[i]; + bool isRequired = level.isRequired; + bool isEmpty = (displayMode == "name") ? + (level.nameValue.isEmpty() && level.displayText.isEmpty()) : + (level.tagValue.isEmpty() && level.displayText.isEmpty()); + + if (!isRequired && isEmpty) { + continue; + } + + // 确定显示文本 + QString displayText = level.displayText; + bool isMissing = false; + + if (displayText.isEmpty()) { + if (isRequired) { + displayText = level.placeholder; + isMissing = true; + } else { + // 可选层级为空,跳过 + continue; + } + } + + // 查找当前层级是否已存在 + QStandardItem* foundChild = nullptr; + + for (int row = 0; row < currentParent->rowCount(); ++row) { + QStandardItem* child = currentParent->child(row, 0); + if (child && child->text() == displayText) { + // 检查是否为同一层级 + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + int childLevelIndex = childData["levelIndex"].toInt(); + + if (childLevelIndex == static_cast(i)) { + foundChild = child; + break; + } + } + } + + if (foundChild) { + currentParent = foundChild; + } else { + QStandardItem* newNode = new QStandardItem(displayText); + QVariantMap levelData; + levelData["levelIndex"] = static_cast(i); + levelData["levelType"] = getLevelType(i); + + newNode->setData(levelData, Qt::UserRole + 1); + currentParent->appendRow(newNode); + currentParent = newNode; + } + } + + // 现在currentParent是component节点 + // 处理group层级(第7层) + QStandardItem* groupItem = processGroupLevel(currentParent, property); + + // 处理category层级(在group下) + processCategoryLevel(groupItem, property); +} + +void DataSourceDlg::onOkClicked() +{ + /*if(_curTargetHandel){ + if(_curProperty){ + DataSourceType type; + _curTargetHandel->setVar(QVariant::fromValue(type)); + } + }*/ + accept(); +} + +void DataSourceDlg::onCancelClicked() +{ + reject(); +} + +void DataSourceDlg::onTreeSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { + Q_UNUSED(previous); + + clearPropertyList(); + + if (!current.isValid()) { + return; + } + + QStandardItem* item = _treeModel->itemFromIndex(current); + if (!item) { + return; + } + + QVariantMap itemData = item->data(Qt::UserRole + 1).toMap(); + QString levelType = itemData.value("levelType", "").toString(); + + if (levelType == "category") { + // 点击分类节点,从category节点获取属性 + loadCategoryProperties(item); + + // 检查是否需要选中特定的listWidgetItem + if (!m_targetPropertyCode.isEmpty()) { + // 在listWidget中查找并选中对应的item + for (int i = 0; i < ui->listWidget->count(); ++i) { + QListWidgetItem* listItem = ui->listWidget->item(i); + if (listItem && listItem->text() == m_targetPropertyCode) { + ui->listWidget->setCurrentRow(i); + ui->listWidget->scrollToItem(listItem, QAbstractItemView::EnsureVisible); + + // 触发listWidget的itemClicked信号 + emit ui->listWidget->itemClicked(listItem); + break; + } + } + } + } else { + m_targetPropertyCode.clear(); // 清空目标code + } +} + +void DataSourceDlg::onListWidgetClicked(QListWidgetItem* pItem) +{ + QString sPara = pItem->data(Qt::UserRole).toString(); + ui->listWidget_select->clear(); + QListWidgetItem* pCur = new QListWidgetItem(pItem->text()+":"+sPara); + pCur->setData(Qt::UserRole,sPara); + pCur->setData(Qt::UserRole+1,pItem->text()); + ui->listWidget_select->addItem(pCur); + _curProperty = pCur; +} + +void DataSourceDlg::clearPropertyList() +{ + ui->listWidget->clear(); + ui->listWidget_select->clear(); + m_currentCategoryItem = nullptr; +} + +void DataSourceDlg::loadCategoryProperties(QStandardItem* categoryItem) { + m_currentCategoryItem = categoryItem; + + // 从category节点获取属性列表 + QVariantMap categoryData = categoryItem->data(Qt::UserRole + 1).toMap(); + QVector properties = categoryData["properties"].value>(); + + if (properties.isEmpty()) { + // 如果没有属性,从DataManager重新获取 + properties = getCategoryPropertiesFromDataManager(categoryData); + } + + if (properties.isEmpty()) { + clearItems(); + return; + } + + QTimer::singleShot(50, this, [this,properties]() { + updatePropertyList(properties); + }); + +} + +QVector DataSourceDlg::getCategoryPropertiesFromDataManager(const QVariantMap& categoryData) { + QString groupTag = categoryData.value("groupTag").toString(); + QString modelName = categoryData.value("modelName").toString(); + QString paraType = categoryData.value("paraType").toString(); + QString sourceType = categoryData.value("sourceType").toString(); + QString componentUuid = categoryData.value("component_uuid").toString(); + + QVector result; + + for (auto it = m_dataSource->allProperties.begin(); + it != m_dataSource->allProperties.end(); ++it) { + const ExtraProperty& prop = it.value(); + + bool match = (prop.group_tag == groupTag) && + (prop.sourceType == sourceType) && + (prop.type_tag == paraType) && + (prop.component_uuid.toString() == componentUuid); + + if (sourceType == "property") { + match = match && (prop.sourceConfig.value("modelName").toString() == modelName); + } + + if (match) { + result.append(prop); + } + } + + return result; +} + +void DataSourceDlg::updateCategoryProperties(QStandardItem* categoryItem, + const ExtraProperty& property) { + QVariantMap categoryData = categoryItem->data(Qt::UserRole + 1).toMap(); + QVector properties = categoryData["properties"].value>(); + + // 检查属性是否已存在 + bool exists = false; + for (const auto& prop : properties) { + if (prop.code == property.code) { + exists = true; + break; + } + } + + if (!exists) { + properties.append(property); + categoryData["properties"] = QVariant::fromValue(properties); + + int newCount = properties.size(); + categoryData["propertyCount"] = newCount; + + // 更新显示文本 + QString baseName = property.type_name; + categoryItem->setText(QString("%1 (%2)").arg(baseName).arg(newCount)); + + // 更新工具提示 + QString tooltip = QString("属性数量: %1") + .arg(newCount); + categoryItem->setToolTip(tooltip); + + categoryItem->setData(categoryData, Qt::UserRole + 1); + } +} + +QStandardItem* DataSourceDlg::processGroupLevel(QStandardItem* componentItem, + const ExtraProperty& property) { + QString groupDisplayText = (property.group_name.isEmpty() ? + property.group_tag : property.group_name); + + if (groupDisplayText.isEmpty()) { + groupDisplayText = "未命名分组"; + } + + // 查找group节点 + for (int row = 0; row < componentItem->rowCount(); ++row) { + QStandardItem* child = componentItem->child(row, 0); + if (child) { + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + QString levelType = childData.value("levelType").toString(); + + if (levelType == "group") { + // 检查是否是同一个group + QString existingGroupTag = childData.value("groupTag", "").toString(); + QString existingModelName = childData.value("modelName", "").toString(); + QString propertyModelName = property.sourceConfig.value("modelName").toString(); + + if (existingGroupTag == property.group_tag) { + if (property.sourceType == "property") { + // 对于property类型,还需要检查modelName + if (existingModelName == propertyModelName) { + return child; + } + } else { + // 对于measurement类型,只需要groupTag匹配 + return child; + } + } + } + } + } + + // 创建新的group节点 + QStandardItem* groupItem = new QStandardItem(groupDisplayText); + QVariantMap groupData; + groupData["levelIndex"] = 6; // 第7层 + groupData["levelType"] = "group"; + groupData["groupName"] = property.group_name; + groupData["groupTag"] = property.group_tag; + groupData["sourceType"] = property.sourceType; + + if (property.sourceType == "property") { + groupData["modelName"] = property.sourceConfig.value("modelName"); + } + + groupData["properties"] = QVariant::fromValue(QVector()); + groupItem->setData(groupData, Qt::UserRole + 1); + + // 设置工具提示 + QString tooltip = QString("分组: %1\n类型: %2") + .arg(groupDisplayText) + .arg(property.sourceType == "property" ? "属性" : "量测"); + groupItem->setToolTip(tooltip); + + componentItem->appendRow(groupItem); + return groupItem; +} + +// 处理category层级 +void DataSourceDlg::processCategoryLevel(QStandardItem* groupItem, + const ExtraProperty& property) { + QString categoryName = property.type_name; + QString paraType = property.type_tag; + + if (categoryName.isEmpty() || paraType.isEmpty()) { + qWarning() << "Invalid category or paraType for property:" << property.code; + return; + } + + // 查找category节点 + for (int row = 0; row < groupItem->rowCount(); ++row) { + QStandardItem* child = groupItem->child(row, 0); + if (child) { + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + QString levelType = childData.value("levelType").toString(); + QString childParaType = childData.value("paraType", "").toString(); + + if (levelType == "category" && childParaType == paraType) { + // 找到对应的category节点,更新属性列表 + updateCategoryProperties(child, property); + return; + } + } + } + + // 创建新的category节点 + QStandardItem* categoryItem = new QStandardItem(categoryName); + QVariantMap categoryData; + categoryData["levelType"] = "category"; + categoryData["paraType"] = paraType; + categoryData["sourceType"] = property.sourceType; + categoryData["groupTag"] = property.group_tag; + categoryData["groupName"] = property.group_name; + + if (property.sourceType == "property") { + categoryData["modelName"] = property.sourceConfig.value("modelName"); + } + + // 初始化属性列表 + QVector properties; + properties.append(property); + categoryData["properties"] = QVariant::fromValue(properties); + categoryData["propertyCount"] = 1; + + categoryItem->setData(categoryData, Qt::UserRole + 1); + + // 设置显示文本(包含数量) + categoryItem->setText(QString("%1 (1)").arg(categoryName)); + + // 设置工具提示 + QString tooltip = QString("分类: %1\n参量类型: %2\n属性数量: 1") + .arg(categoryName) + .arg(categoryName); + categoryItem->setToolTip(tooltip); + + groupItem->appendRow(categoryItem); +} + +void DataSourceDlg::updatePropertyList(QVector vec) +{ + ui->listWidget->clear(); + + for(auto& pro:vec){ + QListWidgetItem* pItem = new QListWidgetItem(pro.code); + pItem->setData(Qt::UserRole, pro.connect_para); + ui->listWidget->addItem(pItem); + } + + QTimer::singleShot(50, this, [this]() { + emit listWidgetUpdated(); + }); +} + +void DataSourceDlg::expandToNodeByCode(const QString& code, QStandardItemModel* model) +{ + if (!model || code.isEmpty()) { + return; + } + + // 设置目标code + m_targetPropertyCode = code; + + // 查找节点 + QStandardItem* targetNode = findCategoryNodeByPropertyCode(model->invisibleRootItem(), code); + + if (!targetNode) { + qDebug() << "未找到code为" << code << "的节点"; + m_targetPropertyCode.clear(); + return; + } + + // 展开路径 + QList path; + QStandardItem* current = targetNode; + + while (current && current != model->invisibleRootItem()) { + path.prepend(current); + current = current->parent(); + } + + for (QStandardItem* item : path) { + QModelIndex index = model->indexFromItem(item); + if (index.isValid()) { + ui->treeView->expand(index); + } + } + + // 🔴 关键:阻塞currentChanged信号 + QItemSelectionModel* selectionModel = ui->treeView->selectionModel(); + if (selectionModel) { + selectionModel->blockSignals(true); + } + + QModelIndex targetIndex = model->indexFromItem(targetNode); + + // 设置当前项(这会触发currentChanged,但信号被阻塞了) + selectionModel->setCurrentIndex(targetIndex, QItemSelectionModel::ClearAndSelect); + + // 手动调用处理函数 + onTreeSelectionChanged(targetIndex, QModelIndex()); + + // 恢复信号 + if (selectionModel) { + selectionModel->blockSignals(false); + } + + // 滚动 + ui->treeView->scrollTo(targetIndex, QAbstractItemView::EnsureVisible); +} + +void DataSourceDlg::selectListItemByCode(const QString& code) +{ + for (int i = 0; i < ui->listWidget->count(); ++i) { + QListWidgetItem* listItem = ui->listWidget->item(i); + if (listItem && listItem->text() == code) { + ui->listWidget->setCurrentRow(i); + ui->listWidget->scrollToItem(listItem, QAbstractItemView::EnsureVisible); + return; + } + } +} + +QStandardItem* DataSourceDlg::findCategoryNodeByPropertyCode(QStandardItem* item, const QString& code) +{ + if (!item) { + return nullptr; + } + + QVariant itemData = item->data(Qt::UserRole + 1); + + if (itemData.isValid() && itemData.canConvert()) { + QVariantMap dataMap = itemData.toMap(); + + // 检查当前节点是否是category节点 + QString levelType = dataMap.value("levelType", "").toString(); + + if (levelType == "category") { + // 检查category节点的properties列表 + if (dataMap.contains("properties")) { + QVector properties = dataMap["properties"].value>(); + for (int i = 0; i < properties.size(); ++i) { + if (properties[i].code == code) { + return item; // 找到包含该属性的category节点 + } + } + } + } + } + + // 递归搜索子节点 + for (int row = 0; row < item->rowCount(); ++row) { + QStandardItem* child = item->child(row, 0); + QStandardItem* found = findCategoryNodeByPropertyCode(child, code); + if (found) { + return found; + } + } + + return nullptr; +} + +void DataSourceDlg::clearItems() +{ + if(_treeModel){ + QStandardItem *root = _treeModel->invisibleRootItem(); //先清空model + int rowCount = root->rowCount(); + if (rowCount > 0) { + _treeModel->removeRows(0, rowCount); + } + } +} + +QString DataSourceDlg::getLevelType(int index) { + switch (index) { + case 0: return "电网"; + case 1: return "区域"; + case 2: return "站点"; + case 3: return "电压等级"; + case 4: return "间隔"; + case 5: return "设备"; + case 6: return "分组"; + default: return "未知层级"; + } +} + diff --git a/diagramCavas/source/designerScene.cpp b/diagramCavas/source/designerScene.cpp new file mode 100644 index 0000000..6a2fa1e --- /dev/null +++ b/diagramCavas/source/designerScene.cpp @@ -0,0 +1,259 @@ +#include "designerScene.h" +#include "graphicsItem/graphicsItemGroup.h" +#include "baseDrawingPanel.h" +#include "util/selectorManager.h" +//#include "global.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +DesignerScene::DesignerScene(FixedPortsModel* graphModel, QObject *parent) + : BaseScene(graphModel,parent), + m_pDrawingPanel(NULL), + _graphModel(graphModel) +{ + m_bGridVisible = true; + m_pView = nullptr; + m_pDrawingPanel = dynamic_cast(parent); + m_backGroundColor = Qt::white; + m_gridColor = Qt::darkCyan; +} +DesignerScene::~DesignerScene() +{ +} + +void DesignerScene::drawBackground(QPainter* painter, const QRectF& rect) +{ + QGraphicsScene::drawBackground(painter, rect); + painter->fillRect(sceneRect(), m_backGroundColor); + if(!m_bGridVisible) + return; + + QRectF sceneRect = this->sceneRect(); + QPen pen; + pen.setBrush(m_gridColor);//藏青色 + pen.setStyle(Qt::DashLine); + pen.setWidthF(0.2); + painter->setPen(pen); + int nGridSpace = 20; //暂时写死 + //竖线 + for(int nX = sceneRect.left(); nX < sceneRect.right(); nX += nGridSpace) + painter->drawLine(nX,sceneRect.top(),nX,sceneRect.bottom()); + //横线 + for(int nY = sceneRect.top(); nY < sceneRect.bottom(); nY += nGridSpace) + painter->drawLine(sceneRect.left(),nY,sceneRect.right(),nY); + //补齐最后两条 + painter->drawLine(sceneRect.right(), sceneRect.top(), sceneRect.right(), sceneRect.bottom()); + painter->drawLine(sceneRect.left(), sceneRect.bottom(), sceneRect.right(), sceneRect.bottom()); + + //原点 + // painter->setPen(Qt::red); + // painter->setBrush(Qt::red); + // painter->drawEllipse(0, 0, 50, 50); +} + +void DesignerScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) +{ + if(m_pDrawingPanel) + { + DiagramMode mode = m_pDrawingPanel->getMode(); + m_pDrawingPanel->selectorManager()->getWorkingSelector()->mousePressEvent(mouseEvent, this,mode); + update(); + } + else + QGraphicsScene::mousePressEvent(mouseEvent); +} + +void DesignerScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) +{ + if(m_pDrawingPanel) + { + DiagramMode mode = m_pDrawingPanel->getMode(); + m_pDrawingPanel->selectorManager()->getWorkingSelector()->mouseMoveEvent(mouseEvent, this,mode); + update(); + } + else + QGraphicsScene::mouseMoveEvent(mouseEvent); +} + +void DesignerScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) +{ + if(m_pDrawingPanel) + { + DiagramMode mode = m_pDrawingPanel->getMode(); + m_pDrawingPanel->selectorManager()->getWorkingSelector()->mouseReleaseEvent(mouseEvent, this,mode); + update(); + } + else + QGraphicsScene::mouseReleaseEvent(mouseEvent); +} + +void DesignerScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) +{ + if(m_pDrawingPanel) + { + DiagramMode mode = m_pDrawingPanel->getMode(); + m_pDrawingPanel->selectorManager()->getWorkingSelector()->mouseDoubleClickEvent(mouseEvent, this,mode); + update(); + } + else + QGraphicsScene::mouseDoubleClickEvent(mouseEvent); +} + +void DesignerScene::keyPressEvent(QKeyEvent* event) +{ + QGraphicsScene::keyPressEvent(event); +} + +void DesignerScene::keyReleaseEvent(QKeyEvent* event) +{ + QGraphicsScene::keyReleaseEvent(event); +} + +void DesignerScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + if(m_pDrawingPanel) + { + DiagramMode mode = m_pDrawingPanel->getMode(); + m_pDrawingPanel->selectorManager()->getWorkingSelector()->contextMenuEvent(event, this,mode); + update(); + } + else + QGraphicsScene::contextMenuEvent(event); +} + +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(); + } + else + QGraphicsScene::dragEnterEvent(event); +} + +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(); + } + else + QGraphicsScene::dragMoveEvent(event); +} + +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(); + } + else + QGraphicsScene::dropEvent(event); +} + +void DesignerScene::setGridVisible(bool bVisible) +{ + m_bGridVisible = bVisible; + update(); +} + +void DesignerScene::callParentEvent(QGraphicsSceneMouseEvent* mouseEvent) +{ + //调用QGraphicsScene的函数,会直接触发一些操作,比如取消item的selected状态,从而触发item的一些函数(itemChange),然后在item的相应函数中书写执行一些操作 + switch (mouseEvent->type()) + { + case QEvent::GraphicsSceneMousePress: + QGraphicsScene::mousePressEvent(mouseEvent); + break; + + case QEvent::GraphicsSceneMouseMove: + QGraphicsScene::mouseMoveEvent(mouseEvent); + break; + + case QEvent::GraphicsSceneMouseRelease: + QGraphicsScene::mouseReleaseEvent(mouseEvent); + break; + + default: + break; + } +} + +GraphicsItemGroup* DesignerScene::createGroup() +{ + QList listItem = selectedItems(); + if(listItem.isEmpty()) + return nullptr; + else if(listItem.count() == 1) //判断只选中了一个时是不是已经打组,如果是不做操作,防止循环打组 + { + AbstractShape* item = qgraphicsitem_cast(listItem.first()); + if(item && item->getType()==T_group) + return nullptr; + } + else //如果选择的有组群,则拆散该组群,并和其它单独的itme组合成新组群,防止多层组群出现,方便管理和计算 + { + for(int n=0; n(listItem[n]); + if(shape && shape->getType()==T_group) + { + GraphicsItemGroup* group = qgraphicsitem_cast(listItem[n]); + QList childItems = group->childItems(); + foreach (QGraphicsItem* child, childItems) + { + if(qgraphicsitem_cast(child)) + continue; + + group->removeFromGroup(child); + listItem.push_back(child); + } + + listItem.takeAt(n); + removeItem(group); + delete group; + n--; + } + } + } + + GraphicsItemGroup* group = new GraphicsItemGroup(); + group->addItems(listItem); + addItem(group); + return group; +} + +void DesignerScene::destroyGroup() +{ + +} + diff --git a/diagramCavas/source/designerView.cpp b/diagramCavas/source/designerView.cpp new file mode 100644 index 0000000..b11dc03 --- /dev/null +++ b/diagramCavas/source/designerView.cpp @@ -0,0 +1,194 @@ +#include "designerView.h" +#include "designerScene.h" +#include + +#define MAX_ZoomValue 50.0 +#define MIN_ZoomValue 0.02 + +DesignerView::DesignerView(QWidget *parent) + : QGraphicsView(parent) +{ + m_bMousePress = false; + m_dScale = 1.0; + initialize(); + m_nLevel = 10; + this->setFocusPolicy(Qt::ClickFocus); +} +DesignerView::~DesignerView() +{ + +} + +void DesignerView::initialize() +{ + //去掉QGraphicsView自带滚动条 + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + //设置背景 + setStyleSheet("background-image: url(:/images/checkerboard.png);\ + padding: 0px; \ + border: 0px;"); + //打开反锯齿 + setRenderHint(QPainter::Antialiasing); + setMouseTracking(true); + //setDragMode(QGraphicsView::RubberBandDrag); //将控制交给selector + + centerOn(0, 0); +} + +/*void DesignerView::contextMenuEvent(QContextMenuEvent* event) +{ + /*Q_UNUSED(event); + m_bMousePress = false; + QMenu menu; + QAction *removeAction = menu.addAction(QString::fromWCharArray(L"删除")); + menu.exec(QCursor::pos()); +}*/ + +void DesignerView::mousePressEvent(QMouseEvent* event) +{ + QGraphicsItem* item = scene()->itemAt(mapToScene(event->pos()), transform()); + if (event->button() == Qt::MiddleButton /*&& !item*/) //在空白处按住中键进行拖动 + { + m_bMousePress = true; + setCursor(Qt::ClosedHandCursor); + m_ptLatstMouse_view = event->pos(); + } + else + QGraphicsView::mousePressEvent(event); +} + +void DesignerView::mouseMoveEvent(QMouseEvent* event) +{ + if(m_bMousePress) + { + QPointF mouseMoveLength = event->pos() - m_ptLatstMouse_view.toPoint(); + translate(mouseMoveLength); + m_ptLatstMouse_view = event->pos(); + } + else + QGraphicsView::mouseMoveEvent(event); +} + +void DesignerView::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::MiddleButton) //按住中键进行拖动 + { + m_bMousePress = false; + setCursor(Qt::ArrowCursor); + } + else + QGraphicsView::mouseReleaseEvent(event); +} + +void DesignerView::wheelEvent(QWheelEvent* event) //滚轮进行放大缩小 +{ + QPointF mousePos = event->position(); + double angleDeltaY = event->angleDelta().y(); + double zoomFactor = qPow(1.0015, angleDeltaY); //可以实现更平滑的缩放 + + zoom(mousePos, zoomFactor); + + //注意不要调用基类的滚轮函数,否则滚轮事件会映射到滚动条操作 + //QGraphicsView::wheelEvent(event); +} + +double DesignerView::getScaleFactor() +{ + //QTransform为一个3*3的矩阵,m11和m22元素分别为水平和垂直的缩放值 + return this->transform().m11(); +} + +bool DesignerView::zoomLimit(double& value) +{ + m_dScale *= value; + if(m_dScale >= MAX_ZoomValue) + { + m_dScale = MAX_ZoomValue; + value = m_dScale / getScaleFactor(); + } + else if(m_dScale <= MIN_ZoomValue) + { + m_dScale = MIN_ZoomValue; + value = m_dScale / getScaleFactor(); + } + + if(qFabs(value - 1) < 0.01) //缩放因子近似为1 + return true; + + if(value > 1) + m_nLevel +=1; + else + m_nLevel -=1; + + emit onScaleChanged(m_nLevel); + return false; +} + +void DesignerView::zoom(const QPointF& mousePos, double zoomFactor) +{ + if(zoomLimit(zoomFactor)) + return; + + /* + * QGraphicsView的默认transformationAnchor(变换锚点)是AnchorViewCenter,也就是中心,此处希望以鼠标指向为变换中心, + * AnchorUnderMouse可以实现,但经尝试没有自己进行移动(translate)效果好,所以先设置成NoAnchor,变换后再设置回来 + */ + ViewportAnchor lastAnchor = this->transformationAnchor(); + setTransformationAnchor(QGraphicsView::NoAnchor); + QPointF targetScenePos = mapToScene(mousePos.toPoint()); + QTransform trans = transform(); + trans.translate(targetScenePos.x(), targetScenePos.y()); + trans.scale(zoomFactor, zoomFactor); + trans.translate(-targetScenePos.x(), -targetScenePos.y()); + setTransform(trans); + setTransformationAnchor(lastAnchor); + //emit onScaleChanged(m_nLevel); +} + +void DesignerView::zoomIn() +{ + //以view的中心点作为放大中心 + double dWidth_View = viewport()->width(); + double dHeight_View = viewport()->height(); + QPoint pt(dWidth_View * 0.5, dHeight_View * 0.5); + zoom(pt, 1.1); +} + +void DesignerView::zoomOut() +{ + //以view的中心点作为缩小中心 + double dWidth_View = viewport()->width(); + double dHeight_View = viewport()->height(); + QPoint pt(dWidth_View * 0.5, dHeight_View * 0.5); + zoom(pt, 0.9); +} + +void DesignerView::zoomFit() +{ + resetTransform(); + + double dWidth_View = viewport()->width(); + double dHeight_View = viewport()->height(); + double dWidth_Scene = scene()->sceneRect().width(); + double dHeight_Scene = scene()->sceneRect().height(); + + double dWidthScale = dWidth_View / dWidth_Scene; + double dHeightScale = dHeight_View / dHeight_Scene; + m_dScale = qMin(dWidthScale, dHeightScale); + + QTransform trans = transform(); + trans.scale(m_dScale, m_dScale); + setTransform(trans); + centerOn(0, 0); +} + +void DesignerView::translate(const QPointF& delta) +{ + ViewportAnchor lastAnchor = this->transformationAnchor(); + setTransformationAnchor(QGraphicsView::NoAnchor); + QTransform trans = transform(); + trans.translate(delta.x(), delta.y()); + setTransform(trans); + setTransformationAnchor(lastAnchor); +} diff --git a/diagramCavas/source/diagramCavas.cpp b/diagramCavas/source/diagramCavas.cpp new file mode 100644 index 0000000..5ad9411 --- /dev/null +++ b/diagramCavas/source/diagramCavas.cpp @@ -0,0 +1,619 @@ +#include +#include +#include +#include +#include +#include +#include "monitorPanel.h" +#include "diagramCavas.h" +#include "dataBase.h" +#include "graphicsItem/graphicsBaseItem.h" +#include "projectModelManager.h" +#include "topologyManager.h" +#include "powerEntity.h" +#include "cornerMonitorLauncher.h" +#include "loadMonitorPageDlg.h" +#include "diagramConnectSetting.h" +#include "diagramCommunication/include/communicationManager.h" +#include "diagramCommunication/include/configManager.h" +#include "instance/dataAccessor.h" +#include "uiCommunicationBus.h" +#include "itemPropertyDlg.h" +#include "monitorConfigDlg.h" +#include "structDataPreviewDlg.h" +#include "extraPropertyManager.h" +#include "createHMIdlg.h" + +#include "QQuickDetailsViewMananger.h" +#include "propertyType/propertyTypeCustomization_DataSourceType.h" +#include "propertyType/dataSourceType.h" + +DiagramCavas::DiagramCavas(QWidget *parent) + : QMdiArea(parent) + ,_cornerButton(nullptr) + ,_loadMonitorPageDlg(nullptr) + ,_connectSetting(nullptr) + ,_dataAccessor(nullptr) + ,_structDataPreviewDlg(nullptr) + ,_extraPropertyManager(nullptr) + ,_createHMIDlg(nullptr) +{ + _pageIndex = 0; +} + +DiagramCavas::~DiagramCavas() +{ + disconnect(); //断开连接避免动态库释放时信号问题 +} + +MonitorPanel* DiagramCavas::getMonitorPanel(QString sPage) +{ + if(m_mapMonitorPanel.contains(sPage)) + return m_mapMonitorPanel[sPage].first; + return nullptr; +} + +void DiagramCavas::updateSubPos() +{ + if(_cornerButton) + _cornerButton->positionAtCorner(); +} + +void DiagramCavas::passRecommmandHttpData(HttpRecommandInfo info) +{ + if(_structDataPreviewDlg){ //数据预览dlg + QStringList lst; + for(auto &str:info.lstRecommand){ + QString sCompleteName = info.sInput+str; + if(!lst.contains(sCompleteName)) + lst.append(sCompleteName); + } + _structDataPreviewDlg->updateRecommandLst(lst); + } + //=============================================== + QMdiSubWindow* pSub = currentSubWindow(); + if(!pSub) + return; + QWidget* pWindow= pSub->widget(); + BaseDrawingPanel* pPanel = dynamic_cast(pWindow); + if(pPanel) + { + if(pPanel->getMode() == DM_run) + { + auto pMonitor = dynamic_cast(pPanel); + if(pMonitor){ + auto pDlg = pMonitor->getMonitorConfigDlg(); + if(pDlg){ + + QStringList lst; + for(auto &str:info.lstRecommand){ + QString sCompleteName = info.sInput+str; + if(!lst.contains(sCompleteName)) + lst.append(sCompleteName); + } + pDlg->updateRecommandLst(lst); + } + //FixedPortsModel* pModel = pMonitor->getModelController(); + } + } + } +} + +void DiagramCavas::initial() +{ + //todo:读取数据并初始化 + QList lst = DataBase::GetInstance()->getAllPage(); + for(auto &info:lst) + { + TopologyManager::instance().createDiagram(QString::number(info.id),info.name); + } + _cornerButton = new CornerMonitorLauncher(this); + _cornerButton->showDlg(); + _loadMonitorPageDlg = new LoadMonitorPageDlg(this); + connect(_cornerButton,&CornerMonitorLauncher::openLoadMonitorDlg,this,[&](){ + if(_loadMonitorPageDlg){ + updateMonitorListFromDB(1); + _loadMonitorPageDlg->show(); + } + }); + connect(_loadMonitorPageDlg,&LoadMonitorPageDlg::monitorSelected,this,&DiagramCavas::onSignal_monitorSelected); + _connectSetting = new DiagramConnectSetting(this); + + // 初始化通信管理器 + CommunicationManager* comm = CommunicationManager::instance(); + comm->initialize(); + + // 加载配置 + ConfigManager* config = ConfigManager::instance(); + config->loadConfig("config.json"); + + _extraPropertyManager = new ExtraPropertyManager(this); + _extraPropertyManager->initial(); + + // 应用配置 + comm->updateHttpConfig(config->getHttpConfig()); + comm->updateWebSocketConfig(config->getWebSocketConfig()); + + _dataAccessor = new DataAccessor(this); + _dataAccessor->setParent(this); + connect(UiCommunicationBus::instance(), + SIGNAL(httpDataProcessed(QString,QVariant)), + _dataAccessor, + SLOT(onReceiveHttpData(QString,QVariant)), + Qt::AutoConnection); + connect(UiCommunicationBus::instance(), + SIGNAL(websocketDataProcessed(QVariant)), + _dataAccessor, + SLOT(onReceiveWebsocketData(QVariant)), + Qt::AutoConnection); + _structDataPreviewDlg = new StructDataPreviewDlg(this); + _structDataPreviewDlg->setExtraPropertyManager(_extraPropertyManager); + _structDataPreviewDlg->loadData(); + + QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization(); + + _createHMIDlg = new CreateHMIdlg(this); + connect(_createHMIDlg,&CreateHMIdlg::createHMI,this,&DiagramCavas::onSignal_createHMIClicked); + updateHMIlstFromDB(); +} + +void DiagramCavas::onSignal_addDrawingPanel(PowerEntity* pItem,DiagramMode mode,QString parent) +{ + if(mode == DM_run){ + MonitorPanel* pPanel = new MonitorPanel(pItem,this,mode); + _curPage = pItem->name(); + pPanel->setPageName(_curPage); + pPanel->setWindowTitle(_curPage); + pPanel->setParentPage(parent); + QMdiSubWindow* pSub = this->addSubWindow(pPanel); + m_mapMonitorPanel.insert(_curPage,qMakePair(pPanel,pSub)); + pPanel->show(); + + FixedPortsModel* pModel = pPanel->getModelController(); + pModel->setCavas(QPointer(this)); + connect(pModel,&FixedPortsModel::activatePage,this,&DiagramCavas::onSignal_activatePage); + connect(pPanel,&MonitorPanel::panelDelete,this,&DiagramCavas::onSignal_panelDelete); + connect(pModel,&FixedPortsModel::monitorCreated,this,&DiagramCavas::onSignal_monitorCreated); + connect(pModel,&FixedPortsModel::monitorItems,this,&DiagramCavas::onSignal_monitorItemCreated); + //connect(pModel,&FixedPortsModel::notifyUpdateMonitorTopology,this,&DiagramCavas::onSignal_updateMonitorTopology); ***使用属性构建拓扑层级,暂不使用260317 by + connect(pModel,&FixedPortsModel::updateTopologyItems,this,&DiagramCavas::onSignal_updateTopology); + + connect(pModel,&FixedPortsModel::HMIUpdated,this,&DiagramCavas::onSignal_updateHMI); + } + calculateLauncherVisible(); +} + +void DiagramCavas::onSignal_addGraphicsItem(ModelStateInfo& info) +{ + QMdiSubWindow* actWindow = activeSubWindow(); + if(!actWindow) + return; + QWidget* pWindow= currentSubWindow()->widget(); + /*DrawingPanel* pPanel = dynamic_cast(pWindow); + + if(pPanel) + pPanel->onSignal_addGraphicsItem(info);*/ +} + +void DiagramCavas::onSignal_addPage() +{ + //onSignal_addDrawingPanel(QString("Page_")+QString::number(++_pageIndex)); +} + +void DiagramCavas::onSignal_savePage() +{ + QWidget* pWindow= currentSubWindow()->widget(); + BaseDrawingPanel* pPanel = dynamic_cast(pWindow); + if(pPanel) + { + if(pPanel->getMode() == DM_run) + { + auto pMonitor = dynamic_cast(pPanel); + if(pMonitor){ + auto pEntity = pMonitor->getEntity(); + if(pEntity){ + QMap& mapResource = ProjectModelManager::instance().getHMIimageMap(); + DataBase::GetInstance()->insertHMIimagesWithCheck(mapResource.values()); //智能查重插入HMIsource + + QList& imgRefLst = pMonitor->getModelController()->getHMIimageRef(); //获取当前HMI所有img引用 + if(imgRefLst.size()){ + auto lstRef = DataBase::GetInstance()->getHMIRefAll(imgRefLst.first().hmiId); //检测HMI是否存储过,删除旧img引用 + if(lstRef.size()) + DataBase::GetInstance()->removeHMIRefAll(imgRefLst.first().hmiId); + DataBase::GetInstance()->insertHMIimageRefBatch(imgRefLst); //全部重新插入 + } + + if(DataBase::GetInstance()->getHMIdByName(pEntity->name()).isNull()) //不存在,创建 + DataBase::GetInstance()->insertHMI(QUuid(pEntity->id()),pEntity->name(),pEntity->name(),pMonitor->getDiagramInfo()); + else + DataBase::GetInstance()->updateHMI(pEntity->name(),pMonitor->getDiagramInfo()); + auto pControl = pMonitor->getModelController(); + if(pControl) + emit pControl->HMIUpdated(pEntity->name(),QUuid(pEntity->id()),1,QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"保存成功")); + } + } + } + } +} + +void DiagramCavas::onSignal_loadMonitor(PowerEntity* p) +{ + if(!m_mapMonitorPanel.contains(p->name())) + { + onSignal_addDrawingPanel(p,DiagramMode::DM_run); + QJsonObject context = DataBase::GetInstance()->getHMIContextByTag(p->name()); + m_mapMonitorPanel[p->name()].first->loadNodes(context); + } + else //已存在 + { + m_mapMonitorPanel[p->name()].first->show(); + } +} + +void DiagramCavas::runPage(const QString sName) +{ + QString pageName = sName+QString("_run"); + if(!m_mapMonitorPanel.contains(pageName)) + { + //onSignal_addDrawingPanel(pageName,DM_run); + QJsonObject context = DataBase::GetInstance()->getPageContextByName(sName); + //m_mapDrawPanel[pageName]->loadNodes(context); + } + else //已存在 + { + //m_mapDrawPanel[pageName]->show(); + } +} + +void DiagramCavas::onSignal_runPage() +{ + runPage(_curPage); +} + +void DiagramCavas::onSignal_deletePage() +{ + +} + +void DiagramCavas::onSignal_activatePage(const QString& name) +{ + _curPage = name; +} + +void DiagramCavas::onSignal_panelDelete(const QString& name,int nType) +{ + if(nType == 2){ + MonitorPanel* pPanel = m_mapMonitorPanel.take(name).first; + this->removeSubWindow(pPanel); + delete pPanel; + } + calculateLauncherVisible(); +} + +void DiagramCavas::onSignal_createEntity(EntityInfo info) +{ + PowerEntity* pEntity; + pEntity = TopologyManager::instance().findEntity(info.sUuid); + if(!pEntity) //不存在创建 + { + pEntity = TopologyManager::instance().createEntity(info.eType,info.sUuid,info.sName); + PowerEntity* pParent = TopologyManager::instance().findEntity(info.sParentId); + if(pParent) //构建父子关系 + { + pParent->addChild(pEntity); + } + + switch (info.eType){ + case EntityType::Grid: + case EntityType::Zone: + case EntityType::Station: + break; + case EntityType::ConfigurationDiagram: + onSignal_addDrawingPanel(pEntity); + break; + default: + break; + } + } + +} + +void DiagramCavas::onSignal_changeEntity(EntityInfo info) +{ + +} + +void DiagramCavas::onSignal_deleteEntity(EntityInfo info) +{ + PowerEntity* pEntity = TopologyManager::instance().findEntity(info.sUuid); + if(pEntity) + { + switch (info.eType){ + case EntityType::Grid: + case EntityType::Zone: + case EntityType::Station: + TopologyManager::instance().deleteEntity(info.sUuid); + break; + case EntityType::ConfigurationDiagram: + removePanel(pEntity); + TopologyManager::instance().deleteEntity(info.sUuid); + break; + default: + break; + } + } +} + +void DiagramCavas::onSignal_selectEntity(EntityInfo info) +{ + PowerEntity* pEntity = TopologyManager::instance().findEntity(info.sUuid); + if(pEntity) + { + switch (info.eType){ + case EntityType::Grid: + case EntityType::Zone: + case EntityType::Station: + break; + default: + break; + } + } + else //不存在,从数据库中load + { + + } +} + +void DiagramCavas::onSignal_createDiagram(DiagramInfo info,DiagramMode mode) +{ + PowerEntity* pEntity; + ModelFunctionType type; + if(mode == DM_run) + type = ModelFunctionType::RuntimeModel; + else if(mode == DM_edit) + type = ModelFunctionType::ProjectModel; + pEntity = TopologyManager::instance().findDiagram(info.id.toString(),type); + if(!pEntity) //不存在创建 + { + pEntity = TopologyManager::instance().createDiagram(info.id.toString(),info.sName,type); + /*PowerEntity* pParent = TopologyManager::instance().findDiagram(info.parentId.toString()); + if(pParent) //构建父子关系 + { + pParent->addChild(pEntity); + }*/ + + onSignal_addDrawingPanel(pEntity,mode); + } +} + +void DiagramCavas::onSignal_changeDiagram(DiagramInfo info) +{ + +} + +void DiagramCavas::onSignal_deleteDiagram(DiagramInfo info) +{ + PowerEntity* pEntity = TopologyManager::instance().findDiagram(info.id.toString()); + if(pEntity) + { + removePanel(pEntity); + TopologyManager::instance().deleteEntity(info.id.toString()); + } +} + +void DiagramCavas::onSignal_selectDiagram(DiagramInfo info) +{ + PowerEntity* pEntity = TopologyManager::instance().findDiagram(QString::number(info.id.toInt())); + if(pEntity) + { + //onSignal_loadPage(pEntity); + } + else //不存在,从数据库中load + { + + } +} + +void DiagramCavas::onSignal_openNetSetting() +{ + if(_connectSetting){ + _connectSetting->showDlg(); + } +} + +void DiagramCavas::onSignal_openStructDataPreview() +{ + if(_structDataPreviewDlg) + _structDataPreviewDlg->showDlg(); +} + +void DiagramCavas::onSignal_createHMI(QString sName,QUuid id) +{ + emit createHMI(sName,id); +} + +void DiagramCavas::onSignal_updateHMI(QString sName,QUuid id,int nState,QString sTime) +{ + emit updateHMI(sName,id,nState,sTime); +} + +void DiagramCavas::removePanel(PowerEntity* pEntity) +{ + QMap>::Iterator iter; + for(iter = m_mapMonitorPanel.begin(); iter != m_mapMonitorPanel.end();++iter) + { + if(pEntity->name() == iter.value().first->pageName()) + { + MonitorPanel* pPanel = m_mapMonitorPanel.take(iter.key()).first; + + QWidget* pWindow = static_cast(pPanel); + setActiveSubWindow(iter->second); + closeActiveSubWindow(); + //removeSubWindow(pPanel); + //todo:记录删除组态图,从数据库中删除 + delete pPanel; + break; + } + } +} + +void DiagramCavas::calculateLauncherVisible() +{ + if(_cornerButton){ + if (m_mapMonitorPanel.isEmpty()) { + _cornerButton->setVisible(true); + } else { + _cornerButton->setVisible(false); + } + } +} + +void DiagramCavas::resizeEvent(QResizeEvent* event) +{ + if(_cornerButton) + _cornerButton->positionAtCorner(); +} + +void DiagramCavas::onTargetSelected(QObject* obj) +{ + if(obj) + emit selectTarget(obj); +} +/****************************************************/ + +void DiagramCavas::onSignal_updateTopology(QList lst,bool refresh,bool showFull) +{ + emit prepareUpdateTopology(lst,refresh,showFull); +} + +/*******************bay************************/ +void DiagramCavas::onSignl_openCurrentBay() +{ + /*QWidget* pWindow= currentSubWindow()->widget(); + if(pWindow == nullptr){ + QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"当前无打开的工程模")); + } + MonitorPanel* pPanel = dynamic_cast(pWindow); + if(pPanel) + { + if(pPanel->getMode() == DM_run) + { + pPanel->getModelController()->onSignal_openBayManager(); + } + }*/ +} +/*********************monitor**************************/ + +void DiagramCavas::onCreateHMIClicked() +{ + if(_createHMIDlg) + _createHMIDlg->showDlg(); +} + +void DiagramCavas::onSignal_createHMIClicked(QString sHMI,QString sStructPage,int modelType) +{ + QUuid id = QUuid::createUuid(); + auto *pEntity = TopologyManager::instance().createDiagram(id.toString(),sHMI,ModelFunctionType::RuntimeModel); + onSignal_addDrawingPanel(pEntity,DiagramMode::DM_run); + QJsonObject context = DataBase::GetInstance()->getMonitorContextByTag(sStructPage); + m_mapMonitorPanel[sHMI].first->loadNodes(context); //加载系统图结构 + onSignal_createHMI(sHMI,id); +} + +void DiagramCavas::onSignal_updateCurItems(QList lst,bool refresh) +{ + emit prepareUpdateItems(lst,refresh); +} + +void DiagramCavas::onSignal_selectedItems(QList lst) +{ + emit prepareSelectItems(lst); +} + +void DiagramCavas::onSignal_monitorCreated(QString sProj,QPair pair) +{ + emit updateMonitorList(sProj,pair); +} + +void DiagramCavas::onSignal_monitorItemCreated(QList lst) +{ + emit createdMonitorItems(lst); +} + +void DiagramCavas::onSignal_monitorSelected(DiagramInfo info) +{ + PowerEntity* pEntity = TopologyManager::instance().findDiagram(info.id.toString(),ModelFunctionType::RuntimeModel); + if(pEntity) + { + onSignal_loadMonitor(pEntity); + } +} + +void DiagramCavas::onSignal_saveMonitor(QList> lst) +{ + for(auto &pair:lst){ + auto pMonitor = m_mapMonitorPanel.value(pair.first).first; + if(pMonitor){ + if(DataBase::GetInstance()->getPageIdByName(pMonitor->getParentPage()) == -1){ + QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"请先保存工程模型")); + return; + } + } + } + + for(auto& pair:lst){ + auto pMonitor = m_mapMonitorPanel.value(pair.first).first; + if(pMonitor){ + if(DataBase::GetInstance()->getMonitorIdByName(pair.first).isNull()) //不存在,创建 + DataBase::GetInstance()->insertMonitor(pair.second,pair.first,pair.first,pMonitor->getParentPage(),pMonitor->getDiagramInfo()); + else + DataBase::GetInstance()->updateMonitor(pair.first,pMonitor->getDiagramInfo()); + } + } + QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"保存成功")); +} + +void DiagramCavas::updateMonitorListFromDB(int dest) +{ + if(dest != 0) + _loadMonitorPageDlg->clearItems(); + + QList lstMonitor = DataBase::GetInstance()->getAllMonitor(); + for(auto &info:lstMonitor) + { + auto p = TopologyManager::instance().findDiagram(info.uid.toString(),ModelFunctionType::RuntimeModel); + if(!p){ + TopologyManager::instance().createDiagram(info.uid.toString(),info.name,ModelFunctionType::RuntimeModel); + if(dest == 0) + emit updateMonitorList(info.parent,qMakePair(info.name,info.uid)); + else{ + _loadMonitorPageDlg->updateItems(info.parent,qMakePair(info.name,info.uid)); + } + } + else{ //存在直接发送 + if(dest == 0) + emit updateMonitorList(info.parent,qMakePair(info.name,info.uid)); + else{ + _loadMonitorPageDlg->updateItems(info.parent,qMakePair(info.name,info.uid)); + } + } + } +} + +void DiagramCavas::onSignal_updateMonitorTopology(QList lst) +{ + emit updateMonitorTopology(lst); +} + +void DiagramCavas::updateHMIlstFromDB() +{ + QList lstMonitor = DataBase::GetInstance()->getAllHMI(); + for(auto &info:lstMonitor) + { + auto p = TopologyManager::instance().findDiagram(info.uid.toString(),ModelFunctionType::RuntimeModel); + if(!p){ + TopologyManager::instance().createDiagram(info.uid.toString(),info.name,ModelFunctionType::RuntimeModel); + } + } +} diff --git a/diagramCavas/source/diagramConnectSetting.cpp b/diagramCavas/source/diagramConnectSetting.cpp new file mode 100644 index 0000000..953fbd0 --- /dev/null +++ b/diagramCavas/source/diagramConnectSetting.cpp @@ -0,0 +1,115 @@ +#include "diagramConnectSetting.h" +#include "ui_diagramConnectSetting.h" +#include "diagramCommunication/include/configManager.h" +#include "uiCommunicationBus.h" +#include "communicationManager.h" + +DiagramConnectSetting::DiagramConnectSetting(QWidget *parent) + : QDialog(parent) + , ui(new Ui::diagramConnectSetting) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +DiagramConnectSetting::~DiagramConnectSetting() +{ + delete ui; +} + +void DiagramConnectSetting::initial() +{ + connect(ui->btn_testRecommand,&QPushButton::clicked,this,&DiagramConnectSetting::onTestHttpRecommandClicked); + connect(ui->btn_testData,&QPushButton::clicked,this,&DiagramConnectSetting::onTestHttpDataClicked); + connect(ui->btn_testData,&QPushButton::clicked,this,&DiagramConnectSetting::onTestWebsocketClicked); + connect(ui->btn_testWebsoc,&QPushButton::clicked,this,&DiagramConnectSetting::onTestWebsocketClicked); + connect(ui->btn_ok,&QPushButton::clicked,this,&DiagramConnectSetting::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&DiagramConnectSetting::onCancelClicked); +} + +void DiagramConnectSetting::updateByConfig(ChannelConfig info,int nType) +{ + if(nType == 0){ + ui->le_httpName->setText(info.name); + ui->le_httpIp->setText(info.endpoint); + ui->le_httpOver->setText(QString::number(info.timeout)); + } + else if(nType == 1){ + ui->le_websocName->setText(info.name); + ui->le_websocIp->setText(info.endpoint); + ui->le_websocOver->setText(QString::number(info.timeout)); + ui->le_websocHeart->setText(QString::number(info.heartbeatInterval)); + } +} + +void DiagramConnectSetting::showDlg() +{ + show(); + ConfigManager* config = ConfigManager::instance(); + auto httpInfo = config->getHttpConfig(); + updateByConfig(httpInfo,0); + auto socketInfo = config->getWebSocketConfig(); + updateByConfig(socketInfo,1); +} + +void DiagramConnectSetting::updateHttpLog(QString sType,QString data) +{ + ui->lst_log->addItem("接收http:"+data); + if(sType == "recommend"){ + ui->label_httpRecommand->setText("联通"); + } + else if(sType == "subscriptions"){ + ui->label_httpData->setText("联通"); + } + else if(sType == "subscriptionTest"){ + ui->label_httpData->setText("联通"); + } +} + +void DiagramConnectSetting::updateWebsocketLog(QString str) +{ + ui->lst_log->addItem("接收websocket:"+str); +} + +void DiagramConnectSetting::onTestHttpRecommandClicked() +{ + QString strUrl = ui->le_httpIp->text()+"/measurement/recommend?input="; + UiCommunicationBus::instance()->sendHttpRequest(strUrl); +} + +void DiagramConnectSetting::onTestHttpDataClicked() +{ + QString sPath = ui->le_httpIp->text()+"/monitors/data/subscriptions"; + + UiCommunicationBus::instance()->sendHttpRequest(sPath,QVariant(),"POST"); +} + +void DiagramConnectSetting::onTestWebsocketClicked() +{ + +} + +void DiagramConnectSetting::onOkClicked() +{ + CommunicationManager* comm = CommunicationManager::instance(); + ConfigManager* config = ConfigManager::instance(); + + // 应用配置 + auto httpConfig = config->getHttpConfig(); + auto websocketConfig = config->getWebSocketConfig(); + httpConfig.endpoint = ui->le_httpIp->text(); + httpConfig.timeout = ui->le_httpOver->text().toInt(); + + websocketConfig.endpoint = ui->le_websocIp->text(); + websocketConfig.timeout = ui->le_websocOver->text().toInt(); + websocketConfig.heartbeatInterval = ui->le_websocHeart->text().toInt(); + comm->updateHttpConfig(httpConfig); + comm->updateWebSocketConfig(config->getWebSocketConfig()); + hide(); +} + +void DiagramConnectSetting::onCancelClicked() +{ + hide(); +} diff --git a/diagramCavas/source/graphicsDataModel/baseModel.cpp b/diagramCavas/source/graphicsDataModel/baseModel.cpp new file mode 100644 index 0000000..37fdaac --- /dev/null +++ b/diagramCavas/source/graphicsDataModel/baseModel.cpp @@ -0,0 +1,454 @@ +#include "graphicsDataModel/baseModel.h" +#include "graphicsItem/itemPort.h" +#include "graphicsItem/graphicsBaseItem.h" +#include "baseProperty.h" +#include "topologyManager.h" +#include "powerEntity.h" +#include "baseModelItem/electricBaseModelLineItem.h" + +void BaseModel::createTopoTerminalsByItem(GraphicsBaseItem* pItem,ModelFunctionType funType) +{ + PowerEntity* pEntity = pItem->entity(); + + if(pEntity) + { + QMap mapPorts = pItem->getPorts(); //创建实体port对应的拓扑port + for(auto &port:mapPorts) + { + TerminalType terType; + + HandleType tpe = port->getType(); + switch (tpe) { + case T_lineIn: + terType = TerminalType::PowerInput; + break; + case T_lineOut: + terType = TerminalType::PowerOutput; + break; + case T_lineInOut: + terType = TerminalType::PowerConnect; + break; + case T_newTral: + terType = TerminalType::NewTral; + break; + default: + break; + } + QPointF f = port->pos(); + auto pTer = TopologyManager::instance().createTerminal(pEntity->id(),terType,"",port->pos(),port->getId(),funType); + if(pTer) + pTer->setPortLocate(port->portPos()); + } + } +} + +QPointF BaseModel::calculateBusPortPos(GraphicsBaseItem* pBus,GraphicsBaseItem* item) +{ + //return QPointF(item->pos().x(),pBus->pos().y()); + QRectF recBus = pBus->sceneBoundingRect(); + QLineF busLine = QLineF(recBus.left(),recBus.y(),recBus.right(),recBus.y()); + + QPointF p1 = busLine.p1(); + QPointF p2 = busLine.p2(); + + // 计算item的中心点在垂直于线段方向上的投影 + QPointF itemCenter = item->pos();// + QPointF(item->boundingRect().width()/2, item->boundingRect().height()/2); + + // 计算投影点在线段上的位置 + QPointF projection; + if (busLine.length() == 0) { // 如果线段长度为0,直接返回端点 + projection = p1; + } else { + // 计算投影点 + QPointF vec = p2 - p1; + QPointF relPos = itemCenter - p1; + qreal dot = QPointF::dotProduct(relPos, vec) / (busLine.length() * busLine.length()); + + // 限制投影点在线段范围内 + dot = qBound(0.0, dot, 1.0); + projection = p1 + dot * vec; + } + + return projection; +} + +ItemPort* BaseModel::getClosestUnusedPort(QMap mapPorts,GraphicsBaseItem* item,ModelFunctionType nType) +{ + QMap mapDistance; + + // 收集所有端口及其距离 + for(auto& port: mapPorts) { + double dis = distanceBetweenItems(port, item); + mapDistance.insert(dis, port); + } + + for(auto& port:mapDistance){ //从小到大遍历 + auto lst = TopologyManager::instance().getConnectionsForTerminal(port->getId(),nType); + if(lst.isEmpty()){ //没被占用 + return port; + } + } + return nullptr; +} + +template void BaseModel::establishConnection(GraphicsBaseItem* pSrc,GraphicsBaseItem* pDest,TypeLine* pItem,ModelFunctionType nType,int nMode,int nParam) +{ + ItemPort* pSrcPort = nullptr; + ItemPort* pDestPort = nullptr; + int nTypeSrc = pSrc->getProperty()->type(); + int nTypeDest = pDest->getProperty()->type(); + //if(pSrc->getItemType() == GIT_baseBus) + if((nTypeSrc == 1 || nTypeSrc == 0) && (nTypeDest != 1 && nTypeDest != 0)) //src是母线或节点 + { + int index = 0; + if(nTypeSrc == 1 ) + index = pSrc->addPort(p_movable,pSrc->mapFromScene(calculateBusPortPos(pSrc,pDest))); + else + index = pSrc->addPort(p_movable,QPoint(0,0)); //节点port与自身坐标重合 + createTopoTerminalsByItem(pSrc,nType); + pSrcPort = pSrc->getPortPtr(index); + + QMap mapPorts = pDest->getPorts(); + + if(nMode == 0){ + pDestPort = getClosestUnusedPort(mapPorts,pSrc,nType); + } + else if(nMode == 1){ + for(auto& port:mapPorts) //连接中性点 + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe == T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pDestPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pDestPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pDestPort = port; + } + } + } + } + } + else if(nMode == 2){ + for(auto& port:mapPorts) //连接非中性点 + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe != T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pDestPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pDestPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pDestPort = port; + } + } + } + } + } + } + else if((nTypeDest == 1 || nTypeDest == 0) && (nTypeSrc != 1 && nTypeSrc != 0)) + { + int index = 0; + if(nTypeDest == 1 ) + index = pDest->addPort(p_movable,pDest->mapFromScene(calculateBusPortPos(pDest,pSrc))); + else + index = pDest->addPort(p_movable,QPoint(0,0)); //节点port与自身坐标重合 + createTopoTerminalsByItem(pDest,nType); + pDestPort = pDest->getPortPtr(index); + + QMap mapPorts = pSrc->getPorts(); + + if(nMode == 0){ + pSrcPort = getClosestUnusedPort(mapPorts,pDest,nType); + } + else if(nMode == 1){ + for(auto& port:mapPorts) //连接中性点 + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe == T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pSrcPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pSrcPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pSrcPort = port; + } + } + } + } + } + else if(nMode == 2){ + for(auto& port:mapPorts) //连接非中性点 + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe != T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pSrcPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pSrcPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pSrcPort = port; + } + } + } + } + } + } + else if((nTypeSrc == 1 || nTypeSrc == 0) && (nTypeDest == 1 || nTypeDest == 0)){ //两个都是母线或节点 + int index = 0; + if(nTypeSrc == 1 ) + index = pSrc->addPort(p_movable,pSrc->mapFromScene(calculateBusPortPos(pSrc,pDest))); + else + index = pSrc->addPort(p_movable,QPoint(0,0)); //节点port与自身坐标重合 + createTopoTerminalsByItem(pSrc,nType); + pSrcPort = pSrc->getPortPtr(index); + + index = 0; + if(nTypeDest == 1 ) + index = pDest->addPort(p_movable,pDest->mapFromScene(calculateBusPortPos(pDest,pSrc))); + else + index = pDest->addPort(p_movable,QPoint(0,0)); //节点port与自身坐标重合 + createTopoTerminalsByItem(pDest,nType); + pDestPort = pDest->getPortPtr(index); + } + else + { + QMap mapSrc = pSrc->getPorts(); + QMap mapDest = pDest->getPorts(); + pSrcPort = nullptr; + pDestPort = nullptr; + + if(nMode == 1){ //连接中性点 + if(nTypeSrc == 15 || nTypeSrc == 16){ //src是变压器中性点 + for(auto& port:mapSrc) //连接中性点 + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe == T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pSrcPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pSrcPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pSrcPort = port; + } + } + } + } + + for(auto& port:mapDest) + { + if(port->getType() == T_lineIn) + { + pDestPort = port; + break; + } + } + } + else if(nTypeDest == 15 || nTypeDest == 16){ //dest是变压器中性点 + + for(auto& port:mapSrc) + { + if(port->getType() == T_lineIn) //始终从中性点出 + { + pSrcPort = port; + break; + } + } + + for(auto& port:mapDest) //连接中性点 + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe == T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pDestPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pDestPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pDestPort = port; + } + } + } + } + } + } + else if(nMode == 2){ //连接变压器 + if(nTypeSrc == 15 || nTypeSrc == 16){ //src是变压器 + for(auto& port:mapSrc) + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe != T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pSrcPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pSrcPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pSrcPort = port; + } + } + } + } + + int transType = 0; + if(pSrcPort){ + transType = pSrcPort->getType(); + } + for(auto& port:mapDest) //根据变压器端点选择连接端点类型 + { + if(transType == T_lineOut) + { + if(port->getType() == T_lineIn) + { + pDestPort = port; + break; + } + } + else{ + if(port->getType() == T_lineOut) + { + pDestPort = port; + break; + } + } + } + } + else if(nTypeDest == 15 || nTypeDest == 16){ //dest是变压器 + + for(auto& port:mapDest) + { + int nTpe = port->getType(); + int nPos = port->portPos(); + if(nTpe != T_newTral){ + if(nParam == 0){ + if(nPos == P_top){ + pDestPort = port; + } + } + else if(nParam == 1){ + if(nPos == P_left || nPos == P_right){ + pDestPort = port; + } + } + else if(nParam == 2){ + if(nPos == P_down){ + pDestPort = port; + } + } + } + } + + int transType = 0; + if(pDestPort){ + transType = pDestPort->getType(); + } + for(auto& port:mapSrc) //根据变压器端点选择连接端点类型 + { + if(transType == T_lineOut) + { + if(port->getType() == T_lineIn) + { + pSrcPort = port; + break; + } + } + else{ + if(port->getType() == T_lineOut) + { + pSrcPort = port; + break; + } + } + } + } + } + else{ + for(auto& port:mapSrc) + { + if(port->getType() == T_lineOut) + { + pSrcPort = port; + break; + } + } + + for(auto& port:mapDest) + { + if(port->getType() == T_lineIn) + { + pDestPort = port; + break; + } + } + } + } + if(pSrcPort && pDestPort) + { + QPointF srcPortPos = pSrcPort->scenePos(); + QPointF destPortPos = pDestPort->scenePos(); + pItem->setStartPoint(srcPortPos); + pItem->setEndPoint(destPortPos); + pItem->calculatePath(); + + PowerConnection* pCon = TopologyManager::instance().createConnection(pItem->itemId().toString(),pSrcPort->getId(),pDestPort->getId(),pSrc->itemId().toString(),pDest->itemId().toString(),nType); //创建拓扑连接(逻辑) + if(pCon) + pCon->setState(DataState::Changed); + pItem->getProperty()->setConnection(Connection(pSrc->itemId(),QUuid(pSrcPort->getId()),pSrcPort->getType(),pSrcPort->portPos(),pDest->itemId(),QUuid(pDestPort->getId()),pDestPort->getType(),pDestPort->portPos())); + } +} + +template void BaseModel::establishConnection(GraphicsBaseItem*,GraphicsBaseItem*,ElectricBaseModelLineItem*,ModelFunctionType,int,int); diff --git a/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp b/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp new file mode 100644 index 0000000..efb3d34 --- /dev/null +++ b/diagramCavas/source/graphicsDataModel/fixedPortsModel.cpp @@ -0,0 +1,2406 @@ +#include "graphicsDataModel/fixedPortsModel.h" +#include "graphicsItem/graphicsBaseItem.h" +#include "graphicsItem/electricBayItem.h" +#include "graphicsItem/electricBayItem.h" + +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h" +#include "graphicsItem/functionModelItem/electricFunctionModelPortItem.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h" +#include "graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h" + +#include "graphicsItem/itemPort.h" +#include "designerScene.h" +#include "dataBase.h" +#include "httpInterface.h" +#include "itemPropertyDlg.h" +#include "dataManager.h" +#include "powerEntity.h" +#include "topologyManager.h" +#include "basePropertyManager.h" +#include "diagramCavas.h" +#include +#include +#include +#include +#include +#include +#include "baseProperty.h" +#include "bayManagerDlg.h" +#include "projectModelManager.h" +#include "projectIconSetting.h" +#include "monitorPanel.h" +#include "designerView.h" +#include "uiCommunicationBus.h" +#include "instance/dataAccessor.h" +#include "graphicsItem/handleText.h" +#include "bayMeasureDlg.h" +#include "basePannelPropertyProxy.h" +#include "common/core_model/types.h" + +bool FixedPortsModel::_dataInitialised = false; + +FixedPortsModel::FixedPortsModel(PowerEntity* pEntity) + :_scene(nullptr) + ,_widget(nullptr) + ,_Interface(nullptr) + ,_pEntity(pEntity) + ,m_projectIconSettingDlg(nullptr) + ,m_pBayManager(nullptr) + ,m_curPropertyDlg(nullptr) + ,m_dataTimer(nullptr) + ,m_bayMeasureDlg(nullptr) +{ + _cavas = nullptr; + loadNodeDataFromDataBase(); + ProjectModelManager::instance().initialHMISourcr(); + _Interface = new HttpInterface(this); + _timer = new QTimer(this); + m_dataTimer = new QTimer(this); + + _modelStateInfo = DataManager::instance().modelState(); + _modelDataInfo = DataManager::instance().modelData(); + initialPropertyDlg(); + connect(_timer,SIGNAL(timeout()),this,SLOT(onTimeOut())); + connect(m_dataTimer,&QTimer::timeout,this,&FixedPortsModel::onDataTimerOut); + m_dataTimer->setInterval(2000); + connect(_Interface,&HttpInterface::sendPointData,this,&FixedPortsModel::onSignal_GetPointData); +} + +FixedPortsModel::~FixedPortsModel() +{ + _cavas.clear(); +} + +QMap FixedPortsModel::allNodePos() const +{ + QMap map; + for(auto pItem:_nodeItem) + { + if(pItem->getItemType() != GIT_link){ + 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; + for(auto pItem:_nodeItem) + { + if(pItem->getItemType() == GIT_link) + { + auto pLine = dynamic_cast(pItem); + if(pLine) + { + ModelProperty* pPro = pLine->getProperty(); + if(pPro){ + vec.push_back(pPro); + } + } + } + } + return vec; +} + +QMap& FixedPortsModel::allItems() +{ + return _nodeItem; +} + +GraphicsFunctionModelItem* FixedPortsModel::nodeItem(QUuid uuid) +{ + return _nodeItem.value(uuid,nullptr); +} + +bool FixedPortsModel::addNodeItem(QUuid uuid,GraphicsFunctionModelItem* pItem) +{ + + if(_nodeItem.contains(uuid)) + return false; + else + { + pItem->setHandle(this); + _nodeItem.insert(uuid,pItem); + //connect(pItem,&GraphicsFunctionModelItem::ifExist,this,&FixedPortsModel::onSignal_ifExits); + connect(pItem,&GraphicsBaseItem::itemRotated,this,[this](GraphicsBaseItem* pBase){ + if(pBase){ + auto pPro = pBase->getProperty(); + QUuid uid = pPro->uuid(); + if(pPro->type() != 8){ //排除线 + updateItemLinePort(uid,ModelFunctionType::ProjectModel); //hmi不改变拓扑结构,继续使用系统图的连接关系 + } + } + }); + return true; + } +} + +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:完善判断条件,如判断拓扑节点) + + if(mapData.contains(id)) + { + pro = mapData.value(id); + if(pro) + { + int type = pro->graphicsType(); + if(type == GIT_link) //连线对象外部处理 + return QString("err"); + QString sMeta = pro->metaModelName(); + QString sProModel = pro->modelName(); + + ModelDataInfo mapValue = DataManager::instance().modelData()[sProModel]; + PropertyValueInfo valueInfo; //属性组设置的数据 (ct,pt二次绕组) + if(mapValue.groupInfo.contains("base_extend")) + { + valueInfo = mapValue.groupInfo["base_extend"].mapInfo[id]; + } + + double dX = 0.0; + double dY = 0.0; + if(type == GIT_itemRect) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[3].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["cb"]; + } + + if(width == 0 && height == 0){ + dX = 30; + dY = 30; + } + else{ + dX = width; + dY = height; + } + + auto pCb = new ElectricFunctionModelSvgItemCB(QRect(-dX*0.5, -dY*0.5, dX, dY),false); + pCb->setItemType(GIT_itemRect); + pCb->loadSvg(svg); + item = pCb; + QJsonArray portArr = pro->context()["port"].toArray(); + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_node) + { + auto pNode = new ElectricFunctionModelPortItem(); + pNode->setItemType(GIT_node); + item = pNode; + + QJsonArray portArr = pro->context()["port"].toArray(); + addPortsToItem_json(p_movable,portArr,item); + } + else if(type == GIT_bus) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[1].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["bus"]; + } + + if(width == 0 && height == 0){ + dX = 200; + dY = 6; + } + else{ + dX = width; + dY = height; + } + + auto pBus = new ElectricFunctionModelSvgItemBus(QRect(0, 0, dX, dY)); + pBus->setItemType(GIT_bus); + pBus->loadSvg(svg); + item = pBus; + + QJsonArray portArr = pro->context()["port"].toArray(); + addPortsToItem_json(p_movable,portArr,item); + } + else if(type == GIT_ctGroup) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[5].icon; + } + else{ + svg = model.modelSetting.mapSvg.first(); + } + + if(width == 0 && height == 0){ + dX = 20; + dY = 20; + } + else{ + dX = width; + dY = height; + } + + auto pCt = new ElectricFunctionModelSvgGroupCT(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pCt->updateMapSvg(ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg); + pCt->setItemType(GIT_ctGroup); + item = pCt; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + if(!valueInfo.isEmpty()){ //使用设置的绕组数更新自己 + int nCtSize = 0; //ct设置的个数 + int nCtType = 0; //ct类型 1三相0零相 + if(valueInfo.contains("ct_winding")){ + PropertyStateInfo val = valueInfo.value("ct_winding"); + + QJsonDocument jsonDocument = QJsonDocument::fromJson(val.defaultValue.toString().toUtf8().data()); + if(!jsonDocument.isNull()){ + QJsonObject obj = jsonDocument.object(); + if(obj.contains("winding")){ + QJsonArray arr = obj["winding"].toArray(); + nCtSize = arr.size(); + } + } + } + if(valueInfo.contains("phase_num")){ + PropertyStateInfo val = valueInfo.value("phase_num"); + nCtType = val.defaultValue.toInt(); + } + + QPair pair(nCtType,nCtSize); + QVariant var = QVariant::fromValue(pair); + pCt->setupFinish(var); + } + + } + else if(type == GIT_ptGroup) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[6].icon; + } + else{ + svg = model.modelSetting.mapSvg.first(); + } + + if(width == 0 && height == 0){ + dX = 50; + dY = 50; + } + else{ + dX = width; + dY = height; + } + + auto pPt = new ElectricFunctionModelSvgGroupPT(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pPt->updateMapSvg(ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg); + pPt->setItemType(GIT_ptGroup); + item = pPt; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + if(!valueInfo.isEmpty()){ //使用设置的绕组数更新自己 + QList lst; //二次绕组接法 + if(valueInfo.contains("pt_sec_winding")){ + PropertyStateInfo val = valueInfo.value("pt_sec_winding"); + + QJsonDocument jsonDocument = QJsonDocument::fromJson(val.defaultValue.toString().toUtf8().data()); + if(!jsonDocument.isNull()){ + QJsonObject obj = jsonDocument.object(); + QJsonArray arr = obj["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + QJsonObject node = jsonObj.toObject(); + QString sWinding = node["windingConnectionMethod"].toString(); + if(sWinding == "Y"){ + lst.append(1); + } + else{ + lst.append(0); + } + } + } + + QVariant var = QVariant::fromValue(lst); + pPt->setupFinish(var); + } + } + } + else if(type == GIT_DS) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[7].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["ds"]; + } + + if(width == 0 && height == 0){ + dX = 30; + dY = 30; + } + else{ + dX = width; + dY = height; + } + + auto pDs = new ElectricFunctionModelSvgItemDS(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pDs->setItemType(GIT_DS); + pDs->loadSvg(svg); + item = pDs; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_ES) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[8].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["es"]; + } + + if(width == 0 && height == 0){ + dX = 30; + dY = 60; + } + else{ + dX = width; + dY = height; + } + + auto pEs = new ElectricFunctionModelSvgItemES(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pEs->setItemType(GIT_ES); + pEs->loadSvg(svg); + item = pEs; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_FES) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[9].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["fes"]; + } + + if(width == 0 && height == 0){ + dX = 30; + dY = 60; + } + else{ + dX = width; + dY = height; + } + + auto pFes = new ElectricFunctionModelSvgItemFES(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pFes->setItemType(GIT_FES); + pFes->loadSvg(svg); + item = pFes; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_DTEDS) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[10].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["dteds"]; + } + + if(width == 0 && height == 0){ + dX = 60; + dY = 30; + } + else{ + dX = width; + dY = height; + } + + auto pDteds = new ElectricFunctionModelSvgItemDTEDS(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pDteds->setItemType(GIT_DTEDS); + pDteds->loadSvg(svg); + item = pDteds; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_PI) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[11].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["potential_indicator"]; + } + + if(width == 0 && height == 0){ + dX = 30; + dY = 60; + } + else{ + dX = width; + dY = height; + } + + auto pPi = new ElectricFunctionModelSvgItemPI(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pPi->setItemType(GIT_PI); + pPi->loadSvg(svg); + item = pPi; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_LA) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[12].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["lightning_arrester"]; + } + + if(width == 0 && height == 0){ + dX = 30; + dY = 60; + } + else{ + dX = width; + dY = height; + } + + auto pLa = new ElectricFunctionModelSvgItemLA(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pLa->setItemType(GIT_LA); + pLa->loadSvg(svg); + item = pLa; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_cableTer) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[13].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["cable_termination"]; + } + + if(width == 0 && height == 0){ + dX = 20; + dY = 20; + } + else{ + dX = width; + dY = height; + } + + auto pCt = new ElectricFunctionModelSvgItemCableTer(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pCt->setItemType(GIT_cableTer); + pCt->loadSvg(svg); + item = pCt; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_cableEnd) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[14].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["cable_end"]; + } + + if(width == 0 && height == 0){ + dX = 20; + dY = 20; + } + else{ + dX = width; + dY = height; + } + + auto pCe = new ElectricFunctionModelSvgItemCableEnd(QRect(-dX*0.5, -dY*0.5, dX, dY)); + pCe->setItemType(GIT_cableEnd); + pCe->loadSvg(svg); + item = pCe; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_2wTransformer) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[15].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["transformer_2w"]; + } + + if(width == 0 && height == 0){ + dX = 100; + dY = 100; + } + else{ + dX = width; + dY = height; + } + + auto p2w = new ElectricFunctionModelSvgItem2wTransformer(QRect(-dX*0.5, -dY*0.5, dX, dY)); + p2w->setItemType(GIT_2wTransformer); + p2w->loadSvg(svg); + item = p2w; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + else if(type == GIT_3wTransformer) + { + PropertyModel model = ProjectModelManager::instance().getData()[sMeta][sProModel]; + QByteArray svg; + if(model.modelSetting.mapSvg.isEmpty()){ + svg = DataBase::GetInstance()->ModelType()[16].icon; + } + else{ + svg = ProjectModelManager::instance().getData()[sMeta][sProModel].modelSetting.mapUsedSvg["transformer_3w"]; + } + + if(width == 0 && height == 0){ + dX = 100; + dY = 100; + } + else{ + dX = width; + dY = height; + } + + auto p3w = new ElectricFunctionModelSvgItem3wTransformer(QRect(-dX*0.5, -dY*0.5, dX, dY)); + p3w->setItemType(GIT_3wTransformer); + p3w->loadSvg(svg); + item = p3w; + QJsonArray portArr = pro->context()["port"].toArray(); + + addPortsToItem_json(P_const,portArr,item); + } + if(item) + { + item->setItemId(id); + item->editShape(0, pos); + item->setPos(pos); + item->setRotation(rotate); + _scene->addItem(item); + item->addPoint(pos); + item->setProperty(pro); //绑定模型 + item->updateByProperty(); //使用模型更新自身 + item->setHandle(this); + _nodeItem.insert(id,item); + //connect(item,&GraphicsFunctionModelItem::ifExist,this,&FixedPortsModel::onSignal_ifExits); + connect(item,&GraphicsBaseItem::itemRotated,this,[this](GraphicsBaseItem* pBase){ + if(pBase){ + auto pPro = pBase->getProperty(); + QUuid uid = pPro->uuid(); + if(pPro->type() != 8){ //排除线 + updateItemLinePort(uid,ModelFunctionType::ProjectModel); //hmi不改变拓扑结构,继续使用系统图的连接关系 + } + } + }); + } + return pro->name(); + } + } + return QString("err"); +} + +BaseProperty* FixedPortsModel::addNodeData(QUuid id,int type,QString name,QString modelName) +{ + BaseProperty* pData = BasePropertyManager::instance().findEntityData(id); //已存在不不创建 + if(pData != nullptr) + return pData; + + VariableProperty* item = new VariableProperty(); + //todo:关联到对应data + + if(item) + { + item->setUuid(id); + item->setModelName(modelName); + item->setType(type); + GraphicsItemType localType = typeToProGraphic[type]; //将通用类型转换为工程模图元 + item->setGraphicsType(localType); + item->setTag(name); + item->setName(name); + item->setDataChanged(true); + BasePropertyManager::instance().insertEntityData(id,item); + } + return item; +} + +void FixedPortsModel::loadNodeDataFromDataBase() +{ + if(!_dataInitialised) + { + QList lst= DataBase::GetInstance()->getAllComponents(); + for(auto &info:lst) + { + QString preTag = removeSuffix(info.tag); + QString prePath = removeSuffix(info.nspath); + BaseProperty* pData = addNodeData(info.uuid,info.type,info.name,info.modelName); + pData->setTag(preTag); + pData->setName(info.name); + pData->setPath(prePath); + pData->setDescription(info.description); + pData->setInService(info.inService); + pData->setState(info.state); + pData->setStatus(info.status); + pData->setConnectedBus(info.connected_bus); + pData->setLabel(info.label); + pData->setContext(info.context); + pData->setGrid(info.grid); + pData->setZone(info.zone); + pData->setStation(info.station); + pData->setBay(prePath); + pData->setDataChanged(false); + + QString sMeta = info.context["metaModel"].toString(); + pData->setMetaModelName(sMeta); + + double dVoltage = info.context["extraInfo"].toDouble(); + pData->setVoltageLevel(dVoltage); + + QJsonArray nodesJsonArray = info.context["subList"].toArray(); + QList> lst; + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + int nCategory = node["category"].toInt(); + QUuid uid = QUuid(node["uuid"].toString()); + lst.append(qMakePair(nCategory,uid)); + } + pData->setSubList(lst); + + PowerEntity* pEntity = TopologyManager::instance().createEntity(EntityType::Component,info.uuid.toString(),info.name); //首先load所有data和entity,全局唯一 + if(pEntity){ + createTopoTerminalsByData(pEntity,info.context); + } + + QList lstMeasure = DataBase::GetInstance()->getMeasurement(info.uuid); //添加设备量测 + QMap mapMeasure; + for(auto& info:lstMeasure) + { + info.tag = removeSuffix(info.tag); + mapMeasure.insert(info.name,info); + } + assignMeasureSymmetry(mapMeasure); + pData->setMeasurement(mapMeasure); + } + + QList lstTopo = DataBase::GetInstance()->getAllTopologics(); + for(auto &info:lstTopo) + { + QString from_pin = info.context["from_pin"].toString(); + QString to_pin = info.context["to_pin"].toString(); + + TopologyManager::instance().createConnection(QString::number(info.id),from_pin,to_pin,info.uuid_from.toString(),info.uuid_to.toString()); + } + + QList lstBay = DataBase::GetInstance()->getAllBay(); + for(auto& bay:lstBay) + { + QString showTag = removeSuffix(bay.tag); + BayProperty* pBay = addBayData(bay.uuid); + pBay->setName(bay.name); + pBay->setTag(showTag); + pBay->setType(bay.type); + pBay->setLstComponent(bay.components); + pBay->setVoltage(bay.unom); + pBay->setFla(bay.fla); + pBay->setCapacity(bay.capacity); + pBay->setInService(bay.inService); + auto fromLst = turnJsonArrToList(bay.fromUuid,"id","ids"); + pBay->setLstFrom(fromLst); + auto toLst = turnJsonArrToList(bay.toUuid,"id","ids"); + pBay->setLstTo(toLst); + auto proptecLst = turnJsonArrToList(bay.protect,"id","ids"); + pBay->setLstProtect(proptecLst); + auto falRecLst = turnJsonArrToList(bay.faultRec,"id","ids"); + pBay->setLstFaultRecord(falRecLst); + auto dynSenLst = turnJsonArrToList(bay.dynSense,"id","ids"); + pBay->setLstDynSense(dynSenLst); + auto staLst = turnJsonArrToList(bay.status,"id","ids"); + pBay->setLstStatus(staLst); + auto insLst = turnJsonArrToList(bay.instruct,"id","ids"); + pBay->setLstInstruct(insLst); + auto etcLst = turnJsonArrToList(bay.etc,"id","ids"); + pBay->setLstEtc(etcLst); + + QMap mapMeasure; + QList tempLst = DataBase::GetInstance()->getBayMeasurement(bay.uuid); + for(auto& info:tempLst){ + if(info.componentUuid == QUuid("11111111-1111-1111-1111-111111111111")) //只处理间隔外量测 + mapMeasure.insert(info.name,info); + } + pBay->setMeasurement(mapMeasure); + } + _dataInitialised = true; + } + else + { + //for(auto p:_nodeData) + { + int a = 1; + } + } +} + +QString FixedPortsModel::addConnectLline(QUuid lineId,QUuid srcId,QUuid destId,QUuid srcPort,QUuid destPort) +{ + GraphicsFunctionModelItem* src = nullptr; + if(_nodeItem.contains(srcId)) + src = _nodeItem.value(srcId); + GraphicsFunctionModelItem* dest = nullptr; + if(_nodeItem.contains(destId)) + dest = _nodeItem.value(destId); + QMap mapData = BasePropertyManager::instance().getEntityData(); + if(mapData.contains(lineId)) + { + BaseProperty* pPro = mapData.value(lineId); + if(src && dest && pPro) + { + ElectricFunctionModelConnectLineItem* pItem = new ElectricFunctionModelConnectLineItem(); + pItem->setItemId(lineId); + pItem->setItemType(GIT_link); + _scene->addItem(pItem); + + ItemPort* ptSrc = src->getPortById(srcPort.toString()); + HandleType srcType = ptSrc->getType(); + PortPos srcPos = ptSrc->portPos(); + pItem->setStartPoint(ptSrc->scenePos()); + ptSrc->setConnect(pItem); + + ItemPort* ptDest = nullptr; + ptDest = dest->getPortById(destPort.toString()); + pItem->setEndPoint(ptDest->scenePos()); + + if(ptDest != nullptr) + { + HandleType destType = ptDest->getType(); + PortPos destPos = ptDest->portPos(); + + pItem->calculatePath(); + + pPro->setConnection(Connection(srcId,srcPort,srcType,srcPos,destId,destPort,destType,destPos)); + ptDest->setConnect(pItem); + + addNodeItem(pItem->itemId(),pItem); + pItem->setProperty(pPro); + } + + return pPro->name(); + } + } + return QString("err"); +} + +void FixedPortsModel::deleteNodeItem(GraphicsFunctionModelItem* pItem) +{ + if(pItem->getItemType() == GIT_link) + { + auto pLine = dynamic_cast(pItem); + if(pLine) + { + QUuid conId = pLine->itemId(); + TopologyManager::instance().removeConnection(conId.toString()); + + PowerConnection* pCon = TopologyManager::instance().connection(conId.toString()); + if(pCon){ + PowerEntity* itemFrom = TopologyManager::instance().getEntityByTerminal(pCon->fromTerminalId()); + PowerEntity* itemTo = TopologyManager::instance().getEntityByTerminal(pCon->toTerminalId()); + if(itemFrom && itemTo) + { + _nodeItem.value(QUuid(itemFrom->id()))->setItemChanged(true); + _nodeItem.value(QUuid(itemTo->id()))->setItemChanged(true); + } + } + + _nodeItem.take(conId); + _scene->removeItem(pLine); + delete pLine; + } + } + else + { + QUuid srcId = pItem->itemId(); + QList lstCon = TopologyManager::instance().getConnectionsFor(srcId.toString()); + for(auto &con:lstCon) + { + PowerEntity* pFrom = TopologyManager::instance().getEntityByTerminal(con->fromTerminalId()); + PowerEntity* pTo = TopologyManager::instance().getEntityByTerminal(con->toTerminalId()); + if(pFrom && pTo) + { + if(srcId.toString() == pFrom->id()) //对象是srcItem + _nodeItem.value(QUuid(pTo->id()))->setItemChanged(true); + else //对象是toItem + _nodeItem.value(QUuid(pFrom->id()))->setItemChanged(true); + } + + //TopologyManager::instance().removeConnection(con->id()); //删除entity时会同时删除相关连接,无需手动删除 + } + TopologyManager::instance().deleteEntity(srcId.toString()); + + pItem->setItemChanged(true); + _nodeItem.take(srcId); + _scene->removeItem(pItem); + delete pItem; + } +} + +void FixedPortsModel::saveNode(int nPageId) +{ + QList lstGrid = DataBase::GetInstance()->getAllGrid(); + QList lstZone = DataBase::GetInstance()->getAllZone(); + QList lstStation = DataBase::GetInstance()->getAllStation(); + QMap groupMap = DataBase::GetInstance()->AttributeGroup(); //属性组名 + bool bExist = DataBase::GetInstance()->componentExist(QUuid("11111111-1111-1111-1111-111111111111").toString()); + bool bSaveFlag = false; + + for(auto& bay:_bayItem) + { + AbstractProperty* pro = bay->getProperty(); //间隔 + BayProperty* pBay = dynamic_cast(pro); + if(pBay){ + bool val = DataBase::GetInstance()->ifBayExist(pBay->uuid()); + QString tempTag = pBay->tag()+"_"+_pageName; //tag后加工程名保持全局唯一 + if(!val){ + QJsonObject objFrom = turnListToJson(pBay->getLstFrom(),"id","ids"); + QJsonObject objTo= turnListToJson(pBay->getLstTo(),"id","ids"); + QJsonObject objProtec= turnListToJson(pBay->getLstProtect(),"id","ids"); + QJsonObject objFalRec= turnListToJson(pBay->getLstFaultRecord(),"id","ids"); + QJsonObject objStatus= turnListToJson(pBay->getLstStatus(),"id","ids"); + QJsonObject objDynSen= turnListToJson(pBay->getLstDynSense(),"id","ids"); + QJsonObject objIns= turnListToJson(pBay->getLstInstruct(),"id","ids"); + QJsonObject objEtc= turnListToJson(pBay->getLstEtc(),"id","ids"); + + DataBase::GetInstance()->insertBay(pBay->uuid(),pBay->name(),tempTag,pBay->getType(),pBay->getVoltage(),pBay->getFla(),pBay->getCapacity(),"1",pBay->getInService(),0,"1","1","1",QJsonObject(),objFrom,objTo,objProtec,objFalRec,objStatus,objDynSen,objIns,objEtc,pBay->getLstComponent(),QJsonObject()); + } + else{ + QJsonObject objFrom = turnListToJson(pBay->getLstFrom(),"id","ids"); + QJsonObject objTo= turnListToJson(pBay->getLstTo(),"id","ids"); + QJsonObject objProtec= turnListToJson(pBay->getLstProtect(),"id","ids"); + QJsonObject objFalRec= turnListToJson(pBay->getLstFaultRecord(),"id","ids"); + QJsonObject objStatus= turnListToJson(pBay->getLstStatus(),"id","ids"); + QJsonObject objDynSen= turnListToJson(pBay->getLstDynSense(),"id","ids"); + QJsonObject objIns= turnListToJson(pBay->getLstInstruct(),"id","ids"); + QJsonObject objEtc= turnListToJson(pBay->getLstEtc(),"id","ids"); + + DataBase::GetInstance()->updateBay(pBay->uuid(),pBay->name(),tempTag,pBay->getVoltage(),pBay->getFla(),pBay->getCapacity(),"",pBay->getInService(),0,QJsonObject(),objFrom,objTo,objProtec,objFalRec,objStatus,objDynSen,objIns,objEtc,pBay->getLstComponent(),QJsonObject()); + } + + QString sGridTag; + QString sGridName; + QString sZoneTag; + QString sZoneName; + QString sStationTag; + QString sStationName; + QList lstId = pBay->getLstComponent(); //获取间隔下的所有item,取一个item所属的层级关系 + if(!lstId.isEmpty()){ + auto pItemData = BasePropertyManager::instance().findEntityData(lstId.first()); + if(pItemData){ + for(auto& gridInfo:lstGrid){ + if(pItemData->grid() == gridInfo.tagname){ + sGridTag= gridInfo.tagname; + sGridName= gridInfo.name; + break; + } + } + + for(auto& zoneInfo:lstZone){ + if(pItemData->zone() == zoneInfo.tagname){ + sZoneTag = zoneInfo.tagname; + sZoneName = zoneInfo.name; + break; + } + } + + for(auto& stationInfo:lstStation){ + if(pItemData->station() == stationInfo.tagname){ + sStationTag = stationInfo.tagname; + sStationName = stationInfo.name; + break; + } + } + } + } + + if(!bExist){ + if(!bSaveFlag){ + DataBase::GetInstance()->insertComponent(QUuid("11111111-1111-1111-1111-111111111111"),"","","","","","","",sStationTag,-1,false,-1,-1,QJsonObject(),QJsonObject(),QJsonObject(),1); + bSaveFlag = true; + } + } + + QMap mapMeasure = pBay->getMeasurement(); //量测 + QList lstTemp= DataBase::GetInstance()->getBayExtraProperty(tempTag); //component中的层级关系 + QList lstExtra; + for(auto& info:lstTemp){ + if(info.component_uuid != QUuid("11111111-1111-1111-1111-111111111111")) //只判断设备外量测 + continue; + info.bay_tag = removeSuffix(info.bay_tag); + lstExtra.append(info); + } + + QList lstDataBase; + QList lstTempBase = DataBase::GetInstance()->getBayMeasurement(pBay->uuid()); //数据库中现有量测 + for(auto& info:lstTempBase){ + if(info.componentUuid != QUuid("11111111-1111-1111-1111-111111111111")) //只判断设备外量测 + continue; + lstDataBase.append(info); + } + + for(auto& info:mapMeasure) + { + int tpe = info.type; //todo:建立类型映射表 + + QJsonObject objDataSource; + QJsonObject objIoAddress; + if(info.nSource == 1){ //3611 + objDataSource["type"] = 1; + objIoAddress["station"] = info.sStation; + objIoAddress["device"] = info.sDevice; + objIoAddress["channel"] = info.sChannel; + } + else if(info.nSource == 2){ //104 + objDataSource["type"] = 2; + objIoAddress["station"] = info.sStation; + objIoAddress["packet"] = info.nPacket; + objIoAddress["offset"] = info.nOffset; + } + objDataSource["io_address"] = objIoAddress; + + QJsonObject objEventPlan; + QJsonObject objCause; + QJsonObject objAction; + QJsonArray arrPara; + objEventPlan["enable"] = info.bEnable; + if(tpe == 0){ //遥测 + for(auto iter = info.mapTE.begin();iter != info.mapTE.end();++iter){ + objCause[iter.key()] = iter.value(); + } + } + else if(tpe == 1){ //遥信 + objCause["edge"] = info.sEdge; + } + objEventPlan["cause"] = objCause; + + objAction["command"] = info.sCommand; + for(auto ¶:info.lstParameter){ + arrPara.append(para); + } + objAction["parameters"] = arrPara; + objEventPlan["action"] = objAction; + + QJsonObject objBinding; + if(!info.sWindType.isEmpty()){ + QJsonObject objWind; + objWind["ratio"] = info.nRatio; + objWind["polarity"] = info.nPolarity; + objWind["index"] = info.nIndex; + objBinding[info.sWindType] = objWind; + } + + QString tempMeasure = info.tag+"_"+_pageName; //tag后加工程名,保持全局唯一 + + bool val = DataBase::GetInstance()->ifBayMeasureExist(info.name,pBay->uuid()); + if(val){ + DataBase::GetInstance()->updateMeasurement(info.name,tpe,objDataSource,objEventPlan,objBinding,info.size,QUuid("11111111-1111-1111-1111-111111111111")); + } + else{ + DataBase::GetInstance()->insertMeasurement(info.name,tempMeasure,tpe,objDataSource,objEventPlan,objBinding,info.size,info.bayUuid,QUuid("11111111-1111-1111-1111-111111111111")); + } + + for(int i = 0;i < lstDataBase.size();++i) //从数据库记录中移除操作过的对象 + { + if(lstDataBase[i].name == info.name){ + lstDataBase.removeAt(i); + break; + } + } + + for(int i = 0;i < lstExtra.size();++i){ //同步层级信息计数 + if(lstExtra[i].name == info.name){ + lstExtra.removeAt(i); + break; + } + } + + ExtraProperty extraPro; //层级信息 + extraPro.name = info.name; + extraPro.tag = tempMeasure; + extraPro.grid_tag = sGridTag; + extraPro.grid_name = sGridName; + extraPro.zone_tag = sZoneTag; + extraPro.zone_name = sZoneName; + extraPro.station_tag = sStationTag; + extraPro.station_name = sStationName; + + extraPro.currentLevel = QString::number(pBay->getVoltage())+"kv"; + extraPro.page_tag = _widget->pageName(); //暂时相同 + extraPro.bay_name = pBay->name(); + extraPro.bay_tag = tempTag; + extraPro.component_uuid = QUuid("11111111-1111-1111-1111-111111111111"); + + for(auto& groupInfo:groupMap){ + if("bay" == groupInfo.groupType){ + extraPro.group_name = groupInfo.groupName; + break; + } + } + + if(tpe == 0){ + extraPro.type_name = "遥测"; + extraPro.type_tag = "telemetry"; + } + else if(tpe == 1){ + extraPro.type_name = "遥信"; + extraPro.type_tag = "telesignal"; + } + else if(tpe == 2){ + extraPro.type_name = "遥控"; + extraPro.type_tag = "telecontrol"; + } + extraPro.code = extraPro.getFullName(); + extraPro.sourceType = "measurement"; + extraPro.connect_para = sGridTag+"."+sZoneTag+"."+sStationTag+"."+extraPro.bay_tag+"."+extraPro.tag; + //取data 量测tag + + bool exist = DataBase::GetInstance()->ifExtraPropertyExist(extraPro.code); + if(exist) + DataBase::GetInstance()->updateExtraProperty(extraPro); + else + DataBase::GetInstance()->insertExtraProperty(extraPro); + } + + for(auto& info:lstDataBase) //操作的记录小于数据库中的记录,删除库中多出的记录 + { + DataBase::GetInstance()->delteMeasurement(info.name,info.componentUuid); + } + + for(auto& info:lstExtra){ //删除库中多出的层级信息 + DataBase::GetInstance()->deleteExtraProperty(info.code); + } + } + } + + QMap mapItems = allItems(); + for(auto& pItem:mapItems) + { + BaseProperty* pData = dynamic_cast(pItem->getProperty()); + if(pData){ + Connection con = pData->getConnection(); + QString fromPin = con.nSrcPortId.toString(); + QString toPin = con.nDestPortId.toString(); + QJsonObject context; + context["from_pin"] = fromPin; + context["to_pin"] = toPin; + if(pData->prepareDelete()) + { + DataBase::GetInstance()->deleteComponent(pData->uuid().toString()); + if(pData->type() == 8){ + PowerConnection* pCon = TopologyManager::instance().connection(fromPin,toPin); + if(pCon){ + DataBase::GetInstance()->deleteTopologic(con.nSrcPortId,con.nDestPortId); + TopologyManager::instance().removeConnection(pCon->id()); + } + } + continue; + } + if(pData->dataChanged()) + { + pData->setDataChanged(false); + bool exist = DataBase::GetInstance()->componentExist(pData->uuid().toString()); + VariableProperty* pVariable = dynamic_cast(pData); + if(pVariable) + { + ModelDataInfo& dataInfo = pVariable->getPropertyValue(); + QString tempTag = pData->tag()+"_"+_pageName; //tag后加工程名使得全局唯一 + if(exist) //已存在更新 + { + DataBase::GetInstance()->updateComponent(pData->uuid(),tempTag,pData->name(),pData->context(),pData->inService(),pData->state(),pData->status()); + for(auto &val:dataInfo.groupInfo) + { + if(val.groupName == "component") + continue; + DataBase::GetInstance()->updateDynamicProperty(pData->uuid(),val); + if(val.mapInfo.contains(pData->uuid())){ //保存时将数据锁复原 + auto& mapPro = val.mapInfo[pData->uuid()]; + for(auto& pro:mapPro) + { + pro.lock = false; + } + } + } + } + else + { + QString tempPath = pData->path()+"_"+_pageName; + DataBase::GetInstance()->insertComponent(pData->uuid(),pData->modelName(),tempPath,tempTag,pData->name(),pData->description(),pData->grid(),pData->zone(),pData->station(),pData->type(),true,pData->state(),pData->status(),pData->connectedBus(),pData->label(),pData->context(),1); + for(auto &val:dataInfo.groupInfo) + { + if(val.groupName == "component") + continue; + DataBase::GetInstance()->insertDynamicProperty(pData->uuid(),val); + if(val.mapInfo.contains(pData->uuid())){ //保存时将数据锁复原 + auto& mapPro = val.mapInfo[pData->uuid()]; + for(auto& pro:mapPro) + { + pro.lock = false; + } + } + } + } + + //层级关系与连接参数 + BayProperty* bayPro = nullptr; //本data对应的bay + QMap mapBay = allBayItem(); + for(auto& item:mapBay){ + AbstractProperty* pPro = item->getProperty(); + BayProperty* pBayPro = dynamic_cast(pPro); + if(pBayPro){ + QList lstCompo = pBayPro->getLstComponent(); //获取间隔下的component,找到本component对应的间隔 + for(auto& id:lstCompo){ + if(id == pData->uuid()){ + bayPro = pBayPro; + break; + } + } + } + } + QString sGridTag; + QString sGridName; + QString sZoneTag; + QString sZoneName; + QString sStationTag; + QString sStationName; + for(auto& gridInfo:lstGrid){ + if(pData->grid() == gridInfo.tagname){ + sGridTag= gridInfo.tagname; + sGridName= gridInfo.name; + break; + } + } + + for(auto& zoneInfo:lstZone){ + if(pData->zone() == zoneInfo.tagname){ + sZoneTag = zoneInfo.tagname; + sZoneName = zoneInfo.name; + break; + } + } + + for(auto& stationInfo:lstStation){ + if(pData->station() == stationInfo.tagname){ + sStationTag = stationInfo.tagname; + sStationName = stationInfo.name; + break; + } + } + for(auto &val:dataInfo.groupInfo) + { + if(val.groupName == "component" || val.groupName == "bay") + continue; + if(val.mapInfo.contains(pData->uuid())){ + auto mapPro = val.mapInfo[pData->uuid()]; + for(auto& pro:mapPro) + { + ExtraProperty extraPro; //层级信息 + extraPro.name = pro.name; + extraPro.tag = pro.tagName; + extraPro.grid_tag = sGridTag; + extraPro.grid_name = sGridName; + extraPro.zone_tag = sZoneTag; + extraPro.zone_name = sZoneName; + extraPro.station_tag = sStationTag; + extraPro.station_name = sStationName; + + extraPro.currentLevel = QString::number(pData->getVoltageLevel())+"kv"; + extraPro.page_tag = _widget->pageName(); //暂时相同 + if(bayPro){ + extraPro.bay_name = bayPro->name(); + extraPro.bay_tag = bayPro->tag(); + } + extraPro.component_name = pData->name(); + extraPro.component_uuid = pData->uuid(); + extraPro.component_tag = tempTag; + extraPro.group_tag = val.groupName; + + for(auto& groupInfo:groupMap){ + if(val.groupName == groupInfo.groupType){ + extraPro.group_name = groupInfo.groupName; + break; + } + } + extraPro.type_name = "参量"; + extraPro.type_tag = "parameter"; + + extraPro.code = extraPro.getFullName(); + extraPro.sourceType = "property"; + extraPro.sourceConfig.insert("modelName",pData->modelName()); + extraPro.connect_para = sGridTag+"."+sZoneTag+"."+sStationTag+"."+extraPro.bay_tag+"."+tempTag+"."+extraPro.group_tag+"."+extraPro.tag; + //取data:模型.属性组.id.属性名 + + bool exist = DataBase::GetInstance()->ifExtraPropertyExist(extraPro.code); + if(exist) + DataBase::GetInstance()->updateExtraProperty(extraPro); + else + DataBase::GetInstance()->insertExtraProperty(extraPro); + } + } + } + + + QMap mapMeasure = pData->getMeasurement(); //量测 + QList lstTemp= DataBase::GetInstance()->getCompoExtraProperty(pData->uuid()); //component中的层级关系 + QList lstExtra; + for(auto& info:lstTemp){ + if(info.group_tag != "bay") //只对量测判断 + continue; + info.bay_tag = removeSuffix(info.bay_tag); + lstExtra.append(info); + } + + QList lstDataBase = DataBase::GetInstance()->getMeasurement(pData->uuid()); //数据库中现有量测 + for(auto& info:mapMeasure) + { + int tpe = info.type; //todo:建立类型映射表 + + QJsonObject objDataSource; + QJsonObject objIoAddress; + if(info.nSource == 1){ //3611 + objDataSource["type"] = 1; + objIoAddress["station"] = info.sStation; + objIoAddress["device"] = info.sDevice; + objIoAddress["channel"] = info.sChannel; + } + else if(info.nSource == 2){ //104 + objDataSource["type"] = 2; + objIoAddress["station"] = info.sStation; + objIoAddress["packet"] = info.nPacket; + objIoAddress["offset"] = info.nOffset; + } + objDataSource["io_address"] = objIoAddress; + + QJsonObject objEventPlan; + QJsonObject objCause; + QJsonObject objAction; + QJsonArray arrPara; + objEventPlan["enable"] = info.bEnable; + if(tpe == 0){ //遥测 + for(auto iter = info.mapTE.begin();iter != info.mapTE.end();++iter){ + objCause[iter.key()] = iter.value(); + } + } + else if(tpe == 1){ //遥信 + objCause["edge"] = info.sEdge; + } + objEventPlan["cause"] = objCause; + + objAction["command"] = info.sCommand; + for(auto ¶:info.lstParameter){ + arrPara.append(para); + } + objAction["parameters"] = arrPara; + objEventPlan["action"] = objAction; + + QJsonObject objBinding; + if(!info.sWindType.isEmpty()){ + QJsonObject objWind; + objWind["ratio"] = info.nRatio; + objWind["polarity"] = info.nPolarity; + objWind["index"] = info.nIndex; + objBinding[info.sWindType] = objWind; + } + + QString tempMeasure = info.tag+"_"+_pageName; //tag后加工程名,保持全局唯一 + + bool val = DataBase::GetInstance()->ifMeasureExist(info.name,pData->uuid()); + if(val){ + DataBase::GetInstance()->updateMeasurement(info.name,tpe,objDataSource,objEventPlan,objBinding,info.size,pData->uuid()); + } + else{ + DataBase::GetInstance()->insertMeasurement(info.name,tempMeasure,tpe,objDataSource,objEventPlan,objBinding,info.size,info.bayUuid,info.componentUuid); + } + + for(int i = 0;i < lstDataBase.size();++i) //从数据库记录中移除操作过的对象 + { + if(lstDataBase[i].name == info.name){ + lstDataBase.removeAt(i); + break; + } + } + + for(int i = 0;i < lstExtra.size();++i){ //同步层级信息计数 + if(lstExtra[i].name == info.name){ + lstExtra.removeAt(i); + break; + } + } + + ExtraProperty extraPro; //层级信息 + extraPro.name = info.name; + extraPro.tag = tempMeasure; + extraPro.grid_tag = sGridTag; + extraPro.grid_name = sGridName; + extraPro.zone_tag = sZoneTag; + extraPro.zone_name = sZoneName; + extraPro.station_tag = sStationTag; + extraPro.station_name = sStationName; + + extraPro.currentLevel = QString::number(pData->getVoltageLevel())+"kv"; + extraPro.page_tag = _widget->pageName(); //暂时相同 + if(bayPro){ + extraPro.bay_name = bayPro->name(); + extraPro.bay_tag = bayPro->tag(); + } + extraPro.component_name = pData->name(); + extraPro.component_uuid = pData->uuid(); + extraPro.component_tag = tempTag; + extraPro.group_tag = "bay"; + + for(auto& groupInfo:groupMap){ + if("bay" == groupInfo.groupType){ + extraPro.group_name = groupInfo.groupName; + break; + } + } + + if(tpe == 0){ + extraPro.type_name = "遥测"; + extraPro.type_tag = "telemetry"; + } + else if(tpe == 1){ + extraPro.type_name = "遥信"; + extraPro.type_tag = "telesignal"; + } + else if(tpe == 2){ + extraPro.type_name = "遥控"; + extraPro.type_tag = "telecontrol"; + } + extraPro.code = extraPro.getFullName(); + extraPro.sourceType = "measurement"; + extraPro.connect_para = sGridTag+"."+sZoneTag+"."+sStationTag+"."+extraPro.bay_tag+"."+tempTag+"."+extraPro.group_tag+"."+extraPro.tag; + //取data 量测tag + + bool exist = DataBase::GetInstance()->ifExtraPropertyExist(extraPro.code); + if(exist) + DataBase::GetInstance()->updateExtraProperty(extraPro); + else + DataBase::GetInstance()->insertExtraProperty(extraPro); + } + + for(auto& info:lstDataBase) //操作的记录小于数据库中的记录,删除库中多出的记录 + { + DataBase::GetInstance()->delteMeasurement(info.name,info.componentUuid); + } + + for(auto& info:lstExtra){ //删除库中多出的层级信息 + DataBase::GetInstance()->deleteExtraProperty(info.code); + } + } + + if(pData->type() == 8){ + PowerConnection* pCon = TopologyManager::instance().connection(fromPin,toPin); + if(pCon){ + int id = DataBase::GetInstance()->topologicExist(con.nSrcNodeId,con.nDestNodeId); + if(id == -1) + DataBase::GetInstance()->insertTopologic(con.nSrcNodeId,con.nDestNodeId,context,0,"",0); + } + } + } + } + } +} + +void FixedPortsModel::onSignal_ifExits(QUuid id,const QString& str,int type,GraphicsFunctionModelItem* pitem) +{ + bool exist = false; + BaseProperty* pData = nullptr; + QMap mapData = BasePropertyManager::instance().getEntityData(); + for(auto pro:mapData) + { + if(pro->tag() == str) + { + pData = pro; + exist = true; + break; + } + } + if(exist) //已存在,将发出信号的item绑定到此data + { + if(_nodeItem.contains(id)) //发出信号对象id与data对象id相同,已绑定,不做响应 + return; + QMessageBox msgBox; + msgBox.setText(QString::fromWCharArray(L"提示")); + msgBox.setInformativeText(QString::fromWCharArray(L"此名称对象已存在,是否使用该对象?")); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret = msgBox.exec(); + switch (ret) { + case QMessageBox::Ok: + { + //todo:断开原来的连接关系 + pitem->setProperty(pData); + pitem->setItemId(pData->uuid()); + PowerEntity* pEntity = TopologyManager::instance().findEntity(pData->uuid().toString()); + if(pEntity){ + pitem->setEntity(pEntity); //item对象的逻辑接线点无需重复创建 + } + _nodeItem.take(id); + _nodeItem.insert(pData->uuid(),pitem); + } + break; + case QMessageBox::Cancel: + // Cancel was clicked + break; + default: + // should never be reached + break; + } + } + else //创建新data并绑定 + { + BaseProperty* pData = addNodeData(id,type,str,pitem->getModelName()); + if(pData) + { + pitem->setProperty(pData); + pData->setDataChanged(true); //数据状态改变 + } + + PowerEntity* pEntity = TopologyManager::instance().createEntity(EntityType::Component,id.toString(),str); + if(pEntity) + pitem->setEntity(pEntity); + createTopoTerminalsByItem(pitem); //创建item对象的逻辑接线点 + } +} + + +void FixedPortsModel::onTimeOut() +{ + _Interface->getPointData("i"); + _Interface->getPointData("v"); +} + +void FixedPortsModel::onSignal_GetPointData(QString type,QMap map) +{ + if(map.size() == 1) //实时数据 + { + double d = map.first(); + + //for(auto pro:_nodeData) //demo版本只有一个数据 + { + //int t = pro->type(); + //if(t == GIT_itemRect) + { + //todo:根据id匹配数据 + /*auto p = dynamic_cast(pro); + if(p) + { + p->notifyUpdate(); //通知更新 + }*/ + } + } + } +} + +void FixedPortsModel::onSignal_openBayManager() +{ + if(m_pBayManager == nullptr){ + m_pBayManager = new BayManagerDlg(_widget); + m_pBayManager->setModelController(this); + } + m_pBayManager->showDlg(); +} + +void FixedPortsModel::onDataTimerOut() +{ + if(!_curRequestLst.isEmpty()){ + auto pDataAccessor = _cavas->getDataAccessor(); + if(pDataAccessor){ + _curData = pDataAccessor->getTargetData(_curRequestLst); + if(!_curData.isEmpty()) + updateMonitor(_curData); + } + } +} + +void FixedPortsModel::onSelectionChanged() +{ + QList selectedItems = _scene->selectedItems(); + if(_cavas){ + if(selectedItems.count() != 1) { + if(_widget) + _cavas->onTargetSelected(_widget->getPropertyProxy()); + return; + } + GraphicsBaseItem *item = static_cast(selectedItems.first()); + _cavas->onTargetSelected(item); + } +} + +void FixedPortsModel::startHttpRequest() +{ + if(_timer) + { + _timer->setInterval(1000); + _timer->start(); + } +} + +DiagramCavas* FixedPortsModel::getCavas() +{ + if(_cavas) + return _cavas.data();; + return nullptr; +} + +void FixedPortsModel::addPortsToItem_json(PortState sta,QJsonArray jArr,GraphicsBaseItem* pItem) +{ + for(QJsonValueRef portJson:jArr) + { + QJsonObject portObj = portJson.toObject(); + QString portId = portObj["portId"].toString(); + double x = portObj["x"].toDouble(); + double y = portObj["y"].toDouble(); + if(sta == P_const){ + HandleType tye = HandleType(portObj["portType"].toInt()); + PortPos locate = PortPos(portObj["locate"].toInt()); + double ratioX = 0.0; + double ratioY = 0.0; + if(portObj.contains("xRatio") || portObj.contains("yRatio")){ + ratioX = portObj["xRatio"].toDouble(); + ratioY = portObj["yRatio"].toDouble(); + } + pItem->addPort(P_const,QPointF(x,y),portId,tye,locate,ratioX,ratioY); + } + else if(sta == p_movable){ + pItem->addPort(p_movable,QPointF(x,y),portId); + } + } +} + +void FixedPortsModel::autoSetModelName(GraphicsBaseModelItem* pItem) +{ + ModelProperty* p = pItem->getProperty(); + BaseModelProperty* pro = dynamic_cast(p); + if(pro){ + QString sMeta = pro->metaModelName(); + QString sModel = sMeta+"_"+_pageName; + bool exist = ProjectModelManager::instance().getData()[sMeta].contains(sModel); + if(exist){ + pro->setModelName(sModel); + pro->getModelProperty().modelSetting.modelName = sModel; + } + } +} + +QString FixedPortsModel::removeSuffix(const QString& str) +{ + int lastUnderscore = str.lastIndexOf('_'); + if (lastUnderscore == -1) return str; // 没有下划线 + + return str.left(lastUnderscore); +} + +ModelProperty* FixedPortsModel::getItemByUid(QList lst,QUuid uid) +{ + for(auto& item:lst){ + auto pPro = item->getProperty(); + if(pPro->uuid() == uid){ + return pPro; + } + } + return nullptr; +} + +void FixedPortsModel::updateMonitor(QMap> data) +{ + for(auto iter = m_monitorPara.begin();iter != m_monitorPara.end();++iter){ + for(auto &info:iter.value()){ + //if(info.bSelected){ + if(data.contains(info.sConnectPara)){ + info.mapValue = data.value(info.sConnectPara); + } + //} + } + } + emit dataUpdated(); + + for(auto& item:_nodeItem){ //更新界面中的数据 + QMap mapText = item->getDynamicText(); + for(auto &pText:mapText){ + QString sPara = pText->getPara(); + if(data.contains(sPara)){ + QString sVal= QString::number(data.value(sPara).last()); + pText->setText(sVal); + pText->update(); + } + } + } +} + +void FixedPortsModel::assignMeasureSymmetry(QMap& measurementMap) +{ + // 用于存储每个键对应的tag + QMap keyToTagMap; + + for (auto it = measurementMap.begin(); it != measurementMap.end(); ++it) { + MeasurementKey currentKey(it.value()); + QString currentTag = it.key(); + + // 检查是否已经有相同的键 + if (keyToTagMap.contains(currentKey)) { + QString otherTag = keyToTagMap[currentKey]; + + // 找到一对,互相设置sSymmetry + measurementMap[currentTag].sSymmetry = measurementMap[otherTag].name; + measurementMap[otherTag].sSymmetry = measurementMap[currentTag].name; + + // 从map中移除,因为"有且仅有两个item" + keyToTagMap.remove(currentKey); + } else { + // 还没有配对的,添加到map中 + keyToTagMap[currentKey] = currentTag; + } + } +} + +QWidget* FixedPortsModel::getTopWidget() +{ + return dynamic_cast(_widget); +} + +QPointF FixedPortsModel::getTerminalPos(const QString& sTerminalId) +{ + PowerEntity* pParent = TopologyManager::instance().getEntityByTerminal(sTerminalId); + if(pParent) + { + for(auto &item:_nodeItem) + { + if(pParent->id() == item->itemId().toString()) //找到terminal父图元 + { + for(auto &pPort:item->getPorts()) + { + if(pPort->getId() == sTerminalId) + { + return pPort->scenePos(); + } + } + } + } + } + return QPointF(0,0); +} + +ElectricFunctionModelConnectLineItem* FixedPortsModel::getLineItemById(const QString& terminalId) +{ + for(auto &iter:_nodeItem) //获取id所在的lineitem + { + auto item = dynamic_cast(iter); + if(item) + { + if(item->getItemType() == GIT_link) + { + PowerConnection* pCon = TopologyManager::instance().getConnectionContainsTerminal(terminalId); + if(pCon) + { + ModelProperty* pPro = item->getProperty(); + if(pPro){ + Connection con = pPro->getConnection(); + if((pCon->fromTerminalId() == con.nSrcPortId.toString() && pCon->toTerminalId() == con.nDestPortId.toString()) + || (pCon->fromTerminalId() == con.nDestPortId.toString() && pCon->toTerminalId() == con.nSrcPortId.toString())){ //port相同为同一条线 + return dynamic_cast(item); + } + } + //QUuid uid = item->itemId(); + //QUuid conId = QUuid(pCon->id()); + //if(uid == conId) /************load的拓扑没有uid,使用其他方式判断相等 + } + } + } + } + return nullptr; +} + +void FixedPortsModel::updateItemLinePort(QUuid uid,ModelFunctionType type) +{ + QList lstCon = TopologyManager::instance().getConnectionsFor(uid.toString(),type); //获取item的所有连接 + for(auto &pCon:lstCon){ + QString baseFromComponentId = pCon->fromComponent(); + QString baseToComponentId = pCon->toComponent(); + QString baseFromTerId = pCon->fromTerminalId(); + QString baseToTerId = pCon->toTerminalId(); + + auto pLine = static_cast(nodeItem(QUuid(pCon->id()))); + if(pLine){ + if(uid.toString() == baseFromComponentId){ + QPointF posFrom = getTerminalPos(baseFromTerId); + pLine->setStartPoint(posFrom); + pLine->calculatePath(); + } + else if(uid.toString() == baseToComponentId){ + QPointF posTo = getTerminalPos(baseToTerId); + pLine->setEndPoint(posTo); + pLine->calculatePath(); + } + } + } +} + +void FixedPortsModel::showModelDlg(const QString& sName,QUuid uuid,GraphicsFunctionModelItem* pItem) +{ + ModelStateInfo stateInfo = _modelStateInfo[sName]; + ModelDataMap mapData = DataManager::instance().modelData(); + ItemPropertyDlg* pDlg = dynamic_cast(stateInfo._PropertyDlg); + if(pDlg) + { + pDlg->showDlg(mapData[sName],uuid,pItem); + m_curPropertyDlg = pDlg; + } + else + qDebug()<<"showModelDlg err"; +} + +void FixedPortsModel::initialPropertyDlg() +{ + for(auto &modelInfo:_modelStateInfo) + { + if(modelInfo._PropertyDlg == NULL) + { + generatePropertyDlg(modelInfo.modelName); + } + } +} + +void FixedPortsModel::generatePropertyDlg(const QString& sModel) +{ + ModelStateInfo info = _modelStateInfo[sModel]; + if(info._PropertyDlg == NULL) + { + ItemPropertyDlg* dlg = new ItemPropertyDlg(_widget); + if(dlg) + { + dlg->setModelController(this); + dlg->loadGroupButton(info.groupInfo); + _modelStateInfo[sModel]._PropertyDlg = dlg; + } + } +} + +ConfigurationDiagram* FixedPortsModel::getTopologyDiagram() +{ + return dynamic_cast(_pEntity); +} + +void FixedPortsModel::createTopoTerminalsByData(PowerEntity* pParent,QJsonObject componentCon,ModelFunctionType funType) +{ + QJsonArray portsArray = componentCon["port"].toArray(); + for (QJsonValueRef portJson : portsArray) //每个属性的状态信息 + { + QJsonObject node = portJson.toObject(); + QString portId = node["portId"].toString(); + int x = node["x"].toInt(); + int y = node["y"].toInt(); + PortPos locate = PortPos(node["locate"].toInt()); + HandleType portType = HandleType(node["portType"].toInt()); + + TerminalType terType; + switch (portType) { + case T_lineIn: + terType = TerminalType::PowerInput; + break; + case T_lineOut: + terType = TerminalType::PowerOutput; + break; + case T_lineInOut: + terType = TerminalType::PowerConnect; + break; + case T_newTral: + terType = TerminalType::NewTral; + break; + default: + break; + } + auto pTer = TopologyManager::instance().createTerminal(pParent->id(),terType,"",QPointF(x,y),portId,funType); + if(pTer) + pTer->setPortLocate(locate); + } +} + +bool FixedPortsModel::isItemValid(GraphicsFunctionModelItem* pItem) +{ + ModelProperty* pData = pItem->getProperty(); + PowerEntity* pEntity = pItem->entity(); + return (pData != nullptr && pEntity != nullptr)?true:false; +} + + +void FixedPortsModel::insertProjectModelName(QString uuid,QString name) +{ + if(!_projectModelName.contains(uuid)) + _projectModelName.insert(uuid,name); +} + +void FixedPortsModel::showProjectIconSettingDlg(GraphicsFunctionModelItem* pItem) +{ + if(m_projectIconSettingDlg == nullptr) + { + m_projectIconSettingDlg = new ProjectIconSetting(_widget); + m_projectIconSettingDlg->setController(this); + } + m_projectIconSettingDlg->showDlg(pItem); +} + +void FixedPortsModel::updateItemIcon(QString sMeta,QString sModel,QMap mapData,QString sIndex,int type,int slot) +{ + /*if(sIndex.isEmpty()) //此处不再修改系统图图标 + ProjectModelManager::instance().getData()[sMeta][sModel].modelSetting.mapUsedSvg = mapData; + else{ + ProjectModelManager::instance().getData()[sMeta][sModel].modelSetting.mapUsedSvg[sIndex] = mapData[sIndex]; + }*/ + + 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 = type; + imageInfo.imageName = mapData.firstKey(); + imageInfo.hash256 = sha256Hash; + imageInfo.svgData = mapData.first(); + mapResource.insert(sha256Hash,imageInfo); + } + updateHMIRef(QUuid(_widget->getEntity()->id()),sModel,sha256Hash,slot); //更新本页的资源引用 + } + updateModelIcon(sMeta,sModel,mapData,sIndex); +} + +void FixedPortsModel::updateModelIcon(QString sMeta,QString sModel,QMap mapData,QString sIndex) +{ + for(auto &pItem:_nodeItem){ + auto pro = pItem->getProperty(); + QString sMe = pro->metaModelName(); + QString sMo = pro->modelName(); + if(/*sMeta == sMe &&*/sModel == sMo){ + auto pI = dynamic_cast(pItem); + auto pG = dynamic_cast(pItem); + if(pI){ + pI->updateMapSvg(mapData,sIndex); + } + if(pG){ + pG->updateMapSvg(mapData,sIndex); + } + } + } +} + +void FixedPortsModel::addBayItem(QUuid id,ModelFunctionType tpe) +{ + if(tpe == ModelFunctionType::BaseModel){ + QMap mapData = BasePropertyManager::instance().getBaseBayData(); //加载的图形必定关联component(todo:完善判断条件,如判断拓扑节点) + if(mapData.contains(id)) + { + BayProperty* pro = mapData.value(id); + if(pro) + { + addBayByData(pro,tpe); + } + } + } + else if(tpe == ModelFunctionType::ProjectModel) + { + QMap mapData = BasePropertyManager::instance().getBayData(); //加载的图形必定关联component(todo:完善判断条件,如判断拓扑节点) + if(mapData.contains(id)) + { + BayProperty* pro = mapData.value(id); + if(pro) + { + addBayByData(pro); + } + } + } + +} + +bool FixedPortsModel::addBayItem(QUuid id,ElectricBayItem* pBay,ModelFunctionType typ) +{ + if(typ == ModelFunctionType::ProjectModel){ + if(_bayItem.contains(id)) + return false; + else + { + _bayItem.insert(id,pBay); + return true; + } + } + return false; +} + +void FixedPortsModel::addItemsToBay(QList lstItem,ElectricBayItem* pBay) +{ + if(pBay == nullptr) + return; + BayProperty* proBay = dynamic_cast(pBay->getProperty()); + if(proBay){ + for(auto& item:lstItem){ + if(item){ + ModelProperty* p = item->getProperty(); + auto lstCom = proBay->getLstComponent(); + if(!lstCom.contains(p->uuid())){ + proBay->getLstComponent().append(p->uuid()); + } + } + } + } +} + +BayProperty* FixedPortsModel::addBayData(QUuid uuid,ModelFunctionType typ) +{ + if(typ == ModelFunctionType::BaseModel){ + BayProperty* pData = BasePropertyManager::instance().findBaseBayData(uuid); //已存在不不创建 + if(pData != nullptr) + return pData; + + BayProperty* item = new BayProperty(); + + if(item) + { + item->setUuid(uuid); + BasePropertyManager::instance().insertBaseBayData(uuid,item); + } + return item; + } + else if(typ == ModelFunctionType::ProjectModel){ + BayProperty* pData = BasePropertyManager::instance().findBayData(uuid); //已存在不不创建 + if(pData != nullptr) + return pData; + + BayProperty* item = new BayProperty(); + + if(item) + { + item->setUuid(uuid); + BasePropertyManager::instance().insertBayData(uuid,item); + } + return item; + } + return nullptr; +} + +QMap& FixedPortsModel::allBayItem() +{ + return _bayItem; +} + +BayProperty* FixedPortsModel::generateBayData(BayProperty* pData,QList lst) +{ + BayProperty* p = addBayData(pData->uuid()); + p->setTag(pData->tag()); + p->setName(pData->name()); + p->setType(pData->getType()); + p->setVoltage(pData->getVoltage()); + p->setFla(pData->getFla()); + p->setCapacity(pData->getCapacity()); + p->setInService(pData->getInService()); + QList lstCompo = pData->getLstComponent(); + QList lstNewCompo = getCorrespondId(lstCompo,lst); //将基模component替换为工程模component + p->setLstComponent(lstNewCompo); + + QList lstFrom = pData->getLstFrom(); + QList lstNewFrom = getCorrespondId(lstFrom,lst); + p->setLstFrom(lstNewFrom); + + QList lstTo = pData->getLstTo(); + QList lstNewTo = getCorrespondId(lstTo,lst); + p->setLstTo(lstNewTo); + + QList lstProtect = pData->getLstProtect(); + QList lstNewProtect = getCorrespondId(lstProtect,lst); + p->setLstProtect(lstNewProtect); + + QList lstFaultRecord = pData->getLstFaultRecord(); + QList lstNewFaultRecord = getCorrespondId(lstFaultRecord,lst); + p->setLstFaultRecord(lstNewFaultRecord); + + QList lstDynSense = pData->getLstDynSense(); + QList lstNewDynSense = getCorrespondId(lstDynSense,lst); + p->setLstDynSense(lstNewDynSense); + + QList lstStatus = pData->getLstStatus(); + QList lstNewStatus = getCorrespondId(lstStatus,lst); + p->setLstStatus(lstNewStatus); + + QList lstInstruct = pData->getLstInstruct(); + QList lstNewInstruct = getCorrespondId(lstInstruct,lst); + p->setLstInstruct(lstNewInstruct); + + QList lstEtc = pData->getLstEtc(); + QList lstNewEtc= getCorrespondId(lstEtc,lst); + p->setLstEtc(lstNewEtc); + return p; +} + +QList FixedPortsModel::getCorrespondId(QList lstCompo,QList lst) +{ + QList lstNewCompo; + for(auto& uuid:lstCompo) + { + for(auto& basePro:lst) + { + if(basePro->getSourceItemId() == uuid.toString()){ //工程模sourceid等于基模存储的componentid, + lstNewCompo.append(basePro->uuid()); + break; + } + } + } + return lstNewCompo; +} + +QRectF FixedPortsModel::calculateItemsBoundingRect(QList items) +{ + if (items.isEmpty()) + return QRectF(); + + // 初始化矩形为第一个item的场景边界矩形 + QRectF boundingRect = items.first()->sceneBoundingRect(); + + // 遍历剩余item,扩展矩形以包含所有item + for (int i = 1; i < items.size(); ++i) { + QGraphicsItem* item = items.at(i); + // 确保item在场景中且有效 + if (item && item->scene()) { + boundingRect = boundingRect.united(item->sceneBoundingRect()); + } + } + return boundingRect.adjusted(-10,-10,10,10); +} + +void FixedPortsModel::addBayByData(BayProperty* pData,ModelFunctionType typ) +{ + if(typ == ModelFunctionType::ProjectModel){ + QList items; + QList lstCompo = pData->getLstComponent(); + for(auto& id:lstCompo){ + if(_nodeItem.contains(id)){ + items.append(_nodeItem.value(id)); + } + } + + QRectF rec = calculateItemsBoundingRect(items); + auto pBay = new ElectricBayItem(rec); + pBay->setItemType(GIT_bay); + pBay->setProperty(pData); + pBay->setText(pData->name()); + addBayItem(pData->uuid(),pBay); + getScene()->addItem(pBay); + } +} + +void FixedPortsModel::showBayMeasureDlg(BayProperty* pPro) +{ + if(m_bayMeasureDlg == nullptr) + { + m_bayMeasureDlg = new BayMeasureDlg(_widget); + } + m_bayMeasureDlg->showDlg(pPro); +} + +QJsonObject FixedPortsModel::turnListToJson(QList lst,QString sInerTag,QString sOutTag) +{ + QJsonObject o; + QJsonArray arr; + if(lst.isEmpty()) + return QJsonObject(); + for(auto id:lst) + { + QJsonObject obj; + obj[sInerTag] = id.toString(); + arr.push_back(obj); + } + o[sOutTag] = arr; + return o; +} + +QList FixedPortsModel::turnJsonArrToList(QJsonObject object,QString sInner,QString sOut) +{ + QJsonArray jsonArray = object[sOut].toArray(); + + QList lst; + for (QJsonValueRef nodeJson : jsonArray) + { + QJsonObject node = nodeJson.toObject(); + QUuid uid = QUuid(node[sInner].toString()); + lst.append(uid); + } + return lst; +} + +void FixedPortsModel::generateMonitorConfig(MonitorPanel* pPanel) +{ + auto itemData = DataManager::instance().modelData(); + auto itemState = DataManager::instance().modelState(); + for(auto& pItem:_nodeItem){ + auto pBasePro = pItem->getProperty(); + auto pPro = dynamic_cast(pBasePro); + if(pPro){ + QString sModel = pPro->modelName(); + QUuid uid = pPro->uuid(); + QList lstInfo; + if(itemState.contains(sModel)){ //动态表字段数据使用modelState初始化(兼容无值情况) + auto mapData = itemState.value(sModel).groupInfo; + for(auto iter = mapData.begin();iter != mapData.end();++iter){ + if(iter.value().isPublic == true) //公共属性组暂不显示 + continue; + for(auto& attr: iter.value().info){ + MonitorItemAttributeInfo info; + info.sGroup = iter.key(); + info.sTag = attr.tagName; + info.sName = attr.name; + info.nConnectType = 0; + lstInfo.append(info); + } + } + } + + if(itemData.contains(sModel)){ //量测数据使用modelData初始化 + auto mapData = itemData.value(sModel).groupInfo; + for(auto iter = mapData.begin();iter != mapData.end();iter++){ //遍历所有属性组 + if(iter.key() == "bay"){ //量测数据放到间隔组 + auto mapMeasure = pPro->getMeasurement(); + for(auto it = mapMeasure.begin(); it != mapMeasure.end();++it){ + MonitorItemAttributeInfo info; + info.sGroup = iter.key(); + info.sTag = it->name; + info.sName = it->tag; + info.nConnectType = 1; + lstInfo.append(info); + } + } + } + } + if(!lstInfo.isEmpty()){ + if(!pPanel->getModelController()->getMonitorPara().contains(uid)){ + pPanel->getModelController()->getMonitorPara().insert(uid,lstInfo); + } + } + } + } +} + +void FixedPortsModel::monitorItemSelected(QUuid uid) +{ + auto pMonitor = dynamic_cast(_widget); + if(pMonitor){ + pMonitor->itemSelected(uid); + } +} + +void FixedPortsModel::monitorItemDetailAttr(QUuid uid) +{ + auto pMonitor = dynamic_cast(_widget); + if(pMonitor){ + pMonitor->detailItemSelected(uid); + } +} + +void FixedPortsModel::monitorItemSet(QUuid uid) +{ + if(_nodeItem.contains(uid)){ + QList lstAdd; //需要向item添加的动态数据 + + auto pItem = _nodeItem.value(uid); + auto lst = m_monitorPara.value(uid); + + for(auto& info:lst){ + if(info.bShowDiagram && info.bSelected){ + lstAdd.append(info.sTag); + } + } + + pItem->removeAllDynamicText(); + + for(auto& str:lstAdd){ + for(auto& info:lst){ + if(info.sTag == str){ + pItem->addDynamicText(info.sTag,info.sConnectPara); + continue; + } + } + } + } +} + +void FixedPortsModel::updateMonitorDisplay() +{ + for(auto& pItem:_nodeItem){ + auto pPro = pItem->getProperty(); + if(pPro){ + QString sMeta = pPro->metaModelName(); + for(auto iter = m_monitorDisplaySetting.begin();iter != m_monitorDisplaySetting.end();++iter){ //将设置的显示数据更新到item(显示数据不放入item的property) + if(iter.key().sTag == sMeta){ + pItem->setMonitorDisplayInfo(iter.value()); + pItem->updateCurState(MonitorItemState::Normal); + break; + } + } + } + } +} + +void FixedPortsModel::startAcceptData() +{ + QStringList lstTarget; //待订阅的对象 + for(auto& lst:m_monitorPara){ + for(auto& para:lst){ + if(para.bSelected && !para.sConnectPara.isEmpty()){ + if(!lstTarget.contains(para.sConnectPara)) + lstTarget.append(para.sConnectPara); + } + } + } + + QJsonObject obj; //构建开始请求 + obj["action"] = "start"; + QJsonArray arrMeasure; + + QJsonObject objMeasure; + objMeasure["interval"] = "2s"; + QJsonArray arrTargets; + for(auto& strTarget:lstTarget){ + arrTargets.append(strTarget); + } + objMeasure["targets"] = arrTargets; + + arrMeasure.append(objMeasure); + obj["measurements"] = arrMeasure; + + QString sPath = "/monitors/data/subscriptions"; + + QJsonDocument doc(obj); + QVariant variant = doc.toVariant(); + UiCommunicationBus::instance()->sendHttpRequest(sPath,variant,"POST"); + + QList> requestLst; //等待请求的队列 + for(auto& str:lstTarget){ + requestLst.append(qMakePair(str,"request")); + } + + UiCommunicationBus::instance()->insertTempRequest(_pageName,requestLst); + _curRequestLst = lstTarget; + m_dataTimer->start(); +} + +void FixedPortsModel::stopAcceptData(QString page) +{ + if(UiCommunicationBus::instance()->getSesstionMap().contains(page)){ + auto& curSession = UiCommunicationBus::instance()->getSesstionMap()[page]; + + QJsonObject obj; //构建开始请求 + obj["action"] = "stop"; + obj["client_id"] = curSession.first; + QJsonArray arrMeasure; + + QJsonObject objMeasure; + objMeasure["interval"] = "2s"; + QJsonArray arrTargets; + for(auto& pairTarget:curSession.second){ + arrTargets.append(pairTarget.first); + pairTarget.second = "closing"; + } + objMeasure["targets"] = arrTargets; + + arrMeasure.append(objMeasure); + obj["measurements"] = arrMeasure; + + QString sPath = "/monitors/data/subscriptions"; + + QJsonDocument doc(obj); + QVariant variant = doc.toVariant(); + UiCommunicationBus::instance()->sendHttpRequest(sPath,variant,"POST"); + } + m_dataTimer->stop(); +} + +int FixedPortsModel::imageRefExist(QString model,QByteArray hash256) +{ + for(auto& info:_HMIimageRef) + { + if(model == info.model && hash256 == info.hash256) + return _HMIimageRef.indexOf(info); + } + return -1; +} + +void FixedPortsModel::updateHMIRef(QUuid hmiId,QString model,QByteArray hash256,int slot) +{ + HMIImageRef ref; + ref.hmiId = hmiId; + ref.model = model; + ref.hash256 = hash256; + ref.slot = slot; + int index = imageRefExist(model,hash256); + if(index != -1){ //已存在,替换 + _HMIimageRef[index] = ref; + } + else{ //新建 + _HMIimageRef.append(ref); + } +} diff --git a/diagramCavas/source/graphicsItem/electricBayItem.cpp b/diagramCavas/source/graphicsItem/electricBayItem.cpp new file mode 100644 index 0000000..b2ab964 --- /dev/null +++ b/diagramCavas/source/graphicsItem/electricBayItem.cpp @@ -0,0 +1,57 @@ +#include "graphicsItem/electricBayItem.h" +#include +#include +#include + +ElectricBayItem::ElectricBayItem(const QRectF &rect,QGraphicsItem *parent) + : GraphicsNonStandardItem(parent) +{ + m_showRect = rect; + //m_boundingRect = rect; + m_dWidth = rect.width(); + m_dHeight = rect.height(); + m_font.setPointSize(12); + setFlag(QGraphicsItem::ItemIgnoresTransformations, false); +} + +ElectricBayItem::~ElectricBayItem() +{ + +} + +void ElectricBayItem::setText(const QString& text) +{ + prepareGeometryChange(); // 通知框架几何变化 + m_text = text; + updateTextShape(); +} + +QPainterPath ElectricBayItem::shape() +{ + QPainterPath path; + path.addRect(_recLabel); + return path; +} + +void ElectricBayItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->setPen(m_pen); + painter->drawText(_recLabel,m_text); + m_pen.setStyle(Qt::DashLine); + if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 + { + painter->drawRect(m_showRect); + } + +} + +void ElectricBayItem::updateTextShape() +{ + QFontMetricsF metrics(m_font); + QRectF recText = metrics.boundingRect(m_text); + //_recLabel = recText.translated(g_offsetX,g_offsetY); + recText.moveTo(m_showRect.topLeft()-QPointF(recText.width(),recText.height())); + recText.adjust(-3,-3,3,3); + _recLabel = recText; + shape(); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.cpp new file mode 100644 index 0000000..eaaa605 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.cpp @@ -0,0 +1,974 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h" +#include +#include +#include + +ElectricFunctionModelConnectLineItem::ElectricFunctionModelConnectLineItem(QGraphicsItem *parent) + : GraphicsFunctionModelItem(parent) +{ + initial(); +} + +ElectricFunctionModelConnectLineItem::~ElectricFunctionModelConnectLineItem() +{ +} + +void ElectricFunctionModelConnectLineItem::initial() +{ + m_boundingRect = QRectF(); + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::NoBrush); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + m_lstPoints.push_back(QPointF()); //起点 + m_lstPoints.push_back(QPointF()); //终点 + _curLine = QPoint(); +} + + +void ElectricFunctionModelConnectLineItem::setStartPoint(const QPointF& p) +{ + if (m_lstPoints.size() >= 2) { + // 保存终点 + QPointF endPoint = m_lstPoints.last(); + + // 清空并设置新起点 + m_lstPoints.clear(); + m_lstPoints.append(p); + m_lstPoints.append(endPoint); + + // 重新计算路径 + calculatePath(); + } else if (m_lstPoints.size() == 1) { + m_lstPoints[0] = p; + } else { + m_lstPoints.append(p); + } + update(); +} +void ElectricFunctionModelConnectLineItem::setEndPoint(const QPointF& p) +{ + if (m_lstPoints.size() >= 2) { + // 保存起点 + QPointF startPoint = m_lstPoints.first(); + + // 清空并设置新终点 + m_lstPoints.clear(); + m_lstPoints.append(startPoint); + m_lstPoints.append(p); + + // 重新计算路径 + calculatePath(); + } else if (m_lstPoints.size() == 1) { + m_lstPoints.append(p); + } else { + m_lstPoints.append(p); + } + update(); +} + +// 开始拖拽 +void ElectricFunctionModelConnectLineItem::startDrag(const QPointF& scenePos) +{ + qDebug() << "\n=== START DRAG ==="; + + // 重置状态 + m_dragData.isActive = false; + m_dragData.segmentIndex = -1; + + QPointF itemPos = mapFromScene(scenePos); + qDebug() << "Item pos:" << itemPos; + + // 从路径提取点 + QList points = extractPointsFromPath(); + qDebug() << "Extracted" << points.size() << "points from path"; + + // 查找线段 + int segmentIndex = findSegmentAt(points, itemPos); + + if (segmentIndex >= 0) { + m_dragData.isActive = true; + m_dragData.segmentIndex = segmentIndex; + m_dragData.startScenePos = scenePos; + m_dragData.originalPath = m_points; // 保存原始路径 + + // 判断线段方向 + if (segmentIndex < points.size() - 1) { + QPointF p1 = points[segmentIndex]; + QPointF p2 = points[segmentIndex + 1]; + m_dragData.isVertical = qFuzzyCompare(p1.x(), p2.x()); + } + + qDebug() << "✓ Dragging segment" << segmentIndex; + } +} + +// 更新拖拽 +void ElectricFunctionModelConnectLineItem::updateDrag(const QPointF& scenePos) +{ + if (!m_dragData.isActive) { + return; + } + + int idx = m_dragData.segmentIndex; + + // 从原始路径提取点 + QList points = extractPointsFromPath(m_dragData.originalPath); + + // 安全检查 + if (idx < 0 || idx >= points.size() - 1) { + qWarning() << "Invalid segment index:" << idx; + m_dragData.isActive = false; + return; + } + + // 计算移动距离 + QPointF startItemPos = mapFromScene(m_dragData.startScenePos); + QPointF currentItemPos = mapFromScene(scenePos); + QPointF delta = currentItemPos - startItemPos; + + // 获取当前线段 + QPointF& p1 = points[idx]; + QPointF& p2 = points[idx + 1]; + + // 限制移动方向 + if (m_dragData.isVertical) { + // 垂直线段:只能水平移动 + delta.setY(0); + + // 移动线段 + p1.rx() += delta.x(); + p2.rx() += delta.x(); + } else { + // 水平线段:只能垂直移动 + delta.setX(0); + + p1.ry() += delta.y(); + p2.ry() += delta.y(); + } + + // 修复连接 + fixConnections(points, idx, m_dragData.isVertical); + + // 更新拖拽起点 + m_dragData.startScenePos = scenePos; + + // 应用修改后的点 + applyPointsToPath(points); + + // 重新计算路径(调用原有的 calculatePath) + calculatePath(); + update(); +} +// 结束拖拽 +void ElectricFunctionModelConnectLineItem::endDrag() +{ + qDebug() << "\n" << QString(50, '='); + qDebug() << "END DRAG"; + qDebug() << QString(50, '='); + + if (!m_dragData.isActive) { + qDebug() << "No active drag to end"; + return; + } + + qDebug() << "Drag was active on segment:" << m_dragData.segmentIndex; + + // 验证和修复路径 + validateAndFixPath(); + + // 重置状态 + m_dragData.isActive = false; + m_dragData.segmentIndex = -1; + m_dragData.isVertical = false; + m_dragData.startScenePos = QPointF(); + m_dragData.originalPath = QPainterPath(); + + // 恢复光标 + setCursor(Qt::ArrowCursor); + + // 确保场景更新 + update(); + + qDebug() << "✓ Drag ended successfully"; + qDebug() << QString(50, '=') << "\n"; +} + +void ElectricFunctionModelConnectLineItem::setLastPoint(const QPointF& point) +{ + m_lastPoint = point; +} + +void ElectricFunctionModelConnectLineItem::setPath(const QPainterPath& path) +{ + qDebug() << "\n=== setPath ==="; + + if (m_points == path) { + return; + } + + prepareGeometryChange(); + m_points = path; // 使用已有的 m_points + updateBoundingRect(); + update(); +} + +QPainterPath ElectricFunctionModelConnectLineItem::shape() const +{ + // 使用路径的轮廓 + if (m_points.isEmpty()) { + return QPainterPath(); + } + + QPainterPathStroker stroker; + stroker.setWidth(8.0); // 选择区域宽度 + + QPainterPath shape = stroker.createStroke(m_points); + return shape; +} + +QRectF ElectricFunctionModelConnectLineItem::boundingRect() const +{ + QRectF rect = shape().boundingRect(); + + // 如果shape为空,使用路径边界 + if (rect.isNull() && !m_points.isEmpty()) { + rect = m_points.boundingRect().adjusted(-5, -5, 5, 5); + } + + return rect; +} + +void ElectricFunctionModelConnectLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if (option->state & QStyle::State_Selected) + { + painter->setPen(Qt::red); + } + else + { + if(_curMonitorStateEnable){ //当前状态被设置 + painter->setPen(QPen(_curMonitorStateColor)); + } + else + painter->setPen(m_pen); + } + + painter->setBrush(m_brush); + + painter->drawPath(m_points); +} + +void ElectricFunctionModelConnectLineItem::calculatePath() +{ + if (m_lstPoints.size() < 2) return; + + QPointF start = m_lstPoints.first(); + QPointF end = m_lstPoints.last(); + + qDebug() << "\n=== calculatePath ==="; + qDebug() << "Start:" << start << "End:" << end; + + // 创建新路径 + m_points = QPainterPath(); + m_points.moveTo(start); + qDebug() << "Initialized path, current position:" << m_points.currentPosition(); + + // 获取元件 + QList components = getAllComponentRects(); + qDebug() << "Found" << components.size() << "components"; + + // 检查起点终点距离 + qDebug() << "Distance:" << QLineF(start, end).length(); + + // 如果距离很近,直接连接 + if (QLineF(start, end).length() < 10) { + qDebug() << "Points are very close, direct connection"; + m_points.lineTo(end); + } else { + // 生成路径 + generateAvoidancePath(start, end, components); + } + + // 验证路径 + qDebug() << "After generation - Path element count:" << m_points.elementCount(); + qDebug() << "After generation - Current position:" << m_points.currentPosition(); + + if (m_points.elementCount() <= 1) { + qWarning() << "Path has only" << m_points.elementCount() << "elements! Adding direct line"; + m_points.lineTo(end); + } + + // 确保路径结束于终点 + if (m_points.currentPosition() != end) { + qWarning() << "Path does not end at destination! Adding final segment"; + qDebug() << "Expected end:" << end << "Actual end:" << m_points.currentPosition(); + m_points.lineTo(end); + } + + update(); + m_boundingRect = m_points.boundingRect(); + prepareGeometryChange(); + + qDebug() << "Final path bounds:" << m_boundingRect; + qDebug() << "=== calculatePath end ===\n"; +} + +// 使用形状进行避障 +void ElectricFunctionModelConnectLineItem::generateAvoidancePath(const QPointF& start, const QPointF& end, + const QList& components) +{ + qDebug() << "=== generateAvoidancePath (rectilinear) ==="; + + m_points = QPainterPath(); + m_points.moveTo(start); + + // 生成候选路径列表,按长度排序 + QMultiMap> candidatePaths; + + // 1. 收集所有可能的直角路径 + collectRectilinearPaths(start, end, components, candidatePaths); + + // 2. 选择最短的安全路径 + for (auto it = candidatePaths.begin(); it != candidatePaths.end(); ++it) { + const QList& path = it.value(); + + if (isPathSafe(path, components)) { + qDebug() << " Selected path with length" << it.key() << ":" << path; + + // 绘制路径 + for (int i = 1; i < path.size(); i++) { + m_points.lineTo(path[i]); + } + return; + } + } + + // 3. 所有路径都失败,使用强制绕行 + qDebug() << " All rectilinear paths failed, using forced bypass"; + generateForcedRectilinearBypass(start, end, components); +} + +// 计算路径长度 +double ElectricFunctionModelConnectLineItem::calculatePathLength(const QList& path) +{ + double length = 0; + for (int i = 0; i < path.size() - 1; i++) { + length += QLineF(path[i], path[i+1]).length(); + } + return length; +} + + +// 单一线段与矩形相交检测 +bool ElectricFunctionModelConnectLineItem::lineIntersectsRect(const QLineF& line, const QRectF& rect) const +{ + // 检查端点是否在矩形内 + if (rect.contains(line.p1()) || rect.contains(line.p2())) { + return true; + } + + // 检查是否与矩形的边相交 + QLineF edges[4] = { + QLineF(rect.topLeft(), rect.topRight()), + QLineF(rect.topRight(), rect.bottomRight()), + QLineF(rect.bottomRight(), rect.bottomLeft()), + QLineF(rect.bottomLeft(), rect.topLeft()) + }; + + QPointF intersection; + for (int i = 0; i < 4; i++) { + if (line.intersects(edges[i], &intersection) == QLineF::BoundedIntersection) { + return true; + } + } + + return false; +} + +// 辅助方法实现 +bool ElectricFunctionModelConnectLineItem::isSegmentSafe(const QPointF& p1, const QPointF& p2, + const QList& components) +{ + QLineF segment(p1, p2); + + for (const QRectF& component : components) { + if (segmentPenetratesComponent(segment, component)) { + return false; + } + } + + return true; +} + +bool ElectricFunctionModelConnectLineItem::isPathSafe(const QList& path, + const QList& components) +{ + for (int i = 0; i < path.size() - 1; i++) { + if (!isSegmentSafe(path[i], path[i+1], components)) { + return false; + } + } + return true; +} + +// 检查线段是否穿透元件 +bool ElectricFunctionModelConnectLineItem::segmentPenetratesComponent(const QLineF& segment, + const QRectF& component) +{ + // 检查线段端点是否在元件内 + bool p1Inside = component.contains(segment.p1()); + bool p2Inside = component.contains(segment.p2()); + + if (p1Inside && p2Inside) { + // 线段完全在元件内 + return true; + } + + // 获取与元件的交点 + int intersectionCount = 0; + QLineF edges[4] = { + QLineF(component.topLeft(), component.topRight()), + QLineF(component.topRight(), component.bottomRight()), + QLineF(component.bottomRight(), component.bottomLeft()), + QLineF(component.bottomLeft(), component.topLeft()) + }; + + QPointF intersection; + for (int i = 0; i < 4; i++) { + if (segment.intersects(edges[i], &intersection) == QLineF::BoundedIntersection) { + intersectionCount++; + } + } + + // 如果线段与元件有2个或更多交点,说明穿透了元件 + return intersectionCount >= 2; +} + +// 获取所有元件的总边界 +QRectF ElectricFunctionModelConnectLineItem::getTotalComponentsBounds(const QList& components) +{ + if (components.isEmpty()) { + return QRectF(); + } + + QRectF total = components.first(); + for (int i = 1; i < components.size(); i++) { + total = total.united(components[i]); + } + + return total; +} + +// 收集所有可能的直角路径 +void ElectricFunctionModelConnectLineItem::collectRectilinearPaths(const QPointF& start, const QPointF& end, + const QList& components, + QMultiMap>& paths) +{ + // 2段路径 + QPointF turn1(start.x(), end.y()); // 水平-垂直 + QPointF turn2(end.x(), start.y()); // 垂直-水平 + + addPathIfRectilinear({start, turn1, end}, paths); + addPathIfRectilinear({start, turn2, end}, paths); + + // 3段路径 + double midX = (start.x() + end.x()) / 2; + double midY = (start.y() + end.y()) / 2; + + // 水平-垂直-水平 + addPathIfRectilinear({start, + QPointF(start.x(), midY), + QPointF(end.x(), midY), + end}, paths); + + // 垂直-水平-垂直 + addPathIfRectilinear({start, + QPointF(midX, start.y()), + QPointF(midX, end.y()), + end}, paths); + + // 4段路径(绕行) + collectBypassPaths(start, end, components, paths); +} + +// 添加路径(如果是直角) +void ElectricFunctionModelConnectLineItem::addPathIfRectilinear(const QList& path, + QMultiMap>& paths) +{ + // 检查是否所有线段都是水平或垂直 + for (int i = 0; i < path.size() - 1; i++) { + if (path[i].x() != path[i+1].x() && path[i].y() != path[i+1].y()) { + return; // 不是直角 + } + } + + double length = calculatePathLength(path); + paths.insert(length, path); +} + +// 收集绕行路径 +void ElectricFunctionModelConnectLineItem::collectBypassPaths(const QPointF& start, const QPointF& end, + const QList& components, + QMultiMap>& paths) +{ + if (components.isEmpty()) return; + + // 获取所有元件的总边界 + QRectF totalBounds = getTotalComponentsBounds(components); + if (!totalBounds.isValid()) return; + + const double clearance = 20.0; + QRectF expanded = totalBounds.adjusted(-clearance, -clearance, clearance, clearance); + + // 从上方绕行 + addPathIfRectilinear({start, + QPointF(start.x(), expanded.top()), + QPointF(end.x(), expanded.top()), + end}, paths); + + // 从下方绕行 + addPathIfRectilinear({start, + QPointF(start.x(), expanded.bottom()), + QPointF(end.x(), expanded.bottom()), + end}, paths); + + // 从左方绕行 + addPathIfRectilinear({start, + QPointF(expanded.left(), start.y()), + QPointF(expanded.left(), end.y()), + end}, paths); + + // 从右方绕行 + addPathIfRectilinear({start, + QPointF(expanded.right(), start.y()), + QPointF(expanded.right(), end.y()), + end}, paths); +} + +// 强制直角绕行 +void ElectricFunctionModelConnectLineItem::generateForcedRectilinearBypass(const QPointF& start, const QPointF& end, + const QList& components) +{ + qDebug() << " In generateForcedRectilinearBypass"; + + // 找到阻挡的元件 + QRectF blocking = findFirstBlockingComponent(start, end, components); + + if (blocking.isValid()) { + qDebug() << " Blocking component:" << blocking; + + // 从上方或下方绕过 + const double clearance = 15.0; + double bypassY = (start.y() < blocking.center().y()) ? + blocking.top() - clearance : blocking.bottom() + clearance; + + QList path = {start, + QPointF(start.x(), bypassY), + QPointF(end.x(), bypassY), + end}; + + qDebug() << " Forced bypass path:" << path; + + for (int i = 1; i < path.size(); i++) { + m_points.lineTo(path[i]); + } + } else { + // 没有阻挡,使用简单的直角路径 + qDebug() << " No blocking component, using simple orthogonal path"; + QPointF turnPoint = (qAbs(start.x() - end.x()) < qAbs(start.y() - end.y())) ? + QPointF(end.x(), start.y()) : QPointF(start.x(), end.y()); + + m_points.lineTo(turnPoint); + m_points.lineTo(end); + } +} + +// 找到第一个阻挡的元件 +QRectF ElectricFunctionModelConnectLineItem::findFirstBlockingComponent(const QPointF& start, const QPointF& end, + const QList& components) +{ + QLineF line(start, end); + + for (const QRectF& rect : components) { + if (lineIntersectsRect(line, rect)) { + return rect; + } + } + + return QRectF(); +} + +double ElectricFunctionModelConnectLineItem::distancePointToLine(const QLineF& line, const QPointF& point) const +{ + // 向量法计算点到线段的最短距离 + + if (line.isNull()) { + return QLineF(point, line.p1()).length(); + } + + QPointF v = line.p2() - line.p1(); // 线段向量 + QPointF w = point - line.p1(); // 点到线段起点的向量 + + // 计算点积 + double c1 = w.x() * v.x() + w.y() * v.y(); // w·v + if (c1 <= 0.0) { + // 点在线段起点后方 + return QLineF(point, line.p1()).length(); + } + + double c2 = v.x() * v.x() + v.y() * v.y(); // v·v + if (c2 <= c1) { + // 点在线段终点前方 + return QLineF(point, line.p2()).length(); + } + + // 计算投影点 + double b = c1 / c2; + QPointF projection = line.p1() + b * v; + return QLineF(point, projection).length(); +} + +void ElectricFunctionModelConnectLineItem::ensureEnoughPointsForDrag() +{ + qDebug() << "\n=== ensureEnoughPointsForDrag ==="; + qDebug() << "Current points:" << m_lstPoints.size(); + + if (m_lstPoints.size() < 2) { + qWarning() << "Not enough points!"; + return; + } + + // 如果是只有2个点的直线,需要添加转折点 + if (m_lstPoints.size() == 2) { + QPointF start = m_lstPoints[0]; + QPointF end = m_lstPoints[1]; + + // 如果是斜线,添加转折点 + if (start.x() != end.x() && start.y() != end.y()) { + QPointF turnPoint; + + // 选择转折方式 + if (qAbs(end.x() - start.x()) < qAbs(end.y() - start.y())) { + // 接近垂直,先水平 + turnPoint = QPointF(end.x(), start.y()); + } else { + // 接近水平,先垂直 + turnPoint = QPointF(start.x(), end.y()); + } + + m_lstPoints.insert(1, turnPoint); + qDebug() << "Added turn point:" << turnPoint; + qDebug() << "Now have" << m_lstPoints.size() << "points"; + + // 重新计算路径 + calculatePath(); + } + } +} + +void ElectricFunctionModelConnectLineItem::debugPathState(const QString& context) const +{ + qDebug() << "\n=== Debug Path State:" << context << "==="; + qDebug() << "m_lstPoints count:" << m_lstPoints.size(); + qDebug() << "m_dragState.isDragging:" << m_dragData.isActive; + qDebug() << "m_dragState.segmentIndex:" << m_dragData.segmentIndex; + qDebug() << "m_dragState.isVertical:" << m_dragData.isVertical; + + for (int i = 0; i < m_lstPoints.size(); i++) { + qDebug() << QString(" Point[%1]: (%2, %3)") + .arg(i) + .arg(m_lstPoints[i].x(), 0, 'f', 1) + .arg(m_lstPoints[i].y(), 0, 'f', 1); + } + + // 检查每个线段 + for (int i = 0; i < m_lstPoints.size() - 1; i++) { + QPointF p1 = m_lstPoints[i]; + QPointF p2 = m_lstPoints[i + 1]; + + bool isVertical = qFuzzyCompare(p1.x(), p2.x()); + bool isHorizontal = qFuzzyCompare(p1.y(), p2.y()); + + QString type = "DIAGONAL"; + if (isVertical) type = "VERTICAL"; + else if (isHorizontal) type = "HORIZONTAL"; + + qDebug() << QString(" Segment[%1]: %2, length=%3") + .arg(i) + .arg(type) + .arg(QLineF(p1, p2).length(), 0, 'f', 1); + } +} + +QVector ElectricFunctionModelConnectLineItem::extractSegmentsFromPainterPath() const +{ + QVector segments; + + if (m_points.isEmpty()) { + return segments; + } + + // 遍历QPainterPath的所有元素 + QPointF lastPoint; + bool hasLastPoint = false; + + for (int i = 0; i < m_points.elementCount(); i++) { + QPainterPath::Element elem = m_points.elementAt(i); + QPointF currentPoint(elem.x, elem.y); + + switch (elem.type) { + case QPainterPath::MoveToElement: + lastPoint = currentPoint; + hasLastPoint = true; + break; + + case QPainterPath::LineToElement: + if (hasLastPoint) { + segments.append(QLineF(lastPoint, currentPoint)); + } + lastPoint = currentPoint; + hasLastPoint = true; + break; + + case QPainterPath::CurveToElement: + // 处理曲线(如果需要) + break; + + case QPainterPath::CurveToDataElement: + // 曲线数据 + break; + } + } + + return segments; +} + +int ElectricFunctionModelConnectLineItem::findSegmentAt(const QList& points, + const QPointF& itemPos) const +{ + if (points.size() < 2) { + return -1; + } + + const double HIT_TOLERANCE = 10.0; + int bestSegment = -1; + double bestDistance = HIT_TOLERANCE; + + for (int i = 0; i < points.size() - 1; i++) { + QLineF segment(points[i], points[i + 1]); + + // 计算距离 + double distance = distanceToSegment(segment, itemPos); + + if (distance < bestDistance) { + bestDistance = distance; + bestSegment = i; + } + } + + return bestSegment; +} + +double ElectricFunctionModelConnectLineItem::distanceToSegment(const QLineF& segment, const QPointF& point) const +{ + if (segment.isNull()) { + return std::numeric_limits::max(); + } + + // 检查是否是正交线段 + bool isHorizontal = qFuzzyCompare(segment.y1(), segment.y2()); + bool isVertical = qFuzzyCompare(segment.x1(), segment.x2()); + + if (isHorizontal) { + // 水平线段 + double minX = qMin(segment.x1(), segment.x2()); + double maxX = qMax(segment.x1(), segment.x2()); + double y = segment.y1(); + + if (point.x() >= minX && point.x() <= maxX) { + return qAbs(point.y() - y); + } else if (point.x() < minX) { + return QLineF(point, QPointF(minX, y)).length(); + } else { + return QLineF(point, QPointF(maxX, y)).length(); + } + } + else if (isVertical) { + // 垂直线段 + double minY = qMin(segment.y1(), segment.y2()); + double maxY = qMax(segment.y1(), segment.y2()); + double x = segment.x1(); + + if (point.y() >= minY && point.y() <= maxY) { + return qAbs(point.x() - x); + } else if (point.y() < minY) { + return QLineF(point, QPointF(x, minY)).length(); + } else { + return QLineF(point, QPointF(x, maxY)).length(); + } + } + else { + // 斜线(理论上不应该出现) + return QLineF(segment.p1(), point).length(); + } +} + +QList ElectricFunctionModelConnectLineItem::extractPointsFromPath() const +{ + QList points; + + if (m_points.isEmpty()) { + return points; + } + + // 从 QPainterPath 提取所有点 + for (int i = 0; i < m_points.elementCount(); i++) { + QPainterPath::Element elem = m_points.elementAt(i); + if (elem.type == QPainterPath::MoveToElement || + elem.type == QPainterPath::LineToElement) { + points.append(QPointF(elem.x, elem.y)); + } + } + + return points; +} + +QList ElectricFunctionModelConnectLineItem::extractPointsFromPath(const QPainterPath& path) const +{ + QList points; + + if (path.isEmpty()) { + return points; + } + + for (int i = 0; i < path.elementCount(); i++) { + QPainterPath::Element elem = path.elementAt(i); + if (elem.type == QPainterPath::MoveToElement || + elem.type == QPainterPath::LineToElement) { + points.append(QPointF(elem.x, elem.y)); + } + } + + return points; +} + +void ElectricFunctionModelConnectLineItem::applyPointsToPath(const QList& points) +{ + if (points.size() < 2) { + m_points = QPainterPath(); + return; + } + + QPainterPath newPath; + newPath.moveTo(points.first()); + + for (int i = 1; i < points.size(); i++) { + newPath.lineTo(points[i]); + } + + m_points = newPath; + setPath(m_points); +} + +void ElectricFunctionModelConnectLineItem::fixConnections(QList& points, + int segmentIndex, bool isVertical) +{ + int n = points.size(); + if (n < 3) { + return; + } + + if (isVertical) { + // 垂直线段移动 + if (segmentIndex > 0) { + points[segmentIndex - 1].setX(points[segmentIndex].x()); + } + if (segmentIndex + 2 < n) { + points[segmentIndex + 2].setX(points[segmentIndex + 1].x()); + } + } else { + // 水平线段移动 + if (segmentIndex > 0) { + points[segmentIndex - 1].setY(points[segmentIndex].y()); + } + if (segmentIndex + 2 < n) { + points[segmentIndex + 2].setY(points[segmentIndex + 1].y()); + } + } +} + +void ElectricFunctionModelConnectLineItem::validateAndFixPath() +{ + qDebug() << "Validating and fixing path..."; + + QList points = extractPointsFromPath(); + + if (points.size() < 2) { + qWarning() << "Path has less than 2 points"; + return; + } + + bool needsFix = false; + + // 检查每个线段 + for (int i = 0; i < points.size() - 1; i++) { + QPointF& p1 = points[i]; + QPointF& p2 = points[i + 1]; + + // 检查是否正交 + if (!qFuzzyCompare(p1.x(), p2.x()) && !qFuzzyCompare(p1.y(), p2.y())) { + qDebug() << "Fixing non-orthogonal segment" << i; + + // 选择主要方向 + double dx = qAbs(p2.x() - p1.x()); + double dy = qAbs(p2.y() - p1.y()); + + if (dx < dy) { + p2.setX(p1.x()); // 改为垂直 + } else { + p2.setY(p1.y()); // 改为水平 + } + + needsFix = true; + } + } + + // 移除连续重复点 + QList cleanedPoints; + cleanedPoints.append(points.first()); + + for (int i = 1; i < points.size(); i++) { + if (!points[i - 1].isNull() && + qFuzzyCompare(points[i - 1].x(), points[i].x()) && + qFuzzyCompare(points[i - 1].y(), points[i].y())) { + continue; // 跳过重复点 + } + cleanedPoints.append(points[i]); + } + + if (needsFix || cleanedPoints.size() != points.size()) { + qDebug() << "Applying fixes to path"; + applyPointsToPath(cleanedPoints); + + // 重新计算路径 + calculatePath(); + } +} + +void ElectricFunctionModelConnectLineItem::updateBoundingRect() +{ + if (m_points.isEmpty()) { + m_boundingRect = QRectF(); + } else { + // 使用路径的边界 + m_boundingRect = m_points.boundingRect(); + + // 添加一些边距 + const qreal MARGIN = 2.0; + m_boundingRect.adjust(-MARGIN, -MARGIN, MARGIN, MARGIN); + } + + qDebug() << "Updated bounds:" << m_boundingRect; +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelPortItem.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelPortItem.cpp new file mode 100644 index 0000000..52aed91 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelPortItem.cpp @@ -0,0 +1,65 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelPortItem.h" +#include "graphicsItem/itemPort.h" +#include "baseProperty.h" +#include +#include +#include + +ElectricFunctionModelPortItem::ElectricFunctionModelPortItem(QGraphicsItem *parent) + : GraphicsFunctionModelItem(parent) +{ + initial(); +} + +ElectricFunctionModelPortItem::~ElectricFunctionModelPortItem() +{ + +} + +QRectF ElectricFunctionModelPortItem::boundingRect() const +{ + return m_boundingRect; +} + +void ElectricFunctionModelPortItem::updateConnectData() +{ + QJsonObject obj; + QJsonArray arr; + if(_property) + { + for(auto& ptr:m_mapPort) + { + //if(ptr->connected()) + { + QJsonObject port; + port["portId"] = ptr->getId(); + //auto pLine = ptr->getConnectPtr(); + port["x"] = ptr->pos().x(); + port["y"] = ptr->pos().y(); + port["portType"] = ptr->getType(); + arr.push_back(port); + } + } + + obj["port"] = arr; + obj["metaModel"] = _property->metaModelName(); + obj["extraInfo"] = _property->getVoltageLevel(); + _property->setContext(obj); + } +} + +void ElectricFunctionModelPortItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->setBrush(m_brush); + painter->drawEllipse(m_boundingRect); +} + +void ElectricFunctionModelPortItem::initial() +{ + m_boundingRect = QRectF(-1,-1,2,2); + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::black); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.cpp new file mode 100644 index 0000000..ec1f7e2 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroup.cpp @@ -0,0 +1,149 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroup.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItem.h" + +#include +#include +#include + +ElectricFunctionModelSvgGroup::ElectricFunctionModelSvgGroup(const QRect &rect,QGraphicsItem *parent) + : GraphicsFunctionModelGroup(parent) +{ + m_lastBoudingRect = rect; + m_boundingRect = rect; + m_dWidth = rect.width(); + m_dHeight = rect.height(); +} + +ElectricFunctionModelSvgGroup::~ElectricFunctionModelSvgGroup() +{ + +} + +QPainterPath ElectricFunctionModelSvgGroup::shape() +{ + QPainterPath path; + path.addRect(m_boundingRect); + return path; +} + +void ElectricFunctionModelSvgGroup::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() +{ + if (!parentItem()) + { + QPointF pt1, pt2, delta; + pt1 = mapToScene(QPointF(0, 0)); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + + prepareGeometryChange(); + //将boundingRect设置成中心点和原点(也是默认变换原点),这样三点重合,有助于简化计算 + m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight); + //setTransformOriginPoint(m_boundingRect.center()); //变换中心默认为item的(0,0)点,所以不执行这句话也没有问题 + //更新bouondingRect后重回会显示位置会有变化,需要做对应的移动 + moveBy(-delta.x(), -delta.y()); + updateHandles(); + } + + m_lastBoudingRect = m_boundingRect; +} + +void ElectricFunctionModelSvgGroup::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->setPen(m_pen); + painter->setBrush(m_brush); + if(m_state == S_prepareConnect) + { + painter->setPen(QPen(QColor(255,51,153,180))); + painter->drawLine(m_beginConnectPoint,m_endConnectPoint); + } + if(m_touched) + { + painter->setPen(QPen(QColor(238,58,140,220))); + painter->drawRect(m_boundingRect); + } + + if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 + { + renderSelectBackground(painter); + } +} + +void ElectricFunctionModelSvgGroup::updateCurState(MonitorItemState e) +{ + GraphicsProjectModelItem::updateCurState(e); + for(auto& p:m_childItems){ + auto pItem = dynamic_cast(p); + if(pItem){ + pItem->updateCurState(e); + } + } +} + + +void ElectricFunctionModelSvgGroup::resize(int nHandle,double dSX, double dSY, const QPointF& basePoint) +{ + switch (nHandle) + { + case H_left: + case H_right: + dSY = 1; //拖拽的是左点右点,为水平缩放,忽略垂直变化 + break; + case H_top: + case H_bottom: + dSX = 1; //拖拽的是顶点底点,为垂直缩放,忽略水平变化 + break; + default: + break; + } + + QTransform trans; + //缩放是以图元原点(中心)位置为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回 + trans.translate(basePoint.x(), basePoint.y()); + trans.scale(dSX, dSY); + trans.translate(-basePoint.x(), -basePoint.y()); + + prepareGeometryChange(); + m_boundingRect = trans.mapRect(m_lastBoudingRect); + m_dWidth = m_boundingRect.width(); + m_dHeight = m_boundingRect.height(); + updateHandles(); +} + +void ElectricFunctionModelSvgGroup::move(const QPointF& point) +{ + moveBy(point.x(), point.y()); +} + +void ElectricFunctionModelSvgGroup::updateMapSvg(QMap map,QString sIndex) +{ + if(sIndex.isEmpty()) + m_mapSvg = map; + else{ + m_mapSvg[sIndex] = map[sIndex]; + } + updateItem(); +} + +void ElectricFunctionModelSvgGroup::setMonitorDisplayInfo(QMap info) +{ + GraphicsProjectModelItem::setMonitorDisplayInfo(info); + for(auto& p:m_childItems){ + auto pItem = dynamic_cast(p); + if(pItem){ + pItem->setMonitorDisplayInfo(info); + } + } +} + +void ElectricFunctionModelSvgGroup::addSvgItem(ElectricFunctionModelSvgItem* item) +{ + item->setParentItem(this); // 关键:设置父项 + m_childItems.append(item); + updateLayout(); +} + +void ElectricFunctionModelSvgGroup::editShape(int nHandle,const QPointF& ptMouse) +{ + prepareGeometryChange(); + updateHandles(); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.cpp new file mode 100644 index 0000000..2e03df4 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.cpp @@ -0,0 +1,116 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroupCT.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgGroupCT::ElectricFunctionModelSvgGroupCT(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgGroup(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgGroupCT::~ElectricFunctionModelSvgGroupCT() +{ + +} + +void ElectricFunctionModelSvgGroupCT::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["ct"] = svgData; + updateMapSvg(mapData,"ct"); + updateItem(); + } + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"ct",nType,0); + GraphicsBaseItem::setImage_1(info); +} + + +void ElectricFunctionModelSvgGroupCT::setupFinish(QVariant var) +{ + if(var.canConvert>()){ + QPair pair = var.value>(); + _nType = pair.first; + _nSize = pair.second; + } + updateItem(); +} + +void ElectricFunctionModelSvgGroupCT::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgGroup::paint(painter,option,widget); + + if(m_childItems.isEmpty()){ //无对象时绘制提示框 + QPen pen(Qt::darkYellow); + pen.setStyle(Qt::DotLine); + painter->setPen(pen); + painter->drawRect(m_boundingRect); + } +} + +void ElectricFunctionModelSvgGroupCT::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("ct")) + _itemImgIndex.append("ct"); + if(!_itemImgIndex.contains("zsct")) + _itemImgIndex.append("zsct"); +} + +void ElectricFunctionModelSvgGroupCT::updateItem() +{ + for(auto pItem:m_childItems){ + delete pItem; + } + m_childItems.clear(); + + QRect rec(0,0,90,30); + + if(_nType == 1){ + for(int i = 0;i < _nSize;++i){ + ElectricFunctionModelSvgItemCT* p = new ElectricFunctionModelSvgItemCT(rec); + p->setItemType(_nType); + p->setMoveable(false); + p->loadSvg(m_mapSvg["ct"]); + addSvgItem(p); + } + } + else if(_nType == 0){ + ElectricFunctionModelSvgItemCT* p = new ElectricFunctionModelSvgItemCT(rec); + p->setItemType(_nType); + p->setMoveable(false); + p->loadSvg(m_mapSvg["zsct"]); + addSvgItem(p); + } + updateTerPos(); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.cpp new file mode 100644 index 0000000..d3d5962 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.cpp @@ -0,0 +1,182 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgGroupPT.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgGroupPT::ElectricFunctionModelSvgGroupPT(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgGroup(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgGroupPT::~ElectricFunctionModelSvgGroupPT() +{ + +} + +void ElectricFunctionModelSvgGroupPT::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["pt"] = svgData; + updateMapSvg(mapData,"pt"); + updateItem(); + } + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"pt",nType,0); + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgGroupPT::setupFinish(QVariant var) +{ + if(var.canConvert>()){ + QList lst = var.value>(); + m_lstType = lst; + } + updateItem(); +} + +void ElectricFunctionModelSvgGroupPT::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgGroup::paint(painter,option,widget); + + if(m_childItems.isEmpty()){ //无对象时绘制提示框 + QPen pen(Qt::lightGray); + pen.setStyle(Qt::DotLine); + painter->setPen(pen); + painter->drawRect(m_boundingRect); + } +} + +void ElectricFunctionModelSvgGroupPT::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("y")) + _itemImgIndex.append("y"); + if(!_itemImgIndex.contains("z")) + _itemImgIndex.append("z"); +} + +void ElectricFunctionModelSvgGroupPT::updateItem() +{ + for(auto pItem:m_childItems){ + delete pItem; + } + m_childItems.clear(); + + QRect rec(0,0,30,30); + for(int i = 0;i < m_lstType.size();++i){ + + ElectricFunctionModelSvgItemPT* p = new ElectricFunctionModelSvgItemPT(rec); + if(m_lstType[i] == 1){ + p->loadSvg(m_mapSvg["y"]); + p->setItemType(1); + } + else{ + p->loadSvg(m_mapSvg["z"]); + p->setItemType(0); + } + p->setMoveable(false); + addSvgItem(p); + } + updateTerPos(); +} + +void ElectricFunctionModelSvgGroupPT::updateLayout() +{ + if (m_childItems.isEmpty()) return; + + // 获取所有子项 + //QList children = childItems(); + int n = m_childItems.size(); + + if (n == 0) return; + + // 中心点 + QPointF center(0, 0); // 假设以当前item的坐标系中心为布局中心 + + // 圆的半径(假设所有子项大小相同) + qreal radius = m_childItems.first()->boundingRect().width()*0.5; // 根据需要调整 + + // 布局半径(子项中心到布局中心的距离) + qreal layoutRadius = radius * 1; // 根据需要调整 + + switch(n) { + case 1: + // 单个子项放在中心 + m_childItems[0]->setPos(center); + break; + + case 2: + // 两个子项上下排列 + m_childItems[0]->setPos(center.x(), center.y() - layoutRadius); + m_childItems[1]->setPos(center.x(), center.y() + layoutRadius); + break; + + case 3: + // 三个子项三角形排列 + for (int i = 0; i < 3; ++i) { + qreal angle = 2 * M_PI * i / 3 - M_PI / 2; // 从顶部开始 + m_childItems[i]->setPos(center.x() + layoutRadius * cos(angle), + center.y() + layoutRadius * sin(angle)); + } + break; + + case 4: + // 四个子项正方形排列 + for (int i = 0; i < 4; ++i) { + qreal angle = 2 * M_PI * i / 4 - M_PI / 4; // 从右上角开始 + m_childItems[i]->setPos(center.x() + layoutRadius * cos(angle), + center.y() + layoutRadius * sin(angle)); + } + break; + + case 5: + // 五个子项五角星排列 + for (int i = 0; i < 5; ++i) { + qreal angle = 2 * M_PI * i / 5 - M_PI / 2; // 从顶部开始 + m_childItems[i]->setPos(center.x() + layoutRadius * cos(angle), + center.y() + layoutRadius * sin(angle)); + } + break; + + default: + // 对于多于5个的情况,使用圆形排列 + for (int i = 0; i < n; ++i) { + qreal angle = 2 * M_PI * i / n; + m_childItems[i]->setPos(center.x() + layoutRadius * cos(angle), + center.y() + layoutRadius * sin(angle)); + } + break; + } + + updateBoundRect(); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp new file mode 100644 index 0000000..264946f --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem.cpp @@ -0,0 +1,190 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItem.h" +#include "graphicsItem/itemControlHandle.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItem::ElectricFunctionModelSvgItem(const QRect &rect, bool autoGenPort,QGraphicsItem *parent) + : GraphicsFunctionModelItem(parent),m_pRender(nullptr),m_pCustomRender(nullptr) +{ + m_lastBoudingRect = rect; + m_boundingRect = rect; + m_dWidth = rect.width(); + m_dHeight = rect.height(); + + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); +} + +ElectricFunctionModelSvgItem::~ElectricFunctionModelSvgItem() +{ + if(m_pRender) + delete m_pRender; +} + +QPainterPath ElectricFunctionModelSvgItem::shape() +{ + QPainterPath path; + path.addRect(m_boundingRect); + return path; +} + +void ElectricFunctionModelSvgItem::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() +{ + if (!parentItem()) + { + QPointF pt1, pt2, delta; + pt1 = mapToScene(QPointF(0, 0)); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + + prepareGeometryChange(); + //将boundingRect设置成中心点和原点(也是默认变换原点),这样三点重合,有助于简化计算 + m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight); + //setTransformOriginPoint(m_boundingRect.center()); //变换中心默认为item的(0,0)点,所以不执行这句话也没有问题 + //更新bouondingRect后重回会显示位置会有变化,需要做对应的移动 + moveBy(-delta.x(), -delta.y()); + updateHandles(); + } + + m_lastBoudingRect = m_boundingRect; +} + +void ElectricFunctionModelSvgItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if(_curMonitorStateEnable){ + + painter->setRenderHint(QPainter::Antialiasing); + + QSize imageSize = m_boundingRect.size().toSize(); + if (imageSize.isEmpty()) return; + + // 1. 渲染SVG到图像 + QImage sourceImage(imageSize, QImage::Format_ARGB32_Premultiplied); + sourceImage.fill(Qt::transparent); + + QPainter sourcePainter(&sourceImage); + sourcePainter.setRenderHint(QPainter::Antialiasing); + m_pCustomRender->render(&sourcePainter, QRectF(0, 0, imageSize.width(), imageSize.height())); + sourcePainter.end(); + + // 2. 直接使用合成模式改变颜色(更高效) + QImage resultImage(imageSize, QImage::Format_ARGB32_Premultiplied); + resultImage.fill(Qt::transparent); + + QPainter resultPainter(&resultImage); + + // 先绘制原始SVG(保留透明度) + resultPainter.drawImage(0, 0, sourceImage); + + // 然后对非透明区域应用颜色叠加 + resultPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + resultPainter.fillRect(resultImage.rect(), QColor(_curMonitorStateColor)); + resultPainter.end(); + + // 3. 最终绘制 + painter->drawImage(m_boundingRect, resultImage); + } + else{ + if(m_pRender) + { + m_pRender->render(painter,m_boundingRect); + } + } + + painter->setPen(m_pen); + painter->setBrush(m_brush); + if(m_state == S_prepareConnect) + { + painter->setPen(QPen(QColor(255,51,153,180))); + painter->drawLine(m_beginConnectPoint,m_endConnectPoint); + } + if(m_touched) + { + painter->setPen(QPen(QColor(238,58,140,220))); + painter->drawRect(m_boundingRect); + } + + if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 + { + renderSelectBackground(painter); + } +} + +void ElectricFunctionModelSvgItem::resize(int nHandle,double dSX, double dSY, const QPointF& basePoint) +{ + switch (nHandle) + { + case H_left: + case H_right: + dSY = 1; //拖拽的是左点右点,为水平缩放,忽略垂直变化 + break; + case H_top: + case H_bottom: + dSX = 1; //拖拽的是顶点底点,为垂直缩放,忽略水平变化 + break; + default: + break; + } + + QTransform trans; + //缩放是以图元原点(中心)位置为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回 + trans.translate(basePoint.x(), basePoint.y()); + trans.scale(dSX, dSY); + trans.translate(-basePoint.x(), -basePoint.y()); + + prepareGeometryChange(); + m_boundingRect = trans.mapRect(m_lastBoudingRect); + m_dWidth = m_boundingRect.width(); + m_dHeight = m_boundingRect.height(); + updateHandles(); +} + +void ElectricFunctionModelSvgItem::move(const QPointF& point) +{ + moveBy(point.x(), point.y()); +} + +void ElectricFunctionModelSvgItem::loadSvg(QByteArray b) +{ + if(m_pRender == nullptr){ + m_pRender = new QSvgRenderer(b); + } + else{ + m_pRender->load(b); + } + _tempSvg = b; + update(); +} + +void ElectricFunctionModelSvgItem::updateMapSvg(QMap map,QString sIndex) +{ + if(sIndex.isEmpty()) + m_mapSvg = map; + else{ + m_mapSvg[sIndex] = map[sIndex]; + } + if(!m_mapSvg.isEmpty()) + loadSvg(m_mapSvg.first()); +} + +void ElectricFunctionModelSvgItem::editShape(int nHandle,const QPointF& ptMouse) +{ + prepareGeometryChange(); + updateHandles(); +} + +void ElectricFunctionModelSvgItem::updateCurState(MonitorItemState e) +{ + GraphicsProjectModelItem::updateCurState(e); + if(m_pCustomRender == nullptr){ + m_pCustomRender = new QSvgRenderer(_curMonitorStateSvg); + } + else{ + m_pCustomRender->load(_curMonitorStateSvg); + } +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.cpp new file mode 100644 index 0000000..ab3b3aa --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.cpp @@ -0,0 +1,69 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItem2wTransformer.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItem2wTransformer::ElectricFunctionModelSvgItem2wTransformer(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItem2wTransformer::~ElectricFunctionModelSvgItem2wTransformer() +{ + +} + +void ElectricFunctionModelSvgItem2wTransformer::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["transformer_2w"] = svgData; + updateMapSvg(mapData,"transformer_2w"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"transformer_2w",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItem2wTransformer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItem2wTransformer::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("transformer_2w")) + _itemImgIndex.append("transformer_2w"); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.cpp new file mode 100644 index 0000000..5d2b506 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.cpp @@ -0,0 +1,69 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItem3wTransformer.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" +#include +#include +#include +#include + +ElectricFunctionModelSvgItem3wTransformer::ElectricFunctionModelSvgItem3wTransformer(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItem3wTransformer::~ElectricFunctionModelSvgItem3wTransformer() +{ + +} + +void ElectricFunctionModelSvgItem3wTransformer::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["transformer_3w"] = svgData; + updateMapSvg(mapData,"transformer_3w"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"transformer_3w",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItem3wTransformer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItem3wTransformer::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("transformer_3w")) + _itemImgIndex.append("transformer_3w"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.cpp new file mode 100644 index 0000000..2eaf9bf --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.cpp @@ -0,0 +1,106 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h" +#include "graphicsItem/itemControlHandle.h" +#include "graphicsItem/itemPort.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemBus::ElectricFunctionModelSvgItemBus(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemBus::~ElectricFunctionModelSvgItemBus() +{ + +} + +void ElectricFunctionModelSvgItemBus::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["bus"] = svgData; + updateMapSvg(mapData,"bus"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"bus",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemBus::updateHandles() +{ + ElectricFunctionModelSvgItem::updateHandles(); +} + +void ElectricFunctionModelSvgItemBus::updateConnectData() +{ + QJsonObject obj; + QJsonArray arr; + if(_property) + { + for(auto &ptr:m_mapPort) + { + //if(ptr->connected()) + { + QJsonObject port; + port["portId"] = ptr->getId(); + //auto pLine = ptr->getConnectPtr(); + port["x"] = ptr->pos().x(); + port["y"] = ptr->pos().y(); + port["portType"] = ptr->getType(); + arr.push_back(port); + } + } + + obj["port"] = arr; + obj["metaModel"] = _property->metaModelName(); + obj["subList"] = _property->saveSubToJsonArr(); + obj["extraInfo"] = _property->getVoltageLevel(); + _property->setContext(obj); + } +} + +void ElectricFunctionModelSvgItemBus::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemBus::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + setHandleEnaable(H_right,true); + setHandleEnaable(H_left,true); + if(!_itemImgIndex.contains("bus")) + _itemImgIndex.append("bus"); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.cpp new file mode 100644 index 0000000..603eafe --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.cpp @@ -0,0 +1,76 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include +#include + +ElectricFunctionModelSvgItemCB::ElectricFunctionModelSvgItemCB(const QRect &rect, bool genNewPort, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemCB::~ElectricFunctionModelSvgItemCB() +{ + +} + +void ElectricFunctionModelSvgItemCB::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["cb"] = svgData; + updateMapSvg(mapData,"cb"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"cb",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + + +void ElectricFunctionModelSvgItemCB::updateHandles() +{ + ElectricFunctionModelSvgItem::updateHandles(); +} + +void ElectricFunctionModelSvgItemCB::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemCB::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("cb")) + _itemImgIndex.append("cb"); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.cpp new file mode 100644 index 0000000..09796fc --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.cpp @@ -0,0 +1,107 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCT.h" + +#include +#include +#include +#include +#include + +ElectricFunctionModelSvgItemCT::ElectricFunctionModelSvgItemCT(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemCT::~ElectricFunctionModelSvgItemCT() +{ + +} + +void ElectricFunctionModelSvgItemCT::initial() +{ + setFlag(QGraphicsItem::ItemIsSelectable, false); + setFlag(QGraphicsItem::ItemIsFocusable, false); + setAcceptedMouseButtons(Qt::NoButton); +} + +void ElectricFunctionModelSvgItemCT::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + // 计算每个图形的宽度 + qreal singleWidth = m_boundingRect.width() / 3.0; + + if(_curMonitorStateEnable){ + + painter->setRenderHint(QPainter::Antialiasing); + + QSize imageSize = m_boundingRect.size().toSize(); + if (imageSize.isEmpty()) return; + + // 1. 渲染SVG到图像 + QImage sourceImage(imageSize, QImage::Format_ARGB32_Premultiplied); + sourceImage.fill(Qt::transparent); + + QPainter sourcePainter(&sourceImage); + sourcePainter.setRenderHint(QPainter::Antialiasing); + m_pCustomRender->render(&sourcePainter, QRectF(0, 0, imageSize.width(), imageSize.height())); + sourcePainter.end(); + + // 2. 直接使用合成模式改变颜色(更高效) + QImage resultImage(imageSize, QImage::Format_ARGB32_Premultiplied); + resultImage.fill(Qt::transparent); + + QPainter resultPainter(&resultImage); + + // 先绘制原始SVG(保留透明度) + resultPainter.drawImage(0, 0, sourceImage); + + // 然后对非透明区域应用颜色叠加 + resultPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + resultPainter.fillRect(resultImage.rect(), QColor(_curMonitorStateColor)); + resultPainter.end(); + + // 3. 最终绘制 + //QRectF rect(m_boundingRect.x() + singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + //painter->drawImage(rect, resultImage); + + if(_itemType == 1){ + QRectF rect1(m_boundingRect.x(), m_boundingRect.y(), singleWidth, m_boundingRect.height()); + painter->drawImage(rect1, resultImage); + + // 绘制第二个图形 + QRectF rect2(m_boundingRect.x() + singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + painter->drawImage(rect2, resultImage); + + // 绘制第三个图形 + QRectF rect3(m_boundingRect.x() + 2 * singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + painter->drawImage(rect3, resultImage); + } + else if(_itemType == 0){ + QRectF rect(m_boundingRect.x() + singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + painter->drawImage(rect, resultImage); + } + } + else{ + if (!m_pRender || !m_pRender->isValid()) + return; + + // 绘制第一个图形 + if(_itemType == 1){ + QRectF rect1(m_boundingRect.x(), m_boundingRect.y(), singleWidth, m_boundingRect.height()); + m_pRender->render(painter, rect1); + + // 绘制第二个图形 + QRectF rect2(m_boundingRect.x() + singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + m_pRender->render(painter, rect2); + + // 绘制第三个图形 + QRectF rect3(m_boundingRect.x() + 2 * singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + m_pRender->render(painter, rect3); + } + else if(_itemType == 0){ + QRectF rect(m_boundingRect.x() + singleWidth, m_boundingRect.y(), singleWidth, m_boundingRect.height()); + m_pRender->render(painter, rect); + } + } +} + + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.cpp new file mode 100644 index 0000000..0597aac --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.cpp @@ -0,0 +1,70 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCableEnd.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemCableEnd::ElectricFunctionModelSvgItemCableEnd(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemCableEnd::~ElectricFunctionModelSvgItemCableEnd() +{ + +} + +void ElectricFunctionModelSvgItemCableEnd::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["cable_end"] = svgData; + updateMapSvg(mapData,"cable_end"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"cable_end",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemCableEnd::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemCableEnd::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("cable_end")) + _itemImgIndex.append("cable_end"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.cpp new file mode 100644 index 0000000..73edf0d --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.cpp @@ -0,0 +1,69 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCableTer.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemCableTer::ElectricFunctionModelSvgItemCableTer(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemCableTer::~ElectricFunctionModelSvgItemCableTer() +{ + +} + +void ElectricFunctionModelSvgItemCableTer::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["cable_termination"] = svgData; + updateMapSvg(mapData,"cable_termination"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"cable_termination",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemCableTer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemCableTer::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("cable_termination")) + _itemImgIndex.append("cable_termination"); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.cpp new file mode 100644 index 0000000..97a9a25 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.cpp @@ -0,0 +1,70 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemDS.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemDS::ElectricFunctionModelSvgItemDS(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemDS::~ElectricFunctionModelSvgItemDS() +{ + +} + +void ElectricFunctionModelSvgItemDS::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["ds"] = svgData; + updateMapSvg(mapData,"ds"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"ds",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemDS::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemDS::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("ds")) + _itemImgIndex.append("ds"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.cpp new file mode 100644 index 0000000..d8251aa --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.cpp @@ -0,0 +1,70 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemDTEDS.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemDTEDS::ElectricFunctionModelSvgItemDTEDS(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + inital(); +} + +ElectricFunctionModelSvgItemDTEDS::~ElectricFunctionModelSvgItemDTEDS() +{ + +} + +void ElectricFunctionModelSvgItemDTEDS::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["dteds"] = svgData; + updateMapSvg(mapData,"dteds"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"dteds",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemDTEDS::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemDTEDS::inital() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("dteds")) + _itemImgIndex.append("dteds"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.cpp new file mode 100644 index 0000000..c980923 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemES.cpp @@ -0,0 +1,71 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemES.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemES::ElectricFunctionModelSvgItemES(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemES::~ElectricFunctionModelSvgItemES() +{ + +} + +void ElectricFunctionModelSvgItemES::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["es"] = svgData; + updateMapSvg(mapData,"es"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"es",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + + +void ElectricFunctionModelSvgItemES::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemES::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("es")) + _itemImgIndex.append("es"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.cpp new file mode 100644 index 0000000..2c74df2 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.cpp @@ -0,0 +1,69 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemFES.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemFES::ElectricFunctionModelSvgItemFES(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemFES::~ElectricFunctionModelSvgItemFES() +{ + +} + +void ElectricFunctionModelSvgItemFES::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["fes"] = svgData; + updateMapSvg(mapData,"fes"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"fes",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemFES::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemFES::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("fes")) + _itemImgIndex.append("fes"); +} diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.cpp new file mode 100644 index 0000000..9405af4 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.cpp @@ -0,0 +1,70 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemLA.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include + +ElectricFunctionModelSvgItemLA::ElectricFunctionModelSvgItemLA(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemLA::~ElectricFunctionModelSvgItemLA() +{ + +} + +void ElectricFunctionModelSvgItemLA::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["lightning_arrester"] = svgData; + updateMapSvg(mapData,"lightning_arrester"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"lightning_arrester",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemLA::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemLA::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("lightning_arrester")) + _itemImgIndex.append("lightning_arrester"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.cpp new file mode 100644 index 0000000..3665169 --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.cpp @@ -0,0 +1,71 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemPI.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "baseProperty.h" + +#include +#include +#include +#include +#include + +ElectricFunctionModelSvgItemPI::ElectricFunctionModelSvgItemPI(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemPI::~ElectricFunctionModelSvgItemPI() +{ + +} + +void ElectricFunctionModelSvgItemPI::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["potential_indicator"] = svgData; + updateMapSvg(mapData,"potential_indicator"); + loadSvg(svgData); + updateItem(); + } + + QString sMeta; + QString sModel; + int nType = -1; + if(_property){ + sMeta = _property->metaModelName(); + sModel = _property->modelName(); + nType = _property->type(); + } + + if(_pHandle && !sMeta.isEmpty() && !sModel.isEmpty()) + _pHandle->updateItemIcon(sMeta,sModel,mapData,"potential_indicator",nType,0); + + GraphicsBaseItem::setImage_1(info); +} + +void ElectricFunctionModelSvgItemPI::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + ElectricFunctionModelSvgItem::paint(painter,option,widget); +} + +void ElectricFunctionModelSvgItemPI::initial() +{ + setHandleIfShow(H_textCaption,false); + setHandleVisible(false); + setFunctionHandleIfShow(false); + setFunctionHandleEnaable(false); + if(!_itemImgIndex.contains("potential_indicator")) + _itemImgIndex.append("potential_indicator"); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.cpp b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.cpp new file mode 100644 index 0000000..e91ab9f --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.cpp @@ -0,0 +1,68 @@ +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemPT.h" + +#include +#include +#include +#include +#include + +ElectricFunctionModelSvgItemPT::ElectricFunctionModelSvgItemPT(const QRect &rect, QGraphicsItem *parent) + : ElectricFunctionModelSvgItem(rect,parent) +{ + initial(); +} + +ElectricFunctionModelSvgItemPT::~ElectricFunctionModelSvgItemPT() +{ + +} + +void ElectricFunctionModelSvgItemPT::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if(_curMonitorStateEnable){ + + painter->setRenderHint(QPainter::Antialiasing); + + QSize imageSize = m_boundingRect.size().toSize(); + if (imageSize.isEmpty()) return; + + // 1. 渲染SVG到图像 + QImage sourceImage(imageSize, QImage::Format_ARGB32_Premultiplied); + sourceImage.fill(Qt::transparent); + + QPainter sourcePainter(&sourceImage); + sourcePainter.setRenderHint(QPainter::Antialiasing); + m_pCustomRender->render(&sourcePainter, QRectF(0, 0, imageSize.width(), imageSize.height())); + sourcePainter.end(); + + // 2. 直接使用合成模式改变颜色(更高效) + QImage resultImage(imageSize, QImage::Format_ARGB32_Premultiplied); + resultImage.fill(Qt::transparent); + + QPainter resultPainter(&resultImage); + + // 先绘制原始SVG(保留透明度) + resultPainter.drawImage(0, 0, sourceImage); + + // 然后对非透明区域应用颜色叠加 + resultPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + resultPainter.fillRect(resultImage.rect(), QColor(_curMonitorStateColor)); + resultPainter.end(); + + // 3. 最终绘制 + painter->drawImage(m_boundingRect, resultImage); + } + else{ + if (!m_pRender || !m_pRender->isValid()) + return; + m_pRender->render(painter, m_boundingRect); + } +} + +void ElectricFunctionModelSvgItemPT::initial() +{ + setFlag(QGraphicsItem::ItemIsSelectable, false); + setFlag(QGraphicsItem::ItemIsFocusable, false); + setAcceptedMouseButtons(Qt::NoButton); +} + diff --git a/diagramCavas/source/graphicsItem/functionModelItem/graphicsFunctionModelItem.cpp b/diagramCavas/source/graphicsItem/functionModelItem/graphicsFunctionModelItem.cpp new file mode 100644 index 0000000..b0c992f --- /dev/null +++ b/diagramCavas/source/graphicsItem/functionModelItem/graphicsFunctionModelItem.cpp @@ -0,0 +1,97 @@ +#include "graphicsItem/functionModelItem/graphicsFunctionModelItem.h" + +GraphicsFunctionModelItem::GraphicsFunctionModelItem(QGraphicsItem *parent) + : GraphicsProjectModelItem(parent) +{ + +} + +GraphicsFunctionModelItem::~GraphicsFunctionModelItem() +{ +} + +void GraphicsFunctionModelItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) +} + + +/********************************功能模item组*************************************/ + +GraphicsFunctionModelGroup::GraphicsFunctionModelGroup(QGraphicsItem *parent) + :GraphicsFunctionModelItem(parent) +{ + +} + +GraphicsFunctionModelGroup::~GraphicsFunctionModelGroup() +{ + +} + +void GraphicsFunctionModelGroup::addItem(GraphicsFunctionModelItem* item) +{ + item->setParentItem(this); // 关键:设置父项 + m_childItems.append(item); + updateLayout(); +} + +QRectF GraphicsFunctionModelGroup::updateBoundRect() +{ + QRectF rect; + if(m_childItems.size()){ + for (auto* child : m_childItems) { + rect |= child->boundingRect().translated(child->pos()); + } + + m_boundingRect = rect; + } + else + { + return m_boundingRect; + } + + updateHandles(); + return rect; +} + +void GraphicsFunctionModelGroup::updateLayout() +{ + if (m_childItems.isEmpty()) return; + + // 计算所有子项的总尺寸 + qreal totalSize = 0; + QList childSizes; + + for (GraphicsBaseItem *child : m_childItems) { + QRectF childRect = child->boundingRect(); + qreal size = (m_direction == 0) ? childRect.width() : childRect.height(); + childSizes.append(size); + totalSize += size; + } + + // 计算总间距 + qreal totalSpacing = m_spacing * (m_childItems.size() - 1); + + // 计算起始位置(相对于父项中心) + qreal startPos = -(totalSize + totalSpacing) / 2; + + // 定位每个子项 + qreal currentPos = startPos; + for (int i = 0; i < m_childItems.size(); ++i) { + GraphicsBaseItem *child = m_childItems[i]; + QRectF childRect = child->boundingRect(); + + if (m_direction == 0) { + // 水平布局:x坐标变化,y坐标保持中心对齐 + child->setPos(currentPos, -childRect.height() / 2); + currentPos += childSizes[i] + m_spacing; + } else { + // 垂直布局:y坐标变化,x坐标保持中心对齐 + child->setPos(-childRect.width() / 2, currentPos); + currentPos += childSizes[i] + m_spacing; + } + } + updateBoundRect(); +} diff --git a/diagramCavas/source/graphicsItem/graphicsBaseItem.cpp b/diagramCavas/source/graphicsItem/graphicsBaseItem.cpp new file mode 100644 index 0000000..d50975e --- /dev/null +++ b/diagramCavas/source/graphicsItem/graphicsBaseItem.cpp @@ -0,0 +1,920 @@ +#include "graphicsItem/graphicsBaseItem.h" +#include "graphicsItem/functionModelItem/graphicsFunctionModelItem.h" +#include "graphicsItem/handleRect.h" +#include "graphicsItem/handleText.h" +#include "graphicsItem/itemPort.h" +#include "baseProperty.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "dataBase.h" +#include +#include +#include +#include +#include + + +GraphicsBaseItem::GraphicsBaseItem(QGraphicsItem *parent) + : AbstractShapeType(parent) + ,_property(nullptr) + ,_pEntity(nullptr) + ,_pHandle(nullptr) +{ + m_type = T_item; + _itemChanged = false; + m_touched = false; + + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + setAcceptHoverEvents(true); + m_layoutRadius = 80; +} + +GraphicsBaseItem::GraphicsBaseItem(const GraphicsBaseItem& obj) + :AbstractShapeType(obj) + ,_property(nullptr) + ,_pEntity(nullptr) +{ + m_type = T_item; + _itemChanged = false; + m_touched = false; + + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + setAcceptHoverEvents(true); + + m_layoutRadius = 80; + + m_lastPoint = obj.m_lastPoint; + m_touched = obj.m_touched; + + PortState tpe; + if(m_Itemtype == GIT_baseNode || m_Itemtype == GIT_baseBus || m_Itemtype == GIT_node || m_Itemtype == GIT_bus){ + tpe = p_movable; + } + else{ + tpe = P_const; + } + + for(auto& port:obj.m_mapPort){ + addPort(tpe,port->pos(),port->getId(),port->getType(),port->portPos(),port->getXPercent(),port->getYPercent()); + } +} + +GraphicsBaseItem::~GraphicsBaseItem() +{ + foreach (int key, m_vecHanle.keys()) + { + ItemControlHandle* pHandle = m_vecHanle.value(key); + if (pHandle) + { + delete pHandle; + pHandle = nullptr; + } + } +} + +QString GraphicsBaseItem::getName() const +{ + if(_property) + return _property->name(); + return QString(); +} + +void GraphicsBaseItem::setName(QString str) +{ + if(_property) + _property->setName(str); +} + +QPointF GraphicsBaseItem::getPosition() const +{ + return pos(); +} + +void GraphicsBaseItem::setPosition(QPointF pos) +{ + setPos(pos); +} + +QRectF GraphicsBaseItem::getSize() const +{ + return m_boundingRect; +} + +void GraphicsBaseItem::setSize(QRectF rec) +{ + prepareGeometryChange(); + m_boundingRect = rec; + update(); +} + +GraphicsBaseItem::RotateAngle GraphicsBaseItem::getRotateAngle() const +{ + int nRotate = rotation(); + + // 标准化角度到 0-360 范围 + nRotate = nRotate % 360; + if (nRotate < 0) nRotate += 360; + + // 映射到最近的 90 度倍数 + int normalized = ((nRotate + 45) / 90) * 90 % 360; + + QMetaEnum metaEnum = QMetaEnum::fromType(); + + // 检查标准化后的角度是否是有效的枚举值 + if (metaEnum.valueToKey(normalized) != nullptr) { + return static_cast(normalized); + } + + return RotateAngle::Angle_0; +} + +void GraphicsBaseItem::setRotateAngle(RotateAngle angle) +{ + int nAngle = static_cast(angle); + setRotation(nAngle); + emit itemRotated(this); +} + +QFileInfo GraphicsBaseItem::getImage_1() const +{ + return QFileInfo(m_bgImagePath); +} + +void GraphicsBaseItem::setImage_1(QFileInfo info) +{ + m_bgImagePath = info.absoluteFilePath(); +} + +int GraphicsBaseItem::addPort(PortState typ,QPointF vec,QString id,HandleType hType,PortPos pos,double dXPercent,double dYPercent) +{ + int ntagId = -1; + for(ntagId = H_connect;ntagId < 999;++ntagId) //添加到未占用位置 + { + if(!m_vecHanle.contains(ntagId)) + break; + } + ItemPort* pPort = new ItemPort(this); + if(id.isEmpty()) + { + pPort->setId(QUuid::createUuid().toString()); + } + else + { + pPort->setId(id); + } + if(typ == p_movable) + { + pPort->setType(T_lineInOut); + } + else + { + pPort->setType(hType); + pPort->setPortPos(pos); + pPort->setXPercent(dXPercent); + pPort->setYPercent(dYPercent); + } + pPort->setTag(ntagId); + m_vecHanle.insert(ntagId,pPort); + pPort->setParent(this); + pPort->setPos(vec); + + m_mapPort.insert(pPort->getId(),pPort); + return ntagId; +} + +void GraphicsBaseItem::movePort(QString id,QPointF vec) +{ + if(m_mapPort.contains(id)){ + auto pPort = m_mapPort.value(id); + pPort->setPos(vec); + } +} + +void GraphicsBaseItem::setEntity(PowerEntity* pEntity) +{ + _pEntity = pEntity; +} + +PowerEntity* GraphicsBaseItem::entity() +{ + return _pEntity; +} + +void GraphicsBaseItem::setProperty(ModelProperty* p) +{ + if(_property) //已经有对象 + { + disconnect(_property,&ModelProperty::updateData,this,&GraphicsBaseItem::onUpdateData); //断开老数据 + } + connect(p,&ModelProperty::updateData,this,&GraphicsBaseItem::onUpdateData); + _property = p; +} + +void GraphicsBaseItem::renderSelectBackground(QPainter* painter) +{ + int nPenWidth = 1; + painter->setPen(QPen(QColor(135,206,235,180))); //蓝色的外框 + + painter->setBrush(QBrush(QColor(135,206,235,180))); + + double dx = m_boundingRect_selected.x(); + double dy = m_boundingRect_selected.y(); + double dWidth = m_boundingRect_selected.width(); + double dHeight = m_boundingRect_selected.height(); + + + //画弧线0度是3点钟方向 + if(dWidth > dHeight) + { + painter->drawRect(m_boundingRect_selected); + painter->drawChord(dx-dHeight*0.5,dy,dHeight,dHeight,90*16,180*16); + painter->drawChord(dx+dWidth-dHeight*0.5,dy,dHeight,dHeight,(-90)*16,180*16); + } + else if(dWidth < dHeight) + { + painter->drawRect(m_boundingRect_selected); + painter->drawChord(dx,dy-dWidth*0.5,dWidth,dWidth,0*16,180*16); + painter->drawChord(dx,dy+dHeight-dWidth*0.5,dWidth,dWidth,180*16,180*16); + //painter->drawChord(dx+dWidth-dHeight*0.5,dy,dHeight,dHeight,(-90)*16,180*16); + } + else + { + painter->drawEllipse(QPointF(dx+dWidth*0.5,dy+dHeight*0.5),dHeight*0.5,dHeight*0.5); + } +} + +ItemPort* GraphicsBaseItem::getPortById(QString id) const +{ + for(auto iter:m_mapPort) + { + QString portId = iter->getId(); + if(portId == id) + return iter; + } + return nullptr; +} + +ItemPort* GraphicsBaseItem::getPortPtr(int n) const +{ + for(auto iter:m_mapPort) + { + int tag = iter->getTag(); + if(tag == n) + return iter; + } + return nullptr; +} + +ItemControlHandle* GraphicsBaseItem::getHandlePtr(int n) const +{ + for(auto iter:m_vecHanle) + { + int tag = iter->getTag(); + if(tag == n) + return iter; + } + return nullptr; +} + + +void GraphicsBaseItem::onUpdateData() +{ + updateByProperty(); +} + +void GraphicsBaseItem::initialPortsByDatabase(int nComponentTypeId) +{ + QMap mapType = DataBase::GetInstance()->getAllComponentType(); + + if(mapType.contains(nComponentTypeId)) + { + QJsonArray nodesJsonArray = mapType[nComponentTypeId].config["port"].toArray(); + + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + double dX = node["xRatio"].toDouble(); + double dY = node["yRatio"].toDouble(); + int movable = node["movable"].toInt(); + int portType = node["portType"].toInt(); + int portLocate = node["portLocate"].toInt(); + + addPort(PortState(movable),QPointF(boundingRect().left() + boundingRect().width() * dX, boundingRect().top()+boundingRect().height() *dY),QUuid::createUuid().toString(),HandleType(portType),PortPos(portLocate),dX,dY); + } + } +} + +void GraphicsBaseItem::addDynamicText(QString tag,QString para) +{ + int ntagId = -1; + for(ntagId = H_textCaption+1;ntagId < H_connect;++ntagId) //添加到未占用位置 + { + if(!m_vecHanle.contains(ntagId)) + break; + } + HandleText* pText = new HandleText(this); + pText->setEditable(false); + pText->setIndex(ntagId); + pText->setTagName(tag); + pText->setType(1); + pText->setPara(para); + m_vecHanle.insert(ntagId,pText); + + m_mapDynamicText.insert(tag,pText); + rearrangeDynamicText(); +} + +void GraphicsBaseItem::removeDynamicText(QString tag) +{ + for(auto& pText:m_mapDynamicText){ + if(pText->getTagName() == tag){ + int nIndex = pText->getIndex(); + m_vecHanle.remove(nIndex); + auto p = m_mapDynamicText.take(tag); + delete p; + return; + } + } + rearrangeDynamicText(); +} + +void GraphicsBaseItem::removeAllDynamicText() +{ + for(auto iter = m_mapDynamicText.begin();iter != m_mapDynamicText.end();++iter){ + int nIndex = iter.value()->getIndex(); + m_vecHanle.remove(nIndex); + //auto p = m_mapDynamicText.take(iter.key()); + //delete p; + } + qDeleteAll(m_mapDynamicText); + m_mapDynamicText.clear(); +} + +bool GraphicsBaseItem::hasDynamicText(const QString& tag) +{ + for(auto& pText:m_mapDynamicText){ + if(pText->getTagName() == tag){ + return true; + } + } + return false; +} + +void GraphicsBaseItem::setDynamicLayoutRadius(qreal radius) +{ + m_layoutRadius = radius; + rearrangeDynamicText(); +} + + +void GraphicsBaseItem::rearrangeDynamicText() +{ + int count = m_mapDynamicText.size(); + if (count == 0) return; + + // 计算左右两侧的项数 + int leftCount = (count + 1) / 2; // 左侧稍多 + int rightCount = count - leftCount; + + qreal itemHeight = 30; + qreal startY = -((qMax(leftCount, rightCount) - 1) * itemHeight) / 2; + + int i = 0; + for (auto& item : m_mapDynamicText) { + qreal x, y; + + if (i < leftCount) { + // 左侧排列 + x = -m_layoutRadius-20; // 左侧偏移多一点 + y = startY + i * itemHeight; + } else { + // 右侧排列 + x = m_layoutRadius; // 右侧偏移 + y = startY + (i - leftCount) * itemHeight; + } + + item->setPos(x, y); + ++i; + } +} + +QList GraphicsBaseItem::getAllComponentRects() const +{ + QList lst; + if(_pHandle){ + auto mapItems = _pHandle->allItems(); + for(auto &pItem:mapItems){ + if (pItem == this) continue; + ModelProperty* pPro = pItem->getProperty(); + if(pPro){ + if(pPro->type() == 8) continue; //排除线 + } + QRectF bounds = pItem->boundingRect(); + bounds = pItem->mapRectToScene(bounds); + lst.append(bounds); + } + } + return lst; +} + +QList GraphicsBaseItem::getObstacleShapes() const +{ + QList obstacles; + + if (!scene()) { + return obstacles; + } + if(_pHandle){ + auto mapItems = _pHandle->allItems(); + for(auto &pItem:mapItems){ + if (pItem == this) continue; + ModelProperty* pPro = pItem->getProperty(); + if(pPro){ + if(pPro->type() == 8) continue; //排除线 + } + + // 获取元件的形状(考虑旋转) + QPainterPath shape = pItem->mapToScene(pItem->shape()); + + // 如果需要,可以稍微扩大形状 + QPainterPathStroker stroker; + stroker.setWidth(10); // 扩大10像素作为安全距离 + QPainterPath expandedShape = stroker.createStroke(shape); + + obstacles.append(expandedShape); + } + } + + return obstacles; +} +/********************************基模****************************************/ +GraphicsBaseModelItem::GraphicsBaseModelItem(QGraphicsItem *parent) + :GraphicsBaseItem(parent) +{ + +} + +GraphicsBaseModelItem::GraphicsBaseModelItem(const GraphicsBaseModelItem& obj) + :GraphicsBaseItem(obj) +{ + +} + +GraphicsBaseModelItem::~GraphicsBaseModelItem() +{ + +} + +GraphicsBaseModelItem* GraphicsBaseModelItem::clone() const +{ + return new GraphicsBaseModelItem(*this); +} + +void GraphicsBaseModelItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if(_stateMask){ + if(getProperty()->modelName().isEmpty()) + { + painter->setPen(QColor(255,0,0,80)); + painter->setBrush(QColor(255,0,0,80)); + painter->drawRoundedRect(m_boundingRect,10,10); + } + } +} + +QVariant GraphicsBaseModelItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) +{ + if (change == QGraphicsItem::ItemSelectedHasChanged) + { + setHandleVisible(value.toBool()); + } + return QGraphicsItem::itemChange(change, value); +} + +/********************************基模组*************************************/ + +GraphicsBaseModelGroup::GraphicsBaseModelGroup(QGraphicsItem *parent) + :GraphicsBaseModelItem(parent) +{ + +} +GraphicsBaseModelGroup::GraphicsBaseModelGroup(const GraphicsBaseModelGroup& obj) + :GraphicsBaseModelItem(obj) +{ + _layout = obj._layout; +} +GraphicsBaseModelGroup::~GraphicsBaseModelGroup() +{ + +} + +GraphicsBaseModelGroup* GraphicsBaseModelGroup::clone() const +{ + return new GraphicsBaseModelGroup(*this); +} + +void GraphicsBaseModelGroup::addItem(GraphicsBaseModelItem* item) +{ + item->setParentItem(this); // 关键:设置父项 + m_childItems.append(item); + updateLayout(); +} + +QRectF GraphicsBaseModelGroup::boundingRect() const +{ + QRectF rect; + for (auto* child : childItems()) { + rect |= child->boundingRect().translated(child->pos()); + } + return rect; +} +void GraphicsBaseModelGroup::updateLayout() +{ + if(_layout == 0){ //横 + + } + else{ //纵 + + } +} + +/********************************工程模**************************************/ + +GraphicsProjectModelItem::GraphicsProjectModelItem(QGraphicsItem *parent) + :GraphicsBaseItem(parent) +{ + _lastPort = -1; + //初始化缩放操作用的handle + //m_vecHanle.reserve(H_left); + for(int i = H_leftTop; i <= H_left; i++) + { + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_resize); + pHandle->setTag(i); + m_vecHanle.insert(i-1,pHandle); + } + for(int i = H_rotate_leftTop; i <= H_rotate_leftBottom; i++) + { + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_rotate); + pHandle->setTag(i); + m_vecHanle.insert(i-1,pHandle); + } + + HandleText* pHandle = new HandleText(this); + pHandle->setType(T_text); + pHandle->setTag(H_textCaption); + //pHandle->setText(QString("uname")); + pHandle->setPos(30,-30); + pHandle->setParent(this); + m_vecHanle.insert(H_textCaption,pHandle); + connect(pHandle,&HandleText::editFinish,this,&GraphicsProjectModelItem::onEditNameFinish); + + /*HandleText* pCurrent = new HandleText(this); //电流 + pCurrent->setEditable(false); + pCurrent->setType(T_text); + pCurrent->setTag(H_textCurrent); + pCurrent->setText(QString("I:")); + pCurrent->setPos(-30,-30); + pCurrent->setParent(this); + m_vecHanle.insert(H_textCurrent,pCurrent); + + HandleText* pVoltage = new HandleText(this); //电压 + pVoltage->setType(T_text); + pVoltage->setTag(h_textVoltage); + pVoltage->setText(QString("V:")); + pVoltage->setPos(-30,30); + pVoltage->setParent(this); + m_vecHanle.insert(h_textVoltage,pVoltage);*/ + + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + + _curMonitorState = MonitorItemState::Normal; + _curMonitorStateEnable = false; +} + +GraphicsProjectModelItem::GraphicsProjectModelItem(const GraphicsProjectModelItem& obj) + :GraphicsBaseItem(obj) +{ + _lastPort = -1; + //初始化缩放操作用的handle + //m_vecHanle.reserve(H_left); + for(int i = H_leftTop; i <= H_left; i++) + { + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_resize); + pHandle->setTag(i); + m_vecHanle.insert(i-1,pHandle); + } + for(int i = H_rotate_leftTop; i <= H_rotate_leftBottom; i++) + { + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_rotate); + pHandle->setTag(i); + m_vecHanle.insert(i-1,pHandle); + } + + HandleText* pHandle = new HandleText(this); + pHandle->setType(T_text); + pHandle->setTag(H_textCaption); + pHandle->setPos(30,-30); + pHandle->setParent(this); + m_vecHanle.insert(H_textCaption,pHandle); + + m_state = obj.m_state; + m_beginConnectPoint = obj.m_beginConnectPoint; + m_endConnectPoint = obj.m_endConnectPoint; + _modelName = obj._modelName; + + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + setPos(obj.pos()); + setRotation(obj.rotation()); + + _curMonitorState = MonitorItemState::Normal; + _curMonitorStateEnable = false; +} + +GraphicsProjectModelItem::~GraphicsProjectModelItem() +{ + +} + +GraphicsProjectModelItem* GraphicsProjectModelItem::clone() const +{ + return new GraphicsProjectModelItem(*this); +} + +void GraphicsProjectModelItem::createOperationCopy() +{ + m_pOperationCopy = new QGraphicsPathItem(this->shape()); + m_pOperationCopy->setPen(Qt::DashLine); + m_pOperationCopy->setPos(this->pos()); + m_pOperationCopy->setTransformOriginPoint(this->transformOriginPoint()); + m_pOperationCopy->setTransform(this->transform()); + m_pOperationCopy->setRotation(this->rotation()); + m_pOperationCopy->setScale(this->scale()); + m_pOperationCopy->setZValue(this->zValue()); + + QGraphicsScene* scene = this->scene(); + if(scene && m_pOperationCopy) + { + scene->addItem(m_pOperationCopy); + m_movingIniPos = this->pos(); + } +} + +void GraphicsProjectModelItem::removeOperationCopy() +{ + QGraphicsScene* scene = this->scene(); + if(scene && m_pOperationCopy) + { + if(this->pos() != m_pOperationCopy->pos()) + this->setPos(m_pOperationCopy->pos()); //本体移动到副本的位置 + if(this->rotation() != m_pOperationCopy->rotation()) + this->setRotation(m_pOperationCopy->rotation()); //本体旋转至副本的角度 + + scene->removeItem(m_pOperationCopy); + delete m_pOperationCopy; + m_pOperationCopy = nullptr; + } +} + +void GraphicsProjectModelItem::moveOperationCopy(const QPointF& distance) +{ + if(m_pOperationCopy) + m_pOperationCopy->setPos(m_movingIniPos + distance); +} + +void GraphicsProjectModelItem::rotateOperationCopy(const double& dAngle) +{ + if(m_pOperationCopy) + m_pOperationCopy->setRotation(dAngle); +} + +void GraphicsProjectModelItem::syncRotationDataFromParent(const double& data) +{ + //m_dSyncRotationByParent = rotation() + data; + m_dSyncRotationByParent += data; + //让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断 + if (m_dSyncRotationByParent > 180) + m_dSyncRotationByParent -= 360; + if (m_dSyncRotationByParent < -180) + m_dSyncRotationByParent += 360; +} + +DataSourceType GraphicsProjectModelItem::getDataSourceType() const +{ + return _sourceType; +} + +void GraphicsProjectModelItem::setDataSourceType(DataSourceType type) +{ + _sourceType = type; +} + +QVariant GraphicsProjectModelItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) +{ + if (change == QGraphicsItem::ItemSelectedHasChanged) + { + QGraphicsItemGroup *group = dynamic_cast(parentItem()); + if(!group) + setHandleVisible(value.toBool()); + //setFunctionHandleVisible(false); + else //在某一组群中,由组群展示是否选中,自身不做展示 + { + setSelected(false); + return QVariant::fromValue(false); + } + } + return QGraphicsItem::itemChange(change, value); +} + +void GraphicsProjectModelItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) +{ + Q_UNUSED(event); +} + +void GraphicsProjectModelItem::paint(QPainter *painter,const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); +} + +void GraphicsProjectModelItem::setLabelTag(const QString& name) +{ + m_vecHanle[H_textCaption]->setText(name); +} + +QString GraphicsProjectModelItem::getLabelTag() const +{ + return m_vecHanle[H_textCaption]->getText(); +} + +void GraphicsProjectModelItem::updateByProperty() +{ + if(_property) + { + setLabelTag(_property->tag()); + } +} + +void GraphicsProjectModelItem::unbindProperty() +{ + _property = nullptr; +} + + +void GraphicsProjectModelItem::updateConnectData() +{ + QJsonObject obj; + QJsonArray arr; + if(_property) + { + for(auto &ptr:m_mapPort) + { + //if(ptr->connected()) + { + QJsonObject port; + port["portId"] = ptr->getId(); + //auto pLine = ptr->getConnectPtr(); + port["x"] = ptr->pos().x(); + port["y"] = ptr->pos().y(); + port["locate"] = ptr->portPos(); + port["portType"] = ptr->getType(); + port["xRatio"] = ptr->getXPercent(); + port["yRatio"] = ptr->getYPercent(); + arr.push_back(port); + } + } + + obj["port"] = arr; + obj["metaModel"] = _property->metaModelName(); + obj["subList"] = _property->saveSubToJsonArr(); + obj["extraInfo"] = _property->getVoltageLevel(); + _property->setContext(obj); + } +} + +void GraphicsProjectModelItem::onEditNameFinish(const QString& str) +{ + emit ifExist(m_itemId,str,m_Itemtype,this); +} + +void GraphicsProjectModelItem::updateTerPos() +{ + for(auto &pPort:m_mapPort){ + double dX = pPort->getXPercent(); + double dY = pPort->getYPercent(); + QPointF pos(boundingRect().left() + boundingRect().width() * dX, boundingRect().top()+boundingRect().height() *dY); + pPort->setPos(pos); + } +} + +/********************************工程模组*************************************/ + +GraphicsProjectModelGroup::GraphicsProjectModelGroup(QGraphicsItem *parent) + :GraphicsProjectModelItem(parent) +{ + +} + +GraphicsProjectModelGroup::GraphicsProjectModelGroup(const GraphicsProjectModelGroup& obj) + :GraphicsProjectModelItem(obj) +{ + m_direction = obj.m_direction; + m_spacing = obj.m_spacing; + _groupType = obj.m_spacing; + + for(auto &pItem:obj.m_childItems){ + auto newItem = pItem->clone(); + m_childItems.append(newItem); + } +} + +GraphicsProjectModelGroup::~GraphicsProjectModelGroup() +{ + +} + +GraphicsProjectModelGroup* GraphicsProjectModelGroup::clone() const +{ + return new GraphicsProjectModelGroup(*this); +} + +void GraphicsProjectModelGroup::addItem(GraphicsProjectModelItem* item) +{ + item->setParentItem(this); // 关键:设置父项 + m_childItems.append(item); + updateLayout(); +} + +/*QRectF GraphicsProjectModelGroup::boundingRect() const +{ + +}*/ + +QRectF GraphicsProjectModelGroup::updateBoundRect() +{ + QRectF rect; + if(m_childItems.size()){ + for (auto* child : m_childItems) { + rect |= child->boundingRect().translated(child->pos()); + } + + m_boundingRect = rect; + } + else + { + return m_boundingRect; + } + + updateHandles(); + //qDebug()< childSizes; + + for (GraphicsBaseItem *child : m_childItems) { + QRectF childRect = child->boundingRect(); + qreal size = (m_direction == 0) ? childRect.width() : childRect.height(); + childSizes.append(size); + totalSize += size; + } + + // 计算总间距 + qreal totalSpacing = m_spacing * (m_childItems.size() - 1); + + // 计算起始位置(相对于父项中心) + qreal startPos = -(totalSize + totalSpacing) / 2; + + // 定位每个子项 + qreal currentPos = startPos; + for (int i = 0; i < m_childItems.size(); ++i) { + GraphicsBaseItem *child = m_childItems[i]; + QRectF childRect = child->boundingRect(); + + if (m_direction == 0) { + // 水平布局:x坐标变化,y坐标保持中心对齐 + child->setPos(currentPos, -childRect.height() / 2); + currentPos += childSizes[i] + m_spacing; + } else { + // 垂直布局:y坐标变化,x坐标保持中心对齐 + child->setPos(-childRect.width() / 2, currentPos); + currentPos += childSizes[i] + m_spacing; + } + } + updateBoundRect(); +} diff --git a/diagramCavas/source/graphicsItem/graphicsItemGroup.cpp b/diagramCavas/source/graphicsItem/graphicsItemGroup.cpp new file mode 100644 index 0000000..cf16c4a --- /dev/null +++ b/diagramCavas/source/graphicsItem/graphicsItemGroup.cpp @@ -0,0 +1,278 @@ +#include "graphicsItem/graphicsItemGroup.h" +//#include "graphicsItem/handleRect.h" +#include +#include +#include +#include + + +GraphicsItemGroup::GraphicsItemGroup(QGraphicsItem *parent) + : AbstractShapeType(parent) +{ + m_type = T_group; + + m_boundingRect = QRectF(); + m_lastBoudingRect = QGraphicsItemGroup::boundingRect(); + + //初始化缩放操作用的handle + //m_vecHanle.reserve(H_left); + /*for(int i = H_leftTop; i <= H_left; i++) + { + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_resize); + pHandle->setTag(i); + m_vecHanle.insert(i-1,pHandle); + } + for(int i = H_rotate_leftTop; i <= H_rotate_leftBottom; i++) + { + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_rotate); + pHandle->setTag(i); + m_vecHanle.insert(i-1,pHandle); + }*/ + + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + setAcceptHoverEvents(true); +} + +GraphicsItemGroup::~GraphicsItemGroup() +{ +} + +QPainterPath GraphicsItemGroup::shape() +{ + QPainterPath path; + path.addRect(m_boundingRect); + return path; +} + +void GraphicsItemGroup::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 + { + int nPenWidth = 1; + painter->setPen(QPen(QColor(70,70,70), nPenWidth, Qt::DashLine)); //蓝色的外框 + painter->setBrush(Qt::NoBrush); + painter->drawRect(m_boundingRect); + + } +} + +void GraphicsItemGroup::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) +{ + Q_UNUSED(event); +} + +QVariant GraphicsItemGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) +{ + if (change == QGraphicsItem::ItemSelectedHasChanged) + { + QGraphicsItemGroup *group = dynamic_cast(parentItem()); + if(group) //在某一组群中,由组群展示是否选中,自身不做展示 + { + setSelected(false); + return QVariant::fromValue(false); + } + } + return QGraphicsItemGroup::itemChange(change, value); +} + +void GraphicsItemGroup::syncRotationDataFromParent(const double& data) +{ + /*m_dSyncRotationByParent += data; + //让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断 + if (m_dSyncRotationByParent > 180) + m_dSyncRotationByParent -= 360; + if (m_dSyncRotationByParent < -180) + m_dSyncRotationByParent += 360; + //同步给子项 + foreach (QGraphicsItem* item, childItems()) + { + if(qgraphicsitem_cast(item)) + continue; + + AbstractShape* shape = qgraphicsitem_cast(item); + if(shape && shape->getType()==T_group) + { + GraphicsItemGroup *group = qgraphicsitem_cast(item); + if(group) + group->syncRotationDataFromParent(data); + } + else + { + GraphicsProjectModelItem *baseItem = qgraphicsitem_cast(item); + if (baseItem) + baseItem->syncRotationDataFromParent(data); + } + }*/ +} + +void GraphicsItemGroup::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() +{ + if (!parentItem()) + { + if (m_boundingRect.isNull()) + { + m_boundingRect = QGraphicsItemGroup::boundingRect(); + m_dWidth = m_boundingRect.width(); + m_dHeight = m_boundingRect.height(); + } + + //因为group包含子item,boundingRect会根据子item自动计算生成,所以不再通过重设boundingRect(中心和原点重合)重合的方式,因为此种方式需要子item做各种同步计算 + //最简单的方式是,每次boundingRect发生变化(缩放)后重新设置变换中心,但是会出现漂移(因为item只存储旋转角度数据,每帧都根据角度进行重绘),具体见开发学习笔记,消除这个漂移即可 + prepareGeometryChange(); + //具体计算 + double dAngle = qDegreesToRadians(rotation()); + QPointF centerPt = m_boundingRect.center(); + QPointF originPt = transformOriginPoint(); + QPointF targetPt = QPointF(0, 0); + targetPt.setX(originPt.x() + qCos(dAngle)*(centerPt.x() - originPt.x()) - qSin(dAngle)*(centerPt.y() - originPt.y())); + targetPt.setY(originPt.y() + qSin(dAngle)*(centerPt.x() - originPt.x()) + qCos(dAngle)*(centerPt.y() - originPt.y())); + QPointF delta = targetPt - centerPt; + //m_boundingRect.adjust(delta.x(), delta.y(), delta.x(), delta.y()); + setTransformOriginPoint(m_boundingRect.center()); + if(dAngle != 0) + moveBy(delta.x(), delta.y()); + + //QTimer::singleShot(2000,[&](){moveBy(-delta.x(), -delta.y());}); + + //updateHandles(); + } + + //调用该组内所有item(过滤handle)相关函数 + foreach (QGraphicsItem *item, childItems()) + { + GraphicsBaseItem *baseItem = qgraphicsitem_cast(item); + if (baseItem && !qgraphicsitem_cast(baseItem)) + { + baseItem->updateCoordinate(); + } + } + m_lastBoudingRect = m_boundingRect; +} + +void GraphicsItemGroup::resize(int nHandle,double dSX, double dSY, const QPointF& basePoint) +{ + switch (nHandle) + { + case H_left: + case H_right: + dSY = 1; //拖拽的是左点右点,为水平缩放,忽略垂直变化 + break; + case H_top: + case H_bottom: + dSX = 1; //拖拽的是顶点底点,为垂直缩放,忽略水平变化 + break; + default: + break; + } + + QTransform trans; + //缩放是以图元变换原点为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回 + trans.translate(basePoint.x(), basePoint.y()); + trans.scale(dSX, dSY); + trans.translate(-basePoint.x(), -basePoint.y()); + + prepareGeometryChange(); + m_boundingRect = trans.mapRect(m_lastBoudingRect); + m_dWidth = m_boundingRect.width(); + m_dHeight = m_boundingRect.height(); + //updateHandles(); + + //调用该组内所有item(过滤handle)相关函数 + foreach (QGraphicsItem *item, childItems()) + { + GraphicsProjectModelItem *baseItem = qgraphicsitem_cast(item); + if (baseItem && !qgraphicsitem_cast(baseItem)) + { + baseItem->resize(nHandle, dSX, dSY, baseItem->mapFromParent(basePoint)); + } + } +} + +void GraphicsItemGroup::createOperationCopy() +{ + m_pOperationCopy = new QGraphicsPathItem(this->shape()); + m_pOperationCopy->setPen(Qt::DashLine); + m_pOperationCopy->setPos(this->pos()); + m_pOperationCopy->setTransformOriginPoint(this->transformOriginPoint()); + m_pOperationCopy->setTransform(this->transform()); + m_pOperationCopy->setRotation(this->rotation()); + m_pOperationCopy->setScale(this->scale()); + m_pOperationCopy->setZValue(this->zValue()); + + QGraphicsScene* scene = this->scene(); + if(scene && m_pOperationCopy) + { + scene->addItem(m_pOperationCopy); + m_movingIniPos = this->pos(); + } +} + +void GraphicsItemGroup::removeOperationCopy() +{ + QGraphicsScene* scene = this->scene(); + if(scene && m_pOperationCopy) + { + if(this->pos() != m_pOperationCopy->pos()) + this->setPos(m_pOperationCopy->pos()); //本体移动到副本的位置 + if(this->rotation() != m_pOperationCopy->rotation()) + { + double dAngle = m_pOperationCopy->rotation(); + this->setRotation(dAngle); //本体旋转至副本的角度 + //子item的旋转数据并不会和所在组同步,需要手动同步 + foreach (QGraphicsItem* item, childItems()) + { + if(qgraphicsitem_cast(item)) + continue; + + // AbstractShape* shape = qgraphicsitem_cast(item); + // if(shape && shape->getType()==T_group) + // { + // GraphicsItemGroup *group = qgraphicsitem_cast(item); + // if(group) + // group->syncRotationDataFromParent(dAngle); + // } + // else + { + GraphicsProjectModelItem *baseItem = qgraphicsitem_cast(item); + if (baseItem) + baseItem->syncRotationDataFromParent(dAngle); + } + } + } + + scene->removeItem(m_pOperationCopy); + delete m_pOperationCopy; + m_pOperationCopy = nullptr; + } +} + +void GraphicsItemGroup::moveOperationCopy(const QPointF& distance) +{ + if(m_pOperationCopy) + m_pOperationCopy->setPos(m_movingIniPos + distance); +} + +void GraphicsItemGroup::rotateOperationCopy(const double& dAngle) +{ + if(m_pOperationCopy) + { + m_pOperationCopy->setRotation(dAngle); + } + +} + +void GraphicsItemGroup::addItems(const QList& items) +{ + foreach (QGraphicsItem *item, items) + { + item->setSelected(false); + addToGroup(item); + m_listItem.push_back(item); + } + + updateCoordinate(); +} diff --git a/diagramCavas/source/graphicsItem/graphicsPolygonItem.cpp b/diagramCavas/source/graphicsItem/graphicsPolygonItem.cpp new file mode 100644 index 0000000..3d0ea46 --- /dev/null +++ b/diagramCavas/source/graphicsItem/graphicsPolygonItem.cpp @@ -0,0 +1,155 @@ +#include "graphicsItem/graphicsPolygonItem.h" +//#include "graphicsItem/itemControlHandle.h" +#include "graphicsItem/handleRect.h" +#include +#include + +GraphicPolygonItem::GraphicPolygonItem(QGraphicsItem *parent) + : GraphicsProjectModelItem(parent) +{ + m_pen = QPen(Qt::black); + m_brush = QBrush(Qt::NoBrush); +} + +GraphicPolygonItem::~GraphicPolygonItem() +{ + +} + +QPainterPath GraphicPolygonItem::shape() +{ + QPainterPath path; + path.addPolygon(m_points); + path.closeSubpath(); //将路径闭合 + return path; +} + +QRectF GraphicPolygonItem::boundingRect() +{ + //m_boundingRect = shape().controlPointRect(); //返回路径中所有点和控制点的矩形,文档介绍比返回精确边界框的boundingRect()函数计算要快 + return m_boundingRect; +} + +void GraphicPolygonItem::updateHandles() +{ + GraphicsProjectModelItem::updateHandles(); + for(int i = 0; i < m_points.size(); i++) + { + if(m_vecHanle.contains(H_edit + i -1)) + { + m_vecHanle[H_edit + i -1]->move(m_points[i].x(), m_points[i].y()); + } + } +} + +void GraphicPolygonItem::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() +{ + if (!parentItem()) + { + QPointF pt1, pt2, delta; + pt1 = mapToScene(QPointF(0, 0)); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + + prepareGeometryChange(); + //更改图形绘制节点坐标,让图形中心和原点对齐 + QPolygonF ptsOnScene = mapToScene(m_points); //所有操作都在scene坐标系下进行,然后在还原至item坐标系,否则会有跳转 + for(int i=0; isetPen(m_pen); + painter->setBrush(m_brush); + painter->drawPolygon(m_points); + + if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 + { + int nPenWidth = 1; + painter->setPen(QPen(QColor(70,70,70), nPenWidth, Qt::DashLine)); //蓝色的外框 + painter->setBrush(Qt::NoBrush); + painter->drawRect(m_boundingRect_selected); + + //绘制变换原点 + QPointF originPoint = transformOriginPoint(); + painter->setBrush(Qt::red); + painter->drawEllipse(originPoint, 4, 4); + } +} + +void GraphicPolygonItem::resize(int nHandle,double dSX, double dSY, const QPointF& basePoint) +{ + switch (nHandle) + { + case H_left: + case H_right: + dSY = 1; //拖拽的是左点右点,为水平缩放,忽略垂直变化 + break; + case H_top: + case H_bottom: + dSX = 1; //拖拽的是顶点底点,为垂直缩放,忽略水平变化 + break; + default: + break; + } + + QTransform trans; + //缩放是以图元原点(中心)位置为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回 + trans.translate(basePoint.x(), basePoint.y()); + trans.scale(dSX, dSY); + trans.translate(-basePoint.x(), -basePoint.y()); + + prepareGeometryChange(); + m_points = trans.map(m_lastPoints); + m_boundingRect = m_points.boundingRect(); + m_dWidth = m_boundingRect.width(); + m_dHeight = m_boundingRect.height(); + updateHandles(); +} + +void GraphicPolygonItem::move(const QPointF& point) +{ + moveBy(point.x(), point.y()); +} + +void GraphicPolygonItem::editShape(int nHandle,const QPointF& ptMouse) +{ + QPointF pt = mapFromScene(ptMouse); + m_points[nHandle - H_rotate_leftBottom - 1] = pt; + prepareGeometryChange(); + m_boundingRect = m_points.boundingRect(); + m_dWidth = m_boundingRect.width(); + m_dHeight = m_boundingRect.height(); + m_lastPoints = m_points; + updateHandles(); +} + +void GraphicPolygonItem::addPoint(const QPointF& point) +{ + m_points.append(mapFromScene(point)); + int nCount = m_points.count(); + //每个顶点都可以编辑,所以一个顶点需要编辑handle + ItemControlHandle* pHandle = new HandleRect(this); + pHandle->setType(T_editShape); + pHandle->setTag(H_edit + nCount - 1); + m_vecHanle.insert(H_edit + nCount - 1,pHandle); +} + +bool GraphicPolygonItem::endDrawing() +{ + bool bSuccess = true; + int nPointCount = m_points.count(); + if(nPointCount < 3) + bSuccess = false; + + return bSuccess; +} diff --git a/diagramCavas/source/graphicsItem/handleRect.cpp b/diagramCavas/source/graphicsItem/handleRect.cpp new file mode 100644 index 0000000..1f803af --- /dev/null +++ b/diagramCavas/source/graphicsItem/handleRect.cpp @@ -0,0 +1,69 @@ +#include "graphicsItem/handleRect.h" +#include + +HandleRect::HandleRect(QGraphicsItem *parent) + :ItemControlHandle(parent) +{ + +} + +HandleRect::~HandleRect() +{ +} + +QRectF HandleRect::boundingRect() const +{ + // 设置边界矩形,这里我们返回一个从(10,10)开始,宽度为100,高度为50的矩形 + return QRectF(-HNDLE_SIZE / 2, + -HNDLE_SIZE / 2, + HNDLE_SIZE, + HNDLE_SIZE); +} + +void HandleRect::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) + + painter->setPen(Qt::SolidLine); + painter->setRenderHint(QPainter::Antialiasing, true); + + if(m_type==T_resize) + { + painter->setBrush(Qt::white); + painter->drawRect(boundingRect()); + } + else if(m_type==T_rotate) + { + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::NoBrush); + painter->drawRect(boundingRect()); + } + else if(m_type==T_editShape) + { + painter->setBrush(Qt::green); + painter->drawEllipse(boundingRect().center(), HNDLE_SIZE / 2, HNDLE_SIZE / 2); + } + else if(m_type==T_lineIn) + { + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::green); + painter->drawEllipse(boundingRect().center(), HNDLE_SIZE / 2, HNDLE_SIZE / 2); + } + else if(m_type==T_lineOut) + { + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::red); + painter->drawEllipse(boundingRect().center(), HNDLE_SIZE / 2, HNDLE_SIZE / 2); + } +} + +void HandleRect::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + ItemControlHandle::hoverEnterEvent(event); +} + +void HandleRect::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + ItemControlHandle::hoverLeaveEvent(event); +} diff --git a/diagramCavas/source/graphicsItem/handleText.cpp b/diagramCavas/source/graphicsItem/handleText.cpp new file mode 100644 index 0000000..d22f748 --- /dev/null +++ b/diagramCavas/source/graphicsItem/handleText.cpp @@ -0,0 +1,82 @@ +#include "graphicsItem/handleText.h" +#include +#include +#include + +HandleText::HandleText(QGraphicsItem *parent) + : ItemControlHandle(parent) + ,_font(QFont()) + ,_proxy(nullptr) + ,_nIndex(-1) + ,_type(0) +{ + _editable = true; + _boundingRect = QRectF(-TEXT_WIDTH*0.5,-TEXT_WIDTH*0.5,TEXT_WIDTH,TEXT_HEIGHT); +} + +HandleText::~HandleText() +{ +} + +void HandleText::setText(QString str) +{ + _text = str; + QFontMetrics fontMetrics(_font); + _boundingRect = fontMetrics.boundingRect(_text); + int w = _boundingRect.width(); + int h = _boundingRect.height(); + _boundingRect.moveTo(QPointF(-w*0.5,-h*0.5)); +} + +QString HandleText::getText() const +{ + return _text; +} + +void HandleText::creatEditor() +{ + if(!_editable) + return; + if(!_proxy) + _proxy = new QGraphicsProxyWidget(this); + _proxy->setVisible(true); + QLineEdit *editor = new QLineEdit(); + editor->setText(_text); + editor->resize(boundingRect().size().toSize()*1.5); + _proxy->setWidget(editor); + + // 将文本编辑的更改连接到文本项 + connect(editor, &QLineEdit::textChanged, this, [this, editor]() { + //_text = editor->text(); + setText(editor->text()); + }); + + // 完成编辑后,删除QTextEdit + connect(editor, &QLineEdit::editingFinished, this, [this, editor]() { + //_text = editor->text(); + setText(editor->text()); + emit editFinish(editor->text()); + _proxy->setVisible(false); + }); +} + +void HandleText::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->setPen(Qt::blue); + painter->setBrush(Qt::blue); + painter->setRenderHint(QPainter::TextAntialiasing, true); + int w = _boundingRect.width(); + int h = _boundingRect.height(); + if(_type == 0) + painter->drawText(QPointF(-w*0.5,h*0.5),_text); + else{ + QString displayText = _sTagName + ":" + _text; + painter->drawText(boundingRect(), Qt::AlignCenter, displayText); + } + +} + +QRectF HandleText::boundingRect() const +{ + return _boundingRect; +} diff --git a/diagramCavas/source/graphicsItem/itemControlHandle.cpp b/diagramCavas/source/graphicsItem/itemControlHandle.cpp new file mode 100644 index 0000000..c31cf91 --- /dev/null +++ b/diagramCavas/source/graphicsItem/itemControlHandle.cpp @@ -0,0 +1,53 @@ +#include "graphicsItem/itemControlHandle.h" +#include +#include "common/frontend/graphics_items.h" + +ItemControlHandle::ItemControlHandle(QGraphicsItem *parent) + : QGraphicsItem(parent) + ,_parent(nullptr) +{ + m_type = T_resize; + m_tag = H_none; + m_enable = true; + m_ifShow = true; +} + +ItemControlHandle::~ItemControlHandle() +{ +} + +void ItemControlHandle::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) +} + +void ItemControlHandle::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + QGraphicsItem::hoverEnterEvent(event); +} + +void ItemControlHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + QGraphicsItem::hoverLeaveEvent(event); +} + +int ItemControlHandle::getSize() +{ + int nSize = HNDLE_SIZE; + return nSize; +} + +void ItemControlHandle::move(double x, double y) +{ + setPos(x, y); +} + +void ItemControlHandle::setText(QString s) +{ + +} +QString ItemControlHandle::getText() const +{ + return QString(); +} diff --git a/diagramCavas/source/graphicsItem/itemPort.cpp b/diagramCavas/source/graphicsItem/itemPort.cpp new file mode 100644 index 0000000..c6505b1 --- /dev/null +++ b/diagramCavas/source/graphicsItem/itemPort.cpp @@ -0,0 +1,68 @@ +#include +#include "graphicsItem/itemPort.h" +#include "graphicsItem/handleRect.h" + +ItemPort::ItemPort(QGraphicsItem *parent,QString uuid) + : HandleRect(parent),_uuid(uuid) +{ + _pos = P_top; + _ptr = nullptr; +} + +ItemPort::~ItemPort() +{ +} + +void ItemPort::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) + + painter->setPen(Qt::SolidLine); + painter->setRenderHint(QPainter::Antialiasing, true); + + QPolygonF Triangle; + QRectF rec = boundingRect(); + qreal left = rec.left(); + qreal right = rec.right(); + qreal width = rec.width(); + qreal height = rec.height(); + qreal bottom = rec.bottom(); + painter->setBrush(Qt::black); + if((_pos == P_top && m_type == T_lineIn) || (_pos == P_down && m_type == T_lineOut) ) //从上入或者从下出都是向下 + { + Triangle.append(QPointF(left,0)); + Triangle.append(QPointF(right,0)); + Triangle.append(QPointF(left+width*0.5,bottom)); + painter->drawPolygon(Triangle); + } + else if((_pos == P_top && m_type == T_lineOut) || (_pos == P_down && m_type == T_lineIn) ) //从上出或者从下入都是向上 + { + Triangle.append(QPointF(left,bottom)); + Triangle.append(QPointF(right,bottom)); + Triangle.append(QPointF(left+width*0.5,0)); + painter->drawPolygon(Triangle); + } + else if((_pos == P_left && m_type == T_lineIn) || (_pos == P_right && m_type == T_lineOut) ) //从左入或者从右出都是向右 + { + Triangle.append(QPointF(0,0)); + Triangle.append(QPointF(0,bottom)); + Triangle.append(QPointF(right,height*0.5)); + painter->drawPolygon(Triangle); + } + else if((_pos == P_left && m_type == T_lineOut) || (_pos == P_right && m_type == T_lineIn) ) //从左出或者从右入都是向左 + { + Triangle.append(QPointF(right,0)); + Triangle.append(QPointF(right,bottom)); + Triangle.append(QPointF(0,height*0.5)); + painter->drawPolygon(Triangle); + } + else if(m_type == T_lineInOut) + { + painter->drawEllipse(boundingRect().center(), HNDLE_SIZE / 4, HNDLE_SIZE / 4); + } + else if(m_type == T_newTral) + { + painter->drawEllipse(boundingRect().center(), HNDLE_SIZE / 4, HNDLE_SIZE / 4); + } +} diff --git a/diagramCavas/source/instance/dataAccessor.cpp b/diagramCavas/source/instance/dataAccessor.cpp new file mode 100644 index 0000000..9229697 --- /dev/null +++ b/diagramCavas/source/instance/dataAccessor.cpp @@ -0,0 +1,226 @@ +#include "instance/dataAccessor.h" +#include "communicationManager.h" +#include "uiCommunicationBus.h" +#include "configManager.h" +#include "diagramCavas.h" +#include "diagramConnectSetting.h" +#include +#include +#include +//#include "global.h" +#include "common/core_model/data_transmission.h" + +const int DIAGRAM_MAX_DATA_COUNT = 1000; + +DataAccessor::DataAccessor(QObject* parent) + : QObject(parent) + ,_parentCavas(nullptr) +{ + +} + +DataAccessor::~DataAccessor() +{ + +} + +void DataAccessor::onReceiveHttpData(const QString& sType,const QVariant& data) +{ + if(sType == "subscriptions"){ + QMap>>& tempRequest = UiCommunicationBus::instance()->getTempRequestMap(); + QJsonObject dataObj = data.toJsonObject(); + QString sClientId = dataObj.value("client_id").toString(); + + QMap lstTarget; + QJsonArray targetArr = dataObj.value("targets").toArray(); + for(const QJsonValue& value : targetArr){ + QJsonObject obj = value.toObject(); + QString sId = obj["id"].toString(); + QString sCode = obj["code"].toString(); + + qDebug() << "subscription:"+sId+"_"+sCode; + if(!lstTarget.contains(sId)){ + lstTarget.insert(sId,sCode); + } + } + + QString sAction; + auto mapSesstion = UiCommunicationBus::instance()->getSesstionMap(); + bool bClientExist = false; + for(auto& session:mapSesstion){ + if(session.first == sClientId){ //在会话列表中已存在,是stop(暂只使用stop和start) + bClientExist = true; + sAction = "stop"; + break; + } + } + + if(sAction.isEmpty()){ //不是stop的情况 + QStringList lstKeys = lstTarget.keys(); + for(auto it = tempRequest.begin(); it != tempRequest.end(); ++it){ + const QString& page = it.key(); + QList>& tempList = it.value(); + + // 从 tempList 提取所有 first 元素 + QStringList firstElements; + firstElements.reserve(tempList.size()); + for (const auto& pair : tempList) + { + firstElements.append(pair.first); + } + + // 对两个列表进行排序(因为 moveMatchingRequests 内部会排序比较) + QStringList sortedFirstElements = firstElements; + QStringList sortedLstKeys = lstKeys; + + std::sort(sortedFirstElements.begin(), sortedFirstElements.end()); + std::sort(sortedLstKeys.begin(), sortedLstKeys.end()); + + // 比较两个列表是否相同 + if (sortedFirstElements == sortedLstKeys) + { + // 调用 moveMatchingRequests + for(auto& pair:tempList){ + pair.second = "connecting"; + } + UiCommunicationBus::instance()->insertSesstionMap(sClientId, lstTarget); + sAction = "start"; + break; + } + } + } + + if(!lstTarget.isEmpty()){ + if(sAction == "start"){ + auto config = ConfigManager::instance()->getWebSocketConfig(); + QString sPre = removeAfterStreamBySplit(config.endpoint); //手动移除 + config.endpoint = sPre + "/" + sClientId; + CommunicationManager::instance()->updateWebSocketConfig(config,sClientId); + bool res = CommunicationManager::instance()->connectWebSocket(sClientId); + int a = 1; + } + else if(sAction == "stop"){ //已经停止完毕,从session中移除会话 + auto &map = UiCommunicationBus::instance()->getSesstionMap(); + for(auto iter = map.begin();iter != map.end();++iter){ + if(iter->first == sClientId){ + iter = map.erase(iter); + break; + } + } + CommunicationManager::instance()->disconnectWebSocket(sClientId); + CommunicationManager::instance()->removeChannel(sClientId); + } + } + } + else if(sType == "recommend"){ + QJsonArray dataArr = data.toJsonArray(); + for(const QJsonValue& value:dataArr){ + QJsonObject dataObj = value.toObject(); + QString input = dataObj.value("input").toString(); + int offSet = dataObj.value("offset").toInt(); + QJsonArray recommendedList = dataObj.value("recommended_list").toArray(); + + HttpRecommandInfo info; + for(const QJsonValue& value : recommendedList){ + QString content = value.toString(); + info.lstRecommand.append(content); + } + info.sInput = input.left(offSet); + info.nOffset = offSet; + if(_parentCavas){ + _parentCavas->passRecommmandHttpData(info); + } + } + } + else if(sType == "subscriptionTest"){ + qDebug()<<"receive null data"; + } + if(_parentCavas){ + auto pDlg = _parentCavas->getConnectSettingDlg(); + if(pDlg){ + QJsonObject jsonObj = data.value(); + QJsonDocument doc(jsonObj); + QString compactJson = doc.toJson(QJsonDocument::Compact); + pDlg->updateHttpLog(sType,compactJson); + } + } +} + +void DataAccessor::onReceiveWebsocketData(const QVariant& data) +{ + QJsonObject dataObj = data.toJsonObject(); + if(dataObj.contains("targets")){ + QJsonArray arrTarget = dataObj.value("targets").toArray(); + for (const QJsonValue& value : arrTarget) { + QJsonObject targetObj = value.toObject(); + QString targetId = targetObj["id"].toString(); + QJsonArray arrData = targetObj["datas"].toArray(); + + QMap newInnerMap; + for (const QJsonValue& data : arrData){ + QJsonObject dataObj = data.toObject(); + QString sTime = dataObj["time"].toString(); + double dVal = dataObj["value"].toDouble(); + bool ok = false; + quint64 value = sTime.toULongLong(&ok); + if (ok) { + newInnerMap.insert(value,dVal); + } + } + + QMutexLocker locker(&m_mutex); + auto& innerMap = _realTimeData[targetId]; // 自动创建或获取 + // 批量插入 + innerMap.insert(newInnerMap); + // 如果数量超限,从开始处批量删除 + int currentSize = innerMap.size(); + if (currentSize > DIAGRAM_MAX_DATA_COUNT) { + int toRemove = currentSize - DIAGRAM_MAX_DATA_COUNT; + auto it = innerMap.begin(); + // 批量删除最旧的toRemove个元素 + for (int i = 0; i < toRemove && it != innerMap.end(); ++i) { + it = innerMap.erase(it); + } + } + } + } + if(_parentCavas){ + auto pDlg = _parentCavas->getConnectSettingDlg(); + if(pDlg){ + QJsonObject jsonObj = data.value(); + QJsonDocument doc(jsonObj); + QString compactJson = doc.toJson(QJsonDocument::Compact); + pDlg->updateWebsocketLog(compactJson); + } + } +} + +QMap> DataAccessor::getTargetData(QStringList paraLst) +{ + QMap> mapData; + + QMutexLocker locker(&m_mutex); + for (const QString& key : paraLst) { + if (_realTimeData.contains(key)) { + mapData.insert(key, _realTimeData.value(key)); + } + } + return mapData; +} + +QString DataAccessor::removeAfterStreamBySplit(const QString& url) +{ + QStringList parts = url.split('/'); + QStringList resultParts; + + for (int i = 0; i < parts.size(); i++) { + if (parts[i] == "stream") { + // 找到 "stream" 后,停止添加后续部分 + resultParts.append("stream"); + break; + } + resultParts.append(parts[i]); + } + + return resultParts.join('/'); +} diff --git a/diagramCavas/source/itemPropertyDlg.cpp b/diagramCavas/source/itemPropertyDlg.cpp new file mode 100644 index 0000000..4fb941c --- /dev/null +++ b/diagramCavas/source/itemPropertyDlg.cpp @@ -0,0 +1,281 @@ +#include +#include +#include "itemPropertyDlg.h" +#include "propertyContentDlg.h" +#include "dataManager.h" +#include "graphicsItem/functionModelItem/graphicsFunctionModelItem.h" +#include "ui_itemPropertyDlg.h" +#include "baseContentDlg.h" +#include "baseInfoDlg.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "basePropertyManager.h" +#include "baseProperty.h" +#include "ptExtraInfoDlg.h" +#include "ctExtraInfoDlg.h" +#include "bayInfoDlg.h" + +ItemPropertyDlg::ItemPropertyDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::itemPropertyDlg) + ,layout_(nullptr) + ,btnGroup_(nullptr) + ,_curItem(nullptr) + ,_curModelController(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint |Qt::WindowStaysOnTopHint | windowFlags()); + initial(); +} + +ItemPropertyDlg::~ItemPropertyDlg() +{ + delete ui; +} + +void ItemPropertyDlg::initial() +{ + layout_ = new QVBoxLayout(ui->widget_button); + btnGroup_ = new QButtonGroup(this); + connect(ui->btn_ok,&QPushButton::clicked,this,&ItemPropertyDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&ItemPropertyDlg::onCancelClicked); +} + +void ItemPropertyDlg::loadGroupButton(QMap map) +{ + groupInfo_ = map; + for(auto &info:map) + { + QPushButton* btn = new QPushButton(ui->widget_button); + btn->setText(info.groupName); + btn->setCheckable(true); + btnGroup_->addButton(btn); + btnMap_.insert(info.groupName, btn); + if(info.isPublic) //公共属性组置顶 + layout_->insertWidget(0,btn); + else + layout_->addWidget(btn); + + connect(btn, &QAbstractButton::clicked, [this, info](){ + onGroupSelected(info.groupName); + }); + } + layout_->addStretch(); +} + +void ItemPropertyDlg::onOkClicked() +{ + //todo:将属性页中的值读取到当前uuid对象 + if(_curItem) + { + if(_curItem->getProperty() == nullptr) + { + //todo:弹出输入框输入名称 + QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"对象未命名")); + return; + } + else + { + curUuid_ = _curItem->getProperty()->uuid(); //使用数据uuid,数据唯一,item不唯一 + } + } + QMap::Iterator iter; + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(curUuid_); + int nType = pPro->type(); //操作的对象类型 + for(iter = groupViews_.begin();iter != groupViews_.end();++iter) + { + BaseContentDlg* pDlg = qobject_cast(iter.value()); + QMap mapPro = pDlg->getPropertyValue(pPro); + if(!mapPro.empty()) + DataManager::instance().updateModelData(_curModel,curUuid_,iter.key(),mapPro); + + if(nType == 4 && iter.key() == "base_extend"){ //ct + int nCtSize = 0; //ct设置的个数 + int nCtType = 0; //ct类型 1三相0零相 + if(mapPro.contains("ct_winding")){ + PropertyStateInfo val = mapPro.value("ct_winding"); + QJsonObject obj = val.defaultValue.toJsonObject(); + if(obj.contains("winding")){ + QJsonArray arr = obj["winding"].toArray(); + nCtSize = arr.size(); + } + } + if(mapPro.contains("phase_num")){ + PropertyStateInfo val = mapPro.value("phase_num"); + nCtType = val.defaultValue.toInt(); + } + + QPair pair(nCtType,nCtSize); + QVariant var = QVariant::fromValue(pair); + _curItem->setupFinish(var); + } + else if(nType == 5 && iter.key() == "base_extend"){ //pt + QList lst; //二次绕组接法 + if(mapPro.contains("pt_sec_winding")){ + PropertyStateInfo val = mapPro.value("pt_sec_winding"); + QJsonObject obj = val.defaultValue.toJsonObject(); + QJsonArray arr = obj["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + QJsonObject node = jsonObj.toObject(); + QString sWinding = node["star"].toString(); + if(sWinding == "Yn"){ + lst.append(1); + } + else{ + lst.append(0); + } + } + + QVariant var = QVariant::fromValue(lst); + _curItem->setupFinish(var); + } + } + } + _curGroup = ""; + _curItem = nullptr; + _curModelController->setCurItemPropertyDlg(nullptr); + hide(); +} + +void ItemPropertyDlg::onCancelClicked() +{ + _curGroup = ""; + _curModelController->setCurItemPropertyDlg(nullptr); + hide(); +} + +void ItemPropertyDlg::onGroupSelected(const QString& str) +{ + if (!groupViews_.contains(str)) { + createGroupView(str); + } + + BaseContentDlg* pDlg = qobject_cast(groupViews_[str]); + if(pDlg) + { + QMap valueMap; + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(curUuid_); + if(str != QString("component") && str != QString("bay")) + { + if(groupValue_[str].mapInfo.contains(curUuid_)) + valueMap = groupValue_[str].mapInfo[curUuid_]; + else //没有值时使用默认值 + valueMap = groupInfo_[str].info; + pDlg->setPropertyValue(QVariant::fromValue(valueMap)); + } + else{ + pDlg->setPropertyValue(QVariant::fromValue(static_cast(pPro))); + bool bWindExist = false; + if(pPro->type() == 4 || pPro->type() == 5){ //ct和pt情况,bay组需要跨界面使用extend信息 + if(groupValue_.contains("base_extend")){ //实时数据中已包含(已设置) + if(groupValue_["base_extend"].mapInfo.contains(curUuid_)){ + QMap extendMap = groupValue_["base_extend"].mapInfo.value(curUuid_); + if(pPro->type() == 4){ + if(extendMap.contains("ct_winding")){ + PropertyStateInfo val = extendMap.value("ct_winding"); + pDlg->setExtendProperty(val); + bWindExist = true; + } + } + else if(pPro->type() == 5){ + if(extendMap.contains("pt_sec_winding")){ + PropertyStateInfo val = extendMap.value("pt_sec_winding"); + pDlg->setExtendProperty(val); + bWindExist = true; + } + } + } + } + + if(!bWindExist){ + if(groupViews_.contains("base_extend")){ //实时数据未包含,从extend界面读取 + BaseContentDlg* pExtendDlg = qobject_cast(groupViews_.value("base_extend")); + QMap mapPro = pExtendDlg->getPropertyValue(pPro); + + if(pPro->type() == 4 ){ //ct + if(mapPro.contains("ct_winding")){ + PropertyStateInfo val = mapPro.value("ct_winding"); + pDlg->setExtendProperty(val); + } + } + else if(pPro->type() == 5){ //pt + if(mapPro.contains("pt_sec_winding")){ + PropertyStateInfo val = mapPro.value("pt_sec_winding"); + pDlg->setExtendProperty(val); + } + } + } + } + } + } + } + + ui->stackedWidget->setCurrentWidget(groupViews_[str]); + _curGroup = str; +} + + +void ItemPropertyDlg::createGroupView(const QString& str) +{ + //todo:基础信息、间隔信息、动态界面在此分支 + BaseContentDlg* contentDlg = nullptr; + BaseProperty* pPro = BasePropertyManager::instance().findEntityData(curUuid_); + if(str == QString("component") || str == QString::fromWCharArray(L"基本信息")){ + auto pDlg = new BaseInfoDlg(ui->stackedWidget); + pDlg->setParentDlg(this); + contentDlg = pDlg; + } + else if(str == "base_extend" || str == QString::fromWCharArray(L"扩展信息")){ + if(pPro->type() == 4) + contentDlg = new CtExtraInfoDlg(ui->stackedWidget); + else if(pPro->type() == 5) + contentDlg = new PtExtraInfoDlg(ui->stackedWidget); + else + contentDlg = new PropertyContentDlg(ui->stackedWidget); + } + else if(str == "bay" || str == QString::fromWCharArray(L"间隔信息")){ + contentDlg = new BayInfoDlg(ui->stackedWidget); + } + else{ + contentDlg = new PropertyContentDlg(ui->stackedWidget); + } + + if(contentDlg) + { + contentDlg->createGroupView(groupInfo_[str]); + } + + contentDlg->setModelController(_curModelController); //传递modelControl + groupViews_.insert(str, contentDlg); + ui->stackedWidget->addWidget(contentDlg); +} + +void ItemPropertyDlg::onHttpDataUpdated(HttpRecommandInfo info) +{ + if(_curGroup == "bay"){ + auto pWidget = groupViews_[_curGroup]; + auto pBay = dynamic_cast(pWidget); + if(pBay){ + pBay->onHttpDataUpdated(info); + } + } +} + +void ItemPropertyDlg::showDlg(ModelDataInfo dataInfo,QUuid uuid,GraphicsFunctionModelItem* pItem) +{ + groupValue_ = dataInfo.groupInfo; + curUuid_ = uuid; + _curModel = dataInfo.modelName; + _curItem = pItem; + + QString str; + auto btn = dynamic_cast(btnGroup_->checkedButton()); + if(btn == nullptr){ + str = groupInfo_.constBegin().key(); //打开默认显示第一个属性组 + } + else{ + str = btn->text(); + } + onGroupSelected(str); + show(); +} diff --git a/diagramCavas/source/loadMonitorPageDlg.cpp b/diagramCavas/source/loadMonitorPageDlg.cpp new file mode 100644 index 0000000..0f8c0d6 --- /dev/null +++ b/diagramCavas/source/loadMonitorPageDlg.cpp @@ -0,0 +1,105 @@ +#include "loadMonitorPageDlg.h" +#include "ui_loadMonitorPageDlg.h" +//#include "global.h" +#include "common/core_model/diagram.h" +#include "tools.h" +#include + +LoadMonitorPageDlg::LoadMonitorPageDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::loadMonitorPageDlg) + ,_pModel(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +LoadMonitorPageDlg::~LoadMonitorPageDlg() +{ + delete ui; +} + +void LoadMonitorPageDlg::initial() +{ + _pModel = new QStandardItemModel(this); + ui->treeView->setModel(_pModel); + ui->treeView->setHeaderHidden(true); + + connect(ui->btn_ok,&QPushButton::clicked,this,&LoadMonitorPageDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&LoadMonitorPageDlg::onCancelClicked); +} + +void LoadMonitorPageDlg::updateItems(QString sProj,QPair pair) +{ + QStandardItem* topLevelItem = findTopLevelItem(sProj); + if (topLevelItem) { + // 如果存在,直接在该项下插入子项 + QStandardItem* childItem = new QStandardItem(pair.first); + childItem->setData(pair.second); + topLevelItem->appendRow(childItem); + } else { + // 如果不存在,创建新的顶层项并插入子项 + QStandardItem* newTopLevelItem = new QStandardItem(sProj); + QStandardItem* childItem = new QStandardItem(pair.first); + childItem->setData(pair.second); + newTopLevelItem->appendRow(childItem); + _pModel->appendRow(newTopLevelItem); + } + ui->treeView->expandAll(); +} + +void LoadMonitorPageDlg::clearItems() +{ + if(_pModel){ + QStandardItem *root = _pModel->invisibleRootItem(); //先清空model + int rowCount = root->rowCount(); + if (rowCount > 0) { + _pModel->removeRows(0, rowCount); + } + } +} + +void LoadMonitorPageDlg::onOkClicked() +{ + QItemSelectionModel* selectionModel = ui->treeView->selectionModel(); + if (selectionModel && selectionModel->hasSelection()) { + // 获取当前选中的索引 + QModelIndex currentIndex = selectionModel->currentIndex(); + if (currentIndex.isValid()) { + QStandardItem* item = _pModel->itemFromIndex(currentIndex); + int nLevel = getLevel(item); + if(nLevel == 0 || nLevel == -1) //顶层不响应 + return; + QStandardItem* parent = item->parent(); + if(item) + { + DiagramInfo info; + info.id = item->data(Qt::UserRole+1).toUuid(); + info.sName = item->text(); + info.sBasePageName = parent->text(); + emit monitorSelected(info); + } + hide(); + } + } + + +} + +void LoadMonitorPageDlg::onCancelClicked() +{ + hide(); +} + +QStandardItem* LoadMonitorPageDlg::findTopLevelItem(const QString& name) +{ + for (int i = 0; i < _pModel->rowCount(); ++i) { + QStandardItem* item = _pModel->item(i); + if (item && item->text() == name) { + return item; + } + } + return nullptr; +} + diff --git a/diagramCavas/source/measureSettingDlg.cpp b/diagramCavas/source/measureSettingDlg.cpp new file mode 100644 index 0000000..2e1ac9f --- /dev/null +++ b/diagramCavas/source/measureSettingDlg.cpp @@ -0,0 +1,680 @@ +#include +#include +#include "measureSettingDlg.h" +#include "bayInfoDlg.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "bayMeasureDlg.h" +#include "baseProperty.h" +#include "basePropertyManager.h" +#include "ui_measureSettingDlg.h" + +MeasureSettingDlg::MeasureSettingDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::measureSettingDlg) + ,_pBayComponent(nullptr) + ,_pBayMeasure(nullptr) + ,_pEventStrategy(nullptr) + ,_pEventYXGroup(nullptr) + ,_curMode(0) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + setStyleSheet("background-color: white;"); + initial(); +} + +MeasureSettingDlg::~MeasureSettingDlg() +{ + delete ui; +} + +void MeasureSettingDlg::initial() +{ + _pEventStrategy = new QButtonGroup(this); + _pEventStrategy->addButton(ui->rb_eventOff,0); + _pEventStrategy->addButton(ui->rb_eventOn,1); + + _pEventYXGroup = new QButtonGroup(this); + _pEventYXGroup->addButton(ui->rb_yxDown,0); + _pEventYXGroup->addButton(ui->rb_yxRise,1); + + connect(_pEventStrategy,&QButtonGroup::idClicked,this,&MeasureSettingDlg::onEventStrategyChange); + + connect(ui->btn_ok,&QPushButton::clicked,this,&MeasureSettingDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&MeasureSettingDlg::onCancelClicked); + connect(ui->btn_addPara,&QPushButton::clicked,this,&MeasureSettingDlg::onAddParaClicked); + connect(ui->btn_delPara,&QPushButton::clicked,this,&MeasureSettingDlg::onDelParaClicked); + connect(ui->cb_tag,&QComboBox::textActivated,this,&MeasureSettingDlg::onTagChanged); + connect(ui->cb_name,&QComboBox::textActivated,this,&MeasureSettingDlg::onNameChanged); + + connect(ui->cb_rule,&QComboBox::currentIndexChanged, this,&MeasureSettingDlg::onRuleIndexChanged); + connect(ui->cb_type,&QComboBox::currentIndexChanged, this,&MeasureSettingDlg::onTypeIndexChanged); + + // 设置正则验证器:1-5000整数 + QRegularExpression regExp("^(?:[1-9]|[1-9]\\d{1,2}|[1-4]\\d{3}|5000)$"); + QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp,ui->le_size); + ui->le_size->setValidator(validator); + + ui->cb_rule->setItemData(0, 1); + ui->cb_rule->setItemData(1, 2); + + ui->gb_yx->setVisible(false); +} + +void MeasureSettingDlg::showDlg(int type,PropertyStateInfo proInfo,bool isDouble) +{ + ui->label_wind->setVisible(false); + ui->cb_windIndex->setVisible(false); + //setDbCheckVisible(false); + if(isDouble) + setDbTagVisible(true); + else + setDbTagVisible(false); + _isDouble = isDouble; + if(type == 4){ //ct,显示index选择 + ui->label_wind->setVisible(true); + ui->cb_windIndex->setVisible(true); + ui->cb_windIndex->clear(); + + QMap map; + + /*QString jsonString = proInfo.defaultValue.toString(); + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8().data()); + QJsonObject obj = jsonDocument.object();*/ + + QJsonObject obj = safeToJsonObject(proInfo.defaultValue); + + if(obj.contains("winding")){ + QJsonArray arr = obj["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + CtExtraInfo info; + QJsonObject node = jsonObj.toObject(); + info.index = node["index"].toInt(); + info.scope = node["scope"].toString(); + info.accuracy= node["accuracy"].toString(); + info.volume = node["volume"].toString(); + info.ratio = node["ratio"].toDouble(); + info.polarity= node["polarity"].toBool(); + map.insert(info.index,info); + + ui->cb_windIndex->addItem(QString::number(info.index)); + } + _tempCtMap = map; + } + } + else if(type == 5){ //pt,显示index选择 + ui->label_wind->setVisible(true); + ui->cb_windIndex->setVisible(true); + + ui->cb_windIndex->clear(); + QMap map; + + /*QString jsonString = proInfo.defaultValue.toString(); + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8().data()); + QJsonObject obj = jsonDocument.object();*/ + QJsonObject obj = safeToJsonObject(proInfo.defaultValue); + + if(obj.contains("winding")){ + QJsonArray arr = obj["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + PtExtraInfo info; + QJsonObject node = jsonObj.toObject(); + info.index = node["index"].toInt(); + info.scope = node["scope"].toString(); + info.accuracy= node["accuracy"].toString(); + info.volume = node["volume"].toString(); + info.star = node["star"].toString(); + info.ratio = node["ratio"].toDouble(); + info.polarity= node["polarity"].toBool(); + map.insert(info.index,info); + + ui->cb_windIndex->addItem(QString::number(info.index)); + } + _tempPtMap = map; + } + } + ui->cb_windIndex->setCurrentIndex(0); + _curComponentType = type; + _curMode = 0; + clearData(); + QStringList lstTag; + QStringList lstName; + show(); + if(_nParentType == 0){ + if(_pBayComponent){ + QString curItemName; //当前元件名称 + auto pItemData = _pBayComponent->getProperty(); + curItemName = pItemData->name(); + auto lst = _pBayComponent->getValidType(); + for(auto& item:lst){ + item.tag = item.tag+"_"+curItemName; + item.name = item.name+"_"+curItemName; + lstTag.append(item.tag); + lstName.append(item.name); + } + ui->cb_tag->addItems(lstTag); + ui->cb_name->addItems(lstName); + + BaseProperty* pro = _pBayComponent->getProperty(); + if(pro){ + ui->le_s1->setText(pro->station()); + ui->le_s2->setText(pro->station()); + } + } + } + else if(_nParentType == 1){ + QString curBayName; + if(_pBayMeasure){ + auto pItemBay = _pBayMeasure->getBayProperty(); + curBayName = pItemBay->name(); + + auto lst = _pBayMeasure->getValidType(); + for(auto& item:lst){ + item.tag = item.tag+"_"+curBayName; + item.name = item.name+"_"+curBayName; + lstTag.append(item.tag); + lstName.append(item.name); + } + ui->cb_tag->addItems(lstTag); + ui->cb_name->addItems(lstName); + + BayProperty* pro = _pBayMeasure->getBayProperty(); //间隔不直接包含站,找到间隔所属设备的站 + if(pro){ + QList lst = pro->getLstComponent(); + QString sStation; + if(!lst.isEmpty()){ + auto pPro = BasePropertyManager::instance().findEntityData(lst.first()); + if(pPro){ + sStation = pPro->station(); + } + } + ui->le_s1->setText(sStation); + ui->le_s2->setText(sStation); + } + } + } +} + +void MeasureSettingDlg::showDlg(MeasurementInfo info,PropertyStateInfo proInfo,bool isDouble,MeasurementInfo symmetryInfo) +{ + ui->label_wind->setVisible(false); + ui->cb_windIndex->setVisible(false); + if(isDouble){ + setDbTagVisible(true); + ui->le_dbTag->setText(symmetryInfo.tag); + ui->le_dbName->setText(symmetryInfo.name); + ui->le_dbTag->setReadOnly(true); + ui->le_dbName->setReadOnly(true); + } + else + setDbTagVisible(false); + _isDouble = isDouble; + if(info.sWindType == "ct"){ //ct,显示index选择 + ui->label_wind->setVisible(true); + ui->cb_windIndex->setVisible(true); + ui->cb_windIndex->clear(); + QMap map; + + QJsonObject obj = safeToJsonObject(proInfo.defaultValue); + + if(obj.contains("winding")){ + QJsonArray arr = obj["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + CtExtraInfo info; + QJsonObject node = jsonObj.toObject(); + info.index = node["index"].toInt(); + info.scope = node["scope"].toString(); + info.accuracy= node["accuracy"].toString(); + info.volume = node["volume"].toString(); + info.ratio = node["ratio"].toDouble(); + info.polarity= node["polarity"].toBool(); + map.insert(info.index,info); + + ui->cb_windIndex->addItem(QString::number(info.index)); + } + ui->cb_windIndex->setCurrentText(QString::number(info.nIndex)); + _tempCtMap = map; + } + } + else if(info.sWindType == "pt"){ //pt,显示index选择 + ui->label_wind->setVisible(true); + ui->cb_windIndex->setVisible(true); + + ui->cb_windIndex->clear(); + QMap map; + + QJsonObject obj = safeToJsonObject(proInfo.defaultValue); + + if(obj.contains("winding")){ + QJsonArray arr = obj["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + PtExtraInfo info; + QJsonObject node = jsonObj.toObject(); + info.index = node["index"].toInt(); + info.scope = node["scope"].toString(); + info.accuracy= node["accuracy"].toString(); + info.volume = node["volume"].toString(); + info.star = node["star"].toString(); + info.ratio = node["ratio"].toDouble(); + info.polarity= node["polarity"].toBool(); + map.insert(info.index,info); + + ui->cb_windIndex->addItem(QString::number(info.index)); + } + ui->cb_windIndex->setCurrentText(QString::number(info.nIndex)); + _tempPtMap = map; + } + } + + if(info.sWindType == "ct"){ + _curComponentType = 4; + } + else if(info.sWindType == "pt"){ + _curComponentType = 5; + } + + show(); + _curMode = 1; + clearData(); + ui->cb_tag->blockSignals(true); + ui->cb_name->blockSignals(true); + ui->cb_tag->addItem(info.tag); + ui->cb_name->addItem(info.name); + ui->cb_tag->setCurrentText(info.tag); + ui->cb_name->setCurrentText(info.name); + ui->cb_type->setCurrentIndex(info.type); + ui->le_size->setText(QString::number(info.size)); + + if(info.nSource == 1){ //3611 + ui->cb_rule->setCurrentIndex(0); + ui->le_s1->setText(info.sStation); + ui->le_d1->setText(info.sDevice); + if(info.type == 0){ //遥测 + ui->cb_channelYC->setCurrentText(info.sChannel); + } + else if(info.type == 1){ //遥信 + ui->cb_channelYX->setCurrentText(info.sChannel); + } + else if(info.type == 2){ //遥控 + ui->cb_channelYK->setCurrentText(info.sChannel); + } + } + else if(info.nSource == 2){ //104 + ui->cb_rule->setCurrentIndex(1); + ui->le_s2->setText(info.sStation); + ui->le_packet->setText(QString::number(info.nPacket)); + ui->le_offset->setText(QString::number(info.nOffset)); + } + + if(info.bEnable){ + ui->rb_eventOn->setChecked(true); + } + else{ + ui->rb_eventOff->setChecked(true); + } + ui->le_measure->setText(ui->cb_type->currentText()); + if(info.type == 0){ //遥测 + if(info.mapTE.contains("upup")){ + ui->checkBox_upup->setChecked(true); + ui->dbsb_upup->setValue(info.mapTE["upup"]); + } + else{ + ui->checkBox_upup->setChecked(false); + } + + if(info.mapTE.contains("up")){ + ui->checkBox_up->setChecked(true); + ui->dbsb_up->setValue(info.mapTE["up"]); + } + else{ + ui->checkBox_up->setChecked(false); + } + + if(info.mapTE.contains("down")){ + ui->checkBox_down->setChecked(true); + ui->dbsb_down->setValue(info.mapTE["down"]); + } + else{ + ui->checkBox_down->setChecked(false); + } + + if(info.mapTE.contains("downdown")){ + ui->checkBox_downdown->setChecked(true); + ui->dbsb_downdown->setValue(info.mapTE["downdown"]); + } + else{ + ui->checkBox_downdown->setChecked(false); + } + } + else if(info.type == 1){ //遥信 + if(info.sEdge == "raising"){ //上升沿 + ui->rb_yxRise->setChecked(true); + } + else if(info.sEdge == "falling"){ //上升沿 + ui->rb_yxDown->setChecked(true); + } + } + + ui->cb_command->setCurrentText(info.sCommand); + for(auto ¶:info.lstParameter){ + ui->lst_parameter->addItem(para); + } +} + +void MeasureSettingDlg::clearData() +{ + if(ui->cb_tag->count()) + ui->cb_tag->clear(); + if(ui->cb_name->count()) + ui->cb_name->clear(); + ui->le_size->clear(); + ui->cb_type->setCurrentIndex(0); + ui->cb_rule->setCurrentIndex(0); + ui->rb_eventOff->setChecked(true); + ui->checkBox_upup->setChecked(false); + ui->checkBox_up->setChecked(false); + ui->checkBox_down->setChecked(false); + ui->checkBox_downdown->setChecked(false); + ui->dbsb_upup->clear(); + ui->dbsb_up->clear(); + ui->dbsb_down->clear(); + ui->dbsb_downdown->clear(); + ui->cb_command->setCurrentIndex(0); + ui->le_parameter->clear(); + ui->lst_parameter->clear(); +} + +/*void MeasureSettingDlg::setDbCheckVisible(bool val) +{ + if(val){ + ui->label_dbCheck->setVisible(true); + ui->cB_dbCheck->setVisible(true); + } + else{ + ui->label_dbCheck->setVisible(false); + ui->cB_dbCheck->setVisible(false); + } +}*/ + +void MeasureSettingDlg::setDbTagVisible(bool val) +{ + ui->le_dbTag->clear(); + ui->le_dbName->clear(); + if(val){ + ui->label_dbTag->setVisible(true); + ui->le_dbTag->setVisible(true); + ui->label_dbName->setVisible(true); + ui->le_dbName->setVisible(true); + } + else{ + ui->label_dbTag->setVisible(false); + ui->le_dbTag->setVisible(false); + ui->label_dbName->setVisible(false); + ui->le_dbName->setVisible(false); + } +} + +QJsonObject MeasureSettingDlg::safeToJsonObject(const QVariant& var) { + switch (var.typeId()) { + case QMetaType::QJsonObject: + return var.toJsonObject(); + case QMetaType::QString: { + QJsonParseError error; + auto doc = QJsonDocument::fromJson( + var.toString().toUtf8(), + &error + ); + if (error.error == QJsonParseError::NoError) + return doc.object(); + break; + } + default: + break; + } + return QJsonObject(); +} + +void MeasureSettingDlg::onOkClicked() +{ + MeasurementInfo info; + info.tag = ui->cb_tag->currentText(); + info.name = ui->cb_name->currentText(); + info.type = ui->cb_type->currentIndex(); + info.size = ui->le_size->text().toInt(); + + info.nSource = ui->cb_rule->currentData().toInt(); + if(info.nSource == 1){ //cl3611 + info.sStation = ui->le_s1->text(); + info.sDevice = ui->le_d1->text(); + } + else if(info.nSource == 2){ + info.sStation = ui->le_s2->text(); + } + if(info.type == 0) + info.sChannel = ui->cb_channelYC->currentText(); + else if(info.type == 1) + info.sChannel = ui->cb_channelYX->currentText(); + else if(info.type == 2) + info.sChannel = ui->cb_channelYK->currentText(); + info.nPacket = ui->le_packet->text().toInt(); //包号 + info.nOffset = ui->le_offset->text().toInt(); //偏移量 + if(_pEventStrategy){ //事件策略开关 + info.bEnable = _pEventStrategy->checkedId(); + } + + if(ui->checkBox_upup->isChecked()){ + info.mapTE.insert("upup",ui->dbsb_upup->value()); + } + if(ui->checkBox_up->isChecked()){ + info.mapTE.insert("up",ui->dbsb_up->value()); + } + if(ui->checkBox_downdown->isChecked()){ + info.mapTE.insert("downdown",ui->dbsb_downdown->value()); + } + if(ui->checkBox_down->isChecked()){ + info.mapTE.insert("down",ui->dbsb_down->value()); + } + + if(_pEventYXGroup->checkedId() == 0){ + info.sEdge = "falling"; + } + else if(_pEventYXGroup->checkedId() == 1){ + info.sEdge = "raising"; + } + info.sCommand = ui->cb_command->currentText(); + for(int i = 0;i < ui->lst_parameter->count();++i){ + QListWidgetItem *item = ui->lst_parameter->item(i); + info.lstParameter.append(item->text()); + } + + if(_curComponentType == 4 || _curComponentType == 5){ + int nNumber; + QString sNumber = ui->cb_windIndex->currentText(); + nNumber = sNumber.isEmpty()?-1:sNumber.toInt(); + if(nNumber != -1){ + if(_curComponentType == 4){ //ct + info.sWindType = "ct"; + info.nIndex = nNumber; + info.nRatio = _tempCtMap.value(nNumber).ratio; + info.nPolarity = _tempCtMap.value(nNumber).polarity; + } + else if(_curComponentType == 5){ //pt + info.sWindType = "pt"; + info.nIndex = nNumber; + info.nRatio = _tempPtMap.value(nNumber).ratio; + info.nPolarity = _tempPtMap.value(nNumber).polarity; + } + } + } + if(_nParentType == 0){ + if(_pBayComponent){ + if(_isDouble) + info.sSymmetry = ui->le_dbName->text(); //与double互相记录 + _pBayComponent->addMeasure(info,_curMode); + } + + if(_isDouble){ + MeasurementInfo dbInfo; + dbInfo = info; + dbInfo.tag = ui->le_dbTag->text(); + dbInfo.name = ui->le_dbName->text(); + dbInfo.nPolarity = -info.nPolarity; + dbInfo.sSymmetry = info.name; //与source互相记录 + + if(_pBayComponent) + _pBayComponent->addMeasure(dbInfo,_curMode); + } + } + else if(_nParentType == 1){ + if(_pBayMeasure){ + _pBayMeasure->addMeasure(info,_curMode); + } + } + + ui->cb_tag->blockSignals(false); + ui->cb_name->blockSignals(false); + ui->le_dbTag->setReadOnly(false); + ui->le_dbName->setReadOnly(false); + hide(); +} + +void MeasureSettingDlg::onCancelClicked() +{ + ui->cb_tag->blockSignals(false); + ui->cb_name->blockSignals(false); + hide(); +} + +void MeasureSettingDlg::onTagChanged(const QString& str) +{ + QList lst; + QString curName; + if(_nParentType == 0){ + if(_pBayComponent){ + lst = _pBayComponent->getValidType(); + auto pItemData = _pBayComponent->getProperty(); + curName = "_"+pItemData->name(); + } + } + else if(_nParentType == 1){ + if(_pBayMeasure){ + lst = _pBayMeasure->getValidType(); + auto pItemBay = _pBayMeasure->getBayProperty(); + curName = "_"+pItemBay->name(); + } + } + + QString name; + for(auto& item:lst){ + if(item.tag+curName == str){ + name = item.name+curName; + ui->cb_name->setCurrentText(name); + break; + } + } + + if(_isDouble){ + ui->le_dbTag->setText(str+"double"); + ui->le_dbName->setText(name+"对称"); + } +} + +void MeasureSettingDlg::onNameChanged(const QString& str) +{ + QList lst; + QString curName; + if(_nParentType == 0){ + if(_pBayComponent){ + lst = _pBayComponent->getValidType(); + auto pItemData = _pBayComponent->getProperty(); + curName = "_"+pItemData->name(); + } + } + else if(_nParentType == 1){ + if(_pBayMeasure){ + lst = _pBayMeasure->getValidType(); + auto pItemBay = _pBayMeasure->getBayProperty(); + curName = "_"+pItemBay->name(); + } + } + + QString tag; + for(auto& item:lst){ + if(item.name+curName == str){ + tag = item.tag+curName; + ui->cb_tag->setCurrentText(tag); + break; + } + } + + if(_isDouble){ + ui->le_dbTag->setText(tag+"double"); + ui->le_dbName->setText(str+"对称"); + } +} + +void MeasureSettingDlg::onRuleIndexChanged(int n) +{ + ui->sw_type->setCurrentIndex(n); +} + +void MeasureSettingDlg::onTypeIndexChanged(int n) +{ + switch (n) { + case 0: //遥测 + ui->sw_channel->setCurrentIndex(0); + ui->gb_yc->setVisible(true); + ui->gb_yx->setVisible(false); + break; + case 1: //遥信 + ui->sw_channel->setCurrentIndex(1); + ui->gb_yc->setVisible(false); + ui->gb_yx->setVisible(true); + break; + case 2: //遥控 + ui->sw_channel->setCurrentIndex(2); + ui->gb_yc->setVisible(false); + ui->gb_yx->setVisible(false); + break; + default: + ui->gb_yc->setVisible(false); + ui->gb_yx->setVisible(false); + break; + } + ui->le_measure->setText(ui->cb_type->currentText()); +} + +void MeasureSettingDlg::onAddParaClicked() +{ + QString str = ui->le_parameter->text(); + auto lst = ui->lst_parameter->findItems(str,Qt::MatchExactly); + if(lst.isEmpty()){ //列表中不存在 + ui->lst_parameter->addItem(str); + } +} + +void MeasureSettingDlg::onDelParaClicked() +{ + auto pItem = ui->lst_parameter->takeItem(ui->lst_parameter->currentRow()); + if(pItem) + delete pItem; +} + +void MeasureSettingDlg::onEventStrategyChange(int n) +{ + if(n == 0){ //不启用 + ui->sw_event->setCurrentIndex(0); + } + else if(n == 1){ //启用 + ui->sw_event->setCurrentIndex(1); + } +} + +void MeasureSettingDlg::onHttpDataUpdated(HttpRecommandInfo info) +{ + // +} diff --git a/diagramCavas/source/monitorAttributeDlg.cpp b/diagramCavas/source/monitorAttributeDlg.cpp new file mode 100644 index 0000000..d0f0705 --- /dev/null +++ b/diagramCavas/source/monitorAttributeDlg.cpp @@ -0,0 +1,74 @@ +#include "monitorAttributeDlg.h" +#include "monitorAttributeGroupDlg.h" +#include "monitorPanel.h" +#include "monitorToolBox.h" +#include "monitorSideBarDlg.h" +#include +#include +//#include "global.h" + +MonitorAttributeDlg::MonitorAttributeDlg(QWidget* parent) + : QDialog(parent) + ,_pLayout(nullptr) + ,_pBox(nullptr) + ,_pParent(nullptr) +{ + _pParent = dynamic_cast(parent); + initial(); +} + +MonitorAttributeDlg::~MonitorAttributeDlg() +{ + +} + +void MonitorAttributeDlg::initial() +{ + // 创建标题栏 + QWidget *titleBar = new QWidget(this); + titleBar->setFixedHeight(21); + titleBar->setStyleSheet("background-color: #2b579a; color: white;"); + + // 标题栏布局 + QHBoxLayout *titleLayout = new QHBoxLayout(titleBar); + titleLayout->setContentsMargins(10, 0, 5, 0); + + // 标题标签 + QLabel *titleLabel = new QLabel("对象属性"); + titleLabel->setStyleSheet("color: white; font-weight: bold;"); + + titleLayout->addWidget(titleLabel); + + _pLayout = new QVBoxLayout(this); + _pBox = new MonitorToolBox(this); + _pBox->setContentsMargins(0, 0, 0, 0); + _pLayout->addWidget(titleBar); + _pLayout->addWidget(_pBox); +} + +void MonitorAttributeDlg::generateAttributeGroups(QUuid uid) +{ + QMap> mapLst; + auto mapPara = getParent()->getParent()->getModelController()->getMonitorPara(); + if(mapPara.contains(uid)){ + auto lst = mapPara[uid]; + for(auto &info:lst){ + if(info.bSelected) + mapLst[info.sGroup].append(info); + } + + for(auto iter = mapLst.begin(); iter != mapLst.end();++iter){ + MonitorAttributeGroupDlg* pDlg = new MonitorAttributeGroupDlg(); + pDlg->setParent(this); + pDlg->createGroupView(iter.value()); + connect(_pParent->getParent()->getModelController(),&FixedPortsModel::dataUpdated,pDlg,&MonitorAttributeGroupDlg::updateData); + _pBox->addWidget(iter.key(),pDlg); + } + _curId = uid; + } +} + +void MonitorAttributeDlg::clearAllGroup() +{ + _pBox->removeAllWidget(); +} diff --git a/diagramCavas/source/monitorAttributeGroupDlg.cpp b/diagramCavas/source/monitorAttributeGroupDlg.cpp new file mode 100644 index 0000000..af42066 --- /dev/null +++ b/diagramCavas/source/monitorAttributeGroupDlg.cpp @@ -0,0 +1,340 @@ +#include "monitorAttributeGroupDlg.h" +#include "monitorAttributeDlg.h" +#include "monitorSideBarDlg.h" +#include "monitorPanel.h" +#include "monitorDetailAttributeDlg.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "global.h" + +MonitorAttributeGroupDlg::MonitorAttributeGroupDlg(QWidget* parent) + : QScrollArea(parent) + ,_layout(nullptr) + ,_pParent(nullptr) + ,_pDetailParent(nullptr) + ,_curMode(0) +{ + initial(); +} + +MonitorAttributeGroupDlg::~MonitorAttributeGroupDlg() +{ + +} + +void MonitorAttributeGroupDlg::initial() +{ + _layout = new QVBoxLayout(this); +} + +void MonitorAttributeGroupDlg::createGroupView(QList lst,int nType) +{ + QWidget* content = new QWidget(); + QGridLayout* gridLayout = new QGridLayout(content); + gridLayout->setHorizontalSpacing(2); + gridLayout->setVerticalSpacing(10); + gridLayout->setContentsMargins(0, 0, 0, 0); + + // 设置列的最小宽度和拉伸比例 + gridLayout->setColumnMinimumWidth(0, 30); // 标签列最小宽度 + gridLayout->setColumnStretch(0, 1); // 标签列拉伸因子 + gridLayout->setColumnStretch(1, 8); // 控件列拉伸因子 + + int row = 0; + for(auto& info : lst) { + QLabel* label = new QLabel(info.sTag, this); + label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + label->setMaximumWidth(40); // 设置标签最大宽度 + + QWidget* editor = createEditor(info); + editor->setProperty("category", info.nShowType); + editor->setProperty("graphType", info.nGraphType); + editor->setProperty("connectPara", info.sConnectPara); + + // 添加到网格布局 + gridLayout->addWidget(label, row, 0, Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(editor, row, 1); + + // 设置行的拉伸因子(可选) + gridLayout->setRowStretch(row, 0); // 不拉伸,保持内容高度 + + _curWidget.insert(info.sTag, editor); + row++; + } + + // 添加一个拉伸行,使内容在顶部对齐 + gridLayout->setRowStretch(row, 1); + + setWidget(content); + setWidgetResizable(true); +} + +QWidget* MonitorAttributeGroupDlg::createEditor(MonitorItemAttributeInfo info,int nType) +{ + QWidget* pWidget = nullptr; + if(info.nShowType == 0){ //正常显示 + QLabel *label = new QLabel(this); + pWidget = label; + } + else{ //图表 + QChartView* chartView = new QChartView(this); + QSize size; + if(nType == 0) + size = QSize(400,300); + else + size = QSize(600,400); + chartView->setMinimumSize(size); + chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + chartView->setRenderHint(QPainter::Antialiasing); + + QChart* chart = new QChart(); + chartView->setChart(chart); + + if (info.nGraphType == 0) { + // 创建折线序列 + QLineSeries* series = new QLineSeries(); + + QFont axisFont; + axisFont.setPointSize(9); + + // 创建图表 + chart->addSeries(series); + chart->setTitle(info.sName); + chart->setAnimationOptions(QChart::SeriesAnimations); + + // 设置线条样式 + QPen pen(Qt::blue); + pen.setWidth(3); + pen.setStyle(Qt::SolidLine); + series->setPen(pen); + + // 创建坐标轴 + QValueAxis* axisX = new QValueAxis(); + QValueAxis* axisY = new QValueAxis(); + + // 添加坐标轴到图表 + chart->addAxis(axisX, Qt::AlignBottom); + chart->addAxis(axisY, Qt::AlignLeft); + + // 将序列关联到坐标轴 + series->attachAxis(axisX); + series->attachAxis(axisY); + + // 设置默认范围或等待数据更新 + axisX->setRange(0, 10); // 临时范围 + axisY->setRange(0, 100); + + // 设置坐标轴标题 + axisX->setTitleText("时间"); + axisX->setLabelsFont(axisFont); + axisX->setTitleFont(axisFont); + axisY->setTitleText("值"); + axisY->setLabelsFont(axisFont); + axisY->setTitleFont(axisFont); + + chart->setTheme(QChart::ChartThemeLight); + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + + QFont legendFont = chart->legend()->font(); + legendFont.setPointSize(9); // 设置图例字体大小 + chart->legend()->setFont(legendFont); + } + else if (info.nGraphType == 1) { + QBarSeries* series = new QBarSeries(); + chart->addSeries(series); + chart->setTitle(info.sName); + chart->setAnimationOptions(QChart::SeriesAnimations); + + // 创建坐标轴 + QBarCategoryAxis* axisX = new QBarCategoryAxis(); + QValueAxis* axisY = new QValueAxis(); + + chart->addAxis(axisX, Qt::AlignBottom); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisX); + series->attachAxis(axisY); + } + + pWidget = chartView; + } + return pWidget; +} + +void MonitorAttributeGroupDlg::updateLineChartData(QChartView* chartView, const QVector& data) +{ + if (!chartView || data.isEmpty()) return; + + QChart* chart = chartView->chart(); + if (!chart) { + chart = new QChart(); + chart->setAnimationOptions(QChart::NoAnimation); + chartView->setChart(chart); + } + + QLineSeries* series = nullptr; + if (chart->series().isEmpty()) { + series = new QLineSeries(); + chart->addSeries(series); + } else { + series = qobject_cast(chart->series().first()); + if (!series) return; + } + + series->clear(); + + // 计算数据范围 + qint64 minTime = std::numeric_limits::max(); + qint64 maxTime = std::numeric_limits::lowest(); + double minVal = std::numeric_limits::max(); + double maxVal = std::numeric_limits::lowest(); + for (const QPointF& point : data) { + qint64 t = static_cast(point.x()); + series->append(t, point.y()); + minTime = qMin(minTime, t); + maxTime = qMax(maxTime, t); + minVal = qMin(minVal, point.y()); + maxVal = qMax(maxVal, point.y()); + } + + // ========== 简单直接的方法:确保只有一个坐标轴 ========== + QDateTimeAxis* xAxis = nullptr; + QValueAxis* yAxis = nullptr; + + // 获取当前所有坐标轴 + QList allAxes = chart->axes(); + + // 如果坐标轴太多,清理所有 + //if (allAxes.size() > 2) { // 应该只有x轴和y轴 + for (auto axis : allAxes) { + chart->removeAxis(axis); + delete axis; + } + allAxes.clear(); + //} + + // 在剩余的坐标轴中查找 + for (auto axis : allAxes) { + if (!xAxis && qobject_cast(axis)) { + xAxis = qobject_cast(axis); + } else if (!yAxis && qobject_cast(axis)) { + yAxis = qobject_cast(axis); + } + } + + // 创建缺失的坐标轴 + if (!xAxis) { + xAxis = new QDateTimeAxis(); + xAxis->setTitleText("时间"); + chart->addAxis(xAxis, Qt::AlignBottom); + } + + if (!yAxis) { + yAxis = new QValueAxis(); + yAxis->setTitleText("值"); + chart->addAxis(yAxis, Qt::AlignLeft); + } + + // 确保系列附加 + series->attachAxis(xAxis); + series->attachAxis(yAxis); + + // 设置范围和其他属性 + qint64 timeMargin = qMax((maxTime - minTime) * 0.05, 1000); + double valMargin = qMax((maxVal - minVal) * 0.1, 0.1); + + xAxis->setRange( + QDateTime::fromMSecsSinceEpoch(minTime - timeMargin), + QDateTime::fromMSecsSinceEpoch(maxTime + timeMargin) + ); + yAxis->setRange(minVal - valMargin, maxVal + valMargin); + + qint64 timeSpan = maxTime - minTime; + xAxis->setFormat((timeSpan > 3600000) ? "HH:mm:ss" : "HH:mm:ss.zzz"); + + xAxis->setTickCount(6); + yAxis->setTickCount(8); + yAxis->setLabelFormat("%.2f"); + + QFont font; + font.setPointSize(9); + xAxis->setLabelsFont(font); + yAxis->setLabelsFont(font); +} + +void MonitorAttributeGroupDlg::updateData() +{ + if(_pParent || _pDetailParent){ + auto pModel = getModelController(); + auto pMap = pModel->getMonitorPara(); + QUuid uid = getCurUid(); + + if(!pMap.isEmpty() && pMap.contains(uid)){ + auto info = pMap.value(uid); + + for(auto &widget:_curWidget){ + int nCate = widget->property("category").toInt(); + int nGraph = widget->property("graphType").toInt(); + QString sPara = widget->property("connectPara").toString(); + + for(auto& data:info){ + if(data.sConnectPara == sPara){ + if(nCate == 0){ + QLabel* pLabel = dynamic_cast(widget); + pLabel->setText(QString::number(data.mapValue.last())); + } + else{ + if(nGraph == 0){ //折线图 + // 转换为 QVector + QVector points; + points.reserve(data.mapValue.size()); // 预分配内存以提高性能 + + // 遍历 QMap + for (auto it = data.mapValue.constBegin(); it != data.mapValue.constEnd(); ++it) { + // 将 quint64 时间戳转换为 qreal(QPointF 使用 qreal) + qint64 timestampMs = it.key() / 1000000; // 纳秒转毫秒 + points.append(QPointF(static_cast(timestampMs), it.value())); + } + QChartView* pView = dynamic_cast(widget); + updateLineChartData(pView,points); + } + else if(nGraph == 1){ + + } + } + } + } + } + } + } +} + +FixedPortsModel* MonitorAttributeGroupDlg::getModelController() +{ + FixedPortsModel* pModel; + if(_curMode == 0){ + pModel = _pParent->getParent()->getParent()->getModelController(); + } + else{ + pModel = _pDetailParent->getParent()->getModelController(); + } + return pModel; +} + +QUuid MonitorAttributeGroupDlg::getCurUid() +{ + if(_curMode == 0){ + return _pParent->getCurId(); + } + else{ + return _pDetailParent->getCurId(); + } +} diff --git a/diagramCavas/source/monitorConfigDlg.cpp b/diagramCavas/source/monitorConfigDlg.cpp new file mode 100644 index 0000000..b63897e --- /dev/null +++ b/diagramCavas/source/monitorConfigDlg.cpp @@ -0,0 +1,265 @@ +#include "monitorConfigDlg.h" +#include "ui_monitorConfigDlg.h" +#include +#include +#include +#include "monitorPanel.h" +#include "uiCommunicationBus.h" +//#include "global.h" + +MonitorConfigDlg::MonitorConfigDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::monitorConfigDlg) + ,_parent(nullptr) + ,_curItemModel(nullptr) + ,_recommandCompleter(nullptr) + ,_strLstModel(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + _parent = dynamic_cast(parent); + initial(); +} + +MonitorConfigDlg::~MonitorConfigDlg() +{ + delete ui; +} + +void MonitorConfigDlg::initial() +{ + ui->treeView_item->setModel(_parent->getLstModel()); + connect(ui->btn_ok,&QPushButton::clicked,this,&MonitorConfigDlg::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&MonitorConfigDlg::onCancelClicked); + connect(ui->cb_type,&QComboBox::currentIndexChanged,this,&MonitorConfigDlg::onTypeChanged); + connect(ui->treeView_item->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &MonitorConfigDlg::onItemSelectionChanged); + ui->treeView_item->setHeaderHidden(true); + ui->treeView_para->setHeaderHidden(true); + ui->le_searchPara->setPlaceholderText("搜索参数"); + _curItemModel = new QStandardItemModel(this); + ui->treeView_para->setModel(_curItemModel); + connect(_curItemModel,&QStandardItemModel::itemChanged, this,&MonitorConfigDlg::onPropertyCheckChanged); + connect(ui->treeView_para->selectionModel(), &QItemSelectionModel::currentChanged, + this, &MonitorConfigDlg::onPropertySelectionChanged); + _recommandCompleter = new QCompleter(this); + _recommandCompleter->setCaseSensitivity(Qt::CaseInsensitive); + _recommandCompleter->setFilterMode(Qt::MatchContains); + _recommandCompleter->setCompletionMode(QCompleter::PopupCompletion); + _strLstModel = new QStringListModel(this); + + _recommandCompleter->setModel(_strLstModel); + ui->le_query->setCompleter(_recommandCompleter); + ui->le_query->installEventFilter(this); + ui->le_query->setPlaceholderText("输入空格获取初始值"); + connect(ui->le_query, &QLineEdit::textChanged, this, [=](const QString &text) { + if (text.endsWith(".")) { + onConnectParamChanged(text); + } + }); +} + +void MonitorConfigDlg::updateSelectedItems() +{ + ui->treeView_item->expandAll(); + _tempConfig = _parent->getModelController()->getMonitorPara(); +} + +void MonitorConfigDlg::updateRecommandLst(QStringList lst) +{ + for(auto& newName:lst){ + if(!newName.isEmpty()){ + if(!_curRecommandLst.contains(newName)){ + _curRecommandLst.append(newName); + } + } + } + _strLstModel->setStringList(_curRecommandLst); + if (_recommandCompleter->popup()->isVisible()) { + _recommandCompleter->popup()->hide(); + } + _recommandCompleter->complete(); // 重新显示补全列表 +} + +void MonitorConfigDlg::onOkClicked() +{ + if (ui->treeView_para->currentIndex().isValid()) { //确定前先保存当前属性 + savePropertyData(ui->treeView_para->currentIndex(),_curUuid); + } + _parent->getModelController()->getMonitorPara() = _tempConfig; //保存临时数据 + _parent->getModelController()->monitorItemSet(_curUuid); + hide(); +} + +void MonitorConfigDlg::onCancelClicked() +{ + hide(); +} + +void MonitorConfigDlg::onTypeChanged(int index) +{ + ui->stackedWidget->setCurrentIndex(index); +} + +void MonitorConfigDlg::onItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + if (ui->treeView_para->currentIndex().isValid()) { //如果切换设备,先保存属性到上个设备 + savePropertyData(ui->treeView_para->currentIndex(),_curUuid); + } + + clearProperty(); + + QStandardItem *root = _curItemModel->invisibleRootItem(); //先清空model + int rowCount = root->rowCount(); + if (rowCount > 0) { + _curItemModel->removeRows(0, rowCount); + } + + QModelIndexList indexes = selected.indexes(); + if(indexes.size() == 1){ + auto index = indexes.first(); + int nCategory = index.data(Qt::UserRole+1).toInt(); + if(nCategory == 0){ //是设备 + QUuid uid = index.data(Qt::UserRole+3).toUuid(); + _curUuid = uid; + if(_tempConfig.contains(uid)){ + auto lstPara = _tempConfig.value(uid); + for(auto& para:lstPara){ + auto pParent = findStandardItemAtLevel(_curItemModel,0,para.sGroup,nullptr,0); + if(pParent){ //组已存在 + QStandardItem *pItem = new QStandardItem(para.sName); + pItem->setCheckable(true); + pItem->setCheckState(para.bSelected ? Qt::Checked : Qt::Unchecked); + pItem->setData(nCategory,Qt::UserRole+1); + pItem->setData(para.sTag,Qt::UserRole+2); + pParent->appendRow(pItem); + } + else{ + QStandardItem *pPar = new QStandardItem(para.sGroup); + pPar->setData(1,Qt::UserRole+1); + QStandardItem *pItem = new QStandardItem(para.sName); + pItem->setCheckable(true); + pItem->setCheckState(para.bSelected ? Qt::Checked : Qt::Unchecked); + pItem->setData(nCategory,Qt::UserRole+1); + pItem->setData(para.sTag,Qt::UserRole+2); + pPar->appendRow(pItem); + _curItemModel->appendRow(pPar); + } + } + } + } + } + ui->treeView_para->expandAll(); +} + +void MonitorConfigDlg::onPropertyCheckChanged(QStandardItem *item) +{ + if(_tempConfig.contains(_curUuid)){ + Qt::CheckState state = item->checkState(); + QString itemText = item->text(); + if (state == Qt::Checked) { + for(auto &info:_tempConfig[_curUuid]){ + if(info.sName == itemText){ + info.bSelected = true; + } + } + } else if (state == Qt::Unchecked) { + for(auto &info:_tempConfig[_curUuid]){ + if(info.sName == itemText){ + info.bSelected = false; + } + } + } + } +} + +void MonitorConfigDlg::onPropertySelectionChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (previous.isValid()) { + savePropertyData(previous,_curUuid); + } + if (current.isValid()) { + loadPropertyData(current,_curUuid); + } +} + +void MonitorConfigDlg::onConnectParamChanged(const QString& str) +{ + QVariantMap map; + map.insert("input",str); + UiCommunicationBus::instance()->sendHttpRequest("/measurement/recommend",QVariant(),"GET",map); +} + +bool MonitorConfigDlg::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->le_query && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + + if (keyEvent->key() == Qt::Key_Space) { + qDebug() << "eventFilter:space"; + onConnectParamChanged(ui->le_query->text()); + _recommandCompleter->complete(); + return true; + } + + if (keyEvent->text() == ".") { + qDebug() << "eventFilter:dot"; + // 返回false让事件继续传播 + } + } + return QDialog::eventFilter(obj, event); +} + +void MonitorConfigDlg::savePropertyData(const QModelIndex &previous,QUuid uid) +{ + if(_tempConfig.contains(uid)){ + if(previous.isValid()){ //保存数据 + QString itemText = previous.data().toString(); + for(auto &info:_tempConfig[uid]){ + if(info.sName == itemText){ + info.sTag = ui->le_name->text(); + info.sConnectPara = ui->le_query->text(); + info.nShowType = ui->cb_type->currentIndex(); + info.bShowDiagram = ui->cb_outVisible->currentIndex(); + info.nGraphType = ui->cb_graphType->currentIndex(); + info.sTimeRange = ui->cb_timeRange->currentText(); + } + } + } + } +} + +void MonitorConfigDlg::loadPropertyData(const QModelIndex ¤t,QUuid uid) +{ + QList lst; + if(_tempConfig.contains(uid)){ + lst = _tempConfig.value(uid); + } + + if(current.isValid()){ //加载新数据 + QString itemText = current.data().toString(); + for(auto &info:lst){ + if(info.sName == itemText){ + ui->le_name->setText(info.sTag); + ui->le_query->setText(info.sConnectPara); + ui->cb_type->setCurrentIndex(info.nShowType); + if(info.nShowType == 0){ //字符 + ui->cb_outVisible->setCurrentIndex(info.bShowDiagram); + }else{ //图表 + ui->cb_graphType->setCurrentIndex(info.nGraphType); + ui->cb_timeRange->setCurrentText(info.sTimeRange); + } + } + } + } +} + +void MonitorConfigDlg::clearProperty() +{ + ui->le_name->clear(); + ui->le_query->clear(); + ui->cb_type->setCurrentIndex(0); + ui->cb_outVisible->setCurrentIndex(0); + ui->cb_graphType->setCurrentIndex(0); + ui->cb_timeRange->setCurrentIndex(0); +} diff --git a/diagramCavas/source/monitorDetailAttributeDlg.cpp b/diagramCavas/source/monitorDetailAttributeDlg.cpp new file mode 100644 index 0000000..09dd15a --- /dev/null +++ b/diagramCavas/source/monitorDetailAttributeDlg.cpp @@ -0,0 +1,88 @@ +#include "monitorDetailAttributeDlg.h" +#include "ui_monitorDetailAttributeDlg.h" +#include "monitorAttributeGroupDlg.h" +#include "monitorPanel.h" +#include "graphicsDataModel/fixedPortsModel.h" +//#include "global.h" + +MonitorDetailAttributeDlg::MonitorDetailAttributeDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::monitorDetailAttributeDlg) + ,_pParent(nullptr) + ,m_gridLayout(nullptr) +{ + _pParent = dynamic_cast(parent); + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); +} + +MonitorDetailAttributeDlg::~MonitorDetailAttributeDlg() +{ + delete ui; +} + +void MonitorDetailAttributeDlg::initial() +{ + _curColNum = 2; + connect(ui->btn_exit,&QPushButton::clicked,this,&MonitorDetailAttributeDlg::onCloseClicked); + connect(ui->cb_colNum,&QComboBox::currentTextChanged,this,&MonitorDetailAttributeDlg::onColChanged); + m_gridLayout = new QGridLayout(ui->content); +} + +void MonitorDetailAttributeDlg::onCloseClicked() +{ + hide(); +} + +void MonitorDetailAttributeDlg::onColChanged(const QString& str) +{ + updateLayout(str.toInt()); +} + +void MonitorDetailAttributeDlg::generateAttributeGroups(QUuid uid) +{ + clearAllGroup(); + QMap> mapLst; + auto mapPara = _pParent->getModelController()->getMonitorPara(); + if(mapPara.contains(uid)){ + _curId = uid; + auto lst = mapPara[uid]; + for(auto &info:lst){ + if(info.bSelected) + mapLst[info.sGroup].append(info); + } + + for(auto iter = mapLst.begin(); iter != mapLst.end();++iter){ + MonitorAttributeGroupDlg* pDlg = new MonitorAttributeGroupDlg(); + pDlg->setDetailParent(this); + pDlg->setCurMode(1); + pDlg->createGroupView(iter.value()); + connect(_pParent->getModelController(),&FixedPortsModel::dataUpdated,pDlg,&MonitorAttributeGroupDlg::updateData); + _curGroups.insert(iter.key(),pDlg); + } + } + + updateLayout(_curColNum); +} + +void MonitorDetailAttributeDlg::updateLayout(int columns) +{ + _curColNum = columns; + int i = 0; + for(auto& pDlg:_curGroups){ + int row = i / _curColNum; + int col = i % _curColNum; + m_gridLayout->addWidget(pDlg, row, col); + i++; + } +} + +void MonitorDetailAttributeDlg::clearAllGroup() +{ + for(auto& dlg:_curGroups){ + m_gridLayout->removeWidget(dlg); + delete dlg; + } + _curGroups.clear(); +} diff --git a/diagramCavas/source/monitorDisplaySettingDlg.cpp b/diagramCavas/source/monitorDisplaySettingDlg.cpp new file mode 100644 index 0000000..ca78ea5 --- /dev/null +++ b/diagramCavas/source/monitorDisplaySettingDlg.cpp @@ -0,0 +1,420 @@ +#include "monitorDisplaySettingDlg.h" +#include "ui_monitorDisplaySettingDlg.h" +#include "monitorPanel.h" +#include +#include +#include +#include +#include "projectModelManager.h" +#include "projectIconSelectionDlg.h" + +MonitorDisplaySettingDlg::MonitorDisplaySettingDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::monitorDisplaySettingDlg) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + _parent = dynamic_cast(parent); + initial(); +} + +MonitorDisplaySettingDlg::~MonitorDisplaySettingDlg() +{ + delete ui; +} + +void MonitorDisplaySettingDlg::initial() +{ + connect(ui->btn_save,&QPushButton::clicked,this,&MonitorDisplaySettingDlg::onSaveClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&MonitorDisplaySettingDlg::onCancelClicked); + + connect(ui->cb_type,&QComboBox::currentTextChanged,this,&MonitorDisplaySettingDlg::onDeviceComboBoxChanged); + connect(ui->cb_state,&QComboBox::currentTextChanged,this,&MonitorDisplaySettingDlg::onStateComboBoxChanged); + + connect(ui->btn_selectColor, &QPushButton::clicked, this, [this]() { + QColor newColor = QColorDialog::getColor(_curColor, this, "选择颜色"); + if (newColor.isValid()) { // 用户点击了OK + _curColor = newColor; + // 更新按钮显示 + QPixmap pixmap(16, 16); + pixmap.fill(newColor); + ui->btn_selectColor->setIcon(QIcon(pixmap)); + ui->btn_selectColor->setText(newColor.name()); + // 触发预览更新 + + if(_curType.isValid() && _curState.isValid()){ + _tempSetting[_curType][_curState].sColor = _curColor.name(); + ui->content->setColors(_curColor); + } + } + }); + + connect(ui->checkBox_custom, &QCheckBox::toggled, this,&MonitorDisplaySettingDlg::onCheckboxToggled); + connect(ui->btn_selectIcon, &QPushButton::clicked, this,&MonitorDisplaySettingDlg::onIconSelectClicked); +} + +void MonitorDisplaySettingDlg::showDlg() +{ + show(); + _tempSetting = _parent->getModelController()->getMonitorDisplaySetting(); + ui->cb_state->blockSignals(true); + ui->cb_type->blockSignals(true); + ui->cb_type->clear(); + ui->cb_state->clear(); + ui->cb_state->blockSignals(false); + ui->cb_type->blockSignals(false); + + for(auto iter = _tempSetting.begin();iter != _tempSetting.end();++iter){ + ui->cb_type->addItem(iter.key().sName,iter.key().sTag); + } + + // 2. 如果有设备,选择第一个设备 + // if (ui->cb_type->count() > 0) { + // QString firstDevice = ui->cb_type->itemText(0); + // onDeviceComboBoxChanged(firstDevice); + // } +} + +void MonitorDisplaySettingDlg::onSaveClicked() +{ + if (validateCurrentDeviceState()) { + if (!saveCurrentSettingsWithIcon()) { + QMessageBox::warning(this, "Warning", "Failed to save current settings"); + } + } + _parent->getModelController()->getMonitorDisplaySetting() = _tempSetting; + _parent->getModelController()->updateMonitorDisplay(); + hide(); +} + +void MonitorDisplaySettingDlg::onCancelClicked() +{ + hide(); +} + +void MonitorDisplaySettingDlg::onCheckboxToggled(bool val) +{ + if(val){ + ui->btn_selectColor->setEnabled(true); + ui->btn_selectIcon->setEnabled(true); + ui->sb_width->setEnabled(true); + ui->sb_height->setEnabled(true); + ui->cb_effect->setEnabled(true); + } + else{ + ui->btn_selectColor->setEnabled(false); + ui->btn_selectIcon->setEnabled(false); + ui->sb_width->setEnabled(false); + ui->sb_height->setEnabled(false); + ui->cb_effect->setEnabled(false); + } +} + +void MonitorDisplaySettingDlg::onIconSelectClicked() +{ + if(_curMeta.isEmpty() || _curModel.isEmpty()){ + return; + } + + auto mapAllSvg = ProjectModelManager::instance().getData()[_curMeta][_curModel].modelSetting.mapSvg; + ProjectIconSelectionDlg dialog(mapAllSvg, this); + if (dialog.exec() == QDialog::Accepted) { + QByteArray selectedSVG = dialog.selectedSVG(); + if (!selectedSVG.isEmpty()) { + QSvgRenderer renderer(selectedSVG); + QPixmap pixmap(32, 32); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + renderer.render(&painter); + + ui->btn_selectIcon->setIcon(QIcon(pixmap)); + if (validateSvgData(selectedSVG)) { + updateIconDisplay(selectedSVG); + + // 立即更新当前设备状态的图标数据 + if(_curType.isValid() && _curState.isValid()){ + _tempSetting[_curType][_curState].bytPicture = selectedSVG; + } + } + } + } +} + +void MonitorDisplaySettingDlg::onDeviceComboBoxChanged(const QString& str) +{ + if (str.isEmpty()) { + return; + } + + // 1. 保存当前设备的非图标数据 + if (_curType.isValid() && _curState.isValid()) { + saveCurrentSettingsWithIcon(); + clearIconDisplay(); + } + + // 2. 构建新设备 + MonitorItemTypeStruct newType; + newType.sTag = ui->cb_type->currentData().toString(); + newType.sName = str; + + if (!newType.isValid()) { + return; + } + + // 3. 检查新设备是否存在 + if (!_tempSetting.contains(newType)) { + _curType = newType; + _curState = MonitorItemStateStruct(); + clearStateDisplay(); + return; + } + + // 4. 清空状态下拉框 + ui->cb_state->blockSignals(true); + ui->cb_state->clear(); + ui->cb_state->blockSignals(false); + + // 5. 填充新设备的状态 + auto& stateMap = _tempSetting[newType]; + for (auto iter = stateMap.begin(); iter != stateMap.end(); ++iter) { + ui->cb_state->addItem(iter.key().sState, static_cast(iter.key().eState)); + } + + // 6. 更新当前设备 + _curType = newType; + + // 7. 加载第一个状态 + if (ui->cb_state->count() > 0) { + loadFirstStateSafely(); + } else { + _curState = MonitorItemStateStruct(); + clearStateDisplay(); + } +} + +void MonitorDisplaySettingDlg::onStateComboBoxChanged(const QString& str) +{ + if (str.isEmpty() || str == _curState.sState) { + return; + } + + // 1. 保存当前状态的完整数据(包括图标) + if (_curType.isValid() && _curState.isValid()) { + saveCurrentSettingsWithIcon(); // 保存完整数据 + } + + // 2. 获取新状态 + QVariant stateData = ui->cb_state->currentData(); + if (!stateData.isValid()) { + return; + } + + MonitorItemStateStruct newState; + newState.sState = str; + newState.eState = static_cast(stateData.toInt()); + + // 3. 更新当前状态 + _curState = newState; + + // 4. 加载新状态设置 + loadSetting(_curType, _curState); +} + +void MonitorDisplaySettingDlg::loadSetting(MonitorItemTypeStruct type, MonitorItemStateStruct state) +{ + if (!type.isValid() || !state.isValid()) { + return; + } + + if (!_tempSetting.contains(type) || !_tempSetting[type].contains(state)) { + return; + } + + auto& setting = _tempSetting[type][state]; + + // 修复:正确设置_curMeta和_curModel + _curMeta = setting.sMeta; + _curModel = setting.sModel; + + // 更新界面控件 + ui->checkBox_custom->setChecked(setting.bEnable); + + // 设置控件可用性 + bool enabled = setting.bEnable; + ui->btn_selectColor->setEnabled(enabled); + ui->btn_selectIcon->setEnabled(enabled); + ui->sb_width->setEnabled(enabled); + ui->sb_height->setEnabled(enabled); + ui->cb_effect->setEnabled(enabled); + + // 更新颜色 + _curColor = QColor(setting.sColor); + if (!_curColor.isValid()) { + _curColor = Qt::black; + } + + QPixmap colorPixmap(16, 16); + colorPixmap.fill(_curColor); + ui->btn_selectColor->setIcon(QIcon(colorPixmap)); + ui->btn_selectColor->setText(_curColor.name()); + + // 更新图标 + if (!setting.bytPicture.isEmpty()) { + QSvgRenderer renderer(setting.bytPicture); + if (renderer.isValid()) { + QPixmap iconPixmap(32, 32); + iconPixmap.fill(Qt::transparent); + QPainter painter(&iconPixmap); + renderer.render(&painter); + ui->btn_selectIcon->setIcon(QIcon(iconPixmap)); + + ui->content->setSvgFile(setting.bytPicture); + } + } else { + ui->btn_selectIcon->setIcon(QIcon()); + ui->content->clearSvg(); + } + + // 更新尺寸 + ui->sb_width->setValue(setting.nWidth); + ui->sb_height->setValue(setting.nHeight); + + // 更新内容显示 + ui->content->setCurType(type.sTag); + ui->content->setColors(_curColor); +} + + +bool MonitorDisplaySettingDlg::saveCurrentSettingsWithIcon() +{ + if (!_curType.isValid() || !_curState.isValid()) { + return false; + } + + if (!_tempSetting.contains(_curType) || !_tempSetting[_curType].contains(_curState)) { + return false; + } + + auto& setting = _tempSetting[_curType][_curState]; + + int oldSvgSize = setting.bytPicture.size(); + + // 保存所有数据,包括图标 + setting.bEnable = ui->checkBox_custom->isChecked(); + setting.sColor = _curColor.name(QColor::HexArgb); + + // 图标数据:使用当前显示的图标 + QByteArray currentSvg = ui->content->getCurSvg(); + if (!currentSvg.isEmpty() && validateSvgData(currentSvg)) { + setting.bytPicture = currentSvg; + } + // 如果图标数据无效,保持原数据不变 + + setting.nWidth = ui->sb_width->value(); + setting.nHeight = ui->sb_height->value(); + + // 修复_curMeta和_curModel + if (!_curMeta.isEmpty()) { + setting.sMeta = _curMeta; + } + + if (!_curModel.isEmpty()) { + setting.sModel = _curModel; + } + + return true; +} + +void MonitorDisplaySettingDlg::clearStateDisplay() +{ + ui->checkBox_custom->setChecked(false); + ui->btn_selectColor->setEnabled(false); + ui->btn_selectIcon->setEnabled(false); + ui->sb_width->setEnabled(false); + ui->sb_height->setEnabled(false); + ui->cb_effect->setEnabled(false); + + // 清空图标显示 + ui->btn_selectIcon->setIcon(QIcon()); + ui->content->clearSvg(); + + // 重置颜色显示 + _curColor = Qt::black; + QPixmap pixmap(16, 16); + pixmap.fill(_curColor); + ui->btn_selectColor->setIcon(QIcon(pixmap)); + ui->btn_selectColor->setText(_curColor.name()); + + // 清空当前状态 + _curState = MonitorItemStateStruct(); + _curModel.clear(); + _curMeta.clear(); +} + +void MonitorDisplaySettingDlg::updateIconDisplay(const QByteArray& svgData) +{ + if (svgData.isEmpty()) { + clearIconDisplay(); + return; + } + + if (!validateSvgData(svgData)) { + clearIconDisplay(); + return; + } + + try { + // 更新按钮图标 + QSvgRenderer renderer(svgData); + QPixmap iconPixmap(32, 32); + iconPixmap.fill(Qt::transparent); + QPainter painter(&iconPixmap); + renderer.render(&painter); + ui->btn_selectIcon->setIcon(QIcon(iconPixmap)); + + // 更新内容显示 + ui->content->setSvgFile(svgData); + ui->content->setColors(_curColor); + + } catch (...) { + clearIconDisplay(); + } +} + +void MonitorDisplaySettingDlg::clearIconDisplay() +{ + ui->btn_selectIcon->setIcon(QIcon()); + ui->content->clearSvg(); +} + +bool MonitorDisplaySettingDlg::validateSvgData(const QByteArray& svgData) const +{ + if (svgData.isEmpty()) { + return false; + } + + QSvgRenderer renderer(svgData); + return renderer.isValid(); +} + +void MonitorDisplaySettingDlg::loadFirstStateSafely() +{ + QString firstStateName = ui->cb_state->itemText(0); + QVariant firstStateData = ui->cb_state->itemData(0); + + MonitorItemStateStruct firstState; + firstState.sState = firstStateName; + firstState.eState = static_cast(firstStateData.toInt()); + + _curState = firstState; + ui->cb_state->setCurrentIndex(0); + loadSetting(_curType, _curState); +} + +bool MonitorDisplaySettingDlg::validateCurrentDeviceState() const +{ + return _curType.isValid() && _curState.isValid() && + _tempSetting.contains(_curType) && + _tempSetting[_curType].contains(_curState); +} + diff --git a/diagramCavas/source/monitorItemPreviewDlg.cpp b/diagramCavas/source/monitorItemPreviewDlg.cpp new file mode 100644 index 0000000..18c920e --- /dev/null +++ b/diagramCavas/source/monitorItemPreviewDlg.cpp @@ -0,0 +1,84 @@ +#include "monitorItemPreviewDlg.h" +#include + +MonitorItemPreviewDlg::MonitorItemPreviewDlg(QWidget* parent) + : QWidget(parent) +{ + initial(); +} + +MonitorItemPreviewDlg::~MonitorItemPreviewDlg() +{ + +} + +void MonitorItemPreviewDlg::initial() +{ + +} + +void MonitorItemPreviewDlg::setSvgFile(const QByteArray &bytSvg) +{ + m_renderer.load(bytSvg); + _curSvg = bytSvg; + update(); // 触发重绘 +} + +void MonitorItemPreviewDlg::setColors(const QColor &color) +{ + m_Color = color; + update(); +} + +void MonitorItemPreviewDlg::clearSvg() +{ + QByteArray emptySvg = ""; + _curSvg.clear(); + m_renderer.load(emptySvg); + m_curDeviceType.clear(); +} + +void MonitorItemPreviewDlg::paintEvent(QPaintEvent *) { + if(m_curDeviceType == "cable"){ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // 设置线宽 + int lineWidth = 3; + painter.setPen(QPen(m_Color, lineWidth)); + + // 定义折线的各个顶点 + QPolygon polyline; + polyline << QPoint(20, 20) // 起点:左上角 + << QPoint(width()/2, height()/2) // 中间点:中心 + << QPoint(width()-20, height()-20); // 终点:右下角 + painter.drawPolyline(polyline); + } + else{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // 1. 先将SVG渲染到一个透明图像上作为遮罩 + QImage maskImage(size(), QImage::Format_ARGB32); + maskImage.fill(Qt::transparent); + + QPainter maskPainter(&maskImage); + m_renderer.render(&maskPainter); + maskPainter.end(); + + // 2. 使用目标颜色填充,但只在SVG的非透明区域显示 + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + for (int y = 0; y < maskImage.height(); ++y) { + for (int x = 0; x < maskImage.width(); ++x) { + QRgb pixel = maskImage.pixel(x, y); + if (qAlpha(pixel) > 0) { // 只处理非透明像素 + // 保持原始透明度,只改变颜色 + QColor newColor = m_Color; + newColor.setAlpha(qAlpha(pixel)); // 保持原始alpha值 + painter.fillRect(x, y, 1, 1, newColor); + } + } + } + } +} diff --git a/diagramCavas/source/monitorPanel.cpp b/diagramCavas/source/monitorPanel.cpp new file mode 100644 index 0000000..ce7f8d2 --- /dev/null +++ b/diagramCavas/source/monitorPanel.cpp @@ -0,0 +1,936 @@ +#include "monitorPanel.h" +#include +#include +#include +#include +#include +#include +#include +#include "graphicsDataModel/fixedPortsModel.h" +#include "graphicsItem/functionModelItem/graphicsFunctionModelItem.h" +#include "powerEntity.h" +#include "statusBar.h" +#include "baseProperty.h" +#include "graphicsItem/electricBayItem.h" +#include "monitorSideBarDlg.h" +#include "monitorSelectedItemsDlg.h" +#include "monitorConfigDlg.h" +#include "monitorAttributeDlg.h" +#include "monitorDetailAttributeDlg.h" +#include "monitorDisplaySettingDlg.h" +#include "dataBase.h" +#include "projectModelManager.h" +#include "diagramCavas.h" + +MonitorPanel::MonitorPanel(PowerEntity* pEntity,QWidget *parent,DiagramMode mode) + : BaseDrawingPanel(pEntity,parent,mode) + ,_toolBar(nullptr) + ,_sideBar(nullptr) + ,_pConfigDlg(nullptr) + ,_itemListmodel(nullptr) + ,_menuSetting(nullptr) + ,_detailAttributeDlg(nullptr) + ,_displaySettingDlg(nullptr) + ,_menuFile(nullptr) +{ + m_pStatusBar->setButtonVisible(false); + initial(); +} + +MonitorPanel::~MonitorPanel() +{ + +} + +void MonitorPanel::initial() +{ + _itemListmodel = new QStandardItemModel(this); + + createToolBar(); + _sideBar = new MonitorSideBarDlg(this); + _hSplitter->addWidget(_sideBar); + _sideBar->show(); + _pConfigDlg = new MonitorConfigDlg(this); + _detailAttributeDlg = new MonitorDetailAttributeDlg(this); + _displaySettingDlg = new MonitorDisplaySettingDlg(this); +} + +void MonitorPanel::closeEvent(QCloseEvent *closeEvent) +{ + emit panelDelete(_name,2); +} + +void MonitorPanel::createToolBar() +{ + _toolBar = new QToolBar(this); + _toolBar->setStyleSheet("QToolBar { background-color: palette(window); border: none; }"); + + _menuFile = new QMenu("系统", this); + + // 保存操作 + QAction *saveAction = _menuFile->addAction("保存"); + connect(saveAction, &QAction::triggered, this, &MonitorPanel::onSaveClicked); + + _menuFile->addSeparator(); // 添加分隔线 + + // 运行操作 + QAction *runAction = _menuFile->addAction("运行"); + connect(runAction, &QAction::triggered, this, &MonitorPanel::onRunClicked); + + // 停止操作 + QAction *stopAction = _menuFile->addAction("停止"); + connect(stopAction, &QAction::triggered, this, &MonitorPanel::onStopClicked); + + QToolButton *fileButton = new QToolButton(this); + fileButton->setText("文件"); + fileButton->setMenu(_menuFile); + fileButton->setPopupMode(QToolButton::InstantPopup); + _toolBar->addWidget(fileButton); + + QAction *configAction = new QAction("参数配置", this); + connect(configAction, &QAction::triggered, this, &MonitorPanel::onConfigClicked); + _toolBar->addAction(configAction); + + QAction *itemConfigAction = new QAction("图元配置", this); + connect(itemConfigAction, &QAction::triggered, this, &MonitorPanel::onItemConfigClicked); + _toolBar->addAction(itemConfigAction); + + QAction *connectAction = new QAction("连接设置", this); + connect(connectAction, &QAction::triggered, this, &MonitorPanel::onConncecClicked); + _toolBar->addAction(connectAction); + + QAction *otherAction = new QAction("其他设置", this); + _menuSetting = new QMenu(this); + QAction *toggleAction = _menuSetting->addAction("显示属性"); + toggleAction->setCheckable(true); + + // 连接菜单项点击事件 + connect(toggleAction, &QAction::triggered, this, [this]() { + _sideBar->setVisible(!_sideBar->isVisible()); + }); + + // 在显示菜单前同步菜单项状态 + connect(_menuSetting, &QMenu::aboutToShow, this, [this, toggleAction]() { + toggleAction->setChecked(_sideBar->isVisible()); + }); + + connect(otherAction, &QAction::triggered, this, [&]() { + QWidget *widget = _toolBar->widgetForAction(otherAction); + if (widget) { + QPoint pos = widget->mapToGlobal(QPoint(0, widget->height())); + _menuSetting->exec(pos); + } else { + _menuSetting->exec(QCursor::pos()); + } + }); + + _toolBar->addAction(otherAction); + + // 设置工具栏样式 + _toolBar->setToolButtonStyle(Qt::ToolButtonTextOnly); + _verticalLayout->setMenuBar(_toolBar); +} + +QJsonObject MonitorPanel::getDiagramInfo() +{ + QJsonObject obj; + QJsonArray arr; + if(_pModel) + { + QMap map = _pModel->allNodePos(); + for(auto iter = map.begin();iter != map.end();++iter) + { + QJsonObject node; + node["id"] = iter.key().toString(); + node["x"] = iter.value().pos.x(); + node["y"] = iter.value().pos.y(); + node["width"] = iter.value().dWidth; + node["height"] = iter.value().dHeight; + node["rotate"] = iter.value().dRotate; + arr.append(node); + } + obj["nodes"] = arr; + + QJsonArray arrConnect; + QVector vec = _pModel->allConnectionProperty(); + for(auto& pPro:vec){ + Connection con = pPro->getConnection(); + QJsonObject connect; + connect["id"] = pPro->uuid().toString(); + connect["SrcNodeId"] = con.nSrcNodeId.toString(); + connect["SrcPortId"] = con.nSrcPortId.toString(); + connect["DestNodeId"] = con.nDestNodeId.toString(); + connect["DestPortId"] = con.nDestPortId.toString(); + arrConnect.append(connect); + } + obj["connections"] = arrConnect; + + QJsonArray arrBay; + QMap mapBay = _pModel->allBayItem(); + for(auto& bayItem:mapBay){ + AbstractProperty* pPro = bayItem->getProperty(); + BayProperty* pBay = dynamic_cast(pPro); + if(pBay) + { + QJsonObject bay; + bay["id"] = pBay->uuid().toString(); + arrBay.append(bay); + } + } + obj["bays"] = arrBay; + + auto relation = serializeRelationToJsonArray(_pModel->getMonitorRelation()); + obj["relation"] = relation; + + auto para = serializeParaToJsonArray(_pModel->getMonitorPara()); //设定参数 + obj["para"] = para; + + auto settingArr = serializeDisplayToJsonArray(_pModel->getMonitorDisplaySetting()); //显示参数 + obj["displaySetting"] = settingArr; + + obj["lastSave"] = QDateTime::currentDateTime().toString("yy-MM-dd HH:mm"); + } + return obj; +} + +void MonitorPanel::loadNodes(QJsonObject obj) +{ + QJsonArray nodesJsonArray = obj["nodes"].toArray(); + + QList> lst; + QList lstId; //通知外部拓扑列表使用 + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QString uuid = node["id"].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 sName = _pModel->addNodeItem(QUuid(uuid),QPointF(dX,dY),dWidth,dHeight,dRotate); + if(sName != "err"){ + lst.append(qMakePair(sName,QUuid(uuid))); + lstId.append(QUuid(uuid)); + } + } + } + + + QJsonArray connectArr = obj["connections"].toArray(); + for(QJsonValueRef connectJson:connectArr) + { + QJsonObject connect = connectJson.toObject(); + QUuid id = QUuid(connect["id"].toString()); //电缆线id,关联component中的电缆 + QUuid srcId = QUuid(connect["SrcNodeId"].toString()); + QUuid srcPortId = QUuid(connect["SrcPortId"].toString()); + QUuid destId = QUuid(connect["DestNodeId"].toString()); + QUuid destPortId = QUuid(connect["DestPortId"].toString()); + + PowerConnection* pCon = TopologyManager::instance().connection(srcPortId.toString(),destPortId.toString()); + if(pCon) + { + pCon->setId(id.toString()); + QString srcItemId = pCon->fromComponent(); + QString destItemId = pCon->toComponent(); + //todo:从拓扑结构中查找port的id + if(_pModel) + { + QString sName = _pModel->addConnectLline(id,QUuid(srcItemId),QUuid(destItemId),srcPortId,destPortId); + if(sName != "err"){ + lst.append(qMakePair(sName,QUuid(id))); + lstId.append(QUuid(id)); + } + } + } + else + { + //todo:提示拓扑结构已改变 + } + } + + emit getModelController()->notifyUpdateMonitorTopology(lstId); + + QJsonArray bayArr = obj["bays"].toArray(); + for(QJsonValueRef bayJson:bayArr) + { + QJsonObject bay = bayJson.toObject(); + QUuid id = QUuid(bay["id"].toString()); + if(_pModel) + { + _pModel->addBayItem(id); + } + } + + QJsonArray paraArr = obj["para"].toArray(); + QMap> mapPara; + bool resPara = deserializeParaFromJsonArray(paraArr,mapPara); + if(resPara){ + if(_pModel){ + _pModel->setMonitorPara(mapPara); + auto pItems = _pModel->getHMIItems(); + for(auto& p:pItems){ //将设置值更新到每个item + auto pPro = p->getProperty(); + if(pPro) + _pModel->monitorItemSet(pPro->uuid()); + } + } + } + + QJsonArray displayArr = obj["displaySetting"].toArray(); + QMap> mapDisplay; + deserializeDisplayFromJsonArray(displayArr,mapDisplay); + if(_pModel){ + _pModel->setMonitorDisplaySetting(mapDisplay); + _pModel->updateMonitorDisplay(); + } + + QJsonArray relationArr = obj["relation"].toArray(); + QList lstRelation; + bool resRel = deserializeRelationFromJsonArray(relationArr, lstRelation); + if(resRel){ + QList lstFirst; + if(_pModel){ + auto pItems = _pModel->getHMIItems(); + auto pBays = _pModel->getProjectBayItems(); + for(auto& itemInfo:lstRelation) //首次循环间隔 + { + if(itemInfo.item.nCategory == 1){ //间隔 + if(pBays.contains(itemInfo.item.uid)){ + lstFirst.append(itemInfo); + } + } + } + + QList lstSecond; + for(auto& itemInfo:lstRelation) //第二次循环设备 + { + if(itemInfo.item.nCategory == 0){ //间隔 + if(pItems.contains(itemInfo.item.uid)){ + lstSecond.append(itemInfo); + } + } + } + + updateSelectedItems(lstFirst,true); //直接生成监控tree + updateSelectedItems(lstSecond,false); + + _pModel->setMonitorRelation(lstRelation); + } + } + + QString sLastSave = obj["lastSave"].toString(); + _pModel->setLastSave(sLastSave); + + if(_pModel){ + QString sG; + QString sZ; + QString sS; + for(auto& pBaseItem:_pModel->getHMIItems()) //取grid_zone_station(间隔不含这些内容) + { + BaseProperty* pBase = dynamic_cast(pBaseItem->getProperty()); + if(sG.isEmpty()) + sG = pBase->grid(); + if(sZ.isEmpty()) + sZ = pBase->zone(); + if(sS.isEmpty()) + sS = pBase->station(); + break; + } + + QMap mapHmiSource = ProjectModelManager::instance().getHMIimageMap(); //获取所有hmi资源 + + QList lstFirst; + + for(auto& pOtherItem:_pModel->getProjectBayItems()) + { + BayProperty* pBay = dynamic_cast(pOtherItem->getProperty()); + if(pBay){ + // 创建间隔项 + HierarchyItem bayInfo; + bayInfo.item.nEquipType = 0; // 间隔的设备类型为0 + bayInfo.item.nCategory = 1; // 类别为1表示间隔 + bayInfo.item.sName = pBay->tag(); + bayInfo.item.uid = pBay->uuid(); + bayInfo.item.sVoltageLevel = QString::number(pBay->getVoltage()); + bayInfo.item.grid = sG; + bayInfo.item.zone = sZ; + bayInfo.item.station = sS; + + lstFirst.append(bayInfo); + } + } + + emit _pModel->updateTopologyItems(lstFirst, true,true); + + // 第二阶段:处理所有设备 + QList lstSecond; + + // 建立间隔UUID到间隔标签的映射,提高查找效率 + QHash bayUuidToTag; + for(auto& pOtherItem:_pModel->getProjectBayItems()) + { + BayProperty* pBay = dynamic_cast(pOtherItem->getProperty()); + if(pBay){ + bayUuidToTag[pBay->uuid().toString()] = pBay->tag(); + } + } + + for(auto& pBaseItem:_pModel->getHMIItems()) + { + BaseProperty* pBase = dynamic_cast(pBaseItem->getProperty()); + + HierarchyItem info; + info.item.nEquipType = pBase->type(); + info.item.nCategory = 0; // 类别为0表示设备 + info.item.sName = pBase->name(); + info.item.uid = pBase->uuid(); + + // 查找设备所属的间隔 + QString bayTag; + QString bayUuid; + QString sVoltage; + + // 通过间隔标签直接查找 + bayTag = pBase->getBay(); + + // 如果需要间隔UUID,可以反向查找 + if(!bayTag.isEmpty()){ + for(auto& pOtherItem:_pModel->getProjectBayItems()){ + BayProperty* pBay = dynamic_cast(pOtherItem->getProperty()); + if(pBay && pBay->tag() == bayTag){ + bayUuid = pBay->uuid().toString(); + sVoltage = QString::number(pBay->getVoltage()); + break; + } + } + } + + if(!bayTag.isEmpty()){ + // 设置父间隔信息 + info.parent.nEquipType = 0; + info.parent.nCategory = 1; + info.parent.sName = bayTag; + info.parent.uid = QUuid(bayUuid); + info.parent.sVoltageLevel = sVoltage; + info.parent.grid = sG; + info.parent.zone = sZ; + info.parent.station = sS; + } + + lstSecond.append(info); + } + + emit _pModel->updateTopologyItems(lstSecond, false,true); + + QList lstRef = DataBase::GetInstance()->getHMIRefAll(QUuid(_pEntity->id())); + _pModel->getHMIimageRef() = lstRef; + + QMap>> imageMap; + + for (const auto &imageRef : lstRef) { + imageMap[imageRef.model].append(qMakePair(imageRef.hash256, imageRef.slot)); + } + + for(auto it = imageMap.begin();it != imageMap.end();++it){ + QString model = it.key(); // 获取 key + QList>& lstModel = it.value(); // 获取 value + // 处理逻辑... + + QMap svgMap; + for (auto& pair : lstModel) { + QString picName = mapHmiSource.value(pair.first).imageName; + QByteArray data = mapHmiSource.value(pair.first).svgData; + svgMap.insert(picName, data); + } + int nType = _pModel->getModelState().value(model).modelType; + _pModel->updateModelIcon("",model,svgMap); //更新全部 //***暂没有判断基模名称 + } + } +} + +void MonitorPanel::saveNodes(int pageId) +{ + +} + +void MonitorPanel::updateSelectedItems(QList lst,bool val) +{ + if(val){ + QStandardItem *root = _itemListmodel->invisibleRootItem(); + + int rowCount = root->rowCount(); + if (rowCount > 0) { + _itemListmodel->removeRows(0, rowCount); + } + } + + // 第一次循环:创建第一层间隔层(nCategory == 1) + QHash intervalItems; // 用于存储间隔项,方便后续查找 + + for (auto &info : lst) { + auto curItem = info.item; + + // 只处理间隔层(nCategory == 1) + if (curItem.nCategory == 1) { + // 创建间隔项 + QStandardItem *pInterval = new QStandardItem(curItem.sName); + pInterval->setData(curItem.nCategory, Qt::UserRole + 1); + pInterval->setData(curItem.nEquipType, Qt::UserRole + 2); + pInterval->setData(curItem.uid, Qt::UserRole + 3); + + // 添加到模型 + _itemListmodel->appendRow(pInterval); + + // 存储到哈希表,方便第二次循环查找 + intervalItems.insert(curItem.sName, pInterval); + } + } + + // 第二次循环:处理设备(nCategory == 0),统一添加到相应的间隔下 + for (auto &info : lst) { + auto curItem = info.item; + + // 只处理设备(nCategory == 0) + if (curItem.nCategory == 0) { + // 创建设备项 + QStandardItem *pDevice = new QStandardItem(curItem.sName); + pDevice->setData(curItem.nCategory, Qt::UserRole + 1); + pDevice->setData(curItem.nEquipType, Qt::UserRole + 2); + pDevice->setData(curItem.uid, Qt::UserRole + 3); + + // 查找父间隔 + if (!info.parent.sName.isEmpty()) { + // 有父间隔的设备 + QStandardItem *pParentInterval = intervalItems.value(info.parent.sName, nullptr); + + if (pParentInterval) { + // 找到父间隔,直接挂接 + pParentInterval->appendRow(pDevice); + } else { + // 父间隔不存在,创建新的间隔 + QStandardItem *pNewInterval = new QStandardItem(info.parent.sName); + pNewInterval->setData(info.parent.nCategory, Qt::UserRole + 1); + pNewInterval->setData(info.parent.nEquipType, Qt::UserRole + 2); + pNewInterval->setData(info.parent.uid, Qt::UserRole + 3); + _itemListmodel->appendRow(pNewInterval); + pNewInterval->appendRow(pDevice); + intervalItems.insert(info.parent.sName, pNewInterval); + } + } else { + // 无父间隔的设备 - 直接添加到顶层 + _itemListmodel->appendRow(pDevice); + } + + // 添加子设备(如果有) + for (auto &subInfo : info.subList) { + QStandardItem *pSub = new QStandardItem(subInfo.sName); + pSub->setData(subInfo.nCategory, Qt::UserRole + 1); + pSub->setData(subInfo.nEquipType, Qt::UserRole + 2); + pSub->setData(subInfo.uid, Qt::UserRole + 3); + pDevice->appendRow(pSub); + } + } + } + + _sideBar->getItemsDlg()->updateItems(); + _pConfigDlg->updateSelectedItems(); +} + +void MonitorPanel::initMonitorConfig() +{ + +} + +void MonitorPanel::itemSelected(QUuid uid) +{ + _sideBar->getAttributeDlg()->clearAllGroup(); + _sideBar->getAttributeDlg()->generateAttributeGroups(uid); +} + +void MonitorPanel::detailItemSelected(QUuid uid) +{ + _detailAttributeDlg->show(); + _detailAttributeDlg->generateAttributeGroups(uid); +} + +void MonitorPanel::onRunClicked() +{ + if(_pModel){ + _pModel->startAcceptData(); + } +} + +void MonitorPanel::onStopClicked() +{ + if(_pModel){ + _pModel->stopAcceptData(_name); + } +} + +void MonitorPanel::onSaveClicked() +{ + _pModel->getCavas()->onSignal_savePage(); +} + +void MonitorPanel::onConfigClicked() +{ + _pConfigDlg->show(); +} + +void MonitorPanel::onItemConfigClicked() +{ + _displaySettingDlg->showDlg(); +} + +void MonitorPanel::onConncecClicked() +{ + +} + +void MonitorPanel::initDisplayState() +{ + auto &mapState = _pModel->getMonitorStateMap(); + + QList> lst_bus; + lst_bus.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_bus.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_bus.append(qMakePair(MonitorItemState::Energized,"带电")); + lst_bus.append(qMakePair(MonitorItemState::DeEnergized,"失电")); + lst_bus.append(qMakePair(MonitorItemState::Grounding,"接地")); + mapState.insert(1,lst_bus); + + QList> lst_asyncmotor; + lst_asyncmotor.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_asyncmotor.append(qMakePair(MonitorItemState::Alarm,"告警")); + lst_asyncmotor.append(qMakePair(MonitorItemState::LineBreak,"断线")); + lst_asyncmotor.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(2,lst_asyncmotor); + + QList> lst_cb; + lst_cb.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_cb.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_cb.append(qMakePair(MonitorItemState::Closing,"合闸")); + lst_cb.append(qMakePair(MonitorItemState::Opening,"分闸")); + lst_cb.append(qMakePair(MonitorItemState::AccidentTrip,"事故跳闸")); + mapState.insert(3,lst_cb); + + QList> lst_ct; + lst_ct.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_ct.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_ct.append(qMakePair(MonitorItemState::LineBreak,"断线")); + mapState.insert(4,lst_ct); + + QList> lst_pt; + lst_pt.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_pt.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_pt.append(qMakePair(MonitorItemState::LineBreak,"断线")); + mapState.insert(5,lst_pt); + + QList> lst_es; + lst_es.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_es.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_es.append(qMakePair(MonitorItemState::Closing,"合闸")); + lst_es.append(qMakePair(MonitorItemState::Opening,"分闸")); + mapState.insert(6,lst_es); + + QList> lst_fes; + lst_fes.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_fes.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_fes.append(qMakePair(MonitorItemState::Closing,"合闸")); + lst_fes.append(qMakePair(MonitorItemState::Opening,"分闸")); + mapState.insert(7,lst_fes); + + QList> lst_cable; + lst_cable.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_cable.append(qMakePair(MonitorItemState::Energized,"带电")); + lst_cable.append(qMakePair(MonitorItemState::DeEnergized,"失电")); + lst_cable.append(qMakePair(MonitorItemState::Grounding,"接地")); + mapState.insert(8,lst_cable); + + QList> lst_ds; + lst_ds.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_ds.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_ds.append(qMakePair(MonitorItemState::Closing,"合闸")); + lst_ds.append(qMakePair(MonitorItemState::Opening,"分闸")); + mapState.insert(9,lst_ds); + + QList> lst_dteds; + lst_dteds.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_dteds.append(qMakePair(MonitorItemState::StatusFault,"故障")); + lst_dteds.append(qMakePair(MonitorItemState::Closing,"合闸")); + lst_dteds.append(qMakePair(MonitorItemState::Opening,"分闸")); + mapState.insert(10,lst_dteds); + + QList> lst_potentialIndicator; + lst_potentialIndicator.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_potentialIndicator.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(11,lst_potentialIndicator); + + QList> lst_lightningArrester; + lst_lightningArrester.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_lightningArrester.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(12,lst_lightningArrester); + + QList> lst_cableTermination; + lst_cableTermination.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_cableTermination.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(13,lst_cableTermination); + + QList> lst_cableEnd; + lst_cableEnd.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_cableEnd.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(14,lst_cableEnd); + + QList> lst_transformer2w; + lst_transformer2w.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_transformer2w.append(qMakePair(MonitorItemState::Alarm,"告警")); + lst_transformer2w.append(qMakePair(MonitorItemState::LineBreak,"断线")); + lst_transformer2w.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(15,lst_transformer2w); + + QList> lst_transformer3w; + lst_transformer3w.append(qMakePair(MonitorItemState::Normal,"正常")); + lst_transformer3w.append(qMakePair(MonitorItemState::Alarm,"告警")); + lst_transformer3w.append(qMakePair(MonitorItemState::LineBreak,"断线")); + lst_transformer3w.append(qMakePair(MonitorItemState::StatusFault,"故障")); + mapState.insert(16,lst_transformer3w); +} + +void MonitorPanel::initDisplaySetting() +{ + auto stateMap = _pModel->getMonitorStateMap(); + QMap mapType = DataBase::GetInstance()->getAllComponentType(); //所有类型 + QMap mapUsed; //本图中使用的类型、模型名 + + auto mapItems = getModelController()->getHMIItems(); + for(auto& pItem:mapItems){ + auto pPro = pItem->getProperty(); + if(pPro){ + QString sMeta = pPro->metaModelName(); + QString sModel = pPro->modelName(); + int nType = pPro->type(); + if(!mapUsed.contains(nType)){ + ModelTypeInfo info; + info.nType = nType; + info.sType = mapType.value(nType).type; + info.sName = mapType.value(nType).name; + info.sMeta = sMeta; + info.sModel = sModel; + mapUsed.insert(nType,info); + } + } + } + + QMap mapAllType = DataBase::GetInstance()->ModelType(); + for(auto iter = mapUsed.begin();iter != mapUsed.end();++iter){ //遍历所有类型 + QMap mapStates; + QString sType = mapUsed.value(iter.key()).sType; + QString sName = mapUsed.value(iter.key()).sName; + QString sMeta = mapUsed.value(iter.key()).sMeta; + QString sModel = mapUsed.value(iter.key()).sModel; + + QList> lstState = stateMap.value(iter.key()); + for(auto &pair:lstState){ + MonitorItemDisplayInfo info; + info.nItemType = iter.key(); + info.sStateName = pair.second; + info.sMeta = sMeta; + info.sModel = sModel; + info.nWidth = 40; + info.nHeight = 40; + + QByteArray svg; + auto mapUsedSvg = ProjectModelManager::instance().getData()[sMeta][sModel].modelSetting.mapUsedSvg; + auto mapAllSvg = ProjectModelManager::instance().getData()[sMeta][sModel].modelSetting.mapSvg; + + if(!mapUsedSvg.empty()){ //先判断已选svg + svg = mapUsedSvg.first(); + } + else if(!mapAllSvg.empty()){ //后判断所有svg + svg = mapAllSvg.first(); + } + else { + for(auto &mt:mapAllType){ //最后采用初始svg + if(mt.modelName == iter->sName){ + svg = mt.icon; + break; + } + } + } + info.bytPicture = svg; + + MonitorItemStateStruct keyState; + keyState.eState = pair.first; + keyState.sState = pair.second; + mapStates.insert(keyState,info); + } + + MonitorItemTypeStruct keyType; + keyType.sTag = sType; + keyType.sName = sName; + _pModel->getMonitorDisplaySetting().insert(keyType,mapStates); + } +} + +QJsonArray MonitorPanel::serializeRelationToJsonArray(const QList& data) const +{ + QJsonArray rootArray; + + for (const auto& relationItem : data) { + rootArray.append(relationItem.toJson()); + } + + return rootArray; +} + +// 从 QJsonArray 反序列化到 QList +bool MonitorPanel::deserializeRelationFromJsonArray(const QJsonArray& jsonArray, QList& result) +{ + result.clear(); + + for (const QJsonValue& itemValue : jsonArray) { + if (!itemValue.isObject()) continue; + + HierarchyItem relationItem; + relationItem.fromJson(itemValue.toObject()); + + if (relationItem.isValid()) { + result.append(relationItem); + } + } + + return true; +} + +QJsonArray MonitorPanel::serializeParaToJsonArray(const QMap>& data) const +{ + QJsonArray rootArray; + + for (auto it = data.begin(); it != data.end(); ++it) { + const QUuid& uuid = it.key(); + const QList& attributeList = it.value(); + + QJsonObject uuidObject; + uuidObject["uuid"] = uuid.toString(); + + QJsonArray attributesArray; + + // 序列化属性列表 + for (const auto& attribute : attributeList) { + attributesArray.append(attribute.toJson()); + } + + uuidObject["attributes"] = attributesArray; + uuidObject["attributeCount"] = attributesArray.size(); + + rootArray.append(uuidObject); + } + + return rootArray; +} + +// 从 QJsonArray 反序列化到 QMap> +bool MonitorPanel::deserializeParaFromJsonArray(const QJsonArray& jsonArray, + QMap>& result) +{ + result.clear(); + + for (const QJsonValue& uuidValue : jsonArray) { + if (!uuidValue.isObject()) continue; + + QJsonObject uuidObject = uuidValue.toObject(); + + // 解析UUID + QUuid uuid = QUuid::fromString(uuidObject["uuid"].toString()); + if (uuid.isNull()) continue; + + // 解析属性列表 + QList attributeList; + QJsonArray attributesArray = uuidObject["attributes"].toArray(); + + for (const QJsonValue& attrValue : attributesArray) { + if (!attrValue.isObject()) continue; + + MonitorItemAttributeInfo attribute; + attribute.fromJson(attrValue.toObject()); + attributeList.append(attribute); + } + + result[uuid] = attributeList; + } + + return true; +} + + +QJsonArray MonitorPanel::serializeDisplayToJsonArray(const QMap>& data) const +{ + + QJsonArray rootArray; + + for (auto typeIt = data.begin(); typeIt != data.end(); ++typeIt) { + const MonitorItemTypeStruct& typeKey = typeIt.key(); + const auto& stateMap = typeIt.value(); + + QJsonObject typeObject; + typeObject["typeKey"] = typeKey.toJson(); + + QJsonArray statesArray; + + for (auto stateIt = stateMap.begin(); stateIt != stateMap.end(); ++stateIt) { + const MonitorItemStateStruct& stateKey = stateIt.key(); + const MonitorItemDisplayInfo& displayInfo = stateIt.value(); + + QJsonObject stateObject; + stateObject["stateKey"] = stateKey.toJson(); + stateObject["displayInfo"] = displayInfo.toJson(); + + statesArray.append(stateObject); + } + + typeObject["states"] = statesArray; + rootArray.append(typeObject); + } + + return rootArray; +} + +// 从JSON数组反序列化 +void MonitorPanel::deserializeDisplayFromJsonArray(const QJsonArray& jsonArray,QMap>& result) +{ + + result.clear(); + + for (const QJsonValue& typeValue : jsonArray) { + if (!typeValue.isObject()) continue; + + QJsonObject typeObject = typeValue.toObject(); + + // 解析类型键 + MonitorItemTypeStruct typeKey; + typeKey.fromJson(typeObject["typeKey"].toObject()); + + QMap stateMap; + QJsonArray statesArray = typeObject["states"].toArray(); + + for (const QJsonValue& stateValue : statesArray) { + if (!stateValue.isObject()) continue; + + QJsonObject stateObject = stateValue.toObject(); + + MonitorItemStateStruct stateKey; + stateKey.fromJson(stateObject["stateKey"].toObject()); + + MonitorItemDisplayInfo displayInfo; + displayInfo.fromJson(stateObject["displayInfo"].toObject()); + + stateMap[stateKey] = displayInfo; + } + + result[typeKey] = stateMap; + } +} diff --git a/diagramCavas/source/monitorSelectedItemsDlg.cpp b/diagramCavas/source/monitorSelectedItemsDlg.cpp new file mode 100644 index 0000000..beb582b --- /dev/null +++ b/diagramCavas/source/monitorSelectedItemsDlg.cpp @@ -0,0 +1,60 @@ +#include "monitorSelectedItemsDlg.h" +#include "monitorSideBarDlg.h" +#include "monitorPanel.h" +#include + + +MonitorSelectedItemsDlg::MonitorSelectedItemsDlg(QWidget* parent) + : QDialog(parent) + ,_treeView(nullptr) + ,_parent(nullptr) + ,_pLayout(nullptr) +{ + _parent = dynamic_cast(parent); + initial(); +} + +MonitorSelectedItemsDlg::~MonitorSelectedItemsDlg() +{ + +} + +void MonitorSelectedItemsDlg::initial() +{ + _treeView = new QTreeView(this); + _treeView->setModel(_parent->getParent()->getLstModel()); + _treeView->setHeaderHidden(true); + + // 创建标题栏 + QWidget *titleBar = new QWidget(this); + titleBar->setFixedHeight(21); + titleBar->setStyleSheet("background-color: #2b579a; color: white;"); + + // 标题栏布局 + QHBoxLayout *titleLayout = new QHBoxLayout(titleBar); + titleLayout->setContentsMargins(10, 0, 5, 0); + + // 标题标签 + QLabel *titleLabel = new QLabel("图元列表"); + titleLabel->setStyleSheet("color: white; font-weight: bold;"); + + titleLayout->addWidget(titleLabel); + _pLayout = new QVBoxLayout(this); + _pLayout->addWidget(titleBar); + _pLayout->addWidget(_treeView); + connect(_treeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &MonitorSelectedItemsDlg::onSelectionChanged); +} + +void MonitorSelectedItemsDlg::updateItems() +{ + _treeView->expandAll(); +} + +void MonitorSelectedItemsDlg::onSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if(current.isValid()){ + QUuid uid = current.data(Qt::UserRole+3).toUuid(); + _parent->getParent()->getModelController()->monitorItemSelected(uid); + } +} diff --git a/diagramCavas/source/monitorSideBarDlg.cpp b/diagramCavas/source/monitorSideBarDlg.cpp new file mode 100644 index 0000000..1c6a44e --- /dev/null +++ b/diagramCavas/source/monitorSideBarDlg.cpp @@ -0,0 +1,44 @@ +#include "monitorSideBarDlg.h" +#include "monitorSelectedItemsDlg.h" +#include "monitorAttributeDlg.h" +#include "monitorPanel.h" +#include +#include + +MonitorSideBarDlg::MonitorSideBarDlg(QWidget* parent) + : QDialog(parent) + ,_itemsDlg(nullptr) + ,_attributeDlg(nullptr) + ,_parent(nullptr) +{ + _parent = dynamic_cast(parent); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + initial(); + setMinimumWidth(200); +} + +MonitorSideBarDlg::~MonitorSideBarDlg() +{ + +} + +void MonitorSideBarDlg::initial() +{ + _itemsDlg = new MonitorSelectedItemsDlg(this); + _attributeDlg = new MonitorAttributeDlg(this); + + QVBoxLayout *sideLayout = new QVBoxLayout(this); + sideLayout->setContentsMargins(0, 0, 0, 0); + sideLayout->setSpacing(0); + + // 创建垂直分割器 + QSplitter *splitter = new QSplitter(Qt::Vertical); + splitter->setHandleWidth(1); // 设置分割条宽度 + + splitter->addWidget(_itemsDlg); + splitter->addWidget(_attributeDlg); + + splitter->setStretchFactor(0, 1); // 第一个部件的拉伸因子为1 + splitter->setStretchFactor(1, 3); // 第二个部件的拉伸因子为3 + sideLayout->addWidget(splitter); +} diff --git a/diagramCavas/source/monitorToolBox.cpp b/diagramCavas/source/monitorToolBox.cpp new file mode 100644 index 0000000..f066106 --- /dev/null +++ b/diagramCavas/source/monitorToolBox.cpp @@ -0,0 +1,73 @@ +#include "monitorToolBox.h" +#include "monitorToolPage.h" + +#include +#include + +MonitorToolBox::MonitorToolBox(QWidget *parent) : + QScrollArea(parent) + ,m_pContentVBoxLayout(nullptr) + ,_container(nullptr) +{ + _container = new QWidget; + m_pContentVBoxLayout = new QVBoxLayout(_container); + setWidget(_container); + m_pContentVBoxLayout->setContentsMargins(0, 0, 0, 0); + setWidgetResizable(true); +} + +MonitorToolBox::~MonitorToolBox() +{ + +} + +void MonitorToolBox::addWidget(const QString &title, QWidget *pWidget) +{ + MonitorToolPage *page = new MonitorToolPage(this); + page->addWidget(title, pWidget); + + m_pContentVBoxLayout->addWidget(page); + m_mapWidget.insert(title,page); +} + +void MonitorToolBox::removeWidget(const QString &title) +{ + bool bExist = m_mapWidget.contains(title); + if(bExist) + { + QWidget *pWidget = m_mapWidget.take(title); + if(pWidget) + { + MonitorToolPage* toolPage = dynamic_cast(pWidget); + if(toolPage) + { + m_pContentVBoxLayout->removeWidget(toolPage); + delete toolPage; + } + + } + } + else + { + //cerr + } + +} + +void MonitorToolBox::removeAllWidget() +{ + for(auto& pWidget:m_mapWidget){ + if(pWidget) + { + MonitorToolPage* toolPage = dynamic_cast(pWidget); + if(toolPage) + { + m_pContentVBoxLayout->removeWidget(toolPage); + delete toolPage; + } + + } + } + + m_mapWidget.clear(); +} diff --git a/diagramCavas/source/monitorToolPage.cpp b/diagramCavas/source/monitorToolPage.cpp new file mode 100644 index 0000000..f079993 --- /dev/null +++ b/diagramCavas/source/monitorToolPage.cpp @@ -0,0 +1,77 @@ +#include "monitorToolPage.h" + +#include +#include +#include +#include +#include +#include + +MonitorToolPage::MonitorToolPage(QWidget *parent) : + QWidget(parent), + m_bIsExpanded(true), + m_pLabel(nullptr), + m_pPushButtonFold(nullptr), + m_pContent(nullptr) +{ + setAttribute(Qt::WA_StyledBackground); + + m_pPushButtonFold = new QPushButton(this); + m_pLabel = new QLabel(this); + m_pLabel->setFixedSize(20, 20); + m_pLabel->setText("+"); + QHBoxLayout *hLayout = new QHBoxLayout(m_pPushButtonFold); + QVBoxLayout *vLayout = new QVBoxLayout(this); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(2); + vLayout->addWidget(m_pPushButtonFold); + hLayout->setContentsMargins(0, 0, 5, 0); + hLayout->addStretch(1); + hLayout->addWidget(m_pLabel); + + connect(m_pPushButtonFold, &QPushButton::clicked, this, &MonitorToolPage::onPushButtonFoldClicked); +} + +MonitorToolPage::~MonitorToolPage() +{ + if(m_pContent) + delete m_pContent; + if(m_pPushButtonFold) + delete m_pPushButtonFold; +} + +void MonitorToolPage::addWidget(const QString &title, QWidget *widget) +{ + if(!m_pContent) + { + m_pPushButtonFold->setText(title); + layout()->addWidget(widget); + m_pContent = widget; + } + +} + +void MonitorToolPage::expand() +{ + if(m_pContent) + m_pContent->show(); + m_bIsExpanded = true; + m_pLabel->setText("-"); +} + +void MonitorToolPage::collapse() +{ + if(m_pContent) + m_pContent->hide(); + m_bIsExpanded = false; + m_pLabel->setText("+"); +} + +void MonitorToolPage::onPushButtonFoldClicked() +{ + if (m_bIsExpanded) { + collapse(); + } else { + expand(); + } +} diff --git a/diagramCavas/source/powerConnection.cpp b/diagramCavas/source/powerConnection.cpp new file mode 100644 index 0000000..ab3c4d6 --- /dev/null +++ b/diagramCavas/source/powerConnection.cpp @@ -0,0 +1,20 @@ +#include +#include "powerConnection.h" + +PowerConnection::PowerConnection(const QString& uuid,const QString& fromTerminal, const QString& toTerminal,const QString& fromId,const QString& toId, QObject* parent) + : QObject(parent),m_uuid(uuid), m_fromTerminal(fromTerminal), m_toTerminal(toTerminal),m_fromComponent(fromId),m_toComponent(toId) {} + + +PowerConnection* PowerConnection::clone() +{ + PowerConnection* newCon = new PowerConnection(m_uuid,m_fromTerminal,m_toTerminal,m_fromComponent,m_toComponent); + return newCon; +} + +QJsonObject PowerConnection::toJson() const { + QJsonObject obj; + obj["id"] = m_uuid; + obj["from"] = m_fromTerminal; + obj["to"] = m_toTerminal; + return obj; +} diff --git a/diagramCavas/source/powerEntity.cpp b/diagramCavas/source/powerEntity.cpp new file mode 100644 index 0000000..0793461 --- /dev/null +++ b/diagramCavas/source/powerEntity.cpp @@ -0,0 +1,43 @@ +#include +#include +#include "powerEntity.h" +#include "powerTerminal.h" + +QJsonObject PowerEntity::toJson() const { + QJsonObject obj; + obj["id"] = m_id; + obj["type"] = static_cast(m_type); + obj["name"] = m_name; + + QJsonArray childrenArray; + for (auto child : m_children) + childrenArray.append(child->id()); + obj["children"] = childrenArray; + + return obj; +} + +void PowerEntity::addTerminal(PowerTerminal* terminal) { + if (terminal && terminal->parentEntityId() == m_id) { + m_terminals.append(terminal); + terminal->setParent(this); + emit terminalAdded(terminal); + } +} + +void PowerEntity::removeTerminal(const QString& terminalId) { + auto it = std::find_if(m_terminals.begin(), m_terminals.end(), + [terminalId](PowerTerminal* t) { return t->id() == terminalId; }); + if (it != m_terminals.end()) { + PowerTerminal* term = *it; + m_terminals.erase(it); + term->deleteLater(); + emit terminalRemoved(terminalId); + } +} + +PowerTerminal* PowerEntity::findTerminal(const QString& terminalId) const { + auto it = std::find_if(m_terminals.begin(), m_terminals.end(), + [terminalId](PowerTerminal* t) { return t->id() == terminalId; }); + return (it != m_terminals.end()) ? *it : nullptr; +} diff --git a/diagramCavas/source/powerTerminal.cpp b/diagramCavas/source/powerTerminal.cpp new file mode 100644 index 0000000..a244df8 --- /dev/null +++ b/diagramCavas/source/powerTerminal.cpp @@ -0,0 +1,53 @@ +#include "powerTerminal.h" + +PowerTerminal::PowerTerminal(const QString& parentEntityId, + TerminalType type, + const QString& name, + const QPointF& relativePos, + const QString& uuid, + const double dPerX, + const double dPerY, + QObject* parent) + : QObject(parent), + m_id(uuid), + m_parentEntityId(parentEntityId), + m_type(type), + m_name(name), + m_relativePosition(relativePos), + m_dPerX(dPerX), + m_dPerY(dPerY) +{ + if(m_id.isEmpty()) + m_id = QUuid::createUuid().toString(); +} + +void PowerTerminal::setRelativePosition(const QPointF& newPos) { + if (m_relativePosition != newPos) { + m_relativePosition = newPos; + emit positionChanged(newPos); + } +} + +QJsonObject PowerTerminal::toJson() const { + QJsonObject obj; + obj["id"] = m_id; + obj["parentEntity"] = m_parentEntityId; + obj["type"] = static_cast(m_type); + obj["name"] = m_name; + obj["relX"] = m_relativePosition.x(); + obj["relY"] = m_relativePosition.y(); + return obj; +} + +PowerTerminal* PowerTerminal::fromJson(const QJsonObject& json, QObject* parent) { + QString id = json["id"].toString(); + QString parentId = json["parentEntity"].toString(); + TerminalType type = static_cast(json["type"].toInt()); + QString name = json["name"].toString(); + qreal x = json["relX"].toDouble(); + qreal y = json["relY"].toDouble(); + + PowerTerminal* term = new PowerTerminal(parentId, type, name, QPointF(x, y),id,0,0, parent); //***不再使用*** + term->m_id = id; // 注意:需要修改m_id为可写,或使用其他机制保持ID一致 + return term; +} diff --git a/diagramCavas/source/projectIconSelectionDlg.cpp b/diagramCavas/source/projectIconSelectionDlg.cpp new file mode 100644 index 0000000..4e087c3 --- /dev/null +++ b/diagramCavas/source/projectIconSelectionDlg.cpp @@ -0,0 +1,54 @@ +#include "projectIconSelectionDlg.h" +#include +#include +#include + +ProjectIconSelectionDlg::ProjectIconSelectionDlg(const QMap mapSvg,QWidget* parent) + : QDialog(parent),svgMap(mapSvg) +{ + + this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint| windowFlags()); + setWindowTitle("选择SVG图标"); + resize(400, 300); + + QVBoxLayout* layout = new QVBoxLayout(this); + + listWidget = new QListWidget(this); + listWidget->setIconSize(QSize(32, 32)); + listWidget->setViewMode(QListWidget::IconMode); + listWidget->setResizeMode(QListWidget::Adjust); + listWidget->setStyleSheet( + "QListWidget::item:selected {" + " background: #4e72b8;" + " color: palette(HighlightedText);" + "}" + ); + + for (auto& svgData : svgMap) { + QSvgRenderer renderer(svgData); + QPixmap pixmap(32, 32); + pixmap.fill(Qt::white); + QPainter painter(&pixmap); + renderer.render(&painter); + + QListWidgetItem* item = new QListWidgetItem(QIcon(pixmap), ""); + item->setData(Qt::UserRole, svgData); + listWidget->addItem(item); + } + + QDialogButtonBox* buttonBox = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + + layout->addWidget(listWidget); + layout->addWidget(buttonBox); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +QByteArray ProjectIconSelectionDlg::selectedSVG() const { + if (listWidget->currentItem()) { + return listWidget->currentItem()->data(Qt::UserRole).toByteArray(); + } + return QByteArray(); +} diff --git a/diagramCavas/source/projectIconSetting.cpp b/diagramCavas/source/projectIconSetting.cpp new file mode 100644 index 0000000..b506e33 --- /dev/null +++ b/diagramCavas/source/projectIconSetting.cpp @@ -0,0 +1,139 @@ +#include "projectIconSetting.h" +#include "ui_projectIconSetting.h" +#include "graphicsDataModel/fixedPortsModel.h" +#include "projectIconSelectionDlg.h" +#include "graphicsItem/graphicsBaseItem.h" +#include "baseProperty.h" +#include "projectModelManager.h" +#include +#include +//#include "global.h" + +ProjectIconSetting::ProjectIconSetting(QWidget *parent) + : QDialog(parent) + , ui(new Ui::projectIconSetting) + ,_controller(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | windowFlags()); + initial(); +} + +ProjectIconSetting::~ProjectIconSetting() +{ + delete ui; +} + +void ProjectIconSetting::showDlg(GraphicsProjectModelItem* pItem) +{ + ui->tableWidget->clear(); + ui->tableWidget->setRowCount(0); + show(); + ModelProperty* p = pItem->getProperty(); + BaseProperty* pro = dynamic_cast(p); + if(pro) + { + QString sName = pro->name(); + QString sMetaModel = pro->metaModelName(); + QString sModel = pro->modelName(); + _sMetaModel = sMetaModel; + _sModel = sModel; + + bool bExist = ProjectModelManager::instance().ifProjectExsit(sModel); + if(bExist){ + auto mapUsed = ProjectModelManager::instance().getData()[sMetaModel][sModel].modelSetting.mapUsedSvg; + addItems(mapUsed); + } + } +} + +void ProjectIconSetting::initial() +{ + QStringList headerText; + headerText<<"类别"<<"图标"; + ui->tableWidget->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectItems); + ui->tableWidget->setColumnCount(headerText.count()); + ui->tableWidget->setHorizontalHeaderLabels(headerText); + ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + connect(ui->tableWidget, &QTableWidget::cellClicked, this, &ProjectIconSetting::onCellClicked); + connect(ui->btn_ok,&QPushButton::clicked,this,&ProjectIconSetting::onOkClicked); + _iconSize = QSize(32,32); +} + +void ProjectIconSetting::addItems(QMap mapSvg) +{ + for(auto iter = mapSvg.begin();iter != mapSvg.end();++iter){ + int row = ui->tableWidget->rowCount(); + ui->tableWidget->insertRow(row); + + QTableWidgetItem* tagItem = new QTableWidgetItem(iter.key()); + ui->tableWidget->setItem(row, 0, tagItem); + + QTableWidgetItem* iconItem = new QTableWidgetItem(); + ui->tableWidget->setItem(row, 1, iconItem); + QByteArray arr = iter.value(); + iconItem->setData(Qt::UserRole, arr); + if(!arr.isEmpty()){ + QSvgRenderer renderer(arr); + + // 创建目标大小的pixmap + QPixmap pixmap(_iconSize); + pixmap.fill(Qt::transparent); // 透明背景 + + // 创建画家并在pixmap上绘制SVG + QPainter painter(&pixmap); + renderer.render(&painter, pixmap.rect()); + + iconItem->setIcon(pixmap); + } + } +} + +void ProjectIconSetting::selectImage(int row) +{ + if(!_sMetaModel.isEmpty() && !_sModel.isEmpty()){ + auto mapAllSvg = ProjectModelManager::instance().getData()[_sMetaModel][_sModel].modelSetting.mapSvg; + ProjectIconSelectionDlg dialog(mapAllSvg, this); + if (dialog.exec() == QDialog::Accepted) { + QByteArray selectedSVG = dialog.selectedSVG(); + if (!selectedSVG.isEmpty()) { + QSvgRenderer renderer(selectedSVG); + QPixmap pixmap(32, 32); + //pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + renderer.render(&painter); + + ui->tableWidget->item(row, 1)->setIcon(QIcon(pixmap)); + // 如果需要保存原始SVG数据,可以这样: + ui->tableWidget->item(row, 1)->setData(Qt::UserRole, selectedSVG); + } + } + } +} + +void ProjectIconSetting::onOkClicked() +{ + QMap mapUsedSvg; + for (int row = 0; row < ui->tableWidget->rowCount(); ++row) { + QTableWidgetItem *itemTag = ui->tableWidget->item(row, 0); + if (itemTag) { + QTableWidgetItem *itemSvg = ui->tableWidget->item(row, 1); + if(itemSvg){ + QByteArray svg = itemSvg->data(Qt::UserRole).toByteArray(); + mapUsedSvg.insert(itemTag->text(),svg); + } + } + } + _controller->updateItemIcon(_sMetaModel,_sModel,mapUsedSvg); + hide(); +} + +void ProjectIconSetting::onCellClicked(int row,int col) +{ + if (col == 1) { // 如果是按钮列 + selectImage(row); + } +} diff --git a/diagramCavas/source/propertyContentDlg.cpp b/diagramCavas/source/propertyContentDlg.cpp new file mode 100644 index 0000000..5ed2fbe --- /dev/null +++ b/diagramCavas/source/propertyContentDlg.cpp @@ -0,0 +1,366 @@ +#include "propertyContentDlg.h" +#include "baseProperty.h" +#include +#include +#include +#include +#include +#include + +PropertyContentDlg::PropertyContentDlg(QWidget *parent) + : BaseContentDlg(parent) +{ + _layout = new QVBoxLayout(this); +} + +PropertyContentDlg::~PropertyContentDlg() +{ + +} + +void PropertyContentDlg::createGroupView(GroupStateInfo infos) +{ + QScrollArea* scrollArea = new QScrollArea(this); + QWidget* content = new QWidget(); + QFormLayout* formLayout = createFormLayout(content); + + // 动态生成字段 + for(auto& info:infos.info) { + QLabel* label = new QLabel(info.tagName,this); + QWidget* editor = createEditor(info); + formLayout->addRow(label, editor); + } + + scrollArea->setWidget(content); + scrollArea->setWidgetResizable(true); + _layout->addWidget(scrollArea); +} + +QWidget* PropertyContentDlg::createEditor(PropertyStateInfo pro) +{ + QWidget* pWidget = nullptr; + if(pro.type.contains("SMALLINT")) + { + QSpinBox* spin = new QSpinBox(this); + if(pro.lengthPrecision > 0) + spin->setRange(-pro.lengthPrecision,pro.lengthPrecision); + else + spin->setRange(-32768, 32767); + pWidget = spin; + } + else if(pro.type.contains("INTEGER")) + { + QSpinBox* spin = new QSpinBox(this); + if(pro.lengthPrecision > 0) + spin->setRange(-pro.lengthPrecision,pro.lengthPrecision); + else + spin->setRange(-32768,32767); + + pWidget = spin; + } + else if(pro.type.contains("BIGINT")) + { + QLineEdit *lineEdit = new QLineEdit(this); + QRegularExpression regExp("^[+-]?(0|[1-9][0-9]{0,18})$"); + QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, this); + lineEdit->setValidator(validator); + pWidget = lineEdit; + } + else if(pro.type.contains("REAL")) + { + QDoubleSpinBox* dbSpin = new QDoubleSpinBox(this); + dbSpin->setDecimals(4); + if(pro.lengthPrecision > 0) + dbSpin->setRange(-pro.lengthPrecision,pro.lengthPrecision); + else + dbSpin->setRange(-9999999,9999999); + pWidget = dbSpin; + } + else if(pro.type.contains("DOUBLE PRECISION")) + { + QDoubleSpinBox* dbSpin = new QDoubleSpinBox(this); + dbSpin->setDecimals(8); + if(pro.lengthPrecision > 0) + dbSpin->setRange(-pro.lengthPrecision,pro.lengthPrecision); + else + dbSpin->setRange(-9999999,9999999); + pWidget = dbSpin; + } + else if(pro.type.contains("NUMERIC") || pro.type.contains("DECIMAL")) + { + QLineEdit *lineEdit = new QLineEdit(this); + + // 正则表达式:支持正负号、整数/小数、科学计数法 + QRegularExpression regExp( + "^[+-]?" // 可选正负号 + "(?:0|[1-9]\\d*)(?:\\.\\d+)?" // 整数部分(避免前导零)和小数部分 + "(?:[eE][+-]?\\d+)?" // 科学计数法(如e5, E-3) + ); + QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, this); + lineEdit->setValidator(validator); + pWidget = lineEdit; + } + else if(pro.type.contains("SERIAL") || pro.type.contains("BIGSERIAL")) + { + QLabel *label = new QLabel(this); + pWidget = label; + } + else if(pro.type.contains("CHAR") || pro.type.contains("VARCHAR")) + { + QLineEdit *lineEdit = new QLineEdit(this); + lineEdit->setMaxLength(pro.lengthPrecision); + pWidget = lineEdit; + } + else if(pro.type.contains("TEXT")) + { + QLineEdit *lineEdit = new QLineEdit(this); + pWidget = lineEdit; + } + else if(pro.type.contains("BYTEA")) + { + //todo:二进制文件上传 + QLabel *label = new QLabel(this); + pWidget = label; + } + else if(pro.type.contains("DATE")) + { + QDateEdit* dateEdit = new QDateEdit(this); + dateEdit->setDisplayFormat("yyyy-MM-dd"); + pWidget = dateEdit; + } + else if(pro.type.contains("TIME")) + { + QTimeEdit* timeEdit = new QTimeEdit(this); + timeEdit->setDisplayFormat("HH:mm:ss"); + pWidget = timeEdit; + } + else if(pro.type.contains("TIMESTAMP")) + { + QDateTimeEdit* dateTimeEidt = new QDateTimeEdit(this); + dateTimeEidt->setDisplayFormat("yyyy-MM-dd HH:mm:ss"); + pWidget = dateTimeEidt; + } + else if(pro.type.contains("UUID")) + { + QLineEdit *lineEdit = new QLineEdit(this); + QRegularExpression regExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, this); + lineEdit->setValidator(validator); + pWidget = lineEdit; + } + else if(pro.type.contains("JSON") || pro.type.contains("JSONB")) + { + QLineEdit *lineEdit = new QLineEdit(this); + pWidget = lineEdit; + } + if(pWidget) + { + pWidget->setProperty("name",pro.tagName); + PropertyContentInfo info; + info.proTag = pro.tagName; + info.proName = pro.name; + info.proType = pro.type; + info.proEditor = pWidget; + _mapPro.insert(pro.tagName,info); + } + return pWidget; +} + +QMap PropertyContentDlg::getPropertyValue(BaseProperty* pPro) +{ + QMap map; + + for(auto &pro:_mapPro) + { + PropertyStateInfo info; + info.type = pro.proType; + info.name = pro.proName; + info.tagName = pro.proTag; + if(pro.proEditor != nullptr) + { + if(pro.proType.contains("SMALLINT") || pro.proType.contains("INTEGER")) + { + QSpinBox* spin = qobject_cast(pro.proEditor); + if(spin) + info.defaultValue = spin->value(); + } + else if(pro.proType.contains("BIGINT")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + info.defaultValue = lineEdit->text(); + } + else if(pro.proType.contains("REAL") || pro.proType.contains("DOUBLE PRECISION")) + { + QDoubleSpinBox* dbSpin = qobject_cast(pro.proEditor); + if(dbSpin) + info.defaultValue = dbSpin->value(); + } + else if(pro.proType.contains("NUMERIC") || pro.proType.contains("DECIMAL")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + info.defaultValue = lineEdit->text(); + } + else if(pro.proType.contains("SERIAL") || pro.proType.contains("BIGSERIAL")) + { + QLabel* label = qobject_cast(pro.proEditor); + if(label) + info.defaultValue = label->text(); + } + else if(pro.proType.contains("CHAR") || pro.proType.contains("VARCHAR")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + info.defaultValue = lineEdit->text(); + } + else if(pro.proType.contains("TEXT")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + info.defaultValue = lineEdit->text(); + } + else if(pro.proType.contains("BYTEA")) + { + QLabel* label = qobject_cast(pro.proEditor); + if(label) + info.defaultValue = label->text(); + } + else if(pro.proType.contains("DATE")) + { + QDateEdit* dateEdit = qobject_cast(pro.proEditor); + if(dateEdit) + info.defaultValue = dateEdit->text(); + } + else if(pro.proType.contains("TIME")) + { + QTimeEdit* timeEdit = qobject_cast(pro.proEditor); + if(timeEdit) + info.defaultValue = timeEdit->text(); + } + else if(pro.proType.contains("TIMESTAMP")) + { + QDateTimeEdit* dateTimeEidt = qobject_cast(pro.proEditor); + if(dateTimeEidt) + info.defaultValue = dateTimeEidt->text(); + } + else if(pro.proType.contains("UUID")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + info.defaultValue = lineEdit->text(); + } + else if(pro.proType.contains("JSON") || pro.proType.contains("JSONB")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + info.defaultValue = lineEdit->text(); + } + map.insert(pro.proTag,info); + } + } + + for(auto it = map.begin();it != map.end();++it) //值被手动改变过,锁定(保存后解除锁定 + { + if(_curValue.contains(it.key())) + { + if(it->defaultValue != _curValue.value(it.key()).defaultValue) + { + it->lock = true; + } + } + } + pPro->setDataChanged(true); + return map; +} + +void PropertyContentDlg::setPropertyValue(QVariant var) +{ + QMap map = var.value>(); + _curValue = map; + for(auto &info:map) + { + PropertyContentInfo pro = _mapPro[info.tagName]; + if(info.type.contains("SMALLINT") || info.type.contains("INTEGER")) + { + QSpinBox* spin = qobject_cast(pro.proEditor); + if(spin) + spin->setValue(info.defaultValue.toInt()); + } + else if(info.type.contains("BIGINT")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + lineEdit->setText(info.defaultValue.toString()); + } + else if(info.type.contains("REAL") || info.type.contains("DOUBLE PRECISION")) + { + QDoubleSpinBox* dbSpin = qobject_cast(pro.proEditor); + if(dbSpin) + dbSpin->setValue(info.defaultValue.toDouble()); + } + else if(info.type.contains("NUMERIC") || info.type.contains("DECIMAL")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + lineEdit->setText(info.defaultValue.toString()); + } + else if(info.type.contains("SERIAL") || info.type.contains("BIGSERIAL")) + { + QLabel* label = qobject_cast(pro.proEditor); + if(label) + label->setText(info.defaultValue.toString()); + } + else if(info.type.contains("CHAR") || info.type.contains("VARCHAR")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + lineEdit->setText(info.defaultValue.toString()); + } + else if(info.type.contains("TEXT")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + lineEdit->setText(info.defaultValue.toString()); + } + else if(info.type.contains("BYTEA")) + { + QLabel* label = qobject_cast(pro.proEditor); + if(label) + label->setText(info.defaultValue.toString()); + } + else if(info.type.contains("DATE")) + { + QDateEdit* dateEdit = qobject_cast(pro.proEditor); + QDate date = QDate::fromString(info.defaultValue.toString(), "yyyy-MM-dd"); + if(dateEdit) + dateEdit->setDate(date); + } + else if(info.type.contains("TIME")) + { + QTimeEdit* timeEdit = qobject_cast(pro.proEditor); + QTime time = QTime::fromString(info.defaultValue.toString(), "HH:mm:ss"); + if(timeEdit) + timeEdit->setTime(time); + } + else if(info.type.contains("TIMESTAMP")) + { + QDateTimeEdit* dateTimeEidt = qobject_cast(pro.proEditor); + QDateTime dateTime = QDateTime::fromString(info.defaultValue.toString(), "yyyy-MM-dd HH:mm:ss"); + if(dateTimeEidt) + dateTimeEidt->setDateTime(dateTime); + } + else if(info.type.contains("UUID")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + lineEdit->setText(info.defaultValue.toString()); + } + else if(info.type.contains("JSON") || info.type.contains("JSONB")) + { + QLineEdit* lineEdit = qobject_cast(pro.proEditor); + if(lineEdit) + lineEdit->setText(info.defaultValue.toString()); + } + } +} diff --git a/diagramCavas/source/propertyType/PropertyTypeCustomization_CustomType.cpp b/diagramCavas/source/propertyType/PropertyTypeCustomization_CustomType.cpp new file mode 100644 index 0000000..612738f --- /dev/null +++ b/diagramCavas/source/propertyType/PropertyTypeCustomization_CustomType.cpp @@ -0,0 +1,73 @@ +#include "propertyType/PropertyTypeCustomization_CustomType.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" +#include +#include +#include +#include "QQuickDetailsViewModel.h" +#include "propertyType/CustomType.h" +#include "QQuickFunctionLibrary.h" + +void PropertyTypeCustomization_CustomType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) +{ + auto editorSlot = inBuilder->makeNameValueSlot(); + inPropertyHandle->setupNameEditor(editorSlot.first); + auto buttonItem = inBuilder->setupItem(editorSlot.second, R"( + import QtQuick; + import QtQuick.Controls; + Button{ + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + width: 80 + height: 20 + text: "Sort" + } + )"); + + + QQuickFunctionLibrary::connect(buttonItem, SIGNAL(clicked()), inPropertyHandle, [inPropertyHandle]() { + QCustomType customType = inPropertyHandle->getVar().value(); + std::sort(customType.Array.begin(), customType.Array.end()); + inPropertyHandle->setVar(QVariant::fromValue(customType)); + if (auto arrayHandle = inPropertyHandle->findChild("Array")) { + } + }); +} + +void PropertyTypeCustomization_CustomType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + auto arrayHandle = inPropertyHandle->findOrCreateChild( + QMetaType::fromType>(), + "Array", + [inPropertyHandle]() { + return QVariant::fromValue(inPropertyHandle->getVar().value().Array); + }, + [inPropertyHandle](QVariant var) { + QCustomType customType = inPropertyHandle->getVar().value(); + customType.Array = var.value>(); + inPropertyHandle->setVar(QVariant::fromValue(customType)); + } + ); + + auto arraySizeHandle = inPropertyHandle->findOrCreateChild( + QMetaType::fromType(), + "ArraySize", + [inPropertyHandle]() { + return inPropertyHandle->getVar().value().ArraySize; + }, + [inPropertyHandle, arrayHandle](QVariant var) { + QCustomType customType = inPropertyHandle->getVar().value(); + customType.ArraySize = var.toUInt(); + customType.Array.resize(customType.ArraySize); + for (int i = 0; i < customType.ArraySize; ++i) { + customType.Array[i] = QRandomGenerator::global()->bounded(-100000, 100000); + } + inPropertyHandle->setVar(QVariant::fromValue(customType)); + arrayHandle->invalidateStructure(); + } + ); + + inBuilder->addProperty(arraySizeHandle); + inBuilder->addProperty(arrayHandle); +} + diff --git a/diagramCavas/source/propertyType/pannelColorGadget.cpp b/diagramCavas/source/propertyType/pannelColorGadget.cpp new file mode 100644 index 0000000..da7df25 --- /dev/null +++ b/diagramCavas/source/propertyType/pannelColorGadget.cpp @@ -0,0 +1,28 @@ +#include "propertyType/pannelColorGadget.h" +#include "baseDrawingPanel.h" + +PannelColorGadget::PannelColorGadget(BaseDrawingPanel* pPanel) + :_pPanel(pPanel) +{ + +} + +QColor PannelColorGadget::getBackColor() const +{ + return _pPanel->getScene()->getBackGoundColor(); +} + +void PannelColorGadget::setBackColor(QColor color) +{ + _pPanel->getScene()->setBackGoundColor(color); +} + +QColor PannelColorGadget::getGridColor() const +{ + return _pPanel->getScene()->getGridColor(); +} + +void PannelColorGadget::setGridColor(QColor color) +{ + _pPanel->getScene()->setGridColor(color); +} \ No newline at end of file diff --git a/diagramCavas/source/propertyType/propertyTypeCustomization_DataSourceType.cpp b/diagramCavas/source/propertyType/propertyTypeCustomization_DataSourceType.cpp new file mode 100644 index 0000000..4f9c0bf --- /dev/null +++ b/diagramCavas/source/propertyType/propertyTypeCustomization_DataSourceType.cpp @@ -0,0 +1,120 @@ +#include "propertyType/propertyTypeCustomization_DataSourceType.h" +#include "QQuickDetailsViewLayoutBuilder.h" +#include "QPropertyHandle.h" +#include +#include +#include "propertyType/dataSourceType.h" +#include "QQuickFunctionLibrary.h" +#include "dataSourceDlg.h" + +void PropertyTypeCustomization_DataSourceType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) +{ + auto editorSlot = inBuilder->makeNameValueSlot(); + inPropertyHandle->setupNameEditor(editorSlot.first); + auto buttonItem = inBuilder->setupItem(editorSlot.second, R"( + import QtQuick + import QtQuick.Controls + + Item { + // 仅设置隐式大小 + implicitWidth: 180 + implicitHeight: 25 + + // 自定义信号 + signal customButtonClicked() + + // 让父容器控制宽度 + anchors.left: parent ? parent.left : undefined + anchors.right: parent ? parent.right : undefined + + // 设置文本的函数 + function setDisplayText(newText) { + displayText.text = newText; + } + + // 获取当前文本的函数 + function getDisplayText() { + return displayText.text; + } + + // 右侧功能按钮 + Button { + id: button + width: 25 + height: 25 + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + } + + text: "..." + font.pixelSize: 9 + font.bold: true + + // 自定义按钮样式 + background: Rectangle { + color: button.down ? "#d0d0d0" : + button.hovered ? "#e0e0e0" : "#f0f0f0" + border.color: "#888" + border.width: 1 + radius: 4 + } + + // 点击时触发自定义信号 + onClicked: { + parent.customButtonClicked() + } + } + + // 文字显示区域 + Rectangle { + id: textArea + anchors { + left: parent.left + right: button.left + rightMargin: 5 + verticalCenter: parent.verticalCenter + } + height: 25 + + border.color: "#ccc" + border.width: 1 + radius: 4 + + Text { + id: displayText + anchors.fill: parent + anchors.margins: 5 + text: "datasource" + color: "#333" + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + } + )"); + DataSourceType curVal = inPropertyHandle->getVar().value(); + QVariant curStr = QVariant(curVal.sPara); + QMetaObject::invokeMethod(buttonItem, "setDisplayText", + Q_ARG(QVariant, curStr)); + + QQuickFunctionLibrary::connect(buttonItem, SIGNAL(customButtonClicked()), inPropertyHandle, [inPropertyHandle,buttonItem]() { + DataSourceDlg dlg; + DataSourceType customType = inPropertyHandle->getVar().value(); + dlg.showDlg(customType); + if (dlg.exec() == QDialog::Accepted) { + DataSourceType setType = dlg.getCurData(); + inPropertyHandle->setVar(QVariant::fromValue(setType)); + + QVariant displayStr = QVariant(setType.sPara); + QMetaObject::invokeMethod(buttonItem, "setDisplayText", + Q_ARG(QVariant, displayStr)); + } + }); +} + +void PropertyTypeCustomization_DataSourceType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) +{ + +} + diff --git a/diagramCavas/source/ptExtraInfoDlg.cpp b/diagramCavas/source/ptExtraInfoDlg.cpp new file mode 100644 index 0000000..9014f8c --- /dev/null +++ b/diagramCavas/source/ptExtraInfoDlg.cpp @@ -0,0 +1,312 @@ +#include "ptExtraInfoDlg.h" +#include "ui_ptExtraInfoDlg.h" +#include "baseProperty.h" +#include "basePropertyManager.h" +#include +#include +#include +#include + +PtExtraInfoDlg::PtExtraInfoDlg(QWidget *parent) + : BaseContentDlg(parent) + , ui(new Ui::ptExtraInfoDlg) +{ + ui->setupUi(this); + _stateGroup_pt = new QButtonGroup(this); + _stateGroup_pt->addButton(ui->rb_tpt_pt,1); + _stateGroup_pt->addButton(ui->rb_spt_pt,0); + + connect(ui->btn_add_pt,&QPushButton::clicked,this,&PtExtraInfoDlg::onAddClicked); + ui->tb_pt->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + _count = 1; + + ui->tb_pt->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->tb_pt, &QTableWidget::customContextMenuRequested, this, &PtExtraInfoDlg::onTableCustomContextMenuRequested); +} + +PtExtraInfoDlg::~PtExtraInfoDlg() +{ + delete ui; +} + +void PtExtraInfoDlg::createGroupView(GroupStateInfo infos) +{ + for(auto& info:infos.info) { + PropertyContentInfo inf; + inf.proTag = info.tagName; + inf.proName = info.name; + inf.proType = info.type; + _mapPro.insert(info.tagName,inf); + } +} + +QMap PtExtraInfoDlg::getPropertyValue(BaseProperty* pPro) +{ + QMap map; + + for(auto &pro:_mapPro) + { + PropertyStateInfo info; + info.type = pro.proType; + info.name = pro.proName; + info.tagName = pro.proTag; + if(info.name == "额定电压(V)" || info.tagName == "un_v") + { + info.defaultValue = ui->le_ratedVol->text(); + } + else if(info.name == "工频耐压(V/1min)" || info.tagName == "uac_v_1min") + { + info.defaultValue = ui->le_pfwv_pt->text(); + } + else if(info.name == "冲击耐压(V)" || info.tagName == "uimp_v") + { + info.defaultValue = ui->le_iwv_pt->text(); + } + else if(info.name == "额定电压因数" || info.tagName == "rvf") + { + info.defaultValue = ui->le_ratedVolFactor->text(); + } + else if(info.name == "一次绕组接线接地方式" || info.tagName == "pwcc") + { + info.defaultValue = ui->le_pwwgm->text(); + } + else if(info.name == "额定频率(Hz)" || info.tagName == "fn_hz") + { + info.defaultValue = ui->le_rf_pt->text(); + } + else if(info.name == "相数" || info.tagName == "phase_num") + { + if(ui->rb_tpt_pt->isChecked()) + info.defaultValue = 1; + else + info.defaultValue = 0; + } + else if(info.name == "PT二次绕组" || info.tagName == "pt_sec_winding") + { + QJsonObject object; + QJsonArray arr; + for(auto &info:_mapPT) + { + QJsonObject obj; + obj["index"] = info.index; + obj["scope"] = info.scope; + obj["accuracy"] = info.accuracy; + obj["volume"] = info.volume; + obj["star"] = info.star; + obj["ratio"] = info.ratio; + obj["polarity"] = info.polarity; + arr.push_back(obj); + } + object["winding"] = arr; + info.defaultValue = object; + } + map.insert(pro.proTag,info); + } + pPro->setDataChanged(true); + return map; +} + +void PtExtraInfoDlg::setPropertyValue(QVariant var) +{ + QMap map = var.value>(); + for(auto &info:map) + { + if(info.name == "额定电压(V)" || info.tagName == "un_v") + { + ui->le_ratedVol->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "工频耐压(V/1min)" || info.tagName == "uac_v_1min") + { + ui->le_pfwv_pt->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "冲击耐压(V)" || info.tagName == "uimp_v") + { + ui->le_iwv_pt->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "额定电压因数" || info.tagName == "rvf") + { + ui->le_ratedVolFactor->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "一次绕组接线接地方式" || info.tagName == "pwcc") + { + int nIndex = 0; + if(info.defaultValue.toString() != "null") + nIndex = info.defaultValue.toInt(); + ui->le_pwwgm->setText(QString::number(nIndex)); + } + else if(info.name == "额定频率(Hz)" || info.tagName == "fn_hz") + { + ui->le_rf_pt->setText(QString::number(info.defaultValue.toDouble())); + } + else if(info.name == "相数" || info.tagName == "phase_num") + { + if(info.defaultValue.toInt() == 1) + ui->rb_tpt_pt->setChecked(true); + else + ui->rb_spt_pt->setChecked(true); + } + else if(info.name == "PT二次绕组" || info.tagName == "pt_sec_winding") + { + QString jsonString = info.defaultValue.toString(); + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8().data()); + QJsonObject jsonObject = jsonDocument.object(); + + QJsonObject object = jsonObject; + + QJsonArray arr = object["winding"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + QJsonObject node = jsonObj.toObject(); + int index = node["index"].toInt(); + QString scope = node["scope"].toString(); + QString accuracy = node["accuracy"].toString(); + QString volume = node["volume"].toString(); + QString star = node["star"].toString(); + double ratio = node["ratio"].toDouble(); + int polarity = node["polarity"].toInt(); + + addTableRow(scope,accuracy,volume,star,ratio,polarity,index); + } + } + } +} + +void PtExtraInfoDlg::onAddClicked() +{ + QString sRatioRange = ui->le_tr_range_pt->text(); + QString sAccuracy= ui->le_ac_pt->text(); + QString sVolume = ui->le_slc_pt->text(); + QString sStar = ui->cb_wcm->currentText(); + double dRatio = ui->le_tr_pt->text().toDouble(); + bool bPolarity = ui->cB_polarity->checkState(); + int index = -1; + if(ui->rb_spt_pt->isChecked()) + index = 0; + addTableRow(sRatioRange,sAccuracy,sVolume,sStar,dRatio,bPolarity,index); +} + +void PtExtraInfoDlg::onTableCustomContextMenuRequested(const QPoint &pos) { + QModelIndex index = ui->tb_pt->indexAt(pos); + if (!index.isValid()) { + return; + } + + int row = index.row(); + + // 创建右键菜单 + QMenu menu(this); + QAction *deleteAction = menu.addAction("删除此行"); + + // 连接删除操作 + connect(deleteAction, &QAction::triggered, this, [this, row]() { + deleteRowWithReindex(row); + //updateLables(); + }); + + menu.exec(ui->tb_pt->viewport()->mapToGlobal(pos)); +} + +void PtExtraInfoDlg::addTableRow(QString sRatioRange,QString sAccuracy,QString sVolume,QString sStar,double dRatio,bool bPolarity,int index) +{ + if(_mapPT.contains(QString::number(index))) + { + return; + } + + PtExtraInfo info; + if(index == -1){ //缺省id时新建,否则加载 + info.index = _count; + _count += 1; + } + else{ + info.index = index; + } + + int row = ui->tb_pt->rowCount(); + ui->tb_pt->insertRow(row); + + // index + QTableWidgetItem *item = new QTableWidgetItem(QString::number(info.index)); + item->setData(Qt::UserRole,info.index); + ui->tb_pt->setItem(row, 0, item); + + //变比范围 + ui->tb_pt->setItem(row, 1, new QTableWidgetItem(sRatioRange)); + + //精度等级 + ui->tb_pt->setItem(row, 2, new QTableWidgetItem(sAccuracy)); + + //二次负载容量 + ui->tb_pt->setItem(row, 3, new QTableWidgetItem(sVolume)); + + // 变比 + ui->tb_pt->setItem(row, 4, new QTableWidgetItem(QString::number(dRatio))); + + // 极性 + ui->tb_pt->setItem(row, 5, new QTableWidgetItem(QString::number(bPolarity? 1 : -1))); + + //接线 + ui->tb_pt->setItem(row, 6, new QTableWidgetItem(sStar)); + + info.scope = sRatioRange; + info.accuracy = sAccuracy; + info.volume = sVolume; + info.star = sStar; + info.ratio = dRatio; + info.polarity = bPolarity? 1 : -1; + _mapPT.insert(QString::number(info.index),info); +} + +void PtExtraInfoDlg::deleteRowWithReindex(int row) { + // 1. 获取要删除的ID + QTableWidgetItem* pFirstItem = ui->tb_pt->item(row, 0); + if (!pFirstItem) return; + + int deletedId = pFirstItem->data(Qt::UserRole).toInt(); + QString deletedKey = QString::number(deletedId); + + // 2. 从表格中删除行 + ui->tb_pt->removeRow(row); + + // 3. 从_mapCT中删除对应项 + if (_mapPT.contains(deletedKey)) { + _mapPT.remove(deletedKey); + } + + // 4. 重新排序和更新index + reorderMapAndUpdateIndices(row); +} + +void PtExtraInfoDlg::reorderMapAndUpdateIndices(int startRow) { + int totalRows = ui->tb_pt->rowCount(); + + // 遍历从startRow开始的所有行 + for (int row = startRow; row < totalRows; ++row) { + QTableWidgetItem* idItem = ui->tb_pt->item(row, 0); + if (!idItem) continue; + + int currentId = idItem->data(Qt::UserRole).toInt(); + QString currentKey = QString::number(currentId); + + // 计算新的ID和索引 + int newId = row + 1; // 新的ID + int newIndex = row + 1; // 新的索引 + + if (_mapPT.contains(currentKey)) { + // 获取并更新数据 + PtExtraInfo info = _mapPT[currentKey]; + info.index = newIndex; + + // 从旧位置移除 + _mapPT.remove(currentKey); + + // 添加到新位置 + QString newKey = QString::number(newId); + _mapPT[newKey] = info; + + // 更新表格显示 + idItem->setText(QString::number(newId)); + idItem->setData(Qt::UserRole, newId); + } + } +} diff --git a/diagramCavas/source/statusBar.cpp b/diagramCavas/source/statusBar.cpp new file mode 100644 index 0000000..e06c505 --- /dev/null +++ b/diagramCavas/source/statusBar.cpp @@ -0,0 +1,41 @@ +#include +#include +#include "statusBar.h" + +StatusBar::StatusBar(QWidget *parent) + : QStatusBar(parent) + ,m_pScaleLevel(nullptr) + ,m_pButtonGenerate(nullptr) +{ + m_pScaleLevel = new QLabel("当前级数:10",this); + m_pScaleLevel->setMinimumWidth(250); + m_pButtonGenerate = new QPushButton("生成工程组态",this); + addWidget(m_pScaleLevel); + addPermanentWidget(m_pButtonGenerate); + initial(); +} + +StatusBar::~StatusBar() +{ + +} + +void StatusBar::initial() +{ + connect(m_pButtonGenerate,&QPushButton::clicked,this,&StatusBar::onGenerateClicked); +} + +void StatusBar::setButtonVisible(bool val) +{ + m_pButtonGenerate->setVisible(val); +} + +void StatusBar::onScaleLevelChanged(double f) +{ + m_pScaleLevel->setText(QString::fromWCharArray(L"当前级数:")+QString::number(f)); +} + +void StatusBar::onGenerateClicked() +{ + emit generateDiagram(); +} diff --git a/diagramCavas/source/structDataActionParaDlg.cpp b/diagramCavas/source/structDataActionParaDlg.cpp new file mode 100644 index 0000000..4721d47 --- /dev/null +++ b/diagramCavas/source/structDataActionParaDlg.cpp @@ -0,0 +1,80 @@ +#include "structDataActionParaDlg.h" +#include +#include +#include + +StructDataActionParaDlg::StructDataActionParaDlg(QWidget *parent) + : QDialog(parent) { + setWindowTitle("动作参数"); + resize(350, 300); + + QVBoxLayout *layout = new QVBoxLayout(this); + + // 告警列表 + m_listWidget = new QListWidget(this); + layout->addWidget(m_listWidget); + + // 添加区 + QHBoxLayout *addLayout = new QHBoxLayout; + m_editLine = new QLineEdit(this); + m_editLine->setPlaceholderText("输入动作参数"); + m_btnAdd = new QPushButton("添加", this); + + addLayout->addWidget(m_editLine, 1); + addLayout->addWidget(m_btnAdd); + layout->addLayout(addLayout); + + // 操作区 + QHBoxLayout *opLayout = new QHBoxLayout; + m_btnDelete = new QPushButton("删除选中", this); + m_btnClear = new QPushButton("清空", this); + + opLayout->addWidget(m_btnDelete); + opLayout->addWidget(m_btnClear); + opLayout->addStretch(); + layout->addLayout(opLayout); + + // 确定取消 + QHBoxLayout *btnLayout = new QHBoxLayout; + m_btnOk = new QPushButton("确定", this); + m_btnCancel = new QPushButton("取消", this); + + btnLayout->addStretch(); + btnLayout->addWidget(m_btnOk); + btnLayout->addWidget(m_btnCancel); + layout->addLayout(btnLayout); + + // 连接信号 + connect(m_btnAdd, &QPushButton::clicked, this, &StructDataActionParaDlg::onAddClicked); + connect(m_btnDelete, &QPushButton::clicked, this, &StructDataActionParaDlg::onDeleteClicked); + connect(m_btnClear, &QPushButton::clicked, m_listWidget, &QListWidget::clear); + connect(m_editLine, &QLineEdit::returnPressed, this, &StructDataActionParaDlg::onAddClicked); + connect(m_btnOk, &QPushButton::clicked, this, &QDialog::accept); + connect(m_btnCancel, &QPushButton::clicked, this, &QDialog::reject); +} + +void StructDataActionParaDlg::setAlarms(const QStringList &alarms) { + m_listWidget->clear(); + m_listWidget->addItems(alarms); +} + +QStringList StructDataActionParaDlg::alarms() const { + QStringList result; + for (int i = 0; i < m_listWidget->count(); ++i) { + result << m_listWidget->item(i)->text(); + } + return result; +} + +void StructDataActionParaDlg::onAddClicked() { + QString text = m_editLine->text().trimmed(); + if (!text.isEmpty()) { + m_listWidget->addItem(text); + m_editLine->clear(); + } +} + +void StructDataActionParaDlg::onDeleteClicked() { + qDeleteAll(m_listWidget->selectedItems()); +} + diff --git a/diagramCavas/source/structDataCauseEditDlg.cpp b/diagramCavas/source/structDataCauseEditDlg.cpp new file mode 100644 index 0000000..fedc7de --- /dev/null +++ b/diagramCavas/source/structDataCauseEditDlg.cpp @@ -0,0 +1,125 @@ +#include "structDataCauseEditDlg.h" +#include +#include +#include + +StructDataCauseEditDlg::StructDataCauseEditDlg(const QMap& initialData,QWidget* parent) + : QDialog(parent) { + + setupUI(); + setData(initialData); +} + +void StructDataCauseEditDlg::setData(const QMap& data) { + m_data = data; + updateUIFromData(); +} + +QMap StructDataCauseEditDlg::getData() const { + return m_data; +} + +void StructDataCauseEditDlg::setupUI() { + setWindowTitle("编辑 Cause 数据"); + setMinimumSize(350, 200); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + + // 标题 + QLabel* titleLabel = new QLabel("遥测原因", this); + titleLabel->setStyleSheet("font-weight: bold; font-size: 12pt;"); + mainLayout->addWidget(titleLabel); + + // 分隔线 + QFrame* line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + mainLayout->addWidget(line); + + // 可用的键列表 + m_availableKeys = {"upup", "up", "down", "downdown"}; + + // 为每个键创建编辑器 + for (const QString& key : m_availableKeys) { + QHBoxLayout* rowLayout = new QHBoxLayout; + + // 复选框 + QCheckBox* checkBox = new QCheckBox(key, this); + checkBox->setStyleSheet("QCheckBox { font-weight: bold; }"); + + // 数值输入框 + QDoubleSpinBox* spinBox = new QDoubleSpinBox(this); + spinBox->setRange(0.0, 100.0); + //spinBox->setSuffix("%"); + spinBox->setDecimals(1); + spinBox->setSingleStep(0.5); + spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + + // 连接信号 + connect(checkBox, &QCheckBox::toggled, this, [this, key, spinBox, checkBox](bool checked) { + spinBox->setEnabled(checked); + if (checked) { + spinBox->setFocus(); + spinBox->selectAll(); + } else { + spinBox->setValue(0.0); + } + }); + + rowLayout->addWidget(checkBox); + rowLayout->addWidget(spinBox); + rowLayout->addStretch(); + + mainLayout->addLayout(rowLayout); + + // 存储控件指针 + m_checkBoxes[key] = checkBox; + m_spinBoxes[key] = spinBox; + } + + // 分隔线 + QFrame* line2 = new QFrame(this); + line2->setFrameShape(QFrame::HLine); + line2->setFrameShadow(QFrame::Sunken); + mainLayout->addWidget(line2); + + // 按钮 + m_buttonBox = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + + mainLayout->addWidget(m_buttonBox); + + connect(m_buttonBox, &QDialogButtonBox::accepted, this, [this]() { + updateTotal(); + accept(); + }); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + setLayout(mainLayout); + + // 初始更新 + updateTotal(); +} + +void StructDataCauseEditDlg::updateUIFromData() { + for(auto iter = m_data.begin();iter != m_data.end();++iter) { + if (m_checkBoxes.contains(iter.key()) && m_spinBoxes.contains(iter.key())) { + bool hasValue = m_data.contains(iter.key()) && m_data[iter.key()] > 0; + m_checkBoxes[iter.key()]->setChecked(hasValue); + m_spinBoxes[iter.key()]->setValue(hasValue ? m_data[iter.key()] : 0.0); + m_spinBoxes[iter.key()]->setEnabled(hasValue); + } + } +} + +void StructDataCauseEditDlg::updateTotal() { + m_data.clear(); + + for (const QString& key : m_availableKeys) { + if (m_checkBoxes[key]->isChecked()) { + double value = m_spinBoxes[key]->value(); + m_data[key] = value; + } + } +} + diff --git a/diagramCavas/source/structDataMeasurementDelegate.cpp b/diagramCavas/source/structDataMeasurementDelegate.cpp new file mode 100644 index 0000000..1667ada --- /dev/null +++ b/diagramCavas/source/structDataMeasurementDelegate.cpp @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#include +#include +#include "structDataMeasurementDelegate.h" +#include "structDataMeasurementModel.h" +#include "structDataCauseEditDlg.h" +#include "structDataActionParaDlg.h" +#include "uiCommunicationBus.h" + +StructDataMeasurementDelegate::StructDataMeasurementDelegate(QObject* parent) : QStyledItemDelegate(parent) {} + +QWidget* StructDataMeasurementDelegate::createEditor(QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { + Q_UNUSED(option); + + int column = index.column(); + + switch (column) { + case StructDataMeasurementModel::ColConnectPara: + return createConnectParaEditor(parent); + case StructDataMeasurementModel::ColType: + return createMeasurementTypeEditor(parent); + case StructDataMeasurementModel::ColSize: + return createSizeEditor(parent); + case StructDataMeasurementModel::ColSource: + return createSourceEditor(parent); + case StructDataMeasurementModel::ColStation: + return createTextEditor(parent); + case StructDataMeasurementModel::ColEquipment: + return createTextEditor(parent); + case StructDataMeasurementModel::ColChannel: + return createTextEditor(parent); + case StructDataMeasurementModel::ColPacket: + return createNumberEditor(parent); + case StructDataMeasurementModel::ColOffset: + return createNumberEditor(parent); + case StructDataMeasurementModel::ColEnable: + return createEnableEditor(parent); + case StructDataMeasurementModel::ColCause:{ + QString sType = getParaType(index); + if(sType == "遥测"){ //遥测 + return createCauseYCEditor(parent,index); + } + else if(sType == "遥信") { //遥信 + return createCauseYXEditor(parent); + } + } + case StructDataMeasurementModel::ColCommand: + return createCommandEditor(parent); + case StructDataMeasurementModel::ColParameters: + return createActionParaEditor(parent); + } + + return nullptr; +} + +void StructDataMeasurementDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { + int column = index.column(); + QVariant value = index.data(Qt::EditRole); + + switch (column) { + case StructDataMeasurementModel::ColConnectPara: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + lineEdit->setText(value.toString()); + } + break; + case StructDataMeasurementModel::ColType: + if (QComboBox* comboBox = qobject_cast(editor)) { + int typeValue = value.toInt(); + comboBox->setCurrentIndex(qBound(0, typeValue, 2)); + } + break; + case StructDataMeasurementModel::ColSize: + if (QSpinBox* spinBox = qobject_cast(editor)) { + spinBox->setValue(value.toInt()); + } + break; + case StructDataMeasurementModel::ColSource: + if (QComboBox* comboBox = qobject_cast(editor)) { + int sourceValue = value.toInt(); + int index = (sourceValue == 1) ? 0 : 1; // 1: cl3611, 2: 104 + comboBox->setCurrentIndex(index); + } + break; + case StructDataMeasurementModel::ColStation: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + lineEdit->setText(value.toString()); + } + break; + case StructDataMeasurementModel::ColEquipment: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + lineEdit->setText(value.toString()); + } + break; + case StructDataMeasurementModel::ColChannel: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + lineEdit->setText(value.toString()); + } + break; + case StructDataMeasurementModel::ColPacket: + if (QSpinBox* spinBox = qobject_cast(editor)) { + spinBox->setValue(value.toInt()); + } + break; + case StructDataMeasurementModel::ColOffset: + if (QSpinBox* spinBox = qobject_cast(editor)) { + spinBox->setValue(value.toInt()); + } + break; + + case StructDataMeasurementModel::ColEnable: + if (QComboBox* comboBox = qobject_cast(editor)) { + bool res = value.toBool(); + int index = (res) ? 0 : 1; // 0: 启用, 1: 禁用 + comboBox->setCurrentIndex(index); + } + break; + case StructDataMeasurementModel::ColCause:{ + QString sType = getParaType(index); + if(sType == "遥测"){ + if (StructDataCauseEditDlg* dialog = qobject_cast(editor)) { + QMap data = value.value>(); + dialog->setData(data); + } + } + else if(sType == "遥信"){ + if (QComboBox* comboBox = qobject_cast(editor)) { + comboBox->setCurrentText(value.toString()); + } + } + } + break; + case StructDataMeasurementModel::ColCommand: + if (QComboBox* comboBox = qobject_cast(editor)) { + comboBox->setCurrentText(value.toString()); + } + break; + case StructDataMeasurementModel::ColParameters: + if (StructDataActionParaDlg* dialog = qobject_cast(editor)) { + QStringList lst = value.toStringList(); + dialog->setAlarms(lst); + } + break; + } +} + +void StructDataMeasurementDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const { + int column = index.column(); + QVariant newValue; + + switch (column) { + case StructDataMeasurementModel::ColConnectPara: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + newValue = lineEdit->text().trimmed(); + } + break; + case StructDataMeasurementModel::ColEquipment: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + newValue = lineEdit->text().trimmed(); + } + break; + case StructDataMeasurementModel::ColChannel: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + newValue = lineEdit->text().trimmed(); + } + break; + case StructDataMeasurementModel::ColStation: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + newValue = lineEdit->text().trimmed(); + } + break; + case StructDataMeasurementModel::ColType: + if (QComboBox* comboBox = qobject_cast(editor)) { + newValue = comboBox->currentIndex(); // 0:遥测, 1:遥信, 2:遥控 + } + break; + + case StructDataMeasurementModel::ColSize: + if (QSpinBox* spinBox = qobject_cast(editor)) { + newValue = spinBox->value(); + } + break; + case StructDataMeasurementModel::ColPacket: + if (QSpinBox* spinBox = qobject_cast(editor)) { + newValue = spinBox->value(); + } + break; + case StructDataMeasurementModel::ColOffset: + if (QSpinBox* spinBox = qobject_cast(editor)) { + newValue = spinBox->value(); + } + break; + + case StructDataMeasurementModel::ColSource: + if (QComboBox* comboBox = qobject_cast(editor)) { + // 转换文本为数值:cl3611 -> 1, 104 -> 21 + QString text = comboBox->currentText(); + newValue = (text == "cl3611") ? 1 : 2; + } + break; + + case StructDataMeasurementModel::ColEnable: + if (QComboBox* comboBox = qobject_cast(editor)) { + // 转换文本为数值:启用 -> 1, 禁用-> 0 + QString text = comboBox->currentText(); + newValue = (text == "启用") ? true : false; + } + break; + case StructDataMeasurementModel::ColCause:{ + QString sType = getParaType(index); + if(sType == "遥测"){ + if (StructDataCauseEditDlg* dialog = qobject_cast(editor)) { + if (dialog->result() == QDialog::Accepted) { + newValue = QVariant::fromValue(dialog->getData()); + } + } + } + else if(sType == "遥信"){ + if (QComboBox* comboBox = qobject_cast(editor)) { + newValue = comboBox->currentText(); + } + } + break; + } + case StructDataMeasurementModel::ColCommand: + if (QComboBox* comboBox = qobject_cast(editor)) { + newValue = comboBox->currentText(); + } + break; + case StructDataMeasurementModel::ColParameters: + if (StructDataActionParaDlg* dialog = qobject_cast(editor)) { + newValue = dialog->alarms(); + } + break; + } + + if (!newValue.isNull()) { + model->setData(index, newValue, Qt::EditRole); + } +} + +void StructDataMeasurementDelegate::updateEditorGeometry(QWidget* editor, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + Q_UNUSED(index); + editor->setGeometry(option.rect); +} + +QString StructDataMeasurementDelegate::displayText(const QVariant& value, const QLocale& locale) const { + Q_UNUSED(locale); + + // 特殊列显示格式化 + if (value.userType() == QMetaType::Bool) { + return value.toBool() ? "启用" : "禁用"; + } + + return QStyledItemDelegate::displayText(value, locale); +} + +// 提供工具提示 +QString StructDataMeasurementDelegate::displayText(const QVariant& value, const QLocale& locale, + const QModelIndex& index) const { + int column = index.column(); + + if (column == StructDataMeasurementModel::ColType) { + int type = value.toInt(); + switch (type) { + case 0: return "遥测"; + case 1: return "遥信"; + case 2: return "遥控"; + default: return QString::number(type); + } + } else if (column == StructDataMeasurementModel::ColSource) { + int source = value.toInt(); + switch (source) { + case 1: return "cl3611"; + case 2: return "104"; + default: return QString::number(source); + } + } else if (column == StructDataMeasurementModel::ColEnable) { + bool enabled = value.toBool(); + return enabled ? "启用" : "禁用"; + } + + return displayText(value, locale); +} + +void StructDataMeasurementDelegate::onConnectParamChanged(const QString& str) const +{ + QVariantMap map; + map.insert("input",str); + UiCommunicationBus::instance()->sendHttpRequest("/measurement/recommend",QVariant(),"GET",map); +} + +bool StructDataMeasurementDelegate::eventFilter(QObject *obj, QEvent *event) +{ + if (QLineEdit *editor = qobject_cast(obj)){ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + + if (keyEvent->key() == Qt::Key_Space) { + qDebug() << "eventFilter:space"; + onConnectParamChanged(editor->text()); + _connectCompleter->complete(); + return true; + } + + if (keyEvent->text() == ".") { + qDebug() << "eventFilter:dot"; + // 返回false让事件继续传播 + } + } + } + + return QStyledItemDelegate::eventFilter(obj, event); +} + + +QWidget* StructDataMeasurementDelegate::createConnectParaEditor(QWidget* parent) const { + QLineEdit* lineEdit = new QLineEdit(parent); + lineEdit->setMaxLength(150); + lineEdit->installEventFilter(const_cast(this)); + lineEdit->setPlaceholderText("空格获取初始值"); + lineEdit->setCompleter(_connectCompleter); + + connect(lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) { + if (text.endsWith(".")) { + onConnectParamChanged(text); + } + }); + + return lineEdit; +} + +QWidget* StructDataMeasurementDelegate::createTextEditor(QWidget* parent) const { + QLineEdit* lineEdit = new QLineEdit(parent); + lineEdit->setMaxLength(100); + return lineEdit; +} + +QWidget* StructDataMeasurementDelegate::createMeasurementTypeEditor(QWidget* parent) const { + QComboBox* comboBox = new QComboBox(parent); + comboBox->addItems({"遥测", "遥信", "遥控"}); + return comboBox; +} + +QWidget* StructDataMeasurementDelegate::createSizeEditor(QWidget* parent) const { + QSpinBox* spinBox = new QSpinBox(parent); + spinBox->setRange(1, 1000); + return spinBox; +} + +QWidget* StructDataMeasurementDelegate::createSourceEditor(QWidget* parent) const { + QComboBox* comboBox = new QComboBox(parent); + comboBox->addItems({"cl3611", "104"}); + return comboBox; +} + +QWidget* StructDataMeasurementDelegate::createNumberEditor(QWidget* parent) const { + QSpinBox* spinBox = new QSpinBox(parent); + spinBox->setRange(0, 9999); + return spinBox; +} + +QWidget* StructDataMeasurementDelegate::createEnableEditor(QWidget* parent) const { + QComboBox* comboBox = new QComboBox(parent); + comboBox->addItems({"启用", "禁用"}); + return comboBox; +} + +QWidget* StructDataMeasurementDelegate::createCauseYCEditor(QWidget* parent, const QModelIndex& index) const { + QVariant value = index.data(Qt::EditRole); + QMap initialData; + + if (value.canConvert>()) { + initialData = value.value>(); + } + + StructDataCauseEditDlg* dialog = new StructDataCauseEditDlg(initialData, parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + + // 对话框关闭时提交数据 + connect(dialog, &StructDataCauseEditDlg::finished, + this, [this, dialog](int result) { + StructDataMeasurementDelegate* nonConstThis = + const_cast(this); + if (result == QDialog::Accepted) { + emit nonConstThis->commitData(dialog); + } + emit nonConstThis->closeEditor(dialog); + }); + + return dialog; +} + +QWidget* StructDataMeasurementDelegate::createCauseYXEditor(QWidget* parent) const +{ + QComboBox* comboBox = new QComboBox(parent); + comboBox->addItems({"raising", "falling"}); + return comboBox; +} + +QWidget* StructDataMeasurementDelegate::createCommandEditor(QWidget* parent) const +{ + QComboBox* comboBox = new QComboBox(parent); + comboBox->addItems({"info", "warning", "error", "critical", "exception"}); + return comboBox; +} + +QWidget* StructDataMeasurementDelegate::createActionParaEditor(QWidget* parent) const +{ + StructDataActionParaDlg* dlg = new StructDataActionParaDlg(parent); + return dlg; +} + +QString StructDataMeasurementDelegate::getParaType(const QModelIndex& index) const +{ + QModelIndex typeIndex = index.sibling(index.row(), StructDataMeasurementModel::ColType); + return typeIndex.data(Qt::DisplayRole).toString(); +} + diff --git a/diagramCavas/source/structDataMeasurementModel.cpp b/diagramCavas/source/structDataMeasurementModel.cpp new file mode 100644 index 0000000..fd55e2e --- /dev/null +++ b/diagramCavas/source/structDataMeasurementModel.cpp @@ -0,0 +1,334 @@ +#include "structDataMeasurementModel.h" +#include "structDataSource.h" +//#include "global.h" +#include "common/backend/project_model.h" + +StructDataMeasurementModel::StructDataMeasurementModel(StructDataSource* dataManager, QObject* parent) + : QAbstractTableModel(parent) + , m_dataManager(dataManager) +{ + connect(m_dataManager, &StructDataSource::propertyUpdated, + this, &StructDataMeasurementModel::onPropertyUpdated); +} + +void StructDataMeasurementModel::setPropertyCodes(const QStringList& codes) { + beginResetModel(); + m_propertyCodes = codes; + endResetModel(); + + emit propertiesLoaded(m_propertyCodes.size()); +} + +void StructDataMeasurementModel::setProperties(const QVector& properties) { + QStringList codes; + for (const auto& prop : properties) { + if (!prop.code.isEmpty()) { + codes.append(prop.code); + } + } + setPropertyCodes(codes); +} + +int StructDataMeasurementModel::rowCount(const QModelIndex& parent) const { + return parent.isValid() ? 0 : m_propertyCodes.size(); +} + +int StructDataMeasurementModel::columnCount(const QModelIndex& parent) const { + return parent.isValid() ? 0 : ColumnCount; +} + +QVariant StructDataMeasurementModel::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() >= m_propertyCodes.size()) { + return QVariant(); + } + + const ExtraProperty* prop = getProperty(index.row()); + if (!prop) { + return QVariant(); + } + + int col = index.column(); + + if (role == Qt::DisplayRole || role == Qt::EditRole) { + switch (col) { + case ColName: return prop->name; + case ColTag: return prop->tag; + case ColCode: return prop->code; + case ColSourceType: return "量测"; + case ColConnectPara: return prop->connect_para; + default: return getMeasurementData(*prop, col ,role); + } + } + else if (role == Qt::UserRole) { + return QVariant::fromValue(*prop); + } + else if (role == Qt::UserRole + 1) { + return prop->code; + } + + return QVariant(); +} + +bool StructDataMeasurementModel::setData(const QModelIndex& index, const QVariant& value, int role) { + if (!index.isValid() || index.row() >= m_propertyCodes.size() || role != Qt::EditRole) { + return false; + } + + ExtraProperty* prop = getProperty(index.row()); + if (!prop) { + return false; + } + + int col = index.column(); + bool changed = false; + ExtraProperty updatedProp = *prop; + + if (col == ColConnectPara) { + if (updatedProp.connect_para != value.toString()) { + updatedProp.connect_para = value.toString(); + changed = true; + } + } else { + MeasurementInfo* data = m_dataManager->getMeasurementData(updatedProp); + if (data) { + changed = updateMeasurementData(data, col, value, role); + if (changed) { + emit m_dataManager->dataChanged(); + } + } + } + + if (changed) { + updatedProp.bDataChanged = true; + if (m_dataManager->updateProperty(updatedProp)) { + emit dataChanged(index, index, {role}); + emit propertyModified(index.row(), updatedProp); + return true; + } + } + + return false; +} + +Qt::ItemFlags StructDataMeasurementModel::flags(const QModelIndex& index) const { + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + int col = index.column(); + if (col == ColConnectPara || ColType || ColSize || ColSource || ColStation || ColEquipment || ColChannel || ColPacket || ColOffset || ColEnable || ColCause) { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + +QVariant StructDataMeasurementModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + QStringList headers = { + "名称", "标签", "编码", "类型", "连接参数", + "量测类型", "数据包size", "数据来源", "子站", "设备", "通道号","包号(104)","偏移量(104)","开启事件","事件原因","执行动作","动作参数" + }; + return headers.value(section, ""); + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +QVector StructDataMeasurementModel::getDisplayedProperties() const { + QVector result; + for (const QString& code : m_propertyCodes) { + ExtraProperty* prop = m_dataManager->getPropertyByCode(code); + if (prop) { + result.append(*prop); + } + } + return result; +} + +QStringList StructDataMeasurementModel::getDisplayedCodes() const { + return m_propertyCodes; +} + +int StructDataMeasurementModel::findRowByCode(const QString& code) const { + return m_propertyCodes.indexOf(code); +} + +void StructDataMeasurementModel::refreshRow(const QString& code) { + int row = findRowByCode(code); + if (row >= 0) { + QModelIndex start = index(row, 0); + QModelIndex end = index(row, columnCount() - 1); + emit dataChanged(start, end); + } +} + +void StructDataMeasurementModel::onPropertyUpdated(const ExtraProperty& updatedProp, bool isNew) { + Q_UNUSED(isNew); + int row = findRowByCode(updatedProp.code); + if (row >= 0) { + refreshRow(updatedProp.code); + } +} + +ExtraProperty* StructDataMeasurementModel::getProperty(int displayRow) const { + if (displayRow < 0 || displayRow >= m_propertyCodes.size()) { + return nullptr; + } + return m_dataManager->getPropertyByCode(m_propertyCodes[displayRow]); +} + +QVariant StructDataMeasurementModel::getMeasurementData(const ExtraProperty& prop, int col, int role) const { + MeasurementInfo* data = m_dataManager->getMeasurementData(prop); + if (!data) return QVariant(); + + switch (col) { + case ColType: return getTypeText(data->type); + case ColSize: return data->size; + case ColSource: return getSourceText(data->nSource); + case ColStation: return data->sStation; + case ColEquipment: return data->sDevice; + case ColChannel: return data->sChannel; + case ColPacket: return data->nPacket; + case ColOffset: return data->nOffset; + case ColEnable: return data->bEnable ? "启用" : "禁用"; + case ColCause: + if(data->type == 0){ + if(role == Qt::EditRole){ + return QVariant::fromValue(data->mapTE); + } + else if(role == Qt::DisplayRole){ + if (data->mapTE.isEmpty()) { + return "(空)"; + } + + QStringList parts; + for (auto it = data->mapTE.begin(); it != data->mapTE.end(); ++it) { + parts << QString("%1: %2").arg(it.key()).arg(it.value(), 0, 'f', 1); + } + return parts.join("; "); + } + } + else + return data->sEdge; + case ColCommand: return data->sCommand; + case ColParameters:{ + if(role == Qt::EditRole){ + return data->lstParameter; + } + else if(role == Qt::DisplayRole){ + if (data->lstParameter.isEmpty()) { + return "(空)"; + } + return data->lstParameter.join(","); + } + } + } + + return QVariant(); +} + +bool StructDataMeasurementModel::updateMeasurementData(MeasurementInfo* data, int col, const QVariant& value,int role) { + switch (col) { + case ColType: + if (data->type != value.toInt()) { + data->type = value.toInt(); + return true; + } + break; + case ColSize: + if (data->size != value.toInt()) { + data->size = value.toInt(); + return true; + } + break; + case ColSource: + if (getSourceText(data->nSource) != value.toString()) { + data->nSource = getSourceInt(value.toString()); + return true; + } + break; + case ColStation: + if (data->sStation != value.toString()) { + data->sStation = value.toString(); + return true; + } + break; + case ColEquipment: + if (data->sDevice != value.toString()) { + data->sDevice = value.toString(); + return true; + } + break; + case ColChannel: + if (data->sChannel != value.toString()) { + data->sChannel = value.toString(); + return true; + } + break; + case ColEnable: + if (data->bEnable != value.toBool()) { + data->bEnable = value.toBool(); + return true; + } + break; + case ColCause: + if(data->type == 0) + { + if(role == Qt::EditRole){ + if (data->mapTE != value.value>()) { + data->mapTE = value.value>(); + return true; + } + } + } + else{ + if (data->sEdge != value.toString()) { + data->sEdge = value.toString(); + return true; + } + } + break; + case ColCommand: + if (data->sCommand != value.toString()) { + data->sCommand = value.toString(); + return true; + } + break; + case ColParameters: + if(role == Qt::EditRole){ + if (data->lstParameter != value.toStringList()) { + data->lstParameter = value.toStringList(); + return true; + } + } + break; + } + return false; +} + +QString StructDataMeasurementModel::getTypeText(int type) const { + switch (type) { + case 0: return "遥测"; + case 1: return "遥信"; + case 2: return "遥控"; + default: return QString::number(type); + } +} + +QString StructDataMeasurementModel::getSourceText(int source) const { + switch (source) { + case 1: return "cl3611"; + case 2: return "104"; + default: return QString::number(source); + } +} + +int StructDataMeasurementModel::getSourceInt(QString sType) const { + if(sType == "cl3611"){ + return 1; + } + else if(sType == "104"){ + return 2; + } + else { + return 3; + } +} diff --git a/diagramCavas/source/structDataPreviewDlg.cpp b/diagramCavas/source/structDataPreviewDlg.cpp new file mode 100644 index 0000000..ca13d86 --- /dev/null +++ b/diagramCavas/source/structDataPreviewDlg.cpp @@ -0,0 +1,921 @@ +#include "structDataPreviewDlg.h" +#include "ui_structDataPreviewDlg.h" +#include +#include +#include +#include +#include +#include +#include "titleBar.h" +#include "extraPropertyManager.h" +#include "structDataSource.h" +#include "structDataMeasurementModel.h" +#include "structDataPropertyModel.h" +#include "structDataPropertyDelegate.h" +#include "structDataMeasurementDelegate.h" +#include "dataBase.h" +//#include "global.h" + +StructDataPreviewDlg::StructDataPreviewDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::structDataPreviewDlg) + ,_pExtraProManager(nullptr) + ,_treeModel(nullptr) + ,m_currentCategoryItem(nullptr) + ,m_measurementTableModel(nullptr) + ,m_propertyTableModel(nullptr) + ,m_propertyDelegate(nullptr) + ,m_measurementDelegate(nullptr) + ,m_statusBar(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + m_statusBar = new QStatusBar(this); + ui->mainLayout->addWidget(m_statusBar); + m_statusBar->setMaximumHeight(24); + initial(); +} + +StructDataPreviewDlg::~StructDataPreviewDlg() +{ + delete ui; +} + +void StructDataPreviewDlg::loadData() +{ + if(_pExtraProManager) + m_dataSource->loadExtrapro(_pExtraProManager->geAlltProperty()); + m_dataSource->loadMeasurementData(DataBase::GetInstance()->getAllMeasurements()); + m_dataSource->loadPropertyData(DataManager::instance().modelData()); + addLog(QString("载入属性:%1").arg(m_dataSource->getAllProperties().size())); +} + +void StructDataPreviewDlg::initial() +{ + // 创建标题栏 + m_titleBar = new TitleBar(this); + m_titleBar->setTitle("结构数据展示"); + ui->mainLayout->insertWidget(0,m_titleBar); + + QMenuBar *menuBar = new QMenuBar(this); + QMenu *fileMenu = menuBar->addMenu("文件"); + QAction *saveAction = fileMenu->addAction("保存"); + menuBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + menuBar->setFixedHeight(33); + + QString style = R"( + QMenuBar { + background: #f1f5f9; + border-bottom: 1px solid #cbd5e1; + color: #334155; + font-weight: 500; + padding: 2px 0; + } + + QMenuBar::item { + padding: 6px 12px; + border-radius: 3px; + margin: 0 1px; + } + + QMenuBar::item:hover { + background: #e2e8f0; + border: 1px solid #cbd5e1; + } + + QMenuBar::item:pressed { + background: #cbd5e0; + } + + QMenu { + background: #ffffff; + border: 1px solid #cbd5e1; + border-radius: 4px; + color: #475569; + padding: 4px 0; + } + + QMenu::item { + padding: 2px 20px 2px 6px; + margin: 1px 2px; + min-height: 20px; + } + + QMenu::item:selected { + background: #3b82f6; + color: white; + border-radius: 2px; + } + + QMenu::separator { + height: 1px; + background: #e2e8f0; + margin: 3px 8px; + } + )"; + + setStyleSheet(style); + + ui->mainLayout->insertWidget(1,menuBar); + ui->mainLayout->setStretchFactor(menuBar, 0); + ui->splitter_2->setStretchFactor(0,1); + ui->splitter_2->setStretchFactor(1,4); + + ui->splitter->setStretchFactor(0,5); + ui->splitter->setStretchFactor(1,1); + + connect(saveAction,&QAction::triggered,this,&StructDataPreviewDlg::onSaveClicked); + + connect(m_titleBar, &TitleBar::maximizeClicked, this, &StructDataPreviewDlg::toggleMaximize); + connect(m_titleBar, &TitleBar::closeClicked, this, &StructDataPreviewDlg::onExitClicked); + + _treeModel = new QStandardItemModel(this); + _treeModel->setHorizontalHeaderLabels(QStringList() << "属性层级结构"); + ui->tree_level->setModel(_treeModel); + + connect(ui->btn_gird,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(0); + }); + connect(ui->btn_zone,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(1); + }); + connect(ui->btn_station,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(2); + }); + connect(ui->btn_level,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(3); + }); + connect(ui->btn_bay,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(4); + }); + connect(ui->btn_device,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(5); + }); + connect(ui->btn_group,&QPushButton::clicked,this,[=](){ + onLevelButtonClicked(6); + }); + m_dataSource = new StructDataSource(this); + + connect(ui->tree_level->selectionModel(), &QItemSelectionModel::currentChanged, + this, &StructDataPreviewDlg::onTreeSelectionChanged); + + // 连接数据更新信号 + //connect(m_dataSource, &StructDataSource::propertyUpdated,this, &StructDataPreviewDlg::onPropertyUpdated); + + m_propertyDelegate = new StructDataPropertyDelegate(this); + m_measurementDelegate = new StructDataMeasurementDelegate(this); + + _recommandCompleter = new QCompleter(this); + _recommandCompleter->setCaseSensitivity(Qt::CaseInsensitive); + _recommandCompleter->setFilterMode(Qt::MatchContains); + _recommandCompleter->setCompletionMode(QCompleter::PopupCompletion); + _strLstModel = new QStringListModel(this); + + _recommandCompleter->setModel(_strLstModel); + + m_propertyDelegate->setConnectParaCompleter(_recommandCompleter); + m_measurementDelegate->setConnectParaCompleter(_recommandCompleter); +} + + +void StructDataPreviewDlg::clearItems() +{ + if(_treeModel){ + QStandardItem *root = _treeModel->invisibleRootItem(); //先清空model + int rowCount = root->rowCount(); + if (rowCount > 0) { + _treeModel->removeRows(0, rowCount); + } + } +} + +QString StructDataPreviewDlg::getLevelType(int index) { + switch (index) { + case 0: return "电网"; + case 1: return "区域"; + case 2: return "站点"; + case 3: return "电压等级"; + case 4: return "间隔"; + case 5: return "设备"; + case 6: return "分组"; + default: return "未知层级"; + } +} + +void StructDataPreviewDlg::expandToLevel(QTreeView* treeView, int targetLevel) +{ + if (!treeView || !treeView->model()) { + return; + } + + QAbstractItemModel* model = treeView->model(); + + // 先收缩所有 + treeView->collapseAll(); + + // 递归展开到指定层级 + expandToLevelRecursive(treeView, model, QModelIndex(), 0, targetLevel); +} + +void StructDataPreviewDlg::expandToLevelRecursive(QTreeView* treeView, + QAbstractItemModel* model, + const QModelIndex& parent, + int currentDepth, + int targetLevel) +{ + if (!model || !treeView) { + return; + } + + int rowCount = model->rowCount(parent); + + for (int row = 0; row < rowCount; ++row) { + QModelIndex index = model->index(row, 0, parent); + + // 获取节点的实际层级 + int nodeLevel = getNodeLevel(model, index); + + if (nodeLevel <= targetLevel) { + // 展开当前节点 + treeView->expand(index); + } else { + // 收缩当前节点 + treeView->collapse(index); + } + + // 递归处理子节点 + if (model->hasChildren(index)) { + expandToLevelRecursive(treeView, model, index, currentDepth + 1, targetLevel); + } + } +} + +int StructDataPreviewDlg::getNodeLevel(QAbstractItemModel* model, const QModelIndex& index) +{ + if (!model || !index.isValid()) { + return -1; + } + + // 从UserRole+1中获取层级信息 + QVariant data = model->data(index, Qt::UserRole + 1); + if (data.isValid()) { + QVariantMap nodeData = data.toMap(); + + // 先检查是否是属性节点 + if (nodeData.contains("property")) { + return 7; + } + + // 检查层级索引 + if (nodeData.contains("levelIndex")) { + int levelIndex = nodeData["levelIndex"].toInt(); + return qMin(levelIndex, 7); + } + } + + // 如果没有存储层级信息,通过深度判断 + return getDepthFromRoot(model, index); +} + +QStandardItem* StructDataPreviewDlg::processGroupLevel(QStandardItem* componentItem, + const ExtraProperty& property) { + QString groupDisplayText = (property.group_name.isEmpty() ? + property.group_tag : property.group_name); + + if (groupDisplayText.isEmpty()) { + groupDisplayText = "未命名分组"; + } + + // 查找group节点 + for (int row = 0; row < componentItem->rowCount(); ++row) { + QStandardItem* child = componentItem->child(row, 0); + if (child) { + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + QString levelType = childData.value("levelType").toString(); + + if (levelType == "group") { + // 检查是否是同一个group + QString existingGroupTag = childData.value("groupTag", "").toString(); + QString existingModelName = childData.value("modelName", "").toString(); + QString propertyModelName = property.sourceConfig.value("modelName").toString(); + + if (existingGroupTag == property.group_tag) { + if (property.sourceType == "property") { + // 对于property类型,还需要检查modelName + if (existingModelName == propertyModelName) { + return child; + } + } else { + // 对于measurement类型,只需要groupTag匹配 + return child; + } + } + } + } + } + + // 创建新的group节点 + QStandardItem* groupItem = new QStandardItem(groupDisplayText); + QVariantMap groupData; + groupData["levelIndex"] = 6; // 第7层 + groupData["levelType"] = "group"; + groupData["groupName"] = property.group_name; + groupData["groupTag"] = property.group_tag; + groupData["sourceType"] = property.sourceType; + + if (property.sourceType == "property") { + groupData["modelName"] = property.sourceConfig.value("modelName"); + } + + groupData["properties"] = QVariant::fromValue(QVector()); + groupItem->setData(groupData, Qt::UserRole + 1); + + // 设置工具提示 + QString tooltip = QString("分组: %1\n类型: %2") + .arg(groupDisplayText) + .arg(property.sourceType == "property" ? "属性" : "量测"); + groupItem->setToolTip(tooltip); + + componentItem->appendRow(groupItem); + return groupItem; +} + +// 处理category层级 +void StructDataPreviewDlg::processCategoryLevel(QStandardItem* groupItem, + const ExtraProperty& property) { + QString categoryName = property.type_name; + QString paraType = property.type_tag; + + if (categoryName.isEmpty() || paraType.isEmpty()) { + qWarning() << "Invalid category or paraType for property:" << property.code; + return; + } + + // 查找category节点 + for (int row = 0; row < groupItem->rowCount(); ++row) { + QStandardItem* child = groupItem->child(row, 0); + if (child) { + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + QString levelType = childData.value("levelType").toString(); + QString childParaType = childData.value("paraType", "").toString(); + + if (levelType == "category" && childParaType == paraType) { + // 找到对应的category节点,更新属性列表 + updateCategoryProperties(child, property); + return; + } + } + } + + // 创建新的category节点 + QStandardItem* categoryItem = new QStandardItem(categoryName); + QVariantMap categoryData; + categoryData["levelType"] = "category"; + categoryData["paraType"] = paraType; + categoryData["sourceType"] = property.sourceType; + categoryData["groupTag"] = property.group_tag; + categoryData["groupName"] = property.group_name; + + if (property.sourceType == "property") { + categoryData["modelName"] = property.sourceConfig.value("modelName"); + } + + // 初始化属性列表 + QVector properties; + properties.append(property); + categoryData["properties"] = QVariant::fromValue(properties); + categoryData["propertyCount"] = 1; + + categoryItem->setData(categoryData, Qt::UserRole + 1); + + // 设置显示文本(包含数量) + categoryItem->setText(QString("%1 (1)").arg(categoryName)); + + // 设置工具提示 + QString tooltip = QString("分类: %1\n参量类型: %2\n属性数量: 1") + .arg(categoryName) + .arg(categoryName); + categoryItem->setToolTip(tooltip); + + groupItem->appendRow(categoryItem); +} + +// 更新category节点的属性列表 +void StructDataPreviewDlg::updateCategoryProperties(QStandardItem* categoryItem, + const ExtraProperty& property) { + QVariantMap categoryData = categoryItem->data(Qt::UserRole + 1).toMap(); + QVector properties = categoryData["properties"].value>(); + + // 检查属性是否已存在 + bool exists = false; + for (const auto& prop : properties) { + if (prop.code == property.code) { + exists = true; + break; + } + } + + if (!exists) { + properties.append(property); + categoryData["properties"] = QVariant::fromValue(properties); + + int newCount = properties.size(); + categoryData["propertyCount"] = newCount; + + // 更新显示文本 + QString baseName = property.type_name; + categoryItem->setText(QString("%1 (%2)").arg(baseName).arg(newCount)); + + // 更新工具提示 + QString tooltip = QString("属性数量: %1") + .arg(newCount); + categoryItem->setToolTip(tooltip); + + categoryItem->setData(categoryData, Qt::UserRole + 1); + } +} + +void StructDataPreviewDlg::loadCategoryProperties(QStandardItem* categoryItem) { + m_currentCategoryItem = categoryItem; + + // 保存当前修改 + saveCurrentIfModified(); + + // 从category节点获取属性列表 + QVariantMap categoryData = categoryItem->data(Qt::UserRole + 1).toMap(); + QVector properties = categoryData["properties"].value>(); + + if (properties.isEmpty()) { + // 如果没有属性,从DataManager重新获取 + properties = getCategoryPropertiesFromDataManager(categoryData); + } + + if (properties.isEmpty()) { + clearTableView(); + QString categoryName = categoryItem->text(); + ui->tableTitle->setText(QString("分类: %1").arg(categoryName)); + return; + } + + // 重置修改状态 + m_currentModified = false; + //updateWindowTitle(); + + // 获取group和category信息 + QString categoryName = categoryItem->text(); + QString groupName = categoryItem->parent() ? categoryItem->parent()->text() : ""; + QString sourceType = categoryData.value("sourceType").toString(); + + // 根据sourceType显示表格 + if (sourceType == "property") { + setupPropertyTable(properties, categoryName, groupName); + } else if (sourceType == "measurement") { + setupMeasurementTable(properties, categoryName, groupName); + } + + ui->tableView->setVisible(true); +} + +QVector StructDataPreviewDlg::getCategoryPropertiesFromDataManager(const QVariantMap& categoryData) { + QString groupTag = categoryData.value("groupTag").toString(); + QString modelName = categoryData.value("modelName").toString(); + QString paraType = categoryData.value("paraType").toString(); + QString sourceType = categoryData.value("sourceType").toString(); + QString componentUuid = categoryData.value("component_uuid").toString(); + + QVector result; + + for (auto it = m_dataSource->allProperties.begin(); + it != m_dataSource->allProperties.end(); ++it) { + const ExtraProperty& prop = it.value(); + + bool match = (prop.group_tag == groupTag) && + (prop.sourceType == sourceType) && + (prop.type_tag == paraType) && + (prop.component_uuid.toString() == componentUuid); + + if (sourceType == "property") { + match = match && (prop.sourceConfig.value("modelName").toString() == modelName); + } + + if (match) { + result.append(prop); + } + } + + return result; +} + +void StructDataPreviewDlg::setupPropertyTable(const QVector& properties, + const QString& categoryName, + const QString& groupName) { + if (!m_propertyTableModel) { + m_propertyTableModel = new StructDataPropertyModel(m_dataSource, this); + connect(m_propertyTableModel, &StructDataPropertyModel::propertyModified, + this, &StructDataPreviewDlg::onPropertyModified); + } + + m_propertyTableModel->setProperties(properties); + ui->tableView->setModel(m_propertyTableModel); + + // 使用Property代理 + ui->tableView->setItemDelegate(m_propertyDelegate); + + setupPropertyColumns(); + updateTableTitle("属性", categoryName, groupName, properties.size()); +} + +void StructDataPreviewDlg::setupMeasurementTable(const QVector& properties, + const QString& categoryName, + const QString& groupName) { + if (!m_measurementTableModel) { + m_measurementTableModel = new StructDataMeasurementModel(m_dataSource, this); + connect(m_measurementTableModel, &StructDataMeasurementModel::propertyModified, + this, &StructDataPreviewDlg::onPropertyModified); + } + + m_measurementTableModel->setProperties(properties); + ui->tableView->setModel(m_measurementTableModel); + + if (!m_measurementDelegate) { + m_measurementDelegate = new StructDataMeasurementDelegate(this); + } + ui->tableView->setItemDelegate(m_measurementDelegate); + + setupMeasurementColumns(); + + // 去除数量显示 + QString cleanCategoryName = categoryName; + int bracketPos = cleanCategoryName.indexOf(" ("); + if (bracketPos != -1) { + cleanCategoryName = cleanCategoryName.left(bracketPos); + } + + ui->tableTitle->setText(QString("分组: %1 - 分类: %2").arg(groupName).arg(cleanCategoryName)); +} + +void StructDataPreviewDlg::updateCategoryAfterPropertyModified(QStandardItem* categoryItem, const ExtraProperty& updatedProp) { + QVariantMap categoryData = categoryItem->data(Qt::UserRole + 1).toMap(); + QVector properties = categoryData["properties"].value>(); + + // 更新属性列表 + bool found = false; + for (auto& prop : properties) { + if (prop.code == updatedProp.code) { + prop = updatedProp; + found = true; + break; + } + } + + if (found) { + categoryData["properties"] = QVariant::fromValue(properties); + categoryItem->setData(categoryData, Qt::UserRole + 1); + } +} + +void StructDataPreviewDlg::saveCurrentIfModified() +{ + if (m_currentModified && m_currentCategoryItem) { + int result = QMessageBox::question(this, "切换分类", + "当前分类有未保存的修改,是否保存?", + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + + if (result == QMessageBox::Save) { + saveAll(); + } else if (result == QMessageBox::Cancel) { + ui->tree_level->selectionModel()->select(ui->tree_level->selectionModel()->currentIndex(), + QItemSelectionModel::Select); + } + } +} + +void StructDataPreviewDlg::saveAll() { + m_dataSource->saveAll(); + m_currentModified = false; + m_statusBar->showMessage("保存成功", 2000); +} + +void StructDataPreviewDlg::clearTableView() { + ui->tableTitle->setText("未选择分类"); + ui->tableView->setModel(nullptr); + ui->tableView->setVisible(false); + m_currentCategoryItem = nullptr; +} + +void StructDataPreviewDlg::setupPropertyColumns() { + ui->tableView->setColumnWidth(StructDataPropertyModel::ColName, 120); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColTag, 100); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColCode, 200); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColSourceType, 60); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColConnectPara, 400); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColModelName, 100); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColDataType, 80); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColDefaultValue, 100); + ui->tableView->setColumnWidth(StructDataPropertyModel::ColLengthPrecision, 80); +} + +void StructDataPreviewDlg::setupMeasurementColumns() { + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColName, 120); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColTag, 100); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColCode, 200); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColSourceType, 60); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColConnectPara, 400); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColEquipment, 100); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColChannel, 60); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColType, 60); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColSize, 60); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColSource, 60); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColStation, 80); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColPacket, 50); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColOffset, 60); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColEnable, 50); + ui->tableView->setColumnWidth(StructDataMeasurementModel::ColCause, 120); +} + +void StructDataPreviewDlg::updateTableTitle(const QString& dataType, const QString& categoryName, + const QString& groupName, int count) { + ui->tableTitle->setText(QString("%1分组: %2 - %3").arg(dataType).arg(groupName).arg(categoryName)); +} + +int StructDataPreviewDlg::getDepthFromRoot(QAbstractItemModel* model, const QModelIndex& index) { + int depth = 0; + QModelIndex parent = index.parent(); + + while (parent.isValid()) { + depth++; + parent = parent.parent(); + } + + return depth; +} + +void StructDataPreviewDlg::onExitClicked() +{ + hide(); +} + +void StructDataPreviewDlg::onSaveClicked() +{ + +} + +void StructDataPreviewDlg::onLevelButtonClicked(int nLevel) +{ + // 展开到指定层级 + expandToLevel(ui->tree_level,nLevel); +} + +void StructDataPreviewDlg::onTreeSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { + Q_UNUSED(previous); + + if (!current.isValid()) { + clearTableView(); + return; + } + + QStandardItem* item = _treeModel->itemFromIndex(current); + if (!item) { + clearTableView(); + return; + } + + QVariantMap itemData = item->data(Qt::UserRole + 1).toMap(); + QString levelType = itemData.value("levelType", "").toString(); + + if (levelType == "category") { + // 点击分类节点,从category节点获取属性 + loadCategoryProperties(item); + }else{ + clearTableView(); + } +} + +void StructDataPreviewDlg::onPropertyModified(int row, const ExtraProperty& prop) { + m_currentModified = true; + //updateWindowTitle(); + + // 更新树中category的属性数量 + if (m_currentCategoryItem) { + updateCategoryAfterPropertyModified(m_currentCategoryItem, prop); + } + + m_statusBar->showMessage(QString("属性'%1'已修改").arg(prop.name), 2000); +} + +void StructDataPreviewDlg::showEvent(QShowEvent *event) +{ + QDialog::showEvent(event); + if (m_titleBar) { + m_titleBar->updateMaximizeButton(); + } +} + +void StructDataPreviewDlg::showMaximized() +{ + if (!isMaximized()) { + m_normalGeometry = geometry(); // 保存当前位置和大小 + } + + QDialog::showMaximized(); +} + +void StructDataPreviewDlg::showNormal() +{ + QDialog::showNormal(); +} + +void StructDataPreviewDlg::showDlg() +{ + if(_pExtraProManager) + { + show(); + clearItems(); + auto& mapExtra = m_dataSource->allProperties; + QStandardItem* root = _treeModel->invisibleRootItem(); + for(auto& pro:mapExtra){ + QStandardItem* propertyItem = new QStandardItem(); + addItemToView(pro,"name",root,propertyItem); + } + + ui->tree_level->expandAll(); + } +} + +QVector StructDataPreviewDlg::getGroupProperties(QStandardItem* groupItem) { + if (!groupItem) return QVector(); + + QVariantMap data = groupItem->data(Qt::UserRole + 1).toMap(); + if (data.contains("properties")) { + return data["properties"].value>(); + } + + return QVector(); +} + +// 获取group节点的sourceType +QString getGroupSourceType(QStandardItem* groupItem) { + if (!groupItem) return ""; + + QVariantMap data = groupItem->data(Qt::UserRole + 1).toMap(); + return data.value("sourceType", "").toString(); +} + +void StructDataPreviewDlg::addItemToView(const ExtraProperty& property, + const QString& displayMode, // "name" 或 "tag" + QStandardItem* root, + QStandardItem* pItem) +{ + // 设置叶子节点的显示文本(使用name或tag) + if (displayMode == "name") { + pItem->setText(property.name.isEmpty() ? "未命名属性" : property.name); + } else { + pItem->setText(property.tag.isEmpty() ? "unknown_property" : property.tag); + } + + // 在叶子节点存储完整的属性信息 + QVariantMap propertyData; + propertyData["property"] = QVariant::fromValue(property); + propertyData["displayMode"] = displayMode; + propertyData["code"] = property.code; + pItem->setData(propertyData, Qt::UserRole + 1); + + QVector levels = { + {(displayMode == "name") ? property.grid_name : property.grid_tag, + property.grid_name, property.grid_tag, true, + (displayMode == "name") ? "未命名电网" : "unknown_grid"}, + + {(displayMode == "name") ? property.zone_name : property.zone_tag, + property.zone_name, property.zone_tag, true, + (displayMode == "name") ? "未命名区域" : "unknown_zone"}, + + {(displayMode == "name") ? property.station_name : property.station_tag, + property.station_name, property.station_tag, true, + (displayMode == "name") ? "未命名站点" : "unknown_station"}, + + {property.currentLevel, + property.currentLevel, property.currentLevel, false, + (displayMode == "name") ? "通用层级" : "common_level"}, + + {(displayMode == "name") ? property.bay_name : property.bay_tag, + property.bay_name, property.bay_tag, false, + (displayMode == "name") ? "间隔" : "bay"}, + + {property.component_name.isEmpty() ? + (displayMode == "name") ? "未命名设备" : property.component_uuid.toString() : + (displayMode == "name") ? property.component_name : property.component_uuid.toString(), + property.component_name, property.component_uuid.toString(), true, + (displayMode == "name") ? "未命名设备" : "unknown_component"} + }; + + QStandardItem* currentParent = root; + + for (size_t i = 0; i < levels.size(); ++i) { + const auto& level = levels[i]; + bool isRequired = level.isRequired; + bool isEmpty = (displayMode == "name") ? + (level.nameValue.isEmpty() && level.displayText.isEmpty()) : + (level.tagValue.isEmpty() && level.displayText.isEmpty()); + + if (!isRequired && isEmpty) { + continue; + } + + // 确定显示文本 + QString displayText = level.displayText; + bool isMissing = false; + + if (displayText.isEmpty()) { + if (isRequired) { + displayText = level.placeholder; + isMissing = true; + } else { + // 可选层级为空,跳过 + continue; + } + } + + // 查找当前层级是否已存在 + QStandardItem* foundChild = nullptr; + + for (int row = 0; row < currentParent->rowCount(); ++row) { + QStandardItem* child = currentParent->child(row, 0); + if (child && child->text() == displayText) { + // 检查是否为同一层级 + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + int childLevelIndex = childData["levelIndex"].toInt(); + + if (childLevelIndex == static_cast(i)) { + foundChild = child; + break; + } + } + } + + if (foundChild) { + currentParent = foundChild; + } else { + QStandardItem* newNode = new QStandardItem(displayText); + QVariantMap levelData; + levelData["levelIndex"] = static_cast(i); + levelData["levelType"] = getLevelType(i); + + newNode->setData(levelData, Qt::UserRole + 1); + currentParent->appendRow(newNode); + currentParent = newNode; + } + } + + // 现在currentParent是component节点 + // 处理group层级(第7层) + QStandardItem* groupItem = processGroupLevel(currentParent, property); + + // 处理category层级(在group下) + processCategoryLevel(groupItem, property); + +} + +void StructDataPreviewDlg::updateRecommandLst(QStringList lst) +{ + for(auto& newName:lst){ + if(!newName.isEmpty()){ + if(!_curRecommandLst.contains(newName)){ + _curRecommandLst.append(newName); + } + } + } + _strLstModel->setStringList(_curRecommandLst); + if (_recommandCompleter->popup()->isVisible()) { + _recommandCompleter->popup()->hide(); + } + _recommandCompleter->complete(); // 重新显示补全列表 +} + +void StructDataPreviewDlg::addLog(const QString &message) +{ + QString time = QDateTime::currentDateTime().toString("hh:mm:ss"); + ui->listWidget->addItem(QString("[%1] %2").arg(time).arg(message)); + ui->listWidget->scrollToBottom(); +} + +void StructDataPreviewDlg::resizeEvent(QResizeEvent *event) +{ + QDialog::resizeEvent(event); + if (m_titleBar) { + m_titleBar->updateMaximizeButton(); + } +} + +void StructDataPreviewDlg::toggleMaximize() +{ + if (isMaximized()) { + showNormal(); + } else { + showMaximized(); + } + + // 更新标题栏按钮 + if (m_titleBar) { + m_titleBar->updateMaximizeButton(); + } +} diff --git a/diagramCavas/source/structDataPropertyDelegate.cpp b/diagramCavas/source/structDataPropertyDelegate.cpp new file mode 100644 index 0000000..71dbd08 --- /dev/null +++ b/diagramCavas/source/structDataPropertyDelegate.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include +#include "structDataPropertyDelegate.h" +#include "structDataPropertyModel.h" +#include "uiCommunicationBus.h" + +StructDataPropertyDelegate::StructDataPropertyDelegate(QObject* parent) : QStyledItemDelegate(parent) {} + +QWidget* StructDataPropertyDelegate::createEditor(QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { + Q_UNUSED(option); + + int column = index.column(); + + switch (column) { + case StructDataPropertyModel::ColConnectPara: + return createConnectParaEditor(parent); + + case StructDataPropertyModel::ColDefaultValue: + return createDefaultValueEditor(parent, index); + + case StructDataPropertyModel::ColLengthPrecision: + return createLengthPrecisionEditor(parent); + } + + return nullptr; +} + +void StructDataPropertyDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { + int column = index.column(); + QVariant value = index.data(Qt::EditRole); + + switch (column) { + case StructDataPropertyModel::ColConnectPara: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + lineEdit->setText(value.toString()); + } + break; + + case StructDataPropertyModel::ColDefaultValue: + setDefaultValueEditorData(editor, value, index); + break; + + case StructDataPropertyModel::ColLengthPrecision: + if (QSpinBox* spinBox = qobject_cast(editor)) { + spinBox->setValue(value.toInt()); + } + break; + } +} + +void StructDataPropertyDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const { + int column = index.column(); + QVariant newValue; + + switch (column) { + case StructDataPropertyModel::ColConnectPara: + if (QLineEdit* lineEdit = qobject_cast(editor)) { + newValue = lineEdit->text().trimmed(); + } + break; + + case StructDataPropertyModel::ColDefaultValue: + newValue = getDefaultValueEditorData(editor, index); + break; + + case StructDataPropertyModel::ColLengthPrecision: + if (QSpinBox* spinBox = qobject_cast(editor)) { + newValue = spinBox->value(); + } + break; + } + + if (!newValue.isNull()) { + model->setData(index, newValue, Qt::EditRole); + } +} + +void StructDataPropertyDelegate::updateEditorGeometry(QWidget* editor, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { + Q_UNUSED(index); + editor->setGeometry(option.rect); +} + +QString StructDataPropertyDelegate::displayText(const QVariant& value, const QLocale& locale) const { + Q_UNUSED(locale); + + // 格式化显示文本 + if (value.userType() == QMetaType::Bool) { + return value.toBool() ? "是" : "否"; + } + + return QStyledItemDelegate::displayText(value, locale); +} + +QWidget* StructDataPropertyDelegate::createConnectParaEditor(QWidget* parent) const { + QLineEdit* lineEdit = new QLineEdit(parent); + lineEdit->setMaxLength(150); + lineEdit->installEventFilter(const_cast(this)); + lineEdit->setPlaceholderText("空格获取初始值"); + lineEdit->setCompleter(_connectCompleter); + + connect(lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) { + if (text.endsWith(".")) { + onConnectParamChanged(text); + } + }); + + return lineEdit; +} + +QWidget* StructDataPropertyDelegate::createDefaultValueEditor(QWidget* parent, const QModelIndex& index) const { + // 根据数据类型创建不同的编辑器 + QString dataType = index.sibling(index.row(), StructDataPropertyModel::ColDataType).data().toString().toLower(); + + if (dataType == "int" || dataType == "integer") { + QSpinBox* spinBox = new QSpinBox(parent); + spinBox->setRange(-999999, 999999); + return spinBox; + } else if (dataType == "float" || dataType == "double") { + QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent); + spinBox->setRange(-999999.0, 999999.0); + spinBox->setDecimals(3); + return spinBox; + } else if (dataType == "bool" || dataType == "boolean") { + QComboBox* comboBox = new QComboBox(parent); + comboBox->addItems({"false", "true"}); + return comboBox; + } else { + // 字符串或其他类型 + QLineEdit* lineEdit = new QLineEdit(parent); + return lineEdit; + } +} + +QWidget* StructDataPropertyDelegate::createLengthPrecisionEditor(QWidget* parent) const { + QSpinBox* spinBox = new QSpinBox(parent); + spinBox->setRange(0, 999); + spinBox->setSpecialValueText("无限制"); + return spinBox; +} + +void StructDataPropertyDelegate::setDefaultValueEditorData(QWidget* editor, const QVariant& value, const QModelIndex& index) const { + QString dataType = index.sibling(index.row(), StructDataPropertyModel::ColDataType).data().toString().toLower(); + + if (QSpinBox* spinBox = qobject_cast(editor)) { + spinBox->setValue(value.toInt()); + } else if (QDoubleSpinBox* doubleSpinBox = qobject_cast(editor)) { + doubleSpinBox->setValue(value.toDouble()); + } else if (QComboBox* comboBox = qobject_cast(editor)) { + bool boolValue = value.toBool(); + comboBox->setCurrentIndex(boolValue ? 1 : 0); + } else if (QLineEdit* lineEdit = qobject_cast(editor)) { + lineEdit->setText(value.toString()); + } +} + +QVariant StructDataPropertyDelegate::getDefaultValueEditorData(QWidget* editor, const QModelIndex& index) const { + QString dataType = index.sibling(index.row(), StructDataPropertyModel::ColDataType).data().toString().toLower(); + + if (QSpinBox* spinBox = qobject_cast(editor)) { + return spinBox->value(); + } else if (QDoubleSpinBox* doubleSpinBox = qobject_cast(editor)) { + return doubleSpinBox->value(); + } else if (QComboBox* comboBox = qobject_cast(editor)) { + return comboBox->currentIndex() == 1; + } else if (QLineEdit* lineEdit = qobject_cast(editor)) { + return lineEdit->text(); + } + + return QVariant(); +} + +void StructDataPropertyDelegate::onConnectParamChanged(const QString& str) const +{ + QVariantMap map; + map.insert("input",str); + UiCommunicationBus::instance()->sendHttpRequest("/measurement/recommend",QVariant(),"GET",map); +} + +bool StructDataPropertyDelegate::eventFilter(QObject *obj, QEvent *event) +{ + if (QLineEdit *editor = qobject_cast(obj)){ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + + if (keyEvent->key() == Qt::Key_Space) { + qDebug() << "eventFilter:space"; + onConnectParamChanged(editor->text()); + _connectCompleter->complete(); + return true; + } + + if (keyEvent->text() == ".") { + qDebug() << "eventFilter:dot"; + // 返回false让事件继续传播 + } + } + } + + return QStyledItemDelegate::eventFilter(obj, event); +} diff --git a/diagramCavas/source/structDataPropertyModel.cpp b/diagramCavas/source/structDataPropertyModel.cpp new file mode 100644 index 0000000..863318b --- /dev/null +++ b/diagramCavas/source/structDataPropertyModel.cpp @@ -0,0 +1,223 @@ +#include "structDataPropertyModel.h" +#include "structDataSource.h" +//#include "global.h" +#include "common/backend/project_model.h" + + +StructDataPropertyModel::StructDataPropertyModel(StructDataSource* dataManager, QObject* parent) + : QAbstractTableModel(parent) + , m_dataManager(dataManager) +{ + connect(m_dataManager, &StructDataSource::propertyUpdated, + this, &StructDataPropertyModel::onPropertyUpdated); +} + +// 设置要显示的code列表 +void StructDataPropertyModel::setPropertyCodes(const QStringList& codes) { + beginResetModel(); + m_propertyCodes = codes; + endResetModel(); + + //emit propertiesLoaded(m_propertyCodes.size()); +} + +// 设置要显示的属性 +void StructDataPropertyModel::setProperties(const QVector& properties) { + QStringList codes; + for (const auto& prop : properties) { + if (!prop.code.isEmpty()) { + codes.append(prop.code); + } + } + setPropertyCodes(codes); +} + +int StructDataPropertyModel::rowCount(const QModelIndex& parent) const { + return parent.isValid() ? 0 : m_propertyCodes.size(); +} + +int StructDataPropertyModel::columnCount(const QModelIndex& parent) const { + return parent.isValid() ? 0 : ColumnCount; +} + +QVariant StructDataPropertyModel::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() >= m_propertyCodes.size()) { + return QVariant(); + } + + const ExtraProperty* prop = getProperty(index.row()); + if (!prop) { + return QVariant(); + } + + int col = index.column(); + + if (role == Qt::DisplayRole || role == Qt::EditRole) { + switch (col) { + case ColName: return prop->name; + case ColTag: return prop->tag; + case ColCode: return prop->code; + case ColSourceType: return "属性"; + case ColConnectPara: return prop->connect_para; + case ColModelName: return prop->sourceConfig.value("modelName"); + default: return getPropertyData(*prop, col); + } + } + else if (role == Qt::UserRole) { + return QVariant::fromValue(*prop); + } + else if (role == Qt::UserRole + 1) { + return prop->code; // 返回code用于查找 + } + + return QVariant(); +} + +bool StructDataPropertyModel::setData(const QModelIndex& index, const QVariant& value, int role) { + if (!index.isValid() || index.row() >= m_propertyCodes.size() || role != Qt::EditRole) { + return false; + } + + ExtraProperty* prop = getProperty(index.row()); + if (!prop) { + return false; + } + + int col = index.column(); + bool changed = false; + ExtraProperty updatedProp = *prop; // 创建副本 + + if (col == ColConnectPara) { + // 连接参数自由文本输入 + QString newValue = value.toString().trimmed(); + if (updatedProp.connect_para != newValue) { + updatedProp.connect_para = newValue; + changed = true; + } + } else { + // 修改属性数据 + PropertyStateInfo* data = m_dataManager->getPropertyData(updatedProp); + if (data) { + changed = updatePropertyData(data, col, value); + if (changed) { + emit m_dataManager->dataChanged(); + } + } + } + + if (changed) { + // 更新到DataManager + updatedProp.bDataChanged = true; //修改后设为真 + if (m_dataManager->updateProperty(updatedProp)) { + emit dataChanged(index, index, {role}); + emit propertyModified(index.row(), updatedProp); + return true; + } + } + + return false; +} + +Qt::ItemFlags StructDataPropertyModel::flags(const QModelIndex& index) const { + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + int col = index.column(); + if (col == ColConnectPara || + col == ColLengthPrecision || + col == ColDefaultValue) { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + +QVariant StructDataPropertyModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + QStringList headers = { + "名称", "标签", "编码", "类型", "连接参数", + "模型名", "数据类型", "默认值", "长度精度" + }; + return headers.value(section, ""); + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +// 获取当前显示的属性 +QVector StructDataPropertyModel::getDisplayedProperties() const { + QVector result; + for (const QString& code : m_propertyCodes) { + ExtraProperty* prop = m_dataManager->getPropertyByCode(code); + if (prop) { + result.append(*prop); + } + } + return result; +} + +// 获取当前显示的code列表 +QStringList StructDataPropertyModel::getDisplayedCodes() const { + return m_propertyCodes; +} + +// 根据code查找行 +int StructDataPropertyModel::findRowByCode(const QString& code) const { + return m_propertyCodes.indexOf(code); +} + +// 刷新指定code的行 +void StructDataPropertyModel::refreshRow(const QString& code) { + int row = findRowByCode(code); + if (row >= 0) { + QModelIndex start = index(row, 0); + QModelIndex end = index(row, columnCount() - 1); + emit dataChanged(start, end); + } +} + +void StructDataPropertyModel::onPropertyUpdated(const ExtraProperty& updatedProp, bool isNew) { + Q_UNUSED(isNew); + + // 如果这个属性在当前显示列表中,刷新对应行 + int row = findRowByCode(updatedProp.code); + if (row >= 0) { + refreshRow(updatedProp.code); + } +} + +ExtraProperty* StructDataPropertyModel::getProperty(int displayRow) const { + if (displayRow < 0 || displayRow >= m_propertyCodes.size()) { + return nullptr; + } + return m_dataManager->getPropertyByCode(m_propertyCodes[displayRow]); +} + +QVariant StructDataPropertyModel::getPropertyData(const ExtraProperty& prop, int col) const { + PropertyStateInfo* data = m_dataManager->getPropertyData(prop); + if (!data) return QVariant(); + + switch (col) { + case ColDataType: return data->type; + case ColDefaultValue: return data->defaultValue; + case ColLengthPrecision: return data->lengthPrecision; + } + return QVariant(); +} + +bool StructDataPropertyModel::updatePropertyData(PropertyStateInfo* data, int col, const QVariant& value) { + switch (col) { + case ColLengthPrecision: + if (data->lengthPrecision != value.toInt()) { + data->lengthPrecision = value.toInt(); + return true; + } + break; + case ColDefaultValue: + if (data->defaultValue != value) { + data->defaultValue = value; + return true; + } + break; + } + return false; +} + diff --git a/diagramCavas/source/titleBar.cpp b/diagramCavas/source/titleBar.cpp new file mode 100644 index 0000000..037ad56 --- /dev/null +++ b/diagramCavas/source/titleBar.cpp @@ -0,0 +1,113 @@ +#include "titleBar.h" +#include +#include +#include + + +TitleBar::TitleBar(QWidget *parent) + : QWidget(parent) + , m_parentWindow(parent) +{ + setupUI(); + setFixedHeight(35); // 稍微矮一点 + + setAttribute(Qt::WA_StyledBackground, true); // 启用样式背景 + setAttribute(Qt::WA_NoSystemBackground, false); + + // 确保有自己的调色板 + setAutoFillBackground(true); + + // 使用更具体的样式表 + QString style = R"( + QWidget { + background: #2c5282; + } + + QLabel { + color: white; + font-size: 12px; + background: transparent; + } + + QPushButton { + background: transparent; + border: none; + color: white; + min-width: 30px; + min-height: 30px; + } + )"; + + setStyleSheet(style); +} + +void TitleBar::setupUI() +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setContentsMargins(4, 0, 0, 0); + layout->setSpacing(0); + + // 标题 + m_titleLabel = new QLabel("窗口标题"); + m_titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + // 最大化/还原按钮 + m_maximizeButton = new QPushButton(tr("🗖")); // 最大化图标 + m_maximizeButton->setObjectName("maximizeButton"); + m_maximizeButton->setToolTip("最大化"); + + // 关闭按钮 + m_closeButton = new QPushButton("×"); + m_closeButton->setObjectName("closeButton"); + m_closeButton->setToolTip("关闭"); + + layout->addWidget(m_titleLabel); + layout->addWidget(m_maximizeButton); + layout->addWidget(m_closeButton); + + // 连接信号 + connect(m_maximizeButton, &QPushButton::clicked, this, &TitleBar::maximizeClicked); + connect(m_closeButton, &QPushButton::clicked, this, &TitleBar::closeClicked); +} + +void TitleBar::setTitle(const QString &title) +{ + m_titleLabel->setText(title); +} + +void TitleBar::updateMaximizeButton() +{ + if (m_parentWindow) { + m_isMaximized = m_parentWindow->isMaximized(); + if (m_isMaximized) { + m_maximizeButton->setText(tr("🗗")); // 还原图标 + m_maximizeButton->setToolTip("还原"); + } else { + m_maximizeButton->setText(tr("🗖")); // 最大化图标 + m_maximizeButton->setToolTip("最大化"); + } + } +} + +void TitleBar::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + m_dragStartPosition = event->globalPosition().toPoint() - m_parentWindow->frameGeometry().topLeft(); + event->accept(); + } +} + +void TitleBar::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + m_parentWindow->move(event->globalPosition().toPoint() - m_dragStartPosition); + event->accept(); + } +} + +void TitleBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + emit maximizeClicked(); + } +} diff --git a/diagramCavas/source/topologyManager.cpp b/diagramCavas/source/topologyManager.cpp new file mode 100644 index 0000000..3da35ed --- /dev/null +++ b/diagramCavas/source/topologyManager.cpp @@ -0,0 +1,1093 @@ +#include +#include +#include "topologyManager.h" +#include "powerEntity.h" +#include "graphicsItem/graphicsBaseItem.h" + +TopologyManager& TopologyManager::instance() +{ + static TopologyManager instance; + return instance; +} + +TopologyManager::TopologyManager(QObject* parent) + : QObject(parent) {} + +TopologyManager::~TopologyManager() { + clearAllData(); +} + +void TopologyManager::clearAllData() +{ + // 删除所有连接 + qDeleteAll(m_connections); + m_connections.clear(); + m_connectionIndex.clear(); + // 删除所有实体(自动删除子对象) + qDeleteAll(m_entities); + m_entities.clear(); + m_allTerminals.clear(); //端点由父亲entity释放 + + // 删除所有连接 + qDeleteAll(m_baseConnections); + m_baseConnections.clear(); + m_baseConnectionIndex.clear(); + // 删除所有实体(自动删除子对象) + qDeleteAll(m_baseEntities); + m_baseEntities.clear(); + m_baseAllTerminals.clear(); //端点由父亲entity释放 + + // 删除所有连接 + qDeleteAll(m_editorConnections); + m_editorConnections.clear(); + m_editorConnectionIndex.clear(); + // 删除所有实体(自动删除子对象) + qDeleteAll(m_editorEntities); + m_editorEntities.clear(); + m_editorAllTerminals.clear(); //端点由父亲entity释放 + + qDeleteAll(m_blockConnections); + m_blockConnections.clear(); + m_blockConnectionIndex.clear(); + // 删除所有实体(自动删除子对象) + qDeleteAll(m_blockEntities); + m_blockEntities.clear(); + m_blockAllTerminals.clear(); //端点由父亲entity释放 +} + +PowerEntity* TopologyManager::createEntity(EntityType type,const QString& uuid, const QString& name,ModelFunctionType funType,const QString& block) +{ + if(funType == ModelFunctionType::ProjectModel) + { + PowerEntity* entity = nullptr; + switch(type) { + case EntityType::Grid: + case EntityType::Zone: + case EntityType::Station: + entity = new PowerDivision(type, uuid,name); + default: + entity = new PowerComponent(type, uuid,name); + } + + if(entity == nullptr) + return nullptr; + + m_entities.insert(entity->id(), entity); + emit entityCreated(entity->id()); // 发送信号通知 + return entity; + } + else if(funType == ModelFunctionType::BaseModel) + { + PowerEntity* entity = nullptr; + entity = new PowerComponent(type, uuid,name); + + m_baseEntities.insert(entity->id(), entity); + emit entityCreated(entity->id()); // 发送信号通知 + return entity; + } + else if(funType == ModelFunctionType::EditorModel) + { + PowerEntity* entity = nullptr; + entity = new PowerComponent(type, uuid,name); + + m_editorEntities.insert(entity->id(), entity); + return entity; + } + else if(funType == ModelFunctionType::BlockEditorModel) + { + PowerEntity* entity = nullptr; + entity = new PowerComponent(type, uuid,name); + entity->setBlock(block); + + m_blockEntities.insert(entity->id(), entity); + return entity; + } + return nullptr; +} + +PowerEntity* TopologyManager::findEntity(const QString& id,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel) + return m_entities.value(id, nullptr); // 避免异常的安全查询 + else if(funType == ModelFunctionType::BaseModel) + return m_baseEntities.value(id, nullptr); + else if(funType == ModelFunctionType::EditorModel) + return m_editorEntities.value(id, nullptr); + else if(funType == ModelFunctionType::BlockEditorModel) + return m_blockEntities.value(id, nullptr); + return nullptr; +} + +bool TopologyManager::deleteEntity(const QString& id,ModelFunctionType funType) +{ + if(funType == ModelFunctionType::ProjectModel) + { + if (!m_entities.contains(id)) return false; + + PowerEntity* entity = m_entities[id]; + + // 步骤1:删除所有相关连接 + auto relatedConns = getConnectionsFor(id); + + for (auto conn : relatedConns) { + removeConnection(conn->id()); + } + + // 步骤2:从父节点移除(防止悬空指针) + if (PowerEntity* parent = entity->parent()) { + parent->removeChild(entity); + } + + // 步骤3:递归删除子实体 + auto children = entity->children(); + for (auto child : children) { + deleteEntity(child->id()); // 递归删除 + } + + // 步骤4:从哈希表移除并释放内存 + m_entities.remove(id); + delete entity; // 触发PowerEntity析构函数 + + emit entityDeleted(id); + return true; + } + else if(funType == ModelFunctionType::BaseModel) + { + if (!m_baseEntities.contains(id)) return false; + PowerEntity* entity = m_baseEntities[id]; + + // 步骤1:删除所有相关连接 + auto relatedConns = getConnectionsFor(id,funType); + for (auto conn : relatedConns) { + removeConnection(conn->id(),funType); + } + + // 步骤2:从父节点移除(防止悬空指针) + if (PowerEntity* parent = entity->parent()) { + parent->removeChild(entity); + } + + // 步骤3:递归删除子实体 + auto children = entity->children(); + for (auto child : children) { + deleteEntity(child->id(),funType); // 递归删除 + } + + // 步骤4:从哈希表移除并释放内存 + m_baseEntities.remove(id); + delete entity; // 触发PowerEntity析构函数 + + emit entityDeleted(id); + return true; + } + else if(funType == ModelFunctionType::EditorModel) + { + if (!m_editorEntities.contains(id)) return false; + PowerEntity* entity = m_editorEntities[id]; + + // 步骤1:删除所有相关连接 + auto relatedConns = getConnectionsFor(id,funType); + for (auto conn : relatedConns) { + removeConnection(conn->id(),funType); + } + + // 步骤2:从父节点移除(防止悬空指针) + if (PowerEntity* parent = entity->parent()) { + parent->removeChild(entity); + } + + // 步骤3:递归删除子实体 + auto children = entity->children(); + for (auto child : children) { + deleteEntity(child->id(),funType); // 递归删除 + } + + // 步骤4:从哈希表移除并释放内存 + m_editorEntities.remove(id); + delete entity; // 触发PowerEntity析构函数 + + emit entityDeleted(id); + return true; + } + else if(funType == ModelFunctionType::BlockEditorModel) + { + if (!m_blockEntities.contains(id)) return false; + PowerEntity* entity = m_blockEntities[id]; + + // 步骤1:删除所有相关连接 + auto relatedConns = getConnectionsFor(id,funType); + for (auto conn : relatedConns) { + removeConnection(conn->id(),funType); + } + + // 步骤2:从父节点移除(防止悬空指针) + if (PowerEntity* parent = entity->parent()) { + parent->removeChild(entity); + } + + // 步骤3:递归删除子实体 + auto children = entity->children(); + for (auto child : children) { + deleteEntity(child->id(),funType); // 递归删除 + } + + // 步骤4:从哈希表移除并释放内存 + m_blockEntities.remove(id); + delete entity; // 触发PowerEntity析构函数 + + emit entityDeleted(id); + return true; + } + return false; +} + +QList TopologyManager::getEntitiesByBlock(const QString& str) +{ + QList lst; + for(auto& entity:m_blockEntities){ + if(entity->block() == str){ + lst.append(entity); + } + } + return lst; +} + +void TopologyManager::moveTempBlockData() +{ + auto tempEntities = std::move(m_blockEntities); + m_editorEntities.insert(tempEntities); + + auto tempConnections = std::move(m_blockConnections); + m_editorConnections.insert(tempConnections); + + auto tempConIndex = std::move(m_blockConnectionIndex); + m_editorConnectionIndex.unite(tempConIndex); + + auto tempTer = std::move(m_blockAllTerminals); + m_editorAllTerminals.insert(tempTer); + + auto tempEntityTer = std::move(m_blockTerminalsByEntity); + m_editorTerminalsByEntity.insert(tempEntityTer); + + auto tempEntityCon = std::move(m_blockEntityConnections); + m_editorEntityConnections.insert(tempEntityCon); +} + +void TopologyManager::clearGlobalBlockData(const QString& sName) +{ + QList lst = getEntitiesByBlock(sName); + for(auto p:lst){ + deleteEntity(p->id(),ModelFunctionType::EditorModel); + } +} + +void TopologyManager::cloneEditorToBase() +{ + for(auto& editorEntity:m_editorEntities){ + editorEntity->clone(); + } + + for(auto& con:m_editorConnections){ + if(connection(con->id(),ModelFunctionType::BaseModel) == nullptr) + createConnection(con->id(),con->fromTerminalId(),con->toTerminalId(),con->fromComponent(),con->toComponent(),ModelFunctionType::BaseModel); + } +} + +PowerConnection* TopologyManager::createConnection(const QString& connId,const QString& fromTerId, const QString& toTerId,const QString& fromId,const QString& toId,ModelFunctionType funType) +{ + PowerConnection* conn = nullptr; + if(funType == ModelFunctionType::ProjectModel) + { + // 验证有效性 + if (!m_allTerminals.contains(fromTerId) || + !m_allTerminals.contains(toTerId) || + fromTerId == toTerId) + { + qWarning() << "Invalid connection endpoints"; + return nullptr; + } + + // 防止重复连接 + foreach (auto conn, m_connections) { + if (conn->fromTerminalId() == fromTerId && + conn->toTerminalId() == toTerId) + { + return conn; // 返回已存在的连接 + } + } + + // 创建新连接 + conn = new PowerConnection(connId,fromTerId, toTerId,fromId,toId); + m_connections[connId] = conn; + + // 更新索引 + m_connectionIndex.insert(fromTerId, conn); + m_connectionIndex.insert(toTerId, conn); + + emit connectionCreated(connId); + } + else if(funType == ModelFunctionType::BaseModel) + { + // 验证有效性 + if (!m_baseAllTerminals.contains(fromTerId) || + !m_baseAllTerminals.contains(toTerId) || + fromTerId == toTerId) + { + qWarning() << "Invalid connection endpoints"; + return nullptr; + } + + // 防止重复连接 + foreach (auto conn, m_baseConnections) { + if (conn->fromTerminalId() == fromTerId && + conn->toTerminalId() == toTerId) + { + return conn; // 返回已存在的连接 + } + } + + // 创建新连接 + conn = new PowerConnection(connId,fromTerId, toTerId,fromId,toId); + m_baseConnections[connId] = conn; + + // 更新索引 + m_baseConnectionIndex.insert(fromTerId, conn); + m_baseConnectionIndex.insert(toTerId, conn); + + emit connectionCreated(connId); + } + else if(funType == ModelFunctionType::EditorModel) + { + // 验证有效性 + if (!m_editorAllTerminals.contains(fromTerId) || + !m_editorAllTerminals.contains(toTerId) || + fromTerId == toTerId) + { + qWarning() << "Invalid connection endpoints"; + return nullptr; + } + + // 防止重复连接 + foreach (auto conn, m_editorConnections) { + if (conn->fromTerminalId() == fromTerId && + conn->toTerminalId() == toTerId) + { + return conn; // 返回已存在的连接 + } + } + + // 创建新连接 + conn = new PowerConnection(connId,fromTerId, toTerId,fromId,toId); + m_editorConnections[connId] = conn; + + // 更新索引 + m_editorConnectionIndex.insert(fromTerId, conn); + m_editorConnectionIndex.insert(toTerId, conn); + + emit connectionCreated(connId); + } + else if(funType == ModelFunctionType::BlockEditorModel) + { + // 验证有效性 + if (!m_blockAllTerminals.contains(fromTerId) || + !m_blockAllTerminals.contains(toTerId) || + fromTerId == toTerId) + { + qWarning() << "Invalid connection endpoints"; + return nullptr; + } + + // 防止重复连接 + foreach (auto conn, m_blockConnections) { + if (conn->fromTerminalId() == fromTerId && + conn->toTerminalId() == toTerId) + { + return conn; // 返回已存在的连接 + } + } + + // 创建新连接 + conn = new PowerConnection(connId,fromTerId, toTerId,fromId,toId); + m_blockConnections[connId] = conn; + + // 更新索引 + m_blockConnectionIndex.insert(fromTerId, conn); + m_blockConnectionIndex.insert(toTerId, conn); + + emit connectionCreated(connId); + } + return conn; +} + +QList TopologyManager::getConnectionsForTerminal(const QString& terminalId,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel) + return m_connectionIndex.values(terminalId); + else if(funType == ModelFunctionType::BaseModel) + return m_baseConnectionIndex.values(terminalId); + else if(funType == ModelFunctionType::EditorModel) + return m_editorConnectionIndex.values(terminalId); + else if(funType == ModelFunctionType::BlockEditorModel) + return m_blockConnectionIndex.values(terminalId); + return QList(); +} + +void TopologyManager::removeConnection(const QString& connId,ModelFunctionType funType) +{ + if(funType == ModelFunctionType::ProjectModel){ + if (!m_connections.contains(connId)) return; + + PowerConnection* conn = m_connections[connId]; + + // 更新索引 + m_connectionIndex.remove(conn->fromTerminalId(), conn); + m_connectionIndex.remove(conn->toTerminalId(), conn); + + // 清理内存 + m_connections.remove(connId); + delete conn; + + emit connectionRemoved(connId); + } + else if(funType == ModelFunctionType::BaseModel){ + if (!m_baseConnections.contains(connId)) return; + + PowerConnection* conn = m_baseConnections[connId]; + + // 更新索引 + m_baseConnectionIndex.remove(conn->fromTerminalId(), conn); + m_baseConnectionIndex.remove(conn->toTerminalId(), conn); + + // 清理内存 + m_baseConnections.remove(connId); + delete conn; + + emit connectionRemoved(connId); + } + else if(funType == ModelFunctionType::EditorModel){ + if (!m_editorConnections.contains(connId)) return; + + PowerConnection* conn = m_editorConnections[connId]; + + // 更新索引 + m_editorConnectionIndex.remove(conn->fromTerminalId(), conn); + m_editorConnectionIndex.remove(conn->toTerminalId(), conn); + + // 清理内存 + m_editorConnections.remove(connId); + delete conn; + + emit connectionRemoved(connId); + } + else if(funType == ModelFunctionType::BlockEditorModel){ + if (!m_blockConnections.contains(connId)) return; + + PowerConnection* conn = m_blockConnections[connId]; + + // 更新索引 + m_blockConnectionIndex.remove(conn->fromTerminalId(), conn); + m_blockConnectionIndex.remove(conn->toTerminalId(), conn); + + // 清理内存 + m_blockConnections.remove(connId); + delete conn; + + emit connectionRemoved(connId); + } +} + +bool TopologyManager::validateConnection(const QString& fromTermId, const QString& toTermId,ModelFunctionType funType) const +{ + PowerTerminal* fromTerm = nullptr; + PowerTerminal* toTerm = nullptr; + if(funType == ModelFunctionType::ProjectModel){ + fromTerm = getTerminal(fromTermId); + toTerm = getTerminal(toTermId); + } + else if(funType == ModelFunctionType::BaseModel){ + fromTerm = getTerminal(fromTermId,funType); + toTerm = getTerminal(toTermId,funType); + } + else if(funType == ModelFunctionType::EditorModel){ + fromTerm = getTerminal(fromTermId,funType); + toTerm = getTerminal(toTermId,funType); + } + else if(funType == ModelFunctionType::BlockEditorModel){ + fromTerm = getTerminal(fromTermId,funType); + toTerm = getTerminal(toTermId,funType); + } + + if (!fromTerm || !toTerm) return false; + // 类型兼容性检查(示例规则) + switch(fromTerm->type()) { + case TerminalType::PowerOutput: + if (toTerm->type() != TerminalType::PowerInput && toTerm->type() != TerminalType::PowerConnect) return false; + break; + case TerminalType::PowerInput: + if (toTerm->type() != TerminalType::PowerOutput && toTerm->type() != TerminalType::PowerConnect) return false; + break; + case TerminalType::PowerConnect: + break; + case TerminalType::ControlSignal: + if (toTerm->type() != TerminalType::ControlSignal) return false; + break; + default: + return false; + } + + // 禁止自连接 + return (fromTerm->parentEntityId() != toTerm->parentEntityId()); + +} + +QList TopologyManager::getConnectionsFor(const QString& entityId,ModelFunctionType funType) const +{ + QList lst; + if(funType == ModelFunctionType::ProjectModel) + { + QList lstTerminal = getTerminalsForEntity(entityId); + for(auto &terminal:lstTerminal) + { + PowerConnection* con = getConnectionContainsTerminal(terminal->id()); + if(con) + lst.append(con); + } + } + else if(funType == ModelFunctionType::BaseModel) + { + QList lstTerminal = getTerminalsForEntity(entityId,funType); + for(auto &terminal:lstTerminal) + { + PowerConnection* con = getConnectionContainsTerminal(terminal->id(),funType); + if(con) + lst.append(con); + } + } + else if(funType == ModelFunctionType::EditorModel) + { + QList lstTerminal = getTerminalsForEntity(entityId,funType); + for(auto &terminal:lstTerminal) + { + PowerConnection* con = getConnectionContainsTerminal(terminal->id(),funType); + if(con) + lst.append(con); + } + } + else if(funType == ModelFunctionType::BlockEditorModel) + { + QList lstTerminal = getTerminalsForEntity(entityId,funType); + for(auto &terminal:lstTerminal) + { + PowerConnection* con = getConnectionContainsTerminal(terminal->id(),funType); + if(con) + lst.append(con); + } + } + return lst; +} + +PowerConnection* TopologyManager::connection(const QString& conId,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel) + return m_connections.value(conId,nullptr); + else if(funType == ModelFunctionType::BaseModel) + return m_baseConnections.value(conId,nullptr); + else if(funType == ModelFunctionType::EditorModel) + return m_editorConnections.value(conId,nullptr); + else if(funType == ModelFunctionType::BlockEditorModel) + return m_blockConnections.value(conId,nullptr); + return nullptr; +} + +PowerConnection* TopologyManager::connection(const QString& fromPin,const QString& toPin,ModelFunctionType funType) +{ + if(funType == ModelFunctionType::ProjectModel){ + for(auto &con:m_connections) + { + if(con->fromTerminalId() == fromPin && con->toTerminalId() == toPin) + return con; + } + } + else if(funType == ModelFunctionType::BaseModel){ + for(auto &con:m_baseConnections) + { + if(con->fromTerminalId() == fromPin && con->toTerminalId() == toPin) + return con; + } + } + else if(funType == ModelFunctionType::EditorModel){ + for(auto &con:m_editorConnections) + { + if(con->fromTerminalId() == fromPin && con->toTerminalId() == toPin) + return con; + } + } + else if(funType == ModelFunctionType::BlockEditorModel){ + for(auto &con:m_blockConnections) + { + if(con->fromTerminalId() == fromPin && con->toTerminalId() == toPin) + return con; + } + } + return nullptr; +} + +PowerConnection* TopologyManager::ifConnection(const QString& entityId1,const QString& entityId2,ModelFunctionType funType) +{ + QList lst1; + QList lst2; + + QList lstTerminal1 = getTerminalsForEntity(entityId1,funType); + for(auto &terminal:lstTerminal1) + { + PowerConnection* con = getConnectionContainsTerminal(terminal->id(),funType); + if(con) + lst1.append(con); + } + + QList lstTerminal2 = getTerminalsForEntity(entityId2,funType); + for(auto &terminal:lstTerminal2) + { + PowerConnection* con = getConnectionContainsTerminal(terminal->id(),funType); + if(con) + lst2.append(con); + } + + for(auto pCon1:lst1){ + for(auto pCon2:lst2){ + if(pCon1->id() == pCon2->id()){ //两个item的连接有重合,返回该连接 + return pCon1; + } + } + } + return nullptr; +} + +QHash TopologyManager::getAllConnections(ModelFunctionType funType) +{ + if(funType == ModelFunctionType::ProjectModel) + return m_connections; + else if(funType == ModelFunctionType::BaseModel) + return m_baseConnections; + else if(funType == ModelFunctionType::EditorModel) + return m_editorConnections; + else if(funType == ModelFunctionType::BlockEditorModel) + return m_blockConnections; + return QHash(); +} + +PowerEntity* TopologyManager::getEntity(const QString& id,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel){ + auto it = m_entities.find(id); + return (it != m_entities.end()) ? it.value() : nullptr; + } + else if(funType == ModelFunctionType::BaseModel){ + auto it = m_baseEntities.find(id); + return (it != m_baseEntities.end()) ? it.value() : nullptr; + } + else if(funType == ModelFunctionType::EditorModel){ + auto it = m_editorEntities.find(id); + return (it != m_editorEntities.end()) ? it.value() : nullptr; + } + else if(funType == ModelFunctionType::BlockEditorModel){ + auto it = m_blockEntities.find(id); + return (it != m_blockEntities.end()) ? it.value() : nullptr; + } + return nullptr; +} + +QList TopologyManager::findEntitiesByName(const QString& name,ModelFunctionType funType) const +{ + QList results; + if(funType == ModelFunctionType::ProjectModel){ + foreach (auto entity, m_entities) { + if (entity->name() == name) { + results.append(entity); + } + } + } + else if(funType == ModelFunctionType::BaseModel){ + foreach (auto entity, m_baseEntities) { + if (entity->name() == name) { + results.append(entity); + } + } + } + else if(funType == ModelFunctionType::EditorModel){ + foreach (auto entity, m_editorEntities) { + if (entity->name() == name) { + results.append(entity); + } + } + } + else if(funType == ModelFunctionType::BlockEditorModel){ + foreach (auto entity, m_blockEntities) { + if (entity->name() == name) { + results.append(entity); + } + } + } + return results; +} + +PowerEntity* TopologyManager::createDiagram(const QString& id,const QString& name,ModelFunctionType funType) +{ + PowerEntity* entity = nullptr; + + if(funType == ModelFunctionType::ProjectModel){ + if(!m_diagrams.contains(id)) + { + entity = new ConfigurationDiagram(id,name); + m_diagrams.insert(entity->id(), entity); + } + } + else if(funType == ModelFunctionType::BaseModel){ + if(!m_baseDiagrams.contains(id)) + { + entity = new ConfigurationDiagram(id,name); + m_baseDiagrams.insert(entity->id(), entity); + } + } + else if(funType == ModelFunctionType::RuntimeModel){ + if(!m_monitorDiagrams.contains(id)) + { + entity = new ConfigurationDiagram(id,name); + m_monitorDiagrams.insert(entity->id(), entity); + } + } + + return entity; +} + +PowerEntity* TopologyManager::findDiagram(const QString& id,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel) + return m_diagrams.value(id, nullptr); // 避免异常的安全查询 + else if(funType == ModelFunctionType::BaseModel) + return m_baseDiagrams.value(id, nullptr); + else if(funType == ModelFunctionType::RuntimeModel) + return m_monitorDiagrams.value(id, nullptr); + return nullptr; +} + +bool TopologyManager::deleteDiagram(const QString& id,ModelFunctionType funType) +{ + if(funType == ModelFunctionType::ProjectModel){ + if (!m_diagrams.contains(id)) return false; + + PowerEntity* entity = m_diagrams[id]; + + + // 步骤2:从父节点移除(防止悬空指针) + if (PowerEntity* parent = entity->parent()) { + parent->removeChild(entity); + } + + // 步骤3:递归删除子实体 + auto children = entity->children(); + for (auto child : children) { + deleteDiagram(child->id()); // 递归删除 + } + + // 步骤4:从哈希表移除并释放内存 + m_diagrams.remove(id); + delete entity; // 触发析构函数 + + return true; + } + else if(funType == ModelFunctionType::BaseModel){ + if (!m_baseDiagrams.contains(id)) return false; + + PowerEntity* entity = m_baseDiagrams[id]; + + // 步骤2:从父节点移除(防止悬空指针) + if (PowerEntity* parent = entity->parent()) { + parent->removeChild(entity); + } + + // 步骤3:递归删除子实体 + auto children = entity->children(); + for (auto child : children) { + deleteDiagram(child->id(),funType); // 递归删除 + } + + // 步骤4:从哈希表移除并释放内存 + m_baseDiagrams.remove(id); + delete entity; // 触发析构函数 + + return true; + } + return false; +} + +PowerTerminal* TopologyManager::createTerminal(const QString& parentEntityId, + TerminalType type, + const QString& name, + const QPointF& relPos, + const QString& uuid, + ModelFunctionType funType, + double dPerX, + double dPerY) { + if(funType == ModelFunctionType::ProjectModel){ + if (!m_entities.contains(parentEntityId)) return nullptr; + + PowerTerminal* term = new PowerTerminal(parentEntityId, type, name, relPos, uuid, dPerX, dPerY); + m_allTerminals[term->id()] = term; + m_terminalsByEntity[parentEntityId].append(term); + + // 关联到父实体 + if (PowerEntity* parent = getEntity(parentEntityId)) { + parent->addTerminal(term); + } + + return term; + } + else if(funType == ModelFunctionType::BaseModel){ + if (!m_baseEntities.contains(parentEntityId)) return nullptr; + + PowerTerminal* term = new PowerTerminal(parentEntityId, type, name, relPos, uuid, dPerX, dPerY); + m_baseAllTerminals[term->id()] = term; + m_baseTerminalsByEntity[parentEntityId].append(term); + + // 关联到父实体 + if (PowerEntity* parent = getEntity(parentEntityId,funType)) { + parent->addTerminal(term); + } + + return term; + } + else if(funType == ModelFunctionType::EditorModel){ + if (!m_editorEntities.contains(parentEntityId)) return nullptr; + + PowerTerminal* term = new PowerTerminal(parentEntityId, type, name, relPos, uuid, dPerX, dPerY); + m_editorAllTerminals[term->id()] = term; + m_editorTerminalsByEntity[parentEntityId].append(term); + + // 关联到父实体 + if (PowerEntity* parent = getEntity(parentEntityId,funType)) { + parent->addTerminal(term); + } + + return term; + } + else if(funType == ModelFunctionType::BlockEditorModel){ + if (!m_blockEntities.contains(parentEntityId)) return nullptr; + + PowerTerminal* term = new PowerTerminal(parentEntityId, type, name, relPos, uuid, dPerX, dPerY); + m_blockAllTerminals[term->id()] = term; + m_blockTerminalsByEntity[parentEntityId].append(term); + + // 关联到父实体 + if (PowerEntity* parent = getEntity(parentEntityId,funType)) { + parent->addTerminal(term); + } + + return term; + } + return nullptr; +} + +bool TopologyManager::deleteTerminal(const QString& terminalId,ModelFunctionType funType) { + if(funType == ModelFunctionType::ProjectModel){ + if (!m_allTerminals.contains(terminalId)) return false; + + PowerTerminal* term = m_allTerminals[terminalId]; + QString parentId = term->parentEntityId(); + + // 从父实体移除 + if (PowerEntity* parent = getEntity(parentId)) { + parent->removeTerminal(terminalId); + } + + // 清理全局存储 + m_terminalsByEntity[parentId].removeAll(term); + m_allTerminals.remove(terminalId); + + return true; + } + else if(funType == ModelFunctionType::BaseModel){ + if (!m_baseAllTerminals.contains(terminalId)) return false; + + PowerTerminal* term = m_baseAllTerminals[terminalId]; + QString parentId = term->parentEntityId(); + + // 从父实体移除 + if (PowerEntity* parent = getEntity(parentId,funType)) { + parent->removeTerminal(terminalId); + } + + // 清理全局存储 + m_baseTerminalsByEntity[parentId].removeAll(term); + m_baseAllTerminals.remove(terminalId); + + return true; + } + else if(funType == ModelFunctionType::EditorModel){ + if (!m_editorAllTerminals.contains(terminalId)) return false; + + PowerTerminal* term = m_editorAllTerminals[terminalId]; + QString parentId = term->parentEntityId(); + + // 从父实体移除 + if (PowerEntity* parent = getEntity(parentId,funType)) { + parent->removeTerminal(terminalId); + } + + // 清理全局存储 + m_editorTerminalsByEntity[parentId].removeAll(term); + m_editorAllTerminals.remove(terminalId); + + return true; + } + else if(funType == ModelFunctionType::BlockEditorModel){ + if (!m_blockAllTerminals.contains(terminalId)) return false; + + PowerTerminal* term = m_blockAllTerminals[terminalId]; + QString parentId = term->parentEntityId(); + + // 从父实体移除 + if (PowerEntity* parent = getEntity(parentId,funType)) { + parent->removeTerminal(terminalId); + } + + // 清理全局存储 + m_blockTerminalsByEntity[parentId].removeAll(term); + m_blockAllTerminals.remove(terminalId); + + return true; + } + return false; +} + +PowerTerminal* TopologyManager::getTerminal(const QString& terminalId,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel){ + auto it = m_allTerminals.find(terminalId); + return (it != m_allTerminals.end()) ? it.value() : nullptr; + } + else if(funType == ModelFunctionType::BaseModel){ + auto it = m_baseAllTerminals.find(terminalId); + return (it != m_baseAllTerminals.end()) ? it.value() : nullptr; + } + else if(funType == ModelFunctionType::EditorModel){ + auto it = m_editorAllTerminals.find(terminalId); + return (it != m_editorAllTerminals.end()) ? it.value() : nullptr; + } + else if(funType == ModelFunctionType::BlockEditorModel){ + auto it = m_blockAllTerminals.find(terminalId); + return (it != m_blockAllTerminals.end()) ? it.value() : nullptr; + } + return nullptr; +} + +QList TopologyManager::getTerminalsForEntity(const QString& entityId,ModelFunctionType funType) const { + if(funType == ModelFunctionType::ProjectModel) + return m_terminalsByEntity.value(entityId); + else if(funType == ModelFunctionType::BaseModel) + return m_baseTerminalsByEntity.value(entityId); + else if(funType == ModelFunctionType::EditorModel) + return m_editorTerminalsByEntity.value(entityId); + else if(funType == ModelFunctionType::BlockEditorModel) + return m_blockTerminalsByEntity.value(entityId); + return QList(); +} + +PowerEntity* TopologyManager::getEntityByTerminal(const QString& terminalId,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel){ + QHash>::ConstIterator iter; + for(iter = m_terminalsByEntity.begin();iter != m_terminalsByEntity.end();++iter) + { + for(auto &terminal:iter.value()) + { + if(terminal->id() == terminalId) + { + return m_entities[iter.key()]; + } + } + } + } + else if(funType == ModelFunctionType::BaseModel){ + QHash>::ConstIterator iter; + for(iter = m_baseTerminalsByEntity.begin();iter != m_baseTerminalsByEntity.end();++iter) + { + for(auto &terminal:iter.value()) + { + if(terminal->id() == terminalId) + { + return m_baseEntities[iter.key()]; + } + } + } + } + else if(funType == ModelFunctionType::EditorModel){ + QHash>::ConstIterator iter; + for(iter = m_editorTerminalsByEntity.begin();iter != m_editorTerminalsByEntity.end();++iter) + { + for(auto &terminal:iter.value()) + { + if(terminal->id() == terminalId) + { + return m_editorEntities[iter.key()]; + } + } + } + } + else if(funType == ModelFunctionType::BlockEditorModel){ + QHash>::ConstIterator iter; + for(iter = m_blockTerminalsByEntity.begin();iter != m_blockTerminalsByEntity.end();++iter) + { + for(auto &terminal:iter.value()) + { + if(terminal->id() == terminalId) + { + return m_blockEntities[iter.key()]; + } + } + } + } + + return nullptr; +} + +PowerConnection* TopologyManager::getConnectionContainsTerminal(const QString& terminalId,ModelFunctionType funType) const +{ + if(funType == ModelFunctionType::ProjectModel){ + for(auto &con:m_connections) + { + if(con->fromTerminalId() == terminalId || con->toTerminalId() == terminalId) + { + return con; + } + } + } + else if(funType == ModelFunctionType::BaseModel){ + for(auto &con:m_baseConnections) + { + if(con->fromTerminalId() == terminalId || con->toTerminalId() == terminalId) + { + return con; + } + } + } + else if(funType == ModelFunctionType::EditorModel){ + for(auto &con:m_editorConnections) + { + if(con->fromTerminalId() == terminalId || con->toTerminalId() == terminalId) + { + return con; + } + } + } + else if(funType == ModelFunctionType::BlockEditorModel){ + for(auto &con:m_blockConnections) + { + if(con->fromTerminalId() == terminalId || con->toTerminalId() == terminalId) + { + return con; + } + } + } + return nullptr; +} diff --git a/diagramCavas/source/util/baseSelector.cpp b/diagramCavas/source/util/baseSelector.cpp new file mode 100644 index 0000000..73a7894 --- /dev/null +++ b/diagramCavas/source/util/baseSelector.cpp @@ -0,0 +1,675 @@ +#include "util/baseSelector.h" +#include +#include +#include +#include +#include +#include "graphicsItem/graphicsBaseItem.h" +#include "topologyManager.h" +#include "graphicsItem/itemPort.h" +#include "graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h" +#include "graphicsItem/electricBayItem.h" +#include "topologyManager.h" + +QPointF BaseSelector::ms_ptMouseDown(0.0,0.0); +QPointF BaseSelector::ms_ptMouseLast(0.0,0.0); +double BaseSelector::ms_dAngleMouseDownToItem = 0.0; +int BaseSelector::ms_nDragHandle = 0; + +BaseSelector::BaseSelector(FixedPortsModel* model,QObject *parent) + : _model(model) + ,QObject(parent) +{ + m_type = ST_base; + m_opMode = OM_none; + m_bHoverOnHandel = false; +} +BaseSelector::~BaseSelector() +{ + +} + +void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if(sceneMode == DM_edit) + { + if (event->button() != Qt::LeftButton) + return; + + _model->activateModel(); //激活当前窗口 + ms_ptMouseDown = event->scenePos(); + ms_ptMouseLast = event->scenePos(); + + if(!m_bHoverOnHandel) + scene->callParentEvent(event); //此处是通过触发QGraphicsScene的事件函数来取消所有被选中item的选中状态 + + m_opMode = OM_none; + + QList items = scene->selectedItems(); + if (items.count() == 1) //只有一个选中 + { + GraphicsProjectModelItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + GraphicsItemType tpe = item->getItemType(); + if(tpe == GIT_link) //对象是连接线 + { + m_opMode = OM_linkMove; + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_linkMoving); + } + else + { + //需要增加当前是否点击在控制点的判断函数 + ms_nDragHandle = item->collidesWithHandle(event->scenePos()); + if(ms_nDragHandle != H_none && ms_nDragHandle <= H_left) //在缩放控制点上 + { + m_opMode = OM_scale; + emit setWorkingSelector(ST_scaling); + } + else if(ms_nDragHandle >= H_rotate_leftTop && ms_nDragHandle <= H_rotate_leftBottom) //在旋转控制点上 + { + m_opMode = OM_rotate; + //计算夹角 + QPointF originPoint = item->mapToScene(item->boundingRect().center()); + double dLengthY = ms_ptMouseLast.y() - originPoint.y(); + double dLengthX = ms_ptMouseLast.x() - originPoint.x(); + ms_dAngleMouseDownToItem = atan2(dLengthY, dLengthX) * 180 / M_PI; + // if(atan2(dLengthY, dLengthX) < 0) + // ms_dAngleMouseDownToItem += 360.0; + //创建副本 + item->createOperationCopy(); + emit setWorkingSelector(ST_rotation); + } + else if(ms_nDragHandle > H_rotate_leftBottom && ms_nDragHandle < H_textCaption) //编辑控制点上 + { + m_opMode = OM_edit; + setCursor(scene, Qt::ClosedHandCursor); + emit setWorkingSelector(ST_editing); + } + else if(ms_nDragHandle >= H_connect ) //连接控制点 + { + m_opMode = OM_connect; + //setCursor(scene, Qt::ClosedHandCursor); + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_connecting); + } + else + { + m_opMode = OM_move; + setCursor(scene, Qt::ClosedHandCursor); + emit setWorkingSelector(ST_moving); + } + } + } + } + else if (items.count() > 1) //选中多个 + { + m_opMode = OM_move; + emit setWorkingSelector(ST_moving); + setCursor(scene, Qt::ClosedHandCursor); + } + else if(items.count() == 0) //单独移动子类 + { + QList items = scene->items(ms_ptMouseLast); + if (items.count() == 1) + { + ItemControlHandle* pHandle = qgraphicsitem_cast(items.first()); + if(pHandle) + { + //GraphicsProjectModelItem* item = pHandle->getParentPtr(); + ms_nDragHandle = pHandle->getTag(); + //ms_nDragHandle = item->collidesWithHandle(event->scenePos()); + if(ms_nDragHandle >= H_textCaption && ms_nDragHandle < H_connect) //移动文本 + { + m_opMode = OM_subMove; + setCursor(scene, Qt::ClosedHandCursor); + emit setWorkingSelector(ST_subMoving); + } + } + } + else + { + QPointF pos = event->scenePos(); + // 遍历所有项检测点击 + for (QGraphicsItem* item : scene->items()) { + ElectricBayItem* pItem = dynamic_cast(item); + if (pItem) { + if(pItem->containsPoint(pos)){ + // 处理命中 + pItem->setSelected(true); + return; + } + } + } + } + } + + if(m_opMode == OM_move) //可以多个选中同时移动 + { + for(int n = 0; n < items.size(); n++) + { + //创建副本 + GraphicsProjectModelItem* item = qgraphicsitem_cast(items.at(n)); + //GraphicsBaseModelItem* item = qgraphicsitem_cast(items.at(n)); + GraphicsItemType tpe = item->getItemType(); + if(tpe == GIT_link) + continue; + item->createOperationCopy(); + //item->update(); + } + } + else if(m_opMode == OM_none) + { + QGraphicsView *view = scene->getView(); + if(view) + view->setDragMode(QGraphicsView::RubberBandDrag); + } + } + else if(sceneMode == DM_baseModel) + { + _model->activateModel(); + scene->callParentEvent(event); + QList items = scene->selectedItems(); + if (items.count() == 1) + { + GraphicsBaseModelItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + item->setSelected(true); + item->updateHandles(); + } + } + else{ + QPointF pos = event->scenePos(); + // 遍历所有项检测点击 + for (QGraphicsItem* item : scene->items()) { + ElectricBayItem* pItem = dynamic_cast(item); + if (pItem) { + if(pItem->containsPoint(pos)){ + // 处理命中 + pItem->setSelected(true); + return; + } + } + } + } + } + else if(sceneMode == DM_run){ + if (event->button() != Qt::LeftButton) + return; + + _model->activateModel(); //激活当前窗口 + ms_ptMouseDown = event->scenePos(); + ms_ptMouseLast = event->scenePos(); + + if(!m_bHoverOnHandel) + scene->callParentEvent(event); //此处是通过触发QGraphicsScene的事件函数来取消所有被选中item的选中状态 + + m_opMode = OM_none; + + QList items = scene->selectedItems(); + if (items.count() == 1) //只有一个选中 + { + GraphicsProjectModelItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + GraphicsItemType tpe = item->getItemType(); + if(tpe == GIT_link) //对象是连接线 + { + m_opMode = OM_linkMove; + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_linkMoving); + } + else + { + m_opMode = OM_move; + setCursor(scene, Qt::ClosedHandCursor); + emit setWorkingSelector(ST_moving); + } + + auto pPro = item->getProperty(); + if(pPro) + _model->monitorItemSelected(pPro->uuid()); + } + } + else if (items.count() > 1) //选中多个 + { + m_opMode = OM_move; + emit setWorkingSelector(ST_moving); + setCursor(scene, Qt::ClosedHandCursor); + } + else{ + QPointF pos = event->scenePos(); + // 遍历所有项检测点击 + for (QGraphicsItem* item : scene->items()) { + ElectricBayItem* pItem = dynamic_cast(item); + if (pItem) { + if(pItem->containsPoint(pos)){ + // 处理命中 + pItem->setSelected(true); + return; + } + } + } + } + + if(m_opMode == OM_move) //可以多个选中同时移动 + { + for(int n = 0; n < items.size(); n++) + { + //创建副本 + GraphicsProjectModelItem* item = qgraphicsitem_cast(items.at(n)); + GraphicsItemType tpe = item->getItemType(); + if(tpe == GIT_link) + continue; + item->createOperationCopy(); + } + } + else if(m_opMode == OM_none) + { + QGraphicsView *view = scene->getView(); + if(view) + view->setDragMode(QGraphicsView::RubberBandDrag); + } + } +} + +void BaseSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if(sceneMode == DM_edit) + { + ms_ptMouseLast = event->scenePos(); + + QList items = scene->selectedItems(); + + if (items.count() == 1) + { + GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + if(item->getItemType() == GIT_bay) + return; + if(ms_nDragHandle == H_none) + { + //设置光标样式 + int nHandle = item->collidesWithHandle(event->scenePos()); + if(nHandle == H_none) + { + setCursor(scene, Qt::ArrowCursor); + m_bHoverOnHandel = false; + } + else if(nHandle >= H_edit) + { + setCursor(scene, Qt::OpenHandCursor); + m_bHoverOnHandel = true; + } + else + { + //划分为四组区间范围,分别水平组、垂直组、一三象限倾斜组、二四象限倾斜组,每组由两个对称区间构成 + double dRotation = item->rotation(); + dRotation += item->getSyncRotationDataFromParent(); + //让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断 + if (dRotation > 180) + dRotation -= 360; + if (dRotation < -180) + dRotation += 360; + //qDebug() << "selfRotation:" << item->rotation() << " syncRotation:" << item->getSyncRotationDataFromParent() << " fininalRotatio:" << dRotation; + double dTileAngle = 15.0; + + switch (nHandle) + { + case H_leftTop: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeBDiagCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeHorCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeVerCursor); + else //水平区间 + setCursor(scene, Qt::SizeFDiagCursor); + break; + } + case H_top: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeHorCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeFDiagCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeBDiagCursor); + else + setCursor(scene, Qt::SizeVerCursor); + break; + } + case H_rightTop: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeFDiagCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeVerCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeHorCursor); + else + setCursor(scene, Qt::SizeBDiagCursor); + break; + } + case H_right: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeVerCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeBDiagCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeFDiagCursor); + else + setCursor(scene, Qt::SizeHorCursor); + break; + } + case H_rightBottom: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeBDiagCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeHorCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeVerCursor); + else //水平区间 + setCursor(scene, Qt::SizeFDiagCursor); + break; + } + case H_bottom: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeHorCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeFDiagCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeBDiagCursor); + else + setCursor(scene, Qt::SizeVerCursor); + break; + } + case H_leftBottom: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeFDiagCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeVerCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeHorCursor); + else //水平区间 + setCursor(scene, Qt::SizeBDiagCursor); + break; + } + case H_left: + { + if((dRotation > -145+dTileAngle && dRotation < -45-dTileAngle) || (dRotation > 45+dTileAngle && dRotation < 145-dTileAngle)) //垂直区间 + setCursor(scene, Qt::SizeVerCursor); + else if((dRotation >= -45-dTileAngle && dRotation <= -45+dTileAngle) || (dRotation >= 145-dTileAngle && dRotation <= 145+dTileAngle)) //一三象限倾斜 + setCursor(scene, Qt::SizeBDiagCursor); + else if((dRotation >= -145-dTileAngle && dRotation <= -145+dTileAngle) || (dRotation >= 45-dTileAngle && dRotation <= 45+dTileAngle)) //二四象限倾斜 + setCursor(scene, Qt::SizeFDiagCursor); + else + setCursor(scene, Qt::SizeHorCursor); + break; + } + case H_rotate_leftTop: + { + int nSize = 24; + QCursor rotateCursor = QCursor(QPixmap(":/images/icon_rotate.png")/*.scaled(nSize, nSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)*/); + setCursor(scene, rotateCursor); + break; + } + case H_rotate_rightTop: + { + int nSize = 24; + QCursor rotateCursor = QCursor(QPixmap(":/images/icon_rotate.png")/*.scaled(nSize, nSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)*/); + setCursor(scene, rotateCursor); + break; + } + case H_rotate_rightBottom: + { + int nSize = 24; + QCursor rotateCursor = QCursor(QPixmap(":/images/icon_rotate.png")/*.scaled(nSize, nSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)*/); + setCursor(scene, rotateCursor); + break; + } + case H_rotate_leftBottom: + { + int nSize = 24; + QCursor rotateCursor = QCursor(QPixmap(":/images/icon_rotate.png")/*.scaled(nSize, nSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)*/); + setCursor(scene, rotateCursor); + break; + } + default: + break; + } + + m_bHoverOnHandel = true; + } + } + } + } + } + else if(sceneMode == DM_baseModel) + { + int a = 1; + } +} + +void BaseSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if(sceneMode == DM_edit) + { + setCursor(scene, Qt::ArrowCursor); + + if(m_opMode == OM_none) + { + QGraphicsView *view = scene->getView(); + if(view) + view->setDragMode(QGraphicsView::NoDrag); + } + + QList lst; //发送已选中的元件 + QList items = scene->selectedItems(); + for(auto& pItem:items){ + GraphicsProjectModelItem* item = dynamic_cast(pItem); + if(item){ + HierarchyItem info; + auto pPro = item->getProperty(); + if(pPro){ + info.item.nCategory = 0; + info.item.nEquipType = pPro->type(); + info.item.sName = pPro->name(); + info.item.uid = pPro->uuid(); + lst.append(info); + } + } + else{ + GraphicsNonStandardItem* bay = dynamic_cast(pItem); + if(bay){ + HierarchyItem info; + auto pBayPro = bay->getProperty(); + if(pBayPro){ + info.item.nCategory = 1; + info.item.nEquipType = 0; + info.item.sName = pBayPro->name(); + info.item.uid = pBayPro->uuid(); + lst.append(info); + } + } + } + } + emit _model->itemSelected(lst); + + m_opMode = OM_none; + m_bHoverOnHandel = false; + ms_nDragHandle = H_none; + scene->callParentEvent(event); + } + else if(sceneMode == DM_baseModel) + { + m_opMode = OM_none; + scene->callParentEvent(event); + } + else if(sceneMode == DM_run) + { + setCursor(scene, Qt::ArrowCursor); + + if(m_opMode == OM_none) + { + QGraphicsView *view = scene->getView(); + if(view) + view->setDragMode(QGraphicsView::NoDrag); + } + + m_opMode = OM_none; + m_bHoverOnHandel = false; + ms_nDragHandle = H_none; + scene->callParentEvent(event); + } +} + +void BaseSelector::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if(sceneMode == DM_run) + { + QList items = scene->selectedItems(); + + if(items.count() == 1) + { + GraphicsFunctionModelItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + if(item->getItemType() == GIT_bay) //间隔暂时返回 + return; + ModelProperty* pro = item->getProperty(); + if(pro){ + QString modelName = pro->modelName(); + QUuid uuid = item->itemId(); + _model->showModelDlg(modelName,uuid,item); + } + } + } + } +} + +void BaseSelector::dragEnterEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode sceneMode) +{ + if (event->mimeData()->hasText()) { + event->acceptProposedAction(); + } +} + +void BaseSelector::dragMoveEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode sceneMode) +{ + if (event->mimeData()->hasText()) { + event->acceptProposedAction(); + } +} + +void BaseSelector::dropEvent(QGraphicsSceneDragDropEvent *event, DesignerScene*,DiagramMode sceneMode) +{ + if(sceneMode == DM_edit) + { + if (event->mimeData()->hasText()) { + QString text = event->mimeData()->text(); + QString uuid = QString::fromLocal8Bit(event->mimeData()->data("application/id")); + // 根据拖拽的数据创建相应的图形项并添加到场景中 + 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; + } + else{ + _model->addNodeItem(QUuid(uuid),event->scenePos()); + } + } + } + else if(sceneMode == DM_baseModel) + { + int a = 1; + } + +} + +void BaseSelector::contextMenuEvent(QGraphicsSceneContextMenuEvent *event,DesignerScene* scene,DiagramMode sceneMode) +{ + if(sceneMode == DM_run){ + QList listItem = scene->selectedItems(); + if(listItem.isEmpty()) + return; + QMenu menu; + QAction *detailAttrAction = menu.addAction(QString::fromWCharArray(L"详细属性")); + //connect(removeAction,&QAction::triggered,this,&DesignerScene::onDeleteClicked); + connect(detailAttrAction,&QAction::triggered,this,[&,scene](){ + QList listItem = scene->selectedItems(); + if(listItem.isEmpty()) + return; + else if(listItem.count() == 1) + { + GraphicsProjectModelItem* item = qgraphicsitem_cast(listItem.first()); + if(item) + { + auto pPro = item->getProperty(); + if(pPro){ + _model->monitorItemDetailAttr(pPro->uuid()); + } + } + } + }); + menu.exec(QCursor::pos()); + } +} + +void BaseSelector::setCursor(DesignerScene *scene, const QCursor &cursor) +{ + QGraphicsView *view = scene->getView(); + if (view) + view->setCursor(cursor); +} + +void BaseSelector::updateConnectLineByTopology(QList lst) +{ + for(auto iter:lst) //更新连接线 + { + auto item = dynamic_cast(iter); + if(item) + { + if(item->getItemType() != GIT_link) //获取非电缆对象 + { + QUuid nId = item->itemId(); + auto lstConnect = TopologyManager::instance().getConnectionsFor(nId.toString()); + for(auto &pConnect:lstConnect) + { + if(pConnect) + { + QString fromTerminalId = pConnect->fromTerminalId(); //connect自身包含头尾巴顺序 + QString toTerminalId = pConnect->toTerminalId(); + + QPointF fromPos = _model->getTerminalPos(fromTerminalId); + QPointF toPos = _model->getTerminalPos(toTerminalId); + + ElectricFunctionModelConnectLineItem* connectItem = _model->getLineItemById(fromTerminalId); + if(connectItem) + { + connectItem->setStartPoint(fromPos); + connectItem->setEndPoint(toPos); + //qDebug()<calculatePath(); + } + } + } + } + } + } +} + diff --git a/diagramCavas/source/util/connectingSelector.cpp b/diagramCavas/source/util/connectingSelector.cpp new file mode 100644 index 0000000..e29820b --- /dev/null +++ b/diagramCavas/source/util/connectingSelector.cpp @@ -0,0 +1,246 @@ +#include "util/connectingSelector.h" +#include "graphicsItem/itemControlHandle.h" +#include "graphicsItem/itemPort.h" +#include "graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h" +#include "topologyManager.h" +#include +#include +#include "graphicsItem/functionModelItem/graphicsFunctionModelItem.h" +#include "baseProperty.h" +#include + +ConnectingSelector::ConnectingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) + ,m_pConnectingItem(nullptr) + ,m_pTouchedItem(nullptr) +{ + m_type = ST_connecting; + m_bReadyConnect = false; +} +ConnectingSelector::~ConnectingSelector() +{ + +} + +void ConnectingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + +} + +bool ConnectingSelector::targetCouldConnect(GraphicsFunctionModelItem* p,QPointF pos) +{ + GraphicsItemType iType = p->getItemType(); + if(iType == GIT_bus) + { + setTargetHighLight(true); + return true; + } + else + { + int nHandle = p->collidesWithHandle(pos); + if(nHandle >= H_connect ) //连接控制点 + { + setTargetHighLight(false); + p->setLastPort(nHandle); + ItemPort* pt = p->getPortPtr(nHandle); + if(pt) + { + HandleType targetDir = pt->getType(); + bool bCon = pt->connected(); + + if(m_pConnectingItem) + { + ItemPort* ptSrc = m_pConnectingItem->getPortPtr(ms_nDragHandle); + HandleType sourceDir = ptSrc->getType(); + + if((targetDir != sourceDir) && !bCon) + { + setTargetHighLight(true); + return true; + } + } + } + } + else //超出了触碰范围 + { + setTargetHighLight(false); + p->setLastPort(-1); + } + } + return false; +} + +void ConnectingSelector::setTargetHighLight(bool val) +{ + if(m_pTouchedItem) + { + GraphicsItemType iType = m_pTouchedItem->getItemType(); + if(iType == GIT_bus) + { + m_pTouchedItem->setTouched(val); + } + else + { + int n = m_pTouchedItem->getLastPort(); + if(n != -1) + m_pTouchedItem->setHandleVisible(n,val); + } + } +} + +void ConnectingSelector::createConnectLline(GraphicsFunctionModelItem* connectingItem,GraphicsFunctionModelItem* touchedItem,DesignerScene* scene) +{ + if (!_model->isItemValid(connectingItem) || !_model->isItemValid(touchedItem)) { + QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"请先完成设备命名")); + return; + } + QUuid uid = QUuid::createUuid(); + ElectricFunctionModelConnectLineItem* pItem = new ElectricFunctionModelConnectLineItem(); + pItem->setItemId(uid); + pItem->setItemType(GIT_link); + scene->addItem(pItem); + + ItemPort* ptSrc = connectingItem->getPortPtr(ms_nDragHandle); + + pItem->setStartPoint(ptSrc->scenePos()); + ptSrc->setConnect(pItem); + QString srcPortId = ptSrc->getId(); //port自身id + + ItemPort* ptDest = nullptr; + if(touchedItem->getItemType() == GIT_bus) //母线动态创建port + { + int nId = touchedItem->addPort(p_movable,touchedItem->mapFromScene(ms_ptMouseLast)); + if(nId != -1) + { + ptDest = touchedItem->getPortPtr(nId); + pItem->setEndPoint(ptDest->scenePos()); + } + + _model->createTopoTerminalsByItem(touchedItem); //创建port时创建对应的terminal + } + else + { + int nLastPort = touchedItem->getLastPort(); + ptDest = touchedItem->getPortPtr(nLastPort); + pItem->setEndPoint(ptDest->scenePos()); + } + + if(ptDest != nullptr) + { + QString destPortId = ptDest->getId(); //port自身id + + if(TopologyManager::instance().validateConnection(srcPortId,destPortId)){ + PowerConnection* pCon = TopologyManager::instance().createConnection(uid.toString(),srcPortId,destPortId,connectingItem->itemId().toString(),touchedItem->itemId().toString()); //创建拓扑连接(逻辑) + if(pCon) + pCon->setState(DataState::Changed); + } + + pItem->getProperty()->setConnection(Connection(connectingItem->itemId(),QUuid(srcPortId),ptSrc->getType(),ptSrc->portPos(),touchedItem->itemId(),QUuid(destPortId),ptDest->getType(),ptDest->portPos())); + _model->addNodeItem(pItem->itemId(),pItem); + auto srcParent = ptSrc->getParentPtr(); + auto destParent = ptDest->getParentPtr(); + + srcParent->setItemChanged(true); + destParent->setItemChanged(true); + } +} + +void ConnectingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + if(m_pConnectingItem == nullptr) + { + QList items = scene->selectedItems(); + if (items.count() == 1) + { + GraphicsFunctionModelItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + if(ms_nDragHandle >= H_connect) //操作的是port端点 + { + ItemPort* pt = item->getPortPtr(ms_nDragHandle); + if(pt) + { + if(pt->connected()) //只判断未连接的port + return; + item->setState(S_prepareConnect); + QPointF start = item->mapFromScene(ms_ptMouseDown); + QPointF end = item->mapFromScene(ms_ptMouseLast); + item->setBeginConnectPos(start); + item->setEndConnectPos(end); + m_pConnectingItem = item; + } + } + } + } + } + else + { + if(m_pConnectingItem) + { + QPointF end = m_pConnectingItem->mapFromScene(ms_ptMouseLast); + m_pConnectingItem->setEndConnectPos(end); + + QList items = scene->items(ms_ptMouseLast); + if (items.count() == 1) + { + GraphicsFunctionModelItem* item = dynamic_cast(items.first()); + if(item) + { + QUuid n1 = item->itemId(); + QUuid n2 = m_pConnectingItem->itemId(); + if(n1 != n2) //判断两个对象是否相同 + { + m_pTouchedItem = item; + m_bReadyConnect = targetCouldConnect(item,ms_ptMouseLast); + } + } + } + if(items.isEmpty()) + { + //todo取消选中 + m_bReadyConnect = false; + setTargetHighLight(false); + if(m_pTouchedItem) + { + m_pTouchedItem->setLastPort(-1); + m_pTouchedItem = nullptr; + } + } + } + } + +} + +void ConnectingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if(m_bReadyConnect) + { + if(m_pConnectingItem && m_pTouchedItem) + { + createConnectLline(m_pConnectingItem,m_pTouchedItem,scene); + auto obj = dynamic_cast(m_pConnectingItem); + QList lst; + lst.append(obj); + updateConnectLineByTopology(lst); + } + } + + if(m_pConnectingItem) + { + m_pConnectingItem->setState(S_normal); + } + if(m_pTouchedItem) + { + setTargetHighLight(false); + m_pTouchedItem->setLastPort(-1); + m_pTouchedItem = nullptr; + } + m_pConnectingItem = nullptr; + ms_nDragHandle = H_none; + setCursor(scene, Qt::ArrowCursor); + scene->callParentEvent(event); + emit setWorkingSelector(ST_base); +} + diff --git a/diagramCavas/source/util/creatingSelector.cpp b/diagramCavas/source/util/creatingSelector.cpp new file mode 100644 index 0000000..c2e358c --- /dev/null +++ b/diagramCavas/source/util/creatingSelector.cpp @@ -0,0 +1,188 @@ +#include "util/creatingSelector.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemCB.h" +#include "graphicsItem/graphicsPolygonItem.h" +#include "graphicsItem/functionModelItem/electricFunctionModelSvgItemBus.h" +#include +#include +#include + + +CreatingSelector::CreatingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) +{ + m_type = ST_cerating; + m_creatingMethod = CM_drag; + m_pCreatingItem = nullptr; + m_scalBasePoint = QPointF(); +} +CreatingSelector::~CreatingSelector() +{ + +} + +void CreatingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if (event->button() != Qt::LeftButton) + return; + + ms_ptMouseDown = event->scenePos(); + ms_ptMouseLast = event->scenePos(); + + QUuid uuid = QUuid::createUuid(); + if(m_pCreatingItem == nullptr) + { + scene->clearSelection(); + + switch (m_creatingItemInfo.modelType) + { + /*case GIT_rect: + { + m_creatingMethod = CM_drag; + m_pCreatingItem = new GraphicsRectItem(QRect(1, 1, 1, 1)); + } + break; + case GIT_roundRect: + { + m_creatingMethod = CM_drag; + m_pCreatingItem = new GraphicsRectItem(QRect(1, 1, 1, 1), true); + } + break; + case GIT_polygon: + { + m_creatingMethod = CM_click; + m_pCreatingItem = new GraphicPolygonItem(); + } + break;*/ + case GIT_bus: + { + m_creatingMethod = CM_click; + m_pCreatingItem = new ElectricFunctionModelSvgItemBus(QRect(-100, -3, 200, 6)); + m_pCreatingItem->setItemId(uuid); + m_pCreatingItem->setItemType(GIT_bus); + m_pCreatingItem->editShape(ms_nDragHandle, ms_ptMouseLast); + emit setWorkingSelector(ST_base); + } + break; + case GIT_itemRect: + { + m_creatingMethod = CM_click; + m_pCreatingItem = new ElectricFunctionModelSvgItemCB(QRect(-15, -15, 30, 30)); + m_pCreatingItem->setItemId(uuid); + m_pCreatingItem->setItemType(GIT_itemRect); + m_pCreatingItem->editShape(ms_nDragHandle, ms_ptMouseLast); + emit setWorkingSelector(ST_base); + } + break; + default: + break; + } + + if(m_pCreatingItem) + { + m_pCreatingItem->setPos(event->scenePos()); + m_pCreatingItem->setSelected(true); + scene->addItem(m_pCreatingItem); + m_pCreatingItem->setItemChanged(true); + + if(m_creatingMethod == CM_drag) + { + ms_ptMouseDown += QPoint(2, 2); + ms_nDragHandle = H_rightBottom; + } + else if(m_creatingMethod == CM_click) + m_pCreatingItem->addPoint(ms_ptMouseDown); + } + else{ + qDebug()<<"item type error"; + return; + } + } + + if(m_pCreatingItem && m_creatingMethod == CM_click) + { + //创建时添加了第一个点,紧接着再次添加第二点,然后从第二个点开始进行移动绘制 + m_pCreatingItem->addPoint(ms_ptMouseDown); + ms_nDragHandle = m_pCreatingItem->handleCount(); + //m_graphicsItem[sceneName()].insert(QString::number(m_pCreatingItem->itemId()),m_pCreatingItem); //插入数据到总表 + m_pCreatingItem->setModelName(m_creatingItemInfo.modelName); + _model->addNodeItem(m_pCreatingItem->itemId(),m_pCreatingItem); + m_pCreatingItem = nullptr; //先舍弃多次点击创建对象241124 by + } +} + +void CreatingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + setCursor(scene, Qt::CrossCursor); + ms_ptMouseLast = event->scenePos(); + + if (m_pCreatingItem && m_creatingMethod == CM_drag) + { + + if(m_scalBasePoint.isNull()) //基准点不能采用临时变量,因为handle的坐标也在不断变化,计算会出现问题 + { + m_scalBasePoint = m_pCreatingItem->getSymmetricPointPos(ms_nDragHandle); + if(m_scalBasePoint.x() == 0) + m_scalBasePoint.setX(1); + if(m_scalBasePoint.y() == 0) + m_scalBasePoint.setY(1); + } + + QPointF scaleBasePoint = m_pCreatingItem->boundingRect().topLeft(); + + //计算缩放倍数 + QPointF iniDelta = m_pCreatingItem->mapFromScene(ms_ptMouseDown) - scaleBasePoint; + QPointF lastDelta = m_pCreatingItem->mapFromScene(ms_ptMouseLast) - scaleBasePoint; + double sx = lastDelta.x() / iniDelta.x(); + double sy = lastDelta.y() / iniDelta.y(); + + m_pCreatingItem->resize(ms_nDragHandle, sx, sy, scaleBasePoint); + } + else if (m_pCreatingItem && m_creatingMethod == CM_click) + { + if(ms_nDragHandle > H_left) + { + m_pCreatingItem->editShape(ms_nDragHandle, ms_ptMouseLast); + } + } +} + +void CreatingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if (m_pCreatingItem && m_creatingMethod == CM_drag) + { + if (event->scenePos() == (ms_ptMouseDown - QPoint(2, 2))) //最小拖动范围 + { + m_pCreatingItem->setSelected(false); + scene->removeItem(m_pCreatingItem); + delete m_pCreatingItem; + } + else if (ms_ptMouseLast != ms_ptMouseDown) + { + m_pCreatingItem->updateCoordinate(); + emit scene->signalAddItem(m_pCreatingItem); + } + + ms_nDragHandle = H_none; + m_pCreatingItem = nullptr; + m_scalBasePoint = QPointF(); + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_base); + } + else if (m_pCreatingItem && m_creatingMethod == CM_click && event->button() == Qt::RightButton) //右键结束绘制 + { + if(m_pCreatingItem->endDrawing()) + m_pCreatingItem->updateCoordinate(); + else + { + m_pCreatingItem->setSelected(false); + scene->removeItem(m_pCreatingItem); + delete m_pCreatingItem; + } + + ms_nDragHandle = H_none; + m_pCreatingItem = nullptr; + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_base); + } +} + diff --git a/diagramCavas/source/util/editingSelector.cpp b/diagramCavas/source/util/editingSelector.cpp new file mode 100644 index 0000000..a22fe87 --- /dev/null +++ b/diagramCavas/source/util/editingSelector.cpp @@ -0,0 +1,54 @@ +#include "util/editingSelector.h" +#include +#include + + +EditingSelector::EditingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) +{ + m_type = ST_editing; +} +EditingSelector::~EditingSelector() +{ + +} + +void EditingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + +} + +void EditingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + QList items = scene->selectedItems(); + if (items.count() == 1) + { + GraphicsProjectModelItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + if(ms_nDragHandle > H_left) + { + item->editShape(ms_nDragHandle, ms_ptMouseLast); + } + } + } +} + +void EditingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + QList items = scene->selectedItems(); + if (items.count() == 1) + { + GraphicsProjectModelItem* item = qgraphicsitem_cast(items.first()); + if(item && ms_ptMouseLast != ms_ptMouseDown) + { + item->updateCoordinate(); + } + } + + ms_nDragHandle = H_none; + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_base); +} + diff --git a/diagramCavas/source/util/linkMovingSelector.cpp b/diagramCavas/source/util/linkMovingSelector.cpp new file mode 100644 index 0000000..93ccd63 --- /dev/null +++ b/diagramCavas/source/util/linkMovingSelector.cpp @@ -0,0 +1,53 @@ +#include "util/linkMovingSelector.h" +#include +#include +#include "graphicsItem/functionModelItem/electricFunctionModelConnectLineItem.h" + +LinkMovingSelector::LinkMovingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) + ,m_pMovingLine(nullptr) +{ + m_type = ST_linkMoving; +} +LinkMovingSelector::~LinkMovingSelector() +{ + +} + +void LinkMovingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); +} + +void LinkMovingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + if (m_pMovingLine == nullptr) { + QList items = scene->selectedItems(); + if (items.count() == 1) { + ElectricFunctionModelConnectLineItem* item = qgraphicsitem_cast(items.first()); + if (item) { + m_pMovingLine = item; + m_pMovingLine->startDrag(event->scenePos()); + qDebug() << "Started drag on line at:" << event->scenePos(); + } + } + } else { + m_pMovingLine->updateDrag(event->scenePos()); + } + +} + +void LinkMovingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + if(m_pMovingLine) + { + m_pMovingLine->endDrag(); + } + m_pMovingLine = nullptr; + ms_nDragHandle = H_none; + setCursor(scene, Qt::ArrowCursor); + scene->callParentEvent(event); + emit setWorkingSelector(ST_base); +} + diff --git a/diagramCavas/source/util/movingSelector.cpp b/diagramCavas/source/util/movingSelector.cpp new file mode 100644 index 0000000..b1dd56c --- /dev/null +++ b/diagramCavas/source/util/movingSelector.cpp @@ -0,0 +1,89 @@ +#include "util/movingSelector.h" +#include "graphicsItem/itemPort.h" +#include +#include +#include +#include "baseProperty.h" + +MovingSelector::MovingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) +{ + m_type = ST_moving; +} +MovingSelector::~MovingSelector() +{ + +} + +void MovingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + + QList items = scene->selectedItems(); + //updateConnectLine(items); + updateConnectLineByTopology(items); +} + +void MovingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + + QList items = scene->selectedItems(); + for(int n = 0; n < items.size(); n++) + { + GraphicsBaseItem* item = qgraphicsitem_cast(items.at(n)); + if(item){ + if(item->getMoveable()){ + item->moveOperationCopy(ms_ptMouseLast - ms_ptMouseDown); + } + } + } +} + +void MovingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + QList lst; //发送已选中的元件 + QList items = scene->selectedItems(); + for(int n = 0; n < items.size(); n++) + { + AbstractShape* item = qgraphicsitem_cast(items.at(n)); + if(item) + item->removeOperationCopy(); + + GraphicsProjectModelItem* p = dynamic_cast(items.at(n)); + if(p){ + auto pPro = p->getProperty(); + HierarchyItem info; + if(pPro){ + info.item.nCategory = 0; + info.item.nEquipType = pPro->type(); + info.item.sName = pPro->name(); + info.item.uid = pPro->uuid(); + lst.append(info); + } + } + else{ + GraphicsNonStandardItem* bay = dynamic_cast(items.at(n)); + if(bay){ + HierarchyItem info; + auto pBayPro = bay->getProperty(); + if(pBayPro){ + info.item.nCategory = 1; + info.item.nEquipType = 0; + info.item.sName = pBayPro->name(); + info.item.uid = pBayPro->uuid(); + lst.append(info); + } + } + } + } + emit _model->itemSelected(lst); + + updateConnectLineByTopology(items); + + setCursor(scene, Qt::ArrowCursor); + scene->callParentEvent(event); + emit setWorkingSelector(ST_base); +} + + diff --git a/diagramCavas/source/util/rotationSelector.cpp b/diagramCavas/source/util/rotationSelector.cpp new file mode 100644 index 0000000..7ec02c9 --- /dev/null +++ b/diagramCavas/source/util/rotationSelector.cpp @@ -0,0 +1,69 @@ +#include "util/rotationSelector.h" +#include +#include +#include + + +RotationSelector::RotationSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) +{ + m_type = ST_rotation; +} +RotationSelector::~RotationSelector() +{ + +} + +void RotationSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + +} + +void RotationSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + QList items = scene->selectedItems(); + if (items.count() == 1) + { + AbstractShape* item = qgraphicsitem_cast(items.first()); + if(item) + { + //计算夹角 + QPointF originPoint = item->mapToScene(item->boundingRect().center()); + double dLengthY = ms_ptMouseLast.y() - originPoint.y(); + double dLengthX = ms_ptMouseLast.x() - originPoint.x(); + double dAngleMouseToItem = atan2(dLengthY, dLengthX) * 180 / M_PI; + // if(atan2(dLengthY, dLengthX) < 0) + // dAngleMouseToItem += 360.0; + + double rotationAngle = item->rotation() + (dAngleMouseToItem - ms_dAngleMouseDownToItem); + //让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断 + if (rotationAngle > 180) + rotationAngle -= 360; + if (rotationAngle < -180) + rotationAngle += 360; + + item->rotateOperationCopy(rotationAngle); + //qDebug() << "rotationAngle: " << rotationAngle; + } + } +} + +void RotationSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + QList items = scene->selectedItems(); + for(int n = 0; n < items.size(); n++) + { + AbstractShape* item = qgraphicsitem_cast(items.at(n)); + if(item) + { + item->removeOperationCopy(); + } + + } + + ms_nDragHandle = H_none; + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_base); +} + diff --git a/diagramCavas/source/util/scalingSelector.cpp b/diagramCavas/source/util/scalingSelector.cpp new file mode 100644 index 0000000..496cb60 --- /dev/null +++ b/diagramCavas/source/util/scalingSelector.cpp @@ -0,0 +1,70 @@ +#include "util/scalingSelector.h" +#include +#include +#include + +ScalingSelector::ScalingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) +{ + m_type = ST_scaling; + m_scalBasePoint = QPointF(); +} +ScalingSelector::~ScalingSelector() +{ + +} + +void ScalingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + BaseSelector::mousePressEvent(event,scene,sceneMode); +} + +void ScalingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + QList items = scene->selectedItems(); + if (items.count() == 1) + { + GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + if(item) + { + if(ms_nDragHandle != H_none) + { + if(m_scalBasePoint.isNull()) //基准点不能采用临时变量,因为handle的坐标也在不断变化,计算会出现问题 + { + m_scalBasePoint = item->getSymmetricPointPos(ms_nDragHandle); + if(m_scalBasePoint.x() == 0) + m_scalBasePoint.setX(1); + if(m_scalBasePoint.y() == 0) + m_scalBasePoint.setY(1); + } + + //计算缩放倍数 + QPointF iniDelta = item->mapFromScene(ms_ptMouseDown) - m_scalBasePoint; + QPointF lastDelta = item->mapFromScene(ms_ptMouseLast) - m_scalBasePoint; + double sx = lastDelta.x() / iniDelta.x(); + double sy = lastDelta.y() / iniDelta.y(); + + item->resize(ms_nDragHandle, sx, sy, m_scalBasePoint); + } + } + } +} + +void ScalingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + QList items = scene->selectedItems(); + if (items.count() == 1) + { + AbstractShape* item = qgraphicsitem_cast(items.first()); + if(item && ms_ptMouseLast != ms_ptMouseDown) + { + item->updateCoordinate(); + } + } + + ms_nDragHandle = H_none; + m_scalBasePoint = QPointF(); + setCursor(scene, Qt::ArrowCursor); + emit setWorkingSelector(ST_base); +} diff --git a/diagramCavas/source/util/selectorManager.cpp b/diagramCavas/source/util/selectorManager.cpp new file mode 100644 index 0000000..b37497e --- /dev/null +++ b/diagramCavas/source/util/selectorManager.cpp @@ -0,0 +1,93 @@ +#include "util/selectorManager.h" +#include "util/creatingSelector.h" +#include "util/movingSelector.h" +#include "util/subMovingSelector.h" +#include "util/linkMovingSelector.h" +#include "util/rotationSelector.h" +#include "util/scalingSelector.h" +#include "util/editingSelector.h" +#include "util/connectingSelector.h" + + +SelectorManager::SelectorManager(FixedPortsModel* model,QObject *parent) + : _graphModel(model) + ,QObject(parent) +{ + + //创建所有的selector + BaseSelector* baseSelector = new BaseSelector(model,this); + connect(baseSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(baseSelector); + CreatingSelector* creatingSelector = new CreatingSelector(model,this); + connect(creatingSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(creatingSelector); + MovingSelector* movingSelector = new MovingSelector(model,this); + connect(movingSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(movingSelector); + SubMovingSelector* subMovSelector = new SubMovingSelector(model,this); + connect(subMovSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(subMovSelector); + LinkMovingSelector* linkMovSelector = new LinkMovingSelector(model,this); + connect(linkMovSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(linkMovSelector); + RotationSelector* rotationSelector = new RotationSelector(model,this); + connect(rotationSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(rotationSelector); + ScalingSelector* scalingSelector = new ScalingSelector(model,this); + connect(scalingSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(scalingSelector); + EditingSelector* editingSelector = new EditingSelector(model,this); + connect(editingSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(editingSelector); + ConnectingSelector* connectingSelector = new ConnectingSelector(model,this); + connect(connectingSelector, SIGNAL(setWorkingSelector(SelectorType)), this, SLOT(onSignal_setWorkingSelector(SelectorType))); + m_vecSelectors.push_back(connectingSelector); + + m_curSelector = ST_base; +} +SelectorManager::~SelectorManager() +{ + //析构所有的selector,因为是通过基类指针析构,所以基类的析构函数必须为虚函数 + // for(auto it = m_vecSelectors.begin(); it != m_vecSelectors.end(); it++) + // delete (*it); +} + + +BaseSelector* SelectorManager::getWorkingSelector() +{ + for(auto it = m_vecSelectors.begin(); it != m_vecSelectors.end(); it++) + { + if((*it)->getSelectorType()==m_curSelector) + { + return (*it); + } + } + + return nullptr; +} + +void SelectorManager::setDrawGraphicsItem(ModelStateInfo& info) +{ + for(auto it = m_vecSelectors.begin(); it != m_vecSelectors.end(); it++) + { + if((*it)->getSelectorType()==ST_cerating) + { + CreatingSelector* creatingSelector = dynamic_cast(*it); + creatingSelector->setCreatingItem(info); + } + } +} + +void SelectorManager::setName(const QString& str) +{ + for(auto iter:m_vecSelectors) + { + iter->setSceneName(str); + } +} + +void SelectorManager::onSignal_setWorkingSelector(SelectorType s) +{ + setWorkingSelector(s); +} + diff --git a/diagramCavas/source/util/subMovingSelector.cpp b/diagramCavas/source/util/subMovingSelector.cpp new file mode 100644 index 0000000..b6a255c --- /dev/null +++ b/diagramCavas/source/util/subMovingSelector.cpp @@ -0,0 +1,73 @@ +#include "util/subMovingSelector.h" +#include +#include +#include "graphicsItem/graphicsBaseItem.h" +#include "graphicsItem/handleText.h" + +SubMovingSelector::SubMovingSelector(FixedPortsModel* model,QObject *parent) + : BaseSelector(model,parent) + ,m_pParentItem(nullptr) +{ + m_type = ST_subMoving; +} +SubMovingSelector::~SubMovingSelector() +{ + +} + +void SubMovingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + + QList items = scene->selectedItems(); +} + +void SubMovingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + ms_ptMouseLast = event->scenePos(); + if(m_pParentItem == nullptr) + { + QList items = scene->items(ms_ptMouseLast); + if (items.count() == 1) + { + ItemControlHandle* pHandle = qgraphicsitem_cast(items.first()); + if(pHandle) + { + GraphicsProjectModelItem* item = dynamic_cast(pHandle->getParentPtr()); + if(item) + { + if(ms_nDragHandle >= H_textCaption && ms_nDragHandle < H_connect) //移动文本 + { + pHandle->setPos(item->mapFromScene(ms_ptMouseLast)); + m_pParentItem = item; + } + } + } + } + } + else + { + if(m_pParentItem) + { + if(ms_nDragHandle >= H_textCaption && ms_nDragHandle < H_connect) //文本 + { + HandleText* pt = dynamic_cast(m_pParentItem->getHandlePtr(ms_nDragHandle)); + if(pt) + { + pt->setPos(m_pParentItem->mapFromScene(ms_ptMouseLast)); + } + } + } + } + +} + +void SubMovingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene,DiagramMode sceneMode) +{ + m_pParentItem = nullptr; + ms_nDragHandle = H_none; + setCursor(scene, Qt::ArrowCursor); + scene->callParentEvent(event); + emit setWorkingSelector(ST_base); +} + diff --git a/diagramCavas/ui/baseInfoDlg.ui b/diagramCavas/ui/baseInfoDlg.ui new file mode 100644 index 0000000..70c734a --- /dev/null +++ b/diagramCavas/ui/baseInfoDlg.ui @@ -0,0 +1,404 @@ + + + baseInfoDlg + + + + 0 + 0 + 736 + 620 + + + + + 12 + + + + Dialog + + + + 10 + + + 10 + + + 10 + + + 10 + + + 8 + + + 10 + + + + + false + + + + + + + UUID + + + + + + + + 20 + 16777215 + + + + -- + + + Qt::AlignmentFlag::AlignCenter + + + + + + + true + + + + + + + 标签 + + + + + + + 间隔名称 + + + + + + + false + + + + + + + OUT 服役外 + + + + + + + + + + 区域 + + + + + + + 序号 + + + + + + + false + + + + + + + border:2px dashed black; +border-radius:5px; + + + + + + + 名称 + + + + + + + + + + 电网 + + + + + + + false + + + + + + + false + + + + + + + 服役状态 + + + + + + + 状态 + + + + + + + false + + + + + + + false + + + + + + + + 0 + 0 + + + + + 16777215 + 23 + + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + true + + + + + + + 子站 + + + + + + + false + + + + + + + 注释 + + + + + + + TAG + + + + + + + border:1px dashed black; + + + + + + + 命名空间 + + + + + + + 联结 从 + + + + + + + false + + + + + + + + + + IN 服役中 + + + + + + + false + + + + + + + + 16777215 + 30 + + + + border:4px double dark; + + + + + + + + + + + 0 + 30 + + + + + -1 + Medium + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 设备库 + + + + + + + + 0 + 30 + + + + + -1 + Medium + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 图标库 + + + + + + + + diff --git a/diagramCavas/ui/bayInfoDlg.ui b/diagramCavas/ui/bayInfoDlg.ui new file mode 100644 index 0000000..48cae71 --- /dev/null +++ b/diagramCavas/ui/bayInfoDlg.ui @@ -0,0 +1,382 @@ + + + bayInfoDlg + + + + 0 + 0 + 784 + 684 + + + + + 12 + + + + Dialog + + + + 6 + + + 10 + + + + + + + + + 本元件对本间隔的量测贡献 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + 名称 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + SIZE + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + + + + + + + + + 设备 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + TAG + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 类型 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 端子 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 事件 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 对称 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + false + + + false + + + + + + + + + + + 本间隔的其他量测 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + false + + + + + + + + + + + 继保/综保设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 其他设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 动态感知设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 安全自动装置 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + border:4px double dark; + + + + + + + + + + + + 间隔名称 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 序号 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + -- + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + 状态监测设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 监控设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + + diff --git a/diagramCavas/ui/bayManagerContentDlg.ui b/diagramCavas/ui/bayManagerContentDlg.ui new file mode 100644 index 0000000..d451db4 --- /dev/null +++ b/diagramCavas/ui/bayManagerContentDlg.ui @@ -0,0 +1,318 @@ + + + bayManagerContentDlg + + + + 0 + 0 + 584 + 562 + + + + + 12 + + + + Dialog + + + + + + 间隔信息 + + + + 10 + + + + + + + + + + + 联结 从 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + + + + IN + + + + + + + OUT + + + + + + + + + 间隔序号 + + + + + + + Qt::Orientation::Horizontal + + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 类型 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 标称电压 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + + 满载电流 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + 名称 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 服役状态 + + + + + + + 装机容量 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + + 设备配置 + + + + + + /* 标签页容器 */ +QTabWidget::pane { + border: 1px solid #e2e8f0; + border-radius: 4px; + background-color: white; + margin-top: 4px; +} + +/* 标签栏 */ +QTabWidget::tab-bar { + alignment: left; + padding: 1px 1px 0 1px; +} + +/* 单个标签 */ +QTabBar::tab { + background-color: #f8fafc; + color: #94a3b8; + border: 1px solid #e2e8f0; + border-bottom: none; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 6px 16px; + margin-right: 2px; + font-size: 11px; + font-weight: 500; +} + +/* 标签悬停 */ +QTabBar::tab:hover { + background-color: #f1f5f9; + color: #64748b; + border-color: #cbd5e1; +} + +/* 标签选中 */ +QTabBar::tab:selected { + background-color: white; + color: #1e293b; + border-color: #e2e8f0; + border-bottom-color: white; + font-weight: 500; + margin-bottom: -1px; + padding-top: 7px; +} + + + 0 + + + + 继保/综保设备 + + + + + + false + + + + + + + + 监控设备 + + + + + + false + + + + + + + + 动态感知 + + + + + + false + + + + + + + + 安全自动装置 + + + + + + false + + + + + + + + 状态监测 + + + + + + false + + + + + + + + 其他设备 + + + + + + false + + + + + + + + + + + + + + + diff --git a/diagramCavas/ui/bayManagerDlg.ui b/diagramCavas/ui/bayManagerDlg.ui new file mode 100644 index 0000000..88e9cb4 --- /dev/null +++ b/diagramCavas/ui/bayManagerDlg.ui @@ -0,0 +1,241 @@ + + + bayManagerDlg + + + + 0 + 0 + 870 + 632 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 21 + + + + + 16777215 + 25 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + 0 + + + + + + -1 + + + + + + + 间隔管理 + + + + + + + Qt::Orientation::Horizontal + + + + 803 + 10 + + + + + + + + + + + + + + 间隔列表 + + + + + + + + + + + + + + + + 16777215 + 30 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + + 469 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + + + + + diff --git a/diagramCavas/ui/bayMeasureDlg.ui b/diagramCavas/ui/bayMeasureDlg.ui new file mode 100644 index 0000000..d9511ca --- /dev/null +++ b/diagramCavas/ui/bayMeasureDlg.ui @@ -0,0 +1,534 @@ + + + bayMeasureDlg + + + + 0 + 0 + 819 + 648 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 6 + + + 0 + + + 0 + + + 0 + + + + + 间隔量测 + + + + + + + Qt::Orientation::Horizontal + + + + 762 + 18 + + + + + + + + + + + + 8 + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + border:4px double dark; + + + + + + + + + + 继保/综保设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 监控设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 动态感知设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 安全自动装置 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 状态监测设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 其他设备 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + + + 间隔名称 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + 序号 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + -- + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + + + + + 本间隔的一次设备外量测 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + 名称 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + SIZE + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + + + + + + + + + 设备 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + TAG + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 类型 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 端子 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 事件 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 对称 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + false + + + false + + + + + + + + + + + 本间隔的其他量测 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + false + + + + + + + + + + + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + + 646 + 20 + + + + + + + + + 75 + 0 + + + + 确定 + + + + + + + + 75 + 0 + + + + 取消 + + + + + + + + + + + diff --git a/diagramCavas/ui/createHMIdlg.ui b/diagramCavas/ui/createHMIdlg.ui new file mode 100644 index 0000000..e38245b --- /dev/null +++ b/diagramCavas/ui/createHMIdlg.ui @@ -0,0 +1,306 @@ + + + createHMIdlg + + + + 0 + 0 + 350 + 382 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + + + 新建组态图 + + + + + + + Qt::Orientation::Horizontal + + + + 286 + 18 + + + + + + + + + + + + 30 + + + 20 + + + 30 + + + 15 + + + + + + + + + + + 流程图 + + + + + 架构图 + + + + + 工艺图 + + + + + + + + 模板: + + + + + + + 名称: + + + + + + + 导入: + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 创建 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/diagramCavas/ui/ctExtraInfoDlg.ui b/diagramCavas/ui/ctExtraInfoDlg.ui new file mode 100644 index 0000000..56f1f66 --- /dev/null +++ b/diagramCavas/ui/ctExtraInfoDlg.ui @@ -0,0 +1,407 @@ + + + ctExtraInfoDlg + + + + 0 + 0 + 764 + 597 + + + + + 12 + + + + Form + + + + 8 + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + border:4px double dark; + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 额定电流 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + A + + + + + + + 动稳定电流 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + A + + + + + + + 短时热电流 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + As + + + + + + + 工频耐压 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + V/1min + + + + + + + 冲击耐压 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + V + + + + + + + 仪表保安系数 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + 额定频率 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + Hz + + + + + + + 相数 + + + + + + + 三相互感器 + + + + + + + 零序互感器 + + + + + + + 本元件内含CT的配置: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 变比范围 + + + + + + + false + + + + + + + 精度等级 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + 二次负载容量 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + 变比 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + 正常极性 + + + + + + + + 23 + 23 + + + + + 23 + 23 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + + + + + + + + + true + + + 100 + + + true + + + false + + + + 序号 + + + + + 变比范围 + + + + + 精度等级 + + + + + 二次负载容量 + + + + + 变比 + + + + + 极性 + + + + + + + + + diff --git a/diagramCavas/ui/dataSourceDlg.ui b/diagramCavas/ui/dataSourceDlg.ui new file mode 100644 index 0000000..0596afb --- /dev/null +++ b/diagramCavas/ui/dataSourceDlg.ui @@ -0,0 +1,280 @@ + + + dataSourceDlg + + + + 0 + 0 + 773 + 537 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 25 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 数据源选择 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Orientation::Horizontal + + + + + 4 + + + 10 + + + 10 + + + 5 + + + 10 + + + + + 层级关系 + + + + + + + + + + + + 4 + + + 5 + + + 10 + + + 10 + + + 10 + + + + + 包含属性 + + + + + + + + + + + + + + + 4 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 选中属性 + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + 0 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + + 75 + 0 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + diff --git a/diagramCavas/ui/diagramConnectSetting.ui b/diagramCavas/ui/diagramConnectSetting.ui new file mode 100644 index 0000000..83b39ef --- /dev/null +++ b/diagramCavas/ui/diagramConnectSetting.ui @@ -0,0 +1,675 @@ + + + diagramConnectSetting + + + + 0 + 0 + 414 + 588 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + + + + + + 网络通信设置 + + + + + + + Qt::Orientation::Horizontal + + + + 302 + 18 + + + + + + + + + + + /* 标签页容器 */ +QTabWidget::pane { + border: 1px solid #e2e8f0; + border-radius: 4px; + background-color: white; + margin-top: 4px; +} + +/* 标签栏 */ +QTabWidget::tab-bar { + alignment: left; + padding: 1px 1px 0 1px; +} + +/* 单个标签 */ +QTabBar::tab { + background-color: #f8fafc; + color: #94a3b8; + border: 1px solid #e2e8f0; + border-bottom: none; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 6px 16px; + margin-right: 2px; + font-size: 11px; + font-weight: 500; +} + +/* 标签悬停 */ +QTabBar::tab:hover { + background-color: #f1f5f9; + color: #64748b; + border-color: #cbd5e1; +} + +/* 标签选中 */ +QTabBar::tab:selected { + background-color: white; + color: #1e293b; + border-color: #e2e8f0; + border-bottom-color: white; + font-weight: 500; + margin-bottom: -1px; + padding-top: 7px; +} + + + 0 + + + + HTTP设置 + + + + 8 + + + + + HTTP服务器配置 + + + + 8 + + + + + 连接名称: + + + + + + + + + + ms + + + + + + + 超时时间: + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 服务器地址: + + + + + + + Qt::Orientation::Horizontal + + + + 217 + 20 + + + + + + + + + 50 + 16777215 + + + + + + + + + + + 连接测试 + + + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 测试节点推荐 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 状态: + + + + + + + 未连接 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 测试数据服务 + + + + + + + 状态: + + + + + + + 未连接 + + + + + + + + + + + WebSoket设置 + + + + + + WebSocket服务器配置 + + + + 8 + + + + + Qt::Orientation::Horizontal + + + + 214 + 20 + + + + + + + + 服务器地址 + + + + + + + 连接名称: + + + + + + + ms + + + + + + + + 50 + 16777215 + + + + + + + + 超时时间: + + + + + + + 心跳间隔: + + + + + + + + + + + 50 + 16777215 + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + ms + + + + + + + Qt::Orientation::Horizontal + + + + 217 + 20 + + + + + + + + + + + 连接测试 + + + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 测试连接 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 状态: + + + + + + + 未连接 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + 操作日志 + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 保存 + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + diff --git a/diagramCavas/ui/drawingPanel.ui b/diagramCavas/ui/drawingPanel.ui new file mode 100644 index 0000000..617a57c --- /dev/null +++ b/diagramCavas/ui/drawingPanel.ui @@ -0,0 +1,39 @@ + + + drawingPanel + + + + 0 + 0 + 801 + 501 + + + + Form + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/diagramCavas/ui/editorSettingDlg.ui b/diagramCavas/ui/editorSettingDlg.ui new file mode 100644 index 0000000..5991e86 --- /dev/null +++ b/diagramCavas/ui/editorSettingDlg.ui @@ -0,0 +1,83 @@ + + + Dialog + + + + 0 + 0 + 311 + 448 + + + + + 12 + + + + Dialog + + + + + 20 + 20 + 281 + 141 + + + + 基础信息 + + + + + 20 + 30 + 31 + 16 + + + + 名称: + + + + + + 50 + 30 + 91 + 20 + + + + + + + 20 + 60 + 31 + 16 + + + + 类型: + + + + + + 50 + 60 + 91 + 22 + + + + + + + + diff --git a/diagramCavas/ui/itemPropertyDlg.ui b/diagramCavas/ui/itemPropertyDlg.ui new file mode 100644 index 0000000..ea5f1b9 --- /dev/null +++ b/diagramCavas/ui/itemPropertyDlg.ui @@ -0,0 +1,221 @@ + + + itemPropertyDlg + + + + 0 + 0 + 879 + 672 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + + + + + + + + + + + 150 + 0 + + + + + + + + + + + + + + + + + + + + + + + + 0 + 30 + + + + + + + + 4 + + + 2 + + + 2 + + + + + + 0 + 20 + + + + + 80 + 30 + + + + + -1 + Medium + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + + 0 + 20 + + + + + 80 + 30 + + + + + -1 + Medium + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + + + + + diff --git a/diagramCavas/ui/loadMonitorPageDlg.ui b/diagramCavas/ui/loadMonitorPageDlg.ui new file mode 100644 index 0000000..98941b7 --- /dev/null +++ b/diagramCavas/ui/loadMonitorPageDlg.ui @@ -0,0 +1,176 @@ + + + loadMonitorPageDlg + + + + 0 + 0 + 182 + 355 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + + + + + + 载入运行时 + + + + + + + Qt::Orientation::Horizontal + + + + 103 + 18 + + + + + + + + + + + + + + + + + 0 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + + 0 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + diff --git a/diagramCavas/ui/measureSettingDlg.ui b/diagramCavas/ui/measureSettingDlg.ui new file mode 100644 index 0000000..a7a744a --- /dev/null +++ b/diagramCavas/ui/measureSettingDlg.ui @@ -0,0 +1,1447 @@ + + + measureSettingDlg + + + + 0 + 0 + 452 + 536 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + + + + -1 + + + + + + + 量测设置 + + + + + + + Qt::Orientation::Horizontal + + + + 314 + 20 + + + + + + + + + + + /* 标签页容器 */ +QTabWidget::pane { + border: 1px solid #e2e8f0; + border-radius: 4px; + background-color: white; + margin-top: 4px; +} + +/* 标签栏 */ +QTabWidget::tab-bar { + alignment: left; + padding: 1px 1px 0 1px; +} + +/* 单个标签 */ +QTabBar::tab { + background-color: #f8fafc; + color: #94a3b8; + border: 1px solid #e2e8f0; + border-bottom: none; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 6px 16px; + margin-right: 2px; + font-size: 11px; + font-weight: 500; +} + +/* 标签悬停 */ +QTabBar::tab:hover { + background-color: #f1f5f9; + color: #64748b; + border-color: #cbd5e1; +} + +/* 标签选中 */ +QTabBar::tab:selected { + background-color: white; + color: #1e293b; + border-color: #e2e8f0; + border-bottom-color: white; + font-weight: 500; + margin-bottom: -1px; + padding-top: 7px; +} + + + 0 + + + + /* 标签页容器 */ +QTabWidget::pane { + border: 1px solid #e2e8f0; + border-radius: 4px; + background-color: white; + margin-top: 4px; +} + +/* 标签栏 */ +QTabWidget::tab-bar { + alignment: left; + padding: 1px 1px 0 1px; +} + +/* 单个标签 */ +QTabBar::tab { + background-color: #f8fafc; + color: #94a3b8; + border: 1px solid #e2e8f0; + border-bottom: none; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 6px 16px; + margin-right: 2px; + font-size: 11px; + font-weight: 500; +} + +/* 标签悬停 */ +QTabBar::tab:hover { + background-color: #f1f5f9; + color: #64748b; + border-color: #cbd5e1; +} + +/* 标签选中 */ +QTabBar::tab:selected { + background-color: white; + color: #1e293b; + border-color: #e2e8f0; + border-bottom-color: white; + font-weight: 500; + margin-bottom: -1px; + padding-top: 7px; +} + + + 基本参数 + + + + 10 + + + + + + 150 + 0 + + + + + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + + + + + + + 对称标签 + + + + + + + + 0 + 0 + + + + + 80 + 16777215 + + + + + + + + (唯一标识) + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 150 + 0 + + + + + + + + 数据大小(size): + + + + + + + 标签(tag): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 类型: + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + 名称: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 150 + 0 + + + + + 遥测 + + + + + 遥信 + + + + + 遥控 + + + + + + + + 绕组序号 + + + + + + + 对称名称 + + + + + + + + 数据配置 + + + + + + 规约类型: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + CL3611规约 + + + + + 104规约 + + + + + + + + + 80 + 0 + + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 10 + + + + + 子站名称(station): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + 设备名称(device): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + 16777215 + 22 + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 22 + + + + + TM1 + + + + + TM2 + + + + + TM3 + + + + + TM4 + + + + + TM5 + + + + + TM6 + + + + + TM7 + + + + + TM8 + + + + + P + + + + + Q + + + + + S + + + + + PF + + + + + F + + + + + dF + + + + + UAB + + + + + UBC + + + + + UCA + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + TS01 + + + + + TS02 + + + + + TS03 + + + + + TS04 + + + + + TS05 + + + + + TS06 + + + + + TS07 + + + + + TS08 + + + + + TS09 + + + + + TS10 + + + + + TS11 + + + + + TS12 + + + + + TS13 + + + + + TS14 + + + + + TS15 + + + + + TS16 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + TC1 + + + + + TC2 + + + + + TC3 + + + + + TC4 + + + + + TC5 + + + + + TC6 + + + + + TC7 + + + + + TC8 + + + + + TC9 + + + + + + + + + + + + + 80 + 0 + + + + + + + + + + + 通道名称(channel): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 10 + + + + + + + + + 80 + 0 + + + + + + + + + + + + + + 偏移量(offset): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 包号(packet): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 子站名称(station): + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 事件策略 + + + + + + 事件策略: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 启用 + + + + + + + 禁用 + + + + + + + + 80 + 0 + + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + 1 + + + + + + + + + 80 + 0 + + + + + + + + + + + 遥信事件原因 + + + + + + 信号变化: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 上升沿(raising) + + + + + + + 下降沿(falling) + + + + + + + + + + 量测类型: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 22 + 22 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + - + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + + 22 + 22 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + + + + + + + + + 遥测事件原因 + + + + + + 越上限(upup) + + + + + + + 阈值: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 1 + + + 0.000000000000000 + + + + + + + + 80 + 0 + + + + + + + + + + + 越上限(up) + + + + + + + 阈值: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 1 + + + 0.000000000000000 + + + + + + + + 80 + 0 + + + + + + + + + + + 越下限(down) + + + + + + + 阈值: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 1 + + + 0.000000000000000 + + + + + + + + 80 + 0 + + + + + + + + + + + 越下限(downdown) + + + + + + + 阈值: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 1 + + + 0.000000000000000 + + + + + + + + 80 + 0 + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 命令级别: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + 参数列表: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + info + + + + + warning + + + + + error + + + + + critical + + + + + exception + + + + + + + + + + + + + + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + + 80 + 22 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 完成 + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + + diff --git a/diagramCavas/ui/monitorConfigDlg.ui b/diagramCavas/ui/monitorConfigDlg.ui new file mode 100644 index 0000000..ed084f6 --- /dev/null +++ b/diagramCavas/ui/monitorConfigDlg.ui @@ -0,0 +1,532 @@ + + + monitorConfigDlg + + + + 0 + 0 + 673 + 506 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 21 + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 6 + + + 0 + + + 0 + + + 0 + + + + + + + + 参数配置 + + + + + + + Qt::Orientation::Horizontal + + + + 584 + 18 + + + + + + + + + + + /* 标签页容器 */ +QTabWidget::pane { + border: 1px solid #e2e8f0; + border-radius: 4px; + background-color: white; + margin-top: 4px; +} + +/* 标签栏 */ +QTabWidget::tab-bar { + alignment: left; + padding: 1px 1px 0 1px; +} + +/* 单个标签 */ +QTabBar::tab { + background-color: #f8fafc; + color: #94a3b8; + border: 1px solid #e2e8f0; + border-bottom: none; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 6px 16px; + margin-right: 2px; + font-size: 11px; + font-weight: 500; +} + +/* 标签悬停 */ +QTabBar::tab:hover { + background-color: #f1f5f9; + color: #64748b; + border-color: #cbd5e1; +} + +/* 标签选中 */ +QTabBar::tab:selected { + background-color: white; + color: #1e293b; + border-color: #e2e8f0; + border-bottom-color: white; + font-weight: 500; + margin-bottom: -1px; + padding-top: 7px; +} + + + 0 + + + + 监控参数配置 + + + + + + 设备列表 + + + + + + + + + + + + 可选参数列表 + + + + + + + + + + + + + + + 配置详情 + + + + + + 显示名称: + + + + + + + + + + 查询参数: + + + + + + + + 9 + + + + + + + + 展示类型: + + + + + + + + 60 + 0 + + + + + 字符 + + + + + 图表 + + + + + + + + Qt::Orientation::Horizontal + + + + 526 + 20 + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 10 + + + + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + 接线图中展示: + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 10 + + + + + + 折线 + + + + + 柱状 + + + + + + + + 时间范围: + + + + + + + 图表类型: + + + + + + + + 5min + + + + + 10min + + + + + 30min + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + 10 + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/diagramCavas/ui/monitorDetailAttributeDlg.ui b/diagramCavas/ui/monitorDetailAttributeDlg.ui new file mode 100644 index 0000000..e52c36a --- /dev/null +++ b/diagramCavas/ui/monitorDetailAttributeDlg.ui @@ -0,0 +1,211 @@ + + + monitorDetailAttributeDlg + + + + 0 + 0 + 796 + 543 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + 0 + + + + + + + + 属性详细信息 + + + + + + + Qt::Orientation::Horizontal + + + + 709 + 18 + + + + + + + + + + + 30 + + + 30 + + + + + 调整列数: + + + + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 退出详情 + + + + + + + + + true + + + + + 0 + 0 + 794 + 497 + + + + + + + + + + diff --git a/diagramCavas/ui/monitorDisplaySettingDlg.ui b/diagramCavas/ui/monitorDisplaySettingDlg.ui new file mode 100644 index 0000000..a8fb92c --- /dev/null +++ b/diagramCavas/ui/monitorDisplaySettingDlg.ui @@ -0,0 +1,540 @@ + + + monitorDisplaySettingDlg + + + + 0 + 0 + 500 + 453 + + + + + 12 + + + + Dialog + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 21 + + + + + 16777215 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 6 + + + 0 + + + 0 + + + 0 + + + + + + + + 图元状态配置 + + + + + + + Qt::Orientation::Horizontal + + + + 584 + 18 + + + + + + + + + + + + + + 40 + + + 40 + + + + + 设备: + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 状态: + + + + + + + + + + + + 状态预览 + + + + + + + 160 + 160 + + + + border:1px solid grey; + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + Qt::Orientation::Vertical + + + + 20 + 80 + + + + + + + + 状态设置 + + + + 6 + + + + + 效果设置: + + + + + + + 颜色设置: + + + + + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 辅助颜色: + + + + + + + 选择颜色 + + + + + + + + + + + + + + + + + 闪烁 + + + + + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 选择图标 + + + + + + + 尺寸 + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + 宽: + + + + + + + + + + 高: + + + + + + + + + + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 选择颜色 + + + + + + + 图标设置: + + + + + + + 启用自定义 + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 保存 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + MonitorItemPreviewDlg + QWidget +
monitorItemPreviewDlg.h
+ 1 +
+
+ + +
diff --git a/diagramCavas/ui/projectDiagramNameInput.ui b/diagramCavas/ui/projectDiagramNameInput.ui new file mode 100644 index 0000000..738691a --- /dev/null +++ b/diagramCavas/ui/projectDiagramNameInput.ui @@ -0,0 +1,292 @@ + + + projectDiagramNameInput + + + + 0 + 0 + 330 + 138 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + + + + -1 + + + + + + + 输入组态图名称 + + + + + + + Qt::Orientation::Horizontal + + + + 227 + 20 + + + + + + + + + + + + 6 + + + 10 + + + 10 + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 12 + + + + color: rgb(0, 0, 0); + + + 名称 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 12 + + + + color: rgb(0, 0, 0); + + + 提示: + + + + + + + + 60 + 0 + + + + + 12 + + + + color: rgb(0, 0, 0); + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 取消 + + + + + + + + + + + + + diff --git a/diagramCavas/ui/projectIconSetting.ui b/diagramCavas/ui/projectIconSetting.ui new file mode 100644 index 0000000..9b99405 --- /dev/null +++ b/diagramCavas/ui/projectIconSetting.ui @@ -0,0 +1,179 @@ + + + projectIconSetting + + + + 0 + 0 + 441 + 324 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 21 + + + + + 0 + 21 + + + + QWidget { + background: #2c5282; + color: white; + border: none; +} + +QWidget QLabel { + color: white; + font-size: 12px; + background: transparent; +} + + + + 0 + + + 0 + + + 0 + + + + + + -1 + + + + + + + 工程模图标设置 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 确定 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/diagramCavas/ui/ptExtraInfoDlg.ui b/diagramCavas/ui/ptExtraInfoDlg.ui new file mode 100644 index 0000000..84bf4d8 --- /dev/null +++ b/diagramCavas/ui/ptExtraInfoDlg.ui @@ -0,0 +1,393 @@ + + + ptExtraInfoDlg + + + + 0 + 0 + 728 + 564 + + + + + 12 + + + + Form + + + + 8 + + + + + 正常极性 + + + + + + + 冲击耐压 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Yn + + + + Yn + + + + + d(open) + + + + + + + + 单相互感器 + + + + + + + 额定电压因数 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + 本元件内含PT二次绕组的配置: + + + + + + + 精度等级 + + + + + + + 变比 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + V + + + + + + + false + + + + + + + Hz + + + + + + + false + + + + + + + false + + + + + + + 相数 + + + + + + + 三相互感器 + + + + + + + + 23 + 23 + + + + + 23 + 23 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + + + + + + + + + V + + + + + + + false + + + + + + + 额定频率 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + border:4px double dark; + + + + + + + + + + false + + + + + + + 二次负载容量 + + + + + + + false + + + + + + + 绕组接法 + + + + + + + true + + + 130 + + + true + + + false + + + + 序号 + + + + + 变比范围 + + + + + 精度等级 + + + + + 二次负载容量 + + + + + 变比 + + + + + 极性 + + + + + 绕组接法 + + + + + + + + false + + + + + + + 变比范围 + + + + + + + 一次绕组接线接地方式 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 工频耐压 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + false + + + + + + + false + + + + + + + V/1min + + + + + + + 额定电压 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + diff --git a/diagramCavas/ui/structDataPreviewDlg.ui b/diagramCavas/ui/structDataPreviewDlg.ui new file mode 100644 index 0000000..4eea1a6 --- /dev/null +++ b/diagramCavas/ui/structDataPreviewDlg.ui @@ -0,0 +1,539 @@ + + + structDataPreviewDlg + + + + 0 + 0 + 1032 + 667 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #f8fafc, + stop:1 #f1f5f9); +} + + + + 0 + + + 0 + + + + + + 12 + + + + color: rgb(6, 6, 6); + + + 层级结构 + + + + + + + Qt::Orientation::Horizontal + + + + 601 + 18 + + + + + + + + + + + + 16777215 + 21 + + + + QPushButton { + background-color: #6b8cb8; /* 稍亮的灰蓝 */ + + color: rgb(255, 255, 255); +} + +QPushButton:hover { + background-color: #5a79a1; /* 悬停到主色 */ +} + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Vertical + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 21 + + + + + 16777215 + 21 + + + + QWidget { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #f8fafc, + stop:1 #f1f5f9); + color: rgb(6, 6, 6); +} + + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Preferred + + + + 0 + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 21 + + + + QWidget { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #f8fafc, + stop:1 #f1f5f9); +} + + + + 0 + + + 0 + + + + + + 12 + + + + color: rgb(6, 6, 6); + + + 状态信息 + + + + + + + Qt::Orientation::Horizontal + + + + 677 + 18 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/diagramCommunication/CMakeLists.txt b/diagramCommunication/CMakeLists.txt new file mode 100644 index 0000000..3c65312 --- /dev/null +++ b/diagramCommunication/CMakeLists.txt @@ -0,0 +1,58 @@ +project(diagramCommunication) + +set(DIAGRACOMMUNICATION_HEADER_FILES + include/channelConfig.h + include/baseChannel.h + include/communicationManager.h + include/httpChannel.h + include/webSocketChannel.h + include/configManager.h + include/uiCommunicationBus.h + include/dataProcessor.h + ../common/include/compiler.hpp + ../common/include/export.hpp + ../common/include/operatingSystem.hpp +) + +set(DIAGRACOMMUNICATION_SOURCE_FILES + source/communicationManager.cpp + source/baseChannel.cpp + source/webSocketChannel.cpp + source/httpChannel.cpp + source/configManager.cpp + source/uiCommunicationBus.cpp + source/dataProcessor.cpp +) + + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(diagramCommunication SHARED + MANUAL_FINALIZATION + ${DIAGRACOMMUNICATION_HEADER_FILES} + ${DIAGRACOMMUNICATION_SOURCE_FILES} + ) +else() + add_library(diagramCommunication SHARED + ${DIAGRACOMMUNICATION_HEADER_FILES} + ${DIAGRACOMMUNICATION_SOURCE_FILES} + ) +endif() + +target_link_libraries(diagramCommunication PUBLIC Qt${QT_VERSION_MAJOR}::Core) +target_link_libraries(diagramCommunication PRIVATE Qt6::Xml) +target_link_libraries(diagramCommunication PRIVATE Qt6::Network) +target_link_libraries(diagramCommunication PRIVATE Qt6::WebSockets) +target_link_libraries(diagramCommunication PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) + +option(BUILD_SHARED_LIBS "Build as shared library" ON) + + +target_include_directories(diagramCommunication PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_compile_definitions(diagramCommunication + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) diff --git a/diagramCommunication/CMakeLists.txt.NaNAUP b/diagramCommunication/CMakeLists.txt.NaNAUP new file mode 100644 index 0000000..3c65312 --- /dev/null +++ b/diagramCommunication/CMakeLists.txt.NaNAUP @@ -0,0 +1,58 @@ +project(diagramCommunication) + +set(DIAGRACOMMUNICATION_HEADER_FILES + include/channelConfig.h + include/baseChannel.h + include/communicationManager.h + include/httpChannel.h + include/webSocketChannel.h + include/configManager.h + include/uiCommunicationBus.h + include/dataProcessor.h + ../common/include/compiler.hpp + ../common/include/export.hpp + ../common/include/operatingSystem.hpp +) + +set(DIAGRACOMMUNICATION_SOURCE_FILES + source/communicationManager.cpp + source/baseChannel.cpp + source/webSocketChannel.cpp + source/httpChannel.cpp + source/configManager.cpp + source/uiCommunicationBus.cpp + source/dataProcessor.cpp +) + + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(diagramCommunication SHARED + MANUAL_FINALIZATION + ${DIAGRACOMMUNICATION_HEADER_FILES} + ${DIAGRACOMMUNICATION_SOURCE_FILES} + ) +else() + add_library(diagramCommunication SHARED + ${DIAGRACOMMUNICATION_HEADER_FILES} + ${DIAGRACOMMUNICATION_SOURCE_FILES} + ) +endif() + +target_link_libraries(diagramCommunication PUBLIC Qt${QT_VERSION_MAJOR}::Core) +target_link_libraries(diagramCommunication PRIVATE Qt6::Xml) +target_link_libraries(diagramCommunication PRIVATE Qt6::Network) +target_link_libraries(diagramCommunication PRIVATE Qt6::WebSockets) +target_link_libraries(diagramCommunication PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) + +option(BUILD_SHARED_LIBS "Build as shared library" ON) + + +target_include_directories(diagramCommunication PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_compile_definitions(diagramCommunication + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) diff --git a/diagramCommunication/CMakeLists.txt.ozECFc b/diagramCommunication/CMakeLists.txt.ozECFc new file mode 100644 index 0000000..3c65312 --- /dev/null +++ b/diagramCommunication/CMakeLists.txt.ozECFc @@ -0,0 +1,58 @@ +project(diagramCommunication) + +set(DIAGRACOMMUNICATION_HEADER_FILES + include/channelConfig.h + include/baseChannel.h + include/communicationManager.h + include/httpChannel.h + include/webSocketChannel.h + include/configManager.h + include/uiCommunicationBus.h + include/dataProcessor.h + ../common/include/compiler.hpp + ../common/include/export.hpp + ../common/include/operatingSystem.hpp +) + +set(DIAGRACOMMUNICATION_SOURCE_FILES + source/communicationManager.cpp + source/baseChannel.cpp + source/webSocketChannel.cpp + source/httpChannel.cpp + source/configManager.cpp + source/uiCommunicationBus.cpp + source/dataProcessor.cpp +) + + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(diagramCommunication SHARED + MANUAL_FINALIZATION + ${DIAGRACOMMUNICATION_HEADER_FILES} + ${DIAGRACOMMUNICATION_SOURCE_FILES} + ) +else() + add_library(diagramCommunication SHARED + ${DIAGRACOMMUNICATION_HEADER_FILES} + ${DIAGRACOMMUNICATION_SOURCE_FILES} + ) +endif() + +target_link_libraries(diagramCommunication PUBLIC Qt${QT_VERSION_MAJOR}::Core) +target_link_libraries(diagramCommunication PRIVATE Qt6::Xml) +target_link_libraries(diagramCommunication PRIVATE Qt6::Network) +target_link_libraries(diagramCommunication PRIVATE Qt6::WebSockets) +target_link_libraries(diagramCommunication PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) + +option(BUILD_SHARED_LIBS "Build as shared library" ON) + + +target_include_directories(diagramCommunication PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_compile_definitions(diagramCommunication + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) diff --git a/diagramCommunication/include/baseChannel.h b/diagramCommunication/include/baseChannel.h new file mode 100644 index 0000000..459a392 --- /dev/null +++ b/diagramCommunication/include/baseChannel.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "export.hpp" + +class DIAGRAM_DESIGNER_PUBLIC BaseChannel : public QObject +{ + Q_OBJECT + +public: + struct ChannelConfig { + QString channelId; + QUrl endpoint; + int timeout = 10000; // 超时时间(ms) + int reconnectInterval = 5000; // 重连间隔(ms) + int maxRetries = 3; // 最大重试次数 + QVariantMap params; // 自定义参数 + }; + + explicit BaseChannel(const ChannelConfig& config, QObject* parent = nullptr); + virtual ~BaseChannel(); + + // 连接管理 + virtual bool connect() = 0; + virtual bool disconnect() = 0; + virtual bool isConnected() const = 0; + + // 数据发送 + virtual bool send(const QByteArray& data) = 0; + + // 信息获取 + QString channelId() const { return m_config.channelId; } + ChannelConfig config() const { return m_config; } + QUrl endpoint() const { return m_config.endpoint; } + QString sessionId() const {return m_sessionId;} + + // 控制 + void setAutoReconnect(bool enable); + bool isAutoReconnect() const { return m_autoReconnect; } + +signals: + void connected(const QString& = ""); + void disconnected(const QString& = ""); + void dataReceived(const QByteArray& data,const QString& = ""); + void errorOccurred(const QString& error,const QString& = ""); + +protected: + // 公共方法 + void startReconnectTimer(); + void stopReconnectTimer(); + void reconnect(); + + // 工具方法 + QByteArray generateMessageId() const; + qint64 currentTimestamp() const; + + // 成员变量 + ChannelConfig m_config; + bool m_autoReconnect = true; + QString m_sessionId; //会话id(http无) + +private slots: + void onReconnectTimeout(); + +private: + QTimer* m_reconnectTimer = nullptr; + int m_reconnectCount = 0; + QMutex m_mutex; +}; diff --git a/diagramCommunication/include/channelConfig.h b/diagramCommunication/include/channelConfig.h new file mode 100644 index 0000000..96b4618 --- /dev/null +++ b/diagramCommunication/include/channelConfig.h @@ -0,0 +1,72 @@ +// ChannelConfig.h +#pragma once + +#include +#include +#include +#include +#include +#include "export.hpp" + +// 简化配置结构 +struct DIAGRAM_DESIGNER_PUBLIC ChannelConfig { + // 通用配置 + QString id; // 通道ID: "http_channel" 或 "websocket_channel" + QString name; // 通道名称 + QString endpoint; // 连接地址 + int timeout = 30000; // 超时时间(ms) + bool enabled = true; // 是否启用 + bool autoConnect = false; // 是否自动连接 + QVariantMap headers; //头 + + // 认证 + QString username; + QString password; + + // 状态 + bool connected = false; + QDateTime lastConnectTime; + int errorCount = 0; + + // WebSocket特有 + int heartbeatInterval = 30000; // 心跳间隔 + + // 转换为Map + QVariantMap toMap() const { + return { + {"id", id}, + {"name", name}, + {"endpoint", endpoint}, + {"timeout", timeout}, + {"enabled", enabled}, + {"autoConnect", autoConnect}, + {"username", username}, + {"password", password}, + {"connected", connected}, + {"lastConnectTime", lastConnectTime.toString(Qt::ISODate)}, + {"errorCount", errorCount}, + {"headers", headers}, + {"heartbeatInterval", heartbeatInterval} + }; + } + + // 从Map创建 + static ChannelConfig fromMap(const QVariantMap& map) { + ChannelConfig config; + config.id = map.value("id").toString(); + config.name = map.value("name").toString(); + config.endpoint = map.value("endpoint").toString(); + config.timeout = map.value("timeout", 30000).toInt(); + config.enabled = map.value("enabled", true).toBool(); + config.autoConnect = map.value("autoConnect", false).toBool(); + config.username = map.value("username").toString(); + config.password = map.value("password").toString(); + config.connected = map.value("connected", false).toBool(); + config.lastConnectTime = QDateTime::fromString( + map.value("lastConnectTime").toString(), Qt::ISODate); + config.errorCount = map.value("errorCount", 0).toInt(); + config.headers = map.value("headers").toMap(); + config.heartbeatInterval = map.value("heartbeatInterval", 30000).toInt(); + return config; + } +}; diff --git a/diagramCommunication/include/communicationManager.h b/diagramCommunication/include/communicationManager.h new file mode 100644 index 0000000..ef825ca --- /dev/null +++ b/diagramCommunication/include/communicationManager.h @@ -0,0 +1,76 @@ +// CommunicationManager.h +#pragma once + +#include "channelConfig.h" +#include "httpChannel.h" +#include "webSocketChannel.h" +#include +#include +#include "export.hpp" + +class DIAGRAM_DESIGNER_PUBLIC CommunicationManager : public QObject +{ + Q_OBJECT + +public: + static CommunicationManager* instance(); + + // 初始化 + bool initialize(); + + // HTTP通道操作 + bool connectHttp(); + bool disconnectHttp(); + bool sendHttpRequest(const QString& path, + const QByteArray& data = QByteArray(), + const QString& method = "GET", + const QVariantMap& query = QVariantMap()); + + // WebSocket通道操作 + bool connectWebSocket(const QString& sessionId = ""); + bool disconnectWebSocket(const QString& sessionId = ""); + bool removeChannel(const QString& sessionId); + + bool sendWebSocketMessage(const QByteArray& data,const QString& sessionId); + bool sendWebSocketText(const QString& text,const QString& sessionId); + + // 状态查询 + bool isHttpConnected() const; + bool isWebSocketConnected(const QString& sessionId) const; + ChannelConfig getHttpConfig() const; + ChannelConfig getWebSocketConfig() const; + + // 配置更新 + void updateHttpConfig(const ChannelConfig& config); + void updateWebSocketConfig(const ChannelConfig& config,const QString& sessionId = ""); + +signals: + // HTTP通道信号 + void httpConnected(); + void httpDisconnected(); + void httpDataReceived(const QByteArray& data); + void httpError(const QString& error); + + // WebSocket通道信号 + void websocketConnected(); + void websocketDisconnected(); + void websocketDataReceived(const QByteArray& data); + void websocketTextReceived(const QString& text); + void websocketError(const QString& error); + +private: + CommunicationManager(QObject* parent = nullptr); + ~CommunicationManager(); + + // 内部初始化 + void initHttpChannel(); + void initWebSocketChannel(QString sessionId = ""); //服务器回传的会话id + + // 配置 + ChannelConfig m_httpConfig; + ChannelConfig m_websocketConfig; + + // 通道实例 + QSharedPointer m_httpChannel; + QMap> m_websocketChannelMap; // +}; diff --git a/diagramCommunication/include/configManager.h b/diagramCommunication/include/configManager.h new file mode 100644 index 0000000..f673c11 --- /dev/null +++ b/diagramCommunication/include/configManager.h @@ -0,0 +1,49 @@ +// ConfigManager.h +#pragma once + +#include "channelConfig.h" +#include +#include +#include +#include +#include "export.hpp" + +class DIAGRAM_DESIGNER_PUBLIC ConfigManager : public QObject +{ + Q_OBJECT + +public: + static ConfigManager* instance(); + + // 加载配置 + bool loadConfig(const QString& configFile = ""); + + // 保存配置 + bool saveConfig(); + + // 获取配置 + ChannelConfig getHttpConfig() const; + ChannelConfig getWebSocketConfig() const; + + // 更新配置 + void setHttpConfig(const ChannelConfig& config); + void setWebSocketConfig(const ChannelConfig& config); + + // 获取配置路径 + QString configFilePath() const; + +signals: + void configLoaded(); + void configSaved(); + void httpConfigChanged(const ChannelConfig& config); + void websocketConfigChanged(const ChannelConfig& config); +private: + ConfigManager(QObject* parent = nullptr); + bool createDefaultConfig(); + ChannelConfig getDefaultHttpConfig() const; + ChannelConfig getDefaultWebSocketConfig() const; + + ChannelConfig m_httpConfig; + ChannelConfig m_websocketConfig; + QString m_configFile; +}; diff --git a/diagramCommunication/include/dataProcessor.h b/diagramCommunication/include/dataProcessor.h new file mode 100644 index 0000000..ba9794e --- /dev/null +++ b/diagramCommunication/include/dataProcessor.h @@ -0,0 +1,40 @@ +// DataProcessor.h +#pragma once + +#include +#include +#include +#include +#include "export.hpp" + +// 网络数据处理中心 +class DIAGRAM_DESIGNER_PUBLIC DataProcessor : public QObject +{ + Q_OBJECT + +public: + static DataProcessor* instance(); + + // 处理数据 + void processData(const QVariant& data,int conType = 0); + void processNullData(const QVariant& data); + // 获取处理后的数据 + QVariant getProcessedData(const QString& key) const; + + // 清除所有数据 + void clearAllData(); + +signals: + // 数据处理完成信号 + void httpProcessed(const QString& sType,const QVariant& data); + void websocketProcessed(const QVariant& data); +private: + DataProcessor(QObject* parent = nullptr); + // 通用处理函数 + void processJson(const QVariant& data,int conType = 0); //0http 1websocket + void processJsonArray(const QVariant& data); + + // 数据缓存 + QMap m_dataCache; + mutable QMutex m_mutex; +}; diff --git a/diagramCommunication/include/httpChannel.h b/diagramCommunication/include/httpChannel.h new file mode 100644 index 0000000..3bca4a6 --- /dev/null +++ b/diagramCommunication/include/httpChannel.h @@ -0,0 +1,32 @@ +// HttpChannel.h +#pragma once + +#include "baseChannel.h" + +class DIAGRAM_DESIGNER_PUBLIC HttpChannel : public BaseChannel +{ + Q_OBJECT + +public: + HttpChannel(const ChannelConfig& config, QObject* parent = nullptr); + + bool connect() override; + bool disconnect() override; + bool isConnected() const override; + bool send(const QByteArray& data) override; + + // HTTP方法 + bool get(const QString& path = "",const QVariantMap& queryMap = QVariantMap()); + bool post(const QByteArray& data, const QString& path = ""); + bool put(const QByteArray& data, const QString& path = ""); + bool deleteResource(const QString& path = ""); + + // 配置 + void setBasicAuth(const QString& username, const QString& password); + void setHeader(const QString& name, const QString& value); + +private: + QString m_username; + QString m_password; + QMap m_headers; +}; diff --git a/diagramCommunication/include/uiCommunicationBus.h b/diagramCommunication/include/uiCommunicationBus.h new file mode 100644 index 0000000..a5ef087 --- /dev/null +++ b/diagramCommunication/include/uiCommunicationBus.h @@ -0,0 +1,123 @@ +// uiCommunicationBus.h +#pragma once + +#include "export.hpp" +#include +#include +#include +#include + +// UI通信总线 +class DIAGRAM_DESIGNER_PUBLIC UiCommunicationBus : public QObject +{ + Q_OBJECT + +public: + static UiCommunicationBus* instance(); + + // 发送HTTP请求 + void sendHttpRequest(const QString& endpoint, const QVariant& data = QVariant(),const QString& method = "GET",const QVariantMap& = QVariantMap()); + + // 发送HTTP请求(无回复) + void sendHttpRequestNoReply(const QString& endpoint, const QVariant& data = QVariant()); + + // 向UI发送数据 + void sendToUi(const QString& uiId, const QString& action, const QVariant& data); + + // 广播到所有UI + void broadcastToUis(const QString& action, const QVariant& data); + + // 注册/注销UI + void registerUi(const QString& uiId, QObject* uiObject); + void unregisterUi(const QString& uiId); + + QMap>>& getTempRequestMap() {return _tempRequest;} + void insertTempRequest(QString page,QList> lst){ + if(!_tempRequest.contains(page)){ + _tempRequest.insert(page,lst); + } + } + + QMap>>>& getSesstionMap() {return _session;} + void insertSesstionMap(QString id,QMap targetMap){ //从临时列表移除,插入到会话列表 + + QStringList sortedTargetList; //排序后的target列表 + for(auto it = targetMap.begin();it != targetMap.end();++it){ + sortedTargetList.append(it.key()); + } + sortedTargetList.sort(); + + auto it = _tempRequest.begin(); + while (it != _tempRequest.end()) + { + const QString& page = it.key(); + QList>& tempList = it.value(); + + // 提取当前 tempList 中所有 first 元素组成的列表 + QStringList firstElements; + firstElements.reserve(tempList.size()); + for (const auto& pair : tempList) + { + firstElements.append(pair.first); + } + + // 先进行简单检查:如果元素数量不同,肯定不匹配 + if (firstElements.size() != sortedTargetList.size()) + { + ++it; + continue; + } + + // 排序 firstElements + QStringList sortedFirstElements = firstElements; + sortedFirstElements.sort(); + + // 比较排序后的列表 + if (sortedFirstElements == sortedTargetList) + { + // 匹配成功 + // 检查 _session 中是否已有此 page + if (!_session.contains(page)) + { + QList> newList; + for (const auto& item : tempList) { //如果订阅不成功,从候选列表中移除 todo:记录错误target + auto it = targetMap.find(item.first); + if (it != targetMap.end()) { + if (it.value() != "1001") { + continue; // 跳过 + } + } + newList.append(item); + } + + tempList = newList; + // _session 中没有此 page,可以插入 + _session[page] = qMakePair(id, tempList); + } + // 无论 _session 中是否有此 page,都从 _tempRequest 删除 *********同一个page中只存在一个会话****** + it = _tempRequest.erase(it); + } + else + { + ++it; + } + } + } +signals: + void httpDataProcessed(const QString& type,const QVariant& data); //发送分拣过的数据给外部 + void websocketDataProcessed(const QVariant& data); +private: + UiCommunicationBus(QObject* parent = nullptr); + + // 处理HTTP响应 + void onHttpDataReceived(const QByteArray& data); + + // 处理WebSocket数据 + void onWebSocketDataReceived(const QByteArray& data); + + // UI注册表 + QMap m_uiObjects; + mutable QMutex m_mutex; + QMap>> _tempRequest; //临时请求队列,一个QList为一次会话 <图名<节点名,状态>> + QMap>>> _session; //会话队列 <图名<会话id<节点名,状态>>> +}; diff --git a/diagramCommunication/include/webSocketChannel.h b/diagramCommunication/include/webSocketChannel.h new file mode 100644 index 0000000..4b1d5db --- /dev/null +++ b/diagramCommunication/include/webSocketChannel.h @@ -0,0 +1,38 @@ +// WebSocketChannel.h +#pragma once + +#include "baseChannel.h" +#include + +class DIAGRAM_DESIGNER_PUBLIC WebSocketChannel : public BaseChannel +{ + Q_OBJECT + +public: + struct WebSocketConfig { + int heartbeatInterval = 30000; + }; + + WebSocketChannel(const ChannelConfig& config, QString sessionId,QObject* parent = nullptr); + ~WebSocketChannel(); + + bool connect() override; + bool disconnect() override; + bool isConnected() const override; + bool send(const QByteArray& data) override; + + bool sendText(const QString& text); + void setWebSocketConfig(const WebSocketConfig& config); + +signals: + void textMessageReceived(const QString& message,const QString& = ""); + +private slots: + void onConnected(); + void onDisconnected(); + void onTextMessageReceived(const QString& message); + void onBinaryMessageReceived(const QByteArray& message); +private: + QWebSocket* m_webSocket = nullptr; + WebSocketConfig m_wsConfig; +}; diff --git a/diagramCommunication/source/baseChannel.cpp b/diagramCommunication/source/baseChannel.cpp new file mode 100644 index 0000000..bc31faa --- /dev/null +++ b/diagramCommunication/source/baseChannel.cpp @@ -0,0 +1,76 @@ +#include "baseChannel.h" +#include +#include +#include + +BaseChannel::BaseChannel(const ChannelConfig& config, QObject* parent) + : QObject(parent) + , m_config(config) + , m_reconnectTimer(new QTimer(this)) +{ + m_reconnectTimer->setSingleShot(true); + QObject::connect(m_reconnectTimer,&QTimer::timeout, this, &BaseChannel::onReconnectTimeout); + + qDebug() << "BaseChannel created:" << m_config.channelId; +} + +BaseChannel::~BaseChannel() +{ + stopReconnectTimer(); + qDebug() << "BaseChannel destroyed:" << m_config.channelId; +} + +void BaseChannel::setAutoReconnect(bool enable) +{ + m_autoReconnect = enable; + if (!enable) { + stopReconnectTimer(); + } +} + +void BaseChannel::startReconnectTimer() +{ + if (m_autoReconnect && m_reconnectCount < m_config.maxRetries) { + int delay = m_config.reconnectInterval * (1 << m_reconnectCount); // 指数退避 + m_reconnectTimer->start(qMin(delay, 30000)); // 最大30秒 + m_reconnectCount++; + } +} + +void BaseChannel::stopReconnectTimer() +{ + m_reconnectTimer->stop(); + m_reconnectCount = 0; +} + +void BaseChannel::reconnect() +{ + if (m_autoReconnect) { + disconnect(); + QTimer::singleShot(100, this, [this]() { + connect(); + }); + } +} + +void BaseChannel::onReconnectTimeout() +{ + if (m_autoReconnect) { + qDebug() << "Reconnecting channel" << m_config.channelId + << "attempt" << m_reconnectCount << "/" << m_config.maxRetries; + connect(); + } +} + +QByteArray BaseChannel::generateMessageId() const +{ + QString id = QString("%1_%2") + .arg(m_config.channelId) + .arg(QDateTime::currentMSecsSinceEpoch()); + return QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex(); +} + +qint64 BaseChannel::currentTimestamp() const +{ + return QDateTime::currentMSecsSinceEpoch(); +} diff --git a/diagramCommunication/source/communicationManager.cpp b/diagramCommunication/source/communicationManager.cpp new file mode 100644 index 0000000..0889d32 --- /dev/null +++ b/diagramCommunication/source/communicationManager.cpp @@ -0,0 +1,370 @@ +// CommunicationManager.cpp +#include "communicationManager.h" +#include + +CommunicationManager* CommunicationManager::instance() +{ + static CommunicationManager* instance = nullptr; + static QMutex mutex; + + if (!instance) { + QMutexLocker locker(&mutex); + if (!instance) { + instance = new CommunicationManager; + } + } + return instance; +} + +CommunicationManager::CommunicationManager(QObject* parent) + : QObject(parent) +{ + // 设置默认配置 + m_httpConfig.id = "http_channel"; + m_httpConfig.name = "HTTP通道"; + m_httpConfig.endpoint = "http://localhost:8080"; + + m_websocketConfig.id = "websocket_channel"; + m_websocketConfig.name = "WebSocket通道"; + m_websocketConfig.endpoint = "ws://localhost:8888/ws"; +} + +CommunicationManager::~CommunicationManager() +{ + disconnectHttp(); + disconnectWebSocket(); +} + +bool CommunicationManager::initialize() +{ + // 初始化HTTP通道 + initHttpChannel(); + + // 初始化WebSocket通道 + //initWebSocketChannel(); + + qInfo() << "CommunicationManager initialized"; + return true; +} + +void CommunicationManager::initHttpChannel() +{ + if (m_httpChannel) { + m_httpChannel->disconnect(); + } + + // 创建HTTP通道 + HttpChannel::ChannelConfig httpConfig; + httpConfig.endpoint = QUrl(m_httpConfig.endpoint); + httpConfig.timeout = m_httpConfig.timeout; + + m_httpChannel.reset(new HttpChannel(httpConfig)); + + // 设置认证 + if (!m_httpConfig.username.isEmpty() && !m_httpConfig.password.isEmpty()) { + m_httpChannel->setBasicAuth(m_httpConfig.username, m_httpConfig.password); + } + + // 设置HTTP头 + for (auto it = m_httpConfig.headers.begin(); it != m_httpConfig.headers.end(); ++it) { + m_httpChannel->setHeader(it.key(), it.value().toString()); + } + + // 连接信号 + connect(m_httpChannel.data(), &HttpChannel::connected, + this, &CommunicationManager::httpConnected); + connect(m_httpChannel.data(), &HttpChannel::disconnected, + this, &CommunicationManager::httpDisconnected); + connect(m_httpChannel.data(), &HttpChannel::dataReceived, + this, &CommunicationManager::httpDataReceived); + connect(m_httpChannel.data(), &HttpChannel::errorOccurred, + this, &CommunicationManager::httpError); +} + +void CommunicationManager::initWebSocketChannel(QString sessionId) +{ + WebSocketChannel::ChannelConfig wsConfig; + wsConfig.endpoint = QUrl(m_websocketConfig.endpoint); + wsConfig.timeout = m_websocketConfig.timeout; + + WebSocketChannel::WebSocketConfig websocketConfig; + websocketConfig.heartbeatInterval = m_websocketConfig.heartbeatInterval; + + if(sessionId.isEmpty()){ //没有指定会话,初始化已有channel + for(auto& pChannel:m_websocketChannelMap){ + pChannel->disconnect(); + pChannel.reset(new WebSocketChannel(wsConfig,pChannel->sessionId())); + pChannel->setWebSocketConfig(websocketConfig); + + // 连接信号 + connect(pChannel.data(), &WebSocketChannel::connected, + this, &CommunicationManager::websocketConnected); + connect(pChannel.data(), &WebSocketChannel::disconnected, + this, &CommunicationManager::websocketDisconnected); + connect(pChannel.data(), &WebSocketChannel::dataReceived, + this, &CommunicationManager::websocketDataReceived); + connect(pChannel.data(), &WebSocketChannel::errorOccurred, + this, &CommunicationManager::websocketError); + connect(pChannel.data(), &WebSocketChannel::textMessageReceived, + this, &CommunicationManager::websocketTextReceived); + } + } + else{ + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ //已存在 + pChannel->disconnect(); + pChannel.reset(new WebSocketChannel(wsConfig,pChannel->sessionId())); + pChannel->setWebSocketConfig(websocketConfig); + + // 连接信号 + connect(pChannel.data(), &WebSocketChannel::connected, + this, &CommunicationManager::websocketConnected); + connect(pChannel.data(), &WebSocketChannel::disconnected, + this, &CommunicationManager::websocketDisconnected); + connect(pChannel.data(), &WebSocketChannel::dataReceived, + this, &CommunicationManager::websocketDataReceived); + connect(pChannel.data(), &WebSocketChannel::errorOccurred, + this, &CommunicationManager::websocketError); + connect(pChannel.data(), &WebSocketChannel::textMessageReceived, + this, &CommunicationManager::websocketTextReceived); + return; + } + } + + QSharedPointer newChannel(new WebSocketChannel(wsConfig,sessionId)); + newChannel->setWebSocketConfig(websocketConfig); + + // 连接信号 + connect(newChannel.data(), &WebSocketChannel::connected, + this, &CommunicationManager::websocketConnected); + connect(newChannel.data(), &WebSocketChannel::disconnected, + this, &CommunicationManager::websocketDisconnected); + connect(newChannel.data(), &WebSocketChannel::dataReceived, + this, &CommunicationManager::websocketDataReceived); + connect(newChannel.data(), &WebSocketChannel::errorOccurred, + this, &CommunicationManager::websocketError); + connect(newChannel.data(), &WebSocketChannel::textMessageReceived, + this, &CommunicationManager::websocketTextReceived); + m_websocketChannelMap.insert(sessionId,newChannel); + } +} + +bool CommunicationManager::connectHttp() +{ + if (!m_httpChannel) { + qWarning() << "HTTP channel not initialized"; + return false; + } + + if (m_httpChannel->isConnected()) { + return true; + } + + return m_httpChannel->connect(); +} + +bool CommunicationManager::disconnectHttp() +{ + if (!m_httpChannel) { + return false; + } + + if (!m_httpChannel->isConnected()) { + return true; + } + + return m_httpChannel->disconnect(); +} + +bool CommunicationManager::sendHttpRequest(const QString& path, + const QByteArray& data, + const QString& method, + const QVariantMap& query) +{ + if (!m_httpChannel || !m_httpChannel->isConnected()) { + qWarning() << "HTTP channel not connected"; + return false; + } + + if (method == "GET") { + return m_httpChannel->get(path,query); + } else if (method == "POST") { + return m_httpChannel->post(data, path); + } else if (method == "PUT") { + return m_httpChannel->put(data, path); + } else if (method == "DELETE") { + return m_httpChannel->deleteResource(path); + } + + qWarning() << "Unsupported HTTP method:" << method; + return false; +} + +bool CommunicationManager::connectWebSocket(const QString& sessionId) +{ + if(sessionId.isEmpty()){ + for(auto& pChannel:m_websocketChannelMap){ + if (!pChannel->isConnected()) { + pChannel->connect(); + } + } + return true; + } + else + { + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if (pChannel->isConnected()) { + return true; + } + + return pChannel->connect(); + } + } + } + + qWarning() << "WebSocket channel not initialized"; + return false; + +} + +bool CommunicationManager::disconnectWebSocket(const QString& sessionId) +{ + + if(sessionId.isEmpty()){ //不指定则断开所有通道 + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if (pChannel->isConnected()) { + pChannel->disconnect(); + } + } + } + return true; + } + else{ + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if (!pChannel->isConnected()) { + return true; + } + + return pChannel->disconnect(); + } + } + } + + return false; +} + +bool CommunicationManager::removeChannel(const QString& sessionId) +{ + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if (pChannel->isConnected()) { + pChannel->disconnect(); + } + m_websocketChannelMap.remove(sessionId); + return true; + } + } + return false; +} + +bool CommunicationManager::sendWebSocketMessage(const QByteArray& data,const QString& sessionId) +{ + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if(!pChannel->isConnected()){ + qWarning() << "WebSocket channel not connected"; + return false; + } + + return pChannel->send(data); + } + } + + qWarning() << "WebSocket channel not connected"; + return false; +} + +bool CommunicationManager::sendWebSocketText(const QString& text,const QString& sessionId) +{ + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if(!pChannel->isConnected()){ + qWarning() << "WebSocket channel not connected"; + return false; + } + + return pChannel->sendText(text); + } + } + + qWarning() << "WebSocket channel not connected"; + return false; +} + +bool CommunicationManager::isHttpConnected() const +{ + return m_httpChannel && m_httpChannel->isConnected(); +} + +bool CommunicationManager::isWebSocketConnected(const QString& sessionId) const +{ + for(auto& pChannel:m_websocketChannelMap){ + if(pChannel->sessionId() == sessionId){ + if(pChannel->isConnected()){ + return true; + } + } + } + + return false; +} + +ChannelConfig CommunicationManager::getHttpConfig() const +{ + return m_httpConfig; +} + +ChannelConfig CommunicationManager::getWebSocketConfig() const +{ + return m_websocketConfig; +} + +void CommunicationManager::updateHttpConfig(const ChannelConfig& config) +{ + bool reconnect = false; + + if (m_httpConfig.endpoint != config.endpoint) { + // 端点变化,需要重新初始化 + reconnect = true; + } + + m_httpConfig = config; + initHttpChannel(); + + if (reconnect && config.autoConnect) { + connectHttp(); + } + + qInfo() << "HTTP config updated"; +} + +void CommunicationManager::updateWebSocketConfig(const ChannelConfig& config,const QString& sessionId) +{ + bool reconnect = false; + + if (m_websocketConfig.endpoint != config.endpoint) { + reconnect = true; + } + + m_websocketConfig = config; + initWebSocketChannel(sessionId); + + if (reconnect && config.autoConnect) { + connectWebSocket(sessionId); + } + + qInfo() << "WebSocket config updated"; +} diff --git a/diagramCommunication/source/configManager.cpp b/diagramCommunication/source/configManager.cpp new file mode 100644 index 0000000..8b668c9 --- /dev/null +++ b/diagramCommunication/source/configManager.cpp @@ -0,0 +1,217 @@ +// ConfigManager.cpp +#include "configManager.h" +#include +#include +#include +#include +#include +#include +#include + +ConfigManager* ConfigManager::instance() +{ + static ConfigManager* instance = nullptr; + static QMutex mutex; + + if (!instance) { + QMutexLocker locker(&mutex); + if (!instance) { + instance = new ConfigManager; + } + } + return instance; +} + +ConfigManager::ConfigManager(QObject* parent) + : QObject(parent) +{ + // 设置默认配置路径 + QString appDir = QCoreApplication::applicationDirPath(); + m_configFile = appDir + "/config.json"; + + qDebug() << "ConfigManager initialized, config file:" << m_configFile; +} + +bool ConfigManager::loadConfig(const QString& configFile) +{ + if (!configFile.isEmpty()) { + m_configFile = configFile; + } + + QFile file(m_configFile); + + // 如果文件不存在,创建默认配置 + if (!file.exists()) { + qWarning() << "Config file not found, creating default config:" << m_configFile; + return createDefaultConfig(); + } + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Failed to open config file:" << m_configFile; + return false; + } + + QByteArray jsonData = file.readAll(); + file.close(); + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error); + + if (error.error != QJsonParseError::NoError) { + qWarning() << "JSON parse error:" << error.errorString(); + return false; + } + + if (!doc.isObject()) { + qWarning() << "Config is not a JSON object"; + return false; + } + + QVariantMap root = doc.object().toVariantMap(); + + // 加载HTTP配置 + if (root.contains("http")) { + m_httpConfig = ChannelConfig::fromMap(root["http"].toMap()); + } else { + qWarning() << "Config missing 'http' section, creating default"; + m_httpConfig = getDefaultHttpConfig(); + } + + // 加载WebSocket配置 + if (root.contains("websocket")) { + m_websocketConfig = ChannelConfig::fromMap(root["websocket"].toMap()); + } else { + qWarning() << "Config missing 'websocket' section, creating default"; + m_websocketConfig = getDefaultWebSocketConfig(); + } + + qInfo() << "Config loaded from:" << m_configFile; + qInfo() << "HTTP endpoint:" << m_httpConfig.endpoint; + qInfo() << "WebSocket endpoint:" << m_websocketConfig.endpoint; + + emit configLoaded(); + + return true; +} + +bool ConfigManager::saveConfig() +{ + // 确保目录存在 + QFileInfo fileInfo(m_configFile); + QDir dir = fileInfo.dir(); + + if (!dir.exists()) { + if (!dir.mkpath(".")) { + qCritical() << "Failed to create config directory:" << dir.path(); + return false; + } + } + + QVariantMap root = { + {"http", m_httpConfig.toMap()}, + {"websocket", m_websocketConfig.toMap()}, + {"lastSaved", QDateTime::currentDateTime().toString(Qt::ISODate)} + }; + + QFile file(m_configFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + qCritical() << "Failed to open config file for writing:" << m_configFile; + return false; + } + + QJsonDocument doc = QJsonDocument::fromVariant(root); + file.write(doc.toJson(QJsonDocument::Indented)); + file.close(); + + qInfo() << "Config saved to:" << m_configFile; + emit configSaved(); + + return true; +} + +bool ConfigManager::createDefaultConfig() +{ + // 设置默认配置 + m_httpConfig = getDefaultHttpConfig(); + m_websocketConfig = getDefaultWebSocketConfig(); + + // 保存默认配置 + bool success = saveConfig(); + + if (success) { + qInfo() << "Default config created at:" << m_configFile; + } else { + qCritical() << "Failed to create default config"; + } + + return success; +} + +ChannelConfig ConfigManager::getDefaultHttpConfig() const +{ + ChannelConfig config; + + config.id = "http_channel"; + config.name = "SCADA数据接口"; + config.endpoint = "http://192.168.46.100:10080"; + config.timeout = 10000; + config.enabled = true; + config.autoConnect = true; + config.username = ""; + config.password = ""; + config.connected = false; + config.lastConnectTime = QDateTime(); + config.errorCount = 0; + + // 默认HTTP头 + config.headers = QVariantMap{ + {"Content-Type", "application/json"}, + }; + + return config; +} + +ChannelConfig ConfigManager::getDefaultWebSocketConfig() const +{ + ChannelConfig config; + + config.id = "websocket_channel"; + config.name = "实时数据推送"; + config.endpoint = "ws://192.168.46.100:10080/monitors/data/realtime/stream/"; + config.timeout = 10000; + config.enabled = true; + config.autoConnect = false; + config.connected = false; + config.lastConnectTime = QDateTime(); + config.errorCount = 0; + config.heartbeatInterval = 30000; + + return config; +} + +ChannelConfig ConfigManager::getHttpConfig() const +{ + return m_httpConfig; +} + +ChannelConfig ConfigManager::getWebSocketConfig() const +{ + return m_websocketConfig; +} + +void ConfigManager::setHttpConfig(const ChannelConfig& config) +{ + m_httpConfig = config; + emit httpConfigChanged(config); +} + +void ConfigManager::setWebSocketConfig(const ChannelConfig& config) +{ + m_websocketConfig = config; + emit websocketConfigChanged(config); +} + +QString ConfigManager::configFilePath() const +{ + return m_configFile; +} diff --git a/diagramCommunication/source/dataProcessor.cpp b/diagramCommunication/source/dataProcessor.cpp new file mode 100644 index 0000000..2117357 --- /dev/null +++ b/diagramCommunication/source/dataProcessor.cpp @@ -0,0 +1,77 @@ +// DataProcessor.cpp +#include "dataProcessor.h" +#include +#include +#include +#include + +DataProcessor* DataProcessor::instance() +{ + static DataProcessor* instance = nullptr; + static QMutex mutex; + + if (!instance) { + QMutexLocker locker(&mutex); + if (!instance) { + instance = new DataProcessor; + } + } + return instance; +} + +DataProcessor::DataProcessor(QObject* parent) + : QObject(parent) +{ + qDebug() << "DataProcessor initialized"; +} + +void DataProcessor::processData(const QVariant& data,int conType) +{ + qDebug() << "data_size:" << data.toJsonObject().size(); + + // 根据数据类型处理 + if (data.canConvert()) { + processJson(data.toJsonObject(),conType); + } + else if(data.canConvert()){ + processJsonArray(data); + } +} + +void DataProcessor::processNullData(const QVariant& data) +{ + emit httpProcessed("subscriptionTest",data); +} + +void DataProcessor::processJson( const QVariant& data,int conType) +{ + QJsonObject dataObj = data.toJsonObject(); + if(conType == 0){ + if(dataObj.contains("client_id")){ //实时数据相关 + emit httpProcessed("subscriptions",data); + } + } + else if(conType == 1){ + if(dataObj.contains("targets")){ //实时数据相关 + emit websocketProcessed(data); + } + } +} + +void DataProcessor::processJsonArray(const QVariant& data) +{ + emit httpProcessed("recommend",data); +} + +QVariant DataProcessor::getProcessedData(const QString& key) const +{ + QMutexLocker locker(&m_mutex); + return m_dataCache.value(key); +} + +void DataProcessor::clearAllData() +{ + QMutexLocker locker(&m_mutex); + m_dataCache.clear(); + qDebug() << "已清除所有处理数据"; +} diff --git a/diagramCommunication/source/httpAdapter.cpp b/diagramCommunication/source/httpAdapter.cpp new file mode 100644 index 0000000..f60b016 --- /dev/null +++ b/diagramCommunication/source/httpAdapter.cpp @@ -0,0 +1,260 @@ +// HttpAdapter.cpp +#include "httpChannel.h" +#include +#include +#include +#include +#include +#include +#include + +HttpAdapter::HttpAdapter(const ChannelConfig& config, QObject* parent) + : CommunicationChannel(config, parent) + , m_networkManager(new QNetworkAccessManager(this)) +{ + // 配置SSL + if (m_config.sslConfig.isNull()) { + QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); + sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + m_config.sslConfig = sslConfig; + } + + // 连接信号 + connect(m_networkManager, &QNetworkAccessManager::authenticationRequired, + this, &HttpAdapter::onAuthenticationRequired); + + qDebug() << "HttpAdapter created for" << m_config.channelId; +} + +HttpAdapter::~HttpAdapter() +{ + // 取消所有未完成的请求 + for (auto reply : m_pendingRequests.keys()) { + reply->abort(); + reply->deleteLater(); + } + m_pendingRequests.clear(); +} + +bool HttpAdapter::connectToHost() +{ + // HTTP协议无需建立持久连接,只需验证端点是否可达 + if (m_config.endpoint.isEmpty() || !m_config.endpoint.isValid()) { + emit errorOccurred("Invalid endpoint URL: " + m_config.endpoint.toString()); + return false; + } + + // 发送一个测试请求验证连接 + HttpRequest testRequest; + testRequest.method = GET; + + QNetworkRequest request(m_config.endpoint); + request.setSslConfiguration(m_config.sslConfig); + request.setRawHeader("User-Agent", "PowerSCADA/1.0"); + + QNetworkReply* reply = m_networkManager->get(request); + connect(reply, &QNetworkReply::finished, + this, [this, reply]() { + onReplyFinished(reply); + }); + + m_pendingRequests.insert(reply, testRequest); + + // 设置超时 + QTimer::singleShot(m_config.timeout, this, [this, reply]() { + if (reply && reply->isRunning()) { + reply->abort(); + emit errorOccurred("Connection timeout"); + } + }); + + emit connected(); + return true; +} + +bool HttpAdapter::disconnectFromHost() +{ + // 取消所有未完成的请求 + for (auto reply : m_pendingRequests.keys()) { + reply->abort(); + reply->deleteLater(); + } + m_pendingRequests.clear(); + + emit disconnected(); + return true; +} + +bool HttpAdapter::sendData(const QByteArray& data) +{ + HttpRequest request; + request.method = POST; + request.data = data; + request.contentType = "application/octet-stream"; + + return sendRequest(request); +} + +bool HttpAdapter::isConnected() const +{ + // HTTP是无状态协议,总是返回true表示可以发送请求 + return m_config.endpoint.isValid(); +} + +bool HttpAdapter::sendRequest(const HttpRequest& request) +{ + if (!m_config.endpoint.isValid()) { + emit errorOccurred("Invalid endpoint URL"); + return false; + } + + QNetworkRequest networkRequest(m_config.endpoint); + networkRequest.setSslConfiguration(m_config.sslConfig); + networkRequest.setRawHeader("User-Agent", "PowerSCADA/1.0"); + networkRequest.setRawHeader("Content-Type", request.contentType.toUtf8()); + + // 添加自定义头 + for (auto it = request.headers.begin(); it != request.headers.end(); ++it) { + networkRequest.setRawHeader(it.key().toUtf8(), it.value().toUtf8()); + } + + QNetworkReply* reply = nullptr; + + switch (request.method) { + case GET: + reply = m_networkManager->get(networkRequest); + break; + case POST: + reply = m_networkManager->post(networkRequest, request.data); + break; + case PUT: + reply = m_networkManager->put(networkRequest, request.data); + break; + case DELETE: + reply = m_networkManager->deleteResource(networkRequest); + break; + default: + emit errorOccurred("Unsupported HTTP method"); + return false; + } + + if (!reply) { + emit errorOccurred("Failed to create network request"); + return false; + } + + // 设置请求超时 + QTimer* timeoutTimer = new QTimer(this); + timeoutTimer->setSingleShot(true); + connect(timeoutTimer, &QTimer::timeout, this, [this, reply]() { + if (reply && reply->isRunning()) { + reply->abort(); + emit errorOccurred("Request timeout"); + } + }); + timeoutTimer->start(m_config.timeout); + + connect(reply, &QNetworkReply::finished, this, [this, reply, timeoutTimer]() { + timeoutTimer->stop(); + timeoutTimer->deleteLater(); + onReplyFinished(reply); + }); + + m_pendingRequests.insert(reply, request); + return true; +} + +void HttpAdapter::setAuthentication(const QString& username, const QString& password) +{ + m_username = username; + m_password = password; +} + +void HttpAdapter::setProxy(const QNetworkProxy& proxy) +{ + m_networkManager->setProxy(proxy); +} + +bool HttpAdapter::readDataPoints(const QStringList& pointIds) +{ + QJsonObject requestObj; + requestObj["command"] = "read"; + requestObj["points"] = QJsonArray::fromStringList(pointIds); + requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); + + HttpRequest request; + request.method = POST; + request.data = QJsonDocument(requestObj).toJson(); + request.contentType = "application/json"; + + return sendRequest(request); +} + +bool HttpAdapter::writeDataPoint(const QString& pointId, const QVariant& value) +{ + QJsonObject requestObj; + requestObj["command"] = "write"; + requestObj["point"] = pointId; + requestObj["value"] = QJsonValue::fromVariant(value); + requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); + + HttpRequest request; + request.method = POST; + request.data = QJsonDocument(requestObj).toJson(); + request.contentType = "application/json"; + + return sendRequest(request); +} + +bool HttpAdapter::sendControlCommand(const QString& deviceId, int command, const QVariant& param) +{ + QJsonObject requestObj; + requestObj["command"] = "control"; + requestObj["device"] = deviceId; + requestObj["cmd"] = command; + requestObj["param"] = QJsonValue::fromVariant(param); + requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); + + HttpRequest request; + request.method = POST; + request.data = QJsonDocument(requestObj).toJson(); + request.contentType = "application/json"; + + return sendRequest(request); +} + +void HttpAdapter::onReplyFinished(QNetworkReply* reply) +{ + if (!reply || !m_pendingRequests.contains(reply)) { + return; + } + + auto request = m_pendingRequests.take(reply); + + if (reply->error() != QNetworkReply::NoError) { + QString errorMsg = QString("HTTP error: %1 - %2") + .arg(reply->error()) + .arg(reply->errorString()); + emit errorOccurred(errorMsg); + } else { + QByteArray responseData = reply->readAll(); + emit dataReceived(responseData); + + // 记录日志 + qDebug() << "HTTP response received:" << reply->url().toString() + << "Status:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() + << "Size:" << responseData.size() << "bytes"; + } + + reply->deleteLater(); +} + +void HttpAdapter::onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator) +{ + if (!m_username.isEmpty() && !m_password.isEmpty()) { + authenticator->setUser(m_username); + authenticator->setPassword(m_password); + } else { + qWarning() << "Authentication required but no credentials provided"; + } +} diff --git a/diagramCommunication/source/httpChannel.cpp b/diagramCommunication/source/httpChannel.cpp new file mode 100644 index 0000000..407af14 --- /dev/null +++ b/diagramCommunication/source/httpChannel.cpp @@ -0,0 +1,168 @@ +// HttpChannel.cpp +#include "httpChannel.h" +#include +#include +#include +#include +#include +#include + +HttpChannel::HttpChannel(const ChannelConfig& config, QObject* parent) + : BaseChannel(config, parent) +{ +} + +bool HttpChannel::connect() +{ + // HTTP无需建立持久连接 + emit connected(); + return true; +} + +bool HttpChannel::disconnect() +{ + // HTTP无需断开持久连接 + emit disconnected(); + return true; +} + +bool HttpChannel::isConnected() const +{ + return m_config.endpoint.isValid(); // 只要端点有效就认为"可连接" +} + +bool HttpChannel::send(const QByteArray& data) +{ + return post(data); +} + +bool HttpChannel::get(const QString& path,const QVariantMap& queryMap) +{ + if (!m_config.endpoint.isValid()) { + emit errorOccurred("Invalid endpoint"); + return false; + } + + QUrl url; + if(path.contains("http")){ //url全部输入 + url = QUrl(path); + } + else{ //url部分输入 + url = m_config.endpoint; + if (!path.isEmpty()) { + url.setPath(url.path() + path); + } + + if(!queryMap.isEmpty()){ + QUrlQuery query; + for(auto iter = queryMap.begin();iter != queryMap.end();++iter){ + query.addQueryItem(iter.key(),iter.value().toString()); + }; + url.setQuery(query); + } + } + + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + + QObject::connect(manager, &QNetworkAccessManager::authenticationRequired, + [this](QNetworkReply* reply, QAuthenticator* authenticator) { + if (!m_username.isEmpty() && !m_password.isEmpty()) { + authenticator->setUser(m_username); + authenticator->setPassword(m_password); + } + }); + + if(url.isEmpty()) + qDebug()<<"http url empty!"; + + QNetworkRequest request(url); + + // 设置头 + // for (auto it = m_headers.begin(); it != m_headers.end(); ++it) { + // request.setRawHeader(it.key().toUtf8(), it.value().toUtf8()); + // } + + QNetworkReply* reply = manager->get(request); + + QObject::connect(reply, &QNetworkReply::finished, this,[this, reply, manager]() { + if (reply->error() == QNetworkReply::NoError) { + QByteArray data = reply->readAll(); + emit dataReceived(data); + } else { + emit errorOccurred(reply->errorString()); + qDebug()<errorString(); + } + + reply->deleteLater(); + manager->deleteLater(); + }); + + return true; +} + +bool HttpChannel::post(const QByteArray& data, const QString& path) +{ + if (!m_config.endpoint.isValid()) { + emit errorOccurred("Invalid endpoint"); + return false; + } + + QUrl url; + if(path.contains("http")){ //url全部输入 + url = QUrl(path); + } + else{ //url部分输入 + url = m_config.endpoint; + if (!path.isEmpty()) { + url.setPath(url.path() + path); + } + } + + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + for (auto it = m_headers.begin(); it != m_headers.end(); ++it) { + request.setRawHeader(it.key().toUtf8(), it.value().toUtf8()); + } + + QNetworkReply* reply = manager->post(request, data); + + QObject::connect(reply, &QNetworkReply::finished,this,[this, reply, manager]() { + if (reply->error() == QNetworkReply::NoError) { + QByteArray data = reply->readAll(); + emit dataReceived(data); + } else { + emit errorOccurred(reply->errorString()); + } + + reply->deleteLater(); + manager->deleteLater(); + }); + + return true; +} + +bool HttpChannel::put(const QByteArray& data, const QString& path) +{ + // 类似post的实现 + return post(data, path); // 简化处理 +} + +bool HttpChannel::deleteResource(const QString& path) +{ + // 类似get的实现 + return get(path); // 简化处理 +} + +void HttpChannel::setBasicAuth(const QString& username, const QString& password) +{ + m_username = username; + m_password = password; +} + +void HttpChannel::setHeader(const QString& name, const QString& value) +{ + m_headers[name] = value; +} diff --git a/diagramCommunication/source/uiCommunicationBus.cpp b/diagramCommunication/source/uiCommunicationBus.cpp new file mode 100644 index 0000000..d9259d9 --- /dev/null +++ b/diagramCommunication/source/uiCommunicationBus.cpp @@ -0,0 +1,191 @@ +// UiCommunicationBus.cpp +#include "uiCommunicationBus.h" +#include "communicationManager.h" +#include "dataProcessor.h" +#include +#include +#include +#include + +UiCommunicationBus* UiCommunicationBus::instance() +{ + static UiCommunicationBus* instance = nullptr; + static QMutex mutex; + + if (!instance) { + QMutexLocker locker(&mutex); + if (!instance) { + instance = new UiCommunicationBus; + } + } + return instance; +} + +UiCommunicationBus::UiCommunicationBus(QObject* parent) + : QObject(parent) +{ + // 连接到CommunicationManager + CommunicationManager* comm = CommunicationManager::instance(); + + // 连接HTTP信号 + connect(comm, &CommunicationManager::httpDataReceived, + this, &UiCommunicationBus::onHttpDataReceived); + + // 连接WebSocket信号 + connect(comm, &CommunicationManager::websocketDataReceived, + this, &UiCommunicationBus::onWebSocketDataReceived); + + // 连接DataProcessor信号 + DataProcessor* processor = DataProcessor::instance(); + connect(processor, &DataProcessor::httpProcessed, + this, [this](const QString& dataType, const QVariant& data) { + // if(dataType == "recommend"){ + // broadcastToUis(dataType, data); + // } + emit httpDataProcessed(dataType,data); + }); + + connect(processor, &DataProcessor::websocketProcessed, + this, [this](const QVariant& data) { + emit websocketDataProcessed(data); + }); + + qDebug() << "UiCommunicationBus initialized"; +} + +void UiCommunicationBus::sendHttpRequest(const QString& endpoint, const QVariant& data,const QString& method,const QVariantMap& query) +{ + CommunicationManager* comm = CommunicationManager::instance(); + + QJsonDocument doc = QJsonDocument::fromVariant(data); + bool success = comm->sendHttpRequest(endpoint, doc.toJson(),method,query); + + if (success) { + qDebug() << "HTTP request send success:" << endpoint; + } else { + qWarning() << "HTTP request send fail:" << endpoint; + } +} + +void UiCommunicationBus::sendHttpRequestNoReply(const QString& endpoint, const QVariant& data) +{ + CommunicationManager* comm = CommunicationManager::instance(); + + QJsonDocument doc = QJsonDocument::fromVariant(data); + comm->sendHttpRequest(endpoint, doc.toJson()); + + qDebug() << "无回复HTTP请求已发送:" << endpoint; +} + +void UiCommunicationBus::onHttpDataReceived(const QByteArray& data) +{ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(data, &error); + + if (error.error != QJsonParseError::NoError) { + qWarning() << "HTTP响应解析失败:" << error.errorString(); + return; + } + + QVariant response = doc.toVariant(); + + if (response.typeId() == QMetaType::QVariantMap) { + QVariantMap responseMap = response.toMap(); + DataProcessor* processor = DataProcessor::instance(); + + QString state = responseMap.value("msg").toString(); + if(state == "success"){ + if(responseMap.contains("payload")){ + processor->processData(responseMap.value("payload")); + } + } + else{ + processor->processNullData(QVariant()); + } + } + + qDebug() << "HTTP "; +} + +void UiCommunicationBus::onWebSocketDataReceived(const QByteArray& data) +{ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(data, &error); + + if (error.error != QJsonParseError::NoError) { + qWarning() << "WebSocket数据解析失败:" << error.errorString(); + return; + } + + QVariant response = doc.toVariant(); + + if (response.typeId() == QMetaType::QVariantMap) { + QVariantMap responseMap = response.toMap(); + DataProcessor* processor = DataProcessor::instance(); + + QString state = responseMap.value("msg").toString(); + if(state == "success"){ + if(responseMap.contains("payload")){ + processor->processData(responseMap.value("payload"),1); + } + } + } + + qDebug() << "WebSocket数据已处理"; +} + +void UiCommunicationBus::sendToUi(const QString& uiId, const QString& action, const QVariant& data) +{ + QMutexLocker locker(&m_mutex); + + if (!m_uiObjects.contains(uiId)) { + qWarning() << "UI未注册:" << uiId; + return; + } + + QObject* uiObject = m_uiObjects[uiId]; + + // 尝试调用通用的消息处理槽 + bool success = QMetaObject::invokeMethod(uiObject, "onMessage", + Qt::QueuedConnection, + Q_ARG(QString, action), + Q_ARG(QVariant, data)); + + if (!success) { + qWarning() << "调用UI消息处理失败:" << uiId << "action:" << action; + } +} + +void UiCommunicationBus::broadcastToUis(const QString& action, const QVariant& data) +{ + QMutexLocker locker(&m_mutex); + + for (auto it = m_uiObjects.begin(); it != m_uiObjects.end(); ++it) { + QObject* uiObject = it.value(); + + QMetaObject::invokeMethod(uiObject, "onReceiveHttpData", + Q_ARG(QString, action), + Q_ARG(QVariant, data)); + } + + qDebug() << "消息广播:" << action << "接收者:" << m_uiObjects.size(); +} + +void UiCommunicationBus::registerUi(const QString& uiId, QObject* uiObject) +{ + if (uiId.isEmpty() || !uiObject) { + qWarning() << "注册UI失败: 参数无效"; + return; + } + + QMutexLocker locker(&m_mutex); + m_uiObjects[uiId] = uiObject; + qDebug() << "UI已注册:" << uiId; +} + +void UiCommunicationBus::unregisterUi(const QString& uiId) +{ + QMutexLocker locker(&m_mutex); + m_uiObjects.remove(uiId); + qDebug() << "UI已注销:" << uiId; +} diff --git a/diagramCommunication/source/webSocketChannel.cpp b/diagramCommunication/source/webSocketChannel.cpp new file mode 100644 index 0000000..b16f892 --- /dev/null +++ b/diagramCommunication/source/webSocketChannel.cpp @@ -0,0 +1,123 @@ +// WebSocketChannel.cpp +#include "webSocketChannel.h" +#include + +WebSocketChannel::WebSocketChannel(const ChannelConfig& config, QString sessionId, QObject* parent) + : BaseChannel(config, parent) + , m_webSocket(new QWebSocket) +{ + QObject::connect(m_webSocket, &QWebSocket::connected, + this, &WebSocketChannel::onConnected); + QObject::connect(m_webSocket, &QWebSocket::disconnected, + this, &WebSocketChannel::onDisconnected); + QObject::connect(m_webSocket, &QWebSocket::textMessageReceived, + this, &WebSocketChannel::onTextMessageReceived); + QObject::connect(m_webSocket, &QWebSocket::binaryMessageReceived, + this, &WebSocketChannel::onBinaryMessageReceived); + m_sessionId = sessionId; +} + +WebSocketChannel::~WebSocketChannel() +{ + if(m_webSocket){ + m_webSocket->disconnect(); + if (m_webSocket->state() == QAbstractSocket::ConnectedState) { + m_webSocket->close(); + } + + m_webSocket->deleteLater(); + m_webSocket = nullptr; + } +} + +bool WebSocketChannel::connect() +{ + if (m_webSocket->state() == QAbstractSocket::ConnectedState) { + return true; + } + + if (!m_config.endpoint.isValid()) { + emit errorOccurred("Invalid endpoint"); + return false; + } + + // 1. 从 WebSocket URL 提取 host 和 port + QUrl url(m_config.endpoint); + //QString host = url.host(); + //int port = url.port(10080); // 默认端口 80 + + // 2. 构建 Origin URL + //QString origin = QString("http://%1:%2").arg(host).arg(port); + + // 3. 创建 QNetworkRequest 并设置 Origin + QNetworkRequest request(url); + //request.setRawHeader("Origin", origin.toUtf8()); + + // 4. 使用 QWebSocket 连接 + m_webSocket->open(request); + + qDebug() << "WebSocket URL:" << m_config.endpoint; + //qDebug() << "Origin:" << origin; + return true; +} + +bool WebSocketChannel::disconnect() +{ + if (m_webSocket->state() != QAbstractSocket::UnconnectedState) { + m_webSocket->close(); + } + return true; +} + +bool WebSocketChannel::isConnected() const +{ + return m_webSocket->state() == QAbstractSocket::ConnectedState; +} + +bool WebSocketChannel::send(const QByteArray& data) +{ + if (!isConnected()) { + emit errorOccurred("WebSocket not connected"); + return false; + } + + qint64 sent = m_webSocket->sendBinaryMessage(data); + return sent == data.size(); +} + +bool WebSocketChannel::sendText(const QString& text) +{ + if (!isConnected()) { + emit errorOccurred("WebSocket not connected"); + return false; + } + + qint64 sent = m_webSocket->sendTextMessage(text); + return sent == text.size(); +} + +void WebSocketChannel::setWebSocketConfig(const WebSocketConfig& config) +{ + m_wsConfig = config; +} + +void WebSocketChannel::onConnected() +{ + emit connected(m_sessionId); +} + +void WebSocketChannel::onDisconnected() +{ + emit disconnected(m_sessionId); +} + +void WebSocketChannel::onTextMessageReceived(const QString& message) +{ + emit textMessageReceived(message,m_sessionId); + emit dataReceived(message.toUtf8(),m_sessionId); +} + +void WebSocketChannel::onBinaryMessageReceived(const QByteArray& message) +{ + emit dataReceived(message,m_sessionId); +} diff --git a/diagramUtils/CMakeLists.txt b/diagramUtils/CMakeLists.txt new file mode 100644 index 0000000..4b3c9c2 --- /dev/null +++ b/diagramUtils/CMakeLists.txt @@ -0,0 +1,70 @@ +project(diagramUtils) + +set(DIAGRAMUTILS_HEADER_FILES + include/logger.h + include/dataManager.h + include/componentIconManager.h + include/basePropertyManager.h + include/projectModelManager.h + include/projectManager.h + #../common/include/global.h + ../common/include/baseProperty.h + ../common/include/compiler.hpp + ../common/include/export.hpp + ../common/include/operatingSystem.hpp + + ../common/core_model/types.h + ../common/core_model/topology.h + ../common/core_model/diagram.h + ../common/backend/project_model.h + ../common/backend/meta_model.h + ../common/frontend/monitor_item.h +) + +set(DIAGRAMUTILS_SOURCE_FILES + source/logger.cpp + source/dataBase.cpp + source/dataManager.cpp + source/basePropertyManager.cpp + source/projectModelManager.cpp + source/projectManager.cpp + source/componentIconManager.cpp + #../common/source/global.cpp + ../common/source/baseProperty.cpp +) + + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_library(diagramUtils SHARED + MANUAL_FINALIZATION + ${DIAGRAMUTILS_HEADER_FILES} + ${DIAGRAMUTILS_SOURCE_FILES} + ) +else() + add_library(diagramUtils SHARED + ${DIAGRAMUTILS_HEADER_FILES} + ${DIAGRAMUTILS_SOURCE_FILES} + ) +endif() + +target_link_libraries(diagramUtils PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) + +target_link_libraries(diagramUtils PRIVATE Qt6::Xml) +target_link_libraries(diagramUtils PRIVATE Qt6::Network) +target_link_libraries(diagramUtils PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES}) + +option(BUILD_SHARED_LIBS "Build as shared library" ON) + + + +target_include_directories(diagramUtils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_compile_definitions(diagramUtils + PUBLIC + DIAGRAM_DESIGNER_SHARED + PRIVATE + DIAGRAM_DESIGNER_EXPORTS + #QT_NO_KEYWORDS +) diff --git a/diagramUtils/include/basePropertyManager.h b/diagramUtils/include/basePropertyManager.h new file mode 100644 index 0000000..2de6b47 --- /dev/null +++ b/diagramUtils/include/basePropertyManager.h @@ -0,0 +1,68 @@ +#ifndef BASEPROPERTYMANAGER_H +#define BASEPROPERTYMANAGER_H + +#include +#include +#include "export.hpp" +/****元件属性数据管理类*****/ + +class BaseProperty; +class BaseModelProperty; +class BayProperty; +class DiagramEditorItemProperty; + +class DIAGRAM_DESIGNER_PUBLIC BasePropertyManager : public QObject +{ + Q_OBJECT + +public: + explicit BasePropertyManager(QObject *parent = nullptr); + ~BasePropertyManager(); + + static BasePropertyManager& instance(); + + //===========================元件实时数据================================ + void insertEntityData(QUuid,BaseProperty*); + BaseProperty* findEntityData(QUuid); + void deleteEntityData(QUuid); + QMap getEntityData() const; + + void insertBaseEntityData(QUuid,BaseModelProperty*); + BaseModelProperty* findBaseEntityData(QUuid); + void deleteBaseEntityData(QUuid); + QMap getBaseEntityData() const; + + void insertBaseBayData(QUuid,BayProperty*); //基模间隔 + BayProperty* findBaseBayData(QUuid); + void deleteBaseBayData(QUuid); + QMap getBaseBayData() const; + + void insertBayData(QUuid,BayProperty*); + BayProperty* findBayData(QUuid); + void deleteBayData(QUuid); + QMap getBayData() const; + + void insertTempEditorData(QUuid,DiagramEditorItemProperty*); //临时预览对象数据 + DiagramEditorItemProperty* findTempEditorData(QUuid); + void deleteTempEditorData(QUuid); + QMap getTempEditorData() const; + + void insertEditorData(QUuid,DiagramEditorItemProperty*); //预览对象数据 + DiagramEditorItemProperty* findEditorData(QUuid); + void deleteEditorData(QUuid); + QMap getEditorData() const; + void clearEditorData(); +signals: + void dataCreated(QString uuid); + void dataChanged(QString uuid); +public slots: + void onDataDelete(QString uuid); +private: + QMap m_entityData; //工程模实例化元件的唯一数据 + QMap m_baseEntityData; //基模实例元件数据 + QMap m_baseBayData; //基模间隔数据 + QMap m_bayData; //间隔数据 + QMap m_editorTempData; //编辑时临时预览对象 + QMap m_editorData; //编辑时预览对象 +}; +#endif // BASEPROPERTYMANAGER_H diff --git a/diagramUtils/include/componentIconManager.h b/diagramUtils/include/componentIconManager.h new file mode 100644 index 0000000..1047f2b --- /dev/null +++ b/diagramUtils/include/componentIconManager.h @@ -0,0 +1,27 @@ +#ifndef COMPONENTICONMANAGER_H +#define COMPONENTICONMANAGER_H + +#include +//#include "global.h" +#include "export.hpp" +#include "common/core_model/types.h" +/****图标管理类 + * 对各种状态图标的管理 +*****/ + +class DIAGRAM_DESIGNER_PUBLIC ComponentIconManager : public QObject +{ + Q_OBJECT +public: + explicit ComponentIconManager(QObject *parent = nullptr); + ~ComponentIconManager(); + static ComponentIconManager& instance(); +public: + void initialData(); + void addIcon(QString tpe,DiagramMode mode,VariantIcon varIcon,QString iconPath); + QString getIconPath(QString tpe,DiagramMode mode,VariantIcon varIcon); +private: + QMap>> _mapIcon; + bool _init; +}; +#endif // COMPONENTICONMANAGER_H diff --git a/diagramUtils/include/dataBase.h b/diagramUtils/include/dataBase.h new file mode 100644 index 0000000..70c31fc --- /dev/null +++ b/diagramUtils/include/dataBase.h @@ -0,0 +1,223 @@ +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include +#include +#include +//#include "global.h" +#include "common/backend/project_model.h" +#include "common/core_model/topology.h" +#include "common/backend/meta_model.h" +#include "common/frontend/monitor_item.h" +#include "common/core_model/diagram.h" +#include "export.hpp" + + +class DIAGRAM_DESIGNER_PUBLIC DataBase +{ + //Q_OBJECT +public: + DataBase(); + ~DataBase(); + QSqlQuery executeSQL(const QString& strSQL, bool isDDl = false,const QVariantList& params = {}, bool useTranscation = false); //ddl:create,delete,alter etc + /** + * @brief 多条批量SQL语句执行接口 + * @param sqlStatements SQL语句列表 + * @param paramsList 参数列表(要与SQL语句一一对应) + */ + QSqlQuery executeBatchSQL(const QStringList& sqlStatements, bool createOrDrop = false, + const QList& paramsList = QList(), bool useTranscation = false); + static DataBase* GetInstance(); + +public: + bool insertPage(QString tag,QString name,QJsonObject label,QJsonObject context,QString description,int op); + bool insertStation(int zoneId,QString name,QString description,bool isLocal,int op); + bool insertGrid(QString name,QString description,int op); + bool insertZone(int grid_id,QString name,QString description,int op); + bool insertTopologic(QUuid uuid_from,QUuid uuid_to,QJsonObject context,int flag,QString description,int op); + + QString getGridNameById(int); + QString getZoneNameById(int); + QString getStationNameById(int); + + QList getAllGrid(); + QList getAllZone(); + QList getAllStation(); + + QList getAllTopologics(); + int topologicExist(QUuid fromItem,QUuid toItem); + TopologyInfo getTopologicById(int id); + bool deleteTopologic(QUuid fromPin,QUuid toPin); + /*********************************************************************************/ + bool updateComponent(QUuid uuid,QString tag,QString name,QJsonObject context,bool inService,int state,int status); + bool insertComponent(QUuid uuid,QString modelName,QString nspath,QString tag,QString name,QString description,QString grid,QString zone,QString station,int type,bool inService,int state,int status,QJsonObject connected_bus,QJsonObject label,QJsonObject context,int op); + bool insertDynamicProperty(QUuid uuid,GroupStateValue groupValue); + bool updateDynamicProperty(QUuid uuid,GroupStateValue groupValue); + ComponentInfo getComponentInfoByUuid(QString uuid); + QList getAllComponents(); + bool componentExist(QString uuid); + bool deleteComponent(QString uuid); + + QMap getAllComponentType(); //获取所有支持的元件种类 + /*********************************************************************************/ + int getPageIdByName(QString name); + bool updatePage(QString tag,QString name,QJsonObject context); + QJsonObject getPageContextByName(QString name); + QList getAllPage(); + /*********************************************************************************/ + bool insertBay(QUuid uuid,QString name,QString tag,QString type,double unom,double fla,double capacity,QString description,bool inService,int nState,QString grid,QString zone,QString station,QJsonObject business,QJsonObject fromUuid,QJsonObject toUuid,QJsonObject protect,QJsonObject faultRec,QJsonObject status,QJsonObject dynSense,QJsonObject instruct,QJsonObject etc,QList components,QJsonObject context); + bool updateBay(QUuid uuid,QString name,QString tag,double unom,double fla,double capacity,QString description,bool inService,int nState,QJsonObject business,QJsonObject fromUuid,QJsonObject toUuid,QJsonObject protect,QJsonObject faultRec,QJsonObject status,QJsonObject dynSense,QJsonObject instruct,QJsonObject etc,QList components,QJsonObject context); + BayInfo getBay(QUuid uuid); + QList getAllBay(); + bool ifBayExist(QUuid uuid); + bool deleteBay(QUuid uuid); + /*********************************************************************************/ + bool insertMeasurement(QString name,QString tag,int type,QJsonObject dataSource,QJsonObject eventPlan,QJsonObject binding,int size,QUuid bayId,QUuid componentId); + bool updateMeasurement(QString name,int type,QJsonObject dataSource,QJsonObject eventPlan,QJsonObject binding,int size,QUuid componentId); + bool delteMeasurement(QString name,QUuid componentId); + bool ifMeasureExist(QString name,QUuid componentId); + bool ifBayMeasureExist(QString name,QUuid bayId); + QList getMeasurement(QUuid componentId); + QList getBayMeasurement(QUuid bayUuid); //返回间隔下的所有量测 + QMap getAllMeasurements(); + /*********************************************************************************/ + bool insertExtraProperty(ExtraProperty); //属性层级与连接信息 + bool updateExtraProperty(ExtraProperty); + bool deleteExtraProperty(QString code); + bool ifExtraPropertyExist(QString code); + QList getCompoExtraProperty(QUuid uid); //获取component中的层级信息 + QList getBayExtraProperty(QString bayTag); //获取间隔的层级信息 + QList getAllExtraProperty(); + /*********************************************************************************/ + bool deleteComponentById(int id); + + QJsonObject QstringToJson(QString jsonString); + QList parseUuidArray(const QString& pgArray); //转化UUID[]类型 +public: + //***********元模 + bool getAttributeGroup(); //获取属性组信息 + bool getDataType(); //获取数据类型信息 + bool getModelType(); //获取模型类型 + bool getModelGroup(); //获取模型组 + bool getAttribute(); //获取属性 + bool getModelAttribute(); //获取模型-属性对照组 + bool getModelAttributePublic(); //获取公共属性组 + bool getModelConnectivity(); //获取连接性 + + QMap AttributeGroup() const {return _attributeGroup;} + QMap DataType() const {return _dataType;} + QMap ModelType() const {return _modelType;} + QMap ModelGroup() const {return _modelGroup;} + QMap Attribute() const {return _attribute;} + QMap ModelAttribute() const {return _modelAttribute;} + QMap ModelAttributePublic() const {return _modelAttributePublic;} + QMap ModelConnectivity() const {return _modelConnectivity;} + //***********工程模 + bool insertProjectSetting(const QString& baseModel,const QString& modelName,QJsonObject setting); //插入工程模设置信息(图标等选择信息) + bool updateProjectSetting(const QString& baseModel,const QString& modelName,QJsonObject setting); + QJsonObject getProjectSetting(const QString& baseModel,const QString& modelName); + QStringList getProjectWithinBase(const QString& baseModel); //获取基模下的所有工程模名称 + bool deleteProjectSetting(const QString& baseModel,const QString& modelName); + + bool createProjectManager(); //生成记录表,包含工程模名称,属性组名,启用和关闭的属性字段(json类型)[一个属性组建一个表] + bool insertProjectManager(const QString& name,const QString& tag,const QString& metaModel,const QString& groupName,int linkType,QJsonObject checkState,bool isPublic = false); + bool updateCheckState(const QString& tableName,QJsonObject checkState); //更新属性选中状态 + QMap getProjectFromManager(const QString& sMeta); //返回元模下已创建的工程名 + QMap getCheckStateFromManager(const QString& sProject); //获取当前工程模型所有属性的选择状态 <属性名,选择状态> + QMap getPublicStateFromManager(); //获取公共属性组 + QMap getProjectTableName(const QString& sProject); //获取当前工程模型下所有表信息 + bool createDynamicTable(const QString&, const QStringList&); + bool deleteProjectModel(const QString&); + bool ifDynamicTableExist(const QString&); //动态表是否存在 + bool updateProjectName(const QString& newTable,const QString& newPro,const QString& oldTable); //更新mangager工程模名称 + bool alterTableName(const QString& oldTable,const QString& newTable); //修改表名 + bool updateComponentModelName(const QString& strOld,const QString& strNew); //修改component中的模型名 + + bool deleteTable(const QString&); //删除表 + bool deleteRecordFromManager(const QString& sProject,const QString& sGroup); //删除某个模型下的组 + bool modifyProjectTable(QString sTable,QMap mOld,QMap mNew); + + QStringList ifModelOccupy(const QString&); //判断模型是否被使用 + //**********使用工程模 + QMap getAllProjectModel(); //获取所有工程模<名称,图元类型> + QMap getModelInfo(const QString&); //获取模型信息 + QMap getPublicInfo(); //获取公共属性组信息 + QMap getProjectModelGroupInfo(const QString&); //获取指定工程模所有属性组信息<属性组名,属性信息> + QMap selectGroupPropertyByState(const QString& tableName,QMap mapPro); //返回属性组表中的信息 + PropertyValueInfo selectGroupPropertyByValue(const QString& tableName,QUuid uuid,PropertyValueInfo value); //通过已有结构更新数据 + + QList getMeasureAttributeTypes(); //获取所有量测属性 + + /************************************运行模式(监控)*********************************************/ + bool insertMonitor(QUuid uid,QString tag,QString name,QString parent,QJsonObject context); + QUuid getMonitorIdByName(QString name); + bool updateMonitor(QString tag,QJsonObject context); + QJsonObject getMonitorContextByTag(QString tag); + QList getAllMonitor(); + + bool insertHMI(QUuid uid,QString tag,QString name,QJsonObject context); + QUuid getHMIdByName(QString name); + bool updateHMI(QString tag,QJsonObject context); + QJsonObject getHMIContextByTag(QString tag); + QList getAllHMI(); + + bool insertHMIimage(int baseType,QString imageName,QByteArray hash256,QByteArray svgData); //hmi使用的图片 + bool insertHMIimagesBatch(const QVector& imageList); + bool insertHMIimagesWithCheck(const QVector& imageList); // 智能批量插入(先查重) + QVector batchCheckHMIimagesExists(const QVector& hashList); + QByteArray findHMIimage(QByteArray hash256); + QList getAllHMIimage(); + + + bool inserHMIimageRef(QUuid hmiId,QString model,QByteArray hash256,int slot); + bool insertHMIimageRefBatch(const QList& refList); + QList getHMIRefAll(QUuid hmiId); //获取hmi所有资源信息 + QList getHMIRef(QUuid hmiId,QString model); + bool removeHMIRef(QUuid hmiId,QString model); //移除hmi中某类模型的图片引用 + bool removeHMIRefAll(QUuid hmiId); //移除某个HMI中所有引用关系 + +public: + /***********************************editor编辑器**********************************************/ + bool insertEditorProject(QUuid,QString,QString); + QList getAllEditorProject(); + bool deleteEditorProject(QString); + bool ifEditorProjectExist(QString name); + /***********************************baseSetting**********************************************/ + bool insertBaseSetting(QUuid,QString,QString,QByteArray,QUuid,QString ts); + bool updateBaseSetting(QUuid,QByteArray context,QString ts); + QByteArray getBaseSettingByUid(QUuid); + EditorBaseSettingInfo getBaseSettingInfo(QUuid); + QList getAllBaseSetting(); + bool deleteBaseSetting(QUuid); + bool ifBaseSettingExist(QUuid); +private: + QMap _attributeGroup; //属性组的组 + QMap _dataType; //数据类型组 + QMap _modelType; //模型类型 + QMap _modelGroup; //模型组 + QMap _attribute; //属性组 + QMap _modelAttribute; //模型-属性对照组 + QMap _modelAttributePublic; //公共属性组 + QMap _modelConnectivity; //连接性组 + + QMap _componentType; //存储系统支持的类型列表 +private: + void initial(); + //bool createProjectDB(); + //void initialProjectDB(); + void readXML(); + static DataBase* dbInstance; + static int _id; + QSqlDatabase db; + //QSqlDatabase prodb; + QString m_sFileName; + QString _DataBaseType; + QString _DataBaseName; + //QString _ProjectDB; //工程模数据库名 + QString _HostName; + int _Port; + QString _UserName; + QString _PassWord; +}; +#endif // DATABASE_H diff --git a/diagramUtils/include/dataManager.h b/diagramUtils/include/dataManager.h new file mode 100644 index 0000000..186897f --- /dev/null +++ b/diagramUtils/include/dataManager.h @@ -0,0 +1,38 @@ +#ifndef DATAMANAGER_H +#define DATAMANAGER_H + +#include +//#include "global.h" +#include "common/backend/project_model.h" +#include "export.hpp" +/****数据管理类 + * 对模型数据的集中分发、更新 +*****/ +typedef QMap ModleStateMap; +typedef QMap ModelDataMap; + +class DIAGRAM_DESIGNER_PUBLIC DataManager : public QObject +{ + Q_OBJECT + +public: + explicit DataManager(QObject *parent = nullptr); + ~DataManager(); + + static DataManager& instance(); +public: + void initialModelState(bool refresh = false); + void initialModelData(bool refresh = false); + + void updateModelData(const QString& sModel,QUuid uuid,const QString& sGroup,QMap mapPro); + + ModleStateMap& modelState(); + ModelDataMap& modelData(); +private: + ModleStateMap _modelStateInfo; //接收的模型结构信息(可直接返回引用) + ModelDataMap _modleDataInfo; //模型实时数据(使用接口获取数据) + + bool _stateInitialised; + bool _dataInitialised; +}; +#endif // DATAMANAGER_H diff --git a/diagramUtils/include/logger.h b/diagramUtils/include/logger.h new file mode 100644 index 0000000..4be132a --- /dev/null +++ b/diagramUtils/include/logger.h @@ -0,0 +1,70 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include "export.hpp" + +// 日志宏定义 +#define LOG(level, module, message) Logger::instance().log(Logger::level, module, message) +#define LOG_DEBUG(module, message) LOG(DEBUG, module, message) +#define LOG_INFO(module, message) LOG(INFO, module, message) +#define LOG_WARN(module, message) LOG(WARNING, module, message) +#define LOG_ERROR(module, message) LOG(ERROR, module, message) +#define LOG_FATAL(module, message) LOG(FATAL, module, message) + +class DIAGRAM_DESIGNER_PUBLIC Logger : public QObject +{ + Q_OBJECT + +public: + enum LogLevel + { + FATAL = 0, + ERROR, + WARNING, + INFO, + DEBUG + }; + + //获取单例实例 + static Logger& instance(); + void log(LogLevel, const QString&, const QString&); + +private: + explicit Logger(); + ~Logger(); + //禁止拷贝 + Logger(const Logger&) = delete; //delete关键字表示该函数不可用,包括编译器自动生成的函数 + Logger& operator=(const Logger&) = delete; + + void initialize(); + void loadConfig(/*const QString&*/); //本系统是通过Settings类进行配置文件信息读取 + void setLogFile(const QString&); + void shutdown(); + + void writeToFile(const QString&); + void rollLogFiles(); //当文件大小超过设置上线时会触发'滚动' + QString formatLogMessage(LogLevel, const QString&, const QString&); + + struct LogEntry + { + QDateTime time; + LogLevel level; + QString module; + QString message; + Qt::HANDLE threadId; + }; + + //配置参数 + LogLevel m_logLevel; + QString m_logFilePath; + QFile m_logFile; + qint64 m_maxFileSize; + int m_maxBackupFiles; + bool m_outputToConsole; + bool m_outputOtFile; +}; + +#endif //LOGGER_H diff --git a/diagramUtils/include/projectManager.h b/diagramUtils/include/projectManager.h new file mode 100644 index 0000000..c3bd86d --- /dev/null +++ b/diagramUtils/include/projectManager.h @@ -0,0 +1,44 @@ +#ifndef PROJECTMANAGER_H +#define PROJECTMANAGER_H + +#include +//#include "global.h" +#include "export.hpp" +#include "common/core_model/diagram.h" +/****工程管理类 + * 对工程进行管理 +*****/ + +class DIAGRAM_DESIGNER_PUBLIC ProjectManager : public QObject +{ + Q_OBJECT +public: + explicit ProjectManager(QObject *parent = nullptr); + ~ProjectManager(); + static ProjectManager& instance(); + + void saveEditorDataToDB(QUuid,const QString&,const QString&,QByteArray,QString autor,QString sTime); + QByteArray getEditorBaseSettingByUid(QUuid); + + bool createEditorProject(const QString&); //创建项目 + void unloadEditorProject(const QString&); //卸载项目 + + void openSetting(const QString&); + void saveEditor(const QString&); + bool deleteEditor(const QString&,QUuid); //删除当前版本 + + void loadBaseSetting(const QString& str,QUuid id); //加载wizard与基模拓扑设置 + + QList getBaseSettingsByProject(const QString&); //获取工程名对应的设置列表 + EditorBaseSettingInfo getBaseSetting(QUuid); //获取设置 +signals: + void prepareUnloadProject(const QString&); + void prepareOpenSetting(const QString& str); + void createNewEditor(const QString& str,QUuid id); + void editorSaved(const QString& strPro,const QString& autor,QUuid uid,QString sTime); + + void prepareSaveEditor(QString); + void prepareLoadBaseSetting(const QString& str,QUuid id); + void prepareDeleteBaseSetting(const QString& str,QUuid id); +}; +#endif // PROJECTMANAGER_H diff --git a/diagramUtils/include/projectModelManager.h b/diagramUtils/include/projectModelManager.h new file mode 100644 index 0000000..afdf2b2 --- /dev/null +++ b/diagramUtils/include/projectModelManager.h @@ -0,0 +1,64 @@ +#ifndef PROJECTMODELMANAGER_H +#define PROJECTMODELMANAGER_H + +#include +#include +#include "export.hpp" +//#include "global.h" +#include "common/backend/project_model.h" +#include "common/frontend/monitor_item.h" +/****工程模管理类*****/ + +class DIAGRAM_DESIGNER_PUBLIC ProjectModelManager : public QObject +{ + Q_OBJECT + +public: + explicit ProjectModelManager(QObject *parent = nullptr); + ~ProjectModelManager(); + + static ProjectModelManager& instance(); +public: + void initialModel(); + void initialHMISourcr(); + void generate(const QString& sMeta,const QString& sPro); //根据输入名称生成表 + MapProperty addNewProject(const QString& sMeta,const QString& sProject,PropertyModel&); //根据元模型、工程模名称生成工程模对象 + QList getGroupSub(QStandardItemModel*,const QString&); //返回指定组下的属性(如果存在) + MapMeta& getData() {return m_mapTotalData;} + void deleteData(const QString& meta,const QString& proj); + void updateSetting(const QString& sMeta,const QString& sPro,bool toHex = false); //更新模型数据(图片 toHex转换数据为16进制 + QStringList getProjectModelLst(const QString& sMeta); + + QMap& getHMIimageMap(){return m_mapHMIimage;} +public: + QStringList getModelList() const; //获取元模型列表 + QStringList getGroupList(const QString& model) const; //返回该元模下的属性组列表 + QStringList getPublicGroupList() const; //返回公共属性组列表 + QStringList getAttributeList(const QString& model,const QString& group) const; //根据元模名和组名返回属性列表 + QStringList getPublicAttributeList(const QString& group); //返回公共属性组的属性列表 + void setItemAttribute(const QString&,QStandardItem*); //设置item的属性(数据库表字段名) + QPair combinePropertySql(const QStandardItem*); //根据item属性生成sql + bool ifProjectEqual(const QString& sMeta,const QString& sPro,QMap); //根据每个属性组的勾选状态判断两个模型是否相同 + QString modifyProjectModel(const QString& sMeta,const QString& sProject,QMap); //修改工程模 + bool renameProjectModel(const QString& strCur,QMap datas); //重命名工程模 + void updateComponentModelName(const QString& strOld,const QString& strNew); //更新component中的工程模 + bool ifProjectExsit(const QString&); //判断工程模存在 +signals: + void modelChange(); //模型改变信号 +private: + int createPropertyTable(const QString& sMeta,const QString& sProject,const QString& sGroup,QList lstSelect,QList lstBase,int nLinkType,bool isPublic); //创建属性组表并插入记录到管理表(工程名,当前项迭代器,关联图元类型) + QJsonObject getSelectedState(QList select,QList base); //返回json格式的选中状态 + QString getItemDataType(const QStandardItem* pItem); //返回数据类型 + ProjectModelSettingStruct getModelSetting(const QString& sMeta,const QString& sProject); //获取指定工程模的设定 + + QByteArray cleanHexData(const QByteArray& hexData); + QByteArray fixHexLength(const QByteArray& hexData); + QByteArray extractHexOnly(const QByteArray& data); + QByteArray safeFromHex(const QByteArray& hexData); +private: + MapMeta m_mapTotalData; + bool _bInitialised; + QMap m_mapHMIimage; + bool _bHMISourceInitialised = false; +}; +#endif // PROJECTMODELMANAGER_H diff --git a/diagramUtils/source/basePropertyManager.cpp b/diagramUtils/source/basePropertyManager.cpp new file mode 100644 index 0000000..3289b24 --- /dev/null +++ b/diagramUtils/source/basePropertyManager.cpp @@ -0,0 +1,176 @@ +#include +#include "basePropertyManager.h" +#include "baseProperty.h" + +BasePropertyManager& BasePropertyManager::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static BasePropertyManager instance; + return instance; +} + +BasePropertyManager::BasePropertyManager(QObject *parent) + : QObject(parent) +{ + +} + +BasePropertyManager::~BasePropertyManager() +{ + qDeleteAll(m_entityData); + qDeleteAll(m_bayData); +} + +void BasePropertyManager::insertEntityData(QUuid uid,BaseProperty* p) +{ + if(!m_entityData.contains(uid)) + m_entityData.insert(uid,p); +} + +BaseProperty* BasePropertyManager::findEntityData(QUuid uid) +{ + return m_entityData.value(uid,nullptr); +} + +void BasePropertyManager::deleteEntityData(QUuid uid) +{ + BaseProperty* pData = m_entityData.value(uid,nullptr); + if(pData) + delete pData; +} + +QMap BasePropertyManager::getEntityData() const +{ + return m_entityData; +} + +void BasePropertyManager::onDataDelete(QString uuid) +{ + +} + +/***************************基模数据******************************/ +void BasePropertyManager::insertBaseEntityData(QUuid uid,BaseModelProperty* p) +{ + if(!m_baseEntityData.contains(uid)) + m_baseEntityData.insert(uid,p); +} +BaseModelProperty* BasePropertyManager::findBaseEntityData(QUuid uid) +{ + return m_baseEntityData.value(uid,nullptr); +} + +void BasePropertyManager::deleteBaseEntityData(QUuid uid) +{ + BaseModelProperty* pData = m_baseEntityData.value(uid,nullptr); + if(pData) + delete pData; +} +QMap BasePropertyManager::getBaseEntityData() const +{ + return m_baseEntityData; +} + +/*****************************基模间隔数据*******************************/ +void BasePropertyManager::insertBaseBayData(QUuid id,BayProperty* p) +{ + if(!m_baseBayData.contains(id)) + m_baseBayData.insert(id,p); +} + +BayProperty* BasePropertyManager::findBaseBayData(QUuid id) +{ + return m_baseBayData.value(id,nullptr); +} + +void BasePropertyManager::deleteBaseBayData(QUuid id) +{ + BayProperty* pData = m_baseBayData.value(id,nullptr); + if(pData) + delete pData; +} + +QMap BasePropertyManager::getBaseBayData() const +{ + return m_baseBayData; +} + +/*****************************间隔数据*******************************/ +void BasePropertyManager::insertBayData(QUuid id,BayProperty* p) +{ + if(!m_bayData.contains(id)) + m_bayData.insert(id,p); +} + +BayProperty* BasePropertyManager::findBayData(QUuid id) +{ + return m_bayData.value(id,nullptr); +} + +void BasePropertyManager::deleteBayData(QUuid id) +{ + BayProperty* pData = m_bayData.value(id,nullptr); + if(pData) + delete pData; +} + +QMap BasePropertyManager::getBayData() const +{ + return m_bayData; +} + +/*****************************临时预览数据*******************************/ +void BasePropertyManager::insertTempEditorData(QUuid id,DiagramEditorItemProperty* p) +{ + if(!m_editorTempData.contains(id)) + m_editorTempData.insert(id,p); +} + +DiagramEditorItemProperty* BasePropertyManager::findTempEditorData(QUuid id) +{ + return m_editorTempData.value(id,nullptr); +} + +void BasePropertyManager::deleteTempEditorData(QUuid id) +{ + DiagramEditorItemProperty* pData = m_editorTempData.value(id,nullptr); + if(pData) + delete pData; +} + +QMap BasePropertyManager::getTempEditorData() const +{ + return m_editorTempData; +} + +/*****************************预览数据*******************************/ +void BasePropertyManager::insertEditorData(QUuid id,DiagramEditorItemProperty* p) +{ + if(!m_editorData.contains(id)) + m_editorData.insert(id,p); +} + +DiagramEditorItemProperty* BasePropertyManager::findEditorData(QUuid id) +{ + return m_editorData.value(id,nullptr); +} + +void BasePropertyManager::deleteEditorData(QUuid id) +{ + DiagramEditorItemProperty* pData = m_editorData.value(id,nullptr); + if(pData) + delete pData; +} + +QMap BasePropertyManager::getEditorData() const +{ + return m_editorData; +} + +void BasePropertyManager::clearEditorData() +{ + for(auto &pPro:m_editorData){ + delete pPro; + } + m_editorData.clear(); +} diff --git a/diagramUtils/source/componentIconManager.cpp b/diagramUtils/source/componentIconManager.cpp new file mode 100644 index 0000000..94062ca --- /dev/null +++ b/diagramUtils/source/componentIconManager.cpp @@ -0,0 +1,45 @@ +#include "componentIconManager.h" + +ComponentIconManager& ComponentIconManager::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static ComponentIconManager instance; + return instance; +} + +ComponentIconManager::ComponentIconManager(QObject *parent) + : QObject(parent) +{ + _init = false; + initialData(); +} + +ComponentIconManager::~ComponentIconManager() +{ + +} + +void ComponentIconManager::initialData() +{ + addIcon("circuitBreaker",DM_edit,VI_Thumbnail,"abcde"); +} + +void ComponentIconManager::addIcon(QString sType,DiagramMode mode,VariantIcon varIcon, QString iconPath) +{ + _mapIcon[sType][mode][varIcon] = iconPath; +} + +QString ComponentIconManager::getIconPath(QString tpe,DiagramMode mode,VariantIcon varIcon) +{ + if(_mapIcon.contains(tpe)) + { + if(_mapIcon[tpe].contains(mode)) + { + if(_mapIcon[tpe][mode].contains(varIcon)) + { + return _mapIcon[tpe][mode][varIcon]; + } + } + } + return QString(); +} diff --git a/diagramUtils/source/dataBase.cpp b/diagramUtils/source/dataBase.cpp new file mode 100644 index 0000000..32cbbc8 --- /dev/null +++ b/diagramUtils/source/dataBase.cpp @@ -0,0 +1,4229 @@ +#include "dataBase.h" +#include "logger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DataBase* DataBase::dbInstance = nullptr; +int DataBase::_id = 0; + +DataBase::DataBase() +{ + m_sFileName = QString("setting.xml"); + initial(); + //createProjectDB(); + //initialProjectDB(); + createProjectManager(); +} + +DataBase::~DataBase() +{ + QString connectionName = QSqlDatabase::database().connectionName(); + QSqlDatabase::removeDatabase(connectionName); +} + +void DataBase::initial() +{ + readXML(); + if (QSqlDatabase::contains(_DataBaseName)) + db = QSqlDatabase::database(_DataBaseName); + else + db = QSqlDatabase::addDatabase(_DataBaseType,_DataBaseName); + + db.setDatabaseName(_DataBaseName); + db.setHostName(_HostName); + db.setPort(_Port); + // 需要改成自己的用户名和密码 + db.setUserName(_UserName); + db.setPassword(_PassWord); + + if (db.open()) { + qDebug()<<"baseDB success"; + } else { + LOG_ERROR("DB", QString("Database not open")); + } + //元模 + + getAttributeGroup(); //获取属性组信息 + getDataType(); //获取数据类型信息 + getModelType(); //获取模型类型 + getModelGroup(); //获取模型组 + getAttribute(); //获取属性 + getModelAttribute(); + getModelAttributePublic(); //获取公共属性组 + getModelConnectivity(); //获取连接性 +} + +DataBase* DataBase::GetInstance() +{ + if(dbInstance == nullptr) + { + dbInstance = new DataBase(); + } + return dbInstance; +} + +QSqlQuery DataBase::executeSQL(const QString& strSQL,bool isDDL,const QVariantList& params, bool useTranscation) +{ + //事务 + bool transactionStarted = false; + if(useTranscation) + { + if(!db.transaction()) + { + LOG_ERROR("DB", QString("Start transaction failed. error: %1").arg(db.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + transactionStarted = true; + } + + QSqlQuery sqlQuery(db); + try + { + if(isDDL) //创建或删除直接执行sql + { + if (!sqlQuery.exec(strSQL)) + { + LOG_ERROR("SQL", QString("SQL '%1' execute error: %2").arg(strSQL, sqlQuery.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + } + else + { + if(!sqlQuery.prepare(strSQL)) + { + LOG_ERROR("SQL", QString("SQL '%1' prepare fialed. error: %2").arg(strSQL, sqlQuery.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + //绑定参数 + + for(int i = 0;i < params.size();++i) + { + sqlQuery.bindValue(i, params[i]); + } + + if (!sqlQuery.exec()) + { + LOG_ERROR("SQL", QString("SQL '%1' execute error: %2").arg(strSQL, sqlQuery.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + } + + // 提交事务(如果已开启) + if(transactionStarted && !db.commit()) + { + throw std::runtime_error(db.lastError().text().toStdString()); + LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1").arg(db.lastError().databaseText())); + } + } + catch (const std::runtime_error& e) + { + // 错误处理:回滚事务(如果已开启) + if(transactionStarted) + { + if(!db.rollback()) // 回滚失败时记录警告 + { + LOG_ERROR("DB", QString("Rollback failed. connectionName: %1").arg(db.lastError().databaseText())); + } + } + + throw; // 重新抛出异常 + } + + return sqlQuery; +} + +//多条批量SQL语句执行接口 +QSqlQuery DataBase::executeBatchSQL(const QStringList& sqlStatements, bool createOrDrop,const QList& paramsList, bool useTranscation) +{ + + //参数数量校验 + if(!paramsList.isEmpty() && sqlStatements.size() != paramsList.size()) + { + LOG_ERROR("SQL", QString("SQL statement does not match the number of parameters")); + throw std::runtime_error(QSqlError("SQL statement does not match the number of parameters").text().toStdString()); + } + + //事务 + bool transactionStarted = false; + if(useTranscation) + { + if(!db.transaction()) + { + LOG_ERROR("DB", QString("Start transaction failed.")); + throw std::runtime_error(db.lastError().text().toStdString()); + } + transactionStarted = true; + } + + QSqlQuery lastQuery(db); + try + { + for(int i = 0; i < sqlStatements.size(); i++) + { + const QString& strSQL = sqlStatements.at(i); + const QVariantList& params = paramsList.isEmpty() ? QVariantList() : paramsList.at(i); + + QSqlQuery sqlQuery(db); + if(createOrDrop) + { + if (!sqlQuery.exec(strSQL)) + { + LOG_ERROR("SQL", QString("SQL '%1' execute error: %2").arg(strSQL, sqlQuery.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + } + else + { + if(!sqlQuery.prepare(strSQL)) + { + LOG_ERROR("SQL", QString("SQL '%1' prepare fialed. error: %2").arg(strSQL, sqlQuery.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + //绑定参数 + for(int i = 0;i < params.size();++i) + { + sqlQuery.bindValue(i, params[i]); + } + + if (!sqlQuery.exec()) + { + LOG_ERROR("SQL", QString("SQL '%1' execute error: %2").arg(strSQL, sqlQuery.lastError().databaseText())); + throw std::runtime_error(db.lastError().text().toStdString()); + } + } + + lastQuery = std::move(sqlQuery); + } + // 提交事务(如果已开启) + if(transactionStarted && !db.commit()) + { + throw std::runtime_error(db.lastError().text().toStdString()); + LOG_ERROR("DB", QString("Commit transaction failed.")); + } + } + catch (const std::runtime_error& e) + { + // 错误处理:回滚事务(如果已开启) + if(transactionStarted) + { + if(!db.rollback()) // 回滚失败时记录警告 + { + LOG_ERROR("DB", QString("Rollback failed. error: %1").arg( db.lastError().databaseText())); + } + } + + throw; // 重新抛出异常 + } + + return lastQuery; +} + + +bool DataBase::insertComponent(QUuid uuid,QString modelName,QString nspath,QString tag,QString name,QString description,QString grid,QString zone,QString station,int type,bool inService,int state,int status,QJsonObject connected_bus,QJsonObject label,QJsonObject context,int op) +{ + if(db.open()) + { + QSqlQuery qry(db); + + QJsonDocument contextDoc(context); + QString strCon = contextDoc.toJson(QJsonDocument::Compact); + + qry.prepare("INSERT INTO component(global_uuid, model_name, nspath, tag, name, grid, zone, station, type, in_service, state, status, context, op, ts) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + qry.bindValue(0,uuid); + qry.bindValue(1,modelName); + qry.bindValue(2,nspath); + qry.bindValue(3,tag); + qry.bindValue(4,name); + qry.bindValue(5,grid); + qry.bindValue(6,zone); + qry.bindValue(7,station); + qry.bindValue(8,type); + qry.bindValue(9,inService); + qry.bindValue(10,state); + qry.bindValue(11,status); + qry.bindValue(12,strCon); + qry.bindValue(13,op); + qry.bindValue(14,QDateTime::currentDateTime()); + bool res = qry.exec(); + QString str = qry.lastQuery(); + const QVariantList list = qry.boundValues(); + for (qsizetype i = 0; i < list.size(); ++i) + qDebug() << i << ":" << list.at(i).toString(); + if(!res) + { + qDebug()<()) //json特殊处理 + { + QJsonDocument contextDoc(pro.defaultValue.toJsonObject()); + QString strCon = contextDoc.toJson(QJsonDocument::Compact); + params.append(strCon); + } + else + params.append(pro.defaultValue); + } + QString strSQL = QString("INSERT INTO %1(global_uuid, attribute_group%2) VALUES (?, ?%3)").arg(groupValue.tableName,strPros,strPronouns); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert dynamic table fail")); + return false; + } +} + +bool DataBase::updateDynamicProperty(QUuid uuid,GroupStateValue groupValue) +{ + QStringList setClauses; + + QVariantList params; + for(auto &pro:groupValue.mapInfo[uuid]) + { + setClauses.append(QString("%1 = ?").arg(pro.tagName)); + if(pro.defaultValue.userType() == qMetaTypeId()) //json特殊处理 + { + QJsonDocument contextDoc(pro.defaultValue.toJsonObject()); + QString strCon = contextDoc.toJson(QJsonDocument::Compact); + params.append(strCon); + } + else + params.append(pro.defaultValue); + } + params.append(uuid); + + QString strSQL = QString("UPDATE %1 SET %2 WHERE global_uuid = ?").arg(groupValue.tableName).arg(setClauses.join(",")); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update table %1 fail").arg(groupValue.tableName)); + return false; + } +} + +bool DataBase::insertPage(QString tag,QString name,QJsonObject label,QJsonObject context,QString description,int op) +{ + if(db.open()) + { + QSqlQuery qry(db); + + QJsonDocument labelDoc(label); + QString strLabel = labelDoc.toJson(QJsonDocument::Compact); + + QJsonDocument contextDoc(context); + QString strCon = contextDoc.toJson(QJsonDocument::Compact); + + qry.prepare("INSERT INTO page(tag, name, label, context, description, op, ts) VALUES (?, ?, ?, ?, ?, ?, ?)"); + qry.bindValue(0,tag); + qry.bindValue(1,name); + qry.bindValue(2,strLabel); + qry.bindValue(3,strCon); + qry.bindValue(4,description); + qry.bindValue(5,op); + qry.bindValue(6,QDateTime::currentDateTime()); + bool res = qry.exec(); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< DataBase::getAllGrid() +{ + QList lst; + QString strSQL = "SELECT id, tagname, name, description FROM grid"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + GridInfo info; + info.id = query.value(0).toInt(); + info.tagname = query.value(1).toString(); + info.name = query.value(2).toString(); + info.description = query.value(3).toString(); + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getAllZone() +{ + QList lst; + QString strSQL = "SELECT id,grid_id,tagname,name,description FROM zone"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + ZoneInfo info; + info.id = query.value(0).toInt(); + info.grid_id = query.value(1).toInt(); + info.tagname = query.value(2).toString(); + info.name = query.value(3).toString(); + info.description = query.value(4).toString(); + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getAllStation() +{ + QList lst; + QString strSQL = "SELECT id,zone_id,tagname,name,description,is_local FROM station"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + StationInfo info; + info.id = query.value(0).toInt(); + info.zone_id = query.value(1).toInt(); + info.tagname = query.value(2).toString(); + info.name = query.value(3).toString(); + info.description = query.value(4).toString(); + info.is_local = query.value(5).toBool(); + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getAllTopologics() +{ + QList lst; + QString strSQL = "SELECT id,uuid_from,uuid_to,context,flag FROM topologic"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + TopologyInfo info; + info.id = query.value(0).toInt(); + info.uuid_from = QUuid(query.value(1).toString()); + info.uuid_to = QUuid(query.value(2).toString()); + QString str = query.value(3).toString(); + info.context = QstringToJson(str); + info.flag = query.value(4).toInt(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +int DataBase::topologicExist(QUuid fromItem,QUuid toItem) +{ + QString strSQL = "SELECT id FROM topologic WHERE uuid_from = ? AND uuid_to = ?"; + QVariantList params; + params.append(fromItem.toString()); + params.append(toItem.toString()); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + int id = query.value(0).toInt(); + return id; + } + query.clear(); + return -1; + } + catch (const std::exception& e) + { + return -1; + } +} + +TopologyInfo DataBase::getTopologicById(int id) +{ + TopologyInfo info; + QString strSQL = "SELECT id,uuid_from,uuid_to,context,flag FROM topologic WHERE id = ?"; + QVariantList params; + params.append(id); + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + info.id = query.value(0).toInt(); + info.uuid_from = QUuid(query.value(1).toString()); + info.uuid_to = QUuid(query.value(2).toString()); + QString str = query.value(3).toString(); + info.context = QstringToJson(str); + info.flag = query.value(4).toInt(); + } + query.clear(); + return info; + } + catch (const std::exception& e) + { + return info; + } +} + +bool DataBase::deleteTopologic(QUuid fromPin,QUuid toPin) +{ + QString strSQL = "DELETE FROM topologic WHERE from_pin = ? AND to_pin = ?"; + QVariantList params; + params.append(fromPin.toString()); + params.append(toPin.toString()); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete topologic from:%1 to:%2 success").arg(fromPin.toString(),toPin.toString())); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete topologic from:%1 to:%2 fail").arg(fromPin.toString(),toPin.toString())); + return false; + } +} + +/************************************************************/ +ComponentInfo DataBase::getComponentInfoByUuid(QString uuid) +{ + ComponentInfo inf; + if(db.open()) + { + QSqlQuery qry(db); + + //qry.prepare("SELECT * FROM component WHERE global_uuid = ?"); + qry.prepare("SELECT global_uuid, nspath, model_name,tag, name, grid, zone, station, type, context, op,in_service,state,status FROM component WHERE global_uuid = ?"); + qry.bindValue(0,uuid); + bool res = qry.exec(); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< DataBase::getAllComponents() +{ + QList lst; + if(db.open()) + { + QSqlQuery qry(db); + + qry.prepare("SELECT global_uuid, nspath, model_name,tag, name, grid, zone, station, type, context, op,in_service,state,status FROM component"); + bool res = qry.exec(); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< 0) + return true; + else + return false; + qry.clear(); + } + } + return false; +} + +bool DataBase::deleteComponent(QString uuid) +{ + if(db.open()) + { + QSqlQuery qry(db); + + qry.prepare("DELETE FROM component WHERE global_uuid = ?"); + qry.bindValue(0,uuid); + bool res = qry.exec(); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< DataBase::getAllComponentType() +{ + if(_componentType.empty()) + { + QMap map; + if(db.open()) + { + QSqlQuery qry(db); + + qry.prepare("SELECT id, type, name, config FROM component_type"); + bool res = qry.exec(); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< DataBase::getAllPage() +{ + QList lst; + QString strSQL = "SELECT id,tag,name,label,context,description,op FROM page"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + PageInfo info; + info.id = query.value(0).toInt(); + info.tag = query.value(1).toString(); + info.name = query.value(2).toString(); + QString label = query.value(3).toString(); + info.label = QstringToJson(label); + QString context = query.value(4).toString(); + info.context = QstringToJson(context); + info.description = query.value(5).toString(); + info.op = query.value(6).toInt(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::deleteComponentById(int id) +{ + if(db.open()) + { + QSqlQuery qry(db); + + qry.prepare("DELETE FROM topologic WHERE com_from = ? or com_to = ?"); + qry.bindValue(0,id); + qry.bindValue(1,id); + bool res = qry.exec(); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< components,QJsonObject context) +{ + QJsonDocument businessDoc(business); + QString strBusiness = businessDoc.toJson(QJsonDocument::Compact); + + QJsonDocument fromUuidDoc(fromUuid); + QString strFromUuid = fromUuidDoc.toJson(QJsonDocument::Compact); + + QJsonDocument toUuidDoc(toUuid); + QString strToUuid = toUuidDoc.toJson(QJsonDocument::Compact); + + QJsonDocument protectDoc(protect); + QString strProtect = protectDoc.toJson(QJsonDocument::Compact); + + QJsonDocument faultRecDoc(faultRec); + QString strFaultRec = faultRecDoc.toJson(QJsonDocument::Compact); + + QJsonDocument statusDoc(status); + QString strStatus = statusDoc.toJson(QJsonDocument::Compact); + + QJsonDocument dynSenseDoc(dynSense); + QString strDynSense = dynSenseDoc.toJson(QJsonDocument::Compact); + + QJsonDocument instructDoc(instruct); + QString strInstruct = instructDoc.toJson(QJsonDocument::Compact); + + QJsonDocument etcDoc(etc); + QString strEtc = etcDoc.toJson(QJsonDocument::Compact); + + QJsonDocument contextDoc(etc); + QString strContext = contextDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "INSERT INTO bay(bay_uuid, name, tag, type, unom, fla, capacity, description, in_service, state, grid, zone, station, business, from_uuids, to_uuids, dev_protect, dev_fault_record, dev_status, dev_dyn_sense, dev_instruct, dev_etc, components, context) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + QStringList uuidStrings; + for (const QUuid &uuid : components) { + // 使用WithoutBraces确保无花括号,符合PG数组元素格式 + uuidStrings << "\"" + uuid.toString(QUuid::WithoutBraces) + "\""; + } + QString arrayUuid = "{" + uuidStrings.join(",") + "}"; + + QVariantList params; + params.append(uuid.toString()); + params.append(name); + params.append(tag); + params.append(type); + params.append(unom); + params.append(fla); + params.append(capacity); + params.append(description); + params.append(inService); + params.append(nState); + params.append(grid); + params.append(zone); + params.append(station); + params.append(strBusiness); + params.append(strFromUuid); + params.append(strToUuid); + params.append(strProtect); + params.append(strFaultRec); + params.append(strStatus); + params.append(strDynSense); + params.append(strInstruct); + params.append(strEtc); + params.append(arrayUuid); + params.append(strContext); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert bay fail")); + return false; + } +} + +bool DataBase::updateBay(QUuid uuid,QString name,QString tag,double unom,double fla,double capacity,QString description,bool inService,int nState,QJsonObject business, + QJsonObject fromUuid,QJsonObject toUuid,QJsonObject protect,QJsonObject faultRec,QJsonObject status,QJsonObject dynSense,QJsonObject instruct, + QJsonObject etc,QList components,QJsonObject context) +{ + QJsonDocument businessDoc(business); + QString strBusiness = businessDoc.toJson(QJsonDocument::Compact); + + QJsonDocument fromUuidDoc(fromUuid); + QString strFromUuid = fromUuidDoc.toJson(QJsonDocument::Compact); + + QJsonDocument toUuidDoc(toUuid); + QString strToUuid = toUuidDoc.toJson(QJsonDocument::Compact); + + QJsonDocument protectDoc(protect); + QString strProtect = protectDoc.toJson(QJsonDocument::Compact); + + QJsonDocument faultRecDoc(faultRec); + QString strFaultRec = faultRecDoc.toJson(QJsonDocument::Compact); + + QJsonDocument statusDoc(status); + QString strStatus = statusDoc.toJson(QJsonDocument::Compact); + + QJsonDocument dynSenseDoc(dynSense); + QString strDynSense = dynSenseDoc.toJson(QJsonDocument::Compact); + + QJsonDocument instructDoc(instruct); + QString strInstruct = instructDoc.toJson(QJsonDocument::Compact); + + QJsonDocument etcDoc(etc); + QString strEtc = etcDoc.toJson(QJsonDocument::Compact); + + QJsonDocument contextDoc(etc); + QString strContext = contextDoc.toJson(QJsonDocument::Compact); + + QStringList uuidStrings; + for (const QUuid &uuid : components) { + // 使用WithoutBraces确保无花括号,符合PG数组元素格式 + uuidStrings << "\"" + uuid.toString(QUuid::WithoutBraces) + "\""; + } + QString arrayUuid = "{" + uuidStrings.join(",") + "}"; + + QString strSQL = "UPDATE bay SET name = ?,tag = ?,unom = ?,fla = ?,capacity = ?,description = ?,in_service = ?, state = ?, business = ?,from_uuids = ?,to_uuids = ?,dev_protect = ?,dev_fault_record = ?, dev_status = ?,dev_dyn_sense = ?,dev_instruct = ?,dev_etc = ?,components = ?,context = ? WHERE bay_uuid = ?"; + QVariantList params; + params.append(name); + params.append(tag); + params.append(unom); + params.append(fla); + params.append(capacity); + params.append(description); + params.append(inService); + params.append(nState); + params.append(strBusiness); + params.append(strFromUuid); + params.append(strToUuid); + params.append(strProtect); + params.append(strFaultRec); + params.append(strStatus); + params.append(strDynSense); + params.append(strInstruct); + params.append(strEtc); + params.append(arrayUuid); + params.append(strContext); + params.append(uuid); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update bay fail")); + return false; + } +} + +BayInfo DataBase::getBay(QUuid uuid) +{ + BayInfo info; + QString strSQL = "SELECT bay_uuid, name, tag, type, unom, fla, capacity, description, in_service, state, grid, zone, station, business, from_uuids, to_uuids, dev_protect, dev_fault_record, dev_status, dev_dyn_sense, dev_instruct, dev_etc, components, context FROM bay WHERE bay_uuid = ?"; + QVariantList params; + params.append(uuid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + info.uuid = QUuid(query.value(0).toString()); + info.name = query.value(1).toString(); + info.tag = query.value(2).toString(); + info.type = query.value(3).toString(); + info.unom = query.value(4).toDouble(); + info.fla = query.value(5).toDouble(); + info.capacity = query.value(6).toDouble(); + info.description = query.value(7).toString(); + info.inService = query.value(8).toBool(); + info.nState = query.value(9).toInt(); + info.grid = query.value(10).toString(); + info.zone = query.value(11).toString(); + info.station = query.value(12).toString(); + QString strBusi = query.value(13).toString(); + info.business = QstringToJson(strBusi); + QString strFrom = query.value(14).toString(); + info.fromUuid = QstringToJson(strFrom); + QString strTo = query.value(15).toString(); + info.toUuid = QstringToJson(strTo); + QString strProtect = query.value(16).toString(); + info.protect = QstringToJson(strProtect); + QString strFaultRec= query.value(17).toString(); + info.faultRec = QstringToJson(strFaultRec); + QString strStatus= query.value(18).toString(); + info.status = QstringToJson(strStatus); + QString strDynSense= query.value(19).toString(); + info.dynSense = QstringToJson(strDynSense); + QString strInstructe= query.value(20).toString(); + info.instruct = QstringToJson(strInstructe); + QString strEtc= query.value(21).toString(); + info.etc = QstringToJson(strEtc); + QString rawData = query.value(22).toString(); + info.components = parseUuidArray(rawData); + QString strContext= query.value(23).toString(); + info.context = QstringToJson(strContext); + } + query.clear(); + return info; + } + catch (const std::exception& e) + { + return info; + } +} + +QList DataBase::getAllBay() +{ + QList lstInfo; + QString strSQL = "SELECT bay_uuid, name, tag, type, unom, fla, capacity, description, in_service, state, grid, zone, station, business, from_uuids, to_uuids, dev_protect, dev_fault_record, dev_status, dev_dyn_sense, dev_instruct, dev_etc, components, context FROM bay"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + BayInfo info; + info.uuid = QUuid(query.value(0).toString()); + info.name = query.value(1).toString(); + info.tag = query.value(2).toString(); + info.type = query.value(3).toString(); + info.unom = query.value(4).toDouble(); + info.fla = query.value(5).toDouble(); + info.capacity = query.value(6).toDouble(); + info.description = query.value(7).toString(); + info.inService = query.value(8).toBool(); + info.nState = query.value(9).toInt(); + info.grid = query.value(10).toString(); + info.zone = query.value(11).toString(); + info.station = query.value(12).toString(); + QString strBusi = query.value(13).toString(); + info.business = QstringToJson(strBusi); + QString strFrom = query.value(14).toString(); + info.fromUuid = QstringToJson(strFrom); + QString strTo = query.value(15).toString(); + info.toUuid = QstringToJson(strTo); + QString strProtect = query.value(16).toString(); + info.protect = QstringToJson(strProtect); + QString strFaultRec= query.value(17).toString(); + info.faultRec = QstringToJson(strFaultRec); + QString strStatus= query.value(18).toString(); + info.status = QstringToJson(strStatus); + QString strDynSense= query.value(19).toString(); + info.dynSense = QstringToJson(strDynSense); + QString strInstructe= query.value(20).toString(); + info.instruct = QstringToJson(strInstructe); + QString strEtc= query.value(21).toString(); + info.etc = QstringToJson(strEtc); + QString rawData = query.value(22).toString(); + info.components = parseUuidArray(rawData); + QString strContext= query.value(23).toString(); + info.context = QstringToJson(strContext); + lstInfo.append(info); + } + query.clear(); + return lstInfo; + } + catch (const std::exception& e) + { + return lstInfo; + } +} + +bool DataBase::ifBayExist(QUuid uuid) +{ + QString strSQL = "SELECT bay_uuid FROM bay WHERE bay_uuid = ?"; + QVariantList params; + params.append(uuid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} + +bool DataBase::deleteBay(QUuid uuid) +{ + QString strSQL = "DELETE FROM bay WHERE bay_uuid = ?"; + QVariantList params; + params.append(uuid); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete bay %1 success").arg(uuid.toString())); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete bay %1 failed").arg(uuid.toString())); + return false; + } +} +/*****************************************************************************/ + +bool DataBase::insertMeasurement(QString name,QString tag,int type,QJsonObject dataSource,QJsonObject eventPlan,QJsonObject binding,int size,QUuid bayId,QUuid componentId) +{ + QString strSQL = "INSERT INTO measurement(tag, name, type, data_source, event_plan, binding, size, bay_uuid, component_uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + QJsonDocument dataDoc(dataSource); + QString strData = dataDoc.toJson(QJsonDocument::Compact); + + QJsonDocument eventDoc(eventPlan); + QString strEvent = eventDoc.toJson(QJsonDocument::Compact); + + QJsonDocument bindDoc(binding); + QString strBind = bindDoc.toJson(QJsonDocument::Compact); + + QVariantList params; + params.append(tag); + params.append(name); + params.append(type); + params.append(strData); + params.append(strEvent); + params.append(strBind); + params.append(size); + params.append(bayId); + params.append(componentId); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert measurement fail")); + return false; + } +} + +bool DataBase::updateMeasurement(QString name,int type,QJsonObject dataSource,QJsonObject eventPlan,QJsonObject binding,int size,QUuid componentId) +{ + QJsonDocument dataDoc(dataSource); + QString strData = dataDoc.toJson(QJsonDocument::Compact); + + QJsonDocument eventDoc(eventPlan); + QString strEvent = eventDoc.toJson(QJsonDocument::Compact); + + QJsonDocument bindDoc(binding); + QString strBind = eventDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "UPDATE measurement SET type = ?,data_source = ?,event_plan = ?,binding = ?,size = ? WHERE name = ? AND component_uuid = ?"; + QVariantList params; + params.append(type); + params.append(strData); + params.append(strEvent); + params.append(strBind); + params.append(size); + params.append(name); + params.append(componentId); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update measurement %1 fail").arg(name)); + return false; + } +} + +bool DataBase::delteMeasurement(QString name,QUuid componentId) +{ + QString strSQL = "DELETE FROM measurement WHERE name = ? AND component_uuid = ?"; + QVariantList params; + params.append(name); + params.append(componentId); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete measurement %1 success").arg(name)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete measurement %1 failed").arg(name)); + return false; + } +} + +bool DataBase::ifMeasureExist(QString name,QUuid componentId) +{ + QString strSQL = "SELECT id FROM measurement WHERE name = ? AND component_uuid = ?"; + QVariantList params; + params.append(name); + params.append(componentId); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} + +bool DataBase::ifBayMeasureExist(QString name,QUuid bayId) +{ + QString strSQL = "SELECT id FROM measurement WHERE name = ? AND bay_uuid = ?"; + QVariantList params; + params.append(name); + params.append(bayId); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} + +QList DataBase::getMeasurement(QUuid componentId) +{ + QList lst; + QString strSQL = "SELECT tag, name, type, data_source, event_plan, binding, size, bay_uuid, component_uuid FROM measurement WHERE component_uuid = ?"; + QVariantList params; + params.append(componentId); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + MeasurementInfo info; + info.tag = query.value(0).toString(); + info.name = query.value(1).toString(); + info.type = query.value(2).toInt(); + + QString conData = query.value(3).toString(); + QJsonObject objData = QstringToJson(conData); + + QString sEvent = query.value(4).toString(); + QJsonObject objEvent = QstringToJson(sEvent); + + QString sBinding = query.value(5).toString(); + QJsonObject objBinding = QstringToJson(sBinding); + + info.size = query.value(6).toInt(); + info.bayUuid = QUuid(query.value(7).toString()); + info.componentUuid = QUuid(query.value(8).toString()); + + info.nSource = objData["type"].toInt(); + QJsonObject objIoAddress = objData["io_address"].toObject(); + info.sStation = objIoAddress["station"].toString(); + info.sDevice = objIoAddress["device"].toString(); + info.sChannel = objIoAddress["channel"].toString(); + info.nPacket = objIoAddress["packet"].toInt(); + info.nOffset = objIoAddress["offset"].toInt(); + + info.bEnable = objEvent["enable"].toBool(); + QJsonObject objCause = objEvent["cause"].toObject(); + if(objCause.contains("upup")){ + info.mapTE.insert("upup",objCause["upup"].toDouble()); + } + if(objCause.contains("up")){ + info.mapTE.insert("up",objCause["up"].toDouble()); + } + if(objCause.contains("down")){ + info.mapTE.insert("down",objCause["down"].toDouble()); + } + if(objCause.contains("downdown")){ + info.mapTE.insert("downdown",objCause["downdown"].toDouble()); + } + info.sEdge = objCause["edge"].toString(); + QJsonObject objAction = objEvent["action"].toObject(); + info.sCommand = objAction["command"].toString(); + + QJsonArray arrPara = objAction["parameters"].toArray(); + for(const QJsonValue ¶Value:arrPara){ + info.lstParameter.append(paraValue.toString()); + } + + if(objBinding.contains("ct") || objBinding.contains("pt") ){ + if(objBinding.contains("ct")){ + QJsonObject objWind = objBinding["ct"].toObject(); + info.nRatio = objWind["ratio"].toInt(); + info.nPolarity = objWind["polarity"].toInt(); + info.nIndex = objWind["index"].toInt(); + info.sWindType = "ct"; + } + else{ + QJsonObject objWind = objBinding["pt"].toObject(); + info.nRatio = objWind["ratio"].toInt(); + info.nPolarity = objWind["polarity"].toInt(); + info.nIndex = objWind["index"].toInt(); + info.sWindType = "pt"; + } + } + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getBayMeasurement(QUuid bayUuid) +{ + QList lst; + QString strSQL = "SELECT tag, name, type, data_source, event_plan, binding, size, bay_uuid, component_uuid FROM measurement WHERE bay_uuid = ?"; + QVariantList params; + params.append(bayUuid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + MeasurementInfo info; + info.tag = query.value(0).toString(); + info.name = query.value(1).toString(); + info.type = query.value(2).toInt(); + + QString conData = query.value(3).toString(); + QJsonObject objData = QstringToJson(conData); + + QString sEvent = query.value(4).toString(); + QJsonObject objEvent = QstringToJson(sEvent); + + QString sBinding = query.value(5).toString(); + QJsonObject objBinding = QstringToJson(sBinding); + + info.size = query.value(6).toInt(); + info.bayUuid = QUuid(query.value(7).toString()); + info.componentUuid = QUuid(query.value(8).toString()); + + info.nSource = objData["type"].toInt(); + QJsonObject objIoAddress = objData["io_address"].toObject(); + info.sStation = objIoAddress["station"].toString(); + info.sDevice = objIoAddress["device"].toString(); + info.sChannel = objIoAddress["channel"].toString(); + info.nPacket = objIoAddress["packet"].toInt(); + info.nOffset = objIoAddress["offset"].toInt(); + + info.bEnable = objEvent["enable"].toBool(); + QJsonObject objCause = objEvent["cause"].toObject(); + if(objCause.contains("upup")){ + info.mapTE.insert("upup",objCause["upup"].toDouble()); + } + if(objCause.contains("up")){ + info.mapTE.insert("up",objCause["up"].toDouble()); + } + if(objCause.contains("down")){ + info.mapTE.insert("down",objCause["down"].toDouble()); + } + if(objCause.contains("downdown")){ + info.mapTE.insert("downdown",objCause["downdown"].toDouble()); + } + info.sEdge = objCause["edge"].toString(); + QJsonObject objAction = objEvent["action"].toObject(); + info.sCommand = objAction["command"].toString(); + + QJsonArray arrPara = objAction["parameters"].toArray(); + for(const QJsonValue ¶Value:arrPara){ + info.lstParameter.append(paraValue.toString()); + } + + if(objBinding.contains("ct") || objBinding.contains("pt") ){ + if(objBinding.contains("ct")){ + QJsonObject objWind = objBinding["ct"].toObject(); + info.nRatio = objWind["ratio"].toInt(); + info.nPolarity = objWind["polarity"].toInt(); + info.nIndex = objWind["index"].toInt(); + info.sWindType = "ct"; + } + else{ + QJsonObject objWind = objBinding["pt"].toObject(); + info.nRatio = objWind["ratio"].toInt(); + info.nPolarity = objWind["polarity"].toInt(); + info.nIndex = objWind["index"].toInt(); + info.sWindType = "pt"; + } + } + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QMap DataBase::getAllMeasurements() +{ + QMap lst; + QString strSQL = "SELECT tag, name, type, data_source, event_plan, binding, size, bay_uuid, component_uuid FROM measurement"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + MeasurementInfo info; + info.tag = query.value(0).toString(); + info.name = query.value(1).toString(); + info.type = query.value(2).toInt(); + + QString conData = query.value(3).toString(); + QJsonObject objData = QstringToJson(conData); + + QString sEvent = query.value(4).toString(); + QJsonObject objEvent = QstringToJson(sEvent); + + QString sBind = query.value(5).toString(); + QJsonObject objBinding = QstringToJson(sBind); + + info.size = query.value(6).toInt(); + info.bayUuid = QUuid(query.value(7).toString()); + info.componentUuid = QUuid(query.value(8).toString()); + + info.nSource = objData["type"].toInt(); + QJsonObject objIoAddress = objData["io_address"].toObject(); + info.sStation = objIoAddress["station"].toString(); + info.sDevice = objIoAddress["device"].toString(); + info.sChannel = objIoAddress["channel"].toString(); + info.nPacket = objIoAddress["packet"].toInt(); + info.nOffset = objIoAddress["offset"].toInt(); + + info.bEnable = objEvent["enable"].toBool(); + QJsonObject objCause = objEvent["cause"].toObject(); + if(objCause.contains("upup")){ + info.mapTE.insert("upup",objCause["upup"].toDouble()); + } + if(objCause.contains("up")){ + info.mapTE.insert("up",objCause["up"].toDouble()); + } + if(objCause.contains("down")){ + info.mapTE.insert("down",objCause["down"].toDouble()); + } + if(objCause.contains("downdown")){ + info.mapTE.insert("downdown",objCause["downdown"].toDouble()); + } + info.sEdge = objCause["edge"].toString(); + QJsonObject objAction = objEvent["action"].toObject(); + info.sCommand = objAction["command"].toString(); + + QJsonArray arrPara = objAction["parameters"].toArray(); + for(const QJsonValue ¶Value:arrPara){ + info.lstParameter.append(paraValue.toString()); + } + + if(objBinding.contains("ct") || objBinding.contains("pt") ){ + if(objBinding.contains("ct")){ + QJsonObject objWind = objBinding["ct"].toObject(); + info.nRatio = objWind["ratio"].toInt(); + info.nPolarity = objWind["polarity"].toInt(); + info.nIndex = objWind["index"].toInt(); + } + else{ + QJsonObject objWind = objBinding["pt"].toObject(); + info.nRatio = objWind["ratio"].toInt(); + info.nPolarity = objWind["polarity"].toInt(); + info.nIndex = objWind["index"].toInt(); + } + } + lst.insert(info.tag,info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} +/*****************************************************************************/ +bool DataBase::insertExtraProperty(ExtraProperty pro) +{ + QString strSQL = "INSERT INTO properties_setting(code, tag, name, grid_name, zone_name, station_name, current_level, bay_name, component_name, group_name, type_name, grid_tag, zone_tag, station_tag, page_tag, bay_tag, component_uuid, component_tag, group_tag, type_tag, source_type, source_config, connect_para) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + QJsonDocument configDoc(QJsonObject::fromVariantMap(pro.sourceConfig)); + QString strConfig = configDoc.toJson(QJsonDocument::Compact); + + QVariantList params; + params.append(pro.code); + params.append(pro.tag); + params.append(pro.name); + params.append(pro.grid_name); + params.append(pro.zone_name); + params.append(pro.station_name); + params.append(pro.currentLevel); + params.append(pro.bay_name); + params.append(pro.component_name); + params.append(pro.group_name); + params.append(pro.type_name); + + params.append(pro.grid_tag); + params.append(pro.zone_tag); + params.append(pro.station_tag); + params.append(pro.page_tag); + params.append(pro.bay_tag); + params.append(pro.component_uuid.toString()); + params.append(pro.component_tag); + params.append(pro.group_tag); + params.append(pro.type_tag); + params.append(pro.sourceType); + params.append(strConfig); + params.append(pro.connect_para); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert properties_setting fail")); + return false; + } +} + +bool DataBase::updateExtraProperty(ExtraProperty pro) +{ + QJsonDocument configDoc(QJsonObject::fromVariantMap(pro.sourceConfig)); + QString strConfig = configDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "UPDATE properties_setting SET source_config = ?,connect_para = ? WHERE code = ?"; + QVariantList params; + params.append(strConfig); + params.append(pro.connect_para); + params.append(pro.code); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update properties_setting %1 fail").arg(pro.code)); + return false; + } +} + +bool DataBase::deleteExtraProperty(QString code) +{ + QString strSQL = "DELETE FROM properties_setting WHERE code = ?"; + QVariantList params; + params.append(code); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete properties_setting %1 success").arg(code)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete properties_setting %1 failed").arg(code)); + return false; + } +} + +bool DataBase::ifExtraPropertyExist(QString code) +{ + QString strSQL = "SELECT id FROM properties_setting WHERE code = ?"; + QVariantList params; + params.append(code); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} + +QList DataBase::getCompoExtraProperty(QUuid uid) +{ + QList lst; + QString strSQL = "SELECT code, tag, name, grid_name, zone_name, station_name, current_level, bay_name, component_name, group_name, type_name, grid_tag, zone_tag, station_tag, page_tag, bay_tag, component_uuid, component_tag, group_tag, type_tag, source_type, source_config, connect_para FROM properties_setting WHERE component_uuid = ?"; + QVariantList params; + params.append(uid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + ExtraProperty info; + info.code = query.value(0).toString(); + info.tag = query.value(1).toString(); + info.name = query.value(2).toString(); + info.grid_name = query.value(3).toString(); + info.zone_name = query.value(4).toString(); + info.station_name = query.value(5).toString(); + info.currentLevel = query.value(6).toString(); + info.bay_name = query.value(7).toString(); + info.component_name = query.value(8).toString(); + info.group_name = query.value(9).toString(); + info.type_name = query.value(10).toString(); + + info.grid_tag = query.value(11).toString(); + info.zone_tag = query.value(12).toString(); + info.station_tag = query.value(13).toString(); + info.page_tag = query.value(14).toString(); + info.bay_tag = query.value(15).toString(); + info.component_uuid = QUuid(query.value(16).toString()); + info.component_tag = query.value(17).toString(); + info.group_tag = query.value(18).toString(); + info.type_tag = query.value(19).toString(); + + info.sourceType = query.value(20).toString(); + QString sConfig = query.value(21).toString(); + QJsonObject objConfig = QstringToJson(sConfig); + info.sourceConfig = objConfig.toVariantMap(); + info.connect_para = query.value(22).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getBayExtraProperty(QString bayTag) +{ + QList lst; + QString strSQL = "SELECT code, tag, name, grid_name, zone_name, station_name, current_level, bay_name, component_name, group_name, type_name, grid_tag, zone_tag, station_tag, page_tag, bay_tag, component_uuid, component_tag, group_tag, type_tag, source_type, source_config, connect_para FROM properties_setting WHERE bay_tag = ?"; + QVariantList params; + params.append(bayTag); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + ExtraProperty info; + info.code = query.value(0).toString(); + info.tag = query.value(1).toString(); + info.name = query.value(2).toString(); + info.grid_name = query.value(3).toString(); + info.zone_name = query.value(4).toString(); + info.station_name = query.value(5).toString(); + info.currentLevel = query.value(6).toString(); + info.bay_name = query.value(7).toString(); + info.component_name = query.value(8).toString(); + info.group_name = query.value(9).toString(); + info.type_name = query.value(10).toString(); + + info.grid_tag = query.value(11).toString(); + info.zone_tag = query.value(12).toString(); + info.station_tag = query.value(13).toString(); + info.page_tag = query.value(14).toString(); + info.bay_tag = query.value(15).toString(); + info.component_uuid = QUuid(query.value(16).toString()); + info.component_tag = query.value(17).toString(); + info.group_tag = query.value(18).toString(); + info.type_tag = query.value(19).toString(); + + info.sourceType = query.value(20).toString(); + QString sConfig = query.value(21).toString(); + QJsonObject objConfig = QstringToJson(sConfig); + info.sourceConfig = objConfig.toVariantMap(); + info.connect_para = query.value(22).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getAllExtraProperty() +{ + QList lst; + QString strSQL = "SELECT code, tag, name, grid_name, zone_name, station_name, current_level, bay_name, component_name, group_name, type_name, grid_tag, zone_tag, station_tag, page_tag, bay_tag, component_uuid, component_tag, group_tag, type_tag, source_type, source_config, connect_para FROM properties_setting"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + ExtraProperty info; + info.code = query.value(0).toString(); + info.tag = query.value(1).toString(); + info.name = query.value(2).toString(); + info.grid_name = query.value(3).toString(); + info.zone_name = query.value(4).toString(); + info.station_name = query.value(5).toString(); + info.currentLevel = query.value(6).toString(); + info.bay_name = query.value(7).toString(); + info.component_name = query.value(8).toString(); + info.group_name = query.value(9).toString(); + info.type_name = query.value(10).toString(); + + info.grid_tag = query.value(11).toString(); + info.zone_tag = query.value(12).toString(); + info.station_tag = query.value(13).toString(); + info.page_tag = query.value(14).toString(); + info.bay_tag = query.value(15).toString(); + info.component_uuid = QUuid(query.value(16).toString()); + info.component_tag = query.value(17).toString(); + info.group_tag = query.value(18).toString(); + info.type_tag = query.value(19).toString(); + + info.sourceType = query.value(20).toString(); + QString sConfig = query.value(21).toString(); + QJsonObject objConfig = QstringToJson(sConfig); + info.sourceConfig = objConfig.toVariantMap(); + info.connect_para = query.value(22).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} +/*****************************************************************************/ + +void DataBase::readXML() +{ + if (m_sFileName.isEmpty()) + return; + + QFile *pFile = new QFile(m_sFileName); + if (!pFile->open(QIODevice::ReadOnly | QFile::Text)) + { + QMessageBox::information(NULL, QString("title"), QString::fromWCharArray(L"配置文件打开错误")); + return; + } + + QXmlStreamReader* m_pReader = new QXmlStreamReader(pFile); + while (!m_pReader->atEnd() && !m_pReader->hasError()) + { + m_pReader->lineNumber(); + QXmlStreamReader::TokenType token = m_pReader->readNext(); + if (token == QXmlStreamReader::StartDocument) + continue; + + //qDebug() << m_pReader->name(); + if (m_pReader->isStartElement()) + { + if(m_pReader->name() == QString("DataBase")) + { + QXmlStreamAttributes attributes = m_pReader->attributes(); + QString tpe = attributes.value("Type").toString(); + QString sName = attributes.value("Name").toString(); + //QString sProDB = attributes.value("ProjectDB").toString(); + if (tpe == QString("PostgreSQL")) + { + _DataBaseType = QString("QPSQL"); + _DataBaseName = sName; + //_ProjectDB = sProDB; + } + } + else if(m_pReader->name() == QString("HostName")) + { + _HostName = m_pReader->readElementText(); + } + else if(m_pReader->name() == QString("Port")) + { + _Port = m_pReader->readElementText().toInt(); + } + else if(m_pReader->name() == QString("UserName")) + { + _UserName = m_pReader->readElementText(); + } + else if(m_pReader->name() == QString("Password")) + { + _PassWord = m_pReader->readElementText(); + } + } + m_pReader->readNext(); + } + if (m_pReader->hasError()) + { + qDebug() << m_pReader->errorString(); + } + m_pReader->clear(); + delete m_pReader; + m_pReader = NULL; + pFile->close(); + delete pFile; + pFile = NULL; +} + +QJsonObject DataBase::QstringToJson(QString jsonString) +{ + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8().data()); + if(jsonDocument.isNull()) + { + qDebug()<< "String NULL"<< jsonString.toLocal8Bit().data(); + } + QJsonObject jsonObject = jsonDocument.object(); + return jsonObject; +} + +QList DataBase::parseUuidArray(const QString& pgArray) +{ + QList uuids; + + if (pgArray.isEmpty() || pgArray == "{}") + return uuids; + + // 移除花括号并分割元素 + QStringList parts = pgArray.mid(1, pgArray.size() - 2).split(","); + + for (QString& part : parts) { + part = part.trimmed(); + + // 处理带双引号的元素 + if (part.startsWith('"') && part.endsWith('"')) { + part = part.mid(1, part.size() - 2); + } + + // 处理 NULL 值(转为空 QUuid) + if (part == "NULL") { + uuids << QUuid(); + } else { + uuids << QUuid(part); + } + } + + return uuids; +} +//=================================元模=============================================// +bool DataBase::getAttributeGroup() +{ + QString strSQL = "SELECT * FROM basic.attribute_group"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + QString groupType = query.value(1).toString(); + QString groupName = query.value(2).toString(); + int ispublic = query.value(3).toInt(); + QString remark = query.value(4).toString(); + + if(!_attributeGroup.contains(id)) + { + attributeGroup ag; + ag.id = id; + ag.groupType = groupType; + ag.groupName = groupName; + ag.isPublic = ispublic; + ag.remark = remark; + _attributeGroup.insert(id,ag); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getDataType() +{ + QString strSQL = "SELECT * FROM basic.data_type"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + QString dt = query.value(1).toString(); + QString dbt = query.value(2).toString(); + + if(!_dataType.contains(id)) + { + dataType type; + type.id = id; + type.dataType = dt; + type.databaseType = dbt; + _dataType.insert(id,type); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getModelType() +{ + QString strSQL = "SELECT id, model_type, model_name, graphic_element, icon, remark FROM basic.model_type"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + QString sModelType = query.value(1).toString(); //模型类型 + QString sModelName = query.value(2).toString(); //模型名称 + int graphicElement = query.value(3).toInt(); + QByteArray bIcon = query.value(4).toByteArray(); //图片 + QString remark = query.value(5).toString(); //备注 + + if(!_modelType.contains(id)) + { + modelType mt; + mt.id = id; + mt.modelType = sModelType; + mt.modelName = sModelName; + mt.graphicElement = graphicElement; + mt.icon = bIcon; + mt.remark = remark; + + _modelType.insert(id,mt); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getModelGroup() +{ + QString strSQL = "SELECT * FROM basic.model_group"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + int modelTypeId = query.value(1).toInt(); + int attrybuteGroupId = query.value(2).toInt(); + + if(!_modelGroup.contains(id)) + { + modelGroup mg; + mg.id = id; + mg.modelTypeId = modelTypeId; + mg.attributeGroupId = attrybuteGroupId; + + _modelGroup.insert(id,mg); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getModelAttribute() +{ + QString strSQL = "SELECT * FROM basic.model_attribute"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + qint64 mti = query.value(1).toLongLong(); + qint64 agi = query.value(2).toLongLong(); + qint64 ai = query.value(3).toLongLong(); + + if(!_modelAttribute.contains(id)) + { + modelAttribute ma; + ma.id = id; + ma.modelTypeId = mti; + ma.attributeGroupId = agi; + ma.attributeId = ai; + + _modelAttribute.insert(id,ma); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getModelAttributePublic() +{ + QString strSQL = "SELECT * FROM basic.model_attribute_public"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + qint64 agi = query.value(1).toLongLong(); + qint64 ai = query.value(2).toLongLong(); + + if(!_modelAttributePublic.contains(id)) + { + modelAttributePublic ma; + ma.id = id; + ma.attributeGroupId = agi; + ma.attributeId = ai; + + _modelAttributePublic.insert(id,ma); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getAttribute() +{ + QString strSQL = "SELECT * FROM basic.attribute"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + QString att = query.value(1).toString(); //属性 + QString attn = query.value(2).toString(); //属性名 + qint64 dt = query.value(3).toLongLong(); //类型号 + int len = query.value(4).toInt(); //类型长度 + int scale = query.value(5).toInt(); //类型精度 + int inn = query.value(6).toInt(); //非空 + QString dv = query.value(7).toString(); //默认值 + QString vr = query.value(8).toString(); //范围 + int visible = query.value(9).toInt(); + + if(!_attribute.contains(id)) + { + attribute attribute; + attribute.id = id; + attribute.attribute = att; + attribute.attributeName = attn; + attribute.dataTypeId = dt; + attribute.lengthPrecision = len; + attribute.scale = scale; + attribute.isNotNull = inn; + attribute.defaultValue = dv; + attribute.valueRange = vr; + attribute.isVisible = visible; + + _attribute.insert(id,attribute); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} + +bool DataBase::getModelConnectivity() +{ + QString strSQL = "SELECT * FROM basic.model_connectivity"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + int id = query.value(0).toInt(); + QString fm = query.value(1).toString(); //from + QString tm = query.value(2).toString(); //to + int con = query.value(3).toInt(); //是否可联 + + if(!_modelConnectivity.contains(id)) + { + modelConnectivity connect; + connect.id = id; + connect.fromModel = fm; + connect.toModel = tm; + connect.connectivity = con; + + _modelConnectivity.insert(id,connect); + } + } + query.finish(); + return true; + } + catch (const std::exception& e) + { + return false; + } +} +//=================================工程模===========================================// + +bool DataBase::insertProjectSetting(const QString& baseModel,const QString& modelName,QJsonObject setting) +{ + QString strSQL = "INSERT INTO diagramui_projectmodelSetting(base_name,model_name, context) VALUES (?, ?, ?)"; + QJsonDocument contextDoc(setting); + QString strContext = contextDoc.toJson(QJsonDocument::Compact); + + QVariantList params; + params.append(baseModel); + params.append(modelName); + params.append(strContext); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_projectmodelSetting fail")); + return false; + } +} + +bool DataBase::updateProjectSetting(const QString& baseModel,const QString& modelName,QJsonObject setting) +{ + QJsonDocument contextDoc(setting); + QString strContext = contextDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "UPDATE diagramui_projectmodelsetting SET context = ? WHERE base_name = ? AND model_name = ?"; + QVariantList params; + params.append(strContext); + params.append(baseModel); + params.append(modelName); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update model %1 setting %2 fail").arg(modelName,strContext)); + return false; + } +} + +QJsonObject DataBase::getProjectSetting(const QString& baseModel,const QString& modelName) +{ + + QString strSQL = "SELECT context FROM diagramui_projectmodelsetting WHERE base_name = ? AND model_name = ?"; + QVariantList params; + params.append(baseModel); + params.append(modelName); + + try + { + QJsonObject obj; + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString con = query.value(0).toString(); + obj= QstringToJson(con); + } + return obj; + } + catch (const std::exception& e) + { + return QJsonObject(); + } +} + +QStringList DataBase::getProjectWithinBase(const QString& baseModel) +{ + QStringList lst; + + QString strSQL = "SELECT model_name FROM diagramui_projectmodelsetting WHERE base_name = ?"; + QVariantList params; + params.append(baseModel); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString name = query.value(0).toString(); + lst.append(name); + } + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::deleteProjectSetting(const QString& baseModel,const QString& modelName) +{ + QString strSQL = "DELETE FROM diagramui_projectmodelsetting WHERE base_name = ? AND model_name = ?"; + QVariantList params; + params.append(baseModel); + params.append(modelName); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete row %1 success").arg(modelName)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete row %1 failed").arg(modelName)); + return false; + } +} + +bool DataBase::createProjectManager() +{ + QString strSQL = R"( + CREATE TABLE IF NOT EXISTS project_manager ( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + tag VARCHAR(64) NOT NULL, + meta_model VARCHAR(64) NOT NULL, + group_name VARCHAR(64) NOT NULL, + link_type integer NOT NULL DEFAULT 0, + check_state JSONB NOT NULL DEFAULT '{}'::jsonb, + "ispublic" boolean NOT NULL DEFAULT false + ); + )"; + + if(db.open()) + { + QSqlQuery qry(db); + + bool res = qry.exec(strSQL); + QString str = qry.lastQuery(); + if(!res) + { + qDebug()< 0) + return true; + else + return false; + qry.clear(); + } + } + return false; + + /*try + { + executeSQL(strSQL); + return true; + } + catch (const std::exception& e) + { + return false; + }*/ +} + +bool DataBase::insertProjectManager(const QString& name,const QString& tag,const QString& metaModel,const QString& groupName,int linkType,QJsonObject checkState,bool ispublic) +{ + QString strSQL = "INSERT INTO project_manager(name, tag, meta_model, group_name, link_type, check_state, ispublic) VALUES (?, ?, ?, ?, ?, ?, ?)"; + QJsonDocument checkDoc(checkState); + QString strCheck = checkDoc.toJson(QJsonDocument::Compact); + + QVariantList params; + params.append(name); + params.append(tag); + params.append(metaModel); + params.append(groupName); + params.append(linkType); + params.append(strCheck); + params.append(ispublic); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert project_manager fail")); + return false; + } + +} + +bool DataBase::updateCheckState(const QString& tableName,QJsonObject checkState) +{ + QJsonDocument checkDoc(checkState); + QString strCheck = checkDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "UPDATE project_manager SET check_state = ? WHERE name = ?"; + QVariantList params; + params.append(strCheck); + params.append(tableName); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update table %1 state %2 fail").arg(tableName,strCheck)); + return false; + } +} + +QMap DataBase::getProjectFromManager(const QString& sMeta) +{ + QMap map; + QString strSQL = "SELECT tag,link_type FROM project_manager WHERE meta_model = ?"; + QVariantList params; + params.append(sMeta); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString tag = query.value(0).toString(); + int nType = query.value(1).toInt(); + if(!map.contains(tag)) + { + map.insert(tag,nType); + } + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getCheckStateFromManager(const QString& sProject) +{ + QMap map; + + QString strSQL = "SELECT group_name, check_state FROM project_manager WHERE tag = ?"; + QVariantList params; + params.append(sProject); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString group = query.value(0).toString(); + QString state = query.value(1).toString(); + QJsonObject jsonObj = QstringToJson(state); + map.insert(group,jsonObj); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getPublicStateFromManager() +{ + QMap map; + bool ispublic = true; + + QString strSQL = "SELECT group_name, check_state FROM project_manager WHERE ispublic = ?"; + QVariantList params; + params.append(ispublic); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString group = query.value(0).toString(); + QString state = query.value(1).toString(); + QJsonObject jsonObj = QstringToJson(state); + map.insert(group,jsonObj); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getProjectTableName(const QString& sProject) +{ + QMap map; + QString strSQL = "SELECT group_name, name FROM project_manager WHERE tag = ?"; + QVariantList params; + params.append(sProject); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString group = query.value(0).toString(); + QString tableName = query.value(1).toString(); + map.insert(group,tableName); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getAllProjectModel() +{ + QMap map; //工程模名,类型 + QString strSQL = "SELECT tag, MAX(link_type) AS link_type,ispublic FROM project_manager GROUP BY tag,ispublic"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + QString tableName = query.value(0).toString(); + int linkType = query.value(1).toInt(); + bool ispublic = query.value(2).toBool(); + if(!ispublic) + map.insert(tableName,linkType); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getModelInfo(const QString& sProject) +{ + QMap map; + QString strSQL = "SELECT group_name,name,check_state FROM project_manager WHERE tag = ?"; + QVariantList params; + params.append(sProject); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + PropertyGroupState sta; + sta.groupName = query.value(0).toString(); + sta.tableName = query.value(1).toString(); + sta.propertyState = QstringToJson(query.value(2).toString()); + map.insert(sta.groupName,sta); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getPublicInfo() +{ + QMap map; + bool ispublic = true; + QString strSQL = "SELECT group_name,name,check_state FROM project_manager WHERE ispublic = ?"; + QVariantList params; + params.append(ispublic); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + PropertyGroupState sta; + sta.groupName = query.value(0).toString(); + sta.tableName = query.value(1).toString(); + sta.propertyState = QstringToJson(query.value(2).toString()); + map.insert(sta.groupName,sta); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::getProjectModelGroupInfo(const QString& sTable) +{ + QMap map; + QString strSQL = "SELECT name,tag,meta_model,group_name,link_type,check_state FROM project_manager WHERE tag = ?"; + QVariantList params; + params.append(sTable); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + ProjectManagerStruct info; + info.name = query.value(0).toString(); + info.tag = query.value(1).toString(); + info.metaModel = query.value(2).toString(); + info.groupName = query.value(3).toString(); + info.linkType = query.value(4).toInt(); + QString json = query.value(5).toString(); + info.checkState = QstringToJson(json); + + if(!map.contains(info.groupName)) + map.insert(info.groupName,info); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QMap DataBase::selectGroupPropertyByState(const QString& tableName,QMap mapPro) +{ + QStringList paramList; + for(auto &pro:mapPro) + { + paramList.append(pro.tagName); + } + QString strSQL = QString("SELECT %1 FROM %2").arg(paramList.join(", ")).arg(tableName); + QMap map; + PropertyValueInfo info; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + QUuid uuid; + for(auto &proVal:mapPro) + { + PropertyStateInfo pro; + if(proVal.tagName == "global_uuid" && tableName != "baseProperty") //除基础属性组,其他组不显示uuid todo:组名适配 + { + uuid = QUuid(query.value(proVal.tagName).toString()); + continue; + } + else if(proVal.tagName == "global_uuid" && tableName == "baseProperty") + { + uuid = QUuid(query.value(proVal.tagName).toString()); + } + pro.tagName = proVal.tagName; + pro.name = proVal.name; + pro.type = proVal.type; + pro.isVisible = proVal.isVisible; + /*if(proVal.type == "JSONB"){ //json单独处理 + pro.defaultValue = query.value(proVal.name).toJsonObject(); + } + else*/ + pro.defaultValue = query.value(proVal.tagName ); + info.insert(proVal.tagName ,pro); + } + map.insert(uuid,info); + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +PropertyValueInfo DataBase::selectGroupPropertyByValue(const QString& tableName,QUuid uuid,PropertyValueInfo value) +{ + PropertyValueInfo map; + QStringList paramList; + for(auto &pro:value) + { + paramList.append(pro.tagName); + } + QString strSQL = QString("SELECT %1 FROM %2 WHERE global_uuid = ?").arg(paramList.join(", ")).arg(tableName); + QVariantList params; + params.append(uuid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QUuid uuid; + for(auto &proVal:value) + { + PropertyStateInfo pro; + pro.tagName = proVal.tagName; + pro.name = proVal.name; + pro.type = proVal.type; + pro.isVisible = proVal.isVisible; + pro.defaultValue = query.value(proVal.tagName ); + map.insert(proVal.tagName,pro); + } + } + query.clear(); + return map; + } + catch (const std::exception& e) + { + return map; + } +} + +QList DataBase::getMeasureAttributeTypes() //暂时调换获取的name与tag +{ + QList lst; + QString strSQL = "SELECT attribute,attribute_name FROM basic.attribute WHERE is_visible = ?"; + QVariantList params; + params.append(2); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString attName = query.value(0).toString(); + QString attTag = query.value(1).toString(); + + if(attName.contains("$")){ //包含$ + QStringList lst_dollar; + lst_dollar<<"s1"<<"s2"<<"s3"; + if(attName.contains("sn")){ //同时包含$与sn,9个分支 + QStringList lst_sn; + lst_sn<<"s1"<<"s2"<<"s3"; + + if(attName.contains("_$")){ //包含_$,特殊处理 + if(attName.first(1) == "I"){ //头字母为I + QStringList lst_I; + lst_I<<"a"<<"b"<<"c"; + + for(auto &i:lst_I) + { + QString tn1 = attName; + QString tt1 = attTag; + + QString name = tn1.replace("_$",i); + QString tag = tt1.replace("_$",i); + for(auto &sn:lst_sn) + { + QString tn2 = name; + QString tt2 = tag; + MeasureAttributeType measure; + measure.tag = tn2.replace("sn",sn); + measure.name = tt2.replace("sn",sn); + lst.append(measure); + } + } + } + else{ //头字母为U + QStringList lst_U; + lst_U<<"AB"<<"BC"<<"CA"; + + for(auto &u:lst_U) + { + QString tn1 = attName; + QString tt1 = attTag; + + QString name = tn1.replace("_$",u); + QString tag = tt1.replace("_$",u); + for(auto &sn:lst_sn) + { + QString tn2 = name; + QString tt2 = tag; + MeasureAttributeType measure; + measure.tag = tn2.replace("sn",sn); + measure.name = tt2.replace("sn",sn); + lst.append(measure); + } + } + } + } + else{ //只包含$与sn + for(auto &dor:lst_dollar) + { + QString tn1 = attName; + QString tt1 = attTag; + + QString name = tn1.replace("$",dor); + QString tag = tt1.replace("$",dor); + for(auto &sn:lst_sn) + { + QString tn2 = name; + QString tt2 = tag; + MeasureAttributeType measure; + measure.tag = tn2.replace("sn",sn); + measure.name = tt2.replace("sn",sn); + lst.append(measure); + } + } + } + } + else{ //不包含sn,3种分支 + if(attName.contains("_$")){ //包含_$ + if(attName.first(1) == "I"){ //头字母为I + QStringList lst_I; + lst_I<<"a"<<"b"<<"c"; + + for(auto &i:lst_I) + { + QString name = attName; + QString tag = attTag; + + MeasureAttributeType measure; + measure.tag = name.replace("_$",i); + measure.name = tag.replace("_$",i); + lst.append(measure); + } + } + else{ //头字母为U + QStringList lst_U; + lst_U<<"AB"<<"BC"<<"CA"; + + for(auto &u:lst_U) + { + QString name = attName; + QString tag = attTag; + + MeasureAttributeType measure; + measure.tag = name.replace("_$",u); + measure.name = tag.replace("_$",u); + lst.append(measure); + } + } + } + else{ //不包含_$ + QStringList lst_dollar; + lst_dollar<<"s1"<<"s2"<<"s3"; + + for(auto &dor:lst_dollar) + { + QString name = attName; + QString tag = attTag; + + MeasureAttributeType measure; + measure.tag = name.replace("$",dor); + measure.name = tag.replace("$",dor); + lst.append(measure); + } + } + } + } + else if(attName.contains("sn")){ //只包含sn,3种分支 + QStringList lst_sn; + lst_sn<<"s1"<<"s2"<<"s3"; + + for(auto &sn:lst_sn) + { + QString name = attName; + QString tag = attTag; + + MeasureAttributeType measure; + measure.tag = name.replace("sn",sn); + measure.name = tag.replace("sn",sn); + lst.append(measure); + } + } + else{ //没有分支 + MeasureAttributeType measure; + measure.tag = attName; + measure.name = attTag; + lst.append(measure); + } + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Select measureAttributeType fail")); + } + return lst; +} + +/************************************运行模式*********************************************/ +bool DataBase::insertMonitor(QUuid uid,QString tag,QString name,QString parent,QJsonObject context) +{ + QString strSQL = "INSERT INTO diagramui_monitor_page(global_uuid, tag, name, parent, context) VALUES (?, ?, ?, ?, ?)"; + QJsonDocument dataContext(context); + QString strContext = dataContext.toJson(QJsonDocument::Compact); + + QVariantList params; + params.append(uid); + params.append(tag); + params.append(name); + params.append(parent); + params.append(strContext); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_monitor_page fail")); + return false; + } +} + +QUuid DataBase::getMonitorIdByName(QString name) +{ + QString strSQL = "SELECT global_uuid FROM diagramui_monitor_page WHERE tag = ?"; + QVariantList params; + params.append(name); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return QUuid(query.value(0).toString()); + } + } + catch (const std::exception& e) + { + + } + return QUuid(); +} + +bool DataBase::updateMonitor(QString tag,QJsonObject context) +{ + QJsonDocument contextDoc(context); + QString strCon = contextDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "UPDATE diagramui_monitor_page SET context = ? WHERE tag = ?"; + QVariantList params; + params.append(strCon); + params.append(tag); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update diagramui_monitor_page %1 fail").arg(tag)); + return false; + } +} + +QJsonObject DataBase::getMonitorContextByTag(QString tag) +{ + QString strSQL = "SELECT context FROM diagramui_monitor_page WHERE tag = ?"; + QVariantList params; + params.append(tag); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString str = query.value(0).toString(); + QJsonObject obj = QstringToJson(str); + return obj; + } + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("SELECT diagramui_monitor_page %1 fail").arg(tag)); + } + return QJsonObject(); +} + +QList DataBase::getAllMonitor() +{ + QList lst; + QString strSQL = "SELECT id,global_uuid,tag,name,parent,context,ts FROM diagramui_monitor_page"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + MonitorPageInfo info; + info.id = query.value(0).toInt(); + info.uid = QUuid(query.value(1).toString()); + info.tag = query.value(2).toString(); + info.name = query.value(3).toString(); + info.parent = query.value(4).toString(); + QString context = query.value(5).toString(); + info.context = QstringToJson(context); + info.ts = query.value(6).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::insertHMI(QUuid uid,QString tag,QString name,QJsonObject context) +{ + QString strSQL = "INSERT INTO diagramui_hmi_page(global_uuid, tag, name, context) VALUES (?, ?, ?, ?)"; + QJsonDocument dataContext(context); + QString strContext = dataContext.toJson(QJsonDocument::Compact); + + QVariantList params; + params.append(uid); + params.append(tag); + params.append(name); + params.append(strContext); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_hmi_page fail")); + return false; + } +} + +QUuid DataBase::getHMIdByName(QString name) +{ + QString strSQL = "SELECT global_uuid FROM diagramui_hmi_page WHERE tag = ?"; + QVariantList params; + params.append(name); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return QUuid(query.value(0).toString()); + } + } + catch (const std::exception& e) + { + + } + return QUuid(); +} + +bool DataBase::updateHMI(QString tag,QJsonObject context) +{ + QJsonDocument contextDoc(context); + QString strCon = contextDoc.toJson(QJsonDocument::Compact); + + QString strSQL = "UPDATE diagramui_hmi_page SET context = ? WHERE tag = ?"; + QVariantList params; + params.append(strCon); + params.append(tag); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update diagramui_hmi_page %1 fail").arg(tag)); + return false; + } +} + +QJsonObject DataBase::getHMIContextByTag(QString tag) +{ + QString strSQL = "SELECT context FROM diagramui_hmi_page WHERE tag = ?"; + QVariantList params; + params.append(tag); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString str = query.value(0).toString(); + QJsonObject obj = QstringToJson(str); + return obj; + } + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("SELECT diagramui_hmi_page %1 fail").arg(tag)); + } + return QJsonObject(); +} + +QList DataBase::getAllHMI() +{ + QList lst; + QString strSQL = "SELECT id,global_uuid,tag,name,context,ts FROM diagramui_hmi_page"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + HMIPageInfo info; + info.id = query.value(0).toInt(); + info.uid = QUuid(query.value(1).toString()); + info.tag = query.value(2).toString(); + info.name = query.value(3).toString(); + QString context = query.value(4).toString(); + info.context = QstringToJson(context); + info.ts = query.value(5).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::insertHMIimage(int baseType,QString imageName,QByteArray hash256,QByteArray svgData) +{ + QString strSQL = "INSERT INTO diagramui_hmi_image(type, svg_name, svg_hash, svg_data) VALUES ( ?, ?, ?, ?)"; + + QVariantList params; + params.append(baseType); + params.append(imageName); + params.append(hash256); + params.append(svgData); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_hmi_image fail")); + return false; + } +} + +bool DataBase::insertHMIimagesBatch(const QVector& imageList) +{ + if (imageList.isEmpty()) { + return true; + } + + // 准备批量参数 + QStringList sqlStatements; + QList paramsList; + + 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); + } + + try { + // 使用现有批量执行函数,强制使用事务 + executeBatchSQL(sqlStatements, false, paramsList, true); + return true; + } + catch (const std::exception& e) { + LOG_ERROR("DB", QString("Batch insert HMI images failed: %1").arg(e.what())); + return false; + } +} + +bool DataBase::insertHMIimagesWithCheck(const QVector& imageList) +{ + if (imageList.isEmpty()) { + return true; + } + + // 1. 批量查询已存在的图片 + QVector hashList; + hashList.reserve(imageList.size()); + + for (const auto& image : imageList) { + hashList.append(image.hash256); + } + + QVector existsResults = batchCheckHMIimagesExists(hashList); + + // 2. 过滤出不存在的图片 + QVector imagesToInsert; + imagesToInsert.reserve(imageList.size()); + + for (int i = 0; i < imageList.size(); ++i) { + if (!existsResults[i]) { + imagesToInsert.append(imageList[i]); + } else { + LOG_DEBUG("DB", QString("Image with hash %1 already exists, skipping") + .arg(QString(hashList[i].toHex()))); + } + } + + if (imagesToInsert.isEmpty()) { + LOG_INFO("DB", "All images already exist, nothing to insert"); + return true; + } + + // 3. 批量插入不存在的图片 + return insertHMIimagesBatch(imagesToInsert); +} + +QVector DataBase::batchCheckHMIimagesExists(const QVector& hashList) +{ + QVector existsResults(hashList.size(), false); + + if (hashList.isEmpty()) { + return existsResults; + } + + // 构建IN查询语句 + QStringList placeholders; + QVariantList params; + + for (const auto& hash : hashList) { + placeholders.append("?"); + params.append(QString(hash.toHex())); // 转换为十六进制字符串 + } + + QString sql = QString("SELECT svg_hash FROM diagramui_hmi_image WHERE svg_hash IN (%1)") + .arg(placeholders.join(",")); + + try { + QSqlQuery result = executeSQL(sql, false, params); + + // 收集已存在的hash + QSet existingHashes; + while (result.next()) { + existingHashes.insert(result.value(0).toString()); + } + + // 更新存在性结果 + for (int i = 0; i < hashList.size(); ++i) { + QString currentHash = QString(hashList[i].toHex()); + if (existingHashes.contains(currentHash)) { + existsResults[i] = true; + } + } + + } catch (const std::exception& e) { + LOG_ERROR("DB", QString("Batch check query failed: %1").arg(e.what())); + } + + return existsResults; +} + +QList DataBase::getAllHMIimage() +{ + QList lst; + QString strSQL = "SELECT id,type,svg_name,svg_hash,svg_data FROM diagramui_hmi_image"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + HMIImageInfo info; + info.id = query.value(0).toInt(); + info.baseType = query.value(1).toInt(); + info.imageName = query.value(2).toString(); + info.hash256 = query.value(3).toByteArray(); + info.svgData = query.value(4).toByteArray(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::inserHMIimageRef(QUuid hmiId,QString model,QByteArray hash256,int slot) +{ + QString strSQL = "INSERT INTO diagramui_hmi_image_ref(hmi_uuid, model_name, svg_hash, img_slot) VALUES (?, ?, ?, ?)"; + + QVariantList params; + params.append(hmiId); + params.append(model); + params.append(hash256); + params.append(slot); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_hmi_image_ref fail")); + return false; + } +} + +bool DataBase::insertHMIimageRefBatch(const QList& refList) +{ + if (refList.isEmpty()) { + return true; + } + + // 准备批量参数 + QStringList sqlStatements; + QList paramsList; + + // 注意:id 是自增主键,不需要插入 + QString strSQL = "INSERT INTO diagramui_hmi_image_ref(hmi_uuid, model_name, svg_hash, img_slot) VALUES (?, ?, ?, ?)"; + + for (const auto& ref : refList) { + sqlStatements.append(strSQL); + + QVariantList params; + params.append(ref.hmiId); + params.append(ref.model); + params.append(ref.hash256); + params.append(ref.slot); + + paramsList.append(params); + } + + try { + // 使用现有批量执行函数,强制使用事务 + executeBatchSQL(sqlStatements, false, paramsList, true); + return true; + } + catch (const std::exception& e) { + LOG_ERROR("DB", QString("Batch insert HMI image refs failed: %1").arg(e.what())); + return false; + } +} + +QList DataBase::getHMIRefAll(QUuid hmiId) +{ + QList lst; + QString strSQL = "SELECT id, hmi_uuid, model_name, svg_hash, img_slot FROM diagramui_hmi_image_ref WHERE hmi_uuid = ?"; + QVariantList params; + params.append(hmiId); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + HMIImageRef info; + info.id = query.value(0).toInt(); + info.hmiId = QUuid(query.value(1).toString()); + info.model = query.value(2).toString(); + info.hash256 = query.value(3).toByteArray(); + info.slot = query.value(4).toInt(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +QList DataBase::getHMIRef(QUuid hmiId,QString model) +{ + QList lst; + QString strSQL = "SELECT id, hmi_uuid, model_name, svg_hash, img_slot FROM diagramui_hmi_image_ref WHERE hmi_uuid = ? AND model_name = ?"; + QVariantList params; + params.append(hmiId); + params.append(model); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + HMIImageRef info; + info.id = query.value(0).toInt(); + info.hmiId = QUuid(query.value(1).toString()); + info.model = query.value(2).toString(); + info.hash256 = query.value(3).toByteArray(); + info.slot = query.value(4).toInt(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::removeHMIRef(QUuid hmiId,QString model) +{ + QString strSQL = "DELETE FROM diagramui_hmi_image_ref WHERE hmi_uuid = ? AND model_name = ?"; + QVariantList params; + params.append(hmiId); + params.append(model); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete diagramui_hmi_image_ref success")); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete diagramui_hmi_image_ref failed")); + return false; + } +} + +bool DataBase::removeHMIRefAll(QUuid hmiId) +{ + QString strSQL = "DELETE FROM diagramui_hmi_image_ref WHERE hmi_uuid = ?"; + QVariantList params; + params.append(hmiId); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete diagramui_hmi_image_ref success")); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete diagramui_hmi_image_ref failed")); + return false; + } +} +//=========================================================================== +bool DataBase::createDynamicTable(const QString &tableName, const QStringList &fields) +{ + QString strSQL = "CREATE TABLE IF NOT EXISTS " + tableName + " ("; + for (const QString &field : fields) { + strSQL += field + ", "; + } + // Remove the last comma and space + strSQL.chop(2); + strSQL += ");"; + + try + { + executeSQL(strSQL,true); + LOG_INFO("DB", QString("Create table %1 success").arg(tableName)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Create table %1 fail").arg(tableName)); + return false; + } +} + +bool DataBase::deleteProjectModel(const QString& sProject) +{ + QStringList lstTable; + QString strSQL = "SELECT name FROM project_manager WHERE tag = ?"; + QVariantList params; + params.append(sProject); + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString sName = query.value(0).toString(); //获取表名 + lstTable.append(sName); + } + query.finish(); + } + catch (const std::exception& e) + { + return false; + } + + if(!db.transaction()) + { + LOG_ERROR("DB", QString("Start transaction failed. error: %1.").arg( db.lastError().databaseText())); + return false; + } + QStringList sqlStatements; + for(auto &sTab:lstTable) + { + sqlStatements << QString("DROP TABLE IF EXISTS %1").arg(sTab); + } + try + { + executeBatchSQL(sqlStatements,true); + } + catch (const std::exception& e) + { + if(!db.rollback()) // 回滚失败时记录警告 + { + LOG_ERROR("DB", QString("Rollback failed. error: %1").arg(db.lastError().databaseText())); + } + return false; + } + if(!db.commit()) // 提交 + { + LOG_ERROR("DB", QString("Commit transaction failed. error: %1.").arg(db.lastError().databaseText())); + return false; + } + + strSQL = "DELETE FROM project_manager WHERE tag = ?"; + params.clear(); + params.append(sProject); + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete row %1 success").arg(sProject)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete row %1 failed").arg(sProject)); + return false; + } +} + +bool DataBase::ifDynamicTableExist(const QString& sTable) +{ + QStringList lstTable; + QString strSQL = "SELECT name FROM project_manager WHERE name = ?"; + QVariantList params; + params.append(sTable); + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} + +bool DataBase::updateProjectName(const QString& newTable,const QString& newPro,const QString& oldTable) +{ + QString strSQL = QString("UPDATE project_manager SET name = ?,tag = ? WHERE name = ?"); + QVariantList params; + params.append(newTable); + params.append(newPro); + params.append(oldTable); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update project_manager %1 fail").arg(oldTable)); + return false; + } +} + +bool DataBase::alterTableName(const QString& oldTable,const QString& newTable) +{ + QString strSQL = QString("ALTER TABLE %1 RENAME TO %2").arg(oldTable,newTable); + QVariantList params; + try + { + executeSQL(strSQL,true); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("ALTER TABLE %1 fail").arg(oldTable)); + return false; + } +} + +bool DataBase::updateComponentModelName(const QString& strOld,const QString& strNew) +{ + QString strSQL = QString("UPDATE component SET model_name = ? WHERE model_name = ?"); + QVariantList params; + params.append(strOld); + params.append(strNew); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update component model_name %1 fail").arg(strOld)); + return false; + } +} + +bool DataBase::deleteTable(const QString& sName) +{ + QString strSQL = QString("DROP TABLE IF EXISTS %1").arg(sName); + try + { + executeSQL(strSQL,true); + LOG_INFO("DB", QString("Drop table %1 success").arg(sName)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Drop table %1 failed").arg(sName)); + return false; + } +} + +bool DataBase::deleteRecordFromManager(const QString& sProject,const QString& sGroup) +{ + QString strSQL = "DELETE FROM project_manager WHERE tag = ? AND group_name = ?"; + QVariantList params; + params.append(sProject); + params.append(sGroup); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete row %1 success").arg(sProject)); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Delete row %1 failed").arg(sProject)); + return false; + } +} + +bool DataBase::modifyProjectTable(QString sTable,QMap mOld,QMap mNew) +{ + QStringList sqlStatements; + for (auto &col : mOld.keys()) { + if (!mNew.contains(col)) { + sqlStatements << QString("ALTER TABLE %1 DROP COLUMN %2") + .arg(sTable, col); + } + } + // 添加/修改列 + for (auto &col : mNew.keys()) { + const QString &newType = mNew[col]; + + // 新增列 + if (!mOld.contains(col)) { + sqlStatements << QString("ALTER TABLE %1 ADD COLUMN %2 %3") + .arg(sTable, col, newType); + } + // 修改列类型 + else if (mOld[col] != newType) { + sqlStatements << QString("ALTER TABLE %1 ALTER COLUMN %2 TYPE %3 USING %2::%3") + .arg(sTable, col, newType); + } + } + + if(!db.transaction()) + { + LOG_ERROR("DB", QString("Start transaction failed. error: %1.").arg( db.lastError().databaseText())); + return false; + } + try + { + executeBatchSQL(sqlStatements); + } + catch (const std::exception& e) + { + if(!db.rollback()) // 回滚失败时记录警告 + { + LOG_ERROR("DB", QString("Rollback failed. error: %1").arg(db.lastError().databaseText())); + } + return false; + } + if(!db.commit()) // 提交 + { + LOG_ERROR("DB", QString("Commit transaction failed. error: %1.").arg(db.lastError().databaseText())); + return false; + } + return true; +} + +QStringList DataBase::ifModelOccupy(const QString& sName) +{ + QStringList lst; + QMap map; + QString strSQL = "SELECT tag FROM component WHERE model_name = ?"; + QVariantList params; + params.append(sName); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + QString str = query.value(0).toString(); + lst.append(str); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::insertEditorProject(QUuid uid,QString name,QString tag) +{ + QString strSQL = "INSERT INTO diagramui_editor_projects(global_uuid, name, tag) VALUES (?, ?, ?)"; + + QVariantList params; + params.append(uid); + params.append(name); + params.append(tag); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_editor_projects fail")); + return false; + } +} + +QList DataBase::getAllEditorProject() +{ + QList lst; + QString strSQL = "SELECT id,global_uuid,name,tag FROM diagramui_editor_projects"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + EditorProjectInfo info; + info.id = query.value(0).toInt(); + info.uuid = QUuid(query.value(1).toString()); + info.name = query.value(2).toString(); + info.tag = query.value(3).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::deleteEditorProject(QString name) +{ + QString strSQL = "DELETE FROM diagramui_editor_projects WHERE name = ?"; + QVariantList params; + params.append(name); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete diagramui_editor_projects %1 success").arg(name)); + return true; + } + catch (const std::exception& e) + { + LOG_INFO("DB", QString("Delete diagramui_editor_projects %1 fail").arg(name)); + return false; + } +} + +bool DataBase::ifEditorProjectExist(QString name) +{ + QString strSQL = "SELECT * FROM diagramui_editor_projects WHERE name = ?"; + QVariantList params; + params.append(name); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} +/***********************basesetting****************************/ +bool DataBase::insertBaseSetting(QUuid uid,QString projectName,QString autorName,QByteArray context,QUuid generateId,QString ts) +{ + QString strSQL = "INSERT INTO diagramui_editor_basesetting(global_uuid, project_name, autor, context, generate_uuid, ts) VALUES (?, ?, ?, ?, ?, ?)"; + + QVariantList params; + params.append(uid); + params.append(projectName); + params.append(autorName); + params.append(context); + params.append(generateId); + params.append(ts); + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Insert diagramui_editor_basesetting fail")); + return false; + } +} +bool DataBase::updateBaseSetting(QUuid uid,QByteArray context,QString ts) +{ + QString strSQL = "UPDATE diagramui_editor_basesetting SET context = ?, ts = ? WHERE global_uuid = ?"; + QVariantList params; + params.append(context); + params.append(ts); + params.append(uid); + + try + { + executeSQL(strSQL,false,params); + return true; + } + catch (const std::exception& e) + { + LOG_ERROR("DB", QString("Update diagramui_editor_basesetting %1 fail").arg(uid.toString())); + return false; + } +} + +QByteArray DataBase::getBaseSettingByUid(QUuid uid) +{ + QByteArray byte; + QString strSQL = "SELECT context FROM diagramui_editor_basesetting WHERE global_uuid = ?"; + QVariantList params; + params.append(uid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + byte = query.value(0).toByteArray(); + } + query.clear(); + return byte; + } + catch (const std::exception& e) + { + return byte; + } +} + +EditorBaseSettingInfo DataBase::getBaseSettingInfo(QUuid uid) +{ + EditorBaseSettingInfo info; + QString strSQL = "SELECT project_name, autor, context, generate_uuid, ts FROM diagramui_editor_basesetting WHERE global_uuid = ?"; + QVariantList params; + params.append(uid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + info.projectName = query.value(0).toString(); + info.autor = query.value(1).toString(); + info.context = query.value(2).toByteArray(); + info.generateUid = QUuid(query.value(3).toString()); + info.ts = query.value(4).toString(); + info.uuid = uid; + } + query.clear(); + return info; + } + catch (const std::exception& e) + { + return info; + } +} + +QList DataBase::getAllBaseSetting() +{ + QList lst; + QString strSQL = "SELECT id,global_uuid, project_name, autor, context, generate_uuid, ts FROM diagramui_editor_basesetting"; + + try + { + QSqlQuery query = executeSQL(strSQL); + while (query.next()) + { + EditorBaseSettingInfo info; + info.id = query.value(0).toInt(); + info.uuid = QUuid(query.value(1).toString()); + info.projectName = query.value(2).toString(); + info.autor = query.value(3).toString(); + info.context = query.value(4).toByteArray(); + info.generateUid = QUuid(query.value(5).toString()); + info.ts = query.value(6).toString(); + + lst.append(info); + } + query.clear(); + return lst; + } + catch (const std::exception& e) + { + return lst; + } +} + +bool DataBase::deleteBaseSetting(QUuid uid) +{ + QString strSQL = "DELETE FROM diagramui_editor_basesetting WHERE global_uuid = ?"; + QVariantList params; + params.append(uid); + + try + { + executeSQL(strSQL,false,params); + LOG_INFO("DB", QString("Delete diagramui_editor_basesetting %1 success").arg(uid.toString())); + return true; + } + catch (const std::exception& e) + { + LOG_INFO("DB", QString("Delete diagramui_editor_basesetting %1 fail").arg(uid.toString())); + return false; + } +} + +bool DataBase::ifBaseSettingExist(QUuid uid) +{ + QString strSQL = "SELECT * FROM diagramui_editor_basesetting WHERE global_uuid = ?"; + QVariantList params; + params.append(uid); + + try + { + QSqlQuery query = executeSQL(strSQL,false,params); + while (query.next()) + { + return true; + } + } + catch (const std::exception& e) + { + return false; + } + return false; +} diff --git a/diagramUtils/source/dataManager.cpp b/diagramUtils/source/dataManager.cpp new file mode 100644 index 0000000..6040698 --- /dev/null +++ b/diagramUtils/source/dataManager.cpp @@ -0,0 +1,292 @@ +#include +#include "dataManager.h" +#include "dataBase.h" + +DataManager& DataManager::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static DataManager instance; + return instance; +} + +DataManager::DataManager(QObject *parent) + : QObject(parent) +{ + _stateInitialised = false; + _dataInitialised = false; +} + +DataManager::~DataManager() +{ + +} + +void DataManager::initialModelState(bool refresh) +{ + QMap model = DataBase::GetInstance()->getAllProjectModel(); + QMap mapAttribute = DataBase::GetInstance()->Attribute(); + + if(refresh) + { + for(auto &info:_modelStateInfo) + { + info.release(); + } + _modelStateInfo.clear(); + } + QMap::Iterator iter; + for(iter = model.begin();iter != model.end(); ++iter) //遍历模型 + { + ModelStateInfo modelInfo; + modelInfo.modelType = iter.value(); //模型类型 + modelInfo.modelName = iter.key(); + + QMap mapState = DataBase::GetInstance()->getModelInfo(iter.key()); + QMap mapPublic = DataBase::GetInstance()->getPublicInfo(); //公共属性组 + + QMap::Iterator it; + for(it = mapPublic.begin();it != mapPublic.end();++it) //遍历公共属性组 + { + GroupStateInfo groupInfo; + groupInfo.groupName = it.key(); + groupInfo.tableName = it->tableName; + groupInfo.isPublic = true; + QJsonArray nodesJsonArray = it->propertyState["checkState"].toArray(); + + for (QJsonValueRef nodeJson : nodesJsonArray) //每个属性的状态信息 + { + PropertyStateInfo propertyInfo; + + QJsonObject node = nodeJson.toObject(); + QString propertyTag = node["name"].toString(); + QString propertyName; + for(auto& info:mapAttribute){ + if(info.attribute == propertyTag){ + propertyName = info.attributeName; + break; + } + } + int nState = node["checked"].toInt(); + QString dataType = node["type"].toString(); + QVariant defaultValue = node["defaultValue"].toVariant(); + int lengthPrecision = node["lengthPrecision"].toInt(); + int nIsVisible = node["isVisible"].toInt(); + if(nState) + { + propertyInfo.tagName = propertyTag; + propertyInfo.name = propertyName; + propertyInfo.type = dataType; + propertyInfo.defaultValue = defaultValue; + propertyInfo.lengthPrecision = lengthPrecision; + propertyInfo.isVisible = nIsVisible; + + groupInfo.info.insert(propertyName,propertyInfo); + } + } + modelInfo.groupInfo.insert(it.key(),groupInfo); + } + + for(it = mapState.begin();it != mapState.end();++it) //遍历模型属性组 + { + GroupStateInfo groupInfo; + groupInfo.groupName = it.key(); + groupInfo.tableName = it->tableName; + QJsonArray nodesJsonArray = it->propertyState["checkState"].toArray(); + + for (QJsonValueRef nodeJson : nodesJsonArray) //每个属性的状态信息 + { + PropertyStateInfo propertyInfo; + + QJsonObject node = nodeJson.toObject(); + QString propertyTag = node["name"].toString(); + QString propertyName; + for(auto& info:mapAttribute){ + if(info.attribute == propertyTag){ + propertyName = info.attributeName; + break; + } + } + int nState = node["checked"].toInt(); + QString dataType = node["type"].toString(); + QVariant defaultValue = node["defaultValue"].toVariant(); + int lengthPrecision = node["lengthPrecision"].toInt(); + int nIsVisible = node["isVisible"].toInt(); + if(nState) + { + propertyInfo.tagName = propertyTag; + propertyInfo.name = propertyName; + propertyInfo.type = dataType; + propertyInfo.defaultValue = defaultValue; + propertyInfo.lengthPrecision = lengthPrecision; + propertyInfo.isVisible = nIsVisible; + + groupInfo.info.insert(propertyName,propertyInfo); + } + } + modelInfo.groupInfo.insert(it.key(),groupInfo); + } + + _modelStateInfo.insert(iter.key(),modelInfo); + } + _stateInitialised = true; +} + +void DataManager::initialModelData(bool refresh) +{ + QMap model = DataBase::GetInstance()->getAllProjectModel(); + QMap mapAttribute = DataBase::GetInstance()->Attribute(); + if(!refresh){ + QMap::Iterator iter; + for(iter = model.begin();iter != model.end(); ++iter) //遍历模型 + { + ModelDataInfo modelInfo; + modelInfo.modelType = iter.value(); //模型类型 + modelInfo.modelName = iter.key(); + + QMap mapState = DataBase::GetInstance()->getModelInfo(iter.key()); + QMap mapPublic = DataBase::GetInstance()->getPublicInfo(); //公共属性组 + QMap::Iterator it; + + for(it = mapPublic.begin();it != mapPublic.end();++it) //遍历公共属性组 + { + GroupStateValue groupValue; + groupValue.groupName = it.key(); + groupValue.tableName = it->tableName; + QJsonArray nodesJsonArray = it->propertyState["checkState"].toArray(); + + QMap mapPro; + for (QJsonValueRef nodeJson : nodesJsonArray) //每个属性的状态信息 + { + PropertyStateInfo propertyInfo; + + QJsonObject node = nodeJson.toObject(); + QString propertyTag = node["name"].toString(); + QString propertyName; + for(auto& info:mapAttribute){ + if(info.attribute == propertyTag){ + propertyName = info.attributeName; + break; + } + } + int nState = node["checked"].toInt(); + QString dataType = node["type"].toString(); + QVariant defaultValue = node["defaultValue"].toVariant(); + int nIsVisible = node["isVisible"].toInt(); + + propertyInfo.tagName = propertyTag; + propertyInfo.name = propertyName; + propertyInfo.type = dataType; + propertyInfo.isVisible = nIsVisible; + if(nState) + { + mapPro.insert(propertyTag,propertyInfo); + } + } + if(!mapPro.contains("global_uuid")) //不包含uuid则手动添加 + { + PropertyStateInfo uuidInfo; + uuidInfo.tagName = "global_uuid"; //全局id未添加到属性状态中,手动添加 + mapPro.insert("global_uuid",uuidInfo); + } + + groupValue.mapInfo = DataBase::GetInstance()->selectGroupPropertyByState(it->tableName,mapPro); //返回表中属性值 + + modelInfo.groupInfo.insert(it.key(),groupValue); + } + + for(it = mapState.begin();it != mapState.end();++it) //遍历模型属性组 + { + GroupStateValue groupValue; + groupValue.groupName = it.key(); + groupValue.tableName = it->tableName; + QJsonArray nodesJsonArray = it->propertyState["checkState"].toArray(); + + QMap mapPro; + for (QJsonValueRef nodeJson : nodesJsonArray) //每个属性的状态信息 + { + PropertyStateInfo propertyInfo; + + QJsonObject node = nodeJson.toObject(); + QString propertyTag = node["name"].toString(); + QString propertyName; + for(auto& info:mapAttribute){ + if(info.attribute == propertyTag){ + propertyName = info.attributeName; + break; + } + } + int nState = node["checked"].toInt(); + QString dataType = node["type"].toString(); + QVariant defaultValue = node["defaultValue"].toVariant(); + int nIsVisible = node["isVisible"].toInt(); + + propertyInfo.tagName = propertyTag; + propertyInfo.name = propertyName; + propertyInfo.type = dataType; + propertyInfo.isVisible = nIsVisible; + if(nState) + { + mapPro.insert(propertyTag,propertyInfo); + } + } + PropertyStateInfo uuidInfo; + uuidInfo.tagName = "global_uuid"; //全局id未添加到属性状态中,手动添加 + mapPro.insert("global_uuid",uuidInfo); + + groupValue.mapInfo = DataBase::GetInstance()->selectGroupPropertyByState(it->tableName,mapPro); //返回表中属性值 + + modelInfo.groupInfo.insert(it.key(),groupValue); + } + _modleDataInfo.insert(iter.key(),modelInfo); + } + _dataInitialised = true; + } + else + { + for(auto itMod = _modleDataInfo.begin();itMod != _modleDataInfo.end();itMod++) //模型 + { + for(auto itGroup = itMod->groupInfo.begin();itGroup != itMod->groupInfo.end();itGroup++) //属性组 + { + for(auto itComponent = itGroup->mapInfo.begin();itComponent != itGroup->mapInfo.end();itComponent++) //设备 + { + //获取component对应id的属性 + PropertyValueInfo info = DataBase::GetInstance()->selectGroupPropertyByValue(itGroup->tableName,itComponent.key(),itComponent.value()); + for(auto itPro = itComponent->begin(); itPro != itComponent->end();itPro++) //属性 + { + if(info.contains(itPro.key())){ + if(!itPro->lock) + itPro->defaultValue = info.value(itPro.key()).defaultValue; + } + } + } + } + } + } +} + +void DataManager::updateModelData(const QString& sModel,QUuid uuid,const QString& sGroup,QMap mapPro) +{ + if(_modleDataInfo[sModel].groupInfo[sGroup].mapInfo.contains(uuid)) + _modleDataInfo[sModel].groupInfo[sGroup].mapInfo[uuid] = mapPro; //暂用设定值直接替换旧属性,待测试 + else{ //新增 + _modleDataInfo[sModel].groupInfo[sGroup].mapInfo.insert(uuid,mapPro); + } +} + +ModleStateMap& DataManager::modelState() +{ + if(!_stateInitialised) + initialModelState(); + else + initialModelState(true); + return _modelStateInfo; +} + +ModelDataMap& DataManager::modelData() +{ + if(!_dataInitialised) + initialModelData(); + else + initialModelData(true); + return _modleDataInfo; +} diff --git a/diagramUtils/source/logger.cpp b/diagramUtils/source/logger.cpp new file mode 100644 index 0000000..9b9c8e8 --- /dev/null +++ b/diagramUtils/source/logger.cpp @@ -0,0 +1,155 @@ +#include "logger.h" +#include +#include +#include +#include + +Logger& Logger::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static Logger instance; + return instance; +} + +Logger::Logger() +{ + initialize(); +} + +Logger::~Logger() +{ + shutdown(); +} + +void Logger::initialize() +{ + //默认配置 + m_logFilePath = ""; + m_logLevel = INFO; + m_maxFileSize = 1024 *1024 * 10; //10MB + m_maxBackupFiles = 5; + m_outputToConsole = true; + m_outputOtFile = true; + //从配置文件中加载配置 + loadConfig(); +} + +void Logger::loadConfig(/*const QString& configFilePath*/) +{ + //QString filePath = Settings::instance().value("Log", "logFile").toString(); + QString filePath = QCoreApplication::applicationDirPath() + "/log/app.log"; + setLogFile(filePath); + + /*QString strLevel = Settings::instance().value("Log", "level").toString().toUpper(); + if(strLevel == "DEBUG") + m_logLevel = DEBUG; + else if(strLevel == "INFO") + m_logLevel = INFO; + else if(strLevel == "WARNING") + m_logLevel = WARNING; + else if(strLevel == "ERROR") + m_logLevel = ERROR; + else if(strLevel == "FATAL") + m_logLevel = FATAL; + + m_maxFileSize = Settings::instance().value("Log", "maxSize").toLongLong(); + m_maxBackupFiles = Settings::instance().value("Log", "backups").toInt(); + QString strOutputToConsole = Settings::instance().value("Log", "consoleOutput").toString(); + if(strOutputToConsole == "true") + m_outputToConsole = true; + else + m_outputToConsole = false; + QString strOutputToFile = Settings::instance().value("Log", "fileOutput").toString(); + if(strOutputToFile == "true") + m_outputOtFile = true; + else + m_outputOtFile = false;*/ +} + +void Logger::setLogFile(const QString& filePath) +{ + //检查目录文件所在目录,如果不存在则创建目录 + QFileInfo fileInfo(filePath); + QDir logDir = fileInfo.dir(); + if(!logDir.exists()) + logDir.mkpath("."); + + //更新log文件前要先关闭当前已打开的文件 + if(m_logFile.isOpen()) + m_logFile.close(); + + m_logFilePath = filePath; + m_logFile.setFileName(filePath); +} + +void Logger::shutdown() +{ + if(m_logFile.isOpen()) + m_logFile.close(); +} + +void Logger::writeToFile(const QString& message) +{ + if(m_logFilePath.isEmpty()) + return; + + if(!m_logFile.isOpen()) + { + if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) + { + qWarning() << "Failed to open log file:" << m_logFile.errorString(); + return; + } + //打开文件时先键入一个换行符 + QTextStream stream(&m_logFile); + stream << Qt::endl; + stream.flush(); //刷新输出缓冲区,确保数据立即写入文件 + } + + QTextStream stream(&m_logFile); + stream << message << Qt::endl; + stream.flush(); //刷新输出缓冲区,确保数据立即写入文件 + + if(m_logFile.size() > m_maxFileSize) + rollLogFiles(); +} + +void Logger::rollLogFiles() +{ + if(m_logFile.isOpen()) + m_logFile.close(); + + //删除最旧的备份文件(备份文件以‘日志文件.数字’的格式命名,数字越大表示文件越旧) + QFile::remove(QString("%1.%2").arg(m_logFilePath).arg(m_maxBackupFiles)); + //剩余文件依次更改名称 + for(int i = m_maxBackupFiles - 1; i > 0; i--) + QFile::rename(QString("%1.%2").arg(m_logFilePath).arg(i), QString("%1.%2").arg(m_logFilePath).arg(i + 1)); + //将当前日志文件更改为'最新'的备份文件(编号为1) + QFile::rename(m_logFilePath, QString("%1.1").arg(m_logFilePath)); + //更新当前配置文件(重新打开) + m_logFile.setFileName(m_logFilePath); + if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) + qWarning() << "Failed to open new log file after rolling:" << m_logFile.errorString(); +} + +QString Logger::formatLogMessage(LogLevel level, const QString& context, const QString& message) +{ + static const char* levelStrings[] = {"FATAL", "ERROR", "WARNING", "INFO", "DEBUG"}; + return QString("[%1] [%2] [%3] %4") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")) + .arg(levelStrings[level]) + .arg(context) + .arg(message); +} + +void Logger::log(LogLevel level, const QString& context, const QString& message) +{ + if(level > m_logLevel) + return; + + QString formatMessage = formatLogMessage(level, context, message); + if(m_outputToConsole) + QTextStream(stderr) << formatMessage << Qt::endl; + if(m_outputOtFile) + writeToFile(formatMessage); +} diff --git a/diagramUtils/source/projectManager.cpp b/diagramUtils/source/projectManager.cpp new file mode 100644 index 0000000..ee6ed34 --- /dev/null +++ b/diagramUtils/source/projectManager.cpp @@ -0,0 +1,89 @@ +#include "projectManager.h" +#include "dataBase.h" +#include + +ProjectManager& ProjectManager::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static ProjectManager instance; + return instance; +} + +ProjectManager::ProjectManager(QObject *parent) + : QObject(parent) +{ + +} + +ProjectManager::~ProjectManager() +{ + +} + +void ProjectManager::saveEditorDataToDB(QUuid uid,const QString& name,const QString& tag,QByteArray byte,QString autor,QString sTime) +{ + bool val = DataBase::GetInstance()->ifEditorProjectExist(name); + if(!val) + DataBase::GetInstance()->insertEditorProject(uid,name,tag); + QUuid uidSetting = QUuid::createUuid(); //暂定没有更新,变更后存储为新记录 + bool res = DataBase::GetInstance()->insertBaseSetting(uidSetting,name,autor,byte,QUuid(),sTime); + if(res) + emit editorSaved(name,autor,uidSetting,sTime); +} + +QByteArray ProjectManager::getEditorBaseSettingByUid(QUuid uid) +{ + return DataBase::GetInstance()->getBaseSettingByUid(uid); +} + +bool ProjectManager::createEditorProject(const QString& name) +{ + bool bExist = DataBase::GetInstance()->ifEditorProjectExist(name); + if(bExist) + return true; + QUuid uid = QUuid::createUuid(); + emit createNewEditor(name,uid); + return false; +} + +void ProjectManager::unloadEditorProject(const QString& name) +{ + emit prepareUnloadProject(name); +} + +void ProjectManager::openSetting(const QString& name) +{ + emit prepareOpenSetting(name); +} + +void ProjectManager::saveEditor(const QString& name) +{ + emit prepareSaveEditor(name); +} + +bool ProjectManager::deleteEditor(const QString& name,QUuid id) +{ + emit prepareDeleteBaseSetting(name,id); + return DataBase::GetInstance()->deleteBaseSetting(id); +} + +void ProjectManager::loadBaseSetting(const QString& str,QUuid id) +{ + emit prepareLoadBaseSetting(str,id); +} + +QList ProjectManager::getBaseSettingsByProject(const QString& strPro) +{ + QList lst; + QList lstAll = DataBase::GetInstance()->getAllBaseSetting(); + for(auto &info:lstAll){ + if(info.projectName == strPro) + lst.append(info); + } + return lst; +} + +EditorBaseSettingInfo ProjectManager::getBaseSetting(QUuid uid) +{ + return DataBase::GetInstance()->getBaseSettingInfo(uid); +} diff --git a/diagramUtils/source/projectModelManager.cpp b/diagramUtils/source/projectModelManager.cpp new file mode 100644 index 0000000..ecbd6bf --- /dev/null +++ b/diagramUtils/source/projectModelManager.cpp @@ -0,0 +1,924 @@ +#include +#include "projectModelManager.h" +#include "dataBase.h" +#include "logger.h" +#include "common/core_model/types.h" +const QSet stringDataTypes = {"VARCHAR", "CHAR", "TEXT", "DATE", "TIME", "TIMESTAMP","JSONB","JSON"}; + +ProjectModelManager& ProjectModelManager::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static ProjectModelManager instance; + return instance; +} + +ProjectModelManager::ProjectModelManager(QObject *parent) + : QObject(parent) +{ + _bInitialised = false; +} + +ProjectModelManager::~ProjectModelManager() +{ + m_mapTotalData.clear(); +} + +void ProjectModelManager::initialModel() +{ + if(_bInitialised) + return; + QStringList lstModel = getModelList(); + for(auto &sModel:lstModel) + { + MapProject mp; + QMap mapProject = DataBase::GetInstance()->getProjectFromManager(sModel); + + + QMap::Iterator iter; + for(iter = mapProject.begin();iter != mapProject.end(); ++iter) + { + PropertyModel pm; + pm.pBase = new QStandardItemModel(this); + pm.pSelect = new QStandardItemModel(this); + pm.mapProperty = addNewProject(sModel,iter.key(),pm); + pm.nType = iter.value(); + pm.formerMeta.sName = sModel; + pm.formerProject.sName = iter.key(); + pm.dataInfo = DataBase::GetInstance()->getProjectModelGroupInfo(iter.key()); + pm.modelSetting = getModelSetting(sModel,iter.key()); + mp.insert(iter.key(),pm); + } + + m_mapTotalData.insert(sModel,mp); + } + + _bInitialised = true; +} + +void ProjectModelManager::initialHMISourcr() +{ + if(_bHMISourceInitialised) + return; + QList lst = DataBase::GetInstance()->getAllHMIimage(); + for(auto& info:lst){ + m_mapHMIimage.insert(info.hash256,info); + } + + _bHMISourceInitialised = true; +} + +void ProjectModelManager::generate(const QString& sMeta,const QString& sPro) +{ + MapMeta::Iterator iter = m_mapTotalData.find(sMeta); //获取元模下的工程 + if(iter != m_mapTotalData.end()) + { + MapProject mp = iter.value(); + MapProject::Iterator ite = mp.find(sPro); //获取工程下的属性组 + if(ite != mp.end()) + { + MapProperty mapProperty = ite.value().mapProperty; + + int createRes = 0; //动态表生成结果 + for(MapProperty::Iterator it = mapProperty.begin();it != mapProperty.end();++it){ //每个属性组单独生成表 + int nType = ite.value().nType; + QStandardItemModel* pSelectModel = ite->pSelect; + QStandardItemModel* pBaseModel = ite->pBase; + + QList lstSelected = getGroupSub(pSelectModel,it.key()); + QList lstBase = getGroupSub(pBaseModel,it.key()); + if(!lstSelected.isEmpty()) + { + bool isPub = it->isPublic; + int res = createPropertyTable(sMeta,sPro,it.key(),lstSelected,lstBase,nType,isPub); + switch (res){ + case int(AlertInfo::Success): + LOG_INFO("DB", QString("create %1 dynamicTable success").arg(sPro)); + break; + case int(AlertInfo::Fail): + LOG_WARN("DB", QString("create %1 dynamicTable fail").arg(sPro)); + break; + case int(AlertInfo::Exist): + LOG_WARN("DB", QString("%1 dynamicTable exist").arg(sPro)); + break; + default: + break; + } + createRes = createRes | res; + } + } + if(!(createRes & int(AlertInfo::Fail))) //结果不含失败就成功 + { + //QMessageBox::information(NULL, QString::fromWCharArray(L"提示"), QString::fromWCharArray(L"创建表成功")); + emit modelChange(); + } + else //创建失败 + { + //QMessageBox::information(NULL, QString::fromWCharArray(L"提示"), QString::fromWCharArray(L"创建表失败")); + } + } + } +} + +MapProperty ProjectModelManager::addNewProject(const QString& sMeta,const QString& sProject,PropertyModel& model) +{ + MapProperty mt; + QStringList lstProperty = getGroupList(sMeta); //返回元模下的属性组名 + QStringList lstProPublic = getPublicGroupList(); //返回公共属性组 + //lstProperty< mapCheckState = DataBase::GetInstance()->getCheckStateFromManager(sProject); //获取选择状态 + for(auto &property:lstProperty) + { + PropertyPage struProperty; + struProperty.isPublic = false; + + if(mapCheckState.contains(property) && !model.formerMeta.bChanged) //生成的模型中勾选了该属性组且元模未改变过 + { + QJsonObject obj = mapCheckState[property]; + QJsonArray nodesJsonArray = obj["checkState"].toArray(); + + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QString propertyName = node["name"].toString(); + int nState = node["checked"].toInt(); + QString dataType = node["type"].toString(); + QString defaultValue = node["defaultValue"].toString(); + + QStandardItem* pItem = new QStandardItem(propertyName); + setItemAttribute(propertyName,pItem); + PropertyState sta; + sta.dataType = dataType; + if(nState) + { + QStandardItem* pGroup = nullptr; + if(model.pSelect->findItems(property).isEmpty()) + { + pGroup = new QStandardItem(property); + model.pSelect->appendRow(pGroup); //属性的组未存在,将组添加到model + } + else + { + pGroup = model.pSelect->findItems(property)[0]; + } + + //QModelIndex index = findIndex(model.pSelect,propertyName); + + if(pGroup){ + pGroup->appendRow(pItem); + sta.checkState = true; + struProperty.checkState.insert(propertyName,sta); + } + } + else + { + QStandardItem* pGroup = nullptr; + if(model.pBase->findItems(property).isEmpty()) + { + pGroup = new QStandardItem(property); + model.pBase->appendRow(pGroup); //属性的组未存在,将组添加到model + } + else + { + pGroup = model.pBase->findItems(property)[0]; + } + + if(pGroup){ + pGroup->appendRow(pItem); + sta.checkState = false; + struProperty.checkState.insert(propertyName,sta); + } + } + } + } + else //未勾选属性组或是新建或元模已改变 + { + QStandardItem* pGroup = nullptr; //新建的不包含属性组 + pGroup = new QStandardItem(property); + model.pBase->appendRow(pGroup); + + QStringList lstName = getAttributeList(sMeta,property); + for(auto &name:lstName) + { + QStandardItem* pItem = new QStandardItem(name); + setItemAttribute(name,pItem); + pGroup->appendRow(pItem); + QString dataType = getItemDataType(pItem); + PropertyState sta; + sta.dataType = dataType; + sta.checkState = false; + struProperty.checkState.insert(name,sta); //初始都是未选择状态 + } + } + mt.insert(property,struProperty); + } + + MapProperty pubMap; + for(auto &pro:lstProPublic) //公共属性组 + { + PropertyPage struProperty; + struProperty.isPublic = true; + + QStandardItem* pGroup = nullptr; + pGroup = new QStandardItem(pro); + //model.pSelect->appendRow(pGroup); //公共属性组默认都包含 + model.pSelect->insertRow(0,pGroup); + + QStringList lstName = getPublicAttributeList(pro); + for(auto &name:lstName) + { + QStandardItem* pItem = new QStandardItem(name); + pItem->setData(QColor(60,140,180,180), Qt::BackgroundRole); + setItemAttribute(name,pItem); + pGroup->appendRow(pItem); + QString dataType = getItemDataType(pItem); + PropertyState sta; + sta.dataType = dataType; + sta.checkState = true; + sta.editable = false; + struProperty.checkState.insert(name,sta); //初始都已经选择 + } + pubMap.insert(pro,struProperty); + } + + for (auto it = mt.begin(); it != mt.end(); ++it) { //将正常属性添到公共属性后 + if(pubMap.contains(it.key())) + continue; //公共属性组已有的不重复添加 + pubMap.insert(it.key(), it.value()); + } + + return pubMap; +} + + +QList ProjectModelManager::getGroupSub(QStandardItemModel* pModel,const QString& str) +{ + QList lst; + if(!pModel) + return lst; + QList items = pModel->findItems(str); + if (!items.isEmpty()) { + // 存在该文本的项 + QStandardItem* item = items[0]; + for (int row = 0; row < item->rowCount(); ++row) { + // 默认获取第 0 列的子项 + QStandardItem* child = item->child(row, 0); + if (child) { + lst.append(child); + } + } + } + return lst; +} + +void ProjectModelManager::deleteData(const QString& sMeta,const QString& sProject) +{ + delete m_mapTotalData[sMeta][sProject].pBase; //手动释放 + delete m_mapTotalData[sMeta][sProject].pSelect; + m_mapTotalData[sMeta].remove(sProject); +} + +void ProjectModelManager::updateSetting(const QString& sMeta,const QString& sProject,bool toHex) +{ + QJsonObject object; + QJsonArray arr; + QJsonArray arrUsed; + QMap mapSvg = m_mapTotalData[sMeta][sProject].modelSetting.mapSvg; + QMap mapUsedSvg = m_mapTotalData[sMeta][sProject].modelSetting.mapUsedSvg; + + for(auto iter = mapSvg.begin();iter != mapSvg.end();++iter) + { + QJsonObject obj; + obj["name"] = iter.key(); + QString sData; + if(toHex){ + QByteArray svgString = QString(iter.value()).toUtf8().toHex(); + sData = QString::fromUtf8(svgString); + } + else + sData = QString::fromUtf8(iter.value()); + obj["data"] = sData; + arr.push_back(obj); + } + object["picture"] = arr; + + for(auto iter = mapUsedSvg.begin();iter != mapUsedSvg.end();++iter) + { + QJsonObject obj; + obj["name"] = iter.key(); + QString sData; + if(toHex){ + QByteArray svgString = QString(iter.value()).toUtf8().toHex(); + sData = QString::fromUtf8(svgString); + } + else + sData = QString::fromUtf8(iter.value()); + obj["data"] = sData; + arrUsed.push_back(obj); + } + object["usingPicture"] = arrUsed; + + QJsonObject jsVal = DataBase::GetInstance()->getProjectSetting(sMeta,sProject); + if(jsVal.empty()){ + DataBase::GetInstance()->insertProjectSetting(sMeta,sProject,object); + } + else{ + DataBase::GetInstance()->updateProjectSetting(sMeta,sProject,object); + } +} + +QStringList ProjectModelManager::getProjectModelLst(const QString& sMeta) +{ + //return DataBase::GetInstance()->getProjectWithinBase(sMeta); + QStringList lst; + QMap mapPro = m_mapTotalData.value(sMeta); + for(auto it = mapPro.begin();it != mapPro.end();++it) + { + lst.append(it.key()); + } + return lst; +} + +QStringList ProjectModelManager::getModelList() const +{ + QMap modelMap = DataBase::GetInstance()->ModelType(); + + QSet modelSet; + for(auto &model:modelMap) + { + modelSet.insert(model.modelType); + } + + return QStringList(modelSet.values()); +} + +QStringList ProjectModelManager::getGroupList(const QString& sM) const +{ + QMap modelType = DataBase::GetInstance()->ModelType(); + QMap modelGroupMap = DataBase::GetInstance()->ModelGroup(); + QMap groupMap = DataBase::GetInstance()->AttributeGroup(); + + int metaId = 0; + for(auto &meta:modelType) + { + if(sM == meta.modelType) //查找元模对应的id + { + metaId = meta.id; + break; + } + } + + QList lstGroupId; + for(auto &group:modelGroupMap) //找到元模id对应的属性组id + { + if(group.modelTypeId == metaId) + { + lstGroupId.push_back(group.attributeGroupId); + } + } + + QStringList groupList; + for(auto &id:lstGroupId) //从属性组中找到id对应的组名 + { + groupList.append(groupMap[id].groupType); + } + + return groupList; +} + +QStringList ProjectModelManager::getPublicGroupList() const +{ + QMap modelAttPublic = DataBase::GetInstance()->ModelAttributePublic(); + QMap groupMap = DataBase::GetInstance()->AttributeGroup(); + + QSet setGroup; + for(auto &model:modelAttPublic) + { + setGroup.insert(model.attributeGroupId); + } + + QStringList groupList; + for(auto &id:setGroup) + { + groupList.append(groupMap[id].groupType); + } + return groupList; +} + +QStringList ProjectModelManager::getAttributeList(const QString& sM,const QString& sG) const +{ + QMap modelType = DataBase::GetInstance()->ModelType(); + QMap groupMap = DataBase::GetInstance()->AttributeGroup(); + QMap modelAttMap = DataBase::GetInstance()->ModelAttribute(); + QMap attMap = DataBase::GetInstance()->Attribute(); + + int metaId = -1; + for(auto &meta:modelType) + { + if(sM == meta.modelType) //查找元模对应的id + { + metaId = meta.id; + break; + } + } + + int groupId = -1; + for(auto &attGroup:groupMap) + { + if(attGroup.groupType == sG) //返回参数属性组名对应的id + { + groupId = attGroup.id; + break; + } + } + + QStringList lst; + for(auto &mt:modelAttMap) + { + if(mt.modelTypeId == metaId && mt.attributeGroupId == groupId) + { + if(attMap[mt.attributeId].isVisible == 2) //2为特殊属性,不加入选择 + continue; + lst.append(attMap[mt.attributeId].attribute); + } + } + + return lst; +} + +QStringList ProjectModelManager::getPublicAttributeList(const QString& group) +{ + QMap modelAttPublic = DataBase::GetInstance()->ModelAttributePublic(); + QMap groupMap = DataBase::GetInstance()->AttributeGroup(); + QMap attMap = DataBase::GetInstance()->Attribute(); + + int groupId = -1; + for(auto &attGroup:groupMap) + { + if(attGroup.groupType == group) //返回参数属性组名对应的id + { + groupId = attGroup.id; + break; + } + } + + QStringList lst; + for(auto &mt:modelAttPublic) + { + if(mt.attributeGroupId == groupId) + { + lst.append(attMap[mt.attributeId].attribute); + } + } + + return lst; +} + +void ProjectModelManager::setItemAttribute(const QString& name,QStandardItem* p) +{ + QMap attMap = DataBase::GetInstance()->Attribute(); + QMap dt = DataBase::GetInstance()->DataType(); + + for(auto &att:attMap) + { + QString sType = dt[att.dataTypeId].dataType; //获得属性id对应的属性名 + if(name == att.attribute) + { + p->setData(att.id,Id); + p->setData(att.attribute,Attribute); + p->setData(att.attributeName,AttributeName); + p->setData(sType,DataType); //不直接使用id,拼接完成str + p->setData(att.lengthPrecision,LengthPrecision); + p->setData(att.scale,Scale); + p->setData(att.isNotNull,IsNotNull); + p->setData(att.defaultValue,DefaultValue); + p->setData(att.valueRange,ValueRange); + p->setData(att.isVisible,IsVisible); + return; + } + } +} + +QPair ProjectModelManager::combinePropertySql(const QStandardItem* pItem) +{ + QMap dt = DataBase::GetInstance()->DataType(); + + int id = pItem->data(Id).toInt(); + QString attribute = pItem->data(Attribute).toString(); + QString dataType = pItem->data(DataType).toString(); + int lengthPrecision = pItem->data(LengthPrecision).toInt(); + int scale = pItem->data(Scale).toInt(); + QString defaultValue = pItem->data(DefaultValue).toString(); + QString valueRange = pItem->data(ValueRange).toString(); + int isNotNull = pItem->data(IsNotNull).toInt(); + QString attributeName = pItem->data(AttributeName).toString(); + + bool needsQuotes = stringDataTypes.contains(dataType); + // 处理数据类型及其长度精度 + QString dataTypePart = dataType; + if (lengthPrecision > 0) { + dataTypePart += QString("(%1").arg(lengthPrecision); + if (scale > 0) { + dataTypePart += QString(",%1").arg(scale); + } + dataTypePart += ")"; + } + + // 开始拼接SQL + QString sql = QString("%1 %2").arg(attribute, dataTypePart); + + // 处理约束条件 + if (isNotNull != -1) { + sql += " NOT NULL"; + } + + if (!defaultValue.isEmpty()) { + QString defValue = defaultValue; + if (needsQuotes && defValue != "null" && defValue != "NULL") { + // 转义单引号并包裹 + defValue.replace("'", "''"); + defValue = QString("'%1'").arg(defValue); + } + sql += QString(" DEFAULT %1").arg(defValue); + } + + /*if (isPrimaryKey != 0) { + sql += " PRIMARY KEY"; + }*/ + + return qMakePair(sql,dataTypePart); +} + +bool ProjectModelManager::ifProjectEqual(const QString& sMeta,const QString& sProject,QMap map) +{ + //todo:判断关联的模型类型 + MapProperty curPro = m_mapTotalData[sMeta][sProject].mapProperty; + + QMap::Iterator iter; + for(iter = curPro.begin();iter != curPro.end();++iter) + { + if(iter->isPublic) //公共组跳过判断 + continue; + if(!map.contains(iter.key())) //已存在的模型中不包含该属性组 + { + QMap curCheckState = iter.value().checkState; + for(auto &val:curCheckState) + { + if(val.checkState) //该属性组不在模型中且被勾选 + return false; + } + continue; //该属性组不在模型中且未被勾选,不做判断 + } + else + { + QJsonObject dbCheckState = map[iter.key()]; //数据库中该属性组的勾选状态 + QMap curCheckState = iter.value().checkState; //当前程序中的勾选状态 + + QJsonArray nodesJsonArray = dbCheckState["checkState"].toArray(); + if(nodesJsonArray.size() != curCheckState.size()) //属性个数对不上,模型不同 + return false; + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QString propertyName = node["name"].toString(); + int nState = node["checked"].toInt(); + if(curCheckState[propertyName].checkState != nState) //相同属性选中状态不同 + return false; + } + } + } + return true; +} + +QString ProjectModelManager::modifyProjectModel(const QString& sMeta,const QString& sProject,QMap mapOld) +{ + QString sRes; + MapProperty curPro = m_mapTotalData[sMeta][sProject].mapProperty; + QStandardItemModel* pSelectModel = m_mapTotalData[sMeta][sProject].pSelect; + QStandardItemModel* pBaseModel = m_mapTotalData[sMeta][sProject].pBase; + int nType = m_mapTotalData[sMeta][sProject].nType; + + QMap::Iterator iter; + for(iter = curPro.begin();iter != curPro.end();++iter) //遍历当前模型所有属性组 + { + QMap curCheckState = iter.value().checkState; //当前程序中的属性勾选状态 + bool isNull = true; //当前属性组是否未空 + for(auto &val:curCheckState) + { + if(val.checkState){ + isNull = false; //当前程序模型有勾选,不为空 + break; + } + } + if(isNull){ + if(mapOld.contains(iter.key())){ //当前模型勾选为空且库模型中包含此属性组,移除库中该属性组表 + QMap map = DataBase::GetInstance()->getProjectTableName(sProject); + bool res = DataBase::GetInstance()->deleteTable(map[iter.key()]); + if(res){ + DataBase::GetInstance()->deleteRecordFromManager(sProject,iter.key()); + sRes = QString::fromWCharArray(L"修改模型成功"); + } + } + } + else{ + QList lstSelected = ProjectModelManager::instance().getGroupSub(pSelectModel,iter.key()); + QList lstBase = ProjectModelManager::instance().getGroupSub(pBaseModel,iter.key()); + if(mapOld.contains(iter.key())){ //新旧模型中都存在,修改模型 + QMap oldSchema; //库中模型 属性名/数据类型 + QMap newSchema; //现有模型 + + QJsonObject obj = mapOld[iter.key()]; + QJsonArray nodesJsonArray = obj["checkState"].toArray(); + + for (QJsonValueRef nodeJson : nodesJsonArray) + { + QJsonObject node = nodeJson.toObject(); + QString propertyName = node["name"].toString(); + int nState = node["checked"].toInt(); + QString dataType = node["type"].toString(); + + if(nState) + oldSchema.insert(propertyName,dataType); + } + + QMap::Iterator it; + for(it = curCheckState.begin(); it != curCheckState.end();++it) + { + if(it->checkState){ + newSchema.insert(it.key(),it->dataType); + } + } + if(oldSchema == newSchema) + continue; + + QMap map = DataBase::GetInstance()->getProjectTableName(sProject); + + bool res = DataBase::GetInstance()->modifyProjectTable(map[iter.key()],oldSchema,newSchema); + + QJsonObject objState = getSelectedState(lstSelected,lstBase); + DataBase::GetInstance()->updateCheckState(map[iter.key()],objState); + if(res) + { + sRes = QString::fromWCharArray(L"修改模型成功"); + } + } + else{ //非空且库模型中不存在,新增 + bool res = createPropertyTable(sMeta,sProject,iter.key(),lstSelected,lstBase,0,iter->isPublic); + if(res) + { + sRes = QString::fromWCharArray(L"修改模型成功"); + } + } + } + } + return sRes; +} + +bool ProjectModelManager::renameProjectModel(const QString& strCur,QMap datas) +{ + for(auto &data:datas) + { + QString sTable = data.metaModel + QString("_") + strCur + QString("_")+data.groupName; + + DataBase::GetInstance()->updateProjectName(sTable,strCur,data.name); + DataBase::GetInstance()->alterTableName(data.name,sTable); + } + return true; +} + +void ProjectModelManager::updateComponentModelName(const QString& strOld,const QString& strNew) +{ + DataBase::GetInstance()->updateComponentModelName(strOld,strNew); +} + +bool ProjectModelManager::ifProjectExsit(const QString& sPro) +{ + for(auto &meta:m_mapTotalData) + { + if(meta.contains(sPro)) + { + return true; + } + } + return false; +} + + +int ProjectModelManager::createPropertyTable(const QString& sMeta,const QString& sProject,const QString& sGroup,QList lstSelect,QList lstBase,int nLinkType,bool isPublic) +{ + if(!isPublic) + { + QString sName = sMeta + QString("_") + sProject + QString("_")+sGroup; + + QStringList fields; + fields.append("id SERIAL NOT NULL PRIMARY KEY"); + fields.append("global_uuid uuid NOT NULL DEFAULT gen_random_uuid()"); + fields.append("attribute_group VARCHAR(64) NOT NULL"); + + for(auto &item:lstSelect) + { + QPair pair = combinePropertySql(item); //拼接单句sql + fields.append(pair.first); + } + fields.append("FOREIGN KEY (global_uuid) REFERENCES PUBLIC.component (global_uuid)"); + + QJsonObject objState = getSelectedState(lstSelect,lstBase); + + if(!DataBase::GetInstance()->createDynamicTable(sName,fields)) + { + return int(AlertInfo::Fail); + } + else + { + DataBase::GetInstance()->insertProjectManager(sName,sProject,sMeta,sGroup,nLinkType,objState); + return int(AlertInfo::Success); + } + } + else + { + QString sName = sGroup; + + QStringList fields; + fields.append("id SERIAL NOT NULL PRIMARY KEY"); + fields.append("global_uuid uuid NOT NULL DEFAULT gen_random_uuid()"); + //fields.append("attribute_group VARCHAR(64) NOT NULL"); + + for(auto &item:lstSelect) + { + QString attribute = item->data(Attribute).toString(); + if(attribute.contains("global_uuid")) + continue; + QPair pair = combinePropertySql(item); //拼接单句sql + fields.append(pair.first); + } + + QJsonObject objState = getSelectedState(lstSelect,lstBase); + + if(!DataBase::GetInstance()->ifDynamicTableExist(sName)) + { + bool val = DataBase::GetInstance()->createDynamicTable(sName,fields); + DataBase::GetInstance()->insertProjectManager(sName,sName,"NULL",sGroup,0,objState,true); + return val; + } + else + { + return int(AlertInfo::Exist); + } + } +} + +QJsonObject ProjectModelManager::getSelectedState(QList select,QList base) +{ + QJsonObject objState; + QJsonArray arrState; + + for(auto &item:select) + { + QString dataType = item->data(DataType).toString(); + int lengthPrecision = item->data(LengthPrecision).toInt(); + int scale = item->data(Scale).toInt(); + QString defaultValue = item->data(DefaultValue).toString(); + int isVisible = item->data(IsVisible).toInt(); + + QString dataTypePart = dataType; //拼接数据类型 + if (lengthPrecision > 0) { + dataTypePart += QString("(%1").arg(lengthPrecision); + if (scale > 0) { + dataTypePart += QString(",%1").arg(scale); + } + dataTypePart += ")"; + } + + QJsonObject node; //保存已选择状态 + node["name"] = item->text(); + node["checked"] = 1; + node["type"] = dataTypePart; + node["defaultValue"] = defaultValue; + node["lengthPrecision"] = lengthPrecision; + node["isVisible"] = isVisible; + arrState.append(node); + } + + for(auto &item:base) + { + QString dataType = item->data(DataType).toString(); + int lengthPrecision = item->data(LengthPrecision).toInt(); + int scale = item->data(Scale).toInt(); + QString defaultValue = item->data(DefaultValue).toString(); + int isVisible = item->data(IsVisible).toInt(); + + QString dataTypePart = dataType; //拼接数据类型 + if (lengthPrecision > 0) { + dataTypePart += QString("(%1").arg(lengthPrecision); + if (scale > 0) { + dataTypePart += QString(",%1").arg(scale); + } + dataTypePart += ")"; + } + + QJsonObject node; //保存未选择状态 + node["name"] = item->text(); + node["checked"] = 0; + node["type"] = dataTypePart; + node["defaultValue"] = defaultValue; + node["lengthPrecision"] = lengthPrecision; + node["isVisible"] = isVisible; + arrState.append(node); + } + + objState["checkState"] = arrState; + return objState; +} + +QString ProjectModelManager::getItemDataType(const QStandardItem* pItem) +{ + QString dataType = pItem->data(DataType).toString(); + int lengthPrecision = pItem->data(LengthPrecision).toInt(); + int scale = pItem->data(Scale).toInt(); + + QString dataTypePart = dataType; + if (lengthPrecision > 0) { + dataTypePart += QString("(%1").arg(lengthPrecision); + if (scale > 0) { + dataTypePart += QString(",%1").arg(scale); + } + dataTypePart += ")"; + } + return dataTypePart; +} + +ProjectModelSettingStruct ProjectModelManager::getModelSetting(const QString& sMeta,const QString& sProject) +{ + ProjectModelSettingStruct setting; + setting.modelName = sProject; + QJsonObject obj = DataBase::GetInstance()->getProjectSetting(sMeta,sProject); + QJsonArray arr = obj["picture"].toArray(); + for (QJsonValueRef jsonObj : arr) + { + QJsonObject node = jsonObj.toObject(); + QString sName = node["name"].toString(); + QByteArray bData = safeFromHex(node["data"].toString().toUtf8()); + setting.mapSvg.insert(sName,bData); + } + + QJsonArray arrUsed = obj["usingPicture"].toArray(); + for (QJsonValueRef jsonObj : arrUsed) + { + QJsonObject node = jsonObj.toObject(); + QString sName = node["name"].toString(); + QByteArray bData = safeFromHex(node["data"].toString().toUtf8()); + setting.mapUsedSvg.insert(sName,bData); + } + return setting; +} + +QByteArray ProjectModelManager::cleanHexData(const QByteArray& hexData) { + QByteArray cleaned = hexData; + // 移除空格、换行等空白字符 + cleaned.replace(" ", ""); + cleaned.replace("\n", ""); + cleaned.replace("\t", ""); + cleaned.replace("\r", ""); + return cleaned; +} + +// 问题2: 数据长度不是偶数 +QByteArray ProjectModelManager::fixHexLength(const QByteArray& hexData) { + if (hexData.length() % 2 != 0) { + // 如果是奇数长度,在末尾补0 + return hexData + "0"; + } + return hexData; +} + +// 问题3: 包含非十六进制字符 +QByteArray ProjectModelManager::extractHexOnly(const QByteArray& data) { + QByteArray result; + for (char c : data) { + if ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) { + result.append(c); + } + } + return result; +} + +QByteArray ProjectModelManager::safeFromHex(const QByteArray& hexData) { + + // 步骤1: 清理数据 + QByteArray cleaned = cleanHexData(hexData); + + // 步骤2: 检查并修复长度 + cleaned = fixHexLength(cleaned); + + // 步骤3: 提取纯十六进制字符(如果需要) + if (cleaned != extractHexOnly(cleaned)) { + cleaned = extractHexOnly(cleaned); + cleaned = fixHexLength(cleaned); + } + + // 步骤4: 尝试转换 + QByteArray result = QByteArray::fromHex(cleaned); + return result; +} diff --git a/include/CommonInclude.h b/include/CommonInclude.h new file mode 100644 index 0000000..acb5d46 --- /dev/null +++ b/include/CommonInclude.h @@ -0,0 +1,18 @@ +#ifndef CommonInclude_h__ +#define CommonInclude_h__ + +#include +#include +#include +#include + +#define Q_PROPERTY_VAR(Type,Name)\ + Q_PROPERTY(Type Name READ get##Name WRITE set##Name) \ + Type get##Name(){ return Name; } \ + void set##Name(Type var){ \ + Name = var; \ + qDebug() << "Set" <<#Name <<": " < + +class BaseDockWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit BaseDockWidget(const QString &title, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~BaseDockWidget(); +}; +#endif // BASEDOCKWIDGET_H diff --git a/include/configToolBar.h b/include/configToolBar.h new file mode 100644 index 0000000..7441e89 --- /dev/null +++ b/include/configToolBar.h @@ -0,0 +1,24 @@ +#ifndef CONFIGTOOLBAR_H +#define CONFIGTOOLBAR_H + +#include "enhancedToolBar.h" +#include "toolBarConfig.h" + +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(); + + ToolBarConfig m_config; +}; + +#endif // CONFIGTOOLBAR_H diff --git a/include/diagramView.h b/include/diagramView.h new file mode 100644 index 0000000..175a2e7 --- /dev/null +++ b/include/diagramView.h @@ -0,0 +1,55 @@ +#ifndef DIAGRAMVIEW_H +#define DIAGRAMVIEW_H + +#include +#include +#include "common/core_model/diagram.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class diagramView; } +QT_END_NAMESPACE + +class QTreeWidgetItem; + +class DiagramView : public QDialog +{ + Q_OBJECT + +public: + DiagramView(QWidget *parent = nullptr); + ~DiagramView(); + + void initial(); +signals: + void diagramCreate(DiagramInfo); + void diagramChange(DiagramInfo); + void diagramDelete(DiagramInfo); + void diagramSelected(DiagramInfo); + + void prepareCreateHMI(); +public slots: + void onIndexRbtnClicked(const QPoint &pos); //索引列表右键菜单 + void onItemChanged(QStandardItem *item); + void onItemClicked(const QModelIndex &index); + + void onNewHMICreated(const QString&,QUuid); //图名,图id + void onHMIUpdated(const QString&,QUuid,int,QString); //*,*,状态(0未保存1已保存),保存时间 + void onHMISaved(const QString& strPro,const QString& autor,QUuid uid,QString sTime); + + void onNewHMIClicked(); + void onSaveHMIClicked(); +private: + void updateState(QStandardItem*,int); //更新状态item + void updateSaveTime(QStandardItem*,QString); //更新时间item +private: + Ui::diagramView *ui; + QStandardItemModel* _pModel; + int _count; + + QIcon m_iconUnsaved; + QIcon m_iconSaved; +private: + QString generateName(); +}; + +#endif diff --git a/include/electricElementsBox.h b/include/electricElementsBox.h new file mode 100644 index 0000000..94db2f7 --- /dev/null +++ b/include/electricElementsBox.h @@ -0,0 +1,38 @@ +#ifndef ELETRICELEMENTSPANELCONTAINER_H +#define ELETRICELEMENTSPANELCONTAINER_H + +#include +//#include +#include "common/core_model/types.h" +#include "common/backend/project_model.h" + +class ToolBox; +class ElectricElementsPanel; + +//电力图元面板 +class ElectricElementsBox : public QObject +{ + Q_OBJECT + +public: + ElectricElementsBox(QObject *parent = nullptr); + ~ElectricElementsBox(); + +public: + void initial(); + void addPanelItems(const QString& sPanel); //添加面板图元 + ToolBox* getToolBox() const; + void getModelInfo(); //获取模型信息 + void updateModelList(); //更新工程模对象列表 +signals: + void addEletricItem(ModelStateInfo&); +public slots: + void onSignal_addEletricItem(ModelStateInfo&); + void onSignal_modelChanged(); +private: + ToolBox* m_pToolBox; + QMap m_mapPanels; + QMap _modelInfo; //模型结构信息 +}; + +#endif diff --git a/include/electricElementsListwidget.h b/include/electricElementsListwidget.h new file mode 100644 index 0000000..6d13bcd --- /dev/null +++ b/include/electricElementsListwidget.h @@ -0,0 +1,18 @@ +#ifndef ELETRICELEMENTLISTWIDGET_H +#define ELETRICELEMENTLISTWIDGET_H + +#include +#include + +class ElectricElementsListwidget : public QListWidget +{ + Q_OBJECT + +public: + ElectricElementsListwidget(QListWidget *parent = nullptr); + ~ElectricElementsListwidget(); +protected: + void mousePressEvent(QMouseEvent *event); +}; + +#endif diff --git a/include/electricElementsPanel.h b/include/electricElementsPanel.h new file mode 100644 index 0000000..0f8ce5c --- /dev/null +++ b/include/electricElementsPanel.h @@ -0,0 +1,32 @@ +#ifndef ELETRICELEMENTSPANEL_H +#define ELETRICELEMENTSPANEL_H + +#include +//#include "global.h" +#include "common/backend/project_model.h" + +class ElectricElementsListwidget; +class QListWidgetItem; + +class ElectricElementsPanel : public QWidget +{ + Q_OBJECT + +public: + ElectricElementsPanel(QWidget *parent = nullptr); + ~ElectricElementsPanel(); + +signals: + void addGraphicsItem(ModelStateInfo&); +public: + void setData(QMap); +private: + void initial(); +public slots: + void onItemClicked(QListWidgetItem*); +private: + ElectricElementsListwidget* m_pListWidget; + QMap m_mapEleData; +}; + +#endif diff --git a/include/enhancedToolBar.h b/include/enhancedToolBar.h new file mode 100644 index 0000000..5f1bb2d --- /dev/null +++ b/include/enhancedToolBar.h @@ -0,0 +1,54 @@ +#ifndef ENHANCEDTOOLBAR_H +#define ENHANCEDTOOLBAR_H + +#include +#include +#include + +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 clearTools(); + void addTools(const QList> &tools); + +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); + +private slots: + void onActionTriggered(QAction *action); + +private: + void init(); + + QActionGroup *m_actionGroup; + QMap m_actions; + QMap m_actionToType; // 反向映射 + QString m_currentTool; +}; + +#endif diff --git a/include/graphicElementsPanel.h b/include/graphicElementsPanel.h new file mode 100644 index 0000000..f90bea4 --- /dev/null +++ b/include/graphicElementsPanel.h @@ -0,0 +1,31 @@ +#ifndef GRAPHICELEMENTSPANEL_H +#define GRAPHICELEMENTSPANEL_H + +#include +//#include "global.h" +#include "common/core_model/types.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class graphicElementsPanel; } +QT_END_NAMESPACE + +class GraphicElementsPanel : public QWidget +{ + Q_OBJECT + +public: + GraphicElementsPanel(QWidget *parent = nullptr); + ~GraphicElementsPanel(); + +signals: + void addGraphicsItem(GraphicsItemType&); + +public slots: + void onBtnClicked_GraphicsItem(); + +private: + Ui::graphicElementsPanel *ui; + +}; + +#endif diff --git a/include/mainwindow.h b/include/mainwindow.h new file mode 100644 index 0000000..1ddee6c --- /dev/null +++ b/include/mainwindow.h @@ -0,0 +1,78 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include + + +QT_BEGIN_NAMESPACE +namespace Ui { class CMainWindow; } +QT_END_NAMESPACE + +class QGraphicsItem; +class QUndoStack; +class DrawingPanel; +class GraphicElementsPanel; +class DesignerScene; +class DiagramCavas; +class ElectricElementsBox; +class TopologyView; +class DiagramView; +class MonitorItemsDlg; +class MonitorPagesDlg; +class QDetailsView; + +class CMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + CMainWindow(QWidget *parent = nullptr); + ~CMainWindow(); + +protected: + virtual void closeEvent(QCloseEvent* event) override; + virtual void changeEvent(QEvent* event) override; + virtual void moveEvent(QMoveEvent *event) override; +private: + void initializeDockUi(); + void initializeAction(); + +private slots: + void onAction_zoomIn(); + void onAction_zoomOut(); + void onAction_zoomFit(); + void onAction_createGroup(); + void onAction_destroyGroup(); + void onAction_editBay(); + void onAction_previewData(); + void onSignal_addItem(QGraphicsItem*); + void onSignal_deleteItem(); + + void onCavasItemSelected(QObject*); +public: + GraphicElementsPanel* graphicsElementsPanel() const; + +private: + QAction* SavePerspectiveAction = nullptr; + QWidgetAction* PerspectiveListAction = nullptr; + QComboBox* PerspectiveComboBox = nullptr; + + QUndoStack* m_pUndoStack; + Ui::CMainWindow *ui; + + DiagramCavas* m_pDiagramCavas; + DrawingPanel* m_pDrawingPanel; + ElectricElementsBox* m_pElectricElementsBox; + TopologyView* m_pTopologyView; + DiagramView* m_pDiagramView; + GraphicElementsPanel* m_pGraphicElementsPanel; + MonitorItemsDlg* m_pMonitorItemsDlg; + MonitorPagesDlg* m_pMonitorPagesDlg; + QDockWidget* m_pMonitorItemsDock; + QDockWidget* m_pMonitorPagesDock; + QDetailsView* m_pPropertiesEditorView; + QAction* _pActMonitor; +}; +#endif // MAINWINDOW_H diff --git a/include/monitorItemsDlg.h b/include/monitorItemsDlg.h new file mode 100644 index 0000000..ffee732 --- /dev/null +++ b/include/monitorItemsDlg.h @@ -0,0 +1,50 @@ +#ifndef MONITORITEMSDLG_H +#define MONITORITEMSDLG_H + +#include +#include +#include +//#include "global.h" +#include "common/core_model/topology.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class monitorItemsDlg; } +QT_END_NAMESPACE + +class MonitorItemsDlg : public QDialog +{ + Q_OBJECT + +public: + MonitorItemsDlg(QWidget *parent = nullptr); + ~MonitorItemsDlg(); + + void initial(); +signals: + void generateMonitor(QString,QList); //生成监控 +public slots: + void onUpdateItems(QList,bool refresh); //更新当前设备列表 + void onSelectItems(QList); //更新当前选中的设备 + void onGenerateClicked(); + void onMonitorCreated(QList); //创建后的设备列表 + void onItemChanged(QStandardItem *item); //item勾选事件 +private: + void resetSelect(); //重置选中 + void setChildrenCheckState(QStandardItem *parent, Qt::CheckState state); + void traverseSelectStandardItemModel(QStandardItemModel *model,Qt::CheckState); //遍历选中 + void traverseSelectStandardItem(QStandardItem *item, int depth,Qt::CheckState); //遍历选中 + + QList getCheckedItems(QStandardItem* parentItem); //返回checked对象 + QList getTreeViewCheckedItems(QTreeView* treeView); //返回checked对象 + + // 查找间隔节点 + QStandardItem* findBayItem(const QString& bayName); +private: + Ui::monitorItemsDlg *ui; + QStandardItemModel* _modelAll; //图中所有item + QStandardItemModel* _modelSelect; //生成的item + // 存储间隔名称到树节点的映射,提高查找效率 + QHash m_mapBayItems; +}; + +#endif diff --git a/include/monitorPagesDlg.h b/include/monitorPagesDlg.h new file mode 100644 index 0000000..64c90c6 --- /dev/null +++ b/include/monitorPagesDlg.h @@ -0,0 +1,37 @@ +#ifndef MONITORPAGESDLG_H +#define MONITORPAGESDLG_H + +#include +#include +//#include "global.h" +#include "common/core_model/diagram.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class monitorPagesDlg; } +QT_END_NAMESPACE + +class MonitorPagesDlg : public QDialog +{ + Q_OBJECT + +public: + MonitorPagesDlg(QWidget *parent = nullptr); + ~MonitorPagesDlg(); + + void initial(); +signals: + void monitorSelected(DiagramInfo); + void prepareSaveMonitor(QList>); +public slots: + void onMonitorCreated(QString,QPair,int); + void onItemClicked(const QModelIndex &index); + + void onIndexRbtnClicked(const QPoint &pos); //索引列表右键菜单 +private: + QStandardItem* findTopLevelItem(const QString& name); //查找顶层项 +private: + Ui::monitorPagesDlg *ui; + QStandardItemModel* _pModel; +}; + +#endif diff --git a/include/operationCommand.h b/include/operationCommand.h new file mode 100644 index 0000000..2178549 --- /dev/null +++ b/include/operationCommand.h @@ -0,0 +1,81 @@ +/** + *\file operationCommand.h + * + *\brief 用来实现“撤销/重做”的操作指令,继承自QUndoCommand + * + *\author dsc + */ + +#ifndef OPERATIONCOMMAND_H +#define OPERATIONCOMMAND_H + +#include +#include + +class GraphicsItemGroup; + + +class AddItemCommand : public QUndoCommand +{ +public: + explicit AddItemCommand(QGraphicsItem* item, QGraphicsScene* graphicsScene, QUndoCommand* parent = 0); + ~AddItemCommand(); + +public: + void undo() override; + void redo() override; + +private: + QGraphicsItem* m_pItem; + QPointF m_itemPos; + QGraphicsScene* m_pGraphicsScene; +}; + +class DeleteItemCommand : public QUndoCommand +{ +public: + explicit DeleteItemCommand(QGraphicsScene* graphicsScene, QUndoCommand* parent = 0); + ~DeleteItemCommand(); + +public: + void undo() override; + void redo() override; + +private: + QList m_listItem; + QGraphicsScene* m_pGraphicsScene; +}; + +class CreateItemGoupCommand : public QUndoCommand +{ +public: + explicit CreateItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* graphicsScene, QUndoCommand* parent = 0); + ~CreateItemGoupCommand(); + +public: + void undo() override; + void redo() override; + +private: + QGraphicsItemGroup* m_pGroup; + QGraphicsScene* m_pGraphicsScene; + QList m_listItem; +}; + +class DestroyItemGoupCommand : public QUndoCommand +{ +public: + explicit DestroyItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* graphicsScene, QUndoCommand* parent = 0); + ~DestroyItemGoupCommand(); + +public: + void undo() override; + void redo() override; + +private: + QGraphicsItemGroup* m_pGroup; + QGraphicsScene* m_pGraphicsScene; + QList m_listItem; +}; + +#endif diff --git a/include/projectTableDelegate.h b/include/projectTableDelegate.h new file mode 100644 index 0000000..3815c9a --- /dev/null +++ b/include/projectTableDelegate.h @@ -0,0 +1,28 @@ +#ifndef PROJECTMODELCOMBODELEGATE_H +#define PROJECTMODELCOMBODELEGATE_H + +#include + +class ProjectTableDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + ProjectTableDelegate(QObject *parent = nullptr); + ~ProjectTableDelegate(); + + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,const QModelIndex& index) const override; + + //bool eventFilter(QObject* obj, QEvent* event) override; + + //void setEditorData(QWidget* editor, const QModelIndex& index) const override; + + //void setModelData(QWidget* editor, QAbstractItemModel* model,const QModelIndex& index) const override; + + bool editorEvent(QEvent* event, QAbstractItemModel* model, + const QStyleOptionViewItem& option, const QModelIndex& index) override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; +signals: + void editingFinished(const QModelIndex &index, const QString &value) const; +}; + +#endif // PROJECTMODELCOMBODELEGATE_H diff --git a/include/renameModel.h b/include/renameModel.h new file mode 100644 index 0000000..accc79c --- /dev/null +++ b/include/renameModel.h @@ -0,0 +1,44 @@ +#ifndef RENAMEMODEL_H +#define RENAMEMODEL_H + +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class renameModel; } +QT_END_NAMESPACE + +/*enum projectState +{ + Err = -1, + NotExist = 0, + Exist, + Changed +};*/ + +class projectModelDlg; + +class RenameModel : public QDialog +{ + Q_OBJECT + +public: + RenameModel(QWidget *parent = nullptr); + ~RenameModel(); + + void initial(); + void showCenter(); +signals: + void selectedPage(const QString&); +public slots: + void onOkClicked(); + void onCancelClicked(); +private: + void setShowName(); //获取当前名称并显示 + //projectState couldSave(); //判断当前名称是否可用 +private: + Ui::renameModel *ui; + projectModelDlg* _pParent; +}; + +#endif diff --git a/include/selectorDialog.h b/include/selectorDialog.h new file mode 100644 index 0000000..007743b --- /dev/null +++ b/include/selectorDialog.h @@ -0,0 +1,38 @@ +#ifndef SELECTORDIALOG_H +#define SELECTORDIALOG_H + +#include +#include +#include +#include +#include "common/core_model/types.h" +#include "common/backend/meta_model.h" + +// 自定义元件选择对话框 +class SelectorDialog : public QDialog { +public: + SelectorDialog(QWidget* parent = nullptr); + + void initial(SelectorDialogType tpe); + QString selectedComponent() const { + return m_selectedComponent; + } + modelType selectedMeta() const { + return m_selectedMeta; + } + +private: + QListView* m_listView; + QString m_selectedComponent; + modelType m_selectedMeta; + QDialogButtonBox* m_buttonBox; + SelectorDialogType m_dlgType; + + void setupUI(); + void setupConnections(); + QStandardItemModel * initialModel(); +private: + QList getMetaList() const; +}; + +#endif //SELECTORDIALOG_H diff --git a/include/toolBarConfig.h b/include/toolBarConfig.h new file mode 100644 index 0000000..4c6ffac --- /dev/null +++ b/include/toolBarConfig.h @@ -0,0 +1,54 @@ +#ifndef TOOLBARCONFIG_H +#define TOOLBARCONFIG_H + +/********************toolbar配置类*******************/ +#include +#include +#include +#include +#include + +struct ToolInfo +{ + QString type; // 工具类型标识符 + QString name; // 显示名称 + QString iconPath; // 图标路径 + QVariantMap properties; // 自定义属性 + + ToolInfo() = default; + ToolInfo(const QString &type, const QString &name, + const QString &iconPath = QString()) + : type(type), name(name), iconPath(iconPath) {} + QIcon getIcon() const; +}; + +class ToolBarConfig : public QObject +{ + Q_OBJECT + +public: + explicit ToolBarConfig(QObject *parent = nullptr); + + // 加载配置 + bool loadFromFile(const QString &filePath); + bool loadFromJson(const QByteArray &jsonData); + + // 获取工具 + ToolInfo getTool(const QString &type) const; + QList getAllTools() const; + QStringList getToolTypes() const; + + // 工具数量 + int count() const { return m_tools.size(); } + + // 检查工具是否存在 + bool contains(const QString &type) const; + +private: + QMap m_tools; + + void addDefaultTools(); + ToolInfo parseTool(const QJsonObject &json); +}; + +#endif diff --git a/include/toolBox.h b/include/toolBox.h new file mode 100644 index 0000000..5645ec0 --- /dev/null +++ b/include/toolBox.h @@ -0,0 +1,20 @@ +#ifndef TOOLBOX_H +#define TOOLBOX_H + +#include + +class QVBoxLayout; +class ToolBox : public QWidget +{ + Q_OBJECT + +public: + explicit ToolBox(QWidget *parent = nullptr); + ~ToolBox(); + void addWidget(const QString &title, QWidget *widget); + void removeWidget(const QString &title); +private: + QVBoxLayout *m_pContentVBoxLayout; + QMap m_mapWidget; +}; +#endif // TOOLBOX_H diff --git a/include/toolPage.h b/include/toolPage.h new file mode 100644 index 0000000..dcd9be8 --- /dev/null +++ b/include/toolPage.h @@ -0,0 +1,30 @@ +#ifndef TOOLPAGE_H +#define TOOLPAGE_H + +#include + +class QFormLayout; +class QLabel; +class QPushButton; + + +class ToolPage : public QWidget +{ + Q_OBJECT +public: + explicit ToolPage(QWidget *parent = nullptr); + ~ToolPage(); +public slots: + void addWidget(const QString &title, QWidget *widget); + void expand(); + void collapse(); +private slots: + void onPushButtonFoldClicked(); +private: + + bool m_bIsExpanded; + QLabel *m_pLabel; + QPushButton *m_pPushButtonFold; + QWidget *m_pContent; +}; +#endif // TOOLPAGE_H diff --git a/include/topologyTree.h b/include/topologyTree.h new file mode 100644 index 0000000..b5c3149 --- /dev/null +++ b/include/topologyTree.h @@ -0,0 +1,17 @@ +#ifndef TOPOLOGYTREE_H +#define TOPOLOGYTREE_H + +#include + +/***************拖拽实现*************/ +class TopologyTree : public QTreeView +{ + Q_OBJECT +public: + TopologyTree(QWidget *parent = nullptr); + ~TopologyTree(); +protected: + void mouseMoveEvent(QMouseEvent *event) override; +}; + +#endif diff --git a/include/topologyView.h b/include/topologyView.h new file mode 100644 index 0000000..27e396d --- /dev/null +++ b/include/topologyView.h @@ -0,0 +1,72 @@ +#ifndef TOPOLOGYVIEW_H +#define TOPOLOGYVIEW_H + +#include +#include +//#include "global.h" +#include "common/core_model/topology.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class topologyView; } +QT_END_NAMESPACE + +class TopologyTree; +class ExtraPropertyManager; +class StructDataSource; + +class TopologyView : public QDialog +{ + Q_OBJECT + +public: + TopologyView(QWidget *parent = nullptr); + ~TopologyView(); + + void initial(); + void loadTopologyFromDB(); //加载拓扑关系 +signals: + void entityCreate(EntityInfo); + void entityChange(EntityInfo); + void entityDelete(EntityInfo); + void entitySelected(EntityInfo); +public slots: + void onItemChanged(QStandardItem *item); + void onItemClicked(const QModelIndex &index); + void onUpdateTopology(QList lst,bool refresh,bool bFull); + void onMonitorUpdate(QList); //更新运行时 +private: + void clearItems(); + QString getLevelType(int index); + void buildTreeStructure(QStandardItemModel* model, const QVector& properties); + QVector getPropertiesForNode(QStandardItem* node); + QString getNodeInfo(QStandardItem* node); + QStandardItem* findBayItem(const QString& voltageLevel, const QString& bayName); + QStandardItem* findOrCreateVoltageLevel(const QString& voltageLevel); // 查找或创建电压层级节点 + QStandardItem* createBayItem(const QString& cacheKey, const HierarchyStructItem& bayInfo); + void createDeviceItem(QStandardItem* pParent, const HierarchyStructItem& deviceInfo); // 创建设备节点 + + void onUpdateTopologyFull(QList lst, bool refresh); + void onUpdateTopologySimple(QList lst, bool refresh); + + QStandardItem* findOrCreateTopLevelItem(const QString& name, int level, QHash& cache); //// 查找或创建顶级节点(电网) + QStandardItem* findOrCreateChildItem(QStandardItem* pParent, const QString& name, const QString& cacheKey, int level,QHash& cache);// 查找或创建子节点 + QStandardItem* findBayItemInFullTree(const QString& grid, const QString& zone, const QString& station, const QString& voltageLevel, const QString& bayName);// 在完整树中查找间隔节点 +private: + Ui::topologyView *ui; + QStandardItemModel* _treeModel; + ExtraPropertyManager* _pExtraProManager; + StructDataSource* m_dataSource; + TopologyTree* _treeView; + // 完整层级结构缓存 + QHash m_mapGrids; // 电网节点 + QHash m_mapZones; // 区域节点 + QHash m_mapStations; // 变电站节点 + QHash m_mapVoltageLevels; // 电压层级节点 + QHash m_mapBayItems; // 间隔节点 + + // 原简化层级缓存 + QHash m_simpleVoltageLevels; // 只用于简化模式 + QHash m_simpleBayItems; // 只用于简化模式 +}; + +#endif diff --git a/resource/DiagramDesigner.qrc b/resource/DiagramDesigner.qrc new file mode 100644 index 0000000..1c4ae84 --- /dev/null +++ b/resource/DiagramDesigner.qrc @@ -0,0 +1,19 @@ + + + images/checkerboard.png + images/icon_rotate_lb.png + images/icon_rotate_lt.png + images/icon_rotate_rb.png + images/icon_rotate_rt.png + images/icon_rotate.png + images/icon_up_arrow.png + images/icon_down_arrow.png + images/icon_left_arrow.png + images/element/icons_triangle.png + images/element/svg_bus.svg + images/element/svg_rect.svg + images/element/svg_triangle.svg + images/element/icon_image.png + images/element/icon_text.png + + diff --git a/resource/images/checkerboard.png b/resource/images/checkerboard.png new file mode 100644 index 0000000000000000000000000000000000000000..466985575f65ed0cc2e746841b94e8d9771ef920 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|oCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#KDV5x7<<9)5DuV_hNp{Th{pNkKmY&RGn*BBe;2#vkq?hbQy}jV2C;^iil|r0*cKQAyFGU6M|Zp z#zaVAc{Yj`HYu!Ck5`gst6zq6ntrCDA z+Kq3CPw%ukR85fpvc#vX1t1*2?Nk@QSD>8~0Sx zwH6|L6HmyfSDSbvZJW3r%31&{z#TOKGr%Wc4(KFB0DZubr*mBbuhIbU6!;TzaG9+q zfa>+HfzM)vkuu-+0{Cn2%)d|%fHvT}&*B^6Z?9egMZ-hOIWVf-+Y9^#uB!#`Se`zE z3fS`uvuWVQpzs|zGByU(^AH%jFTh}khxIrGo=Ar&ms^xmm;U(CDw}-(cvMNzVF1bo zSB5rl3p2?X($?$;^1we&-Z78^nxg`2)#%lVvKR8}FBzQhVS2CSeL&}M0 zp;Z#1qlf_GBt*Fg00jfP)lJPZVf|ju=$_Z(4GB|^Ojwf!paxtb)e$n(T-!{M0%c@K kZOXt~m3E#O&s*WqEq=6_bE-%nR{#J207*qoM6N<$f=wtDtpET3 literal 0 HcmV?d00001 diff --git a/resource/images/element/icon_text.png b/resource/images/element/icon_text.png new file mode 100644 index 0000000000000000000000000000000000000000..43fec1f4c50140dcaed54c382a7b1412c7ae2af0 GIT binary patch literal 532 zcmeAS@N?(olHy`uVBq!ia0vp^DIm;^>#QX*I%m1geGmu=6Af5p^3GzW|0h1pZOgL+Jj$l3 zcuoSM5});o1;;%Vr5&t5Kvp?Op z|9r`D!orJHW~Msx*UbM{QW$CHnXNv*?fzTcYnS(a`M2rrY>5_Cw!}`yh$Mk&^kd%m zJ|X7yzkB`n-Yf3B_IIzm?R{sXwKg7-mG9Zl-ubYi@X20zlbb)5zVZI=XUO5rb4aLJ z=6lh`mp8lL9=u_|@o7)&tcUZTKV2vL;&WMMoBg@dvDP;E;h>06@m%$FA487e2hZix Sotl8r%;4$j=d#Wzp$Pzkvg|zo literal 0 HcmV?d00001 diff --git a/resource/images/element/icons_triangle.png b/resource/images/element/icons_triangle.png new file mode 100644 index 0000000000000000000000000000000000000000..d46ac16ae92bb5be2c2490bdc2a31b118a92f53a GIT binary patch literal 657 zcmV;C0&e|@P)RXal7t9@0$Ui=q97NR?y_A` zt87uE?K0dIuC!K0t<+jnL@HEZ5JHB6K|$c6NCPP%qQWfA7#Qhtc(u9SGc)JHdEkS~ z%=f+LJ3q|4cfjNEcs&281MY?q{LV|x1G7e)BhM%UCdEXifO7JPCt?zF@f27>9&mht zNz6q(j=2Gn{a^JMq__~{R`w* zg*llu-K5MRaa1}Y)ZGwu)uhBS;EmDDXJAc)+G60NQ9BAOCj~BxRXveK{Yg>Zj02|d zS(Y9u$n7^YekN@y9~%w)1}YYzvjg~*mZ_{4N8(BjdRK+sF=A#F@WB}LJ+Lwd%@x4g zq)lbJSiK__bdL(%HezD8FonGq^&bdRSxF40@VPLBTP=>U8Tf9DF=X3RHi)fn!af(p zIA`KCg|Fu^g}G7)ycDK#qYd#cVG0irANz!6swrGc{FDHn zY?;btu_~uXj?-fNOWBHS6Q*!5d<-W=0Wcs;<@RhudW0$5Me^(vrt(qL;##q#t)xe* z=qKp$zx0X6ZAGpN9Y>-&>?!EY + + Layer 1 + + + + \ No newline at end of file diff --git a/resource/images/element/svg_rect.svg b/resource/images/element/svg_rect.svg new file mode 100644 index 0000000..2adf44f --- /dev/null +++ b/resource/images/element/svg_rect.svg @@ -0,0 +1,9 @@ + + + Layer 1 + + + + + + \ No newline at end of file diff --git a/resource/images/element/svg_triangle.svg b/resource/images/element/svg_triangle.svg new file mode 100644 index 0000000..9e43441 --- /dev/null +++ b/resource/images/element/svg_triangle.svg @@ -0,0 +1,9 @@ + + + Layer 1 + + + + + + \ No newline at end of file diff --git a/resource/images/icon_down_arrow.png b/resource/images/icon_down_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..20a2bd1b56fccaaf50946713da0ef6befa164410 GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wkQ1Fna zi(^Q|oVPa|`I-zwTrchxooSi)=v>LxS4=)Ch1?R8CrPk1ap~s1_q6&~+ds*{agvHB z7@gh1yzYQxM(1F&CiuWtGMhl4!e52IV}Qlgy*CcSA^SJnonGP=5GM>4TGnv KpUXO@geCyBVQAL? literal 0 HcmV?d00001 diff --git a/resource/images/icon_left_arrow.png b/resource/images/icon_left_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..391592bd69f6469687eb28b89aea7b6a6cdc8c2a GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wkNY2y6 zF{EP7+gr9=Ook$^4>wOem^JJA4x_tk>bV7YcC>ml9bK?h;m+r&FW;R#-Q39NIZ4HH z(iiu#1A-A8Ar8z|1!@*G{@M%d4sHJ)newCkq4N~;{^a|$pLe`fp2E#99#{Tw;_eSo zK|dZ&lBwIVR6O9Ne?mm#?`19O8`RHaaYh_^ppw8jd2)lQj~~-C152R5lkLp+Rbx0q pW()bXL^U|S;uiwhsPfTTfAj9KP)RCt`VltE6zFc3u_Ra(x|Lm*f}5eO~;rCG4ek`1Ej9YBH` zQIHDU0=fvY^dk+C-Ppl-Qda)+XA);Vq;bc<)VZ5XfJ@*7cvl1WR4q*}PJu6DS_c|@ zNE^)vSQ)cdU=B<@<`~^lwVgEPKfo1Gn6tLIqm94 literal 0 HcmV?d00001 diff --git a/resource/images/icon_rotate_lb.png b/resource/images/icon_rotate_lb.png new file mode 100644 index 0000000000000000000000000000000000000000..4438b6c95b9d382e5553bfc071f4f577edc4f31f GIT binary patch literal 865 zcmV-n1D^beP)1^@s6$I7^K00009a7bBm000XU z000XU0RWnu7ytkR6G=otRCt{2oB2x>Q545NE{WM*-{NJ&Xa zNr@F3fTeT&5OH7~@EhpHI4gl~Kn*a0VXA@ez%tom7-bRg8Cc<6djzb;7z=@~z?$HC z8_?~kxGjNRrGYsr0 z(s79-1Dq)Go;+DGwi(5lizC3nV6`#JsX%j>cE$y%%m~xY zI0v+NwrOW{>4V zFrSp+LT|~GN{}Ym0o?a&mlc18aS_(w**(B{j8X}F351tR8nSl^I)gFPvEq18c|&`d=ei!H0A|8)o-gbIcqi~u&bbZL5feIqk2$LL>y-l; zQbTRA90Lv%Fs?x<%5iC*_PI_`Rs$1xx`*rJ5Z5PmMNTRN7d#&p`5gT-xaYrdMswO_ z*K@H=JKwBwtQiH}Ud4ePIm}IwR}y-KG8jh@SfO8NA}B=VW3BqgcI#-Az$=teInJ;1 z#EvR8!A(k+xc>9mrcx<}STDP|f?U)lm61ki)smSDw?nR19(H4%G0W8-GOt@O%hc@B zr@)%gsuLZfs^AT%>CQFEgw%Wgaxg_Uu$#4%yR4fz#ys87sid^olEqD6iyCO$9>p}R rz%S_!^ORdJUZtd@q@<*z5L^BLeSl*Z$)eJ^00000NkvXXu0mjf?74#e literal 0 HcmV?d00001 diff --git a/resource/images/icon_rotate_lt.png b/resource/images/icon_rotate_lt.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c15c118373f1ea080a7a20e64a57927ea0a0af GIT binary patch literal 864 zcmV-m1E2hfP)1^@s6$I7^K00009a7bBm000XU z000XU0RWnu7ytkR5=lfsRCt{2nrll`K^%u4Ej3d!%}BB$DeY=hcE3YvM%J5d1f?a> z4T++%Br-E|Ev2?HE2Zxc^uEs)1HS{qVRze=v)7pi4m;=UFlWxp{I4_M@pwEQkH_Qj zcs!A@B)X=GpK0J=zB^F}R09*hS70rUGYISf>VO5Hk|Jdta1Iy&-dWd_ekXN}0at*n z3G_qUDsKnIDH1AyUVW^Q^C94vW;GSdfW5#?AO_3;*MU>Otx(;Iz%>e(4Vp1C%cp^* z5wvLr=QU81;J;B|7X`)^t$=61$`tj?;B1yfFe~RWa0WOGYy&DXe6JCuA3y^IKo#&@ zgJ86Mm>PZXl$QOtMBoB&9Owj20Ox@L;H~PK>k?3#Mjx7hIiQ?8azveIPw-#2`9%JieOVP~!Aq8Z4HJLU$fEOXr_zpDU7}dbfkW2m1)$sXA$f4?&>+Bp&3atZLfj2nH z1hB@s|CA(ZEO{D2@xVUdHjXl+FgqX~QC^}biHCHBaeSO)x6*6@CWKyvqdW~MPUGi0 z%Ll96?y};nkVy@=FP-5eZQ7^_Fj1njoK|eB4_f8MSgq1s}(guyQ#H?~{ zD39SBQ&xF}HiqonhRI{>1Xfc*lq-qbtAwh_C%y#!hE~QeYj=E9I^_?rkrHD;+jkv0 zE|5-mURA0dX}wOsWWu0!xJr_4Z3m{iU0_GECCPLsFiS?C3;EA_U^b0vZGk1 qnGYRKahl3}cfjNEcsw2th0H(n!DAAJL11^@s6$I7^K00009a7bBm000XU z000XU0RWnu7ytkR8c9S!RCt{2n(1p3K^TXhG}hLj)@rTyK|S$kp{Q6D!J?vg)eG@x z(UyveqAgWh6-67F^rOscPL zB$or~^crvwgDeH!d*@EAe!?J0;0w^?U2}n)H5g=6r3YC$tj0U<2MnrJz~^eH$EYpi zSPA7qoHv@qr5C?@W_ajczEjKiPYH?P*{D-G(RayfBrw+zBAS%Sn^bVcWykA*=LHH$ zN@2Gu`C0 zxa}Py(n=ZKcZKl#t36EDB4a4Hl=~(0vNWg$%N;AdTxcX?3Nrzdc%mAdrnw@e71CBF y%d_f9dVYyBjAHng>-1xi=BY7112Hi%Sl}-j8)AqG*9dF?0000FSZ?E`!A955p_VK*qL6+BIt&&QCYN`J`dAvVd(6##Y@4lB#j{;lU59t>V#M#oy z+beH8z4`D+{ZpOIKbiM!p2c6#Y^Np|++*POdB(HzJMKlrKjL)0vsmuX`V9G7e^|Tc z?)|=Yk7UwiftM{89Jf4@H2b)9`MQqygqIxW{#*R-n6=Yo+72gk?*)QtGdxqo)@hl0 zE|i_7cg!PMq)_O`QLA~+_~w87sg=5EbKJu4r3VDIp6H$|7wV(CtUyvS)2y~zbM~>f b`!36G-^w$Ot$cw$&{qteu6{1-oD!Ma#`5y&?tuq7sPrVmb5dY}E(EWo? z%?rUlX*DMV|0LF|5d4!`(;@gLx#psB#ljy-?jH_)bnSgG^-;IPLG9!JzjFNUtv~I* z*W2cu;_Toy@tGev+*7wqzLNGM`0&eFvAft|E?5v*o_MtRCD*OqPaq*rS3j3^P6 + + + 192.168.46.33 + 5432 + postgres + 123456 + + + datart/getPointData + + diff --git a/source/baseDockWidget.cpp b/source/baseDockWidget.cpp new file mode 100644 index 0000000..650918e --- /dev/null +++ b/source/baseDockWidget.cpp @@ -0,0 +1,72 @@ +#include "baseDockWidget.h" + +BaseDockWidget::BaseDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags) + : QDockWidget(title,parent,flags) +{ + setStyleSheet(R"( + QDockWidget { + border: 2px solid #e2e8f0; + border-radius: 6px; + background-color: white; + } + + QDockWidget::title { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #f8fafc, + stop:1 #f1f5f9); + color: #334155; + padding: 10px 15px; + font-size: 13px; + font-weight: 600; + border-bottom: 2px solid #e2e8f0; + border-radius: 4px 4px 0 0; + } + + /* 悬停时的标题栏 */ + QDockWidget:hover::title { + background: #f8fafc; + color: #1e40af; + } + + /* 按钮样式 */ + QDockWidget::close-button, + QDockWidget::float-button { + border: 1px solid #cbd5e1; + background: white; + border-radius: 4px; + width: 20px; + height: 20px; + } + + QDockWidget::close-button:hover, + QDockWidget::float-button:hover { + background: #f1f5f9; + border-color: #94a3b8; + } + + QDockWidget::close-button:pressed, + QDockWidget::float-button:pressed { + background: #e2e8f0; + } + + /* 按钮位置 */ + QDockWidget::close-button { + subcontrol-position: top right; + subcontrol-origin: margin; + right: 10px; + top: 10px; + } + + QDockWidget::float-button { + subcontrol-position: top right; + subcontrol-origin: margin; + right: 35px; + top: 10px; + } +)"); +} + +BaseDockWidget::~BaseDockWidget() +{ + +} diff --git a/source/configToolBar.cpp b/source/configToolBar.cpp new file mode 100644 index 0000000..bc1977d --- /dev/null +++ b/source/configToolBar.cpp @@ -0,0 +1,44 @@ +// ConfigToolBar.cpp +#include "configToolBar.h" +#include + +ConfigToolBar::ConfigToolBar(const QString &title, QWidget *parent) + : EnhancedToolBar(title, parent) +{ + loadDefaultTools(); +} + +ConfigToolBar::ConfigToolBar(QWidget *parent) + : EnhancedToolBar(parent) +{ + loadDefaultTools(); +} + +bool ConfigToolBar::loadToolsFromConfig(const QString &configFile) +{ + // 从配置文件加载 + if (!m_config.loadFromFile(configFile)) { + qWarning() << "加载配置文件失败,使用默认工具"; + return false; + } + + // 清空现有工具 + clearTools(); + + // 从配置添加工具 + QList tools = m_config.getAllTools(); + for (const ToolInfo &info : tools) { + addTool(info.type, info.name, info.getIcon()); + } + + return true; +} + +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")); +} diff --git a/source/diagramView.cpp b/source/diagramView.cpp new file mode 100644 index 0000000..5afd7c2 --- /dev/null +++ b/source/diagramView.cpp @@ -0,0 +1,246 @@ +#include +#include +#include +#include "diagramView.h" +#include "ui_diagramView.h" +#include "tools.h" +#include "projectManager.h" +#include "dataBase.h" +#include +#include + +DiagramView::DiagramView(QWidget *parent) + : QDialog(parent) + , ui(new Ui::diagramView) + ,_pModel(nullptr) +{ + ui->setupUi(this); + _pModel = new QStandardItemModel(this); + ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); + _count = 0; +} + +DiagramView::~DiagramView() +{ + delete ui; +} + +void DiagramView::initial() +{ + QStyle *style = QApplication::style(); + m_iconUnsaved = style->standardIcon(QStyle::SP_DialogNoButton); + m_iconSaved = style->standardIcon(QStyle::SP_DialogYesButton); + + connect(ui->treeView, &QTreeView::customContextMenuRequested, this, &DiagramView::onIndexRbtnClicked); + connect(ui->treeView, &QTreeView::clicked, this, &DiagramView::onItemClicked); + connect(_pModel, &QStandardItemModel::itemChanged, this, &DiagramView::onItemChanged); + connect(ui->btn_new, &QPushButton::clicked, this, &DiagramView::onNewHMIClicked); + connect(ui->btn_save, &QPushButton::clicked, this, &DiagramView::onSaveHMIClicked); + ui->treeView->setHeaderHidden(true); + // 设置模型的列数 + _pModel->setColumnCount(3); + _pModel->setHorizontalHeaderLabels({"名称", "状态", "修改时间"}); + + QList lst = DataBase::GetInstance()->getAllHMI(); + for(auto& info:lst) + { + QString sLastSave = info.context["lastSave"].toString(); + + QList rowItems; + + QStandardItem* pItem = new QStandardItem(info.name); //名称 + pItem->setData(info.uid,Qt::UserRole); + rowItems.append(pItem); + + QStandardItem *stateItem = new QStandardItem(); //状态 + updateState(stateItem,1); + rowItems.append(stateItem); + stateItem->setEditable(false); + + QStandardItem *timeItem = new QStandardItem(sLastSave); //修改时间 + rowItems.append(timeItem); + timeItem->setEditable(false); + + _pModel->appendRow(rowItems); + } + + // 创建树视图 + ui->treeView->setModel(_pModel); + + // 展开所有节点 + ui->treeView->expandAll(); + // 显示树视图 + ui->treeView->setWindowTitle(QObject::tr("组态列表")); + ui->treeView->header()->setStretchLastSection(false); + ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->treeView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->treeView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + +} + +void DiagramView::onIndexRbtnClicked(const QPoint &pos) +{ + // 获取当前点击的位置对应的索引 + QMenu menu; + QModelIndex index = ui->treeView->indexAt(pos); + if (index.isValid()) { + QStandardItem* item = _pModel->itemFromIndex(index); + QVariant parentId = item->data(Qt::UserRole+1); + + if(item) + { + QAction *saveAction = new QAction("保存", this); + QAction *delAction = new QAction("删除", this); + connect(delAction,&QAction::triggered,this,[&](){ + QModelIndex currentIndex = ui->treeView->currentIndex(); + if(currentIndex.isValid()) + { + QStandardItem* pItem = _pModel->itemFromIndex(currentIndex); + QVariant id = pItem->data(Qt::UserRole+1); + QString sName = pItem->text(); + if(pItem) + { + QString str = item->text(); + DiagramInfo info; + //todo:具体信息 + info.id = id; //新建的随机赋予临时id + info.sName = sName; + info.sTag = sName; + info.parentId = parentId; + emit diagramDelete(info); + + QStandardItem* pParent = item->parent(); + if(pParent) + { + pParent->removeRow(currentIndex.row()); + } + } + } + }); + menu.addAction(saveAction); + menu.addAction(delAction); + } + } + + // 在点击位置显示菜单 + menu.exec(ui->treeView->mapToGlobal(pos)); +} + +void DiagramView::onItemChanged(QStandardItem *item) +{ + int nLevel = getLevel(item); + QString str = item->text(); //名称可能修改 + DiagramInfo info; + info.id = item->data(Qt::UserRole).toUuid(); + info.sName = str; + emit diagramChange(info); +} + +void DiagramView::onItemClicked(const QModelIndex &index) +{ + QModelIndex firstIndex = index.sibling(0,index.column()); //取行首item + if(firstIndex.isValid()){ + QStandardItem* item = _pModel->itemFromIndex(firstIndex); + QStandardItem* parent = item->parent(); + if(item) + { + DiagramInfo info; + info.id = item->data(Qt::UserRole).toUuid(); + info.sName = item->text(); + if(parent) + info.parentId = parent->data(Qt::UserRole).toInt(); + emit diagramSelected(info); + } + } +} + +void DiagramView::onNewHMICreated(const QString& str,QUuid id) +{ + QList rowItems; + + QStandardItem* pItem = new QStandardItem(str); //名称 + pItem->setData(id,Qt::UserRole); + rowItems.append(pItem); + + QStandardItem *stateItem = new QStandardItem(); //状态 + updateState(stateItem,0); + rowItems.append(stateItem); + stateItem->setEditable(false); + + QStandardItem *timeItem = new QStandardItem(); //修改时间 + rowItems.append(timeItem); + timeItem->setEditable(false); + + _pModel->appendRow(rowItems); +} + +void DiagramView::onHMIUpdated(const QString& str,QUuid id,int nState,QString sTime) +{ + int rowCount = _pModel->rowCount(); + + for (int row = 0; row < rowCount; ++row) { + // 获取第一列(名称列)的item + QStandardItem *nameItem = _pModel->item(row, 0); + if (nameItem) { + QString name = nameItem->text(); + if(name == str){ + QStandardItem *stateItem = _pModel->item(row, 1); + stateItem->setText(nState?"已保存":"未保存"); + + QStandardItem *timeItem = _pModel->item(row, 2); + timeItem->setText(sTime); + break; + } + } + } +} + +void DiagramView::onHMISaved(const QString& strPro,const QString& autor,QUuid uid,QString sTime) +{ + +} + +void DiagramView::onNewHMIClicked() +{ + emit prepareCreateHMI(); +} + +void DiagramView::onSaveHMIClicked() +{ + +} + +void DiagramView::updateState(QStandardItem* p,int n) +{ + if(p){ + if(n == 0){ //未保存 + p->setText("未保存"); + p->setIcon(m_iconUnsaved); + } + else if(n == 1){ //已保存 + p->setText("已保存"); + p->setIcon(m_iconSaved); + } + } +} + +void DiagramView::updateSaveTime(QStandardItem* p,QString s) +{ + if(p){ + p->setText(s); + } +} + +QString DiagramView::generateName() +{ + QString sName = QString::fromWCharArray(L"组态图_")+QString::number(_count); + QModelIndex Idx = findIndex(_pModel,sName); + if(Idx.isValid()) //已存在 + { + _count += 1; + return generateName(); + } + else { + return sName; + } +} diff --git a/source/electricElementsBox.cpp b/source/electricElementsBox.cpp new file mode 100644 index 0000000..18f664a --- /dev/null +++ b/source/electricElementsBox.cpp @@ -0,0 +1,84 @@ +#include +#include +#include "electricElementsPanel.h" +#include "electricElementsBox.h" +#include "toolBox.h" +#include "util/baseSelector.h" +#include "dataBase.h" +#include "dataManager.h" + +ElectricElementsBox::ElectricElementsBox(QObject *parent) + : QObject(parent), + m_pToolBox(nullptr) +{ + m_pToolBox = new ToolBox(); +} + +ElectricElementsBox::~ElectricElementsBox() +{ + if(m_pToolBox) + delete m_pToolBox; +} + +void ElectricElementsBox::initial() +{ + getModelInfo(); + addPanelItems("Items"); +} + +void ElectricElementsBox::addPanelItems(const QString& sPanel) +{ + ElectricElementsPanel* pPanel = new ElectricElementsPanel(); + + for(auto &model:_modelInfo) + { + GraphicsItemType localType = linkType[(AbstractItemType)model.modelType]; + model.modelType = localType; //转换图元类别,从字典表转到内部类别 + } + + pPanel->setData(_modelInfo); + m_mapPanels.insert(sPanel,pPanel); + m_pToolBox->addWidget(sPanel,pPanel); + connect(pPanel,&ElectricElementsPanel::addGraphicsItem,this,&ElectricElementsBox::onSignal_addEletricItem); +} + +ToolBox* ElectricElementsBox::getToolBox() const +{ + return m_pToolBox; +} + +void ElectricElementsBox::getModelInfo() +{ + DataManager::instance().initialModelState(); + DataManager::instance().initialModelData(); + _modelInfo = DataManager::instance().modelState(); +} + +void ElectricElementsBox::updateModelList() +{ + for(auto &model:_modelInfo) + { + GraphicsItemType localType = linkType[(AbstractItemType)model.modelType]; + model.modelType = localType; + } + + if(!m_mapPanels.isEmpty()) //暂时1个类别 + { + QMap::iterator it = m_mapPanels.begin(); + if(it != m_mapPanels.end()) + { + it.value()->setData(_modelInfo); + } + } +} + +void ElectricElementsBox::onSignal_addEletricItem(ModelStateInfo& info) +{ + emit addEletricItem(info); +} + +void ElectricElementsBox::onSignal_modelChanged() +{ + getModelInfo(); + updateModelList(); +} diff --git a/source/electricElementsListwidget.cpp b/source/electricElementsListwidget.cpp new file mode 100644 index 0000000..bf7eb42 --- /dev/null +++ b/source/electricElementsListwidget.cpp @@ -0,0 +1,23 @@ +#include "electricElementsListwidget.h" + +ElectricElementsListwidget::ElectricElementsListwidget(QListWidget *parent) + : QListWidget(parent) +{ + +} + +ElectricElementsListwidget::~ElectricElementsListwidget() +{ + +} + +void ElectricElementsListwidget::mousePressEvent(QMouseEvent *event) +{ + int n = count(); //手动取消着色,处理qt6listwidget选中颜色 + for(int i = 0;i < n;++i) + { + QListWidgetItem* p = item(i); + p->setBackground(QBrush(QColor(0,0,0,0))); + } + QListWidget::mousePressEvent(event); +} diff --git a/source/electricElementsPanel.cpp b/source/electricElementsPanel.cpp new file mode 100644 index 0000000..96391c5 --- /dev/null +++ b/source/electricElementsPanel.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "electricElementsPanel.h" +#include "electricElementsListwidget.h" + +ElectricElementsPanel::ElectricElementsPanel(QWidget *parent) + : QWidget(parent) +{ + m_pListWidget = new ElectricElementsListwidget(); + QVBoxLayout *vBoxLayout = new QVBoxLayout(this); + vBoxLayout->setContentsMargins(0, 0, 0, 0); + vBoxLayout->addWidget(m_pListWidget); + m_pListWidget->setViewMode(QListView::IconMode); + m_pListWidget->setResizeMode(QListView::Adjust); + m_pListWidget->setMovement(QListView::Static); + + connect(m_pListWidget,&QListWidget::itemClicked,this,&ElectricElementsPanel::onItemClicked); +} + +ElectricElementsPanel::~ElectricElementsPanel() +{ + if(m_pListWidget) + delete m_pListWidget; +} + +void ElectricElementsPanel::initial() +{ + for(auto iter = m_mapEleData.begin();iter != m_mapEleData.end();++iter) + { + QIcon icon(":/images/element/icons_triangle.png"); + QListWidgetItem* pItem = new QListWidgetItem(icon,iter.key()); + pItem->setSizeHint(QSize(50,50)); + m_pListWidget->addItem(pItem); + } +} + +void ElectricElementsPanel::setData(QMap map) +{ + if(m_pListWidget->count() != 0) + m_pListWidget->clear(); + m_mapEleData = map; + initial(); +} + +void ElectricElementsPanel::onItemClicked(QListWidgetItem* item) +{ + item->setBackground(QBrush(QColor(135,206,235,220))); + emit addGraphicsItem(m_mapEleData[item->text()]); +} diff --git a/source/enhancedToolBar.cpp b/source/enhancedToolBar.cpp new file mode 100644 index 0000000..d7f69d3 --- /dev/null +++ b/source/enhancedToolBar.cpp @@ -0,0 +1,161 @@ +// EnhancedToolBar.cpp +#include "enhancedToolBar.h" +#include +#include + +EnhancedToolBar::EnhancedToolBar(const QString &title, QWidget *parent) + : QToolBar(title, parent) + , m_currentTool("") +{ + init(); +} + +EnhancedToolBar::EnhancedToolBar(QWidget *parent) + : QToolBar(parent) + , m_currentTool("") +{ + 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; // 已存在 + } + + QAction *action = createAction(toolType, toolName, icon); + + m_actionGroup->addAction(action); + addAction(action); + m_actions[toolType] = action; + m_actionToType[action] = toolType; + + // 如果没有当前工具,自动选择第一个工具 + if (m_currentTool.isEmpty()) { + setCurrentTool(toolType); + } + + emit toolAdded(toolType); +} + +void EnhancedToolBar::removeTool(const QString &toolType) +{ + 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); + } +} + +void EnhancedToolBar::setCurrentTool(const QString &toolType) +{ + if (toolType.isEmpty()) { + // 清除当前选择 + if (m_actionGroup->checkedAction()) { + m_actionGroup->checkedAction()->setChecked(false); + } + m_currentTool = ""; + emit toolSelected(""); + } + else if (m_actions.contains(toolType)) { + m_actions[toolType]->setChecked(true); + m_currentTool = toolType; + emit toolSelected(toolType); + } +} + +QString EnhancedToolBar::currentTool() const +{ + return m_currentTool; +} + +QString EnhancedToolBar::toolName(const QString &toolType) const +{ + 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(); + } + + m_actions.clear(); + m_actionToType.clear(); + m_currentTool = ""; +} + +void EnhancedToolBar::addTools(const QList> &tools) +{ + for (const auto &tool : tools) { + addTool(tool.first, tool.second); + } +} + +void EnhancedToolBar::onActionTriggered(QAction *action) +{ + if (m_actionToType.contains(action)) { + QString toolType = m_actionToType[action]; + m_currentTool = toolType; + emit toolSelected(toolType); + } +} diff --git a/source/graphicElementsPanel.cpp b/source/graphicElementsPanel.cpp new file mode 100644 index 0000000..2213ca5 --- /dev/null +++ b/source/graphicElementsPanel.cpp @@ -0,0 +1,28 @@ +#include "graphicElementsPanel.h" +#include "ui_graphicElementsPanel.h" + +GraphicElementsPanel::GraphicElementsPanel(QWidget *parent) + : QWidget(parent) + , ui(new Ui::graphicElementsPanel) +{ + ui->setupUi(this); + + ui->pushBtn_rect->setProperty("shap",GIT_rect); + connect(ui->pushBtn_rect, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem())); + ui->pushBtn_roundRect->setProperty("shap",GIT_roundRect); + connect(ui->pushBtn_roundRect, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem())); + ui->pushBtn_polygon->setProperty("shap",GIT_polygon); + connect(ui->pushBtn_polygon, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem())); +} + +GraphicElementsPanel::~GraphicElementsPanel() +{ + delete ui; +} + +void GraphicElementsPanel::onBtnClicked_GraphicsItem() +{ + QObject* pObject = QObject::sender(); + GraphicsItemType itetType = (GraphicsItemType)pObject->property("shap").toInt(); + emit addGraphicsItem(itetType); +} diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..f1a0e13 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + CMainWindow w; + w.show(); + + return a.exec(); +} diff --git a/source/mainwindow.cpp b/source/mainwindow.cpp new file mode 100644 index 0000000..8f2131f --- /dev/null +++ b/source/mainwindow.cpp @@ -0,0 +1,329 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diagramCavas.h" +#include "graphicElementsPanel.h" +#include "operationCommand.h" +#include "electricElementsBox.h" +#include "topologyView.h" +#include "diagramView.h" +#include "projectModelManager.h" +#include "monitorItemsDlg.h" +#include "monitorPagesDlg.h" +#include "QDetailsView.h" +#include "baseDockWidget.h" +#include "enhancedToolBar.h" + +#include "QDetailsView.h" + +CMainWindow::CMainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::CMainWindow) +{ + ui->setupUi(this); + m_pUndoStack = nullptr; + m_pTopologyView = nullptr; + m_pDiagramView = nullptr; + m_pMonitorItemsDlg = nullptr; + m_pMonitorPagesDlg = nullptr; + m_pMonitorItemsDock = nullptr; + m_pMonitorPagesDock = nullptr; + m_pPropertiesEditorView = nullptr; + _pActMonitor = nullptr; + + qRegisterMetaType("PropertyStateInfo"); + initializeDockUi(); + initializeAction(); +} + +CMainWindow::~CMainWindow() +{ + delete ui; + if(m_pElectricElementsBox) + delete m_pElectricElementsBox; + if(m_pPropertiesEditorView){ + auto pView = m_pPropertiesEditorView->getQuickDetailsView(); + delete pView; + delete m_pPropertiesEditorView; + } +} + + +void CMainWindow::closeEvent(QCloseEvent* event) +{ + // Delete dock manager here to delete all floating widgets. This ensures + // that all top level windows of the dock manager are properly closed + QMainWindow::closeEvent(event); +} + +void CMainWindow::changeEvent(QEvent* event) +{ + //if (event->type() == QEvent::WindowStateChange) + //m_pDrawingPanel->grahpicsViewZoomFit(); +} + +void CMainWindow::moveEvent(QMoveEvent *event) +{ + if(m_pDiagramCavas) + m_pDiagramCavas->updateSubPos(); +} + +void CMainWindow::initializeDockUi() +{ + EnhancedToolBar *toolBar = new EnhancedToolBar("绘图工具", 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); + + m_pElectricElementsBox = new ElectricElementsBox(); + /*m_pElectricElementsBox->initial(); + QWidget* pBox = m_pElectricElementsBox->getToolBox(); + QDockWidget* ElectricElementsDock = new QDockWidget(QString::fromWCharArray(L"图元面板"),this); + ElectricElementsDock->setWidget(pBox); + ElectricElementsDock->setMinimumSize(200,150); + ElectricElementsDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + this->addDockWidget(Qt::RightDockWidgetArea,ElectricElementsDock);*/ + + m_pTopologyView = new TopologyView(this); + m_pTopologyView->initial(); + BaseDockWidget* topologyDock = new BaseDockWidget(QString::fromWCharArray(L"拓扑关系"),this); + topologyDock->setWidget(m_pTopologyView); + //topologyDock->setMinimumSize(120,550); + topologyDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + this->addDockWidget(Qt::LeftDockWidgetArea,topologyDock); + + m_pDiagramView = new DiagramView(this); + m_pDiagramView->initial(); + BaseDockWidget* diagramDock = new BaseDockWidget(QString::fromWCharArray(L"组态图列表"),this); + diagramDock->setWidget(m_pDiagramView); + 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_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_pDiagramCavas = new DiagramCavas(this); + m_pDiagramCavas->initial(); + this->setCentralWidget(m_pDiagramCavas); + connect(m_pElectricElementsBox,&ElectricElementsBox::addEletricItem,m_pDiagramCavas,&DiagramCavas::onSignal_addGraphicsItem); + + connect(&ProjectModelManager::instance(),&ProjectModelManager::modelChange,m_pElectricElementsBox,&ElectricElementsBox::onSignal_modelChanged); + + m_pPropertiesEditorView = new QDetailsView(); + BaseDockWidget* PropertyEditorDock = new BaseDockWidget(QString::fromWCharArray(L"属性面板"),this); + PropertyEditorDock->setWidget(m_pPropertiesEditorView); + PropertyEditorDock->setMinimumSize(350,150); + m_pPropertiesEditorView->setObject(m_pDiagramCavas); + PropertyEditorDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + this->addDockWidget(Qt::RightDockWidgetArea,PropertyEditorDock); + connect(m_pDiagramCavas,&DiagramCavas::selectTarget,this,&CMainWindow::onCavasItemSelected); +} + +void CMainWindow::initializeAction() +{ + //撤销、重做 + /*m_pUndoStack = new QUndoStack(this); + ui->actionUndo = m_pUndoStack->createUndoAction(this, tr("撤销")); + ui->actionUndo->setIcon(QIcon::fromTheme(QString::fromUtf8("edit-undo"))); + ui->actionUndo->setShortcuts(QKeySequence::Undo); + ui->actionRedo = m_pUndoStack->createRedoAction(this, tr("重做")); + ui->actionRedo->setIcon(QIcon::fromTheme(QString::fromUtf8("edit-redo"))); + ui->actionRedo->setShortcuts(QKeySequence::Redo); + ui->toolBar->addAction(ui->actionUndo); + ui->toolBar->addAction(ui->actionRedo); + ui->actionUndo->setEnabled(m_pUndoStack->canUndo()); + ui->actionRedo->setEnabled(m_pUndoStack->canRedo()); + + ui->actionDelete->setShortcut(QKeySequence::Delete);*/ + + //connect(ui->actionDelete, SIGNAL(triggered()), this, SLOT(onSignal_deleteItem())); + //connect(ui->actionZoomIn, SIGNAL(triggered()), this, SLOT(onAction_zoomIn())); + //connect(ui->actionZoomOut, SIGNAL(triggered()), this, SLOT(onAction_zoomOut())); + //connect(ui->actionZoomFit, SIGNAL(triggered()), this, SLOT(onAction_zoomFit())); + //connect(ui->actionGroup, SIGNAL(triggered()), this, SLOT(onAction_createGroup())); + //connect(ui->actionUngroup, SIGNAL(triggered()), this, SLOT(onAction_destroyGroup())); + + connect(m_pTopologyView,&TopologyView::entityCreate,m_pDiagramCavas,&DiagramCavas::onSignal_createEntity); + connect(m_pTopologyView,&TopologyView::entityChange,m_pDiagramCavas,&DiagramCavas::onSignal_changeEntity); + connect(m_pTopologyView,&TopologyView::entityDelete,m_pDiagramCavas,&DiagramCavas::onSignal_deleteEntity); + connect(m_pTopologyView,&TopologyView::entitySelected,m_pDiagramCavas,&DiagramCavas::onSignal_selectEntity); + + connect(m_pDiagramCavas,&DiagramCavas::prepareUpdateTopology,m_pTopologyView,&TopologyView::onUpdateTopology); + + //connect(m_pDiagramView,&DiagramView::diagramCreate,m_pDiagramCavas,&DiagramCavas::onSignal_createDiagram); + connect(m_pDiagramView,&DiagramView::diagramChange,m_pDiagramCavas,&DiagramCavas::onSignal_changeDiagram); + connect(m_pDiagramView,&DiagramView::diagramDelete,m_pDiagramCavas,&DiagramCavas::onSignal_deleteDiagram); + connect(m_pDiagramView,&DiagramView::diagramSelected,m_pDiagramCavas,&DiagramCavas::onSignal_monitorSelected); + connect(m_pDiagramView,&DiagramView::prepareCreateHMI,m_pDiagramCavas,&DiagramCavas::onCreateHMIClicked); + 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::createdMonitorItems,m_pMonitorItemsDlg,&MonitorItemsDlg::onMonitorCreated); + connect(m_pDiagramCavas,&DiagramCavas::updateMonitorTopology,m_pTopologyView,&TopologyView::onMonitorUpdate); + + 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); + connect(ui->actionSave,&QAction::triggered,m_pDiagramCavas,&DiagramCavas::onSignal_savePage); + connect(ui->actionDelete,&QAction::triggered,m_pDiagramCavas,&DiagramCavas::onSignal_deletePage); + + QAction* actNewEditor = ui->menuFile->addAction(QString::fromWCharArray(L"新建组态")); + + QAction* actRun = ui->menuMode->addAction(QString::fromWCharArray(L"运行")); + connect(actRun,&QAction::triggered,m_pDiagramCavas,&DiagramCavas::onSignal_runPage); + + QAction* actEditBay = ui->menuProject->addAction(QString::fromWCharArray(L"管理间隔")); + connect(actEditBay,&QAction::triggered,this,&CMainWindow::onAction_editBay); + + QAction* actPreviewData = ui->menuProject->addAction(QString::fromWCharArray(L"数据查看")); + connect(actPreviewData,&QAction::triggered,this,&CMainWindow::onAction_previewData); + + ui->menuProject->addSeparator(); + + _pActMonitor = ui->menuProject->addAction(QString::fromWCharArray(L"管理监控")); + _pActMonitor->setCheckable(true); + connect(_pActMonitor,&QAction::triggered,this,[&](){ + if(!_pActMonitor->isChecked()){ + _pActMonitor->setChecked(false); + m_pMonitorItemsDock->hide(); + m_pMonitorPagesDock->hide(); + } + else{ + _pActMonitor->setChecked(true); + m_pMonitorItemsDock->show(); + m_pMonitorPagesDock->show(); + + if(m_pDiagramCavas) + m_pDiagramCavas->updateMonitorListFromDB(); + } + }); + + QAction* settingAct = ui->menuSetting->addAction(QString::fromWCharArray(L"网络设置")); + connect(settingAct,&QAction::triggered,m_pDiagramCavas,&DiagramCavas::onSignal_openNetSetting); + +} + +void CMainWindow::onAction_zoomIn() +{ + //m_pDrawingPanel->grahpicsViewZoomIn(); +} + +void CMainWindow::onAction_zoomOut() +{ + //m_pDrawingPanel->grahpicsViewZoomOut(); +} + +void CMainWindow::onAction_zoomFit() +{ + //m_pDrawingPanel->grahpicsViewZoomFit(); +} + +void CMainWindow::onAction_createGroup() +{ + /*GraphicsItemGroup* group = m_pDrawingPanel->createItemGroup(); + if(group) + { + QGraphicsScene* scene = m_pDrawingPanel->getQGraphicsScene(); + QUndoCommand* createItemGropu = new CreateItemGoupCommand(group, scene); + m_pUndoStack->push(createItemGropu); + }*/ +} + +void CMainWindow::onAction_destroyGroup() +{ + /*QGraphicsScene* scene = m_pDrawingPanel->getQGraphicsScene(); + QList listItem = scene->selectedItems(); + if(listItem.count() != 1) + return; //只能选择一个解组 + + QGraphicsItem* item = listItem.first(); + if(!item) + return; + + GraphicsItemGroup *group = dynamic_cast(item); + if(group) + { + QUndoCommand* destroyItemGropu = new DestroyItemGoupCommand(group, scene); + m_pUndoStack->push(destroyItemGropu); + }*/ +} + +void CMainWindow::onAction_editBay() +{ + m_pDiagramCavas->onSignl_openCurrentBay(); +} + +void CMainWindow::onAction_previewData() +{ + m_pDiagramCavas->onSignal_openStructDataPreview(); +} + +void CMainWindow::onSignal_addItem(QGraphicsItem* item) +{ + if(item) + { + QUndoCommand* addItemCommand = new AddItemCommand(item, item->scene()); + m_pUndoStack->push(addItemCommand); + } +} + +void CMainWindow::onSignal_deleteItem() +{ + /*QGraphicsScene* scene = m_pDrawingPanel->getQGraphicsScene(); + if (scene && scene->selectedItems().isEmpty()) + return; + + QUndoCommand* deleteItemCommand = new DeleteItemCommand(scene); + m_pUndoStack->push(deleteItemCommand); //push时会自动调用一次command的redo函数*/ +} + +void CMainWindow::onCavasItemSelected(QObject* obj) +{ + if(m_pPropertiesEditorView) + m_pPropertiesEditorView->setObject(obj); +} + +GraphicElementsPanel* CMainWindow::graphicsElementsPanel() const +{ + if(m_pGraphicElementsPanel) + return m_pGraphicElementsPanel; + else + { + qDebug("get m_pGraphicElementsPanel err"); + return NULL; + } +} + + diff --git a/source/monitorItemsDlg.cpp b/source/monitorItemsDlg.cpp new file mode 100644 index 0000000..3677a0d --- /dev/null +++ b/source/monitorItemsDlg.cpp @@ -0,0 +1,268 @@ +#include "monitorItemsDlg.h" +#include "ui_monitorItemsDlg.h" +#include "dataBase.h" +#include "tools.h" +#include + + +MonitorItemsDlg::MonitorItemsDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::monitorItemsDlg) + ,_modelAll(nullptr) + ,_modelSelect(nullptr) +{ + ui->setupUi(this); + initial(); +} + +MonitorItemsDlg::~MonitorItemsDlg() +{ + delete ui; +} + +void MonitorItemsDlg::initial() +{ + _modelAll = new QStandardItemModel(this); + ui->treeView_all->setModel(_modelAll); + ui->treeView_all->setHeaderHidden(true); + _modelSelect = new QStandardItemModel(this); + ui->treeView_select->setModel(_modelSelect); + ui->treeView_select->setHeaderHidden(true); + ui->le_name->setPlaceholderText("输入名称"); + connect(ui->btn_generate,&QPushButton::clicked,this,&MonitorItemsDlg::onGenerateClicked); + connect(_modelAll, &QStandardItemModel::itemChanged,this, &MonitorItemsDlg::onItemChanged); +} + +void MonitorItemsDlg::onUpdateItems(QList lst,bool refresh) +{ + if(refresh){ + QStandardItem *root = _modelAll->invisibleRootItem(); + int rowCount = root->rowCount(); + if (rowCount > 0) { + _modelAll->removeRows(0, rowCount); + } + // 清空间隔节点缓存 + m_mapBayItems.clear(); + } + + // 先处理间隔节点(nCategory == 1) + for(auto &info:lst){ + auto curItem = info.item; + if(curItem.nCategory == 1){ // 间隔信息 + // 查找是否已存在该间隔节点 + QStandardItem* pBayItem = findBayItem(curItem.sName); + if(!pBayItem){ + // 创建间隔节点 + pBayItem = new QStandardItem(curItem.sName); + pBayItem->setCheckable(true); // 启用勾选框 + pBayItem->setCheckState(Qt::Unchecked); + pBayItem->setData(curItem.nCategory, Qt::UserRole+1); // 存储类别 + pBayItem->setData(curItem.nEquipType, Qt::UserRole+2); // 存储设备类型 + pBayItem->setData(curItem.uid, Qt::UserRole+3); // 存储UUID + + // 存储到缓存中 + m_mapBayItems[curItem.sName] = pBayItem; + _modelAll->appendRow(pBayItem); + } + } + } + + // 再处理设备节点(nCategory == 0) + for(auto &info:lst){ + auto curItem = info.item; + if(curItem.nCategory == 0){ // 设备信息 + // 获取设备所属的间隔名称 + QString bayName = info.parent.sName; + + if(!bayName.isEmpty()){ + // 查找对应的间隔节点 + QStandardItem* pBayItem = findBayItem(bayName); + + if(pBayItem){ + // 创建设备节点 + QStandardItem *pItem = new QStandardItem(curItem.sName); + pItem->setCheckable(true); // 启用勾选框 + pItem->setCheckState(Qt::Unchecked); + pItem->setData(curItem.nCategory, Qt::UserRole+1); + pItem->setData(curItem.nEquipType, Qt::UserRole+2); + pItem->setData(curItem.uid, Qt::UserRole+3); + + // 不再为母线或独立设备添加子设备 + // 所有设备都作为间隔的直接子节点 + + pBayItem->appendRow(pItem); + } + } + } + } + + // 展开所有节点 + ui->treeView_all->expandAll(); +} + +void MonitorItemsDlg::onSelectItems(QList lst) +{ + ui->stackedWidget->setCurrentIndex(0); + resetSelect(); + for(auto& info:lst){ + QModelIndex itemIndex = findIndex(_modelAll,info.item.uid,Qt::UserRole+3); + if(itemIndex.isValid()) + { + QStandardItem *pItem = _modelAll->itemFromIndex(itemIndex); + pItem->setCheckState(Qt::Checked); + } + } + +} + +void MonitorItemsDlg::onGenerateClicked() +{ + QString sName = ui->le_name->text(); + if (sName.isEmpty()) { + ui->le_name->setFocus(); + return; + } + QList lst; + QList lstItem = getTreeViewCheckedItems(ui->treeView_all); + for(auto& pItem:lstItem){ + HierarchyItem info; + auto pParent = pItem->parent(); + if(pParent){ + info.parent.nCategory = pParent->data(Qt::UserRole+1).toInt(); + info.parent.nEquipType = pParent->data(Qt::UserRole+2).toInt(); + info.parent.uid = pParent->data(Qt::UserRole+3).toUuid(); + info.parent.sName = pParent->text(); + } + info.item.nCategory = pItem->data(Qt::UserRole+1).toInt(); + info.item.nEquipType = pItem->data(Qt::UserRole+2).toInt(); + info.item.uid = pItem->data(Qt::UserRole+3).toUuid(); + info.item.sName = pItem->text(); + + auto lstChild = getAllChildren(pItem); + for(auto &child:lstChild){ + HierarchyStructItem stru; + stru.nCategory = child->data(Qt::UserRole+1).toInt(); + stru.nEquipType = child->data(Qt::UserRole+2).toInt(); + stru.uid = child->data(Qt::UserRole+3).toUuid(); + stru.sName = child->text(); + info.subList.append(stru); + } + lst.append(info); + } + + emit generateMonitor(sName,lst); +} + +void MonitorItemsDlg::onMonitorCreated(QList lst) +{ + /*ui->stackedWidget->setCurrentIndex(1); + ui->listWidget_select->clear(); + + for(auto& pair:lst){ + QListWidgetItem *item = new QListWidgetItem(pair.first); + item->setData(Qt::UserRole,pair.second); + ui->listWidget_select->addItem(item); + }*/ +} + +void MonitorItemsDlg::onItemChanged(QStandardItem *item) +{ + QSignalBlocker blocker(_modelAll); + + if (item->isCheckable()) { + Qt::CheckState newState = item->checkState(); + // 设置所有子项与父项相同的状态 + setChildrenCheckState(item, newState); + } + + ui->treeView_all->repaint(); +} + +void MonitorItemsDlg::setChildrenCheckState(QStandardItem *parent, Qt::CheckState state) { + for (int row = 0; row < parent->rowCount(); ++row) { + QStandardItem *child = parent->child(row, 0); // 第一列 + if (child && child->isCheckable()) { + child->setCheckState(state); + setChildrenCheckState(child, state); + } + } +} + +void MonitorItemsDlg::traverseSelectStandardItemModel(QStandardItemModel *model,Qt::CheckState check) { + if (!model) return; + + // 遍历所有顶层项 + for (int row = 0; row < model->rowCount(); ++row) { + for (int col = 0; col < model->columnCount(); ++col) { + QStandardItem *item = model->item(row, col); + item->setCheckState(check); + traverseSelectStandardItem(item, 0,check); + } + } +} + +void MonitorItemsDlg::traverseSelectStandardItem(QStandardItem *item, int depth,Qt::CheckState check) { + if (!item) return; + + // 遍历子项 + for (int row = 0; row < item->rowCount(); ++row) { + for (int col = 0; col < item->columnCount(); ++col) { + QStandardItem *child = item->child(row, col); + child->setCheckState(check); + traverseSelectStandardItem(child, depth + 1,check); + } + } +} + +QStandardItem* MonitorItemsDlg::findBayItem(const QString& bayName) +{ + // 先从缓存查找 + if(m_mapBayItems.contains(bayName)){ + return m_mapBayItems[bayName]; + } + + // 遍历查找 + for(int i = 0; i < _modelAll->rowCount(); ++i){ + QStandardItem* pItem = _modelAll->item(i); + if(pItem->text() == bayName && + pItem->data(Qt::UserRole+1).toInt() == 1){ // 类别为间隔 + return pItem; + } + } + return nullptr; +} + +QList MonitorItemsDlg::getCheckedItems(QStandardItem* parentItem) { + QList checkedItems; + + if (!parentItem) return checkedItems; + + for (int i = 0; i < parentItem->rowCount(); ++i) { + QStandardItem* childItem = parentItem->child(i); + + if (childItem->checkState() == Qt::Checked) { + checkedItems.append(childItem); + } + + // 递归检查子项 + checkedItems.append(getCheckedItems(childItem)); + } + + return checkedItems; +} + +// 主函数:获取treeView中所有checked项 +QList MonitorItemsDlg::getTreeViewCheckedItems(QTreeView* treeView) { + QList checkedItems; + + QStandardItemModel* model = qobject_cast(treeView->model()); + if (!model) return checkedItems; + + QStandardItem* rootItem = model->invisibleRootItem(); + return getCheckedItems(rootItem); +} + +void MonitorItemsDlg::resetSelect() +{ + traverseSelectStandardItemModel(_modelAll,Qt::Unchecked); +} diff --git a/source/monitorPagesDlg.cpp b/source/monitorPagesDlg.cpp new file mode 100644 index 0000000..7dd87e6 --- /dev/null +++ b/source/monitorPagesDlg.cpp @@ -0,0 +1,126 @@ +#include "monitorPagesDlg.h" +#include "ui_monitorPagesDlg.h" +#include +#include "dataBase.h" +#include "tools.h" + + + +MonitorPagesDlg::MonitorPagesDlg(QWidget *parent) + : QDialog(parent) + , ui(new Ui::monitorPagesDlg) + ,_pModel(nullptr) +{ + ui->setupUi(this); + initial(); +} + +MonitorPagesDlg::~MonitorPagesDlg() +{ + delete ui; +} + +void MonitorPagesDlg::initial() +{ + _pModel = new QStandardItemModel(this); + ui->treeView->setModel(_pModel); + ui->treeView->setHeaderHidden(true); + connect(ui->treeView, &QTreeView::clicked, this, &MonitorPagesDlg::onItemClicked); +} + +void MonitorPagesDlg::onMonitorCreated(QString sProj,QPair pair,int nMode) +{ + QStandardItem* topLevelItem = findTopLevelItem(sProj); + if (topLevelItem) { + // 如果存在,直接在该项下插入子项 + QStandardItem* childItem = new QStandardItem(pair.first); + childItem->setData(pair.second); + topLevelItem->appendRow(childItem); + } else { + // 如果不存在,创建新的顶层项并插入子项 + QStandardItem* newTopLevelItem = new QStandardItem(sProj); + QStandardItem* childItem = new QStandardItem(pair.first); + childItem->setData(pair.second); + newTopLevelItem->appendRow(childItem); + _pModel->appendRow(newTopLevelItem); + } + ui->treeView->expandAll(); +} + +void MonitorPagesDlg::onItemClicked(const QModelIndex &index) +{ + QStandardItem* item = _pModel->itemFromIndex(index); + int nLevel = getLevel(item); + if(nLevel == 0 || nLevel == -1) //顶层不响应 + return; + QStandardItem* parent = item->parent(); + if(item) + { + DiagramInfo info; + info.id = item->data(Qt::UserRole+1).toUuid(); + info.sName = item->text(); + info.sBasePageName = parent->text(); + emit monitorSelected(info); + } +} + +void MonitorPagesDlg::onIndexRbtnClicked(const QPoint &pos) +{ + // 获取当前点击的位置对应的索引 + QMenu menu; + QModelIndex index = ui->treeView->indexAt(pos); + if (!index.isValid()) { + return; + } + else{ //目标组态图下添加子组态图 + QStandardItem* item = _pModel->itemFromIndex(index); + if(item){ + int nLevel = getLevel(item); + if(nLevel == 0){ + QAction *allSaveAction = new QAction("全部保存", this); + + connect(allSaveAction,&QAction::triggered,this,[&](){ + + QList> lstInfo; + auto lstItem = getAllChildren(item); + for(auto& subItem:lstItem){ + QString sName = subItem->text(); + QUuid uid = subItem->data().toUuid(); + lstInfo.append(qMakePair(sName,uid)); + } + emit prepareSaveMonitor(lstInfo); + }); + + menu.addAction(allSaveAction); + } + else if(nLevel == 1){ + QAction *saveAction = new QAction("保存", this); + + connect(saveAction,&QAction::triggered,this,[&](){ + QList> lstInfo; + QString sName = item->text(); + QUuid uid = item->data().toUuid(); + lstInfo.append(qMakePair(sName,uid)); + + emit prepareSaveMonitor(lstInfo); + }); + + menu.addAction(saveAction); + } + } + } + + // 在点击位置显示菜单 + menu.exec(ui->treeView->mapToGlobal(pos)); +} + +QStandardItem* MonitorPagesDlg::findTopLevelItem(const QString& name) +{ + for (int i = 0; i < _pModel->rowCount(); ++i) { + QStandardItem* item = _pModel->item(i); + if (item && item->text() == name) { + return item; + } + } + return nullptr; +} diff --git a/source/operationCommand.cpp b/source/operationCommand.cpp new file mode 100644 index 0000000..c5598e0 --- /dev/null +++ b/source/operationCommand.cpp @@ -0,0 +1,141 @@ +#include "operationCommand.h" +#include "graphicsItem/graphicsItemGroup.h" +#include + + +AddItemCommand::AddItemCommand(QGraphicsItem* item, QGraphicsScene* scene, QUndoCommand* parent) + : QUndoCommand(parent) +{ + m_pItem = item; + m_itemPos = item->pos(); + m_pGraphicsScene = scene; +} +AddItemCommand::~AddItemCommand() +{ +} +void AddItemCommand::undo() +{ + m_pGraphicsScene->removeItem(m_pItem); + m_pGraphicsScene->update(); +} +void AddItemCommand::redo() +{ + if(m_pItem->scene()) //因为添加图元后同步创建一条该指令,平且在push进入stack的时候redo会被触发一次,因此这里加判断,防止重复操作 + return; + + m_pGraphicsScene->addItem(m_pItem); + m_pItem->setPos(m_itemPos); + m_pGraphicsScene->update(); +} + +DeleteItemCommand::DeleteItemCommand(QGraphicsScene* scene, QUndoCommand* parent) + : QUndoCommand(parent) +{ + m_pGraphicsScene = scene; + m_listItem = scene->selectedItems(); +} +DeleteItemCommand::~DeleteItemCommand() +{ +} +void DeleteItemCommand::undo() +{ + foreach(QGraphicsItem* item, m_listItem) + { + // QGraphicsItemGroup* group = dynamic_cast(item->parentItem()); + // if(!group) + { + m_pGraphicsScene->addItem(item); + } + } + + m_pGraphicsScene->update(); +} +void DeleteItemCommand::redo() +{ + foreach(QGraphicsItem* item, m_listItem) + { + // QGraphicsItemGroup* group = dynamic_cast(item->parentItem()); + // if(!group) + { + m_pGraphicsScene->removeItem(item); //remove即可,不要delete,因为会影响撤回(undo)操作 + } + } + + m_pGraphicsScene->update(); +} + +CreateItemGoupCommand::CreateItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* scene, QUndoCommand* parent) + : QUndoCommand(parent) +{ + m_pGroup = group; + m_pGraphicsScene = scene; + m_listItem = group->getItems(); +} +CreateItemGoupCommand::~CreateItemGoupCommand() +{ +} +void CreateItemGoupCommand::undo() +{ + m_pGroup->setSelected(false); + foreach (QGraphicsItem *item, m_listItem) + { + //item->setSelected(true); + m_pGroup->removeFromGroup(item); + AbstractShape *ab = qgraphicsitem_cast(item); + ab->updateCoordinate(); + ab->setSelected(true); + } + m_pGraphicsScene->removeItem(m_pGroup); + m_pGraphicsScene->update(); +} +void CreateItemGoupCommand::redo() +{ + if(!m_pGroup->scene()) //因为添加图元后同步创建一条该指令,平且在push进入stack的时候redo会被触发一次,因此这里加判断,防止重复操作 + { + foreach (QGraphicsItem *item, m_listItem) + { + item->setSelected(false); + m_pGroup->addToGroup(item); + } + m_pGraphicsScene->addItem(m_pGroup); + } + + m_pGroup->setSelected(true); + m_pGraphicsScene->update(); +} + +DestroyItemGoupCommand::DestroyItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* scene, QUndoCommand* parent) + : QUndoCommand(parent) +{ + m_pGroup = group; + m_pGraphicsScene = scene; + m_listItem = group->getItems(); +} +DestroyItemGoupCommand::~DestroyItemGoupCommand() +{ +} +void DestroyItemGoupCommand::undo() +{ + foreach (QGraphicsItem *item, m_listItem) + { + item->setSelected(false); + m_pGroup->addToGroup(item); + } + m_pGroup->setSelected(true); + m_pGraphicsScene->addItem(m_pGroup); + m_pGraphicsScene->update(); +} +void DestroyItemGoupCommand::redo() +{ + m_pGroup->setSelected(false); + foreach (QGraphicsItem *item, m_listItem) + { + //item->setSelected(true); + m_pGroup->removeFromGroup(item); + AbstractShape *ab = qgraphicsitem_cast(item); + ab->updateCoordinate(); + ab->setSelected(true); + } + m_pGraphicsScene->removeItem(m_pGroup); + m_pGraphicsScene->update(); +} diff --git a/source/projectTableDelegate.cpp b/source/projectTableDelegate.cpp new file mode 100644 index 0000000..b848b56 --- /dev/null +++ b/source/projectTableDelegate.cpp @@ -0,0 +1,155 @@ +#include +#include +#include + +#include "projectTableDelegate.h" +#include "selectorDialog.h" +//#include "global.h" + +ProjectTableDelegate::ProjectTableDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ + +}; + +ProjectTableDelegate::~ProjectTableDelegate() +{ + +} + +QWidget* ProjectTableDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if(index.column() == TD_ProjectModel) //editline + { + QLineEdit *editor = new QLineEdit(parent); + + // 连接编辑完成信号 + connect(editor, &QLineEdit::editingFinished, this, [this, editor, index]() { + emit editingFinished(index, editor->text()); + }); + + return editor; + } + return nullptr; +} + +/*bool ProjectTableDelegate::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + QLineEdit* editor = qobject_cast(obj); + if (editor) { + emit yourCustomSignal(editor->text()); + commitData(editor); // 显式提交数据 + closeEditor(editor); // 关闭编辑器 + return true; // 阻止事件继续传播 + } + } + } + return QStyledItemDelegate::eventFilter(obj, event); +}*/ + +/*void ProjectTableDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const +{ + if(index.column() == TD_MetaModel) + { + QComboBox* comboBox = static_cast(editor); + comboBox->setCurrentText(index.data(Qt::EditRole).toString()); + } +} + +void ProjectTableDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const +{ + if(index.column() == TD_MetaModel) + { + QComboBox* comboBox = static_cast(editor); + model->setData(index, comboBox->currentText(), Qt::EditRole); + + emit editingFinished(index, comboBox->currentText()); //发送自定义信号 + } + +}*/ + +bool ProjectTableDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, + const QStyleOptionViewItem& option, const QModelIndex& index) +{ + if (event->type() == QEvent::MouseButtonDblClick) { + if(index.column() == TD_ProjectModel) + { + + } + else if(index.column() == TD_MetaModel) + { + SelectorDialog dialog(option.widget->parentWidget()->parentWidget()->parentWidget()->parentWidget()); + dialog.initial(ST_MetaModel); + if(dialog.exec() == QDialog::Accepted) { + modelType meta = dialog.selectedMeta(); + if(!meta.modelType.isEmpty()) { + model->setData(index, meta.modelName, Qt::EditRole); + model->setData(index, meta.modelType, Qt::EditRole+1); + emit editingFinished(index,meta.modelType); + } + } + return true; + } + else if(index.column() == TD_ComponentType) + { + SelectorDialog dialog(option.widget->parentWidget()->parentWidget()->parentWidget()->parentWidget()); + dialog.initial(ST_ComponentType); + if(dialog.exec() == QDialog::Accepted) { + QString component = dialog.selectedComponent(); + if(!component.isEmpty()) { + model->setData(index, component, Qt::EditRole); + emit editingFinished(index,component); + } + } + return true; + } + } + return QStyledItemDelegate::editorEvent(event, model, option, index); +} + +void ProjectTableDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + //根据行号设置交替色 + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + QModelIndex firstColIndex = index.sibling(index.row(), 0); + TableItemState state = TableItemState(firstColIndex.data(Qt::UserRole).toInt()); + + /*if(index.column() == 0 && state == TS_create) + { + opt.palette.setColor(QPalette::Text, Qt::red); + } + else if(index.column() == 0 && state == TS_select) + { + opt.palette.setColor(QPalette::Text, Qt::green); + } + else if(index.column() == 0 && state == TS_edit) + { + opt.palette.setColor(QPalette::Text, Qt::yellow); + } + QStyledItemDelegate::paint(painter, opt, index);*/ + + + if(state == TS_Create) + { + opt.palette.setColor(QPalette::Text, Qt::red); + } + else if(state == TS_Select) + { + opt.palette.setColor(QPalette::Text, Qt::green); + } + else if(state == TS_Edit) + { + opt.palette.setColor(QPalette::Text, Qt::yellow); + } + + //先执行默认绘制(包括背景、文本等基础元素) + QStyledItemDelegate::paint(painter, opt, index); + +} diff --git a/source/renameModel.cpp b/source/renameModel.cpp new file mode 100644 index 0000000..fb929a4 --- /dev/null +++ b/source/renameModel.cpp @@ -0,0 +1,151 @@ +#include +#include "renameModel.h" +#include "projectModelDlg.h" +#include "dataBase.h" +#include "ui_renameModel.h" + +RenameModel::RenameModel(QWidget *parent) + : QDialog(parent) + , ui(new Ui::renameModel) + ,_pParent(nullptr) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); + _pParent = dynamic_cast(parent); + initial(); +} + +RenameModel::~RenameModel() +{ + delete ui; +} + +void RenameModel::initial() +{ + connect(ui->btn_ok,&QPushButton::clicked,this,&RenameModel::onOkClicked); + connect(ui->btn_cancel,&QPushButton::clicked,this,&RenameModel::onCancelClicked); +} + +void RenameModel::showCenter() +{ + if (!_pParent) { + qWarning("No parent widget found; dialog will appear at the default position."); + show(); + return; + } + + // 父窗口的几何信息 + QRect parentRect = _pParent->geometry(); + + // 对话框的几何信息 + int dialogWidth = width(); + int dialogHeight = height(); + + // 计算中心位置 + int x = parentRect.x() + (parentRect.width() - dialogWidth) / 2; + int y = parentRect.y() + (parentRect.height() - dialogHeight) / 2; + + // 移动对话框到中心位置并显示 + move(x, y); + show(); + + setShowName(); +} + +void RenameModel::setShowName() +{ + if(_pParent) + { + QString str = _pParent->getProjectName(); + ui->lineEdit_name->setText(str); + + ui->lineEdit_name->setSelection(0,str.length()); + } +} + +/*projectState RenameModel::couldSave() +{ + if(_pParent) + { + QString meta = _pParent->getMetaName(); + QString str = ui->lineEdit_name->text(); + if(str == QString::fromWCharArray(L"新建")) + { + return Err; + } + else + { + //todo:判断输入的名称是否存在 + QMap map = DataBase::GetInstance()->getCheckStateFromManager(str); + if(map.isEmpty()) + { + return NotExist; + } + else + { + bool val = _pParent->ifProjectEqual(map); + if(val){ + return Exist; + } + else{ + return Changed; + } + } + } + } +}*/ + +void RenameModel::onOkClicked() +{ + /*if(_pParent) + { + projectState state = couldSave(); + switch(state){ + case Err: + ui->label_info->setText(QString::fromWCharArray(L"请输入需保存的名称")); + break; + case NotExist: + _pParent->generate(ui->lineEdit_name->text()); + ui->label_info->clear(); + hide(); + break; + case Exist: + ui->label_info->setText(QString::fromWCharArray(L"该模型已存在")); + break; + case Changed: + QMessageBox msgBox; + msgBox.setText(QString::fromWCharArray(L"提示")); + msgBox.setInformativeText(QString::fromWCharArray(L"该模型已存在且与同名模型不一致,是否替换库模型?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret = msgBox.exec(); + if(ret == QMessageBox::Yes) + { + QMap mapCheckState = DataBase::GetInstance()->getCheckStateFromManager(ui->lineEdit_name->text()); //获取选择状态 + QString sRes = _pParent->modifyProjectModel(mapCheckState); + if(!sRes.isEmpty()) + { + QMessageBox::information(NULL, QString::fromWCharArray(L"提示"), QString::fromWCharArray(L"修改模型成功")); + } + else + { + QMessageBox::information(NULL, QString::fromWCharArray(L"提示"), QString::fromWCharArray(L"修改模型失败")); + } + hide(); + } + else if(ret == QMessageBox::Cancel) + { + + } + ui->label_info->clear(); + break; + } + }*/ +} + +void RenameModel::onCancelClicked() +{ + hide(); + ui->label_info->clear(); +} + diff --git a/source/selectorDialog.cpp b/source/selectorDialog.cpp new file mode 100644 index 0000000..a5e82ab --- /dev/null +++ b/source/selectorDialog.cpp @@ -0,0 +1,107 @@ +#include +#include "selectorDialog.h" +//#include "global.h" +#include "dataBase.h" + +SelectorDialog::SelectorDialog(QWidget* parent) + : QDialog(parent) + ,m_buttonBox(nullptr) +{ + this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); +} + +void SelectorDialog::initial(SelectorDialogType tpe) +{ + m_dlgType = tpe; + setupUI(); + setupConnections(); +} + +void SelectorDialog::setupUI() { + setWindowTitle("选择类型"); + setFixedSize(200, 200); + + m_listView = new QListView(this); + QStandardItemModel* model = initialModel(); + m_listView->setModel(model); + m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + + m_buttonBox = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(m_listView); + layout->addWidget(m_buttonBox); +} + +QStandardItemModel * SelectorDialog::initialModel() +{ + QStandardItemModel *model = new QStandardItemModel(this); + if(m_dlgType == ST_MetaModel){ + QList metas = getMetaList(); + for(auto &meta:metas) + { + QStandardItem *item = new QStandardItem(); + //item->setIcon(QIcon(":/icons/folder.png")); // 设置图标 + item->setText(meta.modelName); // 设置文本 + item->setData(meta.modelType,Qt::UserRole); + model->appendRow(item); + } + } + else if(m_dlgType == ST_ComponentType){ + QStringList components = {"断路器", "母线", "异步电动机"}; + for(auto &obj:components) + { + QStandardItem *item = new QStandardItem(); + //item->setIcon(QIcon(":/icons/folder.png")); // 设置图标 + item->setText(obj); // 设置文本 + //item->setData("Extra Data for Item 1", Qt::UserRole); // 设置额外属性 + + model->appendRow(item); + } + } + + return model; +} + +void SelectorDialog::setupConnections() { + connect(m_listView, &QListView::doubleClicked, [this](const QModelIndex& index){ + if(m_dlgType == ST_MetaModel){ + m_selectedMeta.modelName = index.data().toString(); + m_selectedMeta.modelType = index.data(Qt::UserRole).toString(); + } + else{ + m_selectedComponent = index.data().toString(); + } + accept(); + }); + + connect(m_buttonBox, &QDialogButtonBox::accepted, [this]{ + if(auto index = m_listView->currentIndex(); index.isValid()) { + if(m_dlgType == ST_MetaModel){ + m_selectedMeta.modelName = index.data().toString(); + m_selectedMeta.modelType = index.data(Qt::UserRole).toString(); + } + else{ + m_selectedComponent = index.data().toString(); + } + } + accept(); + }); + + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +QList SelectorDialog::getMetaList() const +{ + QMap modelMap = DataBase::GetInstance()->ModelType(); + + QList modelSet; + for(auto &model:modelMap) + { + modelSet.append(model); + } + + return modelSet; +} + diff --git a/source/toolBarConfig.cpp b/source/toolBarConfig.cpp new file mode 100644 index 0000000..e0f5351 --- /dev/null +++ b/source/toolBarConfig.cpp @@ -0,0 +1,129 @@ +#include "toolBarConfig.h" +#include +#include +#include +#include +#include + +ToolBarConfig::ToolBarConfig(QObject *parent) + : QObject(parent) +{ + addDefaultTools(); +} + +void ToolBarConfig::addDefaultTools() +{ + // 添加基本工具作为后备 + m_tools["image"] = ToolInfo("image", "图像", ":/images/element/icon_image.png"); + m_tools["text"] = ToolInfo("text", "文本", ":/images/element/icon_text.png"); +} + +bool ToolBarConfig::loadFromFile(const QString &filePath) +{ + QFile file(filePath); + if (!file.exists()) { + qDebug() << "配置文件不存在:" << filePath; + return false; // 文件不存在,使用默认工具 + } + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "无法打开配置文件:" << filePath; + return false; + } + + QByteArray data = file.readAll(); + file.close(); + + return loadFromJson(data); +} + +bool ToolBarConfig::loadFromJson(const QByteArray &jsonData) +{ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error); + + if (error.error != QJsonParseError::NoError) { + qWarning() << "JSON解析错误:" << error.errorString(); + return false; + } + + if (!doc.isArray()) { + qWarning() << "配置文件格式错误: 应为数组"; + return false; + } + + // 清空现有工具,但保留默认工具 + m_tools.clear(); + addDefaultTools(); + + // 加载配置文件中的工具 + QJsonArray toolArray = doc.array(); + for (const QJsonValue &value : toolArray) { + if (!value.isObject()) { + continue; + } + + ToolInfo info = parseTool(value.toObject()); + if (!info.type.isEmpty()) { + m_tools[info.type] = info; + } + } + + return true; +} + +ToolInfo ToolBarConfig::parseTool(const QJsonObject &json) +{ + ToolInfo info; + info.type = json["type"].toString(); + info.name = json["name"].toString(); + info.iconPath = json["iconPath"].toString(); + + if (json.contains("properties")) { + QJsonObject props = json["properties"].toObject(); + for (auto it = props.begin(); it != props.end(); ++it) { + info.properties[it.key()] = it->toVariant(); + } + } + + return info; +} + +ToolInfo ToolBarConfig::getTool(const QString &type) const +{ + return m_tools.value(type); +} + +QList ToolBarConfig::getAllTools() const +{ + return m_tools.values(); +} + +QStringList ToolBarConfig::getToolTypes() const +{ + return m_tools.keys(); +} + +bool ToolBarConfig::contains(const QString &type) const +{ + return m_tools.contains(type); +} + +QIcon ToolInfo::getIcon() const +{ + if (iconPath.isEmpty()) { + return QIcon(); + } + + // 尝试从资源文件加载 + if (iconPath.startsWith(":/")) { + return QIcon(iconPath); + } + + // 尝试从文件系统加载 + if (QFile::exists(iconPath)) { + return QIcon(iconPath); + } + + return QIcon(); +} diff --git a/source/toolBox.cpp b/source/toolBox.cpp new file mode 100644 index 0000000..be73ea2 --- /dev/null +++ b/source/toolBox.cpp @@ -0,0 +1,58 @@ +#include "toolBox.h" +#include "toolPage.h" + +#include +#include + +ToolBox::ToolBox(QWidget *parent) : + QWidget(parent), + m_pContentVBoxLayout(nullptr) +{ + + QVBoxLayout *vBoxLayout = new QVBoxLayout(this); + vBoxLayout->setContentsMargins(0, 0, 0, 0); + //vBoxLayout->addLayout(m_pContentVBoxLayout); + //vBoxLayout->addStretch(0); + +} + +ToolBox::~ToolBox() +{ + +} + +void ToolBox::addWidget(const QString &title, QWidget *pWidget) +{ + ToolPage *page = new ToolPage(this); + page->addWidget(title, pWidget); + + QBoxLayout* pLayout = dynamic_cast(layout()); + pLayout->insertWidget(0,page); + pLayout->addStretch(0); + m_mapWidget.insert(title,page); +} + +void ToolBox::removeWidget(const QString &title) +{ + bool bExist = m_mapWidget.contains(title); + if(bExist) + { + QWidget *pWidget = m_mapWidget.take(title); + if(pWidget) + { + QBoxLayout* pLayout = dynamic_cast(layout()); + ToolPage* toolPage = dynamic_cast(pWidget); + if(toolPage) + { + pLayout->removeWidget(toolPage); + delete toolPage; + } + + } + } + else + { + //cerr + } + +} diff --git a/source/toolPage.cpp b/source/toolPage.cpp new file mode 100644 index 0000000..8700a8e --- /dev/null +++ b/source/toolPage.cpp @@ -0,0 +1,77 @@ +#include "toolPage.h" + +#include +#include +#include +#include +#include +#include + +ToolPage::ToolPage(QWidget *parent) : + QWidget(parent), + m_bIsExpanded(true), + m_pLabel(nullptr), + m_pPushButtonFold(nullptr), + m_pContent(nullptr) +{ + setAttribute(Qt::WA_StyledBackground); + + m_pPushButtonFold = new QPushButton(this); + m_pLabel = new QLabel(this); + m_pLabel->setFixedSize(20, 20); + m_pLabel->setPixmap(QPixmap(":/images/icon_down_arrow.png").scaled(m_pLabel->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + QHBoxLayout *hLayout = new QHBoxLayout(m_pPushButtonFold); + QVBoxLayout *vLayout = new QVBoxLayout(this); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(2); + vLayout->addWidget(m_pPushButtonFold); + hLayout->setContentsMargins(0, 0, 5, 0); + hLayout->addStretch(1); + hLayout->addWidget(m_pLabel); + + connect(m_pPushButtonFold, &QPushButton::clicked, this, &ToolPage::onPushButtonFoldClicked); +} + +ToolPage::~ToolPage() +{ + if(m_pContent) + delete m_pContent; + if(m_pPushButtonFold) + delete m_pPushButtonFold; +} + +void ToolPage::addWidget(const QString &title, QWidget *widget) +{ + if(!m_pContent) + { + m_pPushButtonFold->setText(title); + layout()->addWidget(widget); + m_pContent = widget; + } + +} + +void ToolPage::expand() +{ + if(m_pContent) + m_pContent->show(); + m_bIsExpanded = true; + m_pLabel->setPixmap(QPixmap(":/images/icon_down_arrow.png").scaled(m_pLabel->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +} + +void ToolPage::collapse() +{ + if(m_pContent) + m_pContent->hide(); + m_bIsExpanded = false; + m_pLabel->setPixmap(QPixmap(":/images/icon_left_arrow.png").scaled(m_pLabel->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +} + +void ToolPage::onPushButtonFoldClicked() +{ + if (m_bIsExpanded) { + collapse(); + } else { + expand(); + } +} diff --git a/source/topologyTree.cpp b/source/topologyTree.cpp new file mode 100644 index 0000000..a97ffb2 --- /dev/null +++ b/source/topologyTree.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include "topologyTree.h" +//#include "global.h" + +TopologyTree::TopologyTree(QWidget *parent) + : QTreeView(parent) +{ + +} + +TopologyTree::~TopologyTree() +{ + +} + +void TopologyTree::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + QModelIndex index = indexAt(event->pos()); + if (index.isValid()) { + // 创建QMimeData,并将选中项的文本放入 + QMimeData *mimeData = new QMimeData(); + mimeData->setText(model()->data(index, Qt::DisplayRole).toString()); + QString id = index.data(Qt::UserRole).toString(); + mimeData->setData("application/id",id.toLocal8Bit()); + + // 创建拖拽对象 + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + + // 设置拖拽的图标(这里用一个简单的图标,也可以使用其他方式) + QPixmap pixmap(100, 30); + pixmap.fill(Qt::white); + QPainter painter(&pixmap); + painter.drawText(0, 20, model()->data(index, Qt::DisplayRole).toString()); + drag->setPixmap(pixmap); + drag->setHotSpot(event->pos() - rect().topLeft()); + + // 启动拖拽操作 + drag->exec(Qt::CopyAction); + } + } +} diff --git a/source/topologyView.cpp b/source/topologyView.cpp new file mode 100644 index 0000000..0bc145e --- /dev/null +++ b/source/topologyView.cpp @@ -0,0 +1,659 @@ +#include "topologyView.h" +#include "ui_topologyView.h" +#include "tools.h" +#include "topologyTree.h" +#include "dataBase.h" +#include "basePropertyManager.h" +#include "baseProperty.h" +#include "structDataSource.h" +#include "extraPropertyManager.h" +#include +#include +#include + +TopologyView::TopologyView(QWidget *parent) + : QDialog(parent) + , ui(new Ui::topologyView) + ,_treeModel(nullptr) + ,_treeView(nullptr) + ,_pExtraProManager(nullptr) +{ + ui->setupUi(this); + //setWindowFlags(windowFlags() | Qt::WindowMinMaxButtonsHint&Qt::WindowCloseButtonHint); + _treeModel = new QStandardItemModel(this); + _treeView = new TopologyTree(this); + ui->verticalLayout->addWidget(_treeView); + _treeView->setContextMenuPolicy(Qt::CustomContextMenu); + +} + +TopologyView::~TopologyView() +{ + delete ui; +} + +void TopologyView::initial() +{ + connect(_treeView, &QTreeView::clicked, this, &TopologyView::onItemClicked); + connect(_treeModel, &QStandardItemModel::itemChanged, this, &TopologyView::onItemChanged); + + m_dataSource = new StructDataSource(this); + _pExtraProManager = new ExtraPropertyManager(this); + _pExtraProManager->initial(); + + if(_pExtraProManager) + m_dataSource->loadExtrapro(_pExtraProManager->geAlltProperty()); + + _treeView->setHeaderHidden(true); + // 设置模型的列数 + _treeModel->setColumnCount(1); + + // 创建树视图 + _treeView->setModel(_treeModel); + + // 展开所有节点 + _treeView->expandAll(); + // 显示树视图 + _treeView->setWindowTitle(QObject::tr("拓扑树")); + + //loadTopologyFromDB(); +} + +void TopologyView::loadTopologyFromDB() +{ + if(_pExtraProManager) + { + //clearItems(); + auto& mapExtra = m_dataSource->allProperties; + /*QStandardItem* root = _treeModel->invisibleRootItem(); + for(auto& pro:mapExtra){ + QStandardItem* propertyItem = new QStandardItem(); + addItemToView(pro,"name",root,propertyItem); + }*/ + QVector vec = QVector::fromList(mapExtra.values()); + buildTreeStructure(_treeModel,vec); + _treeView->expandAll(); + } +} + +void TopologyView::onItemChanged(QStandardItem *item) +{ + int nLevel = getLevel(item); + QString str = item->text(); + EntityInfo info; + info.sName = str; + info.sUuid = item->data(Qt::UserRole+1).toString(); + info.eType = EntityType(item->data(Qt::UserRole+2).toInt()); + emit entityChange(info); +} + +void TopologyView::onItemClicked(const QModelIndex &index) +{ + QStandardItem* item = _treeModel->itemFromIndex(index); + if(item) + { + EntityType type = EntityType(item->data(Qt::UserRole+2).toInt()); + if(type == EntityType::ConfigurationDiagram) + { + EntityInfo info; + info.eType = EntityType::ConfigurationDiagram; + info.sName = item->text(); + info.sUuid = item->data(Qt::UserRole+1).toString(); + info.sParentId = item->parent()->data(Qt::UserRole+1).toString(); + emit entitySelected(info); + } + } +} + +void TopologyView::onUpdateTopology(QList lst, bool refresh, bool bFull) +{ + if(refresh){ + QStandardItem *root = _treeModel->invisibleRootItem(); + int rowCount = root->rowCount(); + if (rowCount > 0) { + _treeModel->removeRows(0, rowCount); + } + // 清空所有缓存 + m_mapGrids.clear(); + m_mapZones.clear(); + m_mapStations.clear(); + m_mapVoltageLevels.clear(); + m_mapBayItems.clear(); + } + + if(bFull){ + // 完整层级结构:grid -> zone -> station -> voltage -> bay -> device + onUpdateTopologyFull(lst, refresh); + } else { + // 简化层级结构:voltage -> bay -> device(现有逻辑) + onUpdateTopologySimple(lst, refresh); + } + + // 展开所有节点 + _treeView->expandAll(); +} + +void TopologyView::onMonitorUpdate(QList lst) +{ + clearItems(); + auto& mapExtra = m_dataSource->allProperties; + QVector vec = QVector::fromList(mapExtra.values()); + + QVector filteredVec; + for (const auto& property : vec) { + if (lst.contains(property.component_uuid)) { + filteredVec.append(property); + } + } + + buildTreeStructure(_treeModel,filteredVec); + _treeView->expandAll(); +} + +void TopologyView::onUpdateTopologyFull(QList lst, bool refresh) +{ + // 第一阶段:处理间隔节点(nCategory == 1) + for(auto &info:lst){ + auto curItem = info.item; + if(curItem.nCategory == 1){ // 间隔信息 + // 获取各级参数 + QString grid = curItem.grid; + QString zone = curItem.zone; + QString station = curItem.station; + QString voltageLevel = curItem.sVoltageLevel; + + // 设置默认值 + if(grid.isEmpty()) grid = "未指定电网"; + if(zone.isEmpty()) zone = "未指定区域"; + if(station.isEmpty()) station = "未指定变电站"; + if(voltageLevel.isEmpty()) voltageLevel = "未知电压等级"; + + // 查找或创建电网节点 + QStandardItem* pGridItem = findOrCreateTopLevelItem(grid, 0, m_mapGrids); + + // 查找或创建区域节点 + QString zoneKey = QString("%1|%2").arg(grid).arg(zone); + QStandardItem* pZoneItem = findOrCreateChildItem(pGridItem, zone, zoneKey, 1, m_mapZones); + + // 查找或创建变电站节点 + QString stationKey = QString("%1|%2|%3").arg(grid).arg(zone).arg(station); + QStandardItem* pStationItem = findOrCreateChildItem(pZoneItem, station, stationKey, 2, m_mapStations); + + // 查找或创建电压层级节点 + QString voltageKey = QString("%1|%2|%3|%4").arg(grid).arg(zone).arg(station).arg(voltageLevel); + QStandardItem* pVoltageItem = findOrCreateChildItem(pStationItem, voltageLevel, voltageKey, 3, m_mapVoltageLevels); + + // 创建间隔节点 + QString bayKey = QString("%1|%2|%3|%4|%5").arg(grid).arg(zone).arg(station).arg(voltageLevel).arg(curItem.sName); + QStandardItem* pBayItem = createBayItem(bayKey, curItem); + + pVoltageItem->appendRow(pBayItem); + } + } + + // 第二阶段:处理设备节点(nCategory == 0) + for(auto &info:lst){ + auto curItem = info.item; + if(curItem.nCategory == 0){ // 设备信息 + // 获取父间隔的参数 + QString grid = info.parent.grid; + QString zone = info.parent.zone; + QString station = info.parent.station; + QString voltageLevel = info.parent.sVoltageLevel; + QString bayName = info.parent.sName; + + if(!grid.isEmpty() && !zone.isEmpty() && !station.isEmpty() && + !voltageLevel.isEmpty() && !bayName.isEmpty()){ + + // 构建间隔节点的缓存key + QString bayKey = QString("%1|%2|%3|%4|%5").arg(grid).arg(zone).arg(station).arg(voltageLevel).arg(bayName); + + // 查找对应的间隔节点 + QStandardItem* pBayItem = nullptr; + if(m_mapBayItems.contains(bayKey)){ + pBayItem = m_mapBayItems[bayKey]; + } else { + // 如果没有在缓存中,需要从树中查找 + pBayItem = findBayItemInFullTree(grid, zone, station, voltageLevel, bayName); + } + + if(pBayItem){ + // 创建设备节点 + createDeviceItem(pBayItem, curItem); + } + } + } + } +} + +void TopologyView::onUpdateTopologySimple(QList lst, bool refresh) +{ + // 现有逻辑,只显示 voltage -> bay -> device + // 第一阶段:处理间隔节点(nCategory == 1) + for(auto &info:lst){ + auto curItem = info.item; + if(curItem.nCategory == 1){ // 间隔信息 + // 获取间隔的电压层级 + QString voltageLevel = curItem.sVoltageLevel; + if(voltageLevel.isEmpty()){ + voltageLevel = "未知电压等级"; // 默认值 + } + + // 查找或创建电压层级节点 + QStandardItem* pVoltageItem = findOrCreateVoltageLevel(voltageLevel); + + // 创建间隔节点 + QStandardItem* pBayItem = createBayItem(voltageLevel, curItem); + + pVoltageItem->appendRow(pBayItem); + } + } + + // 第二阶段:处理设备节点(nCategory == 0) + for(auto &info:lst){ + auto curItem = info.item; + if(curItem.nCategory == 0){ // 设备信息 + // 获取设备所属的间隔名称和电压层级 + QString bayName = info.parent.sName; + QString voltageLevel = info.parent.sVoltageLevel; // 从父间隔获取电压层级 + + if(!bayName.isEmpty() && !voltageLevel.isEmpty()){ + // 查找对应的间隔节点 + QStandardItem* pBayItem = findBayItem(voltageLevel, bayName); + + if(pBayItem){ + // 创建设备节点 + createDeviceItem(pBayItem, curItem); + } + } + } + } +} + +QStandardItem* TopologyView::findOrCreateTopLevelItem(const QString& name, int level, QHash& cache) +{ + if(cache.contains(name)){ + return cache[name]; + } + + QStandardItem* pItem = new QStandardItem(name); + pItem->setData(level, Qt::UserRole+1); // 存储层级 + pItem->setData(-1, Qt::UserRole+2); // 特殊标识 + pItem->setData(name, Qt::UserRole+3); // 存储名称 + + cache[name] = pItem; + _treeModel->appendRow(pItem); + + return pItem; +} + +QStandardItem* TopologyView::findOrCreateChildItem(QStandardItem* pParent, const QString& name, + const QString& cacheKey, int level, + QHash& cache) +{ + if(cache.contains(cacheKey)){ + return cache[cacheKey]; + } + + // 在父节点下查找 + for(int i = 0; i < pParent->rowCount(); ++i){ + QStandardItem* pChild = pParent->child(i); + if(pChild->text() == name){ + cache[cacheKey] = pChild; + return pChild; + } + } + + // 创建新节点 + QStandardItem* pItem = new QStandardItem(name); + pItem->setData(level, Qt::UserRole+1); + pItem->setData(-1, Qt::UserRole+2); + pItem->setData(cacheKey, Qt::UserRole+3); + + cache[cacheKey] = pItem; + pParent->appendRow(pItem); + + return pItem; +} + +QStandardItem* TopologyView::findBayItemInFullTree(const QString& grid, const QString& zone, + const QString& station, const QString& voltageLevel, + const QString& bayName) +{ + // 构建完整的缓存key + QString bayKey = QString("%1|%2|%3|%4|%5").arg(grid).arg(zone).arg(station).arg(voltageLevel).arg(bayName); + + // 如果缓存中有,直接返回 + if(m_mapBayItems.contains(bayKey)){ + return m_mapBayItems[bayKey]; + } + + // 遍历树查找 + for(int i = 0; i < _treeModel->rowCount(); ++i){ + QStandardItem* pGridItem = _treeModel->item(i); + if(pGridItem->text() != grid) continue; + + for(int j = 0; j < pGridItem->rowCount(); ++j){ + QStandardItem* pZoneItem = pGridItem->child(j); + if(pZoneItem->text() != zone) continue; + + for(int k = 0; k < pZoneItem->rowCount(); ++k){ + QStandardItem* pStationItem = pZoneItem->child(k); + if(pStationItem->text() != station) continue; + + for(int l = 0; l < pStationItem->rowCount(); ++l){ + QStandardItem* pVoltageItem = pStationItem->child(l); + if(pVoltageItem->text() != voltageLevel) continue; + + for(int m = 0; m < pVoltageItem->rowCount(); ++m){ + QStandardItem* pBayItem = pVoltageItem->child(m); + if(pBayItem->text() == bayName && + pBayItem->data(Qt::UserRole+1).toInt() == 4){ // 间隔层级 + m_mapBayItems[bayKey] = pBayItem; // 加入缓存 + return pBayItem; + } + } + } + } + } + } + + return nullptr; +} + +void TopologyView::clearItems() +{ + if(_treeModel){ + QStandardItem *root = _treeModel->invisibleRootItem(); //先清空model + int rowCount = root->rowCount(); + if (rowCount > 0) { + _treeModel->removeRows(0, rowCount); + } + } +} + +QString TopologyView::getLevelType(int index) { + switch (index) { + case 0: return "电网"; + case 1: return "区域"; + case 2: return "站点"; + case 3: return "电压等级"; + case 4: return "间隔"; + case 5: return "设备"; + case 6: return "分组"; + default: return "未知层级"; + } +} + +void TopologyView::buildTreeStructure(QStandardItemModel* model, + const QVector& properties) { + // 清空模型 + model->clear(); + + // 创建根节点 + //QStandardItem* root = new QStandardItem("拓扑结构"); + //model->appendRow(root); + QStandardItem* root = _treeModel->invisibleRootItem(); + // 存储节点映射:路径 -> 节点 + QMap nodeMap; + + for (const auto& property : properties) { + // 判断是否有设备信息 + bool hasDevice = !property.component_uuid.isNull() && !property.component_name.isEmpty(); + + // 检查间隔信息(根据业务逻辑,间隔应该总是存在) + QString bayDisplayName = property.bay_name.isEmpty() ? + (property.bay_tag.isEmpty() ? "未命名间隔" : property.bay_tag) : + property.bay_name; + + // 构建完整的节点路径 + QStringList pathComponents; + + // 基本层级:电网、区域、站点、电压等级、间隔 + pathComponents << property.grid_name; + pathComponents << property.zone_name; + pathComponents << property.station_name; + pathComponents << property.currentLevel; + pathComponents << bayDisplayName; + + // 如果有设备信息,添加设备层级 + if (hasDevice) { + QString deviceDisplayName = property.component_name.isEmpty() ? + property.component_uuid.toString() : + property.component_name; + pathComponents << deviceDisplayName; + } + + // 构建路径字符串作为唯一标识 + QString pathKey = pathComponents.join("|"); + + // 查找或创建节点路径 + QStandardItem* currentNode = root; + QString currentPath = ""; + + for (int i = 0; i < pathComponents.size(); i++) { + currentPath += (i > 0 ? "|" : "") + pathComponents[i]; + + // 查找是否已存在该节点 + QStandardItem* existingNode = nodeMap.value(currentPath); + + if (!existingNode) { + // 在当前父节点下查找 + for (int row = 0; row < currentNode->rowCount(); row++) { + QStandardItem* child = currentNode->child(row, 0); + if (child && child->text() == pathComponents[i]) { + QVariantMap childData = child->data(Qt::UserRole + 1).toMap(); + QString childPath = childData.value("fullPath").toString(); + if (childPath == currentPath) { + existingNode = child; + nodeMap[currentPath] = child; + break; + } + } + } + } + + if (existingNode) { + currentNode = existingNode; + } else { + // 创建新节点 + QStandardItem* newNode = new QStandardItem(pathComponents[i]); + + // 确定节点类型和层级信息 + QVariantMap nodeData; + nodeData["fullPath"] = currentPath; + nodeData["level"] = i; + + // 根据层级设置节点类型 + QString nodeType; + QString levelType; + + switch (i) { + case 0: nodeType = "grid"; levelType = "电网"; break; + case 1: nodeType = "zone"; levelType = "区域"; break; + case 2: nodeType = "station"; levelType = "站点"; break; + case 3: nodeType = "voltage"; levelType = "电压等级"; break; + case 4: nodeType = "bay"; levelType = "间隔"; break; + case 5: + if (hasDevice) { + nodeType = "device"; + levelType = "设备"; + // 创建设备节点时初始化属性列表 + nodeData["properties"] = QVariant::fromValue(QVector()); + nodeData["propertyCount"] = 0; + nodeData["deviceId"] = property.component_uuid.toString(); + nodeData["deviceName"] = property.component_name; + //nodeData["deviceType"] = property.device_type; + } + break; + } + + // 如果是间隔节点(第4层),也初始化属性列表(用于无设备的情况) + if (i == 4) { + nodeData["properties"] = QVariant::fromValue(QVector()); + nodeData["propertyCount"] = 0; + nodeData["bayName"] = property.bay_name; + nodeData["bayTag"] = property.bay_tag; + } + + nodeData["nodeType"] = nodeType; + nodeData["levelType"] = levelType; + + newNode->setData(nodeData, Qt::UserRole + 1); + currentNode->appendRow(newNode); + currentNode = newNode; + nodeMap[currentPath] = newNode; + } + } + + // 将属性添加到合适的节点 + // 如果有设备,添加到设备节点;否则添加到间隔节点 + QVariantMap targetNodeData = currentNode->data(Qt::UserRole + 1).toMap(); + QVector nodeProperties = targetNodeData["properties"].value>(); + + // 检查属性是否已存在 + bool propertyExists = false; + for (const auto& existingProp : nodeProperties) { + if (existingProp.code == property.code) { + propertyExists = true; + break; + } + } + + if (!propertyExists) { + nodeProperties.append(property); + targetNodeData["properties"] = QVariant::fromValue(nodeProperties); + + // 更新属性数量 + int propertyCount = targetNodeData["propertyCount"].toInt() + 1; + targetNodeData["propertyCount"] = propertyCount; + + // 更新节点显示文本 + QString currentText = currentNode->text(); + // 移除可能存在的数量后缀 + currentText = currentText.replace(QRegularExpression("\\(\\d+\\)$"), "").trimmed(); + currentNode->setText(QString("%1 (%2)").arg(currentText).arg(propertyCount)); + + // 更新工具提示 + QString tooltip = QString("%1: %2").arg(targetNodeData["levelType"].toString()).arg(currentText); + + if (propertyCount > 0) { + tooltip += QString("\n属性数量: %1").arg(propertyCount); + } + + // 添加设备类型信息(如果是设备节点) + if (targetNodeData["nodeType"].toString() == "device") { + tooltip += QString("\n设备类型: %1").arg(targetNodeData["deviceType"].toString()); + } + + currentNode->setToolTip(tooltip); + + // 保存更新后的数据 + currentNode->setData(targetNodeData, Qt::UserRole + 1); + } + } +} + +// 简化的获取属性列表函数 +QVector TopologyView::getPropertiesForNode(QStandardItem* node) { + QVariantMap nodeData = node->data(Qt::UserRole + 1).toMap(); + QString nodeType = nodeData.value("nodeType").toString(); + + if (nodeType == "device" || nodeType == "bay") { + return nodeData["properties"].value>(); + } + + return QVector(); +} + +QString TopologyView::getNodeInfo(QStandardItem* node) { + QVariantMap nodeData = node->data(Qt::UserRole + 1).toMap(); + QString nodeType = nodeData.value("nodeType").toString(); + QString levelType = nodeData.value("levelType").toString(); + QString name = node->text().replace(QRegularExpression("\\(\\d+\\)$"), "").trimmed(); + int propertyCount = nodeData.value("propertyCount", 0).toInt(); + + if (nodeType == "device") { + return QString("%1: %2 (设备类型: %3, 属性数: %4)") + .arg(levelType) + .arg(name) + .arg(nodeData.value("deviceType").toString()) + .arg(propertyCount); + } else if (nodeType == "bay") { + return QString("%1: %2 (属性数: %3)") + .arg(levelType) + .arg(name) + .arg(propertyCount); + } + + return QString("%1: %2").arg(levelType).arg(name); +} + +// 查找或创建电压层级节点 +QStandardItem* TopologyView::findOrCreateVoltageLevel(const QString& voltageLevel) +{ + if(m_mapVoltageLevels.contains(voltageLevel)){ + return m_mapVoltageLevels[voltageLevel]; + } + + QStandardItem* pVoltageItem = new QStandardItem(voltageLevel); + pVoltageItem->setData(-1, Qt::UserRole+1); // 特殊标识,表示电压层级 + pVoltageItem->setData(-1, Qt::UserRole+2); + pVoltageItem->setData(QString(), Qt::UserRole+3); + + m_mapVoltageLevels[voltageLevel] = pVoltageItem; + _treeModel->appendRow(pVoltageItem); + + return pVoltageItem; +} + +// 创建间隔节点 +QStandardItem* TopologyView::createBayItem(const QString& cacheKey, const HierarchyStructItem& bayInfo) +{ + QStandardItem* pBayItem = new QStandardItem(bayInfo.sName); + pBayItem->setData(bayInfo.nCategory, Qt::UserRole+1); + pBayItem->setData(bayInfo.nEquipType, Qt::UserRole+2); + pBayItem->setData(bayInfo.uid, Qt::UserRole+3); + pBayItem->setData(cacheKey, Qt::UserRole+4); // 存储完整路径 + + m_mapBayItems[cacheKey] = pBayItem; + + return pBayItem; +} + +// 在电压层级下查找间隔节点 +QStandardItem* TopologyView::findBayItem(const QString& voltageLevel, const QString& bayName) +{ + QString bayKey = QString("%1|%2").arg(voltageLevel).arg(bayName); + + // 先从缓存查找 + if(m_mapBayItems.contains(bayKey)){ + return m_mapBayItems[bayKey]; + } + + // 在对应电压层级下查找 + if(m_mapVoltageLevels.contains(voltageLevel)){ + QStandardItem* pVoltageItem = m_mapVoltageLevels[voltageLevel]; + for(int i = 0; i < pVoltageItem->rowCount(); ++i){ + QStandardItem* pItem = pVoltageItem->child(i); + if(pItem->text() == bayName && + pItem->data(Qt::UserRole+1).toInt() == 1){ + m_mapBayItems[bayKey] = pItem; // 加入缓存 + return pItem; + } + } + } + return nullptr; +} + +// 创建设备节点 +void TopologyView::createDeviceItem(QStandardItem* pParent, const HierarchyStructItem& deviceInfo) +{ + QStandardItem* pItem = new QStandardItem(deviceInfo.sName); + pItem->setData(deviceInfo.nCategory, Qt::UserRole+1); + pItem->setData(deviceInfo.nEquipType, Qt::UserRole+2); + pItem->setData(deviceInfo.uid, Qt::UserRole+3); + + pParent->appendRow(pItem); +} + diff --git a/ui/diagramView.ui b/ui/diagramView.ui new file mode 100644 index 0000000..f2ea6fd --- /dev/null +++ b/ui/diagramView.ui @@ -0,0 +1,143 @@ + + + diagramView + + + + 0 + 0 + 266 + 455 + + + + Dialog + + + + + + + + + 40 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 新建 + + + + + + + + 40 + 20 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 保存 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 12 + + + + 搜索 + + + + + + + QLineEdit.underline { + border: none; + border-bottom: 2px solid #4CAF50; + padding: 5px 0; + background: transparent; +} + +QLineEdit.underline:focus { + border-bottom: 2px solid #FF9800; +} + + + + + + + + + + + + + diff --git a/ui/drawingPanel.ui b/ui/drawingPanel.ui new file mode 100644 index 0000000..d70447e --- /dev/null +++ b/ui/drawingPanel.ui @@ -0,0 +1,41 @@ + + + drawingPanel + + + + 0 + 0 + 801 + 501 + + + + Form + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + diff --git a/ui/graphicElementsPanel.ui b/ui/graphicElementsPanel.ui new file mode 100644 index 0000000..d18d984 --- /dev/null +++ b/ui/graphicElementsPanel.ui @@ -0,0 +1,80 @@ + + + graphicElementsPanel + + + + 0 + 0 + 167 + 721 + + + + Form + + + + + + QTabWidget::South + + + 0 + + + + 基本图元 + + + + + 30 + 20 + 81 + 51 + + + + 直角矩形 + + + + + + 30 + 100 + 81 + 51 + + + + 圆角矩形 + + + + + + 30 + 180 + 81 + 51 + + + + 多边形 + + + + + + 电力图元 + + + + + + + + + diff --git a/ui/itemPropertyDlg.ui b/ui/itemPropertyDlg.ui new file mode 100644 index 0000000..3636d6d --- /dev/null +++ b/ui/itemPropertyDlg.ui @@ -0,0 +1,331 @@ + + + itemPropertyDlg + + + + 0 + 0 + 642 + 552 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + background-color: rgb(183, 183, 183); + + + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + + + + + + + + 12 + + + + 间隔信息 + + + + + + + Qt::Orientation::Vertical + + + + 20 + 471 + + + + + + + + + + + + + + Microsoft YaHei UI + 10 + false + false + + + + background-color: rgb(224, 224, 224); +color:black; +font: 10pt "Microsoft YaHei UI"; + + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + 间隔信息 + + + + + + 是否锚定电流 + + + + + + + Qt::Orientation::Vertical + + + + + + + 90.000000000000000 + + + + + + + 电压下限 + + + + + + + Qt::Orientation::Vertical + + + + + + + 电流下限 + + + + + + + 200.000000000000000 + + + 110.000000000000000 + + + + + + + 10000.000000000000000 + + + 2.000000000000000 + + + + + + + 是否锚定电压 + + + + + + + 电流上限 + + + + + + + 45.000000000000000 + + + + + + + 200.000000000000000 + + + 55.000000000000000 + + + + + + + 电压上限 + + + + + + + 电阻 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 354 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + + 223 + 20 + + + + + + + + 取消 + + + + + + + 应用 + + + + + + + 确定 + + + + + + + + + + + + + + + + + + + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui new file mode 100644 index 0000000..a9f0a91 --- /dev/null +++ b/ui/mainwindow.ui @@ -0,0 +1,330 @@ + + + CMainWindow + + + + 0 + 0 + 1696 + 1079 + + + + DiagramDesigner + + + QMenuBar#menubar { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2c5282, /* 顶部颜色 */ + stop:1 #1a365d); /* 底部颜色 */ + color: white; + border: none; + padding: 4px 0; + } + + QMenuBar#menubar::item { + padding: 8px 16px; + border: 1px solid transparent; + } + + QMenuBar#menubar::item:hover { + background-color: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + } + + QMenuBar#menubar::item:pressed { + background-color: rgba(255, 255, 255, 0.2); + } +QMenu#menubar { + background-color: white; + border: 1px solid #cbd5e1; + color: #1e293b; + padding: 4px; + } + + QMenu#menubar::item { + padding: 6px 30px 6px 20px; + border-radius: 2px; + } + + QMenu#menubar::item:selected { + background-color: #2563eb; + color: white; + } + + QMenu#menubar::separator { + height: 1px; + background-color: #e2e8f0; + margin: 4px 8px; + } + + + + + + 0 + 0 + 1696 + 38 + + + + + 文件(F) + + + + + 视图(V) + + + + + 模式 + + + + + 工程模 + + + + + 测试 + + + + + 设置 + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + + + + 新建 + + + 新建(N) + + + QAction::MenuRole::NoRole + + + + + + + + 打开 + + + 打开(O) + + + QAction::MenuRole::NoRole + + + + + + + + 保存 + + + 保存(S) + + + QAction::MenuRole::NoRole + + + + + + + + 复制 + + + 复制(C) + + + QAction::MenuRole::NoRole + + + + + + + + 剪切 + + + 剪切(T) + + + QAction::MenuRole::NoRole + + + + + + + + 粘贴 + + + 粘贴(P) + + + QAction::MenuRole::NoRole + + + + + + + + 删除 + + + 删除(D) + + + QAction::MenuRole::NoRole + + + + + + + + 放大 + + + 放大 + + + QAction::MenuRole::NoRole + + + + + + + + 缩小 + + + 缩小 + + + QAction::MenuRole::NoRole + + + + + + + + 自适应 + + + 自适应 + + + QAction::MenuRole::NoRole + + + + + 网格 + + + 网格 + + + QAction::MenuRole::NoRole + + + + + + + + 撤销 + + + 撤销 + + + + + + + + 重做 + + + 重做 + + + QAction::MenuRole::NoRole + + + + + 群组 + + + 群组 + + + QAction::MenuRole::NoRole + + + + + 解组 + + + 解组 + + + QAction::MenuRole::NoRole + + + + + 运行 + + + + + + diff --git a/ui/monitorItemsDlg.ui b/ui/monitorItemsDlg.ui new file mode 100644 index 0000000..799792f --- /dev/null +++ b/ui/monitorItemsDlg.ui @@ -0,0 +1,108 @@ + + + monitorItemsDlg + + + + 0 + 0 + 182 + 446 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 12 + + + + 0 + + + + + + + + + + + 80 + 20 + + + + + 80 + 16777215 + + + + QPushButton { + background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ + color: white; + border: none; /* 无边框,扁平化 */ + border-radius: 4px; /* 小圆角,扁平感 */ + font-size: 13px; /* 适中字号 */ + font-weight: 500; /* 中等字重 */ +} + +QPushButton:hover { + background-color: #4a5d7e; /* 稍深的灰蓝 */ +} + +QPushButton:pressed { + background-color: #3d4e6b; /* 更深的灰蓝 */ +} + +QPushButton:disabled { + background-color: #a0b3d1; /* 灰调的浅蓝 */ + color: #d1dce9; /* 浅灰色文字 */ +} + + + + 生成监控 + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/monitorPagesDlg.ui b/ui/monitorPagesDlg.ui new file mode 100644 index 0000000..a20caef --- /dev/null +++ b/ui/monitorPagesDlg.ui @@ -0,0 +1,24 @@ + + + monitorPagesDlg + + + + 0 + 0 + 182 + 427 + + + + Dialog + + + + + + + + + + diff --git a/ui/renameModel.ui b/ui/renameModel.ui new file mode 100644 index 0000000..be86ca7 --- /dev/null +++ b/ui/renameModel.ui @@ -0,0 +1,177 @@ + + + renameModel + + + + 0 + 0 + 319 + 129 + + + + + 12 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + + 16777215 + 20 + + + + + 12 + + + + background-color: rgb(205, 205, 205); +color: rgb(10, 10, 10); + + + 重命名 + + + + + + + Qt::Orientation::Horizontal + + + + 64 + 20 + + + + + + + + + + 名称: + + + + + + + + + + 提示: + + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 63 + 20 + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 确定 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 取消 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/ui/topologyView.ui b/ui/topologyView.ui new file mode 100644 index 0000000..8e275a0 --- /dev/null +++ b/ui/topologyView.ui @@ -0,0 +1,26 @@ + + + topologyView + + + + 0 + 0 + 182 + 442 + + + + + 0 + 0 + + + + Dialog + + + + + +