|
- #include "hmidocument.h"
- #include "graphicsitems.h"
- #include <QGraphicsItem>
- #include <QGraphicsSceneHoverEvent>
- #include <QMouseEvent>
- #include <QMenu>
- #include <QAction>
- #include <QDataStream>
- #include <QColorDialog>
- #include <QDialog>
- #include <QFormLayout>
- #include <QLineEdit>
- #include <QPushButton>
- #include <QDialogButtonBox>
- #include <QMimeData>
- #include <QDragEnterEvent>
- #include <QDropEvent>
- #include <QDebug>
- #include <QFileInfo>
- #include <QVBoxLayout>
- #include <QJsonArray>
- #include <QJsonDocument>
- #include <QJsonObject>
-
- 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<QMouseEvent*>(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<QDragEnterEvent*>(event);
- dragEvent->acceptProposedAction();
- return true;
- }
- if (event->type() == QEvent::DragMove)
- {
- static_cast<QDragMoveEvent*>(event)->acceptProposedAction();
- return true;
- }
- if (event->type() == QEvent::Drop)
- {
- QDropEvent *dropEvent = static_cast<QDropEvent*>(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<ResizableEllipse*>(clickedItem)) {
- ellipse->setBrush(ellipse->onColor());
- } else if (auto rect = dynamic_cast<ResizableRectangle*>(clickedItem)) {
- rect->setBrush(rect->pressedColor());
- }
- });
- connect(offAction, &QAction::triggered, [=]() {
- if (auto ellipse = dynamic_cast<ResizableEllipse*>(clickedItem)) {
- ellipse->setBrush(ellipse->offColor());
- } else if (auto rect = dynamic_cast<ResizableRectangle*>(clickedItem)) {
- rect->setBrush(rect->releasedColor());
- }
- });
- }
-
- menu.exec(globalPos + QPoint(10, 10));
- }
-
- // 复制选中项
- void HMIDocument::copySelectedItems()
- {
- m_copiedItemsData.clear();
- QList<QGraphicsItem*> 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<QGraphicsItem*> selectedItems = m_scene->selectedItems();
- foreach (QGraphicsItem *item, selectedItems) {
- m_scene->removeItem(item);
- delete item;
- }
- setModified(true);
- }
-
- // 显示属性对话框
- void HMIDocument::showItemProperties()
- {
- QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
- if (selectedItems.isEmpty()) return;
- QGraphicsItem *item = selectedItems.first();
-
- // 将QGraphicsItem转换为NamedItem
- NamedItem *namedItem = dynamic_cast<NamedItem*>(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<ResizableEllipse*>(item)) {
- tempColor1 = ellipse->onColor();
- tempColor2 = ellipse->offColor();
- form->addRow("ON颜色:", colorBtn1);
- form->addRow("OFF颜色:", colorBtn2);
- } else if (auto rect = dynamic_cast<ResizableRectangle*>(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<ResizableEllipse*>(item)) {
- ellipse->setOnColor(tempColor1);
- ellipse->setOffColor(tempColor2);
- } else if (auto rect = dynamic_cast<ResizableRectangle*>(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<QAbstractGraphicsShapeItem*>(item))
- {
- stream << shape->pen();
- stream << shape->brush();
- stream << shape->boundingRect();
- }
- // 具体图形信息
- if (auto rect = dynamic_cast<ResizableRectangle*>(item))
- {
- stream << rect->rect();
- stream << rect->pressedColor();
- stream << rect->releasedColor();
- stream << rect->name();
- } else if (auto ellipse = dynamic_cast<ResizableEllipse*>(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<ResizableEllipse*>(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<ResizableRectangle*>(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); }
|