#include "hmieditorwidget.h" #include "hmibutton.h" #include "hmiled.h" #include "hmicontrolitem.h" #include #include #include #include #include #include #include HmiEditorWidget::HmiEditorWidget(QWidget* parent) : EditorWidget(parent) { // 左侧:在父类 EditorWidget 的左侧工具栏区域追加页面按钮与标签 QLayoutItem* item = layout()->itemAt(0)->widget()->layout()->itemAt(0); auto* leftLayout = dynamic_cast(item->layout()); m_newPageButton = new QPushButton("新建页面", this); m_deletePageButton = new QPushButton("删除页面", this); m_prevPageButton = new QPushButton("上一页", this); m_nextPageButton = new QPushButton("下一页", this); m_pageLabel = new QLabel("无页面", this); leftLayout->addWidget(m_newPageButton); leftLayout->addWidget(m_deletePageButton); leftLayout->addWidget(m_prevPageButton); leftLayout->addWidget(m_nextPageButton); leftLayout->addWidget(m_pageLabel); leftLayout->addStretch(); connect(m_newPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNewPageClicked); connect(m_deletePageButton, &QPushButton::clicked, this, &HmiEditorWidget::onDeletePageClicked); connect(m_prevPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onPreviousPageClicked); connect(m_nextPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNextPageClicked); // 工具栏项 initToolbar(); // 确保视图已创建(父类提供) ensureViewReady(); // 捕获编辑区鼠标:既监听 QGraphicsView 自身,也监听其 viewport if (editArea) { editArea->installEventFilter(this); if (editArea->viewport()) editArea->viewport()->installEventFilter(this); } } HmiEditorWidget::~HmiEditorWidget() { clearPages(); } void HmiEditorWidget::refreshView() { if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) { editArea->setScene(nullptr); editArea->setScene(m_pages[m_currentPageIndex]); initViewScale(); editArea->viewport()->update(); } } void HmiEditorWidget::initToolbar() { // “按钮” auto* buttonItem = new QListWidgetItem(); buttonItem->setIcon(QIcon(":resource/image/button.png")); buttonItem->setData(Qt::UserRole, QString("按钮")); buttonItem->setToolTip("按钮"); toolbar->addItem(buttonItem); // “指示灯” auto* ledItem = new QListWidgetItem(); ledItem->setIcon(QIcon(":resource/image/light.png")); ledItem->setData(Qt::UserRole, QString("指示灯")); ledItem->setToolTip("指示灯"); toolbar->addItem(ledItem); } void HmiEditorWidget::onToolbarItemClicked(QListWidgetItem* item) { if (!item) return; // —— 关键改动 ——: // 不再“立即创建到场景中心”,而是进入“放置模式” m_pendingType = item->data(Qt::UserRole).toString(); setPlacingCursor(true); appendOutput(QString("已选择控件:%1。请在编辑区点击以放置。").arg(m_pendingType)); } QGraphicsScene* HmiEditorWidget::getCurrentScene() const { if (m_currentPageIndex >= 0 && m_currentPageIndex < m_pages.size()) return m_pages[m_currentPageIndex]; return nullptr; } void HmiEditorWidget::clearPages() { for (QGraphicsScene* scene : m_pages) delete scene; m_pages.clear(); m_currentPageIndex = -1; if (editArea) editArea->setScene(nullptr); updatePageLabel(); } void HmiEditorWidget::createNewPage() { auto* newScene = new QGraphicsScene(this); QRect viewRect = editArea->viewport()->rect(); if (viewRect.isEmpty()) { viewRect = QRect(0,0,800,600); editArea->setSceneRect(viewRect); } newScene->setSceneRect(viewRect); m_pages.append(newScene); m_currentPageIndex = m_pages.size() - 1; QMetaObject::invokeMethod(this, [this, newScene](){ editArea->setScene(newScene); initViewScale(); updatePageLabel(); editArea->viewport()->update(); }, Qt::QueuedConnection); } void HmiEditorWidget::switchToPage(int index) { if (index >= 0 && index < m_pages.size()) { m_currentPageIndex = index; editArea->resetTransform(); editArea->setScene(m_pages[index]); updatePageLabel(); QMetaObject::invokeMethod(this, [this](){ initViewScale(); editArea->viewport()->update(); }, Qt::QueuedConnection); } } void HmiEditorWidget::initViewScale() { if (!editArea || m_currentPageIndex < 0 || m_pages.isEmpty()) return; QRect viewRect = editArea->viewport()->rect(); if (viewRect.isEmpty()) viewRect = QRect(0,0,800,600); auto* currentScene = m_pages[m_currentPageIndex]; QRectF currentRect = currentScene->sceneRect(); const qreal newW = qMax(currentRect.width(), qreal(viewRect.width())); const qreal newH = qMax(currentRect.height(), qreal(viewRect.height())); currentScene->setSceneRect(0,0,newW,newH); editArea->viewport()->update(); } void HmiEditorWidget::updatePageLabel() { if (m_pages.isEmpty()) { m_pageLabel->setText("无页面"); m_deletePageButton->setEnabled(false); m_prevPageButton->setEnabled(false); m_nextPageButton->setEnabled(false); } else { m_pageLabel->setText(QString("第 %1/%2 页").arg(m_currentPageIndex+1).arg(m_pages.size())); m_deletePageButton->setEnabled(m_pages.size()>1); m_prevPageButton->setEnabled(m_currentPageIndex>0); m_nextPageButton->setEnabled(m_currentPageIndextype() == QEvent::ContextMenu) { auto* contextEvent = static_cast(event); if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { QGraphicsItem* hit = editArea->itemAt(contextEvent->pos()); if (!hit) { QMenu menu; QAction* pasteAction = new QAction("粘贴", &menu); pasteAction->setEnabled(HmiControlItem::hasClipboardData()); connect(pasteAction, &QAction::triggered, [this, contextEvent](){ if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] && HmiControlItem::hasClipboardData()) { const QPointF scenePos = editArea->mapToScene(contextEvent->pos()); HmiControlItem* newItem = HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), scenePos); if (newItem) { m_pages[m_currentPageIndex]->addItem(newItem); m_pages[m_currentPageIndex]->clearSelection(); newItem->setSelected(true); editArea->viewport()->update(); } } }); menu.addAction(pasteAction); menu.exec(contextEvent->globalPos()); } } return true; } // —— 关键改动:放置模式下,点击编辑区创建 —— // // 捕获 QGraphicsView 的 viewport 左键按下 if (obj == (editArea ? editArea->viewport() : nullptr) && event->type() == QEvent::MouseButtonPress && !m_pendingType.isEmpty()) { auto* me = static_cast(event); if (me->button() == Qt::LeftButton && m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { const QPointF scenePos = editArea->mapToScene(me->pos()); HmiControlItem* item = createItemByType(m_pendingType, scenePos); if (item) { m_pages[m_currentPageIndex]->addItem(item); m_pages[m_currentPageIndex]->clearSelection(); item->setSelected(true); editArea->viewport()->update(); } // 一次放一个:退出放置模式(如需连续放置,注释掉两行) m_pendingType.clear(); setPlacingCursor(false); return true; // 已处理 } } return EditorWidget::eventFilter(obj, event); } void HmiEditorWidget::resizeEvent(QResizeEvent* e) { EditorWidget::resizeEvent(e); if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) initViewScale(); } void HmiEditorWidget::showEvent(QShowEvent* e) { EditorWidget::showEvent(e); if (m_pages.isEmpty()) createNewPage(); } void HmiEditorWidget::keyPressEvent(QKeyEvent* event) { // Ctrl+C:复制(保留原逻辑——复制首个选中项至静态剪贴板) if (event->matches(QKeySequence::Copy)) { if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { auto selectedItems = m_pages[m_currentPageIndex]->selectedItems(); if (!selectedItems.isEmpty()) { if (auto* item = dynamic_cast(selectedItems.first())) { HmiControlItem::copyToClipboard(item->saveToClipboard()); } } } return; } // Ctrl+V:粘贴(关键改动:若有选中源控件 → 旁边粘贴;否则 → 场景中心) if (event->matches(QKeySequence::Paste)) { if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] && HmiControlItem::hasClipboardData()) { QPointF pastePos; auto selectedItems = m_pages[m_currentPageIndex]->selectedItems(); static int s_pasteIndex = 0; // 连续多次粘贴时,用来轻微错行 if (!selectedItems.isEmpty()) { if (auto* src = dynamic_cast(selectedItems.first())) { pastePos = calcPasteBesidePos(src, s_pasteIndex++); } } else { // 无选中项:仍按原逻辑 → 场景中心 QRectF rect = m_pages[m_currentPageIndex]->sceneRect(); pastePos = QPointF(rect.width()/2, rect.height()/2); s_pasteIndex = 0; } HmiControlItem* newItem = HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), pastePos); if (newItem) { m_pages[m_currentPageIndex]->addItem(newItem); m_pages[m_currentPageIndex]->clearSelection(); newItem->setSelected(true); editArea->viewport()->update(); } } return; } // Delete:删除(保留原逻辑) if (event->matches(QKeySequence::Delete)) { if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { auto selectedItems = m_pages[m_currentPageIndex]->selectedItems(); for (QGraphicsItem* gi : selectedItems) { if (auto* ci = dynamic_cast(gi)) ci->deleteItem(); } editArea->viewport()->update(); } return; } EditorWidget::keyPressEvent(event); } void HmiEditorWidget::onNewPageClicked() { createNewPage(); } void HmiEditorWidget::onDeletePageClicked() { if (m_currentPageIndex >= 0 && m_pages.size() > 1) { QGraphicsScene* scene = m_pages[m_currentPageIndex]; m_pages.removeAt(m_currentPageIndex); delete scene; m_currentPageIndex = qMin(m_currentPageIndex, m_pages.size()-1); editArea->setScene(m_pages[m_currentPageIndex]); initViewScale(); updatePageLabel(); editArea->viewport()->update(); appendOutput(QString("删除页面,剩余页面数: %1").arg(m_pages.size())); } } void HmiEditorWidget::onPreviousPageClicked() { switchToPage(m_currentPageIndex - 1); } void HmiEditorWidget::onNextPageClicked() { switchToPage(m_currentPageIndex + 1); } // —— 私有辅助 —— // HmiControlItem* HmiEditorWidget::createItemByType(const QString& type, const QPointF& scenePos) { HmiControlItem* item = nullptr; if (type == "按钮") { item = new HmiButton(); appendOutput("添加按钮控件"); } else if (type == "指示灯") { item = new HmiLed(); appendOutput("添加指示灯控件"); } if (item) item->setPos(scenePos); return item; } void HmiEditorWidget::setPlacingCursor(bool on) { if (!editArea) return; if (on) { editArea->setCursor(Qt::CrossCursor); if (editArea->viewport()) editArea->viewport()->setCursor(Qt::CrossCursor); } else { editArea->unsetCursor(); if (editArea->viewport()) editArea->viewport()->unsetCursor(); } } QPointF HmiEditorWidget::calcPasteBesidePos(HmiControlItem* src, int pasteIndex) const { if (!src) return QPointF(); // 使用“映射到场景后的边界框”来获取可视宽度,避免缩放带来的误差 QRectF sceneRect = src->mapToScene(src->boundingRect()).boundingRect(); const qreal w = qMax(sceneRect.width(), 24.0); // 保底宽度 const qreal gap = 12.0; // 与原件的水平间隔 const qreal stepY = 8.0; // 连续粘贴时的竖向微调 QPointF base = src->pos(); return QPointF(base.x() + w + gap, base.y() + pasteIndex * stepY); }