Improve terminal_strip

This commit is contained in:
Laurent Trinques
2026-10-08 23:32:29 +02:00
parent 3420ce2ee6
commit 36cfa938e9
8 changed files with 333 additions and 14 deletions
@@ -38,10 +38,13 @@ namespace TerminalStripDrawer
class DemoRealTerminal : public AbstractRealTerminalInterface
{
public:
DemoRealTerminal(const QString &label, const QString &xref, const QUuid &bridge) :
DemoRealTerminal(const QString &label, const QString &xref, const QUuid &bridge,
ElementData::TerminalType type = ElementData::TTGeneric, bool led = false) :
m_label { label },
m_xref{ xref },
m_bridge { bridge }
m_bridge { bridge },
m_type { type },
m_led { led }
{}
QString label() const override {
@@ -60,9 +63,19 @@ namespace TerminalStripDrawer
return m_xref;
}
ElementData::TerminalType type() const override {
return m_type;
}
bool isLed() const override {
return m_led;
}
private:
QString m_label, m_xref;
QUuid m_bridge;
ElementData::TerminalType m_type;
bool m_led;
};
class DemoPhysicalTerminal : public AbstractPhysicalTerminalInterface
@@ -109,19 +122,19 @@ namespace TerminalStripDrawer
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("24vdc"),
QStringLiteral("1_A1"),
lvl_1)};
lvl_1, ElementData::TTFuse, false)};
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("0vdc"),
QStringLiteral("1_A2"),
lvl_2)};
lvl_2, ElementData::TTSectional, false)};
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("signal"),
QStringLiteral("1_A3"),
lvl_3)};
lvl_3, ElementData::TTDiode, false)};
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("teach"),
QStringLiteral("1_A4"),
lvl_4)};
lvl_4, ElementData::TTGround, false)};
m_physical_terminal << QSharedPointer<AbstractPhysicalTerminalInterface> {
new DemoPhysicalTerminal {real_terminals_vector}};
@@ -129,11 +142,11 @@ namespace TerminalStripDrawer
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("24vdc"),
QStringLiteral("2_A1"),
lvl_1)};
lvl_1, ElementData::TTGeneric, true)};
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("0vdc"),
QStringLiteral("2_A2"),
lvl_2)};
lvl_2, ElementData::TTFuse, true)};
real_terminals_vector << QSharedPointer<AbstractRealTerminalInterface> {
new DemoRealTerminal( QStringLiteral("signal"),
QStringLiteral("2_A3"),
@@ -44,7 +44,7 @@ class TerminalStripLayoutPattern
TerminalStripLayoutPattern();
//Header of terminal strip
QRectF m_header_rect{0,30,50,130};
QRectF m_header_rect{0,30,50,146};
Qt::Orientation m_header_text_orientation{Qt::Horizontal};
void setHeaderTextAlignment(const Qt::Alignment &alignment);
Qt::Alignment headerTextAlignment() const;
@@ -60,10 +60,10 @@ class TerminalStripLayoutPattern
//Terminals
QVector<QRectF> m_terminal_rect
{
QRectF{0, 0, 20, 190},
QRectF{0, 10, 20, 170},
QRectF{0, 20, 20, 150},
QRectF{0, 30, 20, 130}
QRectF{0, 0, 20, 206},
QRectF{0, 10, 20, 186},
QRectF{0, 20, 20, 166},
QRectF{0, 30, 20, 146}
};
//Terminal text
@@ -82,6 +82,13 @@ class TerminalStripLayoutPattern
qreal m_xref_text_y{95};
Qt::Orientation m_xref_text_orientation {Qt::Vertical};
//Symbol of the terminal type (fuse, sectional, diode, ground, led).
//A height of 0 disable the symbol.
qreal m_type_symbol_y{160};
qreal m_type_symbol_height{14};
//Length of the connection drawn above and under each terminal,
//in the middle of the terminal. 0 disable the connections.
qreal m_connection_length{10};
qreal m_bridge_point_d{5};
QVector<qreal> m_bridge_point_y_offset{50,70,90,110};
@@ -20,6 +20,8 @@
#include <QPainter>
#include <QHash>
#include <algorithm>
namespace TerminalStripDrawer {
/**
@@ -38,6 +40,156 @@ void TerminalStripDrawer::setStrip(QSharedPointer<AbstractTerminalStripInterface
m_strip = strip;
}
namespace {
/*
* The symbols are described in a box of 20 x 14 units centered on (0,0).
* The current flow from the top to the bottom, like in the terminal strip.
* drawTypeSymbol() scale this box to fit the real available area.
*/
constexpr qreal symbol_width{20};
constexpr qreal symbol_height{14};
void drawFuseSymbol(QPainter *painter)
{
painter->drawLine(QPointF{0, -7}, QPointF{0, 7});
painter->drawRect(QRectF{-3, -5, 6, 10});
}
void drawSectionalSymbol(QPainter *painter)
{
painter->drawLine(QPointF{0, -7}, QPointF{0, 7});
painter->drawLine(QPointF{-4, 2}, QPointF{4, -2});
}
void drawDiodeSymbol(QPainter *painter)
{
const qreal t{symbol_height / 3};
const QPointF triangle[3] { {-t, -t}, {t, -t}, {0, t} };
painter->drawLine(QPointF{0, -7}, QPointF{0, -t});
painter->drawPolygon(triangle, 3);
painter->drawLine(QPointF{-t, t}, QPointF{t, t});
painter->drawLine(QPointF{0, t}, QPointF{0, 7});
}
void drawLedSymbol(QPainter *painter)
{
drawDiodeSymbol(painter);
const qreal t{symbol_height / 3};
//The two rays
painter->drawLine(QPointF{t + 0.5, -1}, QPointF{t + 3.5, -4});
painter->drawLine(QPointF{t + 0.5, 2}, QPointF{t + 3.5, -1});
}
void drawGroundSymbol(QPainter *painter)
{
painter->drawLine(QPointF{0, -7}, QPointF{0, -2});
painter->drawLine(QPointF{-5, -2}, QPointF{5, -2});
painter->drawLine(QPointF{-3, 1}, QPointF{3, 1});
painter->drawLine(QPointF{-1, 4}, QPointF{1, 4});
}
/**
* @brief drawScaled
* Call @a draw with the painter moved by @a dx and scaled by @a factor.
* The width of the pen is kept constant.
*/
template<typename Func>
void drawScaled(QPainter *painter, qreal dx, qreal factor, Func draw)
{
painter->save();
painter->translate(dx, 0);
painter->scale(factor, factor);
auto pen_{painter->pen()};
pen_.setWidthF(pen_.widthF() / factor);
painter->setPen(pen_);
draw(painter);
painter->restore();
}
/**
* @brief drawTypeSymbol
* Draw the symbol of a terminal type in @a box. The symbol is scaled
* to fit in @a box, so it never overflow on the neighbouring terminal.
* The painter must have a pen set, the brush is ignored.
* @param painter
* @param box : area available for the symbol, in the painter coordinates
* @param type : type of the terminal, a generic terminal have no symbol
* @param led : if true a led symbol is drawn. If the terminal have
* also a type (fuse...), the type symbol and the led are drawn side by side.
*/
void drawTypeSymbol(QPainter *painter, const QRectF &box, ElementData::TerminalType type, bool led)
{
const bool have_type{type != ElementData::TTGeneric};
if (!led && !have_type) {
return;
}
const qreal scale_{qMin(box.width() / symbol_width, box.height() / symbol_height)};
if (scale_ <= 0) {
return;
}
painter->save();
painter->setBrush(Qt::NoBrush);
painter->translate(box.center());
painter->scale(scale_, scale_);
auto pen_{painter->pen()};
pen_.setWidthF(pen_.widthF() / scale_);
painter->setPen(pen_);
auto draw_type = [type](QPainter *p)
{
switch (type)
{
case ElementData::TTFuse : drawFuseSymbol(p); break;
case ElementData::TTSectional : drawSectionalSymbol(p); break;
case ElementData::TTDiode : drawDiodeSymbol(p); break;
case ElementData::TTGround : drawGroundSymbol(p); break;
default: break;
}
};
if (led && have_type)
{
//Not enough place for two full size symbols, draw them smaller side by side
drawScaled(painter, -5, 0.7, draw_type);
drawScaled(painter, 2.5, 0.7, drawLedSymbol);
}
else if (led) {
drawLedSymbol(painter);
}
else {
draw_type(painter);
}
painter->restore();
}
/**
* @brief drawConnections
* Draw a connection above and a connection under @a terminal_rect, in the middle
* of the rect. A connection is a short line ended by a little circle.
* @param painter
* @param terminal_rect
* @param length : the total length of a connection, circle included
*/
void drawConnections(QPainter *painter, const QRectF &terminal_rect, qreal length)
{
const qreal radius{qMin<qreal>(length / 4, 2)};
const qreal x{terminal_rect.width() / 2};
const qreal top{terminal_rect.top()};
const qreal bottom{terminal_rect.top() + terminal_rect.height()};
painter->drawLine(QPointF{x, top}, QPointF{x, top - length + radius * 2});
painter->drawEllipse(QPointF{x, top - length + radius}, radius, radius);
painter->drawLine(QPointF{x, bottom}, QPointF{x, bottom + length - radius * 2});
painter->drawEllipse(QPointF{x, bottom + length - radius}, radius, radius);
}
} //End anonymous namespace
/**
* @brief TerminalStripDrawer::paint
* @param painter
@@ -141,6 +293,21 @@ void TerminalStripDrawer::paint(QPainter *painter)
terminal_rect = m_pattern->m_terminal_rect[index_];
//Draw terminal rect
painter->drawRect(terminal_rect);
//Draw the symbol of the terminal type (fuse, ground, led...)
if (m_pattern->m_type_symbol_height > 0 && real_terminal_vector[i])
{
drawTypeSymbol(painter,
QRectF{0, m_pattern->m_type_symbol_y,
terminal_rect.width(), m_pattern->m_type_symbol_height},
real_terminal_vector[i]->type(),
real_terminal_vector[i]->isLed());
}
//Draw the connections, above and under the terminal
if (m_pattern->m_connection_length > 0) {
drawConnections(painter, terminal_rect, m_pattern->m_connection_length);
}
//Draw a stronger line if the current terminal have level
//and the current level is the first
if (real_t_count > 1 && i == 0)
@@ -280,7 +447,21 @@ void TerminalStripDrawer::paint(QPainter *painter)
QRectF TerminalStripDrawer::boundingRect() const
{
return QRectF{0, 0, width(), height()};;
QRectF rect_{0, 0, width(), height()};
//The connections are drawn above and under the terminals
if (m_pattern && m_pattern->m_connection_length > 0)
{
const auto length_{m_pattern->m_connection_length};
qreal top_{0};
for (const auto &terminal_rect : std::as_const(m_pattern->m_terminal_rect)) {
top_ = std::min(top_, terminal_rect.top() - length_);
}
rect_.setBottom(rect_.bottom() + length_);
rect_.setTop(top_);
}
return rect_;
}
void TerminalStripDrawer::setLayout(QSharedPointer<TerminalStripLayoutPattern> layout)
@@ -22,6 +22,7 @@
#include <QUuid>
#include "properties/terminalstriplayoutpattern.h"
#include "../../properties/elementdata.h"
class QPainter;
class TerminalStrip;
@@ -56,6 +57,10 @@ namespace TerminalStripDrawer
virtual bool isBridged() const = 0;
virtual AbstractBridgeInterface* bridge() const = 0;
virtual QString xref() const = 0;
//Type and led are only used to draw the symbol of the terminal.
//They are not pure virtual, so an implementation can ignore them.
virtual ElementData::TerminalType type() const { return ElementData::TTGeneric; }
virtual bool isLed() const { return false; }
};
class AbstractPhysicalTerminalInterface
@@ -128,6 +128,16 @@ namespace TerminalStripDrawer
}
}
ElementData::TerminalType TrueRealTerminal::type() const
{
return m_real ? m_real->type() : ElementData::TTGeneric;
}
bool TrueRealTerminal::isLed() const
{
return m_real ? m_real->isLed() : false;
}
TrueBridge::TrueBridge(QSharedPointer<TerminalStripBridge> bridge) :
m_bridge { bridge }
{}
@@ -59,6 +59,8 @@ namespace TerminalStripDrawer
bool isBridged() const override;
AbstractBridgeInterface* bridge() const override;
QString xref() const override;
ElementData::TerminalType type() const override;
bool isLed() const override;
private:
QSharedPointer<RealTerminal> m_real;
@@ -151,6 +151,11 @@ void TerminalStripLayoutEditor::valueEdited()
m_layout.data()->m_xref_text_y = ui->m_xref_y_sb->value();
m_layout.data()->m_xref_text_height = ui->m_xref_height_sb->value();
//Terminal type symbol
m_layout.data()->m_type_symbol_y = ui->m_type_symbol_y_sb->value();
m_layout.data()->m_type_symbol_height = ui->m_type_symbol_height_sb->value();
m_layout.data()->m_connection_length = ui->m_connection_length_sb->value();
updateUi();
m_preview_strip_item.update();
}
@@ -256,6 +261,10 @@ void TerminalStripLayoutEditor::updateUi()
ui->m_xref_y_sb->setValue(data->m_xref_text_y);
ui->m_xref_height_sb->setValue(data->m_xref_text_height);
ui->m_type_symbol_y_sb->setValue(data->m_type_symbol_y);
ui->m_type_symbol_height_sb->setValue(data->m_type_symbol_height);
ui->m_connection_length_sb->setValue(data->m_connection_length);
m_ui_updating = false;
updatePreview();
}
@@ -459,6 +459,50 @@
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QLabel" name="label_symbol">
<property name="text">
<string>Symbol</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="7" column="6">
<widget class="QSpinBox" name="m_type_symbol_y_sb">
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="8" column="6">
<widget class="QSpinBox" name="m_type_symbol_height_sb">
<property name="toolTip">
<string>Height of the terminal type symbol (fuse, ground, led...). 0 disable the symbol.</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="9" column="2">
<widget class="QLabel" name="label_connection">
<property name="text">
<string>Connection length</string>
</property>
</widget>
</item>
<item row="9" column="3">
<widget class="QSpinBox" name="m_connection_length_sb">
<property name="toolTip">
<string>Length of the connections drawn above and under each terminal. 0 disable the connections.</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -1094,6 +1138,54 @@
</hint>
</hints>
</connection>
<connection>
<sender>m_type_symbol_y_sb</sender>
<signal>valueChanged(int)</signal>
<receiver>TerminalStripLayoutEditor</receiver>
<slot>valueEdited()</slot>
<hints>
<hint type="sourcelabel">
<x>900</x>
<y>398</y>
</hint>
<hint type="destinationlabel">
<x>480</x>
<y>311</y>
</hint>
</hints>
</connection>
<connection>
<sender>m_type_symbol_height_sb</sender>
<signal>valueChanged(int)</signal>
<receiver>TerminalStripLayoutEditor</receiver>
<slot>valueEdited()</slot>
<hints>
<hint type="sourcelabel">
<x>900</x>
<y>398</y>
</hint>
<hint type="destinationlabel">
<x>480</x>
<y>311</y>
</hint>
</hints>
</connection>
<connection>
<sender>m_connection_length_sb</sender>
<signal>valueChanged(int)</signal>
<receiver>TerminalStripLayoutEditor</receiver>
<slot>valueEdited()</slot>
<hints>
<hint type="sourcelabel">
<x>700</x>
<y>431</y>
</hint>
<hint type="destinationlabel">
<x>480</x>
<y>311</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>valueEdited()</slot>