@@ -101,6 +101,21 @@ void Button::setCustomImage(const QString &imagePath) | |||
update(); | |||
} | |||
void Button::setCurrentSize(QSizeF size) | |||
{ | |||
currentSize_ = size; | |||
} | |||
QSizeF Button::currentSize() | |||
{ | |||
return currentSize_; | |||
} | |||
QString Button::imagePath() | |||
{ | |||
return imagePath_; | |||
} | |||
void Button::loadImage() | |||
{ | |||
if(!imagePath_.isEmpty()){ | |||
@@ -116,9 +131,7 @@ void Button::mousePressEvent(QGraphicsSceneMouseEvent *event) | |||
// 检查是否点击了调整手柄 | |||
QRectF resizeHandle = QRectF(currentSize_.width() - resizeHandleSize_, | |||
currentSize_.height() - resizeHandleSize_, | |||
resizeHandleSize_, | |||
resizeHandleSize_); | |||
resizeHandleSize_, resizeHandleSize_); | |||
if (resizeHandle.contains(event->pos())) { | |||
resizing_ = true; | |||
resizeStartPos_ = event->scenePos(); | |||
@@ -13,6 +13,9 @@ public: | |||
void addMenuActions(QMenu *menu) override; | |||
void handleMenuAction(QAction *action) override; | |||
void setCustomImage(const QString& imagePath); | |||
void setCurrentSize(QSizeF size); | |||
QSizeF currentSize(); | |||
QString imagePath(); | |||
void loadImage(); | |||
// 鼠标事件处理 | |||
@@ -35,13 +35,25 @@ void Comparator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option | |||
painter->setFont(QFont("Arial", 8)); | |||
painter->setPen(Qt::black); | |||
if (!registerId_.isEmpty()) { | |||
QString text = QString("%1: %2").arg(registerId_).arg(registerValue_); | |||
QString text; | |||
if (registerId_.left(1) == "K") | |||
{ | |||
text = QString("%1").arg(registerId_); | |||
} else { | |||
text = QString("%1: %2").arg(registerId_).arg(registerValue_); | |||
} | |||
painter->drawText(QRectF(-20, -25, 40, 20), Qt::AlignCenter, text); | |||
} | |||
// 在右上角显示第二个寄存器 | |||
// 显示第二个寄存器 | |||
if (!registerId2_.isEmpty()) { | |||
QString text = QString("%1: %2").arg(registerId2_).arg(registerValue2_); | |||
QString text; | |||
if (registerId2_.left(1) == "K") | |||
{ | |||
text = QString("%1").arg(registerId2_); | |||
} else { | |||
text = QString("%1: %2").arg(registerId2_).arg(registerValue2_); | |||
} | |||
painter->drawText(QRectF(-20, 5, 40, 20), Qt::AlignCenter, text); | |||
} | |||
painter->restore(); | |||
@@ -12,6 +12,8 @@ public: | |||
QWidget *) override; | |||
bool setRegisterId(const QString &id) override; | |||
void setRegisterValue(const QString ®isterId, quint16 value) override; | |||
QString registerId2() const { return registerId2_; } | |||
QString compare() const { return compare_; } | |||
void addMenuActions(QMenu *menu) override; | |||
void handleMenuAction(QAction *action) override; | |||
void setCompare(QString) override; | |||
@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE QtCreatorProject> | |||
<!-- Written by QtCreator 4.11.1, 2025-08-12T08:51:43. --> | |||
<!-- Written by QtCreator 4.11.1, 2025-08-12T14:54:10. --> | |||
<qtcreator> | |||
<data> | |||
<variable>EnvironmentId</variable> | |||
@@ -113,6 +113,26 @@ void Light::setOffImage(const QString &imagePath) | |||
update(); | |||
} | |||
void Light::setCurrentSize(QSizeF size) | |||
{ | |||
currentSize_ = size; | |||
} | |||
QSizeF Light::currentSize() | |||
{ | |||
return currentSize_; | |||
} | |||
QString Light::onImagePath() | |||
{ | |||
return onImagePath_; | |||
} | |||
QString Light::offImagePath() | |||
{ | |||
return offImagePath_; | |||
} | |||
void Light::loadImage() | |||
{ | |||
if (!onImagePath_.isEmpty()) { | |||
@@ -16,6 +16,10 @@ public: | |||
void setCustomImage(const QString& imagePath); | |||
void setOnImage(const QString& imagePath); | |||
void setOffImage(const QString& imagePath); | |||
void setCurrentSize(QSizeF size); | |||
QSizeF currentSize(); | |||
QString onImagePath(); | |||
QString offImagePath(); | |||
void loadImage(); | |||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override; | |||
@@ -203,6 +203,25 @@ void MainWindow::applyProjectToScene(const Project &proj, int pageIndex) | |||
if (!item) continue; | |||
item->setPos(d.x, d.y); | |||
item->setRegisterId(d.registerId); | |||
if (d.type == "比较") { | |||
Comparator* compare = dynamic_cast<Comparator*>(item); | |||
if (compare) { | |||
compare->setRegisterId(d.registerId2); | |||
compare->setCompare(d.compare); | |||
if (d.registerId.left(1) == "K") | |||
{ | |||
compare->setRegisterValue(d.registerId, d.registerId.mid(1).toInt()); | |||
} else { | |||
registerManager->bindItem(compare, d.registerId); | |||
} | |||
if (d.registerId2.left(1) == "K") | |||
{ | |||
compare->setRegisterValue(d.registerId2, d.registerId2.mid(1).toInt()); | |||
} else { | |||
registerManager->bindItem(compare, d.registerId2); | |||
} | |||
} | |||
} | |||
connect(item, &Item::requestCopy, plcViews[pageIndex], &MyGraphicsView::onItemRequestCopy); | |||
connect(item, &Item::requestDelete, plcViews[pageIndex], &MyGraphicsView::onItemRequestDelete); | |||
connect(item, &Item::requestBindRegister, plcViews[pageIndex], &MyGraphicsView::onItemRequestBindRegister); | |||
@@ -210,7 +229,10 @@ void MainWindow::applyProjectToScene(const Project &proj, int pageIndex) | |||
connect(item, &Item::requestReset, plcViews[pageIndex], &MyGraphicsView::onItemRequestReset); | |||
connect(item, &Item::requestSetON,plcViews[pageIndex], &MyGraphicsView::onItemRequestSetON); | |||
plcScenes[pageIndex]->addItem(item); | |||
registerManager->bindItem(item,d.registerId); | |||
if (d.type != "比较") | |||
{ | |||
registerManager->bindItem(item,d.registerId); | |||
} | |||
itemObjs.append(item); | |||
} | |||
for (const auto& c : proj.connections_) { | |||
@@ -234,6 +256,20 @@ void MainWindow::applyProjectToScene(const Project &proj, int pageIndex) | |||
if (!item) continue; | |||
item->setPos(d.x, d.y); | |||
item->setRegisterId(d.registerId); | |||
if (d.type == "按钮") { | |||
Button* button = dynamic_cast<Button*>(item); | |||
if (button) { | |||
button->setCustomImage(d.imagePath); | |||
button->setCurrentSize(d.size); | |||
} | |||
} else if (d.type == "指示灯") { | |||
Light* light = dynamic_cast<Light*>(item); | |||
if (light) { | |||
light->setOnImage(d.onImagePath); | |||
light->setOffImage(d.offImagePath); | |||
light->setCurrentSize(d.size); | |||
} | |||
} | |||
connect(item, &Item::requestCopy, hmiViews[pageIndex], &MyGraphicsView::onItemRequestCopy); | |||
connect(item, &Item::requestDelete, hmiViews[pageIndex], &MyGraphicsView::onItemRequestDelete); | |||
connect(item, &Item::requestBindRegister, hmiViews[pageIndex], &MyGraphicsView::onItemRequestBindRegister); | |||
@@ -265,6 +301,13 @@ void MainWindow::extractSceneToProject(Project &proj, int pageIndex) | |||
d.x = it->pos().x(); | |||
d.y = it->pos().y(); | |||
d.registerId = it->registerId(); | |||
if (d.type == "比较") { | |||
Comparator* compare = dynamic_cast<Comparator*>(it); | |||
if (compare) { | |||
d.registerId2 = compare->registerId2(); | |||
d.compare = compare->compare(); | |||
} | |||
} | |||
proj.items_.append(d); | |||
} | |||
for (QGraphicsItem* gi : plcScenes[pageIndex]->items()) { | |||
@@ -298,6 +341,20 @@ void MainWindow::extractSceneToProject(Project &proj, int pageIndex) | |||
d.x = it->pos().x(); | |||
d.y = it->pos().y(); | |||
d.registerId = it->registerId(); | |||
if (d.type == "按钮") { | |||
Button* button = dynamic_cast<Button*>(it); | |||
if (button) { | |||
d.imagePath = button->imagePath(); | |||
d.size = button->currentSize(); | |||
} | |||
} else if (d.type == "指示灯") { | |||
Light* light = dynamic_cast<Light*>(it); | |||
if (light) { | |||
d.onImagePath = light->onImagePath(); | |||
d.offImagePath = light->offImagePath(); | |||
d.size = light->currentSize(); | |||
} | |||
} | |||
proj.items_.append(d); | |||
} | |||
} | |||
@@ -448,7 +505,7 @@ void MainWindow::saveProject() | |||
void MainWindow::connection() | |||
{ | |||
modbusManager->connectToDevice("COM6",QSerialPort::BaudRate(19200),QSerialPort::DataBits(8),QSerialPort::EvenParity,QSerialPort::StopBits(1)); | |||
modbusManager->connectToDevice("COM1",QSerialPort::BaudRate(9600),QSerialPort::DataBits(8),QSerialPort::EvenParity,QSerialPort::StopBits(1)); | |||
modbusManager->startSimulation(1000); | |||
} | |||
@@ -174,54 +174,6 @@ void MyGraphicsView::onItemRequestDelete(Item *item) | |||
delete item; | |||
} | |||
//void MyGraphicsView::onItemRequestBindRegister(Item *item) | |||
//{ | |||
// if (!item) return; | |||
// bool ok = false; | |||
// QString reg = QInputDialog::getText( | |||
// this, | |||
// "绑定寄存器", | |||
// "格式: <类型><地址>\n" | |||
// "类型: D(保持寄存器), M(线圈寄存器)\n" | |||
// "示例: D100, M200\n" | |||
// "地址范围: 0-4000", | |||
// QLineEdit::Normal, | |||
// item->registerId(), | |||
// &ok | |||
// ); | |||
// if (ok && !reg.isEmpty()) { | |||
// // 验证寄存器格式 | |||
// QRegularExpression regex("^[DMdm]\\d{1,4}$"); | |||
// QRegularExpressionMatch match = regex.match(reg); | |||
// if (!match.hasMatch()) { | |||
// QMessageBox::warning(this, "格式错误", "请输入有效的寄存器格式\n示例: D100, M200"); | |||
// return; | |||
// } | |||
// int address = reg.mid(1).toInt(); | |||
// if (address > 4000) { | |||
// QMessageBox::warning(this, "范围错误", "寄存器地址必须在0-4000范围内"); | |||
// return; | |||
// } | |||
// // 标准化格式 (D100) | |||
// QString newReg = QString("%1%2").arg(reg[0].toUpper()).arg(address); | |||
// if (item->setRegisterId(newReg)) | |||
// { | |||
// item->update(); | |||
// // 发出绑定信号 | |||
// emit itemBoundToRegister(item, newReg); | |||
// } | |||
// else | |||
// { | |||
// QMessageBox::warning(this, "提示", "请先重置再绑定新的寄存器"); | |||
// } | |||
// } | |||
//} | |||
void MyGraphicsView::onItemRequestBindRegister(Item *item) | |||
{ | |||
if (!item) return; | |||
@@ -273,8 +225,8 @@ void MyGraphicsView::onItemRequestBindRegister(Item *item) | |||
// 对于比较元件,直接设置值 | |||
if (item->itemType() == "比较" && type == "K") { | |||
// 常数直接设置值 | |||
item->setRegisterId(type); | |||
item->setRegisterValue(type, value); | |||
item->setRegisterId(newReg); | |||
item->setRegisterValue(newReg, value); | |||
item->update(); | |||
}else { | |||
// 寄存器绑定 | |||
@@ -13,6 +13,15 @@ bool Project::saveToFile(const QString& filePath) const { | |||
obj["x"] = item.x; | |||
obj["y"] = item.y; | |||
obj["registerId"] = item.registerId; | |||
obj["registerId2"] = item.registerId2; | |||
obj["compare"] = item.compare; | |||
obj["imagePath"] = item.imagePath; | |||
obj["onImagePath"] = item.onImagePath; | |||
obj["offImagePath"] = item.offImagePath; | |||
obj["width"] = item.size.width(); | |||
obj["height"] = item.size.height(); | |||
itemsArray.append(obj); | |||
} | |||
root["items"] = itemsArray; | |||
@@ -54,6 +63,18 @@ bool Project::loadFromFile(const QString& filePath) { | |||
d.x = obj["x"].toDouble(); | |||
d.y = obj["y"].toDouble(); | |||
d.registerId = obj.contains("registerId") ? obj["registerId"].toString() : ""; | |||
d.registerId2 = obj.contains("registerId2") ? obj["registerId2"].toString() : ""; | |||
d.compare = obj.contains("compare") ? obj["compare"].toString() : "CP"; | |||
d.imagePath = obj.contains("imagePath") ? obj["imagePath"].toString() : ""; | |||
d.onImagePath = obj.contains("onImagePath") ? obj["onImagePath"].toString() : ""; | |||
d.offImagePath = obj.contains("offImagePath") ? obj["offImagePath"].toString() : ""; | |||
// 加载尺寸信息 | |||
double width = obj.contains("width") ? obj["width"].toDouble() : 50.0; | |||
double height = obj.contains("height") ? obj["height"].toDouble() : 50.0; | |||
d.size = QSizeF(width, height); | |||
items_.append(d); | |||
} | |||
QJsonArray connArray = root["connections"].toArray(); | |||
@@ -3,6 +3,7 @@ | |||
#include <QString> | |||
#include <QList> | |||
#include <QSizeF> | |||
// PLC工程数据模型 | |||
class Project | |||
@@ -12,6 +13,13 @@ public: | |||
QString type; | |||
double x, y; | |||
QString registerId = ""; | |||
QString registerId2 = ""; | |||
QString compare = ""; | |||
QString imagePath; // 按钮的图片路径 | |||
QString onImagePath; // 指示灯的ON状态图片路径 | |||
QString offImagePath; // 指示灯的OFF状态图片路径 | |||
QSizeF size = QSizeF(50, 50); // 当前尺寸 | |||
}; | |||
struct ConnectionData { | |||
int from, to; | |||