From 3afdc864d97bb725f7d98154645f39882a8fdb6f Mon Sep 17 00:00:00 2001 From: baiYue Date: Fri, 8 May 2026 18:03:54 +0800 Subject: [PATCH] fix editor topology setting --- diagramCavas/CMakeLists.txt | 1 + .../diagramEditorBayDetailAddDlg.h | 17 + .../diagramEditorBaySettingDlg.h | 2 + .../diagramEditorTransDetailAddDlg.h | 16 + .../diagramEditorTransSettingDlg.h | 2 + .../diagramEditor/diagramEditorWizard.h | 1 + .../diagramEditor/editorDiagramLayoutEngine.h | 78 +-- .../diagramEditor/editorDirectionManager.h | 28 +- .../diagramEditorBayDetailAddDlg.cpp | 277 ++++++++--- .../diagramEditorBaySettingDlg.cpp | 46 ++ .../diagramEditorTransDetailAddDlg.cpp | 336 +++++++++---- .../diagramEditorTransSettingDlg.cpp | 123 ++++- .../diagramEditor/diagramEditorWizard.cpp | 12 +- .../source/diagramEditor/editPanel.cpp | 8 + .../editorDiagramLayoutEngine.cpp | 447 ++++++++++++++++++ diagramCavas/ui/diagramEditorBaySettingDlg.ui | 67 ++- .../ui/diagramEditorTransSettingDlg.ui | 235 +++++---- 17 files changed, 1354 insertions(+), 342 deletions(-) create mode 100644 diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp diff --git a/diagramCavas/CMakeLists.txt b/diagramCavas/CMakeLists.txt index 04b0b25..514e28b 100644 --- a/diagramCavas/CMakeLists.txt +++ b/diagramCavas/CMakeLists.txt @@ -252,6 +252,7 @@ set(DIAGRAMCAVAS_SOURCE_FILES source/diagramEditor/layoutBuilder.cpp source/diagramEditor/transformerBuilder.cpp source/diagramEditor/layoutCalculator.cpp + source/diagramEditor/editorDiagramLayoutEngine.cpp source/graphicsDataModel/baseModel.cpp source/graphicsDataModel/fixedPortsModel.cpp source/graphicsDataModel/diagramEditorModel.cpp diff --git a/diagramCavas/include/diagramEditor/diagramEditorBayDetailAddDlg.h b/diagramCavas/include/diagramEditor/diagramEditorBayDetailAddDlg.h index c4c32f0..9f7d189 100644 --- a/diagramCavas/include/diagramEditor/diagramEditorBayDetailAddDlg.h +++ b/diagramCavas/include/diagramEditor/diagramEditorBayDetailAddDlg.h @@ -11,11 +11,22 @@ QT_END_NAMESPACE class DiagramEditorBayDetailSettingDlg; class QStandardItemModel; +class QComboBox; class DiagramEditorBayDetailAddDlg : public QDialog { Q_OBJECT public: + enum Column + { + Col_Category = 0, + Col_Name, + Col_Type, + Col_BindObj, + Col_Route, + Col_Count + }; + DiagramEditorBayDetailAddDlg(QWidget *parent = nullptr); ~DiagramEditorBayDetailAddDlg(); @@ -25,6 +36,11 @@ public: void setParent(DiagramEditorBayDetailSettingDlg* p) {_pParent = p;} private: void updateBindLst(); //刷新关联列表 + void loadFromModelRow(int row); //载入行数据到界面 + void setComboBoxByData(QComboBox *combo,int targetData); //按 data 设置 ComboBox 适用于:cb_category cb_type + void setBindObjComboBox(QComboBox *combo,int bindType,int bindPara,const QString &bindParent); //专用:设置 cb_bindObj(多 UserRole) + + void syncSelectedModel(const QString &deviceName); //按设备名同步当前线路(todo:其他线路) public slots: void onAddClicked(); void onDeleteClicked(); @@ -35,6 +51,7 @@ public slots: void onComponentDeleteClicked(); void onComponentRbtnClicked(const QPoint &pos); + void onComponentSelected(const QModelIndex &index); void onRouteDeleteClicked(); void onRouteRbtnClicked(const QPoint &pos); //线路右键菜单 private: diff --git a/diagramCavas/include/diagramEditor/diagramEditorBaySettingDlg.h b/diagramCavas/include/diagramEditor/diagramEditorBaySettingDlg.h index b0f9b69..5f4806f 100644 --- a/diagramCavas/include/diagramEditor/diagramEditorBaySettingDlg.h +++ b/diagramCavas/include/diagramEditor/diagramEditorBaySettingDlg.h @@ -10,6 +10,7 @@ QT_END_NAMESPACE class DiagramEditorWizard; class DiagramEditorBayBlock; +class QListWidgetItem; class DiagramEditorBaySettingDlg : public QDialog { @@ -29,6 +30,7 @@ public slots: void onOkClicked(); void onCancelClicked(); void onConnectLevelChanged(const QString&); //连接层级改变信号 + void onListItemClicked(QListWidgetItem *item); private: Ui::diagramEditorBaySettingDlg *ui; DiagramEditorWizard* _pWizard; diff --git a/diagramCavas/include/diagramEditor/diagramEditorTransDetailAddDlg.h b/diagramCavas/include/diagramEditor/diagramEditorTransDetailAddDlg.h index 09a320b..4a8b654 100644 --- a/diagramCavas/include/diagramEditor/diagramEditorTransDetailAddDlg.h +++ b/diagramCavas/include/diagramEditor/diagramEditorTransDetailAddDlg.h @@ -11,11 +11,22 @@ namespace Ui { class diagramEditorTransDetailAddDlg; } QT_END_NAMESPACE class DiagramEditorTransDetailSettingDlg; +class QComboBox; class DiagramEditorTransDetailAddDlg : public QDialog { Q_OBJECT public: + enum Column + { + Col_Category = 0, + Col_Name, + Col_Type, + Col_BindObj, + Col_Route, + Col_Count + }; + DiagramEditorTransDetailAddDlg(QWidget *parent = nullptr); ~DiagramEditorTransDetailAddDlg(); @@ -25,6 +36,11 @@ public: void setParent(DiagramEditorTransDetailSettingDlg* p) {_pParent = p;} private: void updateBindLst(); //刷新关联列表 + void loadFromModelRow(int row); //加载数据到界面 + void setComboByData(QComboBox *combo, int data); + void setBindObjCombo(QComboBox *combo,int bindType,const QString &bindParent); + + void syncSelectedModel(const QString &deviceName); //同步设置到当前线路 public slots: void onAddClicked(); void onDeleteClicked(); diff --git a/diagramCavas/include/diagramEditor/diagramEditorTransSettingDlg.h b/diagramCavas/include/diagramEditor/diagramEditorTransSettingDlg.h index d16d7fc..4401812 100644 --- a/diagramCavas/include/diagramEditor/diagramEditorTransSettingDlg.h +++ b/diagramCavas/include/diagramEditor/diagramEditorTransSettingDlg.h @@ -30,11 +30,13 @@ public slots: void onCancelClicked(); void onConnectLevelChanged(const QString&); //连接层级改变信号 void onBayTypeChanged(int n); + void onTableItemSelected(); private: Ui::diagramEditorTransSettingDlg *ui; DiagramEditorWizard* _pWizard; int _curModel; //0新增,1修改 DiagramEditorTransformerBlock* _curOperateBlock; //当前修改对象 + QList _prepareDisconnectBlock; //准备断开的连接间隔 }; #endif diff --git a/diagramCavas/include/diagramEditor/diagramEditorWizard.h b/diagramCavas/include/diagramEditor/diagramEditorWizard.h index 3ad8b31..f8d8a43 100644 --- a/diagramCavas/include/diagramEditor/diagramEditorWizard.h +++ b/diagramCavas/include/diagramEditor/diagramEditorWizard.h @@ -60,6 +60,7 @@ public: void flushTransPage(); //刷新变压器界面 int getContainerIndex(int nLevel,DiagramEditorStructContainer*); //返回目标层级container的序号 QUuid addConnection(const QString& str1,const QString& str2,int nType1,int nType2,int nPara = 0); //插入连接 + QUuid findConnection(const QString& str1,const QString& str2); //查找两个间隔的连接 QMap& getConnection() {return _mapConnect;} QList findConnectionByBlock(QString,int); //通过block找到连接 void removeConnection(QUuid); diff --git a/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h b/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h index 000e913..239a17c 100644 --- a/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h +++ b/diagramCavas/include/diagramEditor/editorDiagramLayoutEngine.h @@ -6,53 +6,61 @@ class DiagramLayoutEngine { public: - struct LayoutResult { - QRectF boundingRect; - bool success = true; - QString errorMessage; + struct Context { + int sourceId = 0; + bool saveToModel = false; + QMap itemCache; + QMap componentsCache; + + void initComponentsCache(const QMap& compos) { + componentsCache = compos; + } }; - LayoutResult layoutRoutes(QMap& routes, - QMap& components, - const LayoutConfig& config, - int nSource, - bool saveToModel); + QRectF executeLayout( + QMap& routes, + QMap& components, + const LayoutConfig& config, + Context& context + ); private: - // 主线布局 + // 主线相关 + QString findMainRoute(const QMap& routes); void layoutMainRoute(DiagramEditorRouteInfo& route, const LayoutConfig& config, - bool saveToModel, - int nSource); + Context& context); - // 支线布局 + // 支线相关 void layoutBranchRoute(DiagramEditorRouteInfo& route, const LayoutConfig& config, - bool saveToModel, - int nSource); - - // 更新元件信息 - void updateComponentInfo(DiagramEditorComponentInfo& compo, - Direction dir, - const QPoint& delta, - int rotate, - bool saveToModel, - int nSource); - - // 拆分支线 + Context& context); void splitBranchRoute(DiagramEditorRouteInfo& route, - bool saveToModel, - int nSource); + Context& context); + void layoutBranchSequence(QList& sequence, + Direction branchDir, + const LayoutConfig& config, + Context& context, + bool isOrder); - // 计算边界 + // 组件相关 + Direction determineBranchDirection(const DiagramEditorComponentInfo& currentNode, + Direction preferredDir, + Context& context); + QPoint getComponentPosition(const QString& componentName, + Context& context); + void updateComponent(DiagramEditorComponentInfo& compo, + Direction dir, + const QPoint& position, + int rotate, + Context& context); + + // 辅助函数 + QStandardItem* getNameItem(const QString& name, Context& context); + int getComponentDirection(const QString& compoName, Context& context); QRectF calculateBoundingRect(const QMap& components); - // 辅助方法 - QString findMainRoute(const QMap& routes); - QStandardItem* getNameItem(const QString& name, int nSource); - private: - QMap m_itemCache; - int m_compoWidth = 50; // 可配置 - int m_compoHeight = 30; // 可配置 + int m_compoWidth = 50; + int m_compoHeight = 30; }; diff --git a/diagramCavas/include/diagramEditor/editorDirectionManager.h b/diagramCavas/include/diagramEditor/editorDirectionManager.h index 0162eeb..6a4448f 100644 --- a/diagramCavas/include/diagramEditor/editorDirectionManager.h +++ b/diagramCavas/include/diagramEditor/editorDirectionManager.h @@ -22,14 +22,12 @@ public: } } - static int getRotationAngle(Direction dir) { - switch (dir) { - case Direction::Right: return -90; - case Direction::Left: return 90; - case Direction::Down: return 0; - case Direction::Up: return 180; - default: return 0; - } + static bool isDirectionOccupied(int usedFlags, Direction dir) { + return (usedFlags & static_cast(dir)) != 0; + } + + static int markDirectionOccupied(int usedFlags, Direction dir) { + return usedFlags | static_cast(dir); } static QPoint getIncrement(Direction dir, int hSpacing, int vSpacing) { @@ -40,11 +38,13 @@ public: return QPoint(0, 0); } - static bool isDirectionOccupied(int usedDirections, Direction dir) { - return (usedDirections & static_cast(dir)) != 0; - } - - static int markDirectionOccupied(int usedDirections, Direction dir) { - return usedDirections | static_cast(dir); + static int getRotationAngle(Direction dir) { + switch (dir) { + case Direction::Right: return -90; + case Direction::Left: return 90; + case Direction::Down: return 0; + case Direction::Up: return 180; + default: return 0; + } } }; diff --git a/diagramCavas/source/diagramEditor/diagramEditorBayDetailAddDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorBayDetailAddDlg.cpp index cfaea02..b5233bb 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorBayDetailAddDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorBayDetailAddDlg.cpp @@ -40,6 +40,7 @@ void DiagramEditorBayDetailAddDlg::initial() connect(ui->cb_category,&QComboBox::currentTextChanged,this,&DiagramEditorBayDetailAddDlg::onCategoryChanged); connect(ui->tableView_selected, &QTableView::customContextMenuRequested, this, &DiagramEditorBayDetailAddDlg::onRouteRbtnClicked); connect(ui->tableView_items, &QTableView::customContextMenuRequested, this, &DiagramEditorBayDetailAddDlg::onComponentRbtnClicked); + connect(ui->tableView_items, &QTableView::clicked, this, &DiagramEditorBayDetailAddDlg::onComponentSelected); QMap mapType = BaseTypeManager::getInstance()->getMapType(); //直接添加数据库中的基础类型 for(auto iter = mapType.begin();iter != mapType.end();++iter){ @@ -70,6 +71,134 @@ void DiagramEditorBayDetailAddDlg::initial() ui->cb_bindObj->setModel(_bindItemModel); } +void DiagramEditorBayDetailAddDlg::loadFromModelRow(int row) +{ + if (!_pParent) + return; + + auto pModel = _pParent->getComponentModel(); + if (!pModel || row < 0 || row >= pModel->rowCount()) + return; + + // ===== 1. 基础字段 ===== + ui->le_name->setText(pModel->item(row, Col_Name)->text()); + + // ===== 2. 分类 cb_category ===== + int cateData = pModel->item(row, Col_Category)->data().toInt(); + setComboBoxByData(ui->cb_category, cateData); + + // ===== 3. 类型 cb_type ===== + int typeData = pModel->item(row, Col_Type)->data().toInt(); + setComboBoxByData(ui->cb_type, typeData); + + // ===== 4. 绑定对象 cb_bindObj ===== + QStandardItem *objItem = pModel->item(row, Col_BindObj); + if (objItem) + { + int bindType = objItem->data(Qt::UserRole + 1).toInt(); + int bindPara = objItem->data(Qt::UserRole + 2).toInt(); + QString bindParent = objItem->data(Qt::UserRole + 3).toString(); + + setBindObjComboBox( + ui->cb_bindObj, + bindType, + bindPara, + bindParent + ); + } +} + +void DiagramEditorBayDetailAddDlg::setComboBoxByData( + QComboBox *combo, + int targetData) +{ + for (int i = 0; i < combo->count(); ++i) + { + if (combo->itemData(i).toInt() == targetData) + { + combo->setCurrentIndex(i); + return; + } + } + combo->setCurrentIndex(-1); +} + +void DiagramEditorBayDetailAddDlg::setBindObjComboBox( + QComboBox *combo, + int bindType, + int bindPara, + const QString &bindParent) +{ + for (int i = 0; i < combo->count(); ++i) + { + int type = combo->itemData(i, Qt::UserRole + 1).toInt(); + int para = combo->itemData(i, Qt::UserRole + 2).toInt(); + QString parent = combo->itemData(i, Qt::UserRole + 3).toString(); + + if (type == bindType && + para == bindPara && + parent == bindParent) + { + combo->setCurrentIndex(i); + return; + } + } + combo->setCurrentIndex(-1); +} + +void DiagramEditorBayDetailAddDlg::syncSelectedModel( + const QString &deviceName) +{ + if (!_pParent || !_selectedModel) + return; + + auto pMainModel = _pParent->getComponentModel(); + + // ===== 1. 找到主表中最新数据 ===== + QStandardItem *mainNameItem = nullptr; + int mainRow = -1; + + for (int i = 0; i < pMainModel->rowCount(); ++i) + { + QStandardItem *item = pMainModel->item(i, Col_Name); + if (item && item->text() == deviceName) + { + mainNameItem = item; + mainRow = i; + break; + } + } + + if (!mainNameItem) + return; + + // ===== 2. 同步 _selectedModel 中所有引用该设备的行 ===== + for (int i = 0; i < _selectedModel->rowCount(); ++i) + { + QStandardItem *selNameItem = _selectedModel->item(i, Col_Name); + if (!selNameItem) + continue; + + if (selNameItem->text() != deviceName) + continue; + + // ===== 同步字段 ===== + _selectedModel->item(i, Col_Category) + ->setText(pMainModel->item(mainRow, Col_Category)->text()); + + _selectedModel->item(i, Col_Type) + ->setText(pMainModel->item(mainRow, Col_Type)->text()); + + QStandardItem *mainObj = pMainModel->item(mainRow, Col_BindObj); + QStandardItem *selObj = _selectedModel->item(i, Col_BindObj); + + selObj->setText(mainObj->text()); + selObj->setData(mainObj->data(Qt::UserRole + 1), Qt::UserRole + 1); + selObj->setData(mainObj->data(Qt::UserRole + 2), Qt::UserRole + 2); + selObj->setData(mainObj->data(Qt::UserRole + 3), Qt::UserRole + 3); + } +} + void DiagramEditorBayDetailAddDlg::updateBindLst() { _bindItemModel->clear(); @@ -266,76 +395,90 @@ void DiagramEditorBayDetailAddDlg::onDeleteClicked() void DiagramEditorBayDetailAddDlg::onSaveClicked() { - QString sCategory = ui->cb_category->currentText(); - int nCate = ui->cb_category->currentData().toInt(); - QString sName = ui->le_name->text(); - QString sType = ui->cb_type->currentText(); - int nType = ui->cb_type->currentData().toInt(); - QString sBindObj = ui->cb_bindObj->currentText(); - int nBindType = ui->cb_bindObj->currentData(Qt::UserRole+1).toInt(); - int nBindPara = ui->cb_bindObj->currentData(Qt::UserRole+2).toInt(); - QString sBindParent = ui->cb_bindObj->currentData(Qt::UserRole+3).toString(); + // ===== 1. 读取 UI 数据 ===== + const QString sCategory = ui->cb_category->currentText(); + const int nCate = ui->cb_category->currentData().toInt(); - if(_pParent){ - auto pCompoModel = _pParent->getComponentModel(); - int rowCount = pCompoModel->rowCount(); - for(int i = 0;i < rowCount;++i){ - QStandardItem *item = pCompoModel->item(i, 1); - if(item->text() == sName){ - 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(); + const QString sName = ui->le_name->text(); + if (sName.isEmpty()) + return; - switch (ret) { - case QMessageBox::Ok: //覆盖 - { - QStandardItem *itemCate = pCompoModel->item(i, 0); - QStandardItem *itemType = pCompoModel->item(i, 2); - QStandardItem *itemObj = pCompoModel->item(i, 3); - QStandardItem *itemRoute = pCompoModel->item(i, 4); + const QString sType = ui->cb_type->currentText(); + const int nType = ui->cb_type->currentData().toInt(); - item->setData(0); //覆盖时初始化方向占用 - itemCate->setText(sCategory); - itemType->setText(sType); - itemType->setData(nType); - itemObj->setText(sBindObj); - return; - } - break; - case QMessageBox::Cancel: - // Cancel was clicked - return; - default: - // should never be reached - break; - } - } - } + const QString sBindObj = ui->cb_bindObj->currentText(); + const int nBindType = ui->cb_bindObj->currentData(Qt::UserRole + 1).toInt(); + const int nBindPara = ui->cb_bindObj->currentData(Qt::UserRole + 2).toInt(); + const QString sBindParent = ui->cb_bindObj->currentData(Qt::UserRole + 3).toString(); - QStandardItem *itemCate = new QStandardItem(); - QStandardItem *itemName = new QStandardItem(); - QStandardItem *itemType = new QStandardItem(); - QStandardItem *itemObj = new QStandardItem(); - QStandardItem *itemRoute = new QStandardItem(); - itemCate->setText(sCategory); - itemCate->setData(nCate); - itemName->setText(sName); - itemName->setData(0); //初始化方向占用 - itemName->setData(QUuid::createUuid(),Qt::UserRole+3); //初始化时赋予uuid - itemType->setText(sType); - itemType->setData(nType); - itemObj->setText(sBindObj); - itemObj->setData(nBindType,Qt::UserRole+1); - itemObj->setData(nBindPara,Qt::UserRole+2); - itemObj->setData(sBindParent,Qt::UserRole+3); + if (!_pParent) + return; - QList lstItems; - lstItems<appendRow(lstItems); + auto pModel = _pParent->getComponentModel(); + if (!pModel) + return; + + // ===== 2. 查找是否已存在同名设备 ===== + for (int row = 0; row < pModel->rowCount(); ++row) + { + QStandardItem *nameItem = pModel->item(row, Col_Name); + if (!nameItem) + continue; + + if (nameItem->text() != sName) + continue; + + // ===== 3. 重名确认 ===== + QMessageBox msgBox; + msgBox.setText(QString::fromWCharArray(L"提示")); + msgBox.setInformativeText(QString::fromWCharArray(L"设备名已存在,是否覆盖?")); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + + if (msgBox.exec() != QMessageBox::Ok) + return; + + // ===== 4. 覆盖已有行 ===== + pModel->item(row, Col_Category)->setText(sCategory); + pModel->item(row, Col_Category)->setData(nCate); + + nameItem->setData(0); // 初始化方向占用 + + pModel->item(row, Col_Type)->setText(sType); + pModel->item(row, Col_Type)->setData(nType); + + QStandardItem *objItem = pModel->item(row, Col_BindObj); + objItem->setText(sBindObj); + objItem->setData(nBindType, Qt::UserRole + 1); + objItem->setData(nBindPara, Qt::UserRole + 2); + objItem->setData(sBindParent, Qt::UserRole + 3); + + syncSelectedModel(sName); + return; } + + // ===== 5. 新增行 ===== + QList rowItems; + rowItems.resize(Col_Count); + + rowItems[Col_Category] = new QStandardItem(sCategory); + rowItems[Col_Category]->setData(nCate); + + rowItems[Col_Name] = new QStandardItem(sName); + rowItems[Col_Name]->setData(0); // 方向占用 + rowItems[Col_Name]->setData(QUuid::createUuid(), Qt::UserRole + 3); + + rowItems[Col_Type] = new QStandardItem(sType); + rowItems[Col_Type]->setData(nType); + + rowItems[Col_BindObj] = new QStandardItem(sBindObj); + rowItems[Col_BindObj]->setData(nBindType, Qt::UserRole + 1); + rowItems[Col_BindObj]->setData(nBindPara, Qt::UserRole + 2); + rowItems[Col_BindObj]->setData(sBindParent, Qt::UserRole + 3); + + rowItems[Col_Route] = new QStandardItem(); + + pModel->appendRow(rowItems); } void DiagramEditorBayDetailAddDlg::onOkClicked() @@ -573,6 +716,12 @@ void DiagramEditorBayDetailAddDlg::onComponentRbtnClicked(const QPoint &pos) menu.exec(ui->tableView_items->mapToGlobal(pos)); } +void DiagramEditorBayDetailAddDlg::onComponentSelected(const QModelIndex &index) +{ + int row = index.row(); + loadFromModelRow(row); +} + void DiagramEditorBayDetailAddDlg::onRouteDeleteClicked() { // 获取当前选中的索引 diff --git a/diagramCavas/source/diagramEditor/diagramEditorBaySettingDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorBaySettingDlg.cpp index 90b2ca1..24329d8 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorBaySettingDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorBaySettingDlg.cpp @@ -53,15 +53,27 @@ void DiagramEditorBaySettingDlg::showDlg(int nLevel,DiagramEditorBayBlock* p) if(p){ auto lstCon = p->getConnect(); QStringList lst; + QSet otherItems; for(auto& conId:lstCon){ if(_pWizard->getConnection().contains(conId)){ auto con = _pWizard->getConnection().value(conId); QString sOpposite = con.getOpposite(p->getName()).sName; lst.append(sOpposite); + if(con.getOpposite(p->getName()).nType == 3){ + otherItems.insert(sOpposite); + } } } ui->listWidget->addItems(lst); + + for(int i = 0; i < ui->listWidget->count(); ++i){ + QListWidgetItem *item = ui->listWidget->item(i); + if (otherItems.contains(item->text())) + { + item->setFlags(item->flags() & ~Qt::ItemIsEnabled); + } + } } } _curLevel = nLevel; @@ -92,6 +104,7 @@ void DiagramEditorBaySettingDlg::initial() connect(ui->btn_ok,&QPushButton::clicked,this,&DiagramEditorBaySettingDlg::onOkClicked); connect(ui->btn_cancel,&QPushButton::clicked,this,&DiagramEditorBaySettingDlg::onCancelClicked); connect(ui->cb_level,&QComboBox::currentTextChanged,this,&DiagramEditorBaySettingDlg::onConnectLevelChanged); + connect(ui->listWidget, &QListWidget::itemClicked,this,&DiagramEditorBaySettingDlg::onListItemClicked); } void DiagramEditorBaySettingDlg::onAddClicked() @@ -187,3 +200,36 @@ void DiagramEditorBaySettingDlg::onConnectLevelChanged(const QString& str) } } } + +void DiagramEditorBaySettingDlg::onListItemClicked(QListWidgetItem *item) +{ + if (!item || !_pWizard) + return; + + QString targetName = item->text(); + + // 1. 根据 targetName 反查 level + // 这里假设:level -> target 是一对多关系 + // 需要遍历所有 level,查找包含该 target 的 level + int levelCount = ui->cb_level->count(); + for (int i = 0; i < levelCount; ++i) + { + int nIndex = ui->cb_level->itemData(i).toInt(); + auto lstBlock = _pWizard->getTargetLevelBlocks(nIndex, 1); + + for (auto& block : lstBlock) + { + if (block->getName() == targetName) + { + // 2. 设置 cb_level + ui->cb_level->setCurrentIndex(i); + + // 3. cb_target 会自动由 onConnectLevelChanged 刷新 + // 4. 再设置 cb_target 为当前项 + ui->cb_target->setCurrentText(targetName); + + return; + } + } + } +} diff --git a/diagramCavas/source/diagramEditor/diagramEditorTransDetailAddDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorTransDetailAddDlg.cpp index 5dca1b2..698cf99 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorTransDetailAddDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorTransDetailAddDlg.cpp @@ -40,6 +40,10 @@ void DiagramEditorTransDetailAddDlg::initial() connect(ui->cb_category,&QComboBox::currentTextChanged,this,&DiagramEditorTransDetailAddDlg::onCategoryChanged); connect(ui->tableView_selected, &QTableView::customContextMenuRequested, this, &DiagramEditorTransDetailAddDlg::onRouteRbtnClicked); connect(ui->tableView_items, &QTableView::customContextMenuRequested, this, &DiagramEditorTransDetailAddDlg::onComponentRbtnClicked); + connect(ui->tableView_items, &QTableView::clicked, this, [this](const QModelIndex &index){ + int row = index.row(); + loadFromModelRow(row); + }); QMap mapType = BaseTypeManager::getInstance()->getMapType(); //直接添加数据库中的基础类型 for(auto iter = mapType.begin();iter != mapType.end();++iter){ @@ -131,6 +135,95 @@ void DiagramEditorTransDetailAddDlg::showDlg(DiagramEditorRouteInfo info) } } +void DiagramEditorTransDetailAddDlg::setComboByData(QComboBox *combo, int data) +{ + for (int i = 0; i < combo->count(); ++i) + { + if (combo->itemData(i).toInt() == data) + { + combo->setCurrentIndex(i); + return; + } + } + combo->setCurrentIndex(-1); +} + +void DiagramEditorTransDetailAddDlg::setBindObjCombo(QComboBox *combo, + int bindType, + const QString &bindParent) +{ + for (int i = 0; i < combo->count(); ++i) + { + if (combo->itemData(i, Qt::UserRole + 1).toInt() == bindType && + combo->itemData(i, Qt::UserRole + 3).toString() == bindParent) + { + combo->setCurrentIndex(i); + return; + } + } + combo->setCurrentIndex(-1); +} + +void DiagramEditorTransDetailAddDlg::syncSelectedModel( + const QString &deviceName) +{ + if (!_pParent || !_selectedModel) + return; + + auto pMainModel = _pParent->getComponentModel(); + + int mainRow = -1; + for (int i = 0; i < pMainModel->rowCount(); ++i) + { + if (pMainModel->item(i, Col_Name)->text() == deviceName) + { + mainRow = i; + break; + } + } + + if (mainRow < 0) + return; + + for (int i = 0; i < _selectedModel->rowCount(); ++i) + { + if (_selectedModel->item(i, Col_Name)->text() != deviceName) + continue; + + _selectedModel->item(i, Col_Category) + ->setText(pMainModel->item(mainRow, Col_Category)->text()); + + _selectedModel->item(i, Col_Type) + ->setText(pMainModel->item(mainRow, Col_Type)->text()); + + QStandardItem *mainObj = pMainModel->item(mainRow, Col_BindObj); + QStandardItem *selObj = _selectedModel->item(i, Col_BindObj); + + selObj->setText(mainObj->text()); + selObj->setData(mainObj->data(Qt::UserRole + 1), Qt::UserRole + 1); + selObj->setData(mainObj->data(Qt::UserRole + 3), Qt::UserRole + 3); + } +} + +void DiagramEditorTransDetailAddDlg::loadFromModelRow(int row) +{ + auto pModel = _pParent->getComponentModel(); + if (!pModel) return; + + ui->le_name->setText(pModel->item(row, Col_Name)->text()); + + setComboByData(ui->cb_category, + pModel->item(row, Col_Category)->data().toInt()); + + setComboByData(ui->cb_type, + pModel->item(row, Col_Type)->data().toInt()); + + QStandardItem *objItem = pModel->item(row, Col_BindObj); + setBindObjCombo(ui->cb_bindObj, + objItem->data(Qt::UserRole + 1).toInt(), + objItem->data(Qt::UserRole + 3).toString()); +} + void DiagramEditorTransDetailAddDlg::updateBindLst() { _bindItemModel->clear(); @@ -210,55 +303,82 @@ void DiagramEditorTransDetailAddDlg::updateBindLst() void DiagramEditorTransDetailAddDlg::onAddClicked() { - // 获取当前选中的索引 - QModelIndexList selectedIndexes = ui->tableView_items->selectionModel()->selectedRows(); - if (selectedIndexes.isEmpty()) { - return; // 没有选中任何行 - } + if (!_pParent || !_selectedModel) + return; - QSet uniqueRows; - foreach (const QModelIndex &index, selectedIndexes) { - uniqueRows.insert(index.row()); - } + auto pMainModel = _pParent->getComponentModel(); + if (!pMainModel) + return; - auto pCompoModel = _pParent->getComponentModel(); + // ===== 1. 获取选中行 ===== + QModelIndexList indexes = + ui->tableView_items->selectionModel()->selectedRows(); - foreach (int row, uniqueRows) { - QStandardItem *sourceItemName = pCompoModel->item(row, 1); //名称 - int rowCount = _selectedModel->rowCount(); - for(int i = 0;i < rowCount;++i){ - QStandardItem *item = _selectedModel->item(i, 1); - if(item->text() == sourceItemName->text()){ - QMessageBox::information(NULL, QString("提示"), QString::fromWCharArray(L"线路中设备已存在")); + if (indexes.isEmpty()) + return; + + QSet rows; + for (const QModelIndex &idx : indexes) + rows.insert(idx.row()); + + const QString curRoute = ui->le_routeName->text(); + + // ===== 2. 逐行处理 ===== + for (int row : rows) + { + QStandardItem *mainNameItem = pMainModel->item(row, Col_Name); + if (!mainNameItem) + continue; + + const QString deviceName = mainNameItem->text(); + + // ===== 3. 去重校验 ===== + for (int i = 0; i < _selectedModel->rowCount(); ++i) + { + if (_selectedModel->item(i, Col_Name)->text() == deviceName) + { + QMessageBox::information( + this, + QString("提示"), + QString::fromWCharArray(L"线路中设备已存在") + ); return; } } - QList newRowItems; - for (int col = 0; col < pCompoModel->columnCount(); ++col) { - QStandardItem *sourceItem = pCompoModel->item(row, col); - if (sourceItem) { - // 创建深拷贝(重要!) - if(col == 4){ //线路引用,特殊处理 - QString strAllRoute = sourceItem->text(); - QString strCurRoute = ui->le_routeName->text(); + // ===== 4. 深拷贝主表行 ===== + QList newRow; + newRow.reserve(Col_Count); - if(!strAllRoute.contains(strCurRoute)){ //第一个引用不加符号 - if(strAllRoute.isEmpty()) - sourceItem->setText(strCurRoute); - else - sourceItem->setText(strAllRoute+","+strCurRoute); - } - } - - QStandardItem *newItem = sourceItem->clone(); - newRowItems.append(newItem); - } else { - // 如果单元格为空,创建空项目 - newRowItems.append(new QStandardItem()); + for (int col = 0; col < Col_Count; ++col) + { + QStandardItem *srcItem = pMainModel->item(row, col); + if (!srcItem) + { + newRow.append(new QStandardItem()); + continue; } + + QStandardItem *newItem = srcItem->clone(); + + // ===== 5. Route 字段特殊处理 ===== + if (col == Col_Route) + { + QString routeText = newItem->text(); + + if (!routeText.contains(curRoute)) + { + if (routeText.isEmpty()) + newItem->setText(curRoute); + else + newItem->setText(routeText + "," + curRoute); + } + } + + newRow.append(newItem); } - _selectedModel->appendRow(newRowItems); + + _selectedModel->appendRow(newRow); } } @@ -269,74 +389,88 @@ void DiagramEditorTransDetailAddDlg::onDeleteClicked() void DiagramEditorTransDetailAddDlg::onSaveClicked() { - QString sCategory = ui->cb_category->currentText(); - int nCate = ui->cb_category->currentData().toInt(); - QString sName = ui->le_name->text(); - QString sType = ui->cb_type->currentText(); - int nType = ui->cb_type->currentData().toInt(); - QString sBindObj = ui->cb_bindObj->currentText(); - int nBindType = ui->cb_bindObj->currentData(Qt::UserRole+1).toInt(); - QString sBindParent = ui->cb_bindObj->currentData(Qt::UserRole+3).toString(); + // ===== 1. 读取 UI 数据 ===== + const QString sCategory = ui->cb_category->currentText(); + const int nCate = ui->cb_category->currentData().toInt(); - if(_pParent){ - auto pCompoModel = _pParent->getComponentModel(); - int rowCount = pCompoModel->rowCount(); - for(int i = 0;i < rowCount;++i){ - QStandardItem *item = pCompoModel->item(i, 1); - if(item->text() == sName){ - 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(); + const QString sName = ui->le_name->text(); + if (sName.isEmpty()) + return; - switch (ret) { - case QMessageBox::Ok: //覆盖 - { - QStandardItem *itemCate = pCompoModel->item(i, 0); - QStandardItem *itemType = pCompoModel->item(i, 2); - QStandardItem *itemObj = pCompoModel->item(i, 3); - QStandardItem *itemRoute = pCompoModel->item(i, 4); + const QString sType = ui->cb_type->currentText(); + const int nType = ui->cb_type->currentData().toInt(); - item->setData(0); //覆盖时初始化方向占用 - itemCate->setText(sCategory); - itemType->setText(sType); - itemType->setData(nType); - itemObj->setText(sBindObj); - return; - } - break; - case QMessageBox::Cancel: - // Cancel was clicked - return; - default: - // should never be reached - break; - } - } - } + const QString sBindObj = ui->cb_bindObj->currentText(); + const int nBindType = ui->cb_bindObj->currentData(Qt::UserRole + 1).toInt(); + const QString sBindParent = ui->cb_bindObj->currentData(Qt::UserRole + 3).toString(); - QStandardItem *itemCate = new QStandardItem(); - QStandardItem *itemName = new QStandardItem(); - QStandardItem *itemType = new QStandardItem(); - QStandardItem *itemObj = new QStandardItem(); - QStandardItem *itemRoute = new QStandardItem(); - itemCate->setText(sCategory); - itemCate->setData(nCate); - itemName->setText(sName); - itemName->setData(0); //初始化方向占用 - itemName->setData(QUuid::createUuid(),Qt::UserRole+3); //初始化时赋予uuid - itemType->setText(sType); - itemType->setData(nType); - itemObj->setText(sBindObj); - itemObj->setData(nBindType); - itemObj->setData(sBindParent,Qt::UserRole+3); + if (!_pParent) + return; - QList lstItems; - lstItems<appendRow(lstItems); + auto pModel = _pParent->getComponentModel(); + if (!pModel) + return; + + // ===== 2. 查找同名设备 ===== + for (int row = 0; row < pModel->rowCount(); ++row) + { + QStandardItem *nameItem = pModel->item(row, Col_Name); + if (!nameItem) + continue; + + if (nameItem->text() != sName) + continue; + + // ===== 3. 重名确认 ===== + QMessageBox msgBox; + msgBox.setText(QString::fromWCharArray(L"提示")); + msgBox.setInformativeText(QString::fromWCharArray(L"设备名已存在,是否覆盖?")); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + + if (msgBox.exec() != QMessageBox::Ok) + return; + + // ===== 4. 覆盖已有行(完整更新)===== + pModel->item(row, Col_Category)->setText(sCategory); + pModel->item(row, Col_Category)->setData(nCate); + + nameItem->setData(0); // 初始化方向占用 + + QStandardItem *typeItem = pModel->item(row, Col_Type); + typeItem->setText(sType); + typeItem->setData(nType); + + QStandardItem *objItem = pModel->item(row, Col_BindObj); + objItem->setText(sBindObj); + objItem->setData(nBindType, Qt::UserRole + 1); + objItem->setData(sBindParent, Qt::UserRole + 3); + + syncSelectedModel(sName); + return; } + + // ===== 5. 新增行 ===== + QList rowItems; + rowItems.resize(Col_Count); + + rowItems[Col_Category] = new QStandardItem(sCategory); + rowItems[Col_Category]->setData(nCate); + + rowItems[Col_Name] = new QStandardItem(sName); + rowItems[Col_Name]->setData(0); // 方向占用 + rowItems[Col_Name]->setData(QUuid::createUuid(), Qt::UserRole + 3); + + rowItems[Col_Type] = new QStandardItem(sType); + rowItems[Col_Type]->setData(nType); + + rowItems[Col_BindObj] = new QStandardItem(sBindObj); + rowItems[Col_BindObj]->setData(nBindType, Qt::UserRole + 1); + rowItems[Col_BindObj]->setData(sBindParent, Qt::UserRole + 3); + + rowItems[Col_Route] = new QStandardItem(); + + pModel->appendRow(rowItems); } void DiagramEditorTransDetailAddDlg::onOkClicked() diff --git a/diagramCavas/source/diagramEditor/diagramEditorTransSettingDlg.cpp b/diagramCavas/source/diagramEditor/diagramEditorTransSettingDlg.cpp index d673d5e..e42225b 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorTransSettingDlg.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorTransSettingDlg.cpp @@ -6,6 +6,7 @@ #include "diagramEditor/diagramEditorStructContainer.h" #include "diagramEditor/wizardBayContentDlg.h" #include "common/core_model/constants.h" +#include DiagramEditorTransSettingDlg::DiagramEditorTransSettingDlg(QWidget *parent) : QDialog(parent) @@ -74,7 +75,16 @@ void DiagramEditorTransSettingDlg::showDlg(DiagramEditorTransformerBlock* p) ui->tableWidget->insertRow(row); //名称 + int bayType = 0; + auto pBlock = _pWizard->getBlockByName_all(sOpposite); + if(pBlock){ + auto pBay = dynamic_cast(pBlock); + if(pBay){ + bayType = int(pBay->getBayType()); + } + } QTableWidgetItem* nameItem = new QTableWidgetItem(sOpposite); + nameItem->setData(Qt::UserRole, bayType); ui->tableWidget->setItem(row, 0, nameItem); //位置 @@ -112,6 +122,7 @@ void DiagramEditorTransSettingDlg::initial() connect(ui->btn_cancel,&QPushButton::clicked,this,&DiagramEditorTransSettingDlg::onCancelClicked); connect(ui->cb_level,&QComboBox::currentTextChanged,this,&DiagramEditorTransSettingDlg::onConnectLevelChanged); connect(ui->cb_bayType,&QComboBox::currentIndexChanged,this,&DiagramEditorTransSettingDlg::onBayTypeChanged); + connect(ui->tableWidget, &QTableWidget::itemSelectionChanged,this, &DiagramEditorTransSettingDlg::onTableItemSelected); ui->cb_bayType->setItemData(0,3); //将类型与item关联 ui->cb_bayType->setItemData(1,4); @@ -141,6 +152,7 @@ void DiagramEditorTransSettingDlg::onAddClicked() //名称 QTableWidgetItem* nameItem = new QTableWidgetItem(str); + nameItem->setData(Qt::UserRole, ui->cb_bayType->currentData().toInt()); ui->tableWidget->setItem(row, 0, nameItem); //位置 @@ -163,8 +175,10 @@ void DiagramEditorTransSettingDlg::onDeleteClicked() } QModelIndex indexName = index.sibling(index.row(),0); - /*QString sName = indexName.data().toString(); - if(_pWizard){ + QString sName = indexName.data().toString(); + if(!_prepareDisconnectBlock.contains(sName)) + _prepareDisconnectBlock.append(sName); + /*if(_pWizard){ //delete con? }*/ @@ -238,11 +252,55 @@ void DiagramEditorTransSettingDlg::onOkClicked() { nVal = 2; } - QUuid uid = _pWizard->addConnection(_curOperateBlock->getName(),ui->tableWidget->item(i,0)->text(),3,2,nVal); + QString sTargetBay = ui->tableWidget->item(i,0)->text(); + QUuid uid = _pWizard->addConnection(_curOperateBlock->getName(),sTargetBay,3,2,nVal); + auto pBlock = _pWizard->getBlockByName_all(sTargetBay); //连接的双方都保存连接(todo:修改时删除对面保存的连接) + if(pBlock){ + pBlock->addConnect(uid); + } _curOperateBlock->addConnect(uid); } //todo: may delete + if(_prepareDisconnectBlock.size()){ + + for(auto& sTargetBay:_prepareDisconnectBlock){ + QUuid uid = _pWizard->findConnection(_curOperateBlock->getName(),sTargetBay); + if(!uid.isNull()) + { + _pWizard->removeConnection(uid); //从记录中删除链接 + auto pBlock = _pWizard->getBlockByName_all(sTargetBay); //连接的双方都保存连接(todo:修改时删除对面保存的连接) + if(pBlock){ + pBlock->removeConnect(uid);//从各自block中删除链接 + + auto pBay = dynamic_cast(pBlock); + if(pBay){ + auto& mapComponent = pBay->getBayInfo().mapComponent; + for(auto& comp:mapComponent){ + if(comp.sBindParent == _curOperateBlock->getName()){ //block中设备信息断开 + comp.sBindParent.clear(); + comp.sBindObj.clear(); + break; + } + } + + auto& mapRoute = pBay->getBayInfo().mapRoute; + for(auto& route:mapRoute){ //block中线路信息断开 + for(auto& comp:route.lstCompo){ + if(comp.sBindParent == _curOperateBlock->getName()){ + comp.sBindParent.clear(); + comp.sBindObj.clear(); + } + } + } + } + } + _curOperateBlock->removeConnect(uid); //从各自block中删除链接 + } + } + _prepareDisconnectBlock.clear(); + } + _pWizard->flushTransPage(); _curOperateBlock = nullptr; } @@ -252,6 +310,8 @@ void DiagramEditorTransSettingDlg::onOkClicked() void DiagramEditorTransSettingDlg::onCancelClicked() { + if(_prepareDisconnectBlock.size()) + _prepareDisconnectBlock.clear(); hide(); } @@ -294,3 +354,60 @@ void DiagramEditorTransSettingDlg::onBayTypeChanged(int idx) } } } + +void DiagramEditorTransSettingDlg::onTableItemSelected() +{ + auto items = ui->tableWidget->selectedItems(); + if (items.isEmpty()) + return; + + // 取第一列(名称) + QTableWidgetItem* nameItem = ui->tableWidget->item(items.first()->row(), 0); + if (!nameItem) + return; + + QString targetName = nameItem->text(); + int bayTypeValue = nameItem->data(Qt::UserRole).toInt(); + BayType targetBayType = BayType(bayTypeValue); + + if (!_pWizard) + return; + + // 遍历所有 level + for (int i = 0; i < ui->cb_level->count(); ++i) + { + int levelIndex = ui->cb_level->itemData(i).toInt(); + auto lstBlock = _pWizard->getTargetLevelBlocks(levelIndex, 2); + + for (auto& block : lstBlock) + { + auto pBay = dynamic_cast(block); + if (!pBay) + continue; + + if (pBay->getName() == targetName && + pBay->getBayType() == targetBayType) + { + // ✅ 1. 设置 bayType(必须先) + for (int j = 0; j < ui->cb_bayType->count(); ++j) + { + if (ui->cb_bayType->itemData(j).toInt() == bayTypeValue) + { + ui->cb_bayType->setCurrentIndex(j); + break; + } + } + + // ✅ 2. 设置 level + ui->cb_level->setCurrentIndex(i); + + // ✅ 3. 等待 onConnectLevelChanged 刷新 cb_target + QTimer::singleShot(0, this, [=] { + ui->cb_target->setCurrentText(targetName); + }); + + return; + } + } + } +} diff --git a/diagramCavas/source/diagramEditor/diagramEditorWizard.cpp b/diagramCavas/source/diagramEditor/diagramEditorWizard.cpp index de5bff5..67aef32 100644 --- a/diagramCavas/source/diagramEditor/diagramEditorWizard.cpp +++ b/diagramCavas/source/diagramEditor/diagramEditorWizard.cpp @@ -290,13 +290,21 @@ int DiagramEditorWizard::getContainerIndex(int nLevel,DiagramEditorStructContain return -1; } -QUuid DiagramEditorWizard::addConnection(const QString& str1,const QString& str2,int nType1,int nType2,int nPara) +QUuid DiagramEditorWizard::findConnection(const QString& str1,const QString& str2) { for(auto& con:_mapConnect){ - if((con.con1.sName == str1 && con.con2.sName == str2) || (con.con1.sName == str2 && con.con2.sName == str1)){ //已存在不插入 + if((con.con1.sName == str1 && con.con2.sName == str2) || (con.con1.sName == str2 && con.con2.sName == str1)){ //已存在 return con.uid; } } + return QUuid(); +} + +QUuid DiagramEditorWizard::addConnection(const QString& str1,const QString& str2,int nType1,int nType2,int nPara) +{ + QUuid uid = findConnection(str1,str2); + if(!uid.isNull()) + return uid; DiagramEditorBriefConnect con; con.uid = QUuid::createUuid(); diff --git a/diagramCavas/source/diagramEditor/editPanel.cpp b/diagramCavas/source/diagramEditor/editPanel.cpp index 48964ea..fc163f4 100644 --- a/diagramCavas/source/diagramEditor/editPanel.cpp +++ b/diagramCavas/source/diagramEditor/editPanel.cpp @@ -662,6 +662,14 @@ void EditPanel::loadBaseSetting(QUuid uid) _tagName = _projectName; //将临时名称保存到变量 _projectName = info.projectName; _pModel->setWizardInfo(info.context); + + QTimer::singleShot(300, this, [&](){ + //_pModel->clearCurPreview(); + _pModel->generatePreview(); + _pModel->calculateBlockPos(); + _pModel->setItemInBlockPos(); + _pModel->refreshConnection(); + }); } } diff --git a/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp b/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp new file mode 100644 index 0000000..b79f6f6 --- /dev/null +++ b/diagramCavas/source/diagramEditor/editorDiagramLayoutEngine.cpp @@ -0,0 +1,447 @@ +#include "diagramEditor/editorDiagramLayoutEngine.h" +#include "diagramEditor/editorDirectionManager.h" + +// 主入口函数 +QRectF DiagramLayoutEngine::executeLayout( + QMap& routes, + QMap& components, + const LayoutConfig& config, + Context& context) { + + // 1. 初始化上下文 + context.initComponentsCache(components); + context.itemCache.clear(); + + // 2. 查找主线 + QString mainRouteName = findMainRoute(routes); + if (mainRouteName.isEmpty()) { + qWarning() << "No main route found"; + return QRectF(); + } + + // 3. 布局主线 + if (routes.contains(mainRouteName)) { + layoutMainRoute(routes[mainRouteName], config, context); + } + + // 4. 布局所有支线 + for (auto it = routes.begin(); it != routes.end(); ++it) { + if (it->sRouteName == mainRouteName) continue; + layoutBranchRoute(*it, config, context); + } + + // 5. 更新组件映射 + if (!context.saveToModel) { + for (auto& route : routes) { + for (auto& compo : route.lstCompo) { + components[compo.sName] = compo; + } + } + } + + // 6. 计算边界矩形 + if (!context.saveToModel) { + return calculateBoundingRect(components); + } + + return QRectF(); // saveToModel 时由场景计算边界 +} + +// 查找主线 +QString DiagramLayoutEngine::findMainRoute( + const QMap& routes) { + + QString mainRoute; + bool hasMain = false; + + // 查找标记为主线 + for (auto it = routes.begin(); it != routes.end(); ++it) { + if (it->bMainRoute) { + mainRoute = it->sRouteName; + hasMain = true; + break; + } + } + + // 未设置主线,选择设备最多的线路 + if (!hasMain) { + int maxCount = 0; + for (auto it = routes.begin(); it != routes.end(); ++it) { + int count = it->lstCompo.size(); + if (count > maxCount) { + maxCount = count; + mainRoute = it->sRouteName; + } + } + + // 标记为主线 + if (!mainRoute.isEmpty()) { + const_cast&>(routes)[mainRoute].bMainRoute = true; + } + } + + return mainRoute; +} + +// 布局主线 +void DiagramLayoutEngine::layoutMainRoute( + DiagramEditorRouteInfo& route, + const LayoutConfig& config, + Context& context) { + + Direction mainDir = config.mainDirection(); + auto& components = route.lstCompo; + + if (components.isEmpty()) return; + + // 计算分段 + int nSeg = components.size() / 2; + int nSegIndex = 0; + + // 确定起始索引方向 + if (mainDir == Direction::Down || mainDir == Direction::Right) { + nSegIndex = -nSeg; // 正向从负半开始 + } else { + nSegIndex = nSeg; // 反向从正半开始 + } + + // 遍历所有组件 + for (int i = 0; i < components.size(); ++i) { + DiagramEditorComponentInfo& compo = components[i]; + + // 确定方向占用 + Direction dir = mainDir; + if (i == 0) { + // 队首保持主线方向 + } else if (i == components.size() - 1) { + // 队尾取反方向 + dir = DirectionManager::getOpposite(mainDir); + } else { + // 中间元件双向占用 + compo.nUsedDirection = static_cast(mainDir) | + static_cast(DirectionManager::getOpposite(mainDir)); + } + + // 计算位置增量 + QPoint delta = QPoint(0, 0); + int spacing = DirectionManager::isHorizontal(mainDir) + ? config.horizontalSpacing() + : config.verticalSpacing(); + + if (DirectionManager::isHorizontal(mainDir)) { + delta.setX(nSegIndex * spacing); + } else { + delta.setY(nSegIndex * spacing); + } + + // 计算旋转角度 + int rotate = DirectionManager::getRotationAngle(mainDir); + + // 更新组件 + updateComponent(compo, dir, delta, rotate, context); + + // 更新索引 + if (mainDir == Direction::Up || mainDir == Direction::Left) { + nSegIndex -= 1; + } else { + nSegIndex += 1; + } + } +} + +// 拆分支线 +void DiagramLayoutEngine::splitBranchRoute( + DiagramEditorRouteInfo& route, + Context& context) { + + route.lstOrder.clear(); + route.lstReverse.clear(); + + if (route.lstCompo.isEmpty()) { + return; + } + + // 检查首尾元件 + int firstDir = getComponentDirection(route.lstCompo.first().sName, context); + int lastDir = getComponentDirection(route.lstCompo.last().sName, context); + + if (firstDir != 0) { + // 情况1: 首元件是节点 + route.lstOrder = route.lstCompo; + } else if (lastDir != 0) { + // 情况2: 末元件是节点 + for (auto it = route.lstCompo.rbegin(); it != route.lstCompo.rend(); ++it) { + route.lstReverse.append(*it); + } + } else { + // 情况3: 查找中间节点 + int nodeIndex = -1; + for (int i = 0; i < route.lstCompo.size(); ++i) { + int dir = getComponentDirection(route.lstCompo[i].sName, context); + if (dir != 0) { + nodeIndex = i; + break; + } + } + + if (nodeIndex == -1) { + // 没有节点,默认为正序 + route.lstOrder = route.lstCompo; + return; + } + + // 反向序列: 从节点到开头 + for (int i = nodeIndex; i >= 0; --i) { + route.lstReverse.append(route.lstCompo[i]); + } + + // 正序序列: 从节点到结尾 + for (int i = nodeIndex; i < route.lstCompo.size(); ++i) { + route.lstOrder.append(route.lstCompo[i]); + } + } +} + +// 布局支线 +void DiagramLayoutEngine::layoutBranchRoute( + DiagramEditorRouteInfo& route, + const LayoutConfig& config, + Context& context) { + + // 1. 拆分支线 + splitBranchRoute(route, context); + + // 2. 布局正序序列 + if (route.lstOrder.size() > 1) { + layoutBranchSequence(route.lstOrder, config.subDirection(), + config, context, true); + } + + // 3. 布局反序序列 + if (route.lstReverse.size() > 1) { + layoutBranchSequence(route.lstReverse, config.subDirection(), + config, context, false); + } +} + +// 布局分支序列 +void DiagramLayoutEngine::layoutBranchSequence( + QList& sequence, + Direction branchDir, + const LayoutConfig& config, + Context& context, + bool isOrder) { + + if (sequence.size() < 2) return; + + // 获取基准位置 + QPoint basePos = getComponentPosition(sequence[0].sName, context); + int index = 1; // 偏移索引从1开始 + int polarity = 1; // 方向极性 + + // 确定极性 + if (branchDir == Direction::Left || branchDir == Direction::Up) { + polarity = -1; + } + + // 计算间距 + int spacing = DirectionManager::isHorizontal(branchDir) + ? config.horizontalSpacing() + : config.verticalSpacing(); + + // 布局后续元件 + for (int i = 0; i < sequence.size() - 1; ++i) { + DiagramEditorComponentInfo& current = sequence[i]; + DiagramEditorComponentInfo& next = sequence[i + 1]; + + // 确定分支方向 + Direction dir = determineBranchDirection(current, branchDir, context); + + // 计算下一个元件的位置 + QPoint nextPos = basePos; + int offset = index * polarity * spacing; + + if (DirectionManager::isHorizontal(dir)) { + nextPos.setX(basePos.x() + offset); + } else { + nextPos.setY(basePos.y() + offset); + } + + // 计算相对位置 + QPoint deltaPos = nextPos - basePos; + + // 下一个元件的连接方向与当前方向相反 + Direction nextConnectionDir = DirectionManager::getOpposite(dir); + + // 计算旋转角度 + int rotate = DirectionManager::getRotationAngle(dir); + + // 更新元件 + updateComponent(next, nextConnectionDir, deltaPos, rotate, context); + + index++; + } +} + +// 确定支线方向 +Direction DiagramLayoutEngine::determineBranchDirection( + const DiagramEditorComponentInfo& currentNode, + Direction preferredDir, + Context& context) { + + int usedDirections = getComponentDirection(currentNode.sName, context); + bool isHorizontalLayout = DirectionManager::isHorizontal(preferredDir); + + if (isHorizontalLayout) { + // 水平布局,检查左右 + bool leftOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Left); + bool rightOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Right); + + if (leftOccupied && rightOccupied) { + qWarning() << "Component" << currentNode.sName + << "has both left and right directions occupied"; + return preferredDir; + } else if (leftOccupied && !rightOccupied) { + return Direction::Right; + } else if (rightOccupied && !leftOccupied) { + return Direction::Left; + } else { + return preferredDir; + } + } else { + // 垂直布局,检查上下 + bool upOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Up); + bool downOccupied = DirectionManager::isDirectionOccupied(usedDirections, Direction::Down); + + if (upOccupied && downOccupied) { + qWarning() << "Component" << currentNode.sName + << "has both up and down directions occupied"; + return preferredDir; + } else if (upOccupied && !downOccupied) { + return Direction::Down; + } else if (downOccupied && !upOccupied) { + return Direction::Up; + } else { + return preferredDir; + } + } +} + +// 获取组件位置 +QPoint DiagramLayoutEngine::getComponentPosition( + const QString& componentName, + Context& context) { + + if (context.saveToModel) { + QStandardItem* item = getNameItem(componentName, context); + if (item) { + QVariant posData = item->data(Qt::UserRole + 2); + if (posData.isValid() && posData.canConvert()) { + return posData.toPoint(); + } + } + } else { + if (context.componentsCache.contains(componentName)) { + return context.componentsCache[componentName].deltaPos; + } + } + + return QPoint(0, 0); +} + +// 获取组件方向 +int DiagramLayoutEngine::getComponentDirection( + const QString& compoName, + Context& context) { + + if (context.saveToModel) { + QStandardItem* item = getNameItem(compoName, context); + return item ? item->data().toInt() : 0; + } else { + if (context.componentsCache.contains(compoName)) { + return context.componentsCache[compoName].nUsedDirection; + } + return 0; + } +} + +// 获取名称项 +QStandardItem* DiagramLayoutEngine::getNameItem( + const QString& name, + Context& context) { + + QString cacheKey = QString("%1_%2").arg(name).arg(context.sourceId); + + if (!context.itemCache.contains(cacheKey)) { + // 这里应该是具体的模型查找实现 + // 为了示例,返回一个临时项 + QStandardItem* item = new QStandardItem(name); + context.itemCache[cacheKey] = item; + } + + return context.itemCache[cacheKey]; +} + +// 更新组件 +void DiagramLayoutEngine::updateComponent( + DiagramEditorComponentInfo& compo, + Direction dir, + const QPoint& position, + int rotate, + Context& context) { + + if (context.saveToModel) { + QStandardItem* item = getNameItem(compo.sName, context); + if (item) { + int currentDir = item->data().toInt(); + int newDir = DirectionManager::markDirectionOccupied(currentDir, dir); + item->setData(QString::number(newDir)); + item->setData(position, Qt::UserRole + 2); + item->setData(rotate, Qt::UserRole + 5); + } + } else { + compo.nUsedDirection = DirectionManager::markDirectionOccupied(compo.nUsedDirection, dir); + compo.deltaPos = position; + compo.nRotate = rotate; + + // 更新缓存 + context.componentsCache[compo.sName] = compo; + } +} + +// 计算边界矩形 +QRectF DiagramLayoutEngine::calculateBoundingRect( + const QMap& components) { + + if (components.isEmpty()) { + return QRectF(); + } + + qreal minX = 0, maxX = 0, minY = 0, maxY = 0; + bool first = true; + + for (auto it = components.begin(); it != components.end(); ++it) { + if (it->nUsedDirection == 0) continue; // 未使用的组件 + + QPointF pos = it->deltaPos; + + if (first) { + minX = pos.x(); + maxX = pos.x() + m_compoWidth; + minY = pos.y(); + maxY = pos.y() + m_compoHeight; + first = false; + } else { + minX = qMin(minX, pos.x()); + maxX = qMax(maxX, pos.x() + m_compoWidth); + minY = qMin(minY, pos.y()); + maxY = qMax(maxY, pos.y() + m_compoHeight); + } + } + + if (first) { + return QRectF(); // 没有使用的组件 + } + + return QRectF(minX, minY, maxX - minX, maxY - minY); +} diff --git a/diagramCavas/ui/diagramEditorBaySettingDlg.ui b/diagramCavas/ui/diagramEditorBaySettingDlg.ui index 03030b3..09701fb 100644 --- a/diagramCavas/ui/diagramEditorBaySettingDlg.ui +++ b/diagramCavas/ui/diagramEditorBaySettingDlg.ui @@ -177,7 +177,7 @@ QWidget QLabel { 连接信息 - + 20 @@ -216,6 +216,12 @@ QWidget QLabel { + + + 80 + 16777215 + + QPushButton { background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ @@ -245,10 +251,42 @@ QPushButton:disabled { - - - - QPushButton { + + + + + + + + + + 连接列表 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 0 + + + + QPushButton { background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ color: white; border: none; /* 无边框,扁平化 */ @@ -270,26 +308,13 @@ QPushButton:disabled { color: #d1dce9; /* 浅灰色文字 */ } - - - 删除 - - - - - - - - - - - + - 连接列表 + 删除 - + diff --git a/diagramCavas/ui/diagramEditorTransSettingDlg.ui b/diagramCavas/ui/diagramEditorTransSettingDlg.ui index a3c980b..146185d 100644 --- a/diagramCavas/ui/diagramEditorTransSettingDlg.ui +++ b/diagramCavas/ui/diagramEditorTransSettingDlg.ui @@ -147,22 +147,42 @@ QWidget QLabel { 连接信息 - + 20 - + 10 + + + + 间隔类型 + + + + + + + 间隔名称 + + + - 10 + 0 + + + 80 + 16777215 + + QPushButton { background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ @@ -192,10 +212,114 @@ QPushButton:disabled { + + + + + + 间隔位置 + + + + + - - - QPushButton { + + 进线间隔 + + + + + 出线间隔 + + + + + + + + + + + + 高压侧 + + + + + 中压侧 + + + + + 低压侧 + + + + + + + + 连接到 + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 155 + + + + + + + + + + 连接列表 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + QPushButton { background-color: #5a79a1; /* 中性灰蓝,不刺眼 */ color: white; border: none; /* 无边框,扁平化 */ @@ -217,110 +341,17 @@ QPushButton:disabled { color: #d1dce9; /* 浅灰色文字 */ } - - - 删除 - - - - - - - + - 间隔名称 + 删除 - - - - 间隔位置 - - - - - - - 间隔类型 - - - - - - - - - - - 进线间隔 - - - - - 出线间隔 - - - - - - - - - - - 连接到 - - - - - - - - 高压侧 - - - - - 中压侧 - - - - - 低压侧 - - - - - - - - - - - - 连接列表 - - - - + - - - - Qt::Orientation::Vertical - - - - 20 - 155 - - - -