Kaynağa Gözat

HMI界面编辑完成,新建PLC进行绘图操作

main
email 2 gün önce
ebeveyn
işleme
309457dfc6
18 değiştirilmiş dosya ile 1814 ekleme ve 251 silme
  1. +15
    -7
      IntegratedPlatform/hmiwidget.cpp
  2. +24
    -0
      untitled/basedocument.cpp
  3. +32
    -0
      untitled/basedocument.h
  4. +297
    -52
      untitled/document.cpp
  5. +0
    -92
      untitled/document.h
  6. +143
    -22
      untitled/graphicsitems.cpp
  7. +59
    -24
      untitled/graphicsitems.h
  8. +651
    -0
      untitled/hmidocument.cpp
  9. +74
    -0
      untitled/hmidocument.h
  10. BIN
      untitled/images/大于号.png
  11. BIN
      untitled/images/大于等于.png
  12. BIN
      untitled/images/小于号.png
  13. BIN
      untitled/images/小于等于.png
  14. +378
    -49
      untitled/mainwindow.cpp
  15. +13
    -1
      untitled/mainwindow.h
  16. +88
    -0
      untitled/plcdocument.cpp
  17. +32
    -0
      untitled/plcdocument.h
  18. +8
    -4
      untitled/untitled.pro

+ 15
- 7
IntegratedPlatform/hmiwidget.cpp Dosyayı Görüntüle

@@ -11,13 +11,15 @@ ResizableShape<BaseShape>::ResizableShape(qreal x, qreal y, qreal w, qreal h)
this->setFlag(QGraphicsItem::ItemIsSelectable, true);
this->setAcceptHoverEvents(true);
}

template <typename BaseShape>
void ResizableShape<BaseShape>::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<BaseShape>::isInResizeArea(const QPointF &pos) const
template <typename BaseShape>
void ResizableShape<BaseShape>::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<BaseShape>::mousePressEvent(QGraphicsSceneMouseEvent *event)
template <typename BaseShape>
void ResizableShape<BaseShape>::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (resizing) {
if (resizing)
{
QRectF newRect = this->rect();
newRect.setBottomRight(event->pos());
this->setRect(newRect);
} else {
}
else
{
BaseShape::mouseMoveEvent(event);
}
}


+ 24
- 0
untitled/basedocument.cpp Dosyayı Görüntüle

@@ -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;
}

+ 32
- 0
untitled/basedocument.h Dosyayı Görüntüle

@@ -0,0 +1,32 @@
#ifndef BASEDOCUMENT_H
#define BASEDOCUMENT_H

#include <QWidget>
#include <QString>

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

+ 297
- 52
untitled/document.cpp Dosyayı Görüntüle

@@ -16,7 +16,7 @@
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QDebug>
#include<QFileInfo>
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<QMouseEvent*>(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<QDragEnterEvent*>(event);
dragEvent->acceptProposedAction();
return true;
}
if (event->type() == QEvent::DragMove) {
if (event->type() == QEvent::DragMove)
{
static_cast<QDragMoveEvent*>(event)->acceptProposedAction();
return true;
}
if (event->type() == QEvent::Drop) {
if (event->type() == QEvent::Drop)
{
QDropEvent *dropEvent = static_cast<QDropEvent*>(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<QGraphicsItem*> 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<QAbstractGraphicsShapeItem*>(item)) {
if (auto shape = dynamic_cast<QAbstractGraphicsShapeItem*>(item))
{
stream << shape->pen();
stream << shape->brush();
stream << shape->boundingRect();
}

// 具体图形信息
if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
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)) {
} else if (auto ellipse = dynamic_cast<ResizableEllipse*>(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<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;
}

+ 0
- 92
untitled/document.h Dosyayı Görüntüle

@@ -1,92 +0,0 @@
#ifndef DOCUMENT_H
#define DOCUMENT_H

#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QList>
#include <QByteArray>
#include <QPointF>

// 前向声明
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<QByteArray> 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

+ 143
- 22
untitled/graphicsitems.cpp Dosyayı Görüntüle

@@ -1,26 +1,147 @@
#include "graphicsitems.h"
#ifndef GRAPHICSITEMS_H
#define GRAPHICSITEMS_H

#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QString>
#include <QColor>
#include <QCursor>
#include <QGraphicsScene>

// 命名项接口(用于属性设置)
class NamedItem
{
public:
virtual QString name() const = 0;
virtual void setName(const QString &name) = 0;
virtual ~NamedItem() = default;
};

// 可调整大小的图形基类(模板)
template <typename BaseShape>
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<QGraphicsRectItem>(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<QGraphicsRectItem>
{
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<QGraphicsEllipseItem>(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<QGraphicsEllipseItem>
{
m_name = "指示灯";
m_onColor = Qt::green;
m_offColor = Qt::red;
this->setBrush(m_onColor);
}

// 显式实例化模板类
template class ResizableShape<QGraphicsRectItem>;
template class ResizableShape<QGraphicsEllipseItem>;
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

+ 59
- 24
untitled/graphicsitems.h Dosyayı Görüntüle

@@ -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<QGraphicsRectItem>
{
public:
ResizableRectangle(qreal x, qreal y, qreal w, qreal h);
ResizableRectangle(qreal x, qreal y, qreal w, qreal h)
: ResizableShape<QGraphicsRectItem>(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<QGraphicsEllipseItem>
{
public:
ResizableEllipse(qreal x, qreal y, qreal w, qreal h);
ResizableEllipse(qreal x, qreal y, qreal w, qreal h)
: ResizableShape<QGraphicsEllipseItem>(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) {


+ 651
- 0
untitled/hmidocument.cpp Dosyayı Görüntüle

@@ -0,0 +1,651 @@
#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)
{
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); }

+ 74
- 0
untitled/hmidocument.h Dosyayı Görüntüle

@@ -0,0 +1,74 @@
#ifndef HMIDOCUMENT_H
#define HMIDOCUMENT_H

#include "basedocument.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QJsonObject>

// 前向声明
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<QByteArray> m_copiedItemsData;
QPointF m_lastPastePos;
};

#endif // HMIDOCUMENT_H

BIN
untitled/images/大于号.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 200  |  Yükseklik: 200  |  Boyut: 4.5 KiB

BIN
untitled/images/大于等于.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 200  |  Yükseklik: 200  |  Boyut: 5.8 KiB Genişlik: 200  |  Yükseklik: 200  |  Boyut: 3.4 KiB

BIN
untitled/images/小于号.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 200  |  Yükseklik: 200  |  Boyut: 4.5 KiB

BIN
untitled/images/小于等于.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 200  |  Yükseklik: 200  |  Boyut: 3.4 KiB

+ 378
- 49
untitled/mainwindow.cpp Dosyayı Görüntüle

@@ -1,4 +1,6 @@
#include "mainwindow.h"
#include "hmidocument.h"
#include "plcdocument.h"
#include <QMenuBar>
#include <QAction>
#include <QToolButton>
@@ -10,19 +12,24 @@
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QDebug>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextEdit>

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<HMIDocument*>(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<QToolButton*>(obj);
if (toolBtn && event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (me->button() == Qt::LeftButton)
{
// 获取工具类型
QString toolType = toolBtn->property("toolType").toString();
if (!toolType.isEmpty())
{
// 创建拖拽
BaseDocument* currentDoc = dynamic_cast<BaseDocument*>(m_tabWidget->currentWidget());
if (currentDoc && currentDoc->type() == BaseDocument::HMI) {
HMIDocument* hmiDoc = static_cast<HMIDocument*>(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<BaseDocument*>(m_tabWidget->widget(idx));
updateToolBar(doc);
}

// 保存文档
void MainWindow::onSave()
{
BaseDocument *doc = dynamic_cast<BaseDocument*>(m_tabWidget->currentWidget());
if (!doc) return;

if (doc->filePath().isEmpty()) {
saveDocumentAs(doc);
} else {
saveDocument(doc);
}
}

// 另存为文档
void MainWindow::onSaveAs()
{
BaseDocument *doc = dynamic_cast<BaseDocument*>(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<BaseDocument*>(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, "保存文档", "无法保存文档");
}
}

+ 13
- 1
untitled/mainwindow.h Dosyayı Görüntüle

@@ -4,7 +4,8 @@
#include <QMainWindow>
#include <QTabWidget>
#include <QToolBar>
#include "document.h"
#include <QAction>
#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

+ 88
- 0
untitled/plcdocument.cpp Dosyayı Görüntüle

@@ -0,0 +1,88 @@
#include "plcdocument.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPainter>
#include <QBrush>
#include <QPen>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFileInfo>
#include <QVBoxLayout>

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)
{

}

+ 32
- 0
untitled/plcdocument.h Dosyayı Görüntüle

@@ -0,0 +1,32 @@
#ifndef PLCDOCUMENT_H
#define PLCDOCUMENT_H

#include "basedocument.h"
#include <QGraphicsScene>
#include <QGraphicsView>

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

+ 8
- 4
untitled/untitled.pro Dosyayı Görüntüle

@@ -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


Yükleniyor…
İptal
Kaydet