@@ -53,6 +53,16 @@ void HmiComponent::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti | |||||
painter->setPen(QPen(Qt::darkRed, 2, Qt::DashLine)); | painter->setPen(QPen(Qt::darkRed, 2, Qt::DashLine)); | ||||
painter->drawRect(boundingRect()); | painter->drawRect(boundingRect()); | ||||
} | } | ||||
// 如果被选中,绘制缩放控制点 | |||||
if (isSelected()) { | |||||
QRectF rect = boundingRect(); | |||||
// 绘制右下角缩放控制点 | |||||
painter->setBrush(Qt::white); | |||||
painter->setPen(QPen(Qt::black, 1)); | |||||
painter->drawRect(rect.right() - 8, rect.bottom() - 8, 8, 8); | |||||
} | |||||
} | } | ||||
void HmiComponent::setAddress(int address) | void HmiComponent::setAddress(int address) | ||||
@@ -68,9 +78,31 @@ int HmiComponent::address() const | |||||
return m_address; | return m_address; | ||||
} | } | ||||
void HmiComponent::setSize(double width, double height) | |||||
{ | |||||
prepareGeometryChange(); | |||||
m_width = width; | |||||
m_height = height; | |||||
update(); | |||||
} | |||||
void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event) | void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event) | ||||
{ | { | ||||
// 右键不触发选中信号,留给 contextMenuEvent 处理 | |||||
// 检查是否点击了缩放控制点 | |||||
if (event->button() == Qt::LeftButton && isSelected()) { | |||||
QRectF rect = boundingRect(); | |||||
QRectF handleRect(rect.right() - 8, rect.bottom() - 8, 8, 8); | |||||
if (handleRect.contains(event->pos())) { | |||||
m_resizing = true; | |||||
m_resizeStartPos = event->scenePos(); | |||||
m_resizeStartSize = QSizeF(rect.width(), rect.height()); | |||||
event->accept(); | |||||
return; | |||||
} | |||||
} | |||||
// 原有的选中处理 | |||||
if (event->button() == Qt::LeftButton) { | if (event->button() == Qt::LeftButton) { | ||||
emit selected(this); | emit selected(this); | ||||
} | } | ||||
@@ -229,6 +261,24 @@ void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) | |||||
void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event) | void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event) | ||||
{ | { | ||||
if (m_resizing) { | |||||
// 计算尺寸变化 | |||||
QPointF delta = event->scenePos() - m_resizeStartPos; | |||||
double newWidth = qMax(20.0, m_resizeStartSize.width() + delta.x()); | |||||
double newHeight = qMax(20.0, m_resizeStartSize.height() + delta.y()); | |||||
// 更新组件大小 | |||||
prepareGeometryChange(); | |||||
// 子类需要实现setSize函数 | |||||
setSize(newWidth, newHeight); | |||||
// 更新缩放控制点位置 | |||||
update(); | |||||
event->accept(); | |||||
return; | |||||
} | |||||
if (!(flags() & QGraphicsItem::ItemIsMovable)) { | if (!(flags() & QGraphicsItem::ItemIsMovable)) { | ||||
// 如果项不可移动,直接调用基类处理 | // 如果项不可移动,直接调用基类处理 | ||||
QGraphicsObject::mouseMoveEvent(event); | QGraphicsObject::mouseMoveEvent(event); | ||||
@@ -263,8 +313,8 @@ void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event) | |||||
QRectF newBounds = bounds.translated(newPos); | QRectF newBounds = bounds.translated(newPos); | ||||
// 限制组件边界必须完全在视图可见区域内 | // 限制组件边界必须完全在视图可见区域内 | ||||
qreal limitedX = newPos.x(); | |||||
qreal limitedY = newPos.y(); | |||||
double limitedX = newPos.x(); | |||||
double limitedY = newPos.y(); | |||||
if (newBounds.left() < visibleSceneRect.left()) | if (newBounds.left() < visibleSceneRect.left()) | ||||
limitedX += visibleSceneRect.left() - newBounds.left(); | limitedX += visibleSceneRect.left() - newBounds.left(); | ||||
@@ -279,6 +329,30 @@ void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event) | |||||
event->accept(); | event->accept(); | ||||
} | } | ||||
void HmiComponent::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) | |||||
{ | |||||
if (m_resizing && event->button() == Qt::LeftButton) { | |||||
m_resizing = false; | |||||
event->accept(); | |||||
return; | |||||
} | |||||
QGraphicsObject::mouseReleaseEvent(event); | |||||
} | |||||
void HmiComponent::hoverMoveEvent(QGraphicsSceneHoverEvent *event) | |||||
{ | |||||
QRectF rect = boundingRect(); | |||||
QRectF handleRect(rect.right() - 8, rect.bottom() - 8, 8, 8); | |||||
if (isSelected() && handleRect.contains(event->pos())) { | |||||
setCursor(Qt::SizeFDiagCursor); | |||||
m_resizeHandle = BottomRight; | |||||
} else { | |||||
setCursor(Qt::ArrowCursor); | |||||
m_resizeHandle = None; | |||||
} | |||||
QGraphicsObject::hoverMoveEvent(event); | |||||
} | |||||
void HmiComponent::setOnColor(const QColor& color) { | void HmiComponent::setOnColor(const QColor& color) { | ||||
m_onColor = color; | m_onColor = color; | ||||
@@ -300,6 +374,9 @@ QColor HmiComponent::offColor() const { | |||||
HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent), m_isOn(false) | HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent), m_isOn(false) | ||||
{ | { | ||||
// 按钮保持矩形形状 | |||||
m_width = 65.0; | |||||
m_height = 30.0; | |||||
m_color = Qt::gray; | m_color = Qt::gray; | ||||
m_offColor = m_color; // OFF状态颜色设为默认颜色 | m_offColor = m_color; // OFF状态颜色设为默认颜色 | ||||
m_onColor = Qt::green; // ON状态颜色为绿色 | m_onColor = Qt::green; // ON状态颜色为绿色 | ||||
@@ -307,6 +384,11 @@ HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent), m_isOn(false | |||||
setColor(m_offColor); // 当前显示OFF颜色 | setColor(m_offColor); // 当前显示OFF颜色 | ||||
} | } | ||||
void HmiButton::setSize(double width, double height) | |||||
{ | |||||
HmiComponent::setSize(width, height); | |||||
} | |||||
bool HmiButton::isOn() const | bool HmiButton::isOn() const | ||||
{ | { | ||||
return m_isOn; | return m_isOn; | ||||
@@ -326,7 +408,7 @@ void HmiButton::setOn(bool on) | |||||
QRectF HmiButton::boundingRect() const | QRectF HmiButton::boundingRect() const | ||||
{ | { | ||||
return QRectF(0, 0, 65, 30); | |||||
return QRectF(0, 0, m_width, m_height); | |||||
} | } | ||||
void HmiButton::paintShape(QPainter *painter) | void HmiButton::paintShape(QPainter *painter) | ||||
@@ -338,6 +420,9 @@ void HmiButton::paintShape(QPainter *painter) | |||||
HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent) | HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent) | ||||
{ | { | ||||
// 指示灯初始为圆形 | |||||
m_width = 30.0; | |||||
m_height = 30.0; | |||||
m_color = Qt::red; | m_color = Qt::red; | ||||
m_offColor = m_color; // OFF状态颜色设为默认颜色 | m_offColor = m_color; // OFF状态颜色设为默认颜色 | ||||
m_onColor = Qt::green; // ON状态颜色为绿色 | m_onColor = Qt::green; // ON状态颜色为绿色 | ||||
@@ -345,11 +430,17 @@ HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent) | |||||
setColor(m_offColor); // 当前显示OFF颜色 | setColor(m_offColor); // 当前显示OFF颜色 | ||||
} | } | ||||
void HmiIndicator::setSize(double width, double height) | |||||
{ | |||||
HmiComponent::setSize(width, height); | |||||
} | |||||
QRectF HmiIndicator::boundingRect() const | QRectF HmiIndicator::boundingRect() const | ||||
{ | { | ||||
return QRectF(0, 0, 30, 30); | |||||
return QRectF(0, 0, m_width, m_height); | |||||
} | } | ||||
void HmiIndicator::paintShape(QPainter *painter) | void HmiIndicator::paintShape(QPainter *painter) | ||||
{ | { | ||||
painter->setPen(Qt::NoPen); | painter->setPen(Qt::NoPen); | ||||
@@ -7,8 +7,10 @@ | |||||
class QPainter; | class QPainter; | ||||
class QGraphicsSceneMouseEvent; | class QGraphicsSceneMouseEvent; | ||||
class QGraphicsSceneContextMenuEvent; | class QGraphicsSceneContextMenuEvent; | ||||
class QGraphicsSceneHoverEvent; | |||||
enum class ComponentType { | |||||
enum class ComponentType | |||||
{ | |||||
Button, | Button, | ||||
Indicator | Indicator | ||||
}; | }; | ||||
@@ -51,11 +53,16 @@ public: | |||||
void setAddress(int address); | void setAddress(int address); | ||||
int address() const; | int address() const; | ||||
void setSize(double width, double height); | |||||
QSizeF size() const { return QSizeF(m_width, m_height); } | |||||
protected: | protected: | ||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override; | void mousePressEvent(QGraphicsSceneMouseEvent *event) override; | ||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; | void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; | ||||
virtual void paintShape(QPainter* painter) = 0; | virtual void paintShape(QPainter* painter) = 0; | ||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; | void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; | ||||
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; | |||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; | |||||
protected: | protected: | ||||
QColor m_color; // 当前显示颜色(编辑器中为 OFF) | QColor m_color; // 当前显示颜色(编辑器中为 OFF) | ||||
@@ -63,6 +70,16 @@ protected: | |||||
QColor m_offColor; | QColor m_offColor; | ||||
QString m_componentName; | QString m_componentName; | ||||
int m_address = 0; | int m_address = 0; | ||||
// 新增尺寸成员 | |||||
double m_width = 65.0; | |||||
double m_height = 30.0; | |||||
enum ResizeHandle { None, BottomRight }; // 缩放控制点位置 | |||||
ResizeHandle m_resizeHandle = None; | |||||
bool m_resizing = false; | |||||
QPointF m_resizeStartPos; | |||||
QSizeF m_resizeStartSize; | |||||
}; | }; | ||||
@@ -73,6 +90,7 @@ class HmiButton : public HmiComponent | |||||
public: | public: | ||||
HmiButton(QGraphicsItem *parent = nullptr); | HmiButton(QGraphicsItem *parent = nullptr); | ||||
QRectF boundingRect() const override; | QRectF boundingRect() const override; | ||||
void setSize(double width, double height); | |||||
// 新增状态标志,true = ON,false = OFF | // 新增状态标志,true = ON,false = OFF | ||||
bool isOn() const; | bool isOn() const; | ||||
@@ -91,6 +109,7 @@ class HmiIndicator : public HmiComponent | |||||
public: | public: | ||||
HmiIndicator(QGraphicsItem *parent = nullptr); | HmiIndicator(QGraphicsItem *parent = nullptr); | ||||
QRectF boundingRect() const override; | QRectF boundingRect() const override; | ||||
void setSize(double width, double height); | |||||
protected: | protected: | ||||
void paintShape(QPainter *painter) override; | void paintShape(QPainter *painter) override; | ||||
@@ -113,6 +113,10 @@ bool HMIModule::saveToFile(const QString& filePath) | |||||
compObj["address"] = component->address(); | compObj["address"] = component->address(); | ||||
// 添加按钮状态 | // 添加按钮状态 | ||||
compObj["isOn"] = false; // 默认false | compObj["isOn"] = false; // 默认false | ||||
// 保存尺寸 | |||||
compObj["width"] = component->boundingRect().width(); | |||||
compObj["height"] = component->boundingRect().height(); | |||||
if (auto btn = dynamic_cast<HmiButton*>(component)) { | if (auto btn = dynamic_cast<HmiButton*>(component)) { | ||||
compObj["isOn"] = btn->isOn(); | compObj["isOn"] = btn->isOn(); | ||||
} | } | ||||
@@ -255,7 +259,13 @@ bool HMIModule::openFromFile(const QString& filePath) | |||||
continue; | continue; | ||||
} | } | ||||
// 加载尺寸 | |||||
double width = compObj["width"].toDouble(65); // 默认值 | |||||
double height = compObj["height"].toDouble(30); // 默认值 | |||||
if (newItem) { | if (newItem) { | ||||
// 设置尺寸 | |||||
newItem->setSize(width, height); | |||||
newItem->setPos(QPointF(x, y)); | newItem->setPos(QPointF(x, y)); | ||||
QColor color(colorName); | QColor color(colorName); | ||||
if (color.isValid()) { | if (color.isValid()) { | ||||
@@ -452,11 +462,14 @@ void HMIModule::onCopyRequested() | |||||
} | } | ||||
m_copiedColor = itemToCopy->color(); | m_copiedColor = itemToCopy->color(); | ||||
// 新增复制以下属性 | |||||
// 复制以下属性 | |||||
m_copiedOnColor = itemToCopy->onColor(); | m_copiedOnColor = itemToCopy->onColor(); | ||||
m_copiedOffColor = itemToCopy->offColor(); | m_copiedOffColor = itemToCopy->offColor(); | ||||
m_copiedComponentName = itemToCopy->componentName(); | m_copiedComponentName = itemToCopy->componentName(); | ||||
m_copiedAddress = itemToCopy->address(); | m_copiedAddress = itemToCopy->address(); | ||||
// 复制尺寸 | |||||
m_copiedWidth = itemToCopy->size().width(); | |||||
m_copiedHeight = itemToCopy->size().height(); | |||||
m_hasCopiedItem = true; | m_hasCopiedItem = true; | ||||
@@ -488,6 +501,8 @@ void HMIModule::onPasteRequested(const QPointF& scenePos) | |||||
newItem->setOffColor(m_copiedOffColor); | newItem->setOffColor(m_copiedOffColor); | ||||
newItem->setComponentName(m_copiedComponentName); | newItem->setComponentName(m_copiedComponentName); | ||||
newItem->setAddress(m_copiedAddress); | newItem->setAddress(m_copiedAddress); | ||||
// 设置尺寸 | |||||
newItem->setSize(m_copiedWidth, m_copiedHeight); | |||||
newItem->setPos(scenePos); | newItem->setPos(scenePos); | ||||
currentScene->addItem(newItem); // 使用当前页面的 scene | currentScene->addItem(newItem); // 使用当前页面的 scene | ||||
setupNewComponent(newItem); | setupNewComponent(newItem); | ||||
@@ -57,6 +57,9 @@ private: | |||||
QColor m_copiedOffColor; | QColor m_copiedOffColor; | ||||
QString m_copiedComponentName; | QString m_copiedComponentName; | ||||
int m_copiedAddress = 0; | int m_copiedAddress = 0; | ||||
// 新增复制尺寸变量 | |||||
double m_copiedWidth = 0.0; | |||||
double m_copiedHeight = 0.0; | |||||
bool m_hasCopiedItem = false; // 指示当前是否已经有组件被复制 | bool m_hasCopiedItem = false; // 指示当前是否已经有组件被复制 | ||||
int m_pageCount = 1; // 初始化页面计数器 | int m_pageCount = 1; // 初始化页面计数器 | ||||
@@ -19,26 +19,10 @@ MainWindow::MainWindow(QWidget *parent) | |||||
// 连接 HMIModule 的信号到 MainWindow 的槽 | // 连接 HMIModule 的信号到 MainWindow 的槽 | ||||
connect(hmi_, &HMIModule::logMessageGenerated, this, &MainWindow::appendLog); | connect(hmi_, &HMIModule::logMessageGenerated, this, &MainWindow::appendLog); | ||||
// 新建菜单 | |||||
connect(ui_->action, &QAction::triggered, this, &MainWindow::onNewFile); | |||||
connect(ui_->action_3, &QAction::triggered, this, [this]() { | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI文件", "", "JSON文件 (*.hmi)"); | |||||
if (!fileName.isEmpty()) { | |||||
if (!hmi_->saveToFile(fileName)) { | |||||
QMessageBox::warning(this, "保存失败", "文件保存失败!"); | |||||
} | |||||
} | |||||
}); | |||||
connect(ui_->action_2, &QAction::triggered, this, [this]() { | |||||
QString fileName = QFileDialog::getOpenFileName(this, "打开HMI文件", "", "JSON文件 (*.hmi)"); | |||||
if (!fileName.isEmpty()) { | |||||
if (!hmi_->openFromFile(fileName)) { | |||||
QMessageBox::warning(this, "打开失败", "文件打开失败或格式不正确!"); | |||||
} | |||||
} | |||||
}); | |||||
// 新建/打开/保存文件 | |||||
connect(ui_->actionNew, &QAction::triggered, this, &MainWindow::onNewFile); | |||||
connect(ui_->actionOpen, &QAction::triggered, this, &MainWindow::onOpenFile); | |||||
connect(ui_->actionSave, &QAction::triggered, this, &MainWindow::onSaveFile); | |||||
} | } | ||||
MainWindow::~MainWindow() | MainWindow::~MainWindow() | ||||
@@ -64,31 +48,64 @@ void MainWindow::appendLog(const QString& message) | |||||
void MainWindow::onNewFile() | void MainWindow::onNewFile() | ||||
{ | { | ||||
if (!hmi_) | if (!hmi_) | ||||
{ | |||||
return; | return; | ||||
} | |||||
if (hmi_->isModified()) { | |||||
if (hmi_->isModified()) | |||||
{ | |||||
auto ret = QMessageBox::warning(this, "提示", | auto ret = QMessageBox::warning(this, "提示", | ||||
"当前文件有未保存的更改,是否保存?", | "当前文件有未保存的更改,是否保存?", | ||||
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, | |||||
QMessageBox::Save); | |||||
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); | |||||
if (ret == QMessageBox::Save) { | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI设计", "", "JSON文件 (*.json)"); | |||||
if (!fileName.isEmpty()) { | |||||
if (!hmi_->saveToFile(fileName)) { | |||||
if (ret == QMessageBox::Save) | |||||
{ | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI文件", "", "HMI文件 (*.json)"); | |||||
if (!fileName.isEmpty()) | |||||
{ | |||||
if (!hmi_->saveToFile(fileName)) | |||||
{ | |||||
QMessageBox::warning(this, "保存失败", "文件保存失败!"); | QMessageBox::warning(this, "保存失败", "文件保存失败!"); | ||||
return; // 不新建,保存失败 | return; // 不新建,保存失败 | ||||
} | } | ||||
} else { | |||||
} | |||||
else | |||||
{ | |||||
return; // 取消保存,取消新建 | return; // 取消保存,取消新建 | ||||
} | } | ||||
} else if (ret == QMessageBox::Cancel) { | |||||
} | |||||
else if (ret == QMessageBox::Cancel) | |||||
{ | |||||
return; // 取消新建 | return; // 取消新建 | ||||
} | } | ||||
// 如果是Discard,直接继续新建,丢弃当前修改 | // 如果是Discard,直接继续新建,丢弃当前修改 | ||||
} | } | ||||
// 清空编辑区,新建页面 | // 清空编辑区,新建页面 | ||||
hmi_->resetPages(); | hmi_->resetPages(); | ||||
} | } | ||||
void MainWindow::onOpenFile() | |||||
{ | |||||
QString fileName = QFileDialog::getOpenFileName(this, "打开HMI文件", "", "HMI文件 (*.hmi)"); | |||||
if (!fileName.isEmpty()) | |||||
{ | |||||
if (!hmi_->openFromFile(fileName)) | |||||
{ | |||||
QMessageBox::warning(this, "打开失败", "文件打开失败或格式不正确!"); | |||||
} | |||||
} | |||||
} | |||||
void MainWindow::onSaveFile() | |||||
{ | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI文件", "", "HMI文件 (*.hmi)"); | |||||
if (!fileName.isEmpty()) | |||||
{ | |||||
if (!hmi_->saveToFile(fileName)) | |||||
{ | |||||
QMessageBox::warning(this, "保存失败", "文件保存失败!"); | |||||
} | |||||
} | |||||
} | |||||
@@ -5,7 +5,8 @@ | |||||
#include "hmimodule.h" | #include "hmimodule.h" | ||||
QT_BEGIN_NAMESPACE | QT_BEGIN_NAMESPACE | ||||
namespace Ui { | |||||
namespace Ui | |||||
{ | |||||
class MainWindow; | class MainWindow; | ||||
} | } | ||||
QT_END_NAMESPACE | QT_END_NAMESPACE | ||||
@@ -23,7 +24,9 @@ private: | |||||
private slots: | private slots: | ||||
void appendLog(const QString& message); | void appendLog(const QString& message); | ||||
void onNewFile(); // 新增槽函数,处理“新建”菜单 | |||||
void onNewFile(); | |||||
void onOpenFile(); | |||||
void onSaveFile(); | |||||
private: | private: | ||||
Ui::MainWindow *ui_; | Ui::MainWindow *ui_; | ||||