#include "hmidocument.h" #include "graphicsitems.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include HMIDocument::HMIDocument(QWidget *parent) : BaseDocument(HMI, parent), m_title("未命名HMI"), m_canDrawEllipse(false), m_canDrawRectangle(false), m_lastPastePos(0, 0) { // 创建绘图场景 m_scene = new QGraphicsScene(this); m_scene->setSceneRect(0, 0, 800, 600); m_scene->setBackgroundBrush(QBrush(Qt::lightGray)); // 创建视图 m_view = new QGraphicsView(m_scene, this); m_view->setRenderHint(QPainter::Antialiasing); m_view->setDragMode(QGraphicsView::RubberBandDrag); m_view->setAcceptDrops(true); m_view->viewport()->installEventFilter(this); // 布局(让视图占满文档区域) auto layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_view); setLayout(layout); } HMIDocument::~HMIDocument() { } // 事件过滤器(处理绘图、拖拽等事件) bool HMIDocument::eventFilter(QObject *obj, QEvent *event) { if (obj != m_view->viewport()) { return QWidget::eventFilter(obj, event); } // 鼠标按下事件(绘制图形) if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast(event); setModified(true); if (mouseEvent->button() == Qt::RightButton) { showContextMenu(mouseEvent->globalPos());// 右键菜单 return true; } if (mouseEvent->button() == Qt::LeftButton) { QPointF scenePos = m_view->mapToScene(mouseEvent->pos()); QGraphicsItem *item = m_scene->itemAt(scenePos, m_view->transform()); // 如果点击空白区域且有绘制标志,创建图形 if (!item) { if (m_canDrawEllipse) { createShape("ellipse", scenePos); resetDrawFlags(); } else if (m_canDrawRectangle) { createShape("rectangle", scenePos); resetDrawFlags(); } } } } // 拖拽事件(从工具栏拖拽绘制) if (event->type() == QEvent::DragEnter) { QDragEnterEvent *dragEvent = static_cast(event); dragEvent->acceptProposedAction(); return true; } if (event->type() == QEvent::DragMove) { static_cast(event)->acceptProposedAction(); return true; } if (event->type() == QEvent::Drop) { QDropEvent *dropEvent = static_cast(event); const QMimeData *mimeData = dropEvent->mimeData(); QPointF scenePos = m_view->mapToScene(dropEvent->pos()); // 使用QMimeData if (mimeData && mimeData->hasText()) { createShape(mimeData->text(), scenePos); resetDrawFlags();//重置防止拖拽后再次点击生成 } dropEvent->acceptProposedAction(); return true; } return QWidget::eventFilter(obj, event); } void HMIDocument::createShape(const QString& type, const QPointF &pos) { m_scene->clearSelection(); if (type == "指示灯" || type == "ellipse") { ResizableEllipse *ellipse = new ResizableEllipse(pos.x(), pos.y(), 50, 50); ellipse->setBrush(QBrush(Qt::red)); ellipse->setPen(QPen(Qt::black, 1)); m_scene->addItem(ellipse); ellipse->setSelected(true); } else if (type == "按钮" || type == "rectangle") { ResizableRectangle *rect = new ResizableRectangle(pos.x(), pos.y(), 100, 50); rect->setBrush(QBrush(Qt::yellow)); rect->setPen(QPen(Qt::black, 1)); m_scene->addItem(rect); rect->setSelected(true); } setModified(true); } // 重置绘制标志 void HMIDocument::resetDrawFlags() { m_canDrawEllipse = false; m_canDrawRectangle = false; } // 右键菜单 void HMIDocument::showContextMenu(QPoint globalPos) { QPoint viewportPos = m_view->mapFromGlobal(globalPos); QPointF scenePos = m_view->mapToScene(viewportPos); QGraphicsItem *clickedItem = m_scene->itemAt(scenePos, m_view->transform()); QMenu menu(this); if (!clickedItem) { // 空白区域:仅显示粘贴 QAction *pasteAction = menu.addAction("粘贴"); pasteAction->setEnabled(!m_copiedItemsData.isEmpty()); connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems); } else { // 选中图形:显示完整菜单 QAction *propAction = menu.addAction("属性"); QAction *copyAction = menu.addAction("复制"); QAction *pasteAction = menu.addAction("粘贴"); QAction *deleteAction = menu.addAction("删除"); QAction *onAction = menu.addAction("置为ON"); QAction *offAction = menu.addAction("置为OFF"); pasteAction->setEnabled(!m_copiedItemsData.isEmpty()); connect(propAction, &QAction::triggered, this, &HMIDocument::showItemProperties); connect(copyAction, &QAction::triggered, this, &HMIDocument::copySelectedItems); connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems); connect(deleteAction, &QAction::triggered, this, &HMIDocument::deleteSelectedItems); // ON/OFF动作(针对指示灯和按钮) connect(onAction, &QAction::triggered, [=]() { if (auto ellipse = dynamic_cast(clickedItem)) { ellipse->setBrush(ellipse->onColor()); } else if (auto rect = dynamic_cast(clickedItem)) { rect->setBrush(rect->pressedColor()); } }); connect(offAction, &QAction::triggered, [=]() { if (auto ellipse = dynamic_cast(clickedItem)) { ellipse->setBrush(ellipse->offColor()); } else if (auto rect = dynamic_cast(clickedItem)) { rect->setBrush(rect->releasedColor()); } }); } menu.exec(globalPos + QPoint(10, 10)); } // 复制选中项 void HMIDocument::copySelectedItems() { m_copiedItemsData.clear(); QList selectedItems = m_scene->selectedItems(); if (selectedItems.isEmpty()) return; foreach (QGraphicsItem *item, selectedItems) { m_copiedItemsData.append(serializeItem(item)); } m_lastPastePos = selectedItems.first()->pos(); setModified(true); } // 粘贴项 void HMIDocument::pasteItems() { if (m_copiedItemsData.isEmpty()) return; QPointF offset(20, 20); m_lastPastePos += offset; m_scene->clearSelection(); foreach (const QByteArray &itemData, m_copiedItemsData) { QGraphicsItem *newItem = deserializeItem(itemData); if (newItem) { m_scene->addItem(newItem); newItem->setPos(m_lastPastePos); newItem->setSelected(true); } } setModified(true); } // 删除选中项 void HMIDocument::deleteSelectedItems() { QList selectedItems = m_scene->selectedItems(); foreach (QGraphicsItem *item, selectedItems) { m_scene->removeItem(item); delete item; } setModified(true); } // 显示属性对话框 void HMIDocument::showItemProperties() { QList selectedItems = m_scene->selectedItems(); if (selectedItems.isEmpty()) return; QGraphicsItem *item = selectedItems.first(); // 将QGraphicsItem转换为NamedItem NamedItem *namedItem = dynamic_cast(item); if (!namedItem) return; QDialog dialog(this); dialog.setWindowTitle("属性设置"); QFormLayout *form = new QFormLayout(&dialog); // 名称输入 - 使用NamedItem接口 QLineEdit *nameEdit = new QLineEdit(namedItem->name()); // 颜色选择(根据图形类型) QColor tempColor1, tempColor2; QPushButton *colorBtn1 = new QPushButton; QPushButton *colorBtn2 = new QPushButton; if (auto ellipse = dynamic_cast(item)) { tempColor1 = ellipse->onColor(); tempColor2 = ellipse->offColor(); form->addRow("ON颜色:", colorBtn1); form->addRow("OFF颜色:", colorBtn2); } else if (auto rect = dynamic_cast(item)) { tempColor1 = rect->pressedColor(); tempColor2 = rect->releasedColor(); form->addRow("按下颜色:", colorBtn1); form->addRow("释放颜色:", colorBtn2); } // 初始化颜色按钮 colorBtn1->setStyleSheet("background-color:" + tempColor1.name()); colorBtn2->setStyleSheet("background-color:" + tempColor2.name()); // 颜色选择对话框 connect(colorBtn1, &QPushButton::clicked, [&]() { QColor c = QColorDialog::getColor(tempColor1, &dialog); if (c.isValid()) { tempColor1 = c; colorBtn1->setStyleSheet("background-color:" + c.name()); } }); connect(colorBtn2, &QPushButton::clicked, [&]() { QColor c = QColorDialog::getColor(tempColor2, &dialog); if (c.isValid()) { tempColor2 = c; colorBtn2->setStyleSheet("background-color:" + c.name()); } }); // 布局组装 form->addRow("对象:", nameEdit); QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); form->addRow(btnBox); connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); // 应用属性 if (dialog.exec() == QDialog::Accepted) { setModified(true); // 使用NamedItem接口设置名称 namedItem->setName(nameEdit->text()); if (auto ellipse = dynamic_cast(item)) { ellipse->setOnColor(tempColor1); ellipse->setOffColor(tempColor2); } else if (auto rect = dynamic_cast(item)) { rect->setPressedColor(tempColor1); rect->setReleasedColor(tempColor2); } item->update(); } } // 序列化图形项(用于复制粘贴) QByteArray HMIDocument::serializeItem(QGraphicsItem *item) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); // 基础信息 stream << item->type(); stream << item->pos(); stream << item->flags(); stream << item->transform(); stream << item->isVisible(); // 形状信息 if (auto shape = dynamic_cast(item)) { stream << shape->pen(); stream << shape->brush(); stream << shape->boundingRect(); } // 具体图形信息 if (auto rect = dynamic_cast(item)) { stream << rect->rect(); stream << rect->pressedColor(); stream << rect->releasedColor(); stream << rect->name(); } else if (auto ellipse = dynamic_cast(item)) { stream << ellipse->rect(); stream << ellipse->onColor(); stream << ellipse->offColor(); stream << ellipse->name(); } return data; } // 反序列化图形项(用于粘贴) QGraphicsItem *HMIDocument::deserializeItem(const QByteArray &data) { QDataStream stream(data); int type; QPointF pos; QGraphicsItem::GraphicsItemFlags flags; QTransform transform; bool visible; stream >> type >> pos >> flags >> transform >> visible; QGraphicsItem *item = nullptr; if (type == QGraphicsRectItem::Type) { QPen pen; QBrush brush; QRectF boundRect; QRectF rect; QColor pressedColor, releasedColor; QString name; stream >> pen >> brush >> boundRect >> rect >> pressedColor >> releasedColor >> name; ResizableRectangle *rectItem = new ResizableRectangle(0, 0, rect.width(), rect.height()); rectItem->setPen(pen); rectItem->setBrush(brush); rectItem->setPressedColor(pressedColor); rectItem->setReleasedColor(releasedColor); rectItem->setName(name); item = rectItem; } else if (type == QGraphicsEllipseItem::Type) { QPen pen; QBrush brush; QRectF boundRect; QRectF rect; QColor onColor, offColor; QString name; stream >> pen >> brush >> boundRect >> rect >> onColor >> offColor >> name; ResizableEllipse *ellipseItem = new ResizableEllipse(0, 0, rect.width(), rect.height()); ellipseItem->setPen(pen); ellipseItem->setBrush(brush); ellipseItem->setOnColor(onColor); ellipseItem->setOffColor(offColor); ellipseItem->setName(name); item = ellipseItem; } if (item) { item->setFlags(flags); item->setTransform(transform); item->setVisible(visible); } return item; } void HMIDocument::startDrawingEllipse() { m_canDrawEllipse = true; m_canDrawRectangle = false; } void HMIDocument::startDrawingRectangle() { m_canDrawEllipse = false; m_canDrawRectangle = true; } bool HMIDocument::saveToFile(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "无法打开文件进行写入:" << filePath; return false; } // 创建JSON文档结构 QJsonObject docObject; docObject["title"] = m_title; docObject["sceneRect"] = QJsonArray({ m_scene->sceneRect().x(), m_scene->sceneRect().y(), m_scene->sceneRect().width(), m_scene->sceneRect().height() }); docObject["backgroundColor"] = m_scene->backgroundBrush().color().name(); // 序列化所有图形项 QJsonArray itemsArray; foreach (QGraphicsItem *item, m_scene->items()) { QJsonObject itemJson = itemToJson(item); if (!itemJson.isEmpty()) { itemsArray.append(itemJson); } } docObject["items"] = itemsArray; // 写入文件 QJsonDocument doc(docObject); file.write(doc.toJson()); file.close(); // 更新文档状态 setFilePath(filePath); setModified(false); QFileInfo fileInfo(filePath); setTitle(fileInfo.baseName()); return true; } // 从文件加载文档 bool HMIDocument::loadFromFile(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "无法打开文件进行读取:" << filePath; return false; } // 读取JSON文档 QByteArray data = file.readAll(); QJsonDocument doc = QJsonDocument::fromJson(data); if (doc.isNull()) { qWarning() << "无效的JSON文档:" << filePath; return false; } // 解析文档 QJsonObject docObject = doc.object(); m_title = docObject["title"].toString(); // 设置场景属性 QJsonArray sceneRectArray = docObject["sceneRect"].toArray(); if (sceneRectArray.size() == 4) { QRectF sceneRect( sceneRectArray[0].toDouble(), sceneRectArray[1].toDouble(), sceneRectArray[2].toDouble(), sceneRectArray[3].toDouble() ); m_scene->setSceneRect(sceneRect); } if (docObject.contains("backgroundColor")) { QColor bgColor(docObject["backgroundColor"].toString()); m_scene->setBackgroundBrush(QBrush(bgColor)); } // 清除现有内容 m_scene->clear(); // 加载所有图形项 QJsonArray itemsArray = docObject["items"].toArray(); foreach (QJsonValue itemValue, itemsArray) { QJsonObject itemJson = itemValue.toObject(); QGraphicsItem *item = jsonToItem(itemJson); if (item) { m_scene->addItem(item); } } // 更新文档状态 setFilePath(filePath); setModified(false); return true; } // 将图形项转换为JSON对象 QJsonObject HMIDocument::itemToJson(QGraphicsItem *item) { QJsonObject json; // 基础属性 json["type"] = item->type(); json["x"] = item->x(); json["y"] = item->y(); json["zValue"] = item->zValue(); json["visible"] = item->isVisible(); // 具体图形项属性 if (auto ellipse = dynamic_cast(item)) { json["itemType"] = "ellipse"; json["rect"] = QJsonArray({ ellipse->rect().x(), ellipse->rect().y(), ellipse->rect().width(), ellipse->rect().height() }); json["onColor"] = ellipse->onColor().name(); json["offColor"] = ellipse->offColor().name(); json["currentColor"] = ellipse->brush().color().name(); json["penColor"] = ellipse->pen().color().name(); json["penWidth"] = ellipse->pen().width(); json["name"] = ellipse->name(); } else if (auto rect = dynamic_cast(item)) { json["itemType"] = "rectangle"; json["rect"] = QJsonArray({ rect->rect().x(), rect->rect().y(), rect->rect().width(), rect->rect().height() }); json["pressedColor"] = rect->pressedColor().name(); json["releasedColor"] = rect->releasedColor().name(); json["currentColor"] = rect->brush().color().name(); json["penColor"] = rect->pen().color().name(); json["penWidth"] = rect->pen().width(); json["name"] = rect->name(); } return json; } // 从JSON对象创建图形项 QGraphicsItem *HMIDocument::jsonToItem(const QJsonObject &json) { QString itemType = json["itemType"].toString(); QGraphicsItem *item = nullptr; if (itemType == "ellipse") { QJsonArray rectArray = json["rect"].toArray(); if (rectArray.size() != 4) return nullptr; ResizableEllipse *ellipse = new ResizableEllipse( rectArray[0].toDouble(), rectArray[1].toDouble(), rectArray[2].toDouble(), rectArray[3].toDouble() ); ellipse->setOnColor(QColor(json["onColor"].toString())); ellipse->setOffColor(QColor(json["offColor"].toString())); ellipse->setBrush(QColor(json["currentColor"].toString())); ellipse->setPen(QPen( QColor(json["penColor"].toString()), json["penWidth"].toDouble() )); ellipse->setName(json["name"].toString()); item = ellipse; } else if (itemType == "rectangle") { QJsonArray rectArray = json["rect"].toArray(); if (rectArray.size() != 4) return nullptr; ResizableRectangle *rect = new ResizableRectangle( rectArray[0].toDouble(), rectArray[1].toDouble(), rectArray[2].toDouble(), rectArray[3].toDouble() ); rect->setPressedColor(QColor(json["pressedColor"].toString())); rect->setReleasedColor(QColor(json["releasedColor"].toString())); rect->setBrush(QColor(json["currentColor"].toString())); rect->setPen(QPen( QColor(json["penColor"].toString()), json["penWidth"].toDouble() )); rect->setName(json["name"].toString()); item = rect; } // 设置基础属性 if (item) { item->setX(json["x"].toDouble()); item->setY(json["y"].toDouble()); item->setZValue(json["zValue"].toDouble()); item->setVisible(json["visible"].toBool()); } return item; } QString HMIDocument::title() const { return m_title; } void HMIDocument::setTitle(const QString &title) { m_title = title; } QGraphicsView *HMIDocument::view() const { return m_view; } QGraphicsScene *HMIDocument::scene() const { return m_scene; } void HMIDocument::setDrawEllipse(bool enable) { m_canDrawEllipse = enable; } void HMIDocument::setDrawRectangle(bool enable) { m_canDrawRectangle = enable; } void HMIDocument::markModified() { setModified(true); }