#include "expertboxlayoutwin.h" #include "expertwin.h" #include "gutil/qgui.h" #include "gutil/qjson.h" #include "globalfunc.h" #include #include #include #include #include #include #include #include #include ExpertBoxLayoutWin::ExpertBoxLayoutWin(ExpertWin *parent) : BaseWin{parent} { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle("箱体高级布局"); resize(1024, 720); setStyleSheet("QToolButton {border: none; }"); auto vBox = new VBox(center); vBox->setContentsMargins(0,0,0,0); vBox->setSpacing(3); vBox->addLayout(addBtns(new QHBoxLayout)); auto hBox = new HBox(vBox); vBox = new VBox(hBox); vBox->addWidget(new QLabel("模组编辑")); auto grid = new Grid(vBox); auto btnAdd = new QPushButton("添加"); connect(btnAdd, &QPushButton::clicked, this, [=] { auto file = QFileDialog::getOpenFileName(this, tr("打开单元板文件"), gFileHome, tr("单元板文件 (*.module)")); if(file.isEmpty()) return; gFileHome = QFileInfo(file).absolutePath(); QFile qFile(file); if(! qFile.open(QFile::ReadOnly)) { QMessageBox::critical(this, tr("打开单元板文件失败"), file); return; } QString err; auto json = JFrom(&qFile, &err); qFile.close(); if(! err.isEmpty()) { QMessageBox::critical(this, tr("解析 json 失败"), err+" "+file); return; } QDialog dlg(this); dlg.setWindowTitle(tr("添加模组")); auto vBox = new VBox(&dlg); auto hBox = new HBox(vBox); hBox->addLabel(tr("横向数量")); auto fdHNum = new QSpinBox; fdHNum->setRange(1, 999); fdHNum->setValue(1); hBox->addWidget(fdHNum); hBox->addStretch(); hBox = new HBox(vBox); hBox->addLabel(tr("纵向数量")); auto fdVNum = new QSpinBox; fdVNum->setRange(1, 999); fdVNum->setValue(1); hBox->addWidget(fdVNum); hBox->addStretch(); vBox->addStretch(); connect(vBox->addBtnBox(&dlg), &QDialogButtonBox::accepted, &dlg, &QDialog::accept); if(dlg.exec()!=QDialog::Accepted) return; auto hNum = fdHNum->value(); auto vNum = fdVNum->value(); auto width = json["ModuleWidth"].toInt(); auto height = json["ModuleHeight"].toInt(); for(int vv=0; vv < vNum; ++vv) for(int hh=0; hh < hNum; ++hh) { auto unit = new ModuleUnit(width*hh, height*vv, width, height, json.toObj(), "", box); unit->show(); } fdModNum->setNum(box->children().size()); box->fitSize(); }); grid->addWidget(btnAdd, 0, 0); auto btnDel = new QPushButton("删除"); grid->addWidget(btnDel, 0, 1); auto btnMove = new QPushButton("移动"); grid->addWidget(btnMove, 1, 0); auto btnRotate = new QPushButton("旋转"); grid->addWidget(btnRotate, 1, 1); auto btnDraw = new QPushButton("开始走线"); btnDraw->setCheckable(true); connect(btnDraw, &QPushButton::clicked, this, [=](bool checked) { box->isDrawing = checked; btnDraw->setText(checked ? "结束走线" : "开始走线"); }); grid->addWidget(btnDraw, 2, 0); auto btnClearLines = new QPushButton("清空所有走线"); connect(btnClearLines, &QPushButton::clicked, this, [=] { for(auto btn : grpGrp->buttons()) if(btn->property("mods").isValid()) btn->setProperty("mods", QVariant()); for(auto child : box->children()) ((ModuleUnit*)child)->name = QString(); box->update(); }); grid->addWidget(btnClearLines, 3, 0); auto btnClearLine = new QPushButton("清空当前走线"); connect(btnClearLine, &QPushButton::clicked, this, [=] { for(auto btn : grpGrp->buttons()) { auto mods = btn->property("mods").toList(); for(auto mod : mods) if(((ModuleUnit*) mod.value())->isSel) { btn->setProperty("mods", QVariant()); for(auto mod : mods) ((ModuleUnit*) mod.value())->name = QString(); break; } } box->update(); }); grid->addWidget(btnClearLine, 3, 1); vBox->addLabel("数据组数设置"); auto hh = new HBox(vBox); hh->addLabel("组数:"); auto fdGNum = new QSpinBox; fdGNum->setRange(1, 128); fdGNum->setValue(16); hh->addWidget(fdGNum); auto btnSet = new QPushButton("设置"); hh->addWidget(btnSet); grid = new Grid(vBox); grpGrp = new QButtonGroup(fdGNum); connect(btnSet, &QPushButton::clicked, this, [=] { auto newCnt = fdGNum->value(); auto oldCnt = grpGrp->buttons().size(); if(newCnt>oldCnt) for(int i=oldCnt; isetCheckable(true); btn->setMaximumWidth(40); btn->setFixedHeight(24); grpGrp->addButton(btn, i); grid->addWidget(btn, i / 4, i % 4); } else if(newCnt=newCnt; --i) { auto btn = grpGrp->button(i); grpGrp->removeButton(btn); grid->removeWidget(btn); delete btn; } if(grpGrp->checkedButton()==0) grpGrp->buttons()[0]->setChecked(true); }); emit btnSet->clicked(); vBox->addWidget(new QLabel("显示")); grid = new Grid(vBox); fdHasPos = new QCheckBox("位置"); fdHasPos->setChecked(true); grid->addWidget(fdHasPos, 0, 0); fdHasSize = new QCheckBox("大小"); fdHasSize->setChecked(true); grid->addWidget(fdHasSize, 0, 1); fdHasConn = new QCheckBox("连接"); fdHasConn->setChecked(true); grid->addWidget(fdHasConn, 1, 0); fdHasName = new QCheckBox("名称"); fdHasName->setChecked(true); grid->addWidget(fdHasName, 1, 1); fdHasOutline = new QCheckBox("轮廓"); fdHasOutline->setChecked(true); grid->addWidget(fdHasOutline, 2, 0); vBox->addWidget(new QLabel("对齐")); hh = new HBox(vBox); QSize imgSize(32,32); auto bnAlignLeft = new QToolButton; bnAlignLeft->setIconSize(imgSize); bnAlignLeft->setIcon(QIcon(":/imgs/align-left.png")); connect(bnAlignLeft, &QToolButton::clicked, this, [=] { int bound = INT_MAX; ModuleUnit* mod; for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->x() < bound) bound = mod->x(); bool needFit = false; if(bound != INT_MAX) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->x() != bound) { mod->move(bound, mod->y()); mod->mX = qRound(mod->x() / box->rate); needFit = true; } if(needFit) box->fitSize(); }); hh->addWidget(bnAlignLeft); auto bnAlignRight = new QToolButton; bnAlignRight->setIconSize(imgSize); bnAlignRight->setIcon(QIcon(":/imgs/align-right.png")); connect(bnAlignRight, &QToolButton::clicked, this, [=] { int bound = INT_MIN, temp; ModuleUnit* mod; for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && (temp = mod->x()+mod->width()) > bound) bound = temp; bool needFit = false; if(bound != INT_MIN) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->x() != (temp = bound-mod->width())) { mod->move(temp, mod->y()); mod->mX = qRound(mod->x() / box->rate); needFit = true; } if(needFit) box->fitSize(); }); hh->addWidget(bnAlignRight); auto bnAlignTop = new QToolButton; bnAlignTop->setIconSize(imgSize); bnAlignTop->setIcon(QIcon(":/imgs/align-top.png")); connect(bnAlignTop, &QToolButton::clicked, this, [=] { int bound = INT_MAX; ModuleUnit* mod; for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->y() < bound) bound = mod->y(); bool needFit = false; if(bound != INT_MAX) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->y() != bound) { mod->move(mod->x(), bound); mod->mY = qRound(mod->y() / box->rate); needFit = true; } if(needFit) box->fitSize(); }); hh->addWidget(bnAlignTop); auto bnAlignBottom = new QToolButton; bnAlignBottom->setIconSize(imgSize); bnAlignBottom->setIcon(QIcon(":/imgs/align-bottom.png")); connect(bnAlignBottom, &QToolButton::clicked, this, [=] { int bound = INT_MIN, temp; ModuleUnit* mod; for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && (temp = mod->y()+mod->height()) > bound) bound = temp; bool needFit = false; if(bound != INT_MIN) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->y() != (temp = bound-mod->height())) { mod->move(mod->x(), temp); mod->mY = qRound(mod->y() / box->rate); needFit = true; } if(needFit) box->fitSize(); }); hh->addWidget(bnAlignBottom); vBox->addWidget(new QLabel("排列")); hh = new HBox(vBox); auto bnArrangeLeft = new QToolButton; bnArrangeLeft->setIconSize(imgSize); bnArrangeLeft->setIcon(QIcon(":/imgs/arrange-left.png")); connect(bnArrangeLeft, &QToolButton::clicked, this, [=] { int bound = INT_MAX; ModuleUnit* tmod; std::vector mods; for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) { auto it = mods.begin(); while(it!=mods.end() && (*it)->y() <= tmod->y()) it++; mods.insert(it, tmod); if(tmod->x() < bound) bound = tmod->x(); } tmod = 0; for(auto mod : mods) { mod->move(bound, tmod ? tmod->y()+tmod->height() : mod->y()); tmod = mod; mod->mX = qRound(mod->x() / box->rate); mod->mY = qRound(mod->y() / box->rate); } if(! mods.empty()) box->fitSize(); }); hh->addWidget(bnArrangeLeft); auto bnArrangeRight = new QToolButton; bnArrangeRight->setIconSize(imgSize); bnArrangeRight->setIcon(QIcon(":/imgs/arrange-right.png")); connect(bnArrangeRight, &QToolButton::clicked, this, [=] { int bound = INT_MIN; ModuleUnit* tmod; std::vector mods; for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) { auto it = mods.begin(); while(it!=mods.end() && (*it)->y() <= tmod->y()) it++; mods.insert(it, tmod); if(tmod->x()+tmod->width() > bound) bound = tmod->x()+tmod->width(); } tmod = 0; for(auto mod : mods) { mod->move(bound-mod->width(), tmod ? tmod->y()+tmod->height() : mod->y()); tmod = mod; mod->mX = qRound(mod->x() / box->rate); mod->mY = qRound(mod->y() / box->rate); } if(! mods.empty()) box->fitSize(); }); hh->addWidget(bnArrangeRight); auto bnArrangeTop = new QToolButton; bnArrangeTop->setIconSize(imgSize); bnArrangeTop->setIcon(QIcon(":/imgs/arrange-top.png")); connect(bnArrangeTop, &QToolButton::clicked, this, [=] { int bound = INT_MAX; ModuleUnit* tmod; std::vector mods; for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) { auto it = mods.begin(); while(it!=mods.end() && (*it)->x() <= tmod->x()) it++; mods.insert(it, tmod); if(tmod->y() < bound) bound = tmod->y(); } tmod = 0; for(auto mod : mods) { mod->move(tmod ? tmod->x()+tmod->width() : mod->x(), bound); tmod = mod; mod->mX = qRound(mod->x() / box->rate); mod->mY = qRound(mod->y() / box->rate); } if(! mods.empty()) box->fitSize(); }); hh->addWidget(bnArrangeTop); auto bnArrangeBottom = new QToolButton; bnArrangeBottom->setIconSize(imgSize); bnArrangeBottom->setIcon(QIcon(":/imgs/arrange-bottom.png")); connect(bnArrangeBottom, &QToolButton::clicked, this, [=] { int bound = INT_MIN; ModuleUnit* tmod; std::vector mods; for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) { auto it = mods.begin(); while(it!=mods.end() && (*it)->x() <= tmod->x()) it++; mods.insert(it, tmod); if(tmod->y()+tmod->height() > bound) bound = tmod->y()+tmod->height(); } tmod = 0; for(auto mod : mods) { mod->move(tmod ? tmod->x()+tmod->width() : mod->x(), bound-mod->height()); tmod = mod; mod->mX = qRound(mod->x() / box->rate); mod->mY = qRound(mod->y() / box->rate); } if(! mods.empty()) box->fitSize(); }); hh->addWidget(bnArrangeBottom); vBox->addStretch(); vBox = new VBox(hBox); hBox = new HBox(vBox); auto fdRoom = new QComboBox; fdRoom->setEditable(true); fdRoom->addItem("1600%"); fdRoom->addItem("800%"); fdRoom->addItem("400%"); fdRoom->addItem("200%"); fdRoom->addItem("100%"); fdRoom->addItem("50%"); fdRoom->addItem("25%"); connect(fdRoom, &QComboBox::currentTextChanged, this, [=](const QString &text) { if(box==0) return; box->rate = text.trimmed().replace('%',"").toInt() / 100.0; auto childs = box->children(); foreach(auto child, childs) { auto mod = (ModuleUnit*)child; mod->setGeometry(mod->mX*box->rate, mod->mY*box->rate, mod->mW*box->rate, mod->mH*box->rate); } box->fitSize(); }); hBox->addWidget(fdRoom); hBox->addLabel("模组数量: "); fdModNum = hBox->addLabel(); hBox->addSpacing(20); hBox->addLabel("箱体大小: "); fdBoxSize = hBox->addLabel(); hBox->addStretch(); auto scroll = new QScrollArea; vBox->addWidget(scroll); box = new BoxPanel(this); scroll->setWidget(box); box->resize(1024, 1024); box->setAutoFillBackground(true); auto pal = palette(); pal.setColor(QPalette::Window, QColor(0x222222)); box->setPalette(pal); SetCurText(fdRoom, "200%"); connect(fdHasPos, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update); connect(fdHasSize, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update); connect(fdHasConn, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update); connect(fdHasName, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update); connect(fdHasOutline, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update); auto ModuleWidth = parent->mModule["ModuleWidth"].toInt(); auto ModuleHeight = parent->mModule["ModuleHeight"].toInt(); new ModuleUnit(0, 0, ModuleWidth, ModuleHeight, parent->mModule, "", box); new ModuleUnit(ModuleWidth, 0, ModuleWidth, ModuleHeight, parent->mModule, "", box); new ModuleUnit(0, ModuleHeight, ModuleWidth, ModuleHeight, parent->mModule, "", box); new ModuleUnit(ModuleWidth, ModuleHeight, ModuleWidth, ModuleHeight, parent->mModule, "", box); fdModNum->setNum(4); box->fitSize(); } void BoxPanel::mousePressEvent(QMouseEvent *event) { if(event->button() != Qt::LeftButton) return; if(isDrawing) return; rect = QRectF(event->localPos(), event->localPos()); for(auto child : children()) ((ModuleUnit*) child)->isSel = false; update(); } void BoxPanel::mouseReleaseEvent(QMouseEvent *event) { if(event->button() != Qt::LeftButton) return; if(rect.isNull()) return; rect = QRectF(); update(); } void BoxPanel::mouseMoveEvent(QMouseEvent *event) { if(! (event->buttons() & Qt::LeftButton)) return; if(isDrawing) { auto mod = (ModuleUnit*) childAt(event->pos()); if(mod && mod->name.isEmpty()) mod->drawed(); return; } rect.setBottomRight(event->localPos()); for(auto child : children()) { auto mod = (ModuleUnit*) child; mod->isSel = rect.intersects(mod->geometry()); } update(); } void BoxPanel::paintEvent(QPaintEvent *) { QPainter painter(this); ModuleUnit *mod; for(auto child : children()) { mod = (ModuleUnit*) child; QPointF pos(mod->x(), mod->y()); painter.translate(pos); painter.fillRect(QRectF(1, 1, mod->width()-2, mod->height()-2), mod->isSel ? QColor(0, 0xaa, 0, 0xaa) : QColor(0, 0x44, 0xff, 0x77)); painter.setPen(QColor(mod->isSel ? 0xffffff : 0xdddddd)); if(boxWin->fdHasOutline->isChecked()) painter.drawRect(QRectF(0.5, 0.5, mod->width()-1, mod->height()-1)); int off = 0; if(boxWin->fdHasPos->isChecked()) painter.drawText(2, off+=15, QString::number(mod->mX)+", "+QString::number(mod->mY)); if(boxWin->fdHasSize->isChecked()) painter.drawText(2, off+=15, QString::number(mod->mW)+" x "+QString::number(mod->mH)); if(! mod->name.isEmpty()) painter.drawText(2, off+15, mod->name); painter.translate(-pos); } QPen pen(0xff8800); pen.setCapStyle(Qt::FlatCap); pen.setDashPattern(QVector{4, 4}); painter.setPen(pen); painter.drawRect(QRectF(boxRect.x()-0.5, boxRect.y()-0.5, boxRect.width()+1, boxRect.height()+1)); if(boxWin->fdHasConn->isChecked()) { QBrush brush(0x0088ff); painter.setPen(QPen(brush, 3)); QPainterPath path; for(auto btn : boxWin->grpGrp->buttons()) { auto mods = btn->property("mods").toList(); if(mods.isEmpty()) continue; QPointF last = ((ModuleUnit*) mods[0].value())->geometry().center(); for(int mm=1; mm())->geometry().center(); painter.drawLine(last, pos); auto deg = 180 / 3.1415926535 * atan((pos.y()-last.y()) / (pos.x()-last.x())); if(pos.x()rate, y*box->rate, w*box->rate, h*box->rate); } void ModuleUnit::drawed() { auto btn = box->boxWin->grpGrp->checkedButton(); if(btn==0) return; auto mods = btn->property("mods").toList(); mods.append(QVariant::fromValue((void*)this)); btn->setProperty("mods", mods); name = "G"+QString::number(box->boxWin->grpGrp->id(btn)+1)+"-"+QString::number(mods.size()); box->update(); } void ModuleUnit::mousePressEvent(QMouseEvent *event) { QWidget::mousePressEvent(event); if(event->button() != Qt::LeftButton) return; event->accept(); if(box->isDrawing) return; setCursor(Qt::SizeAllCursor); auto glbPos = event->globalPos(); box->grpRect = geometry(); if(isSel) { mOtherSels.clear(); ModuleUnit* mod; for(auto child : box->children()) if(child!=this && (mod = (ModuleUnit*)child)->isSel) { mod->mPressRel = mod->pos() - pos(); box->grpRect |= mod->geometry(); mOtherSels.emplace_back(mod); } } mPressRel = pos() - box->grpRect.topLeft(); box->grpRect.translate(-glbPos); } void ModuleUnit::mouseReleaseEvent(QMouseEvent *event) { QWidget::mouseReleaseEvent(event); if(Qt::LeftButton != event->button()) return; event->accept(); if(box->isDrawing) { if(name.isEmpty()) drawed(); return; } unsetCursor(); mPressRel.setX(INT_MIN); if(! isSel) { isSel = true; update(); for(auto item : box->children()) if(item!=this && ((ModuleUnit*)item)->isSel) { ((ModuleUnit*)item)->isSel = false; ((ModuleUnit*)item)->update(); } } for(auto sel : mOtherSels) sel->mPressRel.setX(INT_MIN); mOtherSels.clear(); } #define SnapSpace 6 void ModuleUnit::mouseMoveEvent(QMouseEvent *e) { if(! (e->buttons() & Qt::LeftButton)) return; if(box->isDrawing) { if(name.isEmpty()) drawed(); else { auto mod = (ModuleUnit*) box->childAt(x()+e->x(), y()+e->y()); if(mod && mod!=this && mod->name.isEmpty()) mod->drawed(); } return; } if(mPressRel.x()==INT_MIN) return; if(! isSel) { isSel = true; for(auto child : box->children()) if(child!=this && ((ModuleUnit*)child)->isSel) ((ModuleUnit*)child)->isSel = false; } auto mousePos = e->globalPos(); auto dstHor = box->grpRect.x() + mousePos.x(); auto dstVer = box->grpRect.y() + mousePos.y(); dstHor = qMax(0, dstHor); dstVer = qMax(0, dstVer); // if(dstHor) for(ModuleUnit *ele : mOtherEles) {//左右 // if(abs(dstHor - ele->x()) < SnapSpace) { // dstHor = ele->x(); // break; // } // auto eleRight = ele->x() + ele->width(); // if(abs(dstHor - eleRight) < SnapSpace) { // dstHor = eleRight; // break; // } // auto right = dstHor + width(); // if(abs(right - ele->x()) < SnapSpace && ele->x() - width() >= 0) { // dstHor = ele->x() - width(); // break; // } // if(abs(right - eleRight) < SnapSpace && eleRight - width() >= 0) { // dstHor = eleRight - width(); // break; // } // } // if(dstVer) for(ModuleUnit *ele : mOtherEles) {//上下 // if(abs(dstVer-ele->y()) < SnapSpace) { // dstVer = ele->y(); // break; // } // auto eleBtm = ele->y() + ele->height(); // if(abs(dstVer - eleBtm) < SnapSpace) { // dstVer = eleBtm; // break; // } // auto btm = dstVer + height(); // if(abs(btm - ele->y()) < SnapSpace && ele->y() - height() >= 0) { // dstVer = ele->y() - height(); // break; // } // if(abs(btm - eleBtm) < SnapSpace && eleBtm - height() >= 0) { // dstVer = eleBtm - height(); // break; // } // } move(mPressRel.x() + dstHor, mPressRel.y() + dstVer); mX = qRound(x() / box->rate); mY = qRound(y() / box->rate); for(auto sel : mOtherSels) { if(sel->mPressRel.x()==INT_MIN) continue; sel->move(sel->mPressRel.x() + x(), sel->mPressRel.y() + y()); sel->mX = qRound(sel->x() / box->rate); sel->mY = qRound(sel->y() / box->rate); } box->fitSize(); }