Explorar el Código

feat: 优化梯形图编辑器画布呈现

main
suyu hace 1 mes
padre
commit
5d4bbfc758
Se han modificado 1 ficheros con 189 adiciones y 56 borrados
  1. +189
    -56
      app/src/ui/logic_editor_widget.cpp

+ 189
- 56
app/src/ui/logic_editor_widget.cpp Ver fichero

@@ -2,7 +2,9 @@

#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsTextItem>
#include <QPainter>
#include <QPainterPath>
#include <QResizeEvent>
#include <QStyleOptionGraphicsItem>

@@ -15,9 +17,20 @@ namespace {
constexpr qreal kMinimumSceneWidth = 1280.0;
constexpr qreal kLeftRailX = 72.0;
constexpr qreal kStageWidth = 184.0;
constexpr qreal kNodeWidth = 118.0;
constexpr qreal kBranchSpacing = 58.0;
constexpr qreal kRungGap = 30.0;
constexpr qreal kNodeWidth = 128.0;
constexpr qreal kNodeTerminalX = kNodeWidth / 2.0;
constexpr qreal kBranchSpacing = 96.0;
constexpr qreal kRungGap = 18.0;
constexpr qreal kRungBaseHeight = 132.0;
constexpr qreal kMainLineOffset = 78.0;
constexpr qreal kLadderLineWidth = 1.6;
constexpr qreal kPlaceholderWidth = 112.0;
constexpr qreal kPlaceholderHeight = 30.0;

const QColor kLadderColor(QStringLiteral("#26343d"));
const QColor kSelectionColor(QStringLiteral("#dcecf4"));
const QColor kSelectionBorderColor(QStringLiteral("#1677a8"));
const QColor kPlaceholderColor(QStringLiteral("#82919b"));

QString comparisonText(ComparisonOperator comparison)
{
@@ -33,21 +46,46 @@ QString comparisonText(ComparisonOperator comparison)
}
}

QString nodeAddressText(const LogicNodeConfig &config)
QString registerAddressText(const RegisterAddress &address)
{
return QStringLiteral("%1%2")
.arg(address.area() == RegisterArea::M ? QStringLiteral("M")
: QStringLiteral("D"))
.arg(address.index());
}

QString nodeToolTip(const LogicNodeConfig &config)
{
return std::visit(
[](const auto &value) -> QString
{
using Config = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<Config, ContactNodeConfig>
|| std::is_same_v<Config, CoilNodeConfig>)
if constexpr (std::is_same_v<Config, ContactNodeConfig>)
{
return value.mode == ContactMode::NormallyOpen
? LogicEditorWidget::tr("常开触点 %1").arg(
registerAddressText(value.address))
: LogicEditorWidget::tr("常闭触点 %1").arg(
registerAddressText(value.address));
}
else if constexpr (std::is_same_v<Config, CoilNodeConfig>)
{
return QStringLiteral("M%1").arg(value.address.index());
QString mode = LogicEditorWidget::tr("普通线圈");
if (value.mode == CoilMode::Set)
{
mode = LogicEditorWidget::tr("置位线圈");
}
else if (value.mode == CoilMode::Reset)
{
mode = LogicEditorWidget::tr("复位线圈");
}
return QStringLiteral("%1 %2").arg(
mode, registerAddressText(value.address));
}
else
{
return QStringLiteral("D%1 %2 %3")
.arg(value.address.index())
return LogicEditorWidget::tr("比较条件:%1 %2 %3")
.arg(registerAddressText(value.address))
.arg(comparisonText(value.comparison))
.arg(value.value);
}
@@ -62,7 +100,30 @@ qreal rungHeight(const LadderRung &rung)
{
maximum_branches = std::max(maximum_branches, stage.branches.size());
}
return 100.0 + static_cast<qreal>(maximum_branches - 1U) * kBranchSpacing;
return kRungBaseHeight
+ static_cast<qreal>(maximum_branches - 1U) * kBranchSpacing;
}

void addPlaceholder(
QGraphicsScene &scene,
const QPointF &center,
const QString &text,
qreal width = kPlaceholderWidth)
{
const QRectF bounds(
center.x() - width / 2.0,
center.y() - kPlaceholderHeight / 2.0,
width,
kPlaceholderHeight);
scene.addRect(
bounds,
QPen(kPlaceholderColor, 1.2, Qt::DashLine),
QBrush(Qt::white));
QGraphicsTextItem *label = scene.addText(text);
label->setDefaultTextColor(kPlaceholderColor);
label->setPos(
center.x() - label->boundingRect().width() / 2.0,
center.y() - label->boundingRect().height() / 2.0);
}

} // namespace
@@ -82,11 +143,13 @@ public:
{
setPos(center);
setFlag(ItemIsSelectable, true);
setZValue(1.0);
setToolTip(nodeToolTip(config_));
}

