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