fix editor topology setting

This commit is contained in:
baiYue 2026-05-08 18:03:54 +08:00
parent b089163f32
commit 3afdc864d9
17 changed files with 1354 additions and 342 deletions

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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();

View File

@ -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<QString> _prepareDisconnectBlock; //准备断开的连接间隔
};
#endif

View File

@ -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<QUuid,DiagramEditorBriefConnect>& getConnection() {return _mapConnect;}
QList<QUuid> findConnectionByBlock(QString,int); //通过block找到连接
void removeConnection(QUuid);

View File

@ -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<QString, QStandardItem*> itemCache;
QMap<QString, DiagramEditorComponentInfo> componentsCache;
void initComponentsCache(const QMap<QString, DiagramEditorComponentInfo>& compos) {
componentsCache = compos;
}
};
LayoutResult layoutRoutes(QMap<QString, DiagramEditorRouteInfo>& routes,
QMap<QString, DiagramEditorComponentInfo>& components,
const LayoutConfig& config,
int nSource,
bool saveToModel);
QRectF executeLayout(
QMap<QString, DiagramEditorRouteInfo>& routes,
QMap<QString, DiagramEditorComponentInfo>& components,
const LayoutConfig& config,
Context& context
);
private:
// 主线布局
// 主线相关
QString findMainRoute(const QMap<QString, DiagramEditorRouteInfo>& 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<DiagramEditorComponentInfo>& 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<QString, DiagramEditorComponentInfo>& components);
// 辅助方法
QString findMainRoute(const QMap<QString, DiagramEditorRouteInfo>& routes);
QStandardItem* getNameItem(const QString& name, int nSource);
private:
QMap<QString, QStandardItem*> m_itemCache;
int m_compoWidth = 50; // 可配置
int m_compoHeight = 30; // 可配置
int m_compoWidth = 50;
int m_compoHeight = 30;
};

View File

@ -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<int>(dir)) != 0;
}
static int markDirectionOccupied(int usedFlags, Direction dir) {
return usedFlags | static_cast<int>(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<int>(dir)) != 0;
}
static int markDirectionOccupied(int usedDirections, Direction dir) {
return usedDirections | static_cast<int>(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;
}
}
};

View File

@ -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<int,modelType> 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<QStandardItem*> lstItems;
lstItems<<itemCate<<itemName<<itemType<<itemObj<<itemRoute;
pCompoModel->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<QStandardItem*> 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()
{
// 获取当前选中的索引

View File

@ -53,15 +53,27 @@ void DiagramEditorBaySettingDlg::showDlg(int nLevel,DiagramEditorBayBlock* p)
if(p){
auto lstCon = p->getConnect();
QStringList lst;
QSet<QString> 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;
}
}
}
}

View File

@ -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<int,modelType> 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<int> 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<int> 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<QStandardItem*> 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<QStandardItem*> 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<QStandardItem*> lstItems;
lstItems<<itemCate<<itemName<<itemType<<itemObj<<itemRoute;
pCompoModel->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<QStandardItem*> 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()

View File

@ -6,6 +6,7 @@
#include "diagramEditor/diagramEditorStructContainer.h"
#include "diagramEditor/wizardBayContentDlg.h"
#include "common/core_model/constants.h"
#include <QTimer>
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<DiagramEditorBayBlock*>(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<DiagramEditorBayBlock*>(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<DiagramEditorBayBlock*>(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;
}
}
}
}

View File

@ -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();

View File

@ -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();
});
}
}

View File

@ -0,0 +1,447 @@
#include "diagramEditor/editorDiagramLayoutEngine.h"
#include "diagramEditor/editorDirectionManager.h"
// 主入口函数
QRectF DiagramLayoutEngine::executeLayout(
QMap<QString, DiagramEditorRouteInfo>& routes,
QMap<QString, DiagramEditorComponentInfo>& 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<QString, DiagramEditorRouteInfo>& 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<QMap<QString, DiagramEditorRouteInfo>&>(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<int>(mainDir) |
static_cast<int>(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<DiagramEditorComponentInfo>& 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<QPoint>()) {
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<QString, DiagramEditorComponentInfo>& 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);
}

View File

@ -177,7 +177,7 @@ QWidget QLabel {
<property name="title">
<string>连接信息</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,1">
<layout class="QGridLayout" name="gridLayout_3">
<property name="horizontalSpacing">
<number>20</number>
</property>
@ -216,6 +216,12 @@ QWidget QLabel {
</property>
<item>
<widget class="QPushButton" name="btn_add">
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #5a79a1; /* 中性灰蓝,不刺眼 */
@ -245,10 +251,42 @@ QPushButton:disabled {
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_delete">
<property name="styleSheet">
<string notr="true">QPushButton {
</layout>
</item>
</layout>
</item>
<item row="0" column="1" rowspan="2">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>连接列表</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btn_delete">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #5a79a1; /* 中性灰蓝,不刺眼 */
color: white;
border: none; /* 无边框,扁平化 */
@ -270,26 +308,13 @@ QPushButton:disabled {
color: #d1dce9; /* 浅灰色文字 */
}
</string>
</property>
<property name="text">
<string>删除</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="0" column="1" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_4">
</property>
<property name="text">
<string>连接列表</string>
<string>删除</string>
</property>
</widget>
</item>
<item>
<item row="1" column="0" colspan="3">
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>

View File

@ -147,22 +147,42 @@ QWidget QLabel {
<property name="title">
<string>连接信息</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="gridLayout_3">
<property name="horizontalSpacing">
<number>20</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout" rowstretch="1,1,1,0,0,0" columnstretch="1,2">
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0" columnstretch="1,2">
<property name="verticalSpacing">
<number>10</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>间隔类型</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>间隔名称</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>10</number>
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="btn_add">
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #5a79a1; /* 中性灰蓝,不刺眼 */
@ -192,10 +212,114 @@ QPushButton:disabled {
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>间隔位置</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cb_bayType">
<item>
<widget class="QPushButton" name="btn_delete">
<property name="styleSheet">
<string notr="true">QPushButton {
<property name="text">
<string>进线间隔</string>
</property>
</item>
<item>
<property name="text">
<string>出线间隔</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cb_level"/>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cb_toPos">
<item>
<property name="text">
<string>高压侧</string>
</property>
</item>
<item>
<property name="text">
<string>中压侧</string>
</property>
</item>
<item>
<property name="text">
<string>低压侧</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>连接到</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cb_target"/>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>155</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" rowspan="2">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>连接列表</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btn_delete">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #5a79a1; /* 中性灰蓝,不刺眼 */
color: white;
border: none; /* 无边框,扁平化 */
@ -217,110 +341,17 @@ QPushButton:disabled {
color: #d1dce9; /* 浅灰色文字 */
}
</string>
</property>
<property name="text">
<string>删除</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
</property>
<property name="text">
<string>间隔名称</string>
<string>删除</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>间隔位置</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>间隔类型</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cb_target"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cb_bayType">
<item>
<property name="text">
<string>进线间隔</string>
</property>
</item>
<item>
<property name="text">
<string>出线间隔</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cb_level"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>连接到</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cb_toPos">
<item>
<property name="text">
<string>高压侧</string>
</property>
</item>
<item>
<property name="text">
<string>中压侧</string>
</property>
</item>
<item>
<property name="text">
<string>低压侧</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="0" column="1" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>连接列表</string>
</property>
</widget>
</item>
<item>
<item row="1" column="0" colspan="3">
<widget class="QTableWidget" name="tableWidget"/>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>155</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>