QRectF boundingRect() const override
{
return {-kNodeWidth / 2.0, -27.0, kNodeWidth, 54.0};
return {-kNodeWidth / 2.0, -46.0, kNodeWidth, 92.0};
}

void paint(
@@ -98,27 +161,45 @@ public:
const bool selected = (option->state & QStyle::State_Selected) != 0;
if (selected)
{
painter->fillRect(
boundingRect(), QColor(QStringLiteral("#dceef7")));
painter->fillRect(boundingRect(), kSelectionColor);
}
painter->setPen(QPen(
selected ? QColor(QStringLiteral("#1677a8"))
: QColor(QStringLiteral("#1f2b33")),
selected ? 2.0 : 1.6));
const QColor symbol_color = selected ? kSelectionBorderColor : kLadderColor;
painter->setPen(QPen(symbol_color, selected ? 2.0 : kLadderLineWidth));

QFont text_font = painter->font();
text_font.setPointSizeF(9.5);
painter->setFont(text_font);

if (const auto *contact = std::get_if<ContactNodeConfig>(&config_))
{
painter->fillRect(QRectF(-24, -21, 48, 42), Qt::white);
painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-18, 0));
painter->drawLine(QPointF(18, 0), QPointF(kNodeTerminalX, 0));
painter->drawLine(QPointF(-18, -15), QPointF(-18, 15));
painter->drawLine(QPointF(18, -15), QPointF(18, 15));
if (contact->mode == ContactMode::NormallyClosed)
{
painter->drawLine(QPointF(-23, 18), QPointF(23, -18));
}
painter->setPen(kLadderColor);
painter->drawText(
QRectF(-kNodeWidth / 2.0, -44, kNodeWidth, 18),
Qt::AlignCenter,
registerAddressText(contact->address));
}
else if (const auto *coil = std::get_if<CoilNodeConfig>(&config_))
{
painter->drawArc(QRectF(-29, -20, 30, 40), -80 * 16, 160 * 16);
painter->drawArc(QRectF(-1, -20, 30, 40), 100 * 16, 160 * 16);
painter->fillRect(QRectF(-34, -23, 68, 46), Qt::white);
painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-16, 0));
painter->drawLine(QPointF(16, 0), QPointF(kNodeTerminalX, 0));
QPainterPath left_coil;
left_coil.moveTo(-2, -20);
left_coil.cubicTo(-24, -16, -24, 16, -2, 20);
painter->drawPath(left_coil);
QPainterPath right_coil;
right_coil.moveTo(2, -20);
right_coil.cubicTo(24, -16, 24, 16, 2, 20);
painter->drawPath(right_coil);
if (coil->mode == CoilMode::Set || coil->mode == CoilMode::Reset)
{
painter->drawText(
@@ -127,16 +208,36 @@ public:
coil->mode == CoilMode::Set ? QStringLiteral("S")
: QStringLiteral("R"));
}
painter->setPen(kLadderColor);
painter->drawText(
QRectF(-kNodeWidth / 2.0, -44, kNodeWidth, 18),
Qt::AlignCenter,
registerAddressText(coil->address));
}
else
else if (const auto *comparison = std::get_if<CompareNodeConfig>(&config_))
{
painter->drawRect(QRectF(-43, -18, 86, 36));
constexpr qreal box_half_width = 47.0;
const QRectF instruction_box(-box_half_width, -17, 94, 34);
painter->fillRect(instruction_box.adjusted(-1, -1, 1, 1), Qt::white);
painter->drawLine(
QPointF(-kNodeTerminalX, 0), QPointF(-box_half_width, 0));
painter->drawLine(
QPointF(box_half_width, 0), QPointF(kNodeTerminalX, 0));
painter->drawRect(instruction_box);
painter->drawText(
instruction_box,
Qt::AlignCenter,
QStringLiteral("%1 INT").arg(comparisonText(comparison->comparison)));
painter->setPen(kLadderColor);
painter->drawText(
QRectF(-kNodeWidth / 2.0, -44, kNodeWidth, 18),
Qt::AlignCenter,
registerAddressText(comparison->address));
painter->drawText(
QRectF(-kNodeWidth / 2.0, 22, kNodeWidth, 18),
Qt::AlignCenter,
QString::number(comparison->value));
}
painter->setPen(QColor(QStringLiteral("#263640")));
painter->drawText(
QRectF(-kNodeWidth / 2.0, -42, kNodeWidth, 18),
Qt::AlignCenter,
nodeAddressText(config_));
}

