commit 07022c71efacf5d261a733c3b7ec8467d1993380 Author: baiYue Date: Mon Mar 23 11:06:17 2026 +0800 exchange commit 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 0000000..5d98b3c Binary files /dev/null and b/PropertyEditor/resources/Icon/arrow-left-l.png differ diff --git a/PropertyEditor/resources/Icon/arrow-right-l.png b/PropertyEditor/resources/Icon/arrow-right-l.png new file mode 100644 index 0000000..27282e2 Binary files /dev/null and b/PropertyEditor/resources/Icon/arrow-right-l.png differ diff --git a/PropertyEditor/resources/Icon/close.png b/PropertyEditor/resources/Icon/close.png new file mode 100644 index 0000000..d7ff2d7 Binary files /dev/null and b/PropertyEditor/resources/Icon/close.png differ diff --git a/PropertyEditor/resources/Icon/delete.png b/PropertyEditor/resources/Icon/delete.png new file mode 100644 index 0000000..6a7c8a1 Binary files /dev/null and b/PropertyEditor/resources/Icon/delete.png differ diff --git a/PropertyEditor/resources/Icon/down.png b/PropertyEditor/resources/Icon/down.png new file mode 100644 index 0000000..76df783 Binary files /dev/null and b/PropertyEditor/resources/Icon/down.png differ diff --git a/PropertyEditor/resources/Icon/expand.png b/PropertyEditor/resources/Icon/expand.png new file mode 100644 index 0000000..a1dbcf7 Binary files /dev/null and b/PropertyEditor/resources/Icon/expand.png differ diff --git a/PropertyEditor/resources/Icon/plus.png b/PropertyEditor/resources/Icon/plus.png new file mode 100644 index 0000000..cea9ef0 Binary files /dev/null and b/PropertyEditor/resources/Icon/plus.png differ diff --git a/PropertyEditor/resources/Icon/reset.png b/PropertyEditor/resources/Icon/reset.png new file mode 100644 index 0000000..c49badc Binary files /dev/null and b/PropertyEditor/resources/Icon/reset.png differ diff --git a/PropertyEditor/resources/Icon/unexpand.png b/PropertyEditor/resources/Icon/unexpand.png new file mode 100644 index 0000000..18ea16e Binary files /dev/null and b/PropertyEditor/resources/Icon/unexpand.png differ diff --git a/PropertyEditor/resources/Icon/up.png b/PropertyEditor/resources/Icon/up.png new file mode 100644 index 0000000..ff302d5 Binary files /dev/null and b/PropertyEditor/resources/Icon/up.png differ 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 0000000..8c95aa1 Binary files /dev/null and b/PropertyEditor/resources/image-20250826114654194.png differ 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 0000000..4669855 Binary files /dev/null and b/resource/images/checkerboard.png differ diff --git a/resource/images/element/icon_image.png b/resource/images/element/icon_image.png new file mode 100644 index 0000000..ef0a82e Binary files /dev/null and b/resource/images/element/icon_image.png differ diff --git a/resource/images/element/icon_text.png b/resource/images/element/icon_text.png new file mode 100644 index 0000000..43fec1f Binary files /dev/null and b/resource/images/element/icon_text.png differ diff --git a/resource/images/element/icons_triangle.png b/resource/images/element/icons_triangle.png new file mode 100644 index 0000000..d46ac16 Binary files /dev/null and b/resource/images/element/icons_triangle.png differ diff --git a/resource/images/element/svg_bus.svg b/resource/images/element/svg_bus.svg new file mode 100644 index 0000000..78240e5 --- /dev/null +++ b/resource/images/element/svg_bus.svg @@ -0,0 +1,7 @@ + + + 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 0000000..20a2bd1 Binary files /dev/null and b/resource/images/icon_down_arrow.png differ diff --git a/resource/images/icon_left_arrow.png b/resource/images/icon_left_arrow.png new file mode 100644 index 0000000..391592b Binary files /dev/null and b/resource/images/icon_left_arrow.png differ diff --git a/resource/images/icon_rotate.png b/resource/images/icon_rotate.png new file mode 100644 index 0000000..39c110a Binary files /dev/null and b/resource/images/icon_rotate.png differ diff --git a/resource/images/icon_rotate_lb.png b/resource/images/icon_rotate_lb.png new file mode 100644 index 0000000..4438b6c Binary files /dev/null and b/resource/images/icon_rotate_lb.png differ diff --git a/resource/images/icon_rotate_lt.png b/resource/images/icon_rotate_lt.png new file mode 100644 index 0000000..c4c15c1 Binary files /dev/null and b/resource/images/icon_rotate_lt.png differ diff --git a/resource/images/icon_rotate_rb.png b/resource/images/icon_rotate_rb.png new file mode 100644 index 0000000..6570833 Binary files /dev/null and b/resource/images/icon_rotate_rb.png differ diff --git a/resource/images/icon_rotate_rt.png b/resource/images/icon_rotate_rt.png new file mode 100644 index 0000000..2d3f985 Binary files /dev/null and b/resource/images/icon_rotate_rt.png differ diff --git a/resource/images/icon_up_arrow.png b/resource/images/icon_up_arrow.png new file mode 100644 index 0000000..d2e8b9a Binary files /dev/null and b/resource/images/icon_up_arrow.png differ diff --git a/setting.xml b/setting.xml new file mode 100644 index 0000000..908f465 --- /dev/null +++ b/setting.xml @@ -0,0 +1,12 @@ + + + + 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 + + + + + +