diff --git a/IntegratedPlatform/hmiwidget.cpp b/IntegratedPlatform/hmiwidget.cpp index 8d0c559..10fa273 100644 --- a/IntegratedPlatform/hmiwidget.cpp +++ b/IntegratedPlatform/hmiwidget.cpp @@ -11,13 +11,15 @@ ResizableShape::ResizableShape(qreal x, qreal y, qreal w, qreal h) this->setFlag(QGraphicsItem::ItemIsSelectable, true); this->setAcceptHoverEvents(true); } - template void ResizableShape::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { - if (isInResizeArea(event->pos())) { + if (isInResizeArea(event->pos())) + { this->setCursor(Qt::SizeFDiagCursor); - } else { + } + else + { this->setCursor(Qt::SizeAllCursor); } BaseShape::hoverMoveEvent(event); @@ -34,10 +36,13 @@ bool ResizableShape::isInResizeArea(const QPointF &pos) const template void ResizableShape::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (isInResizeArea(event->pos())) { + if (isInResizeArea(event->pos())) + { resizing = true; this->setCursor(Qt::SizeFDiagCursor); - } else { + } + else + { resizing = false; this->setCursor(Qt::SizeAllCursor); BaseShape::mousePressEvent(event); @@ -47,11 +52,14 @@ void ResizableShape::mousePressEvent(QGraphicsSceneMouseEvent *event) template void ResizableShape::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - if (resizing) { + if (resizing) + { QRectF newRect = this->rect(); newRect.setBottomRight(event->pos()); this->setRect(newRect); - } else { + } + else + { BaseShape::mouseMoveEvent(event); } } diff --git a/untitled/basedocument.cpp b/untitled/basedocument.cpp new file mode 100644 index 0000000..0bd2745 --- /dev/null +++ b/untitled/basedocument.cpp @@ -0,0 +1,24 @@ +#include "basedocument.h" + +BaseDocument::BaseDocument(DocumentType type, QWidget *parent) + : QWidget(parent), m_type(type) {} + +BaseDocument::DocumentType BaseDocument::type() const { + return m_type; +} + +QString BaseDocument::filePath() const { + return m_filePath; +} + +void BaseDocument::setFilePath(const QString &path) { + m_filePath = path; +} + +bool BaseDocument::isModified() const { + return m_modified; +} + +void BaseDocument::setModified(bool modified) { + m_modified = modified; +} diff --git a/untitled/basedocument.h b/untitled/basedocument.h new file mode 100644 index 0000000..d7f9e78 --- /dev/null +++ b/untitled/basedocument.h @@ -0,0 +1,32 @@ +#ifndef BASEDOCUMENT_H +#define BASEDOCUMENT_H + +#include +#include + +class BaseDocument : public QWidget +{ + Q_OBJECT +public: + enum DocumentType { HMI, PLC }; + BaseDocument(DocumentType type, QWidget *parent = nullptr); + virtual ~BaseDocument() = default; + + DocumentType type() const; + virtual QString title() const = 0; + virtual QString filePath() const; + virtual void setFilePath(const QString &path); + virtual bool isModified() const; + virtual void setModified(bool modified); + + // 保存/加载接口 + virtual bool saveToFile(const QString &filePath) = 0; + virtual bool loadFromFile(const QString &filePath) = 0; + +protected: + DocumentType m_type; + QString m_filePath; + bool m_modified = false; +}; + +#endif // BASEDOCUMENT_H diff --git a/untitled/document.cpp b/untitled/document.cpp index 3bbb386..8af041e 100644 --- a/untitled/document.cpp +++ b/untitled/document.cpp @@ -16,7 +16,7 @@ #include #include #include - +#include HMIDocument::HMIDocument(QWidget *parent) : BaseDocument(HMI, parent), m_title("未命名HMI"), @@ -28,6 +28,24 @@ HMIDocument::HMIDocument(QWidget *parent) m_scene = new QGraphicsScene(this); m_scene->setSceneRect(0, 0, 800, 600); m_scene->setBackgroundBrush(QBrush(Qt::lightGray)); + // 更粗的网格线示例 +// QImage gridImage(40, 40, QImage::Format_ARGB32); +// gridImage.fill(Qt::white); + +// QPainter painter(&gridImage); +// painter.setPen(QPen(QColor(200, 200, 200), 1)); // 浅灰色 + +// // 绘制水平线 +// painter.drawLine(0, 39, 39, 39); +// // 绘制垂直线 +// painter.drawLine(39, 0, 39, 39); + +// // 每隔4个小网格绘制一条稍粗的线 +// painter.setPen(QPen(QColor(180, 180, 180), 1.5)); +// painter.drawLine(0, 39, 39, 39); +// painter.drawLine(39, 0, 39, 39); + +// m_scene->setBackgroundBrush(QBrush(gridImage)); // 创建视图 m_view = new QGraphicsView(m_scene, this); @@ -53,16 +71,14 @@ 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()); + showContextMenu(mouseEvent->globalPos());// 右键菜单 return true; } if (mouseEvent->button() == Qt::LeftButton) @@ -72,42 +88,43 @@ bool HMIDocument::eventFilter(QObject *obj, QEvent *event) // 如果点击空白区域且有绘制标志,创建图形 if (!item) { - if (m_canDrawEllipse) { - createEllipse(scenePos); + if (m_canDrawEllipse) + { + createShape("ellipse", scenePos); resetDrawFlags(); - } else if (m_canDrawRectangle) { - createRectangle(scenePos); + } + else if (m_canDrawRectangle) + { + createShape("rectangle", scenePos); resetDrawFlags(); } - m_scene->clearSelection(); // 清除选择 - return true; } } } // 拖拽事件(从工具栏拖拽绘制) - if (event->type() == QEvent::DragEnter) { + if (event->type() == QEvent::DragEnter) + { QDragEnterEvent *dragEvent = static_cast(event); dragEvent->acceptProposedAction(); return true; } - if (event->type() == QEvent::DragMove) { + if (event->type() == QEvent::DragMove) + { static_cast(event)->acceptProposedAction(); return true; } - if (event->type() == QEvent::Drop) { + if (event->type() == QEvent::Drop) + { QDropEvent *dropEvent = static_cast(event); - const QMimeData *mimeData = dropEvent->mimeData(); // 显式获取mimeData + const QMimeData *mimeData = dropEvent->mimeData(); QPointF scenePos = m_view->mapToScene(dropEvent->pos()); // 使用QMimeData - if (mimeData && mimeData->hasText()) { - QString type = mimeData->text(); - if (type == "指示灯") { - createEllipse(scenePos); - } else if (type == "按钮") { - createRectangle(scenePos); - } + if (mimeData && mimeData->hasText()) + { + createShape(mimeData->text(), scenePos); + resetDrawFlags();//重置防止拖拽后再次点击生成 } dropEvent->acceptProposedAction(); return true; @@ -116,26 +133,24 @@ bool HMIDocument::eventFilter(QObject *obj, QEvent *event) return QWidget::eventFilter(obj, event); } -// 创建椭圆(指示灯) -void HMIDocument::createEllipse(const QPointF &pos) -{ - 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); -} - -// 创建矩形(按钮) -void HMIDocument::createRectangle(const QPointF &pos) +void HMIDocument::createShape(const QString& type, const QPointF &pos) { - 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); + 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() { @@ -152,12 +167,15 @@ void HMIDocument::showContextMenu(QPoint globalPos) QMenu menu(this); - if (!clickedItem) { + if (!clickedItem) + { // 空白区域:仅显示粘贴 QAction *pasteAction = menu.addAction("粘贴"); pasteAction->setEnabled(!m_copiedItemsData.isEmpty()); connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems); - } else { + } + else + { // 选中图形:显示完整菜单 QAction *propAction = menu.addAction("属性"); QAction *copyAction = menu.addAction("复制"); @@ -200,10 +218,12 @@ void HMIDocument::copySelectedItems() QList selectedItems = m_scene->selectedItems(); if (selectedItems.isEmpty()) return; - foreach (QGraphicsItem *item, selectedItems) { + foreach (QGraphicsItem *item, selectedItems) + { m_copiedItemsData.append(serializeItem(item)); } m_lastPastePos = selectedItems.first()->pos(); + setModified(true); } // 粘贴项 @@ -215,14 +235,17 @@ void HMIDocument::pasteItems() m_lastPastePos += offset; m_scene->clearSelection(); - foreach (const QByteArray &itemData, m_copiedItemsData) { + foreach (const QByteArray &itemData, m_copiedItemsData) + { QGraphicsItem *newItem = deserializeItem(itemData); - if (newItem) { + if (newItem) + { m_scene->addItem(newItem); newItem->setPos(m_lastPastePos); newItem->setSelected(true); } } + setModified(true); } // 删除选中项 @@ -233,6 +256,7 @@ void HMIDocument::deleteSelectedItems() m_scene->removeItem(item); delete item; } + setModified(true); } // 显示属性对话框 @@ -291,7 +315,7 @@ void HMIDocument::showItemProperties() }); // 布局组装 - form->addRow("名称:", nameEdit); + form->addRow("对象:", nameEdit); QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); form->addRow(btnBox); connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); @@ -299,6 +323,7 @@ void HMIDocument::showItemProperties() // 应用属性 if (dialog.exec() == QDialog::Accepted) { + setModified(true); // 使用NamedItem接口设置名称 namedItem->setName(nameEdit->text()); @@ -325,27 +350,27 @@ QByteArray HMIDocument::serializeItem(QGraphicsItem *item) stream << item->flags(); stream << item->transform(); stream << item->isVisible(); - // 形状信息 - if (auto shape = dynamic_cast(item)) { + if (auto shape = dynamic_cast(item)) + { stream << shape->pen(); stream << shape->brush(); stream << shape->boundingRect(); } - // 具体图形信息 - if (auto rect = dynamic_cast(item)) { + 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)) { + } else if (auto ellipse = dynamic_cast(item)) + { stream << ellipse->rect(); stream << ellipse->onColor(); stream << ellipse->offColor(); stream << ellipse->name(); } - return data; } @@ -406,3 +431,223 @@ QGraphicsItem *HMIDocument::deserializeItem(const QByteArray &data) 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; +} diff --git a/untitled/document.h b/untitled/document.h deleted file mode 100644 index 8c0a012..0000000 --- a/untitled/document.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef DOCUMENT_H -#define DOCUMENT_H - -#include -#include -#include -#include -#include -#include - -// 前向声明 -class ResizableRectangle; -class ResizableEllipse; -class NamedItem; - -// 文档基类 -class BaseDocument : public QWidget -{ - Q_OBJECT -public: - enum DocumentType { HMI, PLC }; - BaseDocument(DocumentType type, QWidget *parent = nullptr) - : QWidget(parent), m_type(type) {} - virtual ~BaseDocument() = default; - DocumentType type() const { return m_type; } - virtual QString title() const = 0; - -protected: - DocumentType m_type; -}; - -// HMI文档类(包含绘图相关核心功能) -class HMIDocument : public BaseDocument -{ - Q_OBJECT -public: - HMIDocument(QWidget *parent = nullptr); - ~HMIDocument() override; - - QString title() const override { return m_title; } - void setTitle(const QString &title) { m_title = title; } - QGraphicsView *view() const { return m_view; } - QGraphicsScene *scene() const { return m_scene; } - - // 绘图控制 - void setDrawEllipse(bool enable) { m_canDrawEllipse = enable; } - void setDrawRectangle(bool enable) { m_canDrawRectangle = enable; } - - // 编辑功能 - void copySelectedItems(); - void pasteItems(); - void deleteSelectedItems(); - void showItemProperties(); - -signals: - void titleChanged(const QString &title); - -protected: - bool eventFilter(QObject *obj, QEvent *event) override; - -private: - // 绘图相关 - void createEllipse(const QPointF &pos); - void createRectangle(const QPointF &pos); - void resetDrawFlags(); - void showContextMenu(QPoint globalPos); - - // 图形项序列化/反序列化 - QByteArray serializeItem(QGraphicsItem *item); - QGraphicsItem *deserializeItem(const QByteArray &data); - - QString m_title; - QGraphicsScene *m_scene; - QGraphicsView *m_view; - bool m_canDrawEllipse; - bool m_canDrawRectangle; - - // 复制粘贴相关 - QList m_copiedItemsData; - QPointF m_lastPastePos; -}; - -// PLC文档类(预留,暂为空实现) -class PLCDocument : public BaseDocument -{ - Q_OBJECT -public: - PLCDocument(QWidget *parent = nullptr) : BaseDocument(PLC, parent) {} - QString title() const override { return "PLC文档"; } -}; - -#endif // DOCUMENT_H diff --git a/untitled/graphicsitems.cpp b/untitled/graphicsitems.cpp index 28f7e29..c6afd3c 100644 --- a/untitled/graphicsitems.cpp +++ b/untitled/graphicsitems.cpp @@ -1,26 +1,147 @@ -#include "graphicsitems.h" +#ifndef GRAPHICSITEMS_H +#define GRAPHICSITEMS_H + +#include +#include +#include +#include #include +#include +#include +#include +#include + +// 命名项接口(用于属性设置) +class NamedItem +{ +public: + virtual QString name() const = 0; + virtual void setName(const QString &name) = 0; + virtual ~NamedItem() = default; +}; + +// 可调整大小的图形基类(模板) +template +class ResizableShape : public BaseShape, public NamedItem +{ +protected: + bool m_resizing; + QString m_name; + Qt::CursorShape m_currentCursor; + +public: + ResizableShape(qreal x, qreal y, qreal w, qreal h) + : BaseShape(x, y, w, h), m_resizing(false), m_currentCursor(Qt::SizeAllCursor) + { + this->setFlag(QGraphicsItem::ItemIsMovable, true); + this->setFlag(QGraphicsItem::ItemIsSelectable, true); + this->setAcceptHoverEvents(true); + } + + // 事件处理(调整大小逻辑) + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override + { + if (isInResizeArea(event->pos())) + { + m_currentCursor = Qt::SizeFDiagCursor; + this->setCursor(Qt::SizeFDiagCursor); + } else { + m_currentCursor = Qt::SizeAllCursor; + } + BaseShape::hoverMoveEvent(event); + } + + void mousePressEvent(QGraphicsSceneMouseEvent *event) override + { + if (isInResizeArea(event->pos())) { + m_resizing = true; + m_currentCursor = Qt::SizeFDiagCursor; + this->setCursor(Qt::SizeFDiagCursor); + } else { + m_resizing = false; + m_currentCursor = Qt::SizeAllCursor; + BaseShape::mousePressEvent(event); + } + } + + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override + { + if (m_resizing) { + QRectF newRect = this->rect(); + newRect.setBottomRight(event->pos()); + this->setRect(newRect); + } else { + BaseShape::mouseMoveEvent(event); + } + } + + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override + { + m_resizing = false; + m_currentCursor = Qt::SizeAllCursor; + this->setCursor(Qt::SizeFDiagCursor); + BaseShape::mouseReleaseEvent(event); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override + { + BaseShape::paint(painter, option, widget); + } -// 可调整大小的矩形(按钮)实现 -ResizableRectangle::ResizableRectangle(qreal x, qreal y, qreal w, qreal h) - : ResizableShape(x, y, w, h) + // 命名项接口实现 + QString name() const override { return m_name; } + void setName(const QString &name) override { m_name = name; } + + // 光标获取方法 + Qt::CursorShape currentCursor() const { return m_currentCursor; } + +protected: + bool isInResizeArea(const QPointF &pos) const + { + QRectF r = this->rect(); + QRectF resizeArea(r.bottomRight() - QPointF(50, 50), r.bottomRight()); + return resizeArea.contains(pos); + } +}; + +// 可调整大小的矩形(按钮) +class ResizableRectangle : public ResizableShape { - m_name = "按钮"; - m_pressedColor = Qt::green; - m_releasedColor = Qt::yellow; - this->setBrush(m_releasedColor); -} - -// 可调整大小的椭圆(指示灯)实现 -ResizableEllipse::ResizableEllipse(qreal x, qreal y, qreal w, qreal h) - : ResizableShape(x, y, w, h) +public: + ResizableRectangle(qreal x, qreal y, qreal w, qreal h); + + QColor pressedColor() const { return m_pressedColor; } + void setPressedColor(const QColor &color) { m_pressedColor = color; } + + QColor releasedColor() const { return m_releasedColor; } + void setReleasedColor(const QColor &color) { + m_releasedColor = color; + this->setBrush(m_releasedColor); // 更新显示 + } + +private: + QColor m_pressedColor; // 按下颜色 + QColor m_releasedColor; // 释放颜色 +}; + +// 可调整大小的椭圆(指示灯) +class ResizableEllipse : public ResizableShape { - m_name = "指示灯"; - m_onColor = Qt::green; - m_offColor = Qt::red; - this->setBrush(m_onColor); -} - -// 显式实例化模板类 -template class ResizableShape; -template class ResizableShape; +public: + ResizableEllipse(qreal x, qreal y, qreal w, qreal h); + + QColor onColor() const { return m_onColor; } + void setOnColor(const QColor &color) { + m_onColor = color; + this->setBrush(m_onColor); // 更新显示 + } + + QColor offColor() const { return m_offColor; } + void setOffColor(const QColor &color) { m_offColor = color; } + +private: + QColor m_onColor; // 开状态颜色 + QColor m_offColor; // 关状态颜色 +}; + +#endif // GRAPHICSITEMS_H diff --git a/untitled/graphicsitems.h b/untitled/graphicsitems.h index 6e5fd61..f892c2e 100644 --- a/untitled/graphicsitems.h +++ b/untitled/graphicsitems.h @@ -27,24 +27,35 @@ class ResizableShape : public BaseShape, public NamedItem protected: bool m_resizing; QString m_name; - Qt::CursorShape m_currentCursor; - + QPointF m_initialPos; // 用于存储调整开始时的位置 + // 获取右下角调整区域 + QRectF getResizeArea() const + { + QRectF r = this->rect(); + return QRectF(r.bottomRight() - QPointF(20, 20), r.bottomRight()); + } + // 检测是否在右下角调整区域 + bool isInResizeArea(const QPointF &pos) const + { + return getResizeArea().contains(pos); + } public: ResizableShape(qreal x, qreal y, qreal w, qreal h) - : BaseShape(x, y, w, h), m_resizing(false), m_currentCursor(Qt::SizeAllCursor) + : BaseShape(x, y, w, h), m_resizing(false) { this->setFlag(QGraphicsItem::ItemIsMovable, true); this->setFlag(QGraphicsItem::ItemIsSelectable, true); this->setAcceptHoverEvents(true); + this->setCursor(Qt::SizeAllCursor); // 默认光标为移动光标 } // 事件处理(调整大小逻辑) void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override { if (isInResizeArea(event->pos())) { - m_currentCursor = Qt::SizeFDiagCursor; + this->setCursor(Qt::SizeFDiagCursor); // 右下角显示缩放光标 } else { - m_currentCursor = Qt::SizeAllCursor; + this->setCursor(Qt::SizeAllCursor); // 其他区域显示移动光标 } BaseShape::hoverMoveEvent(event); } @@ -53,10 +64,12 @@ public: { if (isInResizeArea(event->pos())) { m_resizing = true; - m_currentCursor = Qt::SizeFDiagCursor; - } else { + m_initialPos = event->pos(); // 存储初始位置 + this->setCursor(Qt::SizeFDiagCursor); + } + else + { m_resizing = false; - m_currentCursor = Qt::SizeAllCursor; BaseShape::mousePressEvent(event); } } @@ -64,9 +77,22 @@ public: void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override { if (m_resizing) { + // 计算尺寸变化量 + QPointF delta = event->pos() - m_initialPos; QRectF newRect = this->rect(); - newRect.setBottomRight(event->pos()); + + // 保持左上角不变,调整右下角 + newRect.setWidth(newRect.width() + delta.x()); + newRect.setHeight(newRect.height() + delta.y()); + + // 设置最小尺寸 + if (newRect.width() < 20) newRect.setWidth(20); + if (newRect.height() < 20) newRect.setHeight(20); + this->setRect(newRect); + this->update(); + + m_initialPos = event->pos(); // 更新初始位置 } else { BaseShape::mouseMoveEvent(event); } @@ -74,8 +100,16 @@ public: void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override { - m_resizing = false; - m_currentCursor = Qt::SizeAllCursor; + if (m_resizing) { + m_resizing = false; + + // 根据位置恢复光标 + if (isInResizeArea(event->pos())) { + this->setCursor(Qt::SizeFDiagCursor); // 仍在调整区域 + } else { + this->setCursor(Qt::SizeAllCursor); // 离开调整区域 + } + } BaseShape::mouseReleaseEvent(event); } @@ -87,24 +121,19 @@ public: // 命名项接口实现 QString name() const override { return m_name; } void setName(const QString &name) override { m_name = name; } - - // 光标获取方法 - Qt::CursorShape currentCursor() const { return m_currentCursor; } - -protected: - bool isInResizeArea(const QPointF &pos) const - { - QRectF r = this->rect(); - QRectF resizeArea(r.bottomRight() - QPointF(20, 20), r.bottomRight()); - return resizeArea.contains(pos); - } }; // 可调整大小的矩形(按钮) class ResizableRectangle : public ResizableShape { public: - ResizableRectangle(qreal x, qreal y, qreal w, qreal h); + ResizableRectangle(qreal x, qreal y, qreal w, qreal h) + : ResizableShape(x, y, w, h) + { + m_pressedColor = Qt::darkYellow; + m_releasedColor = Qt::yellow; + this->setBrush(m_releasedColor); + } QColor pressedColor() const { return m_pressedColor; } void setPressedColor(const QColor &color) { m_pressedColor = color; } @@ -124,7 +153,13 @@ private: class ResizableEllipse : public ResizableShape { public: - ResizableEllipse(qreal x, qreal y, qreal w, qreal h); + ResizableEllipse(qreal x, qreal y, qreal w, qreal h) + : ResizableShape(x, y, w, h) + { + m_onColor = Qt::green; + m_offColor = Qt::red; + this->setBrush(m_offColor); + } QColor onColor() const { return m_onColor; } void setOnColor(const QColor &color) { diff --git a/untitled/hmidocument.cpp b/untitled/hmidocument.cpp new file mode 100644 index 0000000..fd8fd84 --- /dev/null +++ b/untitled/hmidocument.cpp @@ -0,0 +1,651 @@ +#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) +{ + 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); } diff --git a/untitled/hmidocument.h b/untitled/hmidocument.h new file mode 100644 index 0000000..edb698b --- /dev/null +++ b/untitled/hmidocument.h @@ -0,0 +1,74 @@ +#ifndef HMIDOCUMENT_H +#define HMIDOCUMENT_H + +#include "basedocument.h" +#include +#include +#include + +// 前向声明 +class ResizableRectangle; +class ResizableEllipse; +class NamedItem; + +class HMIDocument : public BaseDocument +{ + Q_OBJECT +public: + explicit HMIDocument(QWidget *parent = nullptr); + ~HMIDocument() override; + + QString title() const override; + void setTitle(const QString &title); + QGraphicsView *view() const; + QGraphicsScene *scene() const; + + // 绘图控制 + void setDrawEllipse(bool enable); + void setDrawRectangle(bool enable); + + // 编辑功能 + void copySelectedItems(); + void pasteItems(); + void deleteSelectedItems(); + void showItemProperties(); + void startDrawingEllipse(); + void startDrawingRectangle(); + + // 保存/加载 + bool saveToFile(const QString &filePath) override; + bool loadFromFile(const QString &filePath) override; + + void markModified(); + +signals: + void titleChanged(const QString &title); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + // 辅助方法 + void createShape(const QString& type, const QPointF &pos); + void resetDrawFlags(); + void showContextMenu(QPoint globalPos); + + // 序列化相关 + QByteArray serializeItem(QGraphicsItem *item); + QGraphicsItem *deserializeItem(const QByteArray &data); + QJsonObject itemToJson(QGraphicsItem *item); + QGraphicsItem *jsonToItem(const QJsonObject &json); + + // 成员变量 + QString m_title; + QGraphicsScene *m_scene; + QGraphicsView *m_view; + bool m_canDrawEllipse; + bool m_canDrawRectangle; + + // 复制粘贴缓冲区 + QList m_copiedItemsData; + QPointF m_lastPastePos; +}; + +#endif // HMIDOCUMENT_H diff --git a/untitled/images/大于号.png b/untitled/images/大于号.png new file mode 100644 index 0000000..fbc0ea7 Binary files /dev/null and b/untitled/images/大于号.png differ diff --git a/untitled/images/大于等于.png b/untitled/images/大于等于.png index 2aba72f..dafae93 100644 Binary files a/untitled/images/大于等于.png and b/untitled/images/大于等于.png differ diff --git a/untitled/images/小于号.png b/untitled/images/小于号.png new file mode 100644 index 0000000..c980b92 Binary files /dev/null and b/untitled/images/小于号.png differ diff --git a/untitled/images/小于等于.png b/untitled/images/小于等于.png new file mode 100644 index 0000000..dfa037e Binary files /dev/null and b/untitled/images/小于等于.png differ diff --git a/untitled/mainwindow.cpp b/untitled/mainwindow.cpp index d37a4b6..cca8cf4 100644 --- a/untitled/mainwindow.cpp +++ b/untitled/mainwindow.cpp @@ -1,4 +1,6 @@ #include "mainwindow.h" +#include "hmidocument.h" +#include "plcdocument.h" #include #include #include @@ -10,19 +12,24 @@ #include #include #include +#include +#include +#include +#include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setWindowTitle("综合平台编程器"); - setGeometry(100, 100, 1000, 700); + setGeometry(500, 200, 1000, 700); // 初始化标签页 m_tabWidget = new QTabWidget(this); m_tabWidget->setTabsClosable(true); setCentralWidget(m_tabWidget); connect(m_tabWidget, &QTabWidget::currentChanged, this, &MainWindow::onTabChanged); - + connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::onCloseTab); // 创建菜单和工具栏 createMenus(); createToolbars(); @@ -34,29 +41,45 @@ MainWindow::~MainWindow() } // 创建菜单栏 -// 在createMenus()函数中添加以下代码,用于创建操作菜单及其功能 - void MainWindow::createMenus() { - // 创建文件菜单(保持原有代码) + // 创建文件菜单 QMenu *fileMenu = menuBar()->addMenu("文件"); QMenu *editMenu = menuBar()->addMenu("操作"); - QMenu *simulationMenu = menuBar()->addMenu("仿真"); - // 新建HMI动作(保持原有代码) + // 新建HMI动作 QAction *newHmiAction = new QAction("新建HMI(&H)", this); newHmiAction->setShortcut(tr("Ctrl+H")); - newHmiAction->setStatusTip("创建新的HMI文档"); connect(newHmiAction, &QAction::triggered, this, &MainWindow::onNewHMI); fileMenu->addAction(newHmiAction); - // 新建PLC动作(保持原有代码) + // 新建PLC动作 QAction *newPlcAction = new QAction("新建PLC(&P)", this); newPlcAction->setShortcut(tr("Ctrl+P")); - newPlcAction->setStatusTip("创建新的PLC文档"); connect(newPlcAction, &QAction::triggered, this, &MainWindow::onNewPLC); fileMenu->addAction(newPlcAction); + // 打开动作 + m_openAction = new QAction("打开(&O)", this); + m_openAction->setShortcut(QKeySequence::Open); + m_openAction->setStatusTip("打开现有文档"); + connect(m_openAction, &QAction::triggered, this, &MainWindow::onOpen); + fileMenu->addAction(m_openAction); + + // 保存动作 + m_saveAction = new QAction("保存(&S)", this); + m_saveAction->setShortcut(QKeySequence::Save); + m_saveAction->setStatusTip("保存当前文档"); + connect(m_saveAction, &QAction::triggered, this, &MainWindow::onSave); + fileMenu->addAction(m_saveAction); + + // 另存为动作 + m_saveAsAction = new QAction("另存为(&A)", this); + m_saveAsAction->setShortcut(QKeySequence::SaveAs); + m_saveAsAction->setStatusTip("将文档另存为"); + connect(m_saveAsAction, &QAction::triggered, this, &MainWindow::onSaveAs); + fileMenu->addAction(m_saveAsAction); + // 操作菜单 - 添加复制、粘贴、删除功能 QAction *copyAction = new QAction("复制(&C)", this); copyAction->setShortcut(QKeySequence::Copy); // 标准复制快捷键 Ctrl+C @@ -92,87 +115,278 @@ void MainWindow::createMenus() editMenu->addAction(deleteAction); } - // 创建左侧工具栏 void MainWindow::createToolbars() { - m_leftToolBar = new QToolBar("绘图工具", this); + m_leftToolBar = new QToolBar("绘图工具栏", this); addToolBar(Qt::LeftToolBarArea, m_leftToolBar); m_leftToolBar->setAllowedAreas(Qt::LeftToolBarArea); // 仅允许在左侧 + m_leftToolBar->setFixedWidth(200); } // 更新工具栏(根据当前文档类型) void MainWindow::updateToolBar(BaseDocument *doc) { - // 清空现有工具 - m_leftToolBar->clear(); - + m_leftToolBar->clear();// 清空现有工具 if (!doc) return; - - // HMI文档显示绘图工具 - if (doc->type() == BaseDocument::HMI) + if (doc->type() == BaseDocument::HMI)// HMI文档显示绘图工具 { HMIDocument *hmiDoc = dynamic_cast(doc); if (!hmiDoc) return; - // 画指示灯按钮(支持拖拽) QToolButton *ellipseBtn = new QToolButton; ellipseBtn->setText("指示灯"); - ellipseBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - ellipseBtn->setIcon(QIcon("../two/untitled/images/灯泡.png")); // 可替换为实际图标 - ellipseBtn->setMinimumSize(120, 120); - connect(ellipseBtn, &QToolButton::clicked, [=]() { - hmiDoc->setDrawEllipse(true); - hmiDoc->setDrawRectangle(false); - }); - - // 为按钮安装事件过滤器处理拖拽 - ellipseBtn->installEventFilter(this); + ellipseBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + ellipseBtn->setIcon(QIcon("../two/untitled/images/灯泡.png"));//可替换为实际图标 + ellipseBtn->installEventFilter(this);//为按钮安装事件过滤器处理拖拽 ellipseBtn->setProperty("toolType", "指示灯"); m_leftToolBar->addWidget(ellipseBtn); + ellipseBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 12px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); // 画按钮按钮(支持拖拽) QToolButton *rectBtn = new QToolButton; - rectBtn->setText("按钮"); - rectBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - rectBtn->setIcon(QIcon("../two/untitled/images/按钮.png")); // 可替换为实际图标 - rectBtn->setMinimumSize(120, 120); - connect(rectBtn, &QToolButton::clicked, [=]() { - hmiDoc->setDrawRectangle(true); - hmiDoc->setDrawEllipse(false); - }); - - // 为按钮安装事件过滤器处理拖拽 + rectBtn->setText("按 钮"); + rectBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + rectBtn->setIcon(QIcon("../two/untitled/images/按钮.png"));//可替换为实际图标 rectBtn->installEventFilter(this); rectBtn->setProperty("toolType", "按钮"); m_leftToolBar->addWidget(rectBtn); + rectBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 15px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); } + // PLC按钮工具 + else if (doc->type() == BaseDocument::PLC) + { + // 常开触点按钮 + QToolButton *normallyOpenBtn = new QToolButton; + normallyOpenBtn->setText("常开触点"); + normallyOpenBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + normallyOpenBtn->setIcon(QIcon("../two/untitled/images/T-常开触点-01.png")); // 替换为实际图标 + normallyOpenBtn->installEventFilter(this); + normallyOpenBtn->setProperty("toolType", "常开触点"); + m_leftToolBar->addWidget(normallyOpenBtn); + normallyOpenBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 1px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); + + // 常闭触点按钮 + QToolButton *normallyClosedBtn = new QToolButton; + normallyClosedBtn->setText("常闭触点"); + normallyClosedBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + normallyClosedBtn->setIcon(QIcon("../two/untitled/images/T-常闭触点-01-01.png")); // 替换为实际图标 + normallyClosedBtn->installEventFilter(this); + normallyClosedBtn->setProperty("toolType", "常闭触点"); + m_leftToolBar->addWidget(normallyClosedBtn); + normallyClosedBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 1px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); + + // 大于按钮 + QToolButton *greaterThanBtn = new QToolButton; + greaterThanBtn->setText("大于"); + greaterThanBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + greaterThanBtn->setIcon(QIcon("../two/untitled/images/大于号.png")); // 替换为实际图标 + greaterThanBtn->installEventFilter(this); + greaterThanBtn->setProperty("toolType", "大于"); + m_leftToolBar->addWidget(greaterThanBtn); + greaterThanBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 20px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); + + // 大于等于按钮 + QToolButton *greaterThanEqualBtn = new QToolButton; + greaterThanEqualBtn->setText("大于等于"); + greaterThanEqualBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + greaterThanEqualBtn->setIcon(QIcon("../two/untitled/images/大于等于.png")); // 替换为实际图标 + greaterThanEqualBtn->installEventFilter(this); + greaterThanEqualBtn->setProperty("toolType", "大于等于"); + m_leftToolBar->addWidget(greaterThanEqualBtn); + greaterThanEqualBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 1px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); + + // 小于按钮 + QToolButton *lessThanBtn = new QToolButton; + lessThanBtn->setText("小于"); + lessThanBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + lessThanBtn->setIcon(QIcon("../two/untitled/images/小于号.png")); // 替换为实际图标 + lessThanBtn->installEventFilter(this); + lessThanBtn->setProperty("toolType", "小于"); + m_leftToolBar->addWidget(lessThanBtn); + lessThanBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 20px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); + + // 小于等于按钮 + QToolButton *lessThanEqualBtn = new QToolButton; + lessThanEqualBtn->setText("小于等于"); + lessThanEqualBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + lessThanEqualBtn->setIcon(QIcon("../two/untitled/images/小于等于.png")); // 替换为实际图标 + lessThanEqualBtn->installEventFilter(this); + lessThanEqualBtn->setProperty("toolType", "小于等于"); + m_leftToolBar->addWidget(lessThanEqualBtn); + lessThanEqualBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 1px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); - // PLC文档可添加自己的工具 - else if (doc->type() == BaseDocument::PLC) { - m_leftToolBar->addAction("常开触点"); - m_leftToolBar->addAction("常闭触点"); + // 线圈按钮 + QToolButton *coilBtn = new QToolButton; + coilBtn->setText("线圈"); + coilBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + coilBtn->setIcon(QIcon("../two/untitled/images/线-圈-圈.png")); // 替换为实际图标 + coilBtn->installEventFilter(this); + coilBtn->setProperty("toolType", "小于等于"); + m_leftToolBar->addWidget(coilBtn); + coilBtn->setStyleSheet(R"( + QToolButton { + margin: 2 auto; + background-color: #f0f0f0; + border-radius: 10px; + border: 1px solid #ccc; + padding: 10px 20px; + font-size: 18px; + font-weight: bold; + color: #333; + } + QToolButton:hover { + background-color: #e0e0e0; + } + )"); } } // 事件过滤器处理拖拽 bool MainWindow::eventFilter(QObject *obj, QEvent *event) { - // 检查是否是工具栏按钮的鼠标按下事件 QToolButton *toolBtn = qobject_cast(obj); if (toolBtn && event->type() == QEvent::MouseButtonPress) { QMouseEvent *me = static_cast(event); if (me->button() == Qt::LeftButton) { - // 获取工具类型 QString toolType = toolBtn->property("toolType").toString(); if (!toolType.isEmpty()) { - // 创建拖拽 + BaseDocument* currentDoc = dynamic_cast(m_tabWidget->currentWidget()); + if (currentDoc && currentDoc->type() == BaseDocument::HMI) { + HMIDocument* hmiDoc = static_cast(currentDoc); + + // 设置对应的绘制模式 + if (toolType == "指示灯") + { + hmiDoc->startDrawingEllipse(); + + } + else if (toolType == "按钮") + { + hmiDoc->startDrawingRectangle(); + } + } + + // 创建拖拽对象 QMimeData *mime = new QMimeData; mime->setText(toolType); - QDrag *drag = new QDrag(obj); + + QDrag *drag = new QDrag(toolBtn); drag->setMimeData(mime); drag->exec(Qt::CopyAction); return true; @@ -187,7 +401,7 @@ void MainWindow::onNewHMI() { m_hmiCount++; HMIDocument *doc = new HMIDocument; - doc->setTitle(QString("HMI文档 %1").arg(m_hmiCount)); + doc->setTitle("HMI文档 " + QString::number(m_hmiCount)); m_tabWidget->addTab(doc, doc->title()); m_tabWidget->setCurrentWidget(doc); updateToolBar(doc); // 更新工具栏为HMI工具 @@ -206,10 +420,125 @@ void MainWindow::onNewPLC() // 标签页切换时更新工具栏 void MainWindow::onTabChanged(int idx) { - if (idx < 0) { + if (idx < 0) + { updateToolBar(nullptr); return; } BaseDocument *doc = dynamic_cast(m_tabWidget->widget(idx)); updateToolBar(doc); } + +// 保存文档 +void MainWindow::onSave() +{ + BaseDocument *doc = dynamic_cast(m_tabWidget->currentWidget()); + if (!doc) return; + + if (doc->filePath().isEmpty()) { + saveDocumentAs(doc); + } else { + saveDocument(doc); + } +} + +// 另存为文档 +void MainWindow::onSaveAs() +{ + BaseDocument *doc = dynamic_cast(m_tabWidget->currentWidget()); + if (doc) { + saveDocumentAs(doc); + } +} + +// 打开文档 +void MainWindow::onOpen() +{ + QString filePath = QFileDialog::getOpenFileName( + this, + "打开文档", + "", + "HMI文档 (*.hmi);;PLC文档 (*.plc)" + ); + + if (filePath.isEmpty()) return; + + QFileInfo fileInfo(filePath); + BaseDocument *doc = nullptr; + + if (fileInfo.suffix().toLower() == "hmi") { + doc = new HMIDocument; + } else if (fileInfo.suffix().toLower() == "plc") { + doc = new PLCDocument; + } else { + QMessageBox::warning(this, "打开文档", "不支持的文件格式"); + return; + } + + if (doc->loadFromFile(filePath)) { + m_tabWidget->addTab(doc, doc->title()); + m_tabWidget->setCurrentWidget(doc); + updateToolBar(doc); + } else { + QMessageBox::critical(this, "打开文档", "无法加载文档"); + delete doc; + } +} + +// 关闭标签页 +void MainWindow::onCloseTab(int index) +{ + BaseDocument *doc = dynamic_cast(m_tabWidget->widget(index)); + if (!doc) return; + + if (doc->isModified()) + { + QMessageBox::StandardButton reply = QMessageBox::question( + this, + "保存文档", + QString("文档 '%1' 已修改,是否保存更改?").arg(doc->title()), + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel + ); + + if (reply == QMessageBox::Save) { + saveDocument(doc); + } else if (reply == QMessageBox::Cancel) { + return; + } + } + + m_tabWidget->removeTab(index); + delete doc; +} + +// 保存文档 +void MainWindow::saveDocument(BaseDocument *doc) +{ + if (doc->saveToFile(doc->filePath())) + { + doc->setModified(false); // 清除修改状态 + QMessageBox::information(this, "保存文档", "文档保存成功"); + } else { + QMessageBox::critical(this, "保存文档", "无法保存文档"); + } +} + +// 另存为文档 +void MainWindow::saveDocumentAs(BaseDocument *doc) +{ + QString filePath = QFileDialog::getSaveFileName( + this, + "另存为", + doc->filePath().isEmpty() ? doc->title() : doc->filePath(), + doc->type() == BaseDocument::HMI ?"HMI文档 (*.hmi)" :"PLC文档 (*.plc)" + ); + + if (filePath.isEmpty()) return; + + if (doc->saveToFile(filePath)) { + doc->setModified(false); // 清除修改状态 + QMessageBox::information(this, "保存文档", "文档保存成功"); + } else { + QMessageBox::critical(this, "保存文档", "无法保存文档"); + } +} diff --git a/untitled/mainwindow.h b/untitled/mainwindow.h index 2ecc0d3..8120c61 100644 --- a/untitled/mainwindow.h +++ b/untitled/mainwindow.h @@ -4,7 +4,8 @@ #include #include #include -#include "document.h" +#include +#include "basedocument.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -25,16 +26,27 @@ private slots: void onNewHMI(); // 新建HMI文档 void onNewPLC(); // 新建PLC文档 void onTabChanged(int idx); // 标签页切换时更新工具栏 + void onSave(); // 保存文档 + void onSaveAs(); // 另存为文档 + void onOpen(); // 打开文档 + void onCloseTab(int index); // 关闭标签页 private: void createMenus(); // 创建菜单栏 void createToolbars(); // 创建工具栏(左侧) void updateToolBar(BaseDocument *doc); // 根据文档类型更新工具栏 + void saveDocument(BaseDocument *doc); // 保存文档 + void saveDocumentAs(BaseDocument *doc); // 另存为文档 QTabWidget *m_tabWidget; // 多文档标签页 QToolBar *m_leftToolBar; // 左侧工具栏 int m_hmiCount = 0; // HMI文档计数器 int m_plcCount = 0; // PLC文档计数器 + + // 菜单动作 + QAction *m_saveAction; + QAction *m_saveAsAction; + QAction *m_openAction; }; #endif // MAINWINDOW_H diff --git a/untitled/plcdocument.cpp b/untitled/plcdocument.cpp new file mode 100644 index 0000000..d6362f3 --- /dev/null +++ b/untitled/plcdocument.cpp @@ -0,0 +1,88 @@ +#include "plcdocument.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PLCDocument::PLCDocument(QWidget *parent) + : BaseDocument(PLC, parent) +{ + // 创建绘图场景 + m_scene = new QGraphicsScene(this); + m_scene->setSceneRect(0, 0, 800, 600); + + // 创建网格背景 + createGridBackground(); + + // 创建视图 + m_view = new QGraphicsView(m_scene, this); + m_view->setRenderHint(QPainter::Antialiasing); + m_view->setDragMode(QGraphicsView::RubberBandDrag); + + // 布局(让视图占满文档区域) + auto layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_view); + setLayout(layout); +} + +PLCDocument::~PLCDocument() +{ + // 场景和视图由Qt自动销毁 +} + +QString PLCDocument::title() const { + return "PLC文档"; +} + +void PLCDocument::createGridBackground() +{ + // 创建网格图案 + createGridPattern(); + + // 设置场景背景 + QBrush gridBrush(m_gridPattern); + m_scene->setBackgroundBrush(gridBrush); +} + +void PLCDocument::createGridPattern() +{ + // 网格大小 + const int size = m_gridSize; + + // 创建网格图案 + m_gridPattern = QPixmap(size * 2, size * 2); + m_gridPattern.fill(Qt::white); + + QPainter painter(&m_gridPattern); + painter.setPen(QPen(QColor(220, 220, 220), 1)); + + // 绘制网格线 + painter.drawLine(0, size, size * 2, size); // 水平线 + painter.drawLine(size, 0, size, size * 2); // 垂直线 + + // 绘制网格交点 + painter.setPen(QPen(QColor(180, 180, 180), 1)); + painter.drawPoint(0, 0); + painter.drawPoint(size, size); + painter.drawPoint(0, size * 2); + painter.drawPoint(size * 2, 0); + painter.drawPoint(size * 2, size * 2); +} + +bool PLCDocument::saveToFile(const QString &filePath) +{ + +} + +bool PLCDocument::loadFromFile(const QString &filePath) +{ + +} diff --git a/untitled/plcdocument.h b/untitled/plcdocument.h new file mode 100644 index 0000000..2388e78 --- /dev/null +++ b/untitled/plcdocument.h @@ -0,0 +1,32 @@ +#ifndef PLCDOCUMENT_H +#define PLCDOCUMENT_H + +#include "basedocument.h" +#include +#include + +class PLCDocument : public BaseDocument +{ + Q_OBJECT +public: + explicit PLCDocument(QWidget *parent = nullptr); + ~PLCDocument() override; + + QString title() const override; + QGraphicsView *view() const { return m_view; } + QGraphicsScene *scene() const { return m_scene; } + + bool saveToFile(const QString &filePath) override; + bool loadFromFile(const QString &filePath) override; + +private: + void createGridBackground(); + void createGridPattern(); + + QGraphicsScene *m_scene; + QGraphicsView *m_view; + QPixmap m_gridPattern; // 网格图案缓存 + int m_gridSize = 20; // 网格大小(像素) +}; + +#endif // PLCDOCUMENT_H diff --git a/untitled/untitled.pro b/untitled/untitled.pro index af0ada3..bfde661 100644 --- a/untitled/untitled.pro +++ b/untitled/untitled.pro @@ -16,15 +16,19 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - document.cpp \ + basedocument.cpp \ graphicsitems.cpp \ + hmidocument.cpp \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + plcdocument.cpp HEADERS += \ - document.h \ + basedocument.h \ graphicsitems.h \ - mainwindow.h + hmidocument.h \ + mainwindow.h \ + plcdocument.h FORMS += \ mainwindow.ui