const std::string &nodeId() const { return node_id_; }
@@ -186,10 +287,15 @@ public:
boundingRect(), QColor(QStringLiteral("#f0f7fa")));
}
painter->setPen(QColor(QStringLiteral("#62717b")));
QString title = QStringLiteral("网络 %1").arg(number_);
if (!name_.isEmpty() && !name_.startsWith(QStringLiteral("网络 ")))
{
title += QStringLiteral(":") + name_;
}
painter->drawText(
QRectF(28, 6, 260, 22),
Qt::AlignLeft | Qt::AlignVCenter,
QStringLiteral("%1 %2").arg(number_).arg(name_));
title);
painter->setPen(QPen(QColor(QStringLiteral("#d4dce1")), 1));
painter->drawLine(
QPointF(28, height_ - 1), QPointF(scene_width_ - 28, height_ - 1));
@@ -267,14 +373,14 @@ void LogicEditorWidget::reloadLogic()
{
const qreal height = rungHeight(rung);
scene_->addItem(new RungItem(rung, rung_number, top, height, scene_width));
const qreal main_y = top + 62.0;
const qreal main_y = top + kMainLineOffset;

scene_->addLine(
kLeftRailX, top + 32.0, kLeftRailX, top + height - 18.0,
QPen(QColor(QStringLiteral("#202a31")), 3));
kLeftRailX, top + 38.0, kLeftRailX, top + height - 16.0,
QPen(kLadderColor, 2.4));
scene_->addLine(
right_rail_x, top + 32.0, right_rail_x, top + height - 18.0,
QPen(QColor(QStringLiteral("#202a31")), 3));
right_rail_x, top + 38.0, right_rail_x, top + height - 16.0,
QPen(kLadderColor, 2.4));

qreal cursor_x = kLeftRailX;
for (const LadderStage &stage : rung.stages)
@@ -284,27 +390,27 @@ void LogicEditorWidget::reloadLogic()
const qreal right_join = next_x - 18.0;
scene_->addLine(
cursor_x, main_y, left_join, main_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
QPen(kLadderColor, kLadderLineWidth));
scene_->addLine(
right_join, main_y, next_x, main_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
QPen(kLadderColor, kLadderLineWidth));
if (stage.branches.size() > 1U)
{
const qreal bottom_y = main_y
+ static_cast<qreal>(stage.branches.size() - 1U) * kBranchSpacing;
scene_->addLine(
left_join, main_y, left_join, bottom_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
QPen(kLadderColor, kLadderLineWidth));
scene_->addLine(
right_join, main_y, right_join, bottom_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
QPen(kLadderColor, kLadderLineWidth));
}
for (std::size_t branch = 0; branch < stage.branches.size(); ++branch)
{
const qreal branch_y = main_y + static_cast<qreal>(branch) * kBranchSpacing;
scene_->addLine(
left_join, branch_y, right_join, branch_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
QPen(kLadderColor, kLadderLineWidth));
scene_->addItem(new NodeItem(
stage.branches.at(branch),
rung.id,
@@ -314,32 +420,58 @@ void LogicEditorWidget::reloadLogic()
cursor_x = next_x;
}

const qreal output_x = right_rail_x - 116.0;
scene_->addLine(
cursor_x, main_y, output_x, main_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
scene_->addLine(
output_x, main_y, right_rail_x, main_y,
QPen(QColor(QStringLiteral("#202a31")), 2));
const qreal output_center_x = right_rail_x - 68.0;
if (rung.stages.empty())
{
const qreal condition_center_x = kLeftRailX + kStageWidth / 2.0;
scene_->addLine(
kLeftRailX,
main_y,
condition_center_x - kPlaceholderWidth / 2.0,
main_y,
QPen(kLadderColor, kLadderLineWidth));
scene_->addLine(
condition_center_x + kPlaceholderWidth / 2.0,
main_y,
output_center_x,
main_y,
QPen(kLadderColor, kLadderLineWidth));
addPlaceholder(
*scene_,
QPointF(condition_center_x, main_y),
tr("添加条件"));
}
else
{
scene_->addLine(
cursor_x, main_y, output_center_x, main_y,
QPen(kLadderColor, kLadderLineWidth));
}
if (rung.output.has_value())
{
scene_->addLine(
output_center_x, main_y, right_rail_x, main_y,
QPen(kLadderColor, kLadderLineWidth));
scene_->addItem(new NodeItem(
*rung.output,
rung.id,
{},
QPointF(output_x + 50.0, main_y)));
QPointF(output_center_x, main_y)));
}
else
{
auto *placeholder = scene_->addText(QStringLiteral("< 输出线圈 >"));
placeholder->setDefaultTextColor(QColor(QStringLiteral("#87949c")));
placeholder->setPos(output_x + 4.0, main_y - 14.0);
}
if (rung.stages.empty())
{
auto *placeholder = scene_->addText(QStringLiteral("< 添加条件 >"));
placeholder->setDefaultTextColor(QColor(QStringLiteral("#87949c")));
placeholder->setPos(kLeftRailX + 32.0, main_y - 14.0);
constexpr qreal output_placeholder_width = 96.0;
scene_->addLine(
output_center_x + output_placeholder_width / 2.0,
main_y,
right_rail_x,
main_y,
QPen(kLadderColor, kLadderLineWidth));
addPlaceholder(
*scene_,
QPointF(output_center_x, main_y),
tr("输出线圈"),
output_placeholder_width);
}
top += height + kRungGap;
++rung_number;
@@ -450,7 +582,7 @@ LogicEditorResult LogicEditorWidget::addParallelCondition(
{
result = {false,
LogicEditorError::InvalidOperation,
"select a condition before adding a parallel branch",
"请先选择要添加并联分支的触点或比较条件",
{}};
}
else
@@ -501,7 +633,8 @@ LogicEditorResult LogicEditorWidget::deleteSelected()
const std::string rung_id = selectedRungId();
if (rung_id.empty())
{
return {false, LogicEditorError::InvalidOperation, "no ladder object is selected", {}};
return {false, LogicEditorError::InvalidOperation,
"请先选择要删除的逻辑节点或网络", {}};
}
result = editor_service_.removeRung(logic_id_, rung_id);
if (result.succeeded && current_rung_id_ == rung_id)


Cargando…
Cancelar
Guardar