@@ -0,0 +1,262 @@ | |||||
#include "hmicontrolitem.h" | |||||
#include <QPainter> | |||||
#include <QGraphicsScene> | |||||
#include <QCursor> | |||||
#include <QMenu> | |||||
#include <QAction> | |||||
#include <QGraphicsView> | |||||
#include <QColorDialog> | |||||
#include <QDebug> | |||||
#include <QInputDialog> | |||||
#include "hmibutton.h" | |||||
#include "hmiled.h" | |||||
#include <QMessageBox> | |||||
// 静态剪贴板数据,初始化为空 | |||||
QVariantMap HmiControlItem::m_clipboardData; | |||||
// 构造函数:初始化控件基本属性 | |||||
HmiControlItem::HmiControlItem(QGraphicsItem* parent) | |||||
: QObject(nullptr), QGraphicsItem(parent), m_registerAddress(-1) | |||||
{ | |||||
// 设置控件属性:可移动、可选择、发送几何变化通知 | |||||
setFlags(QGraphicsItem::ItemIsMovable | | |||||
QGraphicsItem::ItemIsSelectable | | |||||
QGraphicsItem::ItemSendsGeometryChanges); | |||||
// 启用鼠标悬停事件 | |||||
setAcceptHoverEvents(true); | |||||
// 设置变换原点为控件左上角 | |||||
setTransformOriginPoint(0, 0); | |||||
} | |||||
// 检查鼠标是否在右下角缩放区域 | |||||
// 返回:true(在缩放区域内),false(不在) | |||||
bool HmiControlItem::isInScaleArea(const QPointF& pos) { | |||||
QRectF contentRect = boundingRect().adjusted(1, 1, -1, -1); | |||||
return (pos.x() >= contentRect.width() - m_scaleAreaSize && | |||||
pos.x() <= contentRect.width()) && | |||||
(pos.y() >= contentRect.height() - m_scaleAreaSize && | |||||
pos.y() <= contentRect.height()); | |||||
} | |||||
// 鼠标悬停事件处理:根据鼠标位置设置光标样式 | |||||
void HmiControlItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event) { | |||||
if (isSelected()) { | |||||
// 如果控件被选中 | |||||
if (isInScaleArea(event->pos())) { | |||||
// 鼠标在右下角缩放区域,显示缩放光标 | |||||
setCursor(Qt::SizeFDiagCursor); | |||||
} else { | |||||
// 鼠标在控件其他区域,显示移动光标 | |||||
setCursor(Qt::SizeAllCursor); | |||||
} | |||||
} else { | |||||
// 控件未选中,显示默认光标 | |||||
setCursor(Qt::ArrowCursor); | |||||
} | |||||
QGraphicsItem::hoverMoveEvent(event); | |||||
} | |||||
// 鼠标按下事件处理:开始拖动或缩放 | |||||
void HmiControlItem::mousePressEvent(QGraphicsSceneMouseEvent* event) { | |||||
if (isSelected() && isInScaleArea(event->pos())) { | |||||
// 选中控件且点击缩放区域,进入缩放模式 | |||||
m_isScaling = true; | |||||
m_originalRect = boundingRect().adjusted(1, 1, -1, -1); | |||||
m_scaleStartPos = event->pos(); | |||||
event->accept(); | |||||
return; | |||||
} | |||||
// 记录拖动开始位置 | |||||
m_dragStartPos = event->pos(); | |||||
QGraphicsItem::mousePressEvent(event); | |||||
} | |||||
// 鼠标移动事件处理:处理控件拖动或缩放 | |||||
void HmiControlItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { | |||||
if (m_isScaling) { | |||||
// 缩放模式:根据鼠标移动调整控件大小 | |||||
qreal deltaX = event->pos().x() - m_scaleStartPos.x(); | |||||
QRectF currentRect = transform().mapRect(m_originalRect); | |||||
qreal newWidth = currentRect.width() + deltaX; | |||||
newWidth = qMax(m_minWidth, qMin(newWidth, m_maxWidth)); | |||||
qreal aspectRatio = getAspectRatio(); | |||||
qreal newHeight = newWidth / aspectRatio; | |||||
newHeight = qMax(m_minHeight, qMin(newHeight, m_maxHeight)); | |||||
newWidth = newHeight * aspectRatio; | |||||
if (qAbs(newWidth - currentRect.width()) > 1 || | |||||
qAbs(newHeight - currentRect.height()) > 1) { | |||||
// 更新控件大小,应用缩放变换 | |||||
prepareGeometryChange(); | |||||
qreal scaleX = newWidth / m_originalRect.width(); | |||||
qreal scaleY = newHeight / m_originalRect.height(); | |||||
resetTransform(); | |||||
setTransform(QTransform::fromScale(scaleX, scaleY), false); | |||||
update(); | |||||
} | |||||
event->accept(); | |||||
return; | |||||
} | |||||
if (isSelected()) { | |||||
// 拖动模式:移动控件位置 | |||||
QPointF delta = event->pos() - m_dragStartPos; | |||||
QPointF newPos = pos() + delta; | |||||
QRectF itemRect = transform().mapRect(boundingRect()); | |||||
QRectF sceneRect = scene()->sceneRect(); | |||||
// 限制控件在场景边界内 | |||||
newPos.setX(qMax(sceneRect.left(), qMin(newPos.x(), sceneRect.right() - itemRect.width()))); | |||||
newPos.setY(qMax(sceneRect.top(), qMin(newPos.y(), sceneRect.bottom() - itemRect.height()))); | |||||
if (newPos.x() + itemRect.width() > sceneRect.right()) { | |||||
newPos.setX(sceneRect.right() - itemRect.width()); | |||||
} | |||||
if (newPos.y() + itemRect.height() > sceneRect.bottom()) { | |||||
newPos.setY(sceneRect.bottom() - itemRect.height()); | |||||
} | |||||
if (newPos.x() < sceneRect.left()) { | |||||
newPos.setX(sceneRect.left()); | |||||
} | |||||
if (newPos.y() < sceneRect.top()) { | |||||
newPos.setY(sceneRect.top()); | |||||
} | |||||
setPos(newPos); | |||||
if (scene()) { | |||||
// 更新场景和视图 | |||||
scene()->update(); | |||||
if (scene()->views().size() > 0) { | |||||
scene()->views().first()->viewport()->update(); | |||||
} | |||||
} | |||||
event->accept(); | |||||
return; | |||||
} | |||||
QGraphicsItem::mouseMoveEvent(event); | |||||
} | |||||
// 鼠标释放事件处理:结束缩放或拖动 | |||||
void HmiControlItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { | |||||
m_isScaling = false; | |||||
QGraphicsItem::mouseReleaseEvent(event); | |||||
} | |||||
// 右键菜单事件处理:显示控件特定的上下文菜单 | |||||
void HmiControlItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { | |||||
QMenu menu; | |||||
createContextMenu(menu); // 子类实现具体菜单项 | |||||
menu.exec(event->screenPos()); | |||||
event->accept(); | |||||
} | |||||
// 删除控件:从场景中移除并释放内存 | |||||
void HmiControlItem::deleteItem() { | |||||
if (scene()) { | |||||
scene()->removeItem(this); | |||||
} | |||||
delete this; | |||||
} | |||||
// 更改控件颜色:弹出颜色对话框选择ON或OFF状态颜色 | |||||
void HmiControlItem::changeAppearanceColor(bool isOnState, QColor& onColor, QColor& offColor) { | |||||
QColor newColor = QColorDialog::getColor( | |||||
Qt::white, | |||||
nullptr, | |||||
isOnState ? "选择ON状态颜色" : "选择OFF状态颜色" | |||||
); | |||||
if (newColor.isValid()) { | |||||
if (isOnState) { | |||||
onColor = newColor; | |||||
} else { | |||||
offColor = newColor; | |||||
} | |||||
update(); | |||||
} | |||||
} | |||||
// 设置寄存器绑定 | |||||
// 设置寄存器绑定 | |||||
bool HmiControlItem::setRegisterBinding(int address) { | |||||
if (address >= 0 && address <= 4000) { | |||||
m_registerAddress = address; | |||||
return true; | |||||
} else { | |||||
qDebug() << "Invalid register binding: address=" << address; | |||||
return false; | |||||
} | |||||
} | |||||
// 存储数据到剪贴板 | |||||
void HmiControlItem::copyToClipboard(const QVariantMap& data) { | |||||
m_clipboardData = data; | |||||
qDebug() << "Copied to clipboard:" << data; | |||||
} | |||||
// 获取剪贴板数据 | |||||
QVariantMap HmiControlItem::getClipboardData() { | |||||
return m_clipboardData; | |||||
} | |||||
// 检查剪贴板是否有数据 | |||||
bool HmiControlItem::hasClipboardData() { | |||||
return !m_clipboardData.isEmpty(); | |||||
} | |||||
// 从剪贴板数据创建控件,指定粘贴位置 | |||||
HmiControlItem* HmiControlItem::createFromClipboard(const QVariantMap& data, const QPointF& pos, QGraphicsItem* parent) { | |||||
QString type = data.value("type").toString(); | |||||
HmiControlItem* item = nullptr; | |||||
if (type == "按钮") { | |||||
// 创建按钮控件并恢复属性 | |||||
item = new HmiButton(parent); | |||||
HmiButton* button = static_cast<HmiButton*>(item); | |||||
button->setButtonState(data.value("isButtonOn", false).toBool()); | |||||
button->setOnColor(QColor(data.value("onColor").toString())); | |||||
button->setOffColor(QColor(data.value("offColor").toString())); | |||||
} else if (type == "指示灯") { | |||||
// 创建指示灯控件并恢复属性 | |||||
item = new HmiLed(parent); | |||||
HmiLed* led = static_cast<HmiLed*>(item); | |||||
led->setOnColor(QColor(data.value("onColor").toString())); | |||||
led->setOffColor(QColor(data.value("offColor").toString())); | |||||
} | |||||
if (item) { | |||||
// 设置粘贴位置 | |||||
item->setPos(pos); | |||||
// 恢复缩放 | |||||
qreal scaleX = data.value("scaleX", 1.0).toDouble(); | |||||
qreal scaleY = data.value("scaleY", 1.0).toDouble(); | |||||
item->setTransform(QTransform::fromScale(scaleX, scaleY)); | |||||
// 恢复寄存器绑定 | |||||
item->setRegisterBinding(data.value("registerAddress", -1).toInt()); | |||||
} | |||||
return item; | |||||
} | |||||
// 创建右键菜单(默认实现,子类可扩展) | |||||
void HmiControlItem::createContextMenu(QMenu& menu) { | |||||
// 默认添加"设置寄存器"选项 | |||||
QAction* setRegisterAction = new QAction("设置寄存器", &menu); | |||||
QObject::connect(setRegisterAction, &QAction::triggered, [this]() { | |||||
bool ok; | |||||
// 显示当前绑定的寄存器地址作为默认值 | |||||
int currentAddress = m_registerAddress >= 0 ? m_registerAddress : 0; | |||||
int address = QInputDialog::getInt(nullptr, | |||||
"输入寄存器地址", | |||||
QString("地址 (0-4000):\n当前地址: %1").arg(currentAddress), | |||||
currentAddress, 0, 4000, 1, &ok); | |||||
if (ok) { | |||||
if (!setRegisterBinding(address)) { | |||||
QMessageBox::warning(nullptr, "错误", "无效的寄存器地址"); | |||||
} | |||||
} | |||||
}); | |||||
menu.addAction(setRegisterAction); | |||||
} | |||||
// 存储控件数据到剪贴板(默认实现) | |||||
QVariantMap HmiControlItem::saveToClipboard() const | |||||
{ | |||||
QVariantMap data; | |||||
data["type"] = getItemType(); | |||||
data["posX"] = pos().x(); | |||||
data["posY"] = pos().y(); | |||||
data["scaleX"] = transform().m11(); | |||||
data["scaleY"] = transform().m22(); | |||||
data["registerAddress"] = m_registerAddress; | |||||
return data; | |||||
} |
@@ -0,0 +1,93 @@ | |||||
#pragma once | |||||
#include <QGraphicsItem> | |||||
#include <QGraphicsSceneMouseEvent> | |||||
#include <QGraphicsSceneHoverEvent> | |||||
#include <QGraphicsSceneContextMenuEvent> | |||||
#include <QColor> | |||||
#include <QVariantMap> | |||||
// HMI控件抽象基类,定义所有控件的通用接口和行为 | |||||
class HmiControlItem : public QObject, public QGraphicsItem { | |||||
Q_OBJECT | |||||
Q_INTERFACES(QGraphicsItem) // 添加此行以支持 QGraphicsItem 接口 | |||||
public: | |||||
// 构造函数,初始化控件基本属性(如可移动、可选择、接受鼠标悬停等) | |||||
explicit HmiControlItem(QGraphicsItem* parent = nullptr); | |||||
// 返回控件的边界矩形(纯虚函数,子类必须实现以定义控件的绘制区域) | |||||
virtual QRectF boundingRect() const override = 0; | |||||
// 绘制控件(纯虚函数,子类实现具体的绘制逻辑,如按钮或指示灯的外观) | |||||
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override = 0; | |||||
// 获取控件类型(用于剪贴板,子类返回特定类型,如“按钮”或“指示灯”) | |||||
virtual QString getItemType() const = 0; | |||||
// 存储控件数据到剪贴板(虚函数,子类实现,保存位置、缩放、状态等) | |||||
virtual QVariantMap saveToClipboard() const; | |||||
// 从剪贴板数据创建控件,允许指定粘贴位置 | |||||
// 参数:data(剪贴板数据),pos(粘贴位置),parent(父项) | |||||
static HmiControlItem* createFromClipboard(const QVariantMap& data, const QPointF& pos, QGraphicsItem* parent = nullptr); | |||||
// 删除控件,从场景中移除并释放内存 | |||||
void deleteItem(); | |||||
// 静态剪贴板操作 | |||||
// 存储控件数据到静态剪贴板 | |||||
static void copyToClipboard(const QVariantMap& data); | |||||
// 获取当前剪贴板数据 | |||||
static QVariantMap getClipboardData(); | |||||
// 检查剪贴板是否有有效数据 | |||||
static bool hasClipboardData(); | |||||
// 设置寄存器绑定 | |||||
bool setRegisterBinding(int address); | |||||
// 获取寄存器地址 | |||||
int getRegisterAddress() const { return m_registerAddress; } | |||||
protected: | |||||
// 获取子类的宽高比(用于缩放时保持比例,默认为2.0) | |||||
virtual qreal getAspectRatio() const { return m_aspectRatio; } | |||||
// 鼠标事件处理 | |||||
// 处理鼠标按下事件(如开始拖动或缩放) | |||||
void mousePressEvent(QGraphicsSceneMouseEvent* event) override; | |||||
// 处理鼠标移动事件(如拖动控件或调整大小) | |||||
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; | |||||
// 处理鼠标释放事件(结束拖动或缩放) | |||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; | |||||
// 处理鼠标悬停事件(设置光标样式,如缩放或拖动光标) | |||||
void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override; | |||||
// 处理右键菜单事件(显示控件特定的上下文菜单) | |||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; | |||||
// 检查鼠标是否在右下角缩放区域(用于判断是否进入缩放模式) | |||||
virtual bool isInScaleArea(const QPointF& pos); | |||||
// 创建右键菜单(虚函数,子类实现特定菜单项,如复制、删除等) | |||||
virtual void createContextMenu(QMenu& menu); | |||||
// 更改控件外观颜色(通过颜色对话框选择ON或OFF状态颜色) | |||||
void changeAppearanceColor(bool isOnState, QColor& onColor, QColor& offColor); | |||||
// 缩放相关成员 | |||||
bool m_isScaling = false; // 是否处于缩放模式 | |||||
QRectF m_originalRect; // 缩放开始时的原始边界矩形 | |||||
QPointF m_scaleStartPos; // 缩放开始时的鼠标位置 | |||||
QPointF m_dragStartPos; // 拖动开始时的鼠标位置 | |||||
const qreal m_aspectRatio = 2.0; // 默认宽高比 | |||||
const qreal m_scaleAreaSize = 15.0; // 右下角缩放区域大小 | |||||
const qreal m_minWidth = 60.0; // 最小宽度 | |||||
const qreal m_maxWidth = 320.0; // 最大宽度 | |||||
const qreal m_minHeight = 30.0; // 最小高度 | |||||
const qreal m_maxHeight = 160.0; // 最大高度 | |||||
// 寄存器绑定成员 | |||||
int m_registerAddress; // 寄存器地址(0-4000) | |||||
private: | |||||
// 静态剪贴板数据,存储单个控件的属性(共享于所有控件实例) | |||||
static QVariantMap m_clipboardData; | |||||
}; |
@@ -0,0 +1,330 @@ | |||||
#include "hmieditorwidget.h" | |||||
#include "hmiwidgetfactory.h" | |||||
#include <QHBoxLayout> | |||||
#include <QVBoxLayout> | |||||
#include <QGraphicsScene> | |||||
#include <QGraphicsView> | |||||
#include <QDebug> | |||||
#include <QKeyEvent> | |||||
#include <QMouseEvent> | |||||
#include <QMenu> | |||||
#include <QTimer> | |||||
#include <QCoreApplication> | |||||
#include <QMetaObject> | |||||
#include "hmicontrolitem.h" | |||||
HmiEditorWidget::HmiEditorWidget(QWidget* parent) | |||||
: QWidget(parent) | |||||
{ | |||||
QHBoxLayout* mainLayout = new QHBoxLayout(this); | |||||
mainLayout->setContentsMargins(10, 10, 10, 10); | |||||
mainLayout->setSpacing(10); | |||||
QVBoxLayout* leftLayout = new QVBoxLayout(); | |||||
leftLayout->setSpacing(5); | |||||
hmiToolbar = new QListWidget(this); | |||||
hmiToolbar->setViewMode(QListWidget::IconMode); | |||||
hmiToolbar->setIconSize(QSize(48, 48)); | |||||
hmiToolbar->setFixedWidth(100); | |||||
hmiToolbar->setSpacing(10); | |||||
hmiToolbar->setDragEnabled(false); | |||||
QListWidgetItem* buttonItem = new QListWidgetItem(); | |||||
buttonItem->setIcon(QIcon(":resource/image/button.png")); | |||||
buttonItem->setData(Qt::UserRole, QString("按钮")); | |||||
buttonItem->setToolTip("按钮"); | |||||
hmiToolbar->addItem(buttonItem); | |||||
QListWidgetItem* ledItem = new QListWidgetItem(); | |||||
ledItem->setIcon(QIcon(":resource/image/light.png")); | |||||
ledItem->setData(Qt::UserRole, QString("指示灯")); | |||||
ledItem->setToolTip("指示灯"); | |||||
hmiToolbar->addItem(ledItem); | |||||
leftLayout->addWidget(hmiToolbar); | |||||
m_newPageButton = new QPushButton("新建页面", this); | |||||
m_deletePageButton = new QPushButton("删除页面", this); | |||||
m_prevPageButton = new QPushButton("上一页", this); | |||||
m_nextPageButton = new QPushButton("下一页", this); | |||||
m_pageLabel = new QLabel("无页面", this); | |||||
leftLayout->addWidget(m_newPageButton); | |||||
leftLayout->addWidget(m_deletePageButton); | |||||
leftLayout->addWidget(m_prevPageButton); | |||||
leftLayout->addWidget(m_nextPageButton); | |||||
leftLayout->addWidget(m_pageLabel); | |||||
leftLayout->addStretch(); | |||||
mainLayout->addLayout(leftLayout); | |||||
hmiEditArea = new QGraphicsView(this); | |||||
hmiEditArea->setRenderHint(QPainter::Antialiasing); | |||||
hmiEditArea->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); | |||||
hmiEditArea->setOptimizationFlags(QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing); | |||||
hmiEditArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); | |||||
hmiEditArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); | |||||
mainLayout->addWidget(hmiEditArea, 1); | |||||
hmiEditArea->installEventFilter(this); | |||||
connect(hmiToolbar, &QListWidget::itemClicked, this, &HmiEditorWidget::onToolbarItemClicked); | |||||
connect(m_newPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNewPageClicked); | |||||
connect(m_deletePageButton, &QPushButton::clicked, this, &HmiEditorWidget::onDeletePageClicked); | |||||
connect(m_prevPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onPreviousPageClicked); | |||||
connect(m_nextPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNextPageClicked); | |||||
ensureViewReady(); | |||||
} | |||||
HmiEditorWidget::~HmiEditorWidget() | |||||
{ | |||||
clearPages(); | |||||
} | |||||
void HmiEditorWidget::ensureViewReady() | |||||
{ | |||||
QCoreApplication::processEvents(); | |||||
hmiEditArea->resetTransform(); | |||||
hmiEditArea->viewport()->update(); | |||||
if (hmiEditArea->viewport()->rect().isEmpty()) { | |||||
hmiEditArea->setFixedSize(600, 400); | |||||
QCoreApplication::processEvents(); | |||||
} | |||||
} | |||||
void HmiEditorWidget::showEvent(QShowEvent* event) | |||||
{ | |||||
QWidget::showEvent(event); | |||||
if (m_firstShow && m_pages.isEmpty()) { | |||||
m_firstShow = false; | |||||
createNewPage(); | |||||
} | |||||
} | |||||
void HmiEditorWidget::createNewPage() | |||||
{ | |||||
QGraphicsScene* newScene = new QGraphicsScene(this); | |||||
QRect viewRect = hmiEditArea->viewport()->rect(); | |||||
if (viewRect.isEmpty()) { | |||||
viewRect = QRect(0, 0, 800, 600); | |||||
hmiEditArea->setSceneRect(viewRect); | |||||
} | |||||
newScene->setSceneRect(viewRect); | |||||
m_pages.append(newScene); | |||||
m_currentPageIndex = m_pages.size() - 1; | |||||
QMetaObject::invokeMethod(this, [this, newScene]() { | |||||
hmiEditArea->setScene(newScene); | |||||
initViewScale(); | |||||
updatePageLabel(); | |||||
hmiEditArea->viewport()->update(); | |||||
}, Qt::QueuedConnection); | |||||
} | |||||
void HmiEditorWidget::refreshView() | |||||
{ | |||||
if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) { | |||||
hmiEditArea->setScene(nullptr); | |||||
hmiEditArea->setScene(m_pages[m_currentPageIndex]); | |||||
initViewScale(); | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
} | |||||
void HmiEditorWidget::initViewScale() | |||||
{ | |||||
if (!hmiEditArea || m_currentPageIndex < 0 || m_pages.isEmpty()) { | |||||
return; | |||||
} | |||||
QRect viewRect = hmiEditArea->viewport()->rect(); | |||||
if (viewRect.isEmpty()) { | |||||
viewRect = QRect(0, 0, 800, 600); | |||||
} | |||||
QGraphicsScene* currentScene = m_pages[m_currentPageIndex]; | |||||
currentScene->setSceneRect(viewRect); | |||||
hmiEditArea->fitInView(viewRect, Qt::KeepAspectRatio); | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
void HmiEditorWidget::updatePageLabel() | |||||
{ | |||||
if (m_pages.isEmpty()) { | |||||
m_pageLabel->setText("无页面"); | |||||
m_deletePageButton->setEnabled(false); | |||||
m_prevPageButton->setEnabled(false); | |||||
m_nextPageButton->setEnabled(false); | |||||
} else { | |||||
m_pageLabel->setText(QString("第 %1/%2 页").arg(m_currentPageIndex + 1).arg(m_pages.size())); | |||||
m_deletePageButton->setEnabled(m_pages.size() > 1); | |||||
m_prevPageButton->setEnabled(m_currentPageIndex > 0); | |||||
m_nextPageButton->setEnabled(m_currentPageIndex < m_pages.size() - 1); | |||||
} | |||||
} | |||||
void HmiEditorWidget::switchToPage(int index) | |||||
{ | |||||
if (index >= 0 && index < m_pages.size()) { | |||||
m_currentPageIndex = index; | |||||
hmiEditArea->resetTransform(); | |||||
hmiEditArea->setScene(m_pages[index]); | |||||
QRectF sceneRect = m_pages[index]->sceneRect(); | |||||
if (!sceneRect.isEmpty()) { | |||||
hmiEditArea->fitInView(sceneRect, Qt::KeepAspectRatio); | |||||
} | |||||
updatePageLabel(); | |||||
QMetaObject::invokeMethod(this, [this]() { | |||||
hmiEditArea->viewport()->update(); | |||||
}, Qt::QueuedConnection); | |||||
} | |||||
} | |||||
bool HmiEditorWidget::eventFilter(QObject* obj, QEvent* event) | |||||
{ | |||||
if (obj == hmiEditArea && event->type() == QEvent::ContextMenu) { | |||||
QContextMenuEvent* contextEvent = static_cast<QContextMenuEvent*>(event); | |||||
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { | |||||
QGraphicsItem* item = hmiEditArea->itemAt(contextEvent->pos()); | |||||
if (!item) { | |||||
QMenu menu; | |||||
QAction* pasteAction = new QAction("粘贴", &menu); | |||||
pasteAction->setEnabled(HmiControlItem::hasClipboardData()); | |||||
QObject::connect(pasteAction, &QAction::triggered, [this, contextEvent]() { | |||||
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] && HmiControlItem::hasClipboardData()) { | |||||
QPointF scenePos = hmiEditArea->mapToScene(contextEvent->pos()); | |||||
HmiControlItem* newItem = HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), scenePos); | |||||
if (newItem) { | |||||
m_pages[m_currentPageIndex]->addItem(newItem); | |||||
m_pages[m_currentPageIndex]->clearSelection(); | |||||
newItem->setSelected(true); | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
} | |||||
}); | |||||
menu.addAction(pasteAction); | |||||
menu.exec(contextEvent->globalPos()); | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
return QWidget::eventFilter(obj, event); | |||||
} | |||||
void HmiEditorWidget::resizeEvent(QResizeEvent* event) | |||||
{ | |||||
QWidget::resizeEvent(event); | |||||
if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) { | |||||
initViewScale(); | |||||
} | |||||
} | |||||
void HmiEditorWidget::keyPressEvent(QKeyEvent* event) | |||||
{ | |||||
if (event->matches(QKeySequence::Copy)) { | |||||
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { | |||||
auto selectedItems = m_pages[m_currentPageIndex]->selectedItems(); | |||||
if (!selectedItems.isEmpty()) { | |||||
HmiControlItem* item = dynamic_cast<HmiControlItem*>(selectedItems.first()); | |||||
if (item) { | |||||
HmiControlItem::copyToClipboard(item->saveToClipboard()); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else if (event->matches(QKeySequence::Paste)) { | |||||
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] && HmiControlItem::hasClipboardData()) { | |||||
QRectF sceneRect = m_pages[m_currentPageIndex]->sceneRect(); | |||||
QPointF centerPos(sceneRect.width() / 2, sceneRect.height() / 2); | |||||
HmiControlItem* newItem = HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), centerPos); | |||||
if (newItem) { | |||||
m_pages[m_currentPageIndex]->addItem(newItem); | |||||
m_pages[m_currentPageIndex]->clearSelection(); | |||||
newItem->setSelected(true); | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
} | |||||
} | |||||
else if (event->matches(QKeySequence::Delete)) { | |||||
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) { | |||||
auto selectedItems = m_pages[m_currentPageIndex]->selectedItems(); | |||||
for (QGraphicsItem* item : selectedItems) { | |||||
HmiControlItem* controlItem = dynamic_cast<HmiControlItem*>(item); | |||||
if (controlItem) { | |||||
controlItem->deleteItem(); | |||||
} | |||||
} | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
} | |||||
QWidget::keyPressEvent(event); | |||||
} | |||||
void HmiEditorWidget::onToolbarItemClicked(QListWidgetItem* item) | |||||
{ | |||||
if (!item || m_currentPageIndex < 0 || !m_pages[m_currentPageIndex]) return; | |||||
QString type = item->data(Qt::UserRole).toString(); | |||||
HmiControlItem* newItem = nullptr; | |||||
if (type == "按钮") { | |||||
newItem = new HmiButton(); | |||||
} else if (type == "指示灯") { | |||||
newItem = new HmiLed(); | |||||
} | |||||
if (newItem) { | |||||
QRectF sceneRect = m_pages[m_currentPageIndex]->sceneRect(); | |||||
newItem->setPos(sceneRect.width() / 2, sceneRect.height() / 2); | |||||
m_pages[m_currentPageIndex]->addItem(newItem); | |||||
m_pages[m_currentPageIndex]->clearSelection(); | |||||
newItem->setSelected(true); | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
} | |||||
void HmiEditorWidget::onNewPageClicked() | |||||
{ | |||||
createNewPage(); | |||||
} | |||||
void HmiEditorWidget::onDeletePageClicked() | |||||
{ | |||||
if (m_currentPageIndex >= 0 && m_pages.size() > 1) { | |||||
QGraphicsScene* scene = m_pages[m_currentPageIndex]; | |||||
m_pages.removeAt(m_currentPageIndex); | |||||
delete scene; | |||||
m_currentPageIndex = qMin(m_currentPageIndex, m_pages.size() - 1); | |||||
hmiEditArea->setScene(m_pages[m_currentPageIndex]); | |||||
initViewScale(); | |||||
updatePageLabel(); | |||||
hmiEditArea->viewport()->update(); | |||||
} | |||||
} | |||||
void HmiEditorWidget::onPreviousPageClicked() | |||||
{ | |||||
switchToPage(m_currentPageIndex - 1); | |||||
} | |||||
void HmiEditorWidget::onNextPageClicked() | |||||
{ | |||||
switchToPage(m_currentPageIndex + 1); | |||||
} | |||||
void HmiEditorWidget::clearPages() | |||||
{ | |||||
for (QGraphicsScene* scene : m_pages) { | |||||
delete scene; | |||||
} | |||||
m_pages.clear(); | |||||
m_currentPageIndex = -1; | |||||
hmiEditArea->setScene(nullptr); | |||||
updatePageLabel(); | |||||
} |
@@ -0,0 +1,51 @@ | |||||
#pragma once | |||||
#include <QWidget> | |||||
#include <QListWidget> | |||||
#include <QGraphicsView> | |||||
#include <QGraphicsScene> | |||||
#include <QLabel> | |||||
#include <QPushButton> | |||||
#include <QList> | |||||
class HmiEditorWidget : public QWidget { | |||||
Q_OBJECT | |||||
public: | |||||
explicit HmiEditorWidget(QWidget* parent = nullptr); | |||||
~HmiEditorWidget(); | |||||
Q_INVOKABLE QList<QGraphicsScene*> getPages() const { return m_pages; } | |||||
Q_INVOKABLE int getCurrentPageIndex() const { return m_currentPageIndex; } | |||||
Q_INVOKABLE void clearPages(); | |||||
Q_INVOKABLE void createNewPage(); | |||||
Q_INVOKABLE void refreshView(); | |||||
void switchToPage(int index); | |||||
protected: | |||||
void keyPressEvent(QKeyEvent* event) override; | |||||
bool eventFilter(QObject* obj, QEvent* event) override; | |||||
void resizeEvent(QResizeEvent* event) override; | |||||
void showEvent(QShowEvent* event) override; | |||||
private slots: | |||||
void onToolbarItemClicked(QListWidgetItem* item); | |||||
void onNewPageClicked(); | |||||
void onDeletePageClicked(); | |||||
void onPreviousPageClicked(); | |||||
void onNextPageClicked(); | |||||
private: | |||||
void initViewScale(); | |||||
void updatePageLabel(); | |||||
void ensureViewReady(); | |||||
QListWidget* hmiToolbar; | |||||
QGraphicsView* hmiEditArea; | |||||
QList<QGraphicsScene*> m_pages; | |||||
int m_currentPageIndex = -1; | |||||
QLabel* m_pageLabel; | |||||
QPushButton* m_newPageButton; | |||||
QPushButton* m_deletePageButton; | |||||
QPushButton* m_prevPageButton; | |||||
QPushButton* m_nextPageButton; | |||||
bool m_firstShow = true; | |||||
}; |
@@ -0,0 +1,11 @@ | |||||
#include "hmiwidgetfactory.h" | |||||
// 根据名称创建HMI控件 | |||||
HmiControlItem* HmiWidgetFactory::createItem(const QString& name) { | |||||
if (name == "按钮") { | |||||
return new HmiButton(); | |||||
} else if (name == "指示灯") { | |||||
return new HmiLed(); | |||||
} | |||||
return nullptr; // 未知类型返回空 | |||||
} |
@@ -0,0 +1,12 @@ | |||||
#pragma once | |||||
#include "hmibutton.h" | |||||
#include "hmiled.h" | |||||
#include <QString> | |||||
// HMI控件工厂类(静态类,用于创建HMI控件) | |||||
class HmiWidgetFactory { | |||||
public: | |||||
// 静态方法:根据名称创建对应的HMI控件 | |||||
static HmiControlItem* createItem(const QString& name); | |||||
}; | |||||
@@ -0,0 +1,63 @@ | |||||
#include "plccontrolitem.h" | |||||
#include <QPainter> | |||||
#include<QGraphicsScene> | |||||
PlcControlItem::PlcControlItem(PlcControlType type, QGraphicsItem* parent) | |||||
: QGraphicsItem(parent), m_type(type) | |||||
{ | |||||
// 设置控件属性:可移动、可选择、发送几何变化信号 | |||||
setFlags(QGraphicsItem::ItemIsMovable | | |||||
QGraphicsItem::ItemIsSelectable | | |||||
QGraphicsItem::ItemSendsGeometryChanges); | |||||
// 设置变换原点为控件中心(优化旋转/缩放时的渲染) | |||||
setTransformOriginPoint(boundingRect().center()); | |||||
} | |||||
// 关键修复1:确保边界矩形足够大,包含所有绘制内容 | |||||
QRectF PlcControlItem::boundingRect() const { | |||||
// 扩大边界1像素,避免边缘被裁剪(解决抗锯齿导致的边缘缺失) | |||||
return QRectF(0, 0, 100, 40).adjusted(-1, -1, 1, 1); | |||||
} | |||||
// 关键修复2:优化绘制逻辑,确保完整重绘 | |||||
void PlcControlItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { | |||||
// 启用抗锯齿(使边缘平滑) | |||||
painter->setRenderHint(QPainter::Antialiasing); | |||||
// 绘制控件背景 | |||||
painter->setPen(Qt::black); // 边框颜色 | |||||
//painter->setBrush(isSelected() ? Qt::gray : Qt::white); // 选中时变灰 | |||||
painter->drawRect(0, 0, 100, 40); // 绘制主体矩形(使用原始尺寸) | |||||
// 绘制控件文本 | |||||
QString label; | |||||
switch (m_type) { | |||||
case PlcControlType::NormallyOpen: label = "常开"; break; | |||||
case PlcControlType::NormallyClosed: label = "常闭"; break; | |||||
case PlcControlType::Coil: label = "线圈"; break; | |||||
case PlcControlType::Compare: label = "比较"; break; | |||||
} | |||||
painter->drawText(QRectF(0, 0, 100, 40), Qt::AlignCenter, label); // 文本居中 | |||||
} | |||||
// 鼠标按下:记录初始位置(控件局部坐标) | |||||
void PlcControlItem::mousePressEvent(QGraphicsSceneMouseEvent* event) { | |||||
m_dragStartPos = event->pos(); | |||||
event->accept(); | |||||
QGraphicsItem::mousePressEvent(event); | |||||
} | |||||
// 鼠标移动:更新位置并强制重绘 | |||||
void PlcControlItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { | |||||
if (isSelected()) { | |||||
// 计算位移(基于控件局部坐标) | |||||
QPointF delta = event->pos() - m_dragStartPos; | |||||
// 移动控件 | |||||
moveBy(delta.x(), delta.y()); | |||||
// 关键修复3:主动触发场景重绘,确保新旧位置都刷新 | |||||
scene()->update(); | |||||
} | |||||
event->accept(); | |||||
QGraphicsItem::mouseMoveEvent(event); | |||||
} |
@@ -0,0 +1,36 @@ | |||||
#pragma once // 防止头文件重复包含 | |||||
#include <QGraphicsItem> // 基类:图形项 | |||||
#include <QGraphicsSceneMouseEvent> // 鼠标事件类 | |||||
// PLC控件类型枚举 | |||||
enum class PlcControlType { | |||||
NormallyOpen, // 常开 | |||||
NormallyClosed, // 常闭 | |||||
Coil, // 线圈 | |||||
Compare // 比较 | |||||
}; | |||||
// PLC控件图形项类 | |||||
class PlcControlItem : public QGraphicsItem { | |||||
public: | |||||
// 构造函数:type为控件类型,parent为父图形项 | |||||
PlcControlItem(PlcControlType type, QGraphicsItem* parent = nullptr); | |||||
// 重写:返回边界矩形 | |||||
QRectF boundingRect() const override; | |||||
// 重写:绘制图形项 | |||||
void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override; | |||||
protected: | |||||
// 重写:鼠标按下事件 | |||||
void mousePressEvent(QGraphicsSceneMouseEvent* event) override; | |||||
// 重写:鼠标移动事件 | |||||
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; | |||||
private: | |||||
// 控件类型 | |||||
PlcControlType m_type; | |||||
// 拖拽起始位置 | |||||
QPointF m_dragStartPos; | |||||
}; |
@@ -0,0 +1,49 @@ | |||||
#include "plceditorwidget.h" | |||||
#include "plcwidgetfactory.h" | |||||
#include <QHBoxLayout> | |||||
#include <QGraphicsScene> | |||||
#include <QGraphicsView> | |||||
PlcEditorWidget::PlcEditorWidget(QWidget* parent) | |||||
: QWidget(parent) | |||||
{ | |||||
QHBoxLayout* layout = new QHBoxLayout(this); | |||||
// 工具栏初始化 | |||||
plcToolbar = new QListWidget(this); | |||||
plcToolbar->addItem("常开"); | |||||
plcToolbar->addItem("常闭"); | |||||
plcToolbar->addItem("线圈"); | |||||
plcToolbar->addItem("比较"); | |||||
plcToolbar->setFixedWidth(100); | |||||
// === 画布(场景+视图)修复配置 === | |||||
plcScene = new QGraphicsScene(this); | |||||
// 1. 设置明确的场景边界 | |||||
plcScene->setSceneRect(0, 0, 1000, 800); | |||||
plcEditArea = new QGraphicsView(plcScene, this); | |||||
// 2. 禁用视图自身的拖拽模式 | |||||
plcEditArea->setDragMode(QGraphicsView::NoDrag); | |||||
// 3. 禁用滚动条(可选) | |||||
plcEditArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | |||||
plcEditArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | |||||
// 4. 确保坐标1:1映射 | |||||
plcEditArea->setTransform(QTransform()); | |||||
plcEditArea->setResizeAnchor(QGraphicsView::NoAnchor); | |||||
layout->addWidget(plcToolbar); | |||||
layout->addWidget(plcEditArea); | |||||
connect(plcToolbar, &QListWidget::itemClicked, | |||||
this, &PlcEditorWidget::onToolbarClicked); | |||||
} | |||||
void PlcEditorWidget::onToolbarClicked(QListWidgetItem* item) { | |||||
QString type = item->text(); | |||||
auto controlItem = PlcWidgetFactory::createItem(type); | |||||
if (controlItem) { | |||||
controlItem->setPos(50, 50); // 固定初始位置 | |||||
plcScene->addItem(controlItem); | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
#pragma once // 防止头文件重复包含 | |||||
#include <QWidget> // 基类:Qt窗口部件类 | |||||
#include <QListWidget> // 工具栏:列表控件 | |||||
#include <QGraphicsView> // 编辑区域:图形视图 | |||||
#include <QGraphicsScene> // 编辑区域:图形场景 | |||||
// PLC编辑器类,继承自QWidget | |||||
class PlcEditorWidget : public QWidget { | |||||
Q_OBJECT // 启用Qt元对象系统 | |||||
public: | |||||
// 构造函数:parent为父窗口,默认为nullptr | |||||
explicit PlcEditorWidget(QWidget* parent = nullptr); | |||||
private slots: | |||||
// 工具栏点击事件的槽函数(响应控件添加请求) | |||||
void onToolbarClicked(QListWidgetItem* item); | |||||
private: | |||||
// PLC工具栏(显示“常开”“常闭”等选项) | |||||
QListWidget* plcToolbar; | |||||
// PLC编辑区域的视图 | |||||
QGraphicsView* plcEditArea; | |||||
// PLC编辑区域的场景 | |||||
QGraphicsScene* plcScene; | |||||
}; |
@@ -0,0 +1,11 @@ | |||||
// 包含PLC工厂头文件 | |||||
#include "plcwidgetfactory.h" | |||||
// 根据名称创建PLC控件 | |||||
PlcControlItem* PlcWidgetFactory::createItem(const QString& name) { | |||||
if (name == "常开") return new PlcControlItem(PlcControlType::NormallyOpen); | |||||
if (name == "常闭") return new PlcControlItem(PlcControlType::NormallyClosed); | |||||
if (name == "线圈") return new PlcControlItem(PlcControlType::Coil); | |||||
if (name == "比较") return new PlcControlItem(PlcControlType::Compare); | |||||
return nullptr; // 未知名称返回空 | |||||
} |
@@ -0,0 +1,11 @@ | |||||
#pragma once // 防止头文件重复包含 | |||||
#include "plccontrolitem.h" // 包含PLC控件类 | |||||
#include <QString> // 字符串类 | |||||
// PLC控件工厂类 | |||||
class PlcWidgetFactory { | |||||
public: | |||||
// 静态方法:根据名称创建PLC控件 | |||||
static PlcControlItem* createItem(const QString& name); | |||||
}; |
@@ -0,0 +1,171 @@ | |||||
#include "mainwindow.h" | |||||
#include "modules/HMI/hmieditorwidget.h" | |||||
#include "modules/PLC/plceditorwidget.h" | |||||
#include <QFileDialog> | |||||
#include <QMessageBox> | |||||
#include <QInputDialog> | |||||
#include <QTabBar> | |||||
#include <QDebug> | |||||
MainWindow::MainWindow(QWidget *parent) | |||||
: QMainWindow(parent) | |||||
{ | |||||
setWindowTitle("综合平台编程器"); | |||||
setFixedSize(1000, 740); | |||||
tabWidget = new QTabWidget(this); | |||||
setCentralWidget(tabWidget); | |||||
tabWidget->setTabsClosable(true); | |||||
connect(tabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::onTabCloseRequested); | |||||
tabWidget->setStyleSheet("QTabWidget { margin: 10px; }"); | |||||
fileManager = new FileManager(this); | |||||
createMenus(); | |||||
} | |||||
MainWindow::~MainWindow() {} | |||||
void MainWindow::createMenus() { | |||||
fileMenu = menuBar()->addMenu("文件(&F)"); | |||||
newAction = new QAction("新建(&N)", this); | |||||
newAction->setShortcut(QKeySequence("Ctrl+N")); | |||||
connect(newAction, &QAction::triggered, this, &MainWindow::onNewFile); | |||||
fileMenu->addAction(newAction); | |||||
openAction = new QAction("打开(&O)", this); | |||||
openAction->setShortcut(QKeySequence("Ctrl+O")); | |||||
connect(openAction, &QAction::triggered, this, &MainWindow::onOpenFile); | |||||
fileMenu->addAction(openAction); | |||||
saveAction = new QAction("保存(&S)", this); | |||||
saveAction->setShortcut(QKeySequence("Ctrl+S")); | |||||
connect(saveAction, &QAction::triggered, this, &MainWindow::onSaveFile); | |||||
fileMenu->addAction(saveAction); | |||||
closeAction = new QAction("关闭(&C)", this); | |||||
closeAction->setShortcut(QKeySequence("Ctrl+W")); | |||||
connect(closeAction, &QAction::triggered, this, &MainWindow::onCloseFile); | |||||
fileMenu->addAction(closeAction); | |||||
simulateMenu = menuBar()->addMenu("仿真(&M)"); | |||||
simulateAction = new QAction("启动仿真(&R)", this); | |||||
simulateAction->setShortcut(QKeySequence("F5")); | |||||
connect(simulateAction, &QAction::triggered, this, &MainWindow::onSimulate); | |||||
simulateMenu->addAction(simulateAction); | |||||
} | |||||
void MainWindow::onTabCloseRequested(int index) | |||||
{ | |||||
if (index < 0 || index >= tabWidget->count()) return; | |||||
QWidget* widget = tabWidget->widget(index); | |||||
if (widget) { | |||||
tabWidget->removeTab(index); | |||||
delete widget; | |||||
} | |||||
} | |||||
void MainWindow::onCloseFile() | |||||
{ | |||||
int currentIndex = tabWidget->currentIndex(); | |||||
if (currentIndex >= 0) { | |||||
onTabCloseRequested(currentIndex); | |||||
} | |||||
} | |||||
void MainWindow::onNewFile() { | |||||
QStringList types = {"HMI文件", "PLC文件"}; | |||||
bool ok; | |||||
QString type = QInputDialog::getItem(this, "选择文件类型", "请选择要创建的文件类型:", types, 0, false, &ok); | |||||
if (ok && !type.isEmpty()) { | |||||
if (type == "HMI文件") { | |||||
HmiEditorWidget* newHmi = new HmiEditorWidget(this); | |||||
tabWidget->addTab(newHmi, "HMI编辑器"); | |||||
tabWidget->setCurrentWidget(newHmi); | |||||
qDebug() << "MainWindow: 创建新HMI编辑器,ID:" << newHmi; | |||||
} else if (type == "PLC文件") { | |||||
PlcEditorWidget* newPlc = new PlcEditorWidget(this); | |||||
tabWidget->addTab(newPlc, "PLC编辑器"); | |||||
tabWidget->setCurrentWidget(newPlc); | |||||
qDebug() << "MainWindow: 创建新PLC编辑器,ID:" << newPlc; | |||||
} | |||||
} | |||||
} | |||||
void MainWindow::onOpenFile() { | |||||
QString filePath = QFileDialog::getOpenFileName(this, "打开文件", "", "HMI文件 (*.hmi);;PLC文件 (*.plc);;所有文件 (*)"); | |||||
if (!filePath.isEmpty()) { | |||||
QFileInfo info(filePath); | |||||
if (info.suffix() == "hmi") { | |||||
HmiEditorWidget* hmi = new HmiEditorWidget(this); | |||||
if (fileManager->loadHmiFile(hmi, filePath)) { | |||||
tabWidget->addTab(hmi, info.fileName()); | |||||
tabWidget->setCurrentWidget(hmi); | |||||
QMessageBox::information(this, "加载成功", "HMI文件已加载:" + filePath); | |||||
qDebug() << "MainWindow: 打开HMI文件,编辑器ID:" << hmi << "路径:" << filePath; | |||||
} else { | |||||
delete hmi; | |||||
QMessageBox::warning(this, "加载失败", "无法加载HMI文件:" + filePath); | |||||
} | |||||
} else if (info.suffix() == "plc") { | |||||
PlcEditorWidget* plc = new PlcEditorWidget(this); | |||||
if (fileManager->loadPlcFile(plc, filePath)) { | |||||
tabWidget->addTab(plc, info.fileName()); | |||||
tabWidget->setCurrentWidget(plc); | |||||
QMessageBox::information(this, "加载成功", "PLC文件已加载:" + filePath); | |||||
qDebug() << "MainWindow: 打开PLC文件,编辑器ID:" << plc << "路径:" << filePath; | |||||
} else { | |||||
delete plc; | |||||
QMessageBox::warning(this, "加载失败", "无法加载PLC文件:" + filePath); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void MainWindow::onSaveFile() | |||||
{ | |||||
QWidget* currentWidget = tabWidget->currentWidget(); | |||||
if (!currentWidget) { | |||||
QMessageBox::warning(this, "保存失败", "没有打开的文件"); | |||||
return; | |||||
} | |||||
QString filter; | |||||
bool isHmi = qobject_cast<HmiEditorWidget*>(currentWidget) != nullptr; | |||||
bool isPlc = qobject_cast<PlcEditorWidget*>(currentWidget) != nullptr; | |||||
if (isHmi) { | |||||
filter = "HMI文件 (*.hmi)"; | |||||
} else if (isPlc) { | |||||
filter = "PLC文件 (*.plc)"; | |||||
} else { | |||||
QMessageBox::warning(this, "保存失败", "无法识别的文件类型"); | |||||
return; | |||||
} | |||||
QString filePath = QFileDialog::getSaveFileName(this, "保存文件", "", filter); | |||||
if (!filePath.isEmpty()) { | |||||
bool success = false; | |||||
if (isHmi) { | |||||
HmiEditorWidget* hmi = qobject_cast<HmiEditorWidget*>(currentWidget); | |||||
success = fileManager->saveHmiFile(hmi, filePath); | |||||
qDebug() << "MainWindow: 保存HMI文件,编辑器ID:" << hmi << "路径:" << filePath << "结果:" << success; | |||||
} else if (isPlc) { | |||||
PlcEditorWidget* plc = qobject_cast<PlcEditorWidget*>(currentWidget); | |||||
success = fileManager->savePlcFile(plc, filePath); | |||||
qDebug() << "MainWindow: 保存PLC文件,编辑器ID:" << plc << "路径:" << filePath << "结果:" << success; | |||||
} | |||||
if (success) { | |||||
QMessageBox::information(this, "保存成功", "文件已保存到:" + filePath); | |||||
int currentIndex = tabWidget->currentIndex(); | |||||
if (currentIndex >= 0) { | |||||
tabWidget->setTabText(currentIndex, QFileInfo(filePath).fileName()); | |||||
} | |||||
} else { | |||||
QMessageBox::warning(this, "保存失败", "无法保存文件:" + filePath); | |||||
} | |||||
} | |||||
} | |||||
void MainWindow::onSimulate() { | |||||
QMessageBox::information(this, "仿真", "启动仿真功能\n(实际实现需要根据业务逻辑开发)"); | |||||
} |
@@ -0,0 +1,35 @@ | |||||
#pragma once | |||||
#include <QMainWindow> | |||||
#include <QTabWidget> | |||||
#include <QMenuBar> | |||||
#include <QMenu> | |||||
#include <QAction> | |||||
#include "filemanager.h" | |||||
class MainWindow : public QMainWindow { | |||||
Q_OBJECT | |||||
public: | |||||
MainWindow(QWidget *parent = nullptr); | |||||
~MainWindow(); | |||||
private slots: | |||||
void onNewFile(); | |||||
void onOpenFile(); | |||||
void onSaveFile(); | |||||
void onCloseFile(); | |||||
void onTabCloseRequested(int index); | |||||
void onSimulate(); | |||||
private: | |||||
void createMenus(); | |||||
QTabWidget* tabWidget; | |||||
FileManager* fileManager; | |||||
QMenu* fileMenu; | |||||
QAction* newAction; | |||||
QAction* openAction; | |||||
QAction* saveAction; | |||||
QAction* closeAction; | |||||
QMenu* simulateMenu; | |||||
QAction* simulateAction; | |||||
}; |