mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-06 21:34:13 +02:00
Merge branch 'master' into qt6_cmake_joshua
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include "../../qetinformation.h"
|
||||
#include "../../qetproject.h"
|
||||
#include "../../qetxml.h"
|
||||
#include "../../utils/qetutils.h"
|
||||
|
||||
#include <QSqlError>
|
||||
#include <QSqlRecord>
|
||||
@@ -252,7 +253,7 @@ QDomElement ProjectDBModel::toXml(QDomDocument &document) const
|
||||
|
||||
//Add index 0,0 data
|
||||
auto index_00 = document.createElement("index00");
|
||||
index_00.setAttribute("font", m_index_0_0_data.value(Qt::FontRole).toString());
|
||||
index_00.setAttribute("font", QETUtils::fontToString(m_index_0_0_data.value(Qt::FontRole).value<QFont>()));
|
||||
auto me = QMetaEnum::fromType<Qt::Alignment>();
|
||||
index_00.setAttribute("alignment", me.valueToKey(m_index_0_0_data.value(Qt::TextAlignmentRole).toInt()));
|
||||
dom_element.appendChild(index_00);
|
||||
@@ -290,7 +291,7 @@ void ProjectDBModel::fromXml(const QDomElement &element)
|
||||
//Index 0,0
|
||||
auto index_00 = element.firstChildElement("index00");
|
||||
QFont font_;
|
||||
font_.fromString(index_00.attribute("font"));
|
||||
QETUtils::fontFromString(font_, index_00.attribute("font"));
|
||||
m_index_0_0_data.insert(Qt::FontRole, font_);
|
||||
auto me = QMetaEnum::fromType<Qt::Alignment>();
|
||||
m_index_0_0_data.insert(Qt::TextAlignmentRole, me.keyToValue(index_00.attribute("alignment").toStdString().data()));
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qetgraphicstableitem.h"
|
||||
|
||||
#include "../../qetproject.h"
|
||||
#include "../../QPropertyUndoCommand/qpropertyundocommand.h"
|
||||
#include "../../createdxf.h"
|
||||
#include "../../diagram.h"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
||||
#include "../autoNum/numerotationcontextcommands.h"
|
||||
#include "../conductorautonumerotation.h"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "crossrefitem.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../diagram.h"
|
||||
#include "../diagramposition.h"
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "elementtextitemgroup.h"
|
||||
#include "qgraphicsitemutility.h"
|
||||
#include "terminal.h"
|
||||
#include "../properties/elementdata.h"
|
||||
|
||||
//define the height of the header.
|
||||
static int header = 5;
|
||||
@@ -235,8 +236,11 @@ void CrossRefItem::updateLabel()
|
||||
qp.setPen(pen_);
|
||||
qp.setFont(QETApp::diagramTextsFont(5));
|
||||
|
||||
//Draw cross or contact, only if master element is linked.
|
||||
if (! m_element->linkedElements().isEmpty())
|
||||
// PLC table is drawn by Element::drawPlcTable(), not by CrossRefItem
|
||||
if (m_element->elementData().m_master_type == ElementData::PLC)
|
||||
return;
|
||||
//Draw cross or contact, only if master element is linked.
|
||||
else if (! m_element->linkedElements().isEmpty())
|
||||
{
|
||||
m_update_map = true;
|
||||
XRefProperties::DisplayHas dh = m_properties.displayHas();
|
||||
@@ -325,6 +329,11 @@ void CrossRefItem::paint(
|
||||
// caused a use-after-free crash (QRegion::begin, Qt5Gui+0x49af60)
|
||||
// confirmed by analysis of 19+ coredumps.
|
||||
// m_update_map=false: draw functions do not overwrite m_hovered_contacts_map.
|
||||
|
||||
// PLC table is drawn by Element::drawPlcTable(), not by CrossRefItem
|
||||
if (m_element->elementData().m_master_type == ElementData::PLC)
|
||||
return;
|
||||
|
||||
if (m_element->linkedElements().isEmpty()) return;
|
||||
|
||||
QPen pen_;
|
||||
@@ -928,7 +937,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
|
||||
if (m_update_map)
|
||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
||||
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||
|
||||
++m_drawed_contacts;
|
||||
}
|
||||
@@ -961,22 +970,21 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
painter.drawPolyline(p2, 3);
|
||||
|
||||
// Draw terminal names for switch contact (3 terminals)
|
||||
// terminal_names[0] = NO side (top left)
|
||||
// terminal_names[1] = NC side (bottom left)
|
||||
// terminal_names[2] = common side (right)
|
||||
// terminal_names[0] = NC (bottom-left)
|
||||
// terminal_names[1] = NO (top-left)
|
||||
// terminal_names[2] = Common (right)
|
||||
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
|
||||
painter.setFont(QETApp::diagramTextsFont(4));
|
||||
// Sort order from parseTerminal (top->bottom, left->right):
|
||||
// [0]=12 (NO, top-left), [1]=14 (common, top-center), [2]=13 (NC, bottom-center)
|
||||
if (terminal_names.size() >= 1)
|
||||
painter.drawText(QRectF(0, offset, 8, 8),
|
||||
Qt::AlignLeft|Qt::AlignTop, terminal_names[1]); // 12 NO left
|
||||
// Storage order set above: [0]=NC, [1]=NO, [2]=Common
|
||||
if (terminal_names.size() >= 2)
|
||||
painter.drawText(QRectF(16, offset+4, 8, 6),
|
||||
Qt::AlignRight|Qt::AlignTop, terminal_names[2]); // 14 common right
|
||||
painter.drawText(QRectF(0, offset, 8, 8),
|
||||
Qt::AlignLeft|Qt::AlignTop, terminal_names[1]); // NO top-left
|
||||
if (terminal_names.size() >= 3)
|
||||
painter.drawText(QRectF(16, offset+4, 8, 6),
|
||||
Qt::AlignRight|Qt::AlignTop, terminal_names[2]); // Common right
|
||||
if (terminal_names.size() >= 1)
|
||||
painter.drawText(QRectF(0, offset+9, 8, 6),
|
||||
Qt::AlignLeft|Qt::AlignTop, terminal_names[0]); // 13 NC left-bottom
|
||||
Qt::AlignLeft|Qt::AlignTop, terminal_names[0]); // NC bottom-left
|
||||
painter.setFont(QETApp::diagramTextsFont(5));
|
||||
}
|
||||
|
||||
@@ -1012,7 +1020,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
|
||||
if (m_update_map)
|
||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
||||
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||
|
||||
//a switch contact take place of two normal contact
|
||||
m_drawed_contacts += 2;
|
||||
@@ -1044,7 +1052,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
|
||||
if (m_update_map)
|
||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
||||
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||
++m_drawed_contacts;
|
||||
}
|
||||
return bounding_rect;
|
||||
@@ -1337,3 +1345,211 @@ QList<Element *> CrossRefItem::NCElements() const
|
||||
|
||||
return nc_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CrossRefItem::drawAsPlcTable
|
||||
* Draw the PLC IO table for PLC master elements.
|
||||
* The table shows columns: Type | Address | Function | Comment | CrossRef
|
||||
* Columns and rows are configurable via PlcMasterData.
|
||||
* @param painter painter to use
|
||||
*/
|
||||
void CrossRefItem::drawAsPlcTable(QPainter &painter)
|
||||
{
|
||||
// Get PLC data from the element
|
||||
ElementData ed = m_element->elementData();
|
||||
if (ed.m_master_type != ElementData::PLC)
|
||||
return;
|
||||
|
||||
ElementData::PlcMasterData plc_data = ed.plcMasterData();
|
||||
if (plc_data.ios.isEmpty())
|
||||
return;
|
||||
|
||||
// Define column indices
|
||||
const int COL_TYPE = 0;
|
||||
const int COL_ADDRESS = 1;
|
||||
const int COL_FUNCTION = 2;
|
||||
const int COL_COMMENT = 3;
|
||||
const int COL_CROSSREF = 4;
|
||||
const int COL_COUNT = 5;
|
||||
|
||||
// Column headers (French)
|
||||
QMap<int, QString> headers;
|
||||
headers[COL_TYPE] = QObject::tr("Type");
|
||||
headers[COL_ADDRESS] = QObject::tr("Adresse");
|
||||
headers[COL_FUNCTION] = QObject::tr("Fonction");
|
||||
headers[COL_COMMENT] = QObject::tr("Commentaire");
|
||||
headers[COL_CROSSREF] = QObject::tr("Réf. croisée");
|
||||
|
||||
// Build list of visible columns
|
||||
QList<int> visible_cols;
|
||||
for (int i = 0; i < COL_COUNT; ++i) {
|
||||
if (plc_data.colVisible.value(i, true))
|
||||
visible_cols.append(i);
|
||||
}
|
||||
if (visible_cols.isEmpty())
|
||||
return;
|
||||
|
||||
// Default column widths if not set (in scene units, roughly mm * 2.835)
|
||||
QMap<int, qreal> col_widths;
|
||||
for (int col : visible_cols) {
|
||||
if (plc_data.colWidths.contains(col) && plc_data.colWidths[col] > 0)
|
||||
col_widths[col] = plc_data.colWidths[col];
|
||||
else {
|
||||
switch (col) {
|
||||
case COL_TYPE: col_widths[col] = 35; break;
|
||||
case COL_ADDRESS: col_widths[col] = 25; break;
|
||||
case COL_FUNCTION: col_widths[col] = 50; break;
|
||||
case COL_COMMENT: col_widths[col] = 40; break;
|
||||
case COL_CROSSREF: col_widths[col] = 30; break;
|
||||
default: col_widths[col] = 30; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qreal row_h = plc_data.rowHeight > 0 ? plc_data.rowHeight : 8.0;
|
||||
qreal header_h = row_h + 2.0;
|
||||
|
||||
// Calculate total width
|
||||
qreal total_width = 0;
|
||||
for (int col : visible_cols)
|
||||
total_width += col_widths[col];
|
||||
|
||||
// Calculate total height: header + all IO rows
|
||||
int total_ios = plc_data.ios.size();
|
||||
|
||||
// Collect active break positions (sorted)
|
||||
QList<int> breaks;
|
||||
for (int bp : plc_data.breakPositions) {
|
||||
if (bp > 0 && bp < total_ios && !breaks.contains(bp))
|
||||
breaks.append(bp);
|
||||
}
|
||||
std::sort(breaks.begin(), breaks.end());
|
||||
|
||||
// Build block boundaries
|
||||
QList<int> block_starts;
|
||||
block_starts.append(0);
|
||||
for (int bp : breaks)
|
||||
block_starts.append(bp);
|
||||
|
||||
qreal total_height;
|
||||
int block_count = block_starts.size();
|
||||
|
||||
if (block_count > 1) {
|
||||
int max_rows = 0;
|
||||
for (int b = 0; b < block_count; ++b) {
|
||||
int start = block_starts.at(b);
|
||||
int end = (b + 1 < block_starts.size()) ? block_starts.at(b + 1) : total_ios;
|
||||
max_rows = qMax(max_rows, end - start);
|
||||
}
|
||||
total_height = header_h + max_rows * row_h;
|
||||
total_width = total_width * block_count + (block_count - 1) * 3;
|
||||
} else {
|
||||
total_height = header_h + total_ios * row_h;
|
||||
}
|
||||
|
||||
// Draw background rectangle
|
||||
QRectF bg_rect(0, 0, total_width, total_height);
|
||||
painter.fillRect(bg_rect, Qt::white);
|
||||
QPen border_pen(Qt::black, 0.5);
|
||||
painter.setPen(border_pen);
|
||||
painter.drawRect(bg_rect);
|
||||
|
||||
// Draw header row
|
||||
qreal x_offset = 0;
|
||||
|
||||
for (int block = 0; block < block_count; ++block) {
|
||||
qreal block_x = block * (col_widths.value(visible_cols.first(), 30) * visible_cols.size() + 3);
|
||||
qreal cx = block_x;
|
||||
|
||||
// Draw column headers
|
||||
QFont header_font = painter.font();
|
||||
header_font.setBold(true);
|
||||
painter.setFont(header_font);
|
||||
|
||||
for (int col : visible_cols) {
|
||||
QRectF header_rect(cx, 0, col_widths[col], header_h);
|
||||
painter.fillRect(header_rect, QColor(220, 220, 220));
|
||||
painter.drawRect(header_rect);
|
||||
painter.drawText(header_rect, Qt::AlignCenter, headers[col]);
|
||||
cx += col_widths[col];
|
||||
}
|
||||
|
||||
painter.setFont(painter.font());
|
||||
|
||||
// Draw IO rows for this block
|
||||
int start_idx = block_starts.at(block);
|
||||
int end_idx = (block + 1 < block_starts.size()) ? block_starts.at(block + 1) : total_ios;
|
||||
|
||||
for (int row = 0; row < (end_idx - start_idx); ++row) {
|
||||
int io_idx = start_idx + row;
|
||||
const ElementData::PlcIO &io = plc_data.ios.at(io_idx);
|
||||
|
||||
qreal ry = header_h + row * row_h;
|
||||
cx = block_x;
|
||||
|
||||
// Get linked slave element for this IO (via group index)
|
||||
QString cross_ref_text = io.crossRef;
|
||||
for (Element *slave : m_element->linkedElements()) {
|
||||
if (m_element->groupIndexForElement(slave) == io_idx) {
|
||||
cross_ref_text = elementPositionText(slave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int col : visible_cols) {
|
||||
QRectF cell_rect(cx, ry, col_widths[col], row_h);
|
||||
painter.drawRect(cell_rect);
|
||||
|
||||
QString cell_text;
|
||||
switch (col) {
|
||||
case COL_TYPE:
|
||||
cell_text = ElementData::translatedPlcIOType(io.type);
|
||||
break;
|
||||
case COL_ADDRESS:
|
||||
cell_text = io.address;
|
||||
break;
|
||||
case COL_FUNCTION:
|
||||
cell_text = io.functionText;
|
||||
break;
|
||||
case COL_COMMENT:
|
||||
cell_text = io.comment;
|
||||
break;
|
||||
case COL_CROSSREF:
|
||||
cell_text = cross_ref_text;
|
||||
break;
|
||||
}
|
||||
|
||||
QRectF text_rect = cell_rect.adjusted(1, 0, -1, 0);
|
||||
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, cell_text);
|
||||
|
||||
// If function text doesn't fit, try to show as much as possible
|
||||
if (col == COL_FUNCTION && !io.functionText.isEmpty()) {
|
||||
QFontMetrics fm(painter.font());
|
||||
QString elided = fm.elidedText(io.functionText, Qt::ElideRight, text_rect.width());
|
||||
// Clear and redraw with elided text
|
||||
painter.save();
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawRect(text_rect);
|
||||
painter.restore();
|
||||
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, elided);
|
||||
}
|
||||
|
||||
// Store hover map for cross-reference cells
|
||||
if (col == COL_CROSSREF && m_update_map && !cross_ref_text.isEmpty()) {
|
||||
for (Element *slave : m_element->linkedElements()) {
|
||||
if (m_element->groupIndexForElement(slave) == io_idx) {
|
||||
m_hovered_contacts_map.insert(slave, cell_rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx += col_widths[col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepareGeometryChange();
|
||||
m_bounding_rect = bg_rect;
|
||||
m_shape_path.addRect(bg_rect);
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ class CrossRefItem : public QGraphicsObject
|
||||
void setUpCrossBoundingRect(QPainter &painter);
|
||||
void drawAsCross(QPainter &painter);
|
||||
void drawAsContacts(QPainter &painter);
|
||||
void drawAsPlcTable(QPainter &painter);
|
||||
QRectF drawContact(QPainter &painter, int flags, Element *elmt, int pole_index = 0);
|
||||
void fillCrossRef(QPainter &painter);
|
||||
void AddExtraInfo(QPainter &painter, const QString&);
|
||||
@@ -126,6 +127,12 @@ class CrossRefItem : public QGraphicsObject
|
||||
ElementTextItemGroup *m_group = nullptr;
|
||||
QList <QMetaObject::Connection> m_slave_connection;
|
||||
QList <QMetaObject::Connection> m_update_connection;
|
||||
|
||||
public:
|
||||
/// Returns the map of linked elements and their clickable rects (local coords).
|
||||
/// Used by the PDF export to inject hyperlink annotations.
|
||||
const QMultiMap<Element *, QRectF> &hoveredContactsMap() const
|
||||
{ return m_hovered_contacts_map; }
|
||||
};
|
||||
|
||||
#endif // CROSSREFITEM_H
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "dynamicelementtextitem.h"
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
||||
#include "../diagram.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../utils/qetutils.h"
|
||||
#include "crossrefitem.h"
|
||||
#include "element.h"
|
||||
#include "elementtextitemgroup.h"
|
||||
@@ -96,7 +97,7 @@ QDomElement DynamicElementTextItem::toXml(QDomDocument &dom_doc) const
|
||||
root_element.setAttribute("uuid", m_uuid.toString());
|
||||
root_element.setAttribute("frame", m_frame? "true" : "false");
|
||||
root_element.setAttribute("text_width", QString::number(m_text_width));
|
||||
root_element.setAttribute("font", font().toString());
|
||||
root_element.setAttribute("font", QETUtils::fontToString(font()));
|
||||
root_element.setAttribute("keep_visual_rotation", m_keep_visual_rotation ? "true" : "false");
|
||||
|
||||
QMetaEnum me = textFromMetaEnum();
|
||||
@@ -167,7 +168,7 @@ void DynamicElementTextItem::fromXml(const QDomElement &dom_elmt)
|
||||
if (dom_elmt.hasAttribute("font"))
|
||||
{
|
||||
QFont font;
|
||||
font.fromString(dom_elmt.attribute("font"));
|
||||
QETUtils::fontFromString(font, dom_elmt.attribute("font"));
|
||||
setFont(font);
|
||||
}
|
||||
else //Retrocompatibility during the 0.7 dev because the font property was added lately. TODO remove this part in futur
|
||||
@@ -280,6 +281,10 @@ Element *DynamicElementTextItem::elementUseForInfo() const
|
||||
return elmt;
|
||||
case Element::Slave:
|
||||
{
|
||||
//PLC slave stores its own PLC variables in elementInformations
|
||||
//(populated from master on link). Return self so %{plc_*} vars resolve.
|
||||
if (elmt->elementData().m_slave_type == ElementData::PLCSlave)
|
||||
return elmt;
|
||||
if(elmt->linkedElements().isEmpty())
|
||||
return nullptr;
|
||||
else
|
||||
@@ -1347,18 +1352,20 @@ void DynamicElementTextItem::updateXref()
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_parent_element.data()->linkType() == Element::Slave)
|
||||
else if (m_parent_element.data()->linkType() == Element::Slave &&
|
||||
!m_parent_element.data()->linkedElements().isEmpty())
|
||||
{
|
||||
if(m_master_element && !parentGroup() &&
|
||||
Element *master_elmt = m_parent_element.data()->linkedElements().first();
|
||||
if(master_elmt && !parentGroup() &&
|
||||
(
|
||||
(m_text_from == DynamicElementTextItem::ElementInfo && m_info_name == "label") ||
|
||||
(m_text_from == DynamicElementTextItem::CompositeText && m_composite_text.contains("%{label}"))
|
||||
)
|
||||
)
|
||||
{
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(m_master_element.data()->kindInformations()["type"].toString());
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, m_master_element.data()->rSequenceStruct(), m_master_element.data()->diagram(), m_master_element.data());
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
if(!m_slave_Xref_item)
|
||||
{
|
||||
@@ -1367,9 +1374,9 @@ void DynamicElementTextItem::updateXref()
|
||||
m_slave_Xref_item->setDefaultTextColor(Qt::black);
|
||||
m_slave_Xref_item->installSceneEventFilter(this);
|
||||
|
||||
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::xChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::yChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::elementInfoChange, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram(), &Diagram::diagramInformationChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateXref);
|
||||
@@ -1439,9 +1446,12 @@ void DynamicElementTextItem::setPlainText(const QString &text)
|
||||
}
|
||||
else if (m_slave_Xref_item)
|
||||
{
|
||||
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(m_master_element.data()->kindInformations()["type"].toString());
|
||||
setXref_item(xrp.getXrefPos());
|
||||
Element *master_elmt = m_parent_element.data()->linkedElements().isEmpty()
|
||||
? nullptr : m_parent_element.data()->linkedElements().first();
|
||||
if (master_elmt) {
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
setXref_item(xrp.getXrefPos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,9 @@ class DynamicElementTextItem : public DiagramTextItem
|
||||
void fromXml(const QDomElement &dom_elmt) override;
|
||||
|
||||
Element *parentElement() const;
|
||||
/// PDF export: slave cross-reference text item ("(folio-pos)") and its master target.
|
||||
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||
Element *masterElement() const { return m_master_element.data(); }
|
||||
ElementTextItemGroup *parentGroup() const;
|
||||
Element *elementUseForInfo() const;
|
||||
void refreshLabelConnection();
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "element.h"
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../PropertiesEditor/propertieseditordialog.h"
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../autoNum/numerotationcontextcommands.h"
|
||||
#include "../diagram.h"
|
||||
#include "../diagramcommands.h"
|
||||
@@ -26,6 +27,8 @@
|
||||
#include "../elementprovider.h"
|
||||
#include "../factory/elementpicturefactory.h"
|
||||
#include "../properties/terminaldata.h"
|
||||
#include "../properties/xrefproperties.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../ui/elementpropertieswidget.h"
|
||||
@@ -229,13 +232,25 @@ void Element::paint(
|
||||
painter->setBrush(brush);
|
||||
if (options && options->levelOfDetailFromTransform(painter->worldTransform()) < 0.5)
|
||||
{
|
||||
painter->drawPicture(0, 0, m_low_zoom_picture);
|
||||
if (!m_low_zoom_picture.isNull())
|
||||
painter->drawPicture(0, 0, m_low_zoom_picture);
|
||||
} else {
|
||||
painter->drawPicture(0, 0, m_picture);
|
||||
if (!m_picture.isNull())
|
||||
painter->drawPicture(0, 0, m_picture);
|
||||
}
|
||||
|
||||
painter->restore(); //Restore the QPainter after use drawPicture
|
||||
|
||||
// Draw PLC table from in-memory data (overwrites the stale cached picture)
|
||||
if (scene() && painter->isActive() &&
|
||||
m_data.m_type == ElementData::Master &&
|
||||
m_data.m_master_type == ElementData::PLC)
|
||||
{
|
||||
const auto &plc_data = m_data.plcMasterData();
|
||||
if (!plc_data.ios.isEmpty())
|
||||
drawPlcTable(painter);
|
||||
}
|
||||
|
||||
//Draw the selection rectangle
|
||||
if ( isSelected() || m_mouse_over ) {
|
||||
QGIUtility::drawBoundingRectSelection(this, painter);
|
||||
@@ -486,6 +501,13 @@ bool Element::buildFromXml(const QDomElement &xml_def_elmt, int *state)
|
||||
if (qde.isNull())
|
||||
continue;
|
||||
|
||||
// Store plc_table positions for runtime rendering
|
||||
if (qde.tagName() == QLatin1String("plc_table")) {
|
||||
qreal x = qde.attribute("x", "0").toDouble();
|
||||
qreal y = qde.attribute("y", "0").toDouble();
|
||||
m_plc_table_positions.append(QPointF(x, y));
|
||||
}
|
||||
|
||||
if (parseElement(qde)) {
|
||||
++ parsed_elements_count;
|
||||
}
|
||||
@@ -502,8 +524,8 @@ bool Element::buildFromXml(const QDomElement &xml_def_elmt, int *state)
|
||||
|
||||
ElementPictureFactory *epf = ElementPictureFactory::instance();
|
||||
epf->getPictures(m_location,
|
||||
const_cast<QPicture&>(m_picture),
|
||||
const_cast<QPicture&>(m_low_zoom_picture));
|
||||
m_picture,
|
||||
m_low_zoom_picture);
|
||||
|
||||
if(!m_picture.isNull())
|
||||
++ parsed_elements_count;
|
||||
@@ -741,7 +763,9 @@ bool Element::fromXml(QDomElement &e,
|
||||
QStringLiteral("links_uuids"),
|
||||
QStringLiteral("link_uuid"));
|
||||
foreach (QDomElement qdo, uuid_list) {
|
||||
tmp_uuids_link << QUuid(qdo.attribute(QStringLiteral("uuid")));
|
||||
QUuid uuid(qdo.attribute(QStringLiteral("uuid")));
|
||||
int group_index = qdo.attribute(QStringLiteral("group_index"), QStringLiteral("-1")).toInt();
|
||||
tmp_uuids_link << LinkInfo(uuid, group_index);
|
||||
}
|
||||
|
||||
//uuid of this element
|
||||
@@ -932,6 +956,13 @@ QDomElement Element::toXml(
|
||||
QDomElement link_uuid =
|
||||
document.createElement(QStringLiteral("link_uuid"));
|
||||
link_uuid.setAttribute(QStringLiteral("uuid"), elmt->uuid().toString());
|
||||
|
||||
// Save group index if assigned (for slave->master links)
|
||||
int gi = m_group_index_map.value(elmt, -1);
|
||||
if (gi >= 0) {
|
||||
link_uuid.setAttribute(QStringLiteral("group_index"), gi);
|
||||
}
|
||||
|
||||
links_uuids.appendChild(link_uuid);
|
||||
}
|
||||
element.appendChild(links_uuids);
|
||||
@@ -1267,8 +1298,21 @@ void Element::initLink(QETProject *prj)
|
||||
if (tmp_uuids_link.isEmpty()) return;
|
||||
|
||||
ElementProvider ep(prj);
|
||||
foreach (Element *elmt, ep.fromUuids(tmp_uuids_link)) {
|
||||
elmt->linkToElement(this);
|
||||
QList<QUuid> uuids;
|
||||
for (const auto &linkInfo : tmp_uuids_link) {
|
||||
uuids.append(linkInfo.uuid);
|
||||
}
|
||||
QList<Element *> elements = ep.fromUuids(uuids);
|
||||
for (int i = 0; i < tmp_uuids_link.size(); ++i) {
|
||||
for (Element *elmt : elements) {
|
||||
if (elmt->uuid() == tmp_uuids_link[i].uuid) {
|
||||
elmt->linkToElement(this);
|
||||
if (tmp_uuids_link[i].group_index >= 0) {
|
||||
m_group_index_map[elmt] = tmp_uuids_link[i].group_index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp_uuids_link.clear();
|
||||
}
|
||||
@@ -1304,6 +1348,34 @@ QString Element::linkTypeToString() const
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Element::groupIndexForElement
|
||||
* Returns the group index assigned to the given linked element.
|
||||
* For slave elements, this indicates which contact group of the master
|
||||
* this slave is assigned to.
|
||||
* @param elmt the linked element to query
|
||||
* @return group index, or -1 if not assigned
|
||||
*/
|
||||
int Element::groupIndexForElement(Element *elmt) const
|
||||
{
|
||||
return m_group_index_map.value(elmt, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Element::setGroupIndexForElement
|
||||
* Sets the group index for a linked element.
|
||||
* @param elmt the linked element
|
||||
* @param index the group index to assign
|
||||
*/
|
||||
void Element::setGroupIndexForElement(Element *elmt, int index)
|
||||
{
|
||||
if (index >= 0) {
|
||||
m_group_index_map[elmt] = index;
|
||||
} else {
|
||||
m_group_index_map.remove(elmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Element::setElementInformations
|
||||
Set new information for this element.
|
||||
@@ -1344,6 +1416,8 @@ ElementData Element::elementData() const
|
||||
void Element::setElementData(ElementData data)
|
||||
{
|
||||
auto old_info = m_data.m_informations;
|
||||
auto old_plc = m_data.m_type == ElementData::Master && m_data.m_master_type == ElementData::PLC
|
||||
? m_data.plcMasterData() : ElementData::PlcMasterData();
|
||||
m_data = data;
|
||||
|
||||
if (old_info != m_data.m_informations) {
|
||||
@@ -1353,6 +1427,42 @@ void Element::setElementData(ElementData data)
|
||||
}
|
||||
emit elementInfoChange(old_info, m_data.m_informations);
|
||||
}
|
||||
|
||||
// Propagate PLC master data changes to linked slaves
|
||||
if (m_data.m_type == ElementData::Master && m_data.m_master_type == ElementData::PLC)
|
||||
{
|
||||
const auto &new_plc = m_data.plcMasterData();
|
||||
bool plc_changed = (old_plc.ios != new_plc.ios);
|
||||
if (plc_changed && !m_group_index_map.isEmpty())
|
||||
{
|
||||
for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it)
|
||||
{
|
||||
Element *slave = it.key();
|
||||
int io_idx = it.value();
|
||||
if (!slave || io_idx < 0 || io_idx >= new_plc.ios.size())
|
||||
continue;
|
||||
const auto &io = new_plc.ios.at(io_idx);
|
||||
DiagramContext ctx = slave->elementInformations();
|
||||
ctx.addValue(QETInformation::ELMT_PLC_TYPE,
|
||||
ElementData::translatedPlcIOType(io.type));
|
||||
ctx.addValue(QETInformation::ELMT_PLC_ADDRESS, io.address);
|
||||
ctx.addValue(QETInformation::ELMT_PLC_FUNCTION, io.functionText);
|
||||
ctx.addValue(QETInformation::ELMT_PLC_COMMENT, io.comment);
|
||||
ctx.addValue(QETInformation::ELMT_PLC_CROSSREF,
|
||||
[&]() -> QString {
|
||||
if (!diagram() || !diagram()->project())
|
||||
return QString();
|
||||
XRefProperties xrp = diagram()->project()
|
||||
->defaultXRefProperties("plc");
|
||||
autonum::sequentialNumbers seq;
|
||||
return autonum::AssignVariables::formulaToLabel(
|
||||
xrp.slaveLabel(), seq, diagram(), this);
|
||||
}());
|
||||
ctx.addValue(QETInformation::ELMT_LABEL, actualLabel());
|
||||
slave->setElementInformations(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1588,3 +1698,213 @@ void Element::updateConductorTexts()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Element::drawPlcTable
|
||||
Draw the PLC table directly from in-memory ElementData.
|
||||
This overlays the stale picture from the .elmt file with current data.
|
||||
*/
|
||||
void Element::drawPlcTable(QPainter *painter)
|
||||
{
|
||||
if (!painter || !painter->isActive() || !scene())
|
||||
return;
|
||||
|
||||
if (m_data.m_type != ElementData::Master ||
|
||||
m_data.m_master_type != ElementData::PLC)
|
||||
return;
|
||||
|
||||
const auto &plc_data = m_data.plcMasterData();
|
||||
if (plc_data.ios.isEmpty())
|
||||
return;
|
||||
|
||||
// Use plc_table positions from the .elmt file, or default to (0,0)
|
||||
QList<QPointF> positions = m_plc_table_positions;
|
||||
if (positions.isEmpty())
|
||||
positions.append(QPointF(0, 0));
|
||||
|
||||
const int COL_COUNT = 5;
|
||||
const int COL_TYPE = 0;
|
||||
const int COL_ADDRESS = 1;
|
||||
const int COL_FUNCTION = 2;
|
||||
const int COL_COMMENT = 3;
|
||||
const int COL_CROSSREF = 4;
|
||||
|
||||
// Build header labels
|
||||
QMap<int, QString> headers;
|
||||
headers[COL_TYPE] = QObject::tr("Type");
|
||||
headers[COL_ADDRESS] = QObject::tr("Adresse");
|
||||
headers[COL_FUNCTION] = QObject::tr("Fonction");
|
||||
headers[COL_COMMENT] = QObject::tr("Commentaire");
|
||||
headers[COL_CROSSREF] = QObject::tr("Réf. croisée");
|
||||
|
||||
// Override with custom column names
|
||||
if (!plc_data.columnNames.isEmpty()) {
|
||||
QList<int> all_cols;
|
||||
all_cols << COL_TYPE << COL_ADDRESS << COL_FUNCTION << COL_COMMENT << COL_CROSSREF;
|
||||
for (int i = 0; i < qMin(plc_data.columnNames.size(), all_cols.size()); ++i) {
|
||||
if (!plc_data.columnNames.at(i).isEmpty())
|
||||
headers[all_cols.at(i)] = plc_data.columnNames.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Build visible columns
|
||||
QList<int> visible_cols;
|
||||
if (!plc_data.columnOrder.isEmpty()) {
|
||||
for (int logical : plc_data.columnOrder) {
|
||||
if (logical >= 0 && logical < COL_COUNT
|
||||
&& plc_data.colVisible.value(logical, true)
|
||||
&& !visible_cols.contains(logical))
|
||||
visible_cols.append(logical);
|
||||
}
|
||||
for (int i = 0; i < COL_COUNT; ++i) {
|
||||
if (plc_data.colVisible.value(i, true) && !visible_cols.contains(i))
|
||||
visible_cols.append(i);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < COL_COUNT; ++i) {
|
||||
if (plc_data.colVisible.value(i, true))
|
||||
visible_cols.append(i);
|
||||
}
|
||||
}
|
||||
if (visible_cols.isEmpty())
|
||||
return;
|
||||
|
||||
// Column widths
|
||||
QMap<int, qreal> col_widths;
|
||||
for (int col : visible_cols) {
|
||||
if (plc_data.colWidths.contains(col) && plc_data.colWidths[col] > 0)
|
||||
col_widths[col] = plc_data.colWidths[col];
|
||||
else {
|
||||
switch (col) {
|
||||
case COL_TYPE: col_widths[col] = 35; break;
|
||||
case COL_ADDRESS: col_widths[col] = 25; break;
|
||||
case COL_FUNCTION: col_widths[col] = 50; break;
|
||||
case COL_COMMENT: col_widths[col] = 40; break;
|
||||
case COL_CROSSREF: col_widths[col] = 30; break;
|
||||
default: col_widths[col] = 30; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qreal row_h = plc_data.rowHeight > 0 ? plc_data.rowHeight : 8.0;
|
||||
qreal header_h = plc_data.showHeaders ? (row_h + 2.0) : 0;
|
||||
int total_ios = plc_data.ios.size();
|
||||
|
||||
// Parse break positions
|
||||
QList<int> breaks;
|
||||
for (int bp : plc_data.breakPositions) {
|
||||
if (bp > 0 && bp < total_ios && !breaks.contains(bp))
|
||||
breaks.append(bp);
|
||||
}
|
||||
std::sort(breaks.begin(), breaks.end());
|
||||
|
||||
QList<int> block_starts;
|
||||
block_starts.append(0);
|
||||
for (int bp : breaks)
|
||||
block_starts.append(bp);
|
||||
int block_count = block_starts.size();
|
||||
|
||||
qreal total_width = 0;
|
||||
for (int col : visible_cols)
|
||||
total_width += col_widths[col];
|
||||
|
||||
QPen border_pen(Qt::black, 0.5);
|
||||
|
||||
// Fonts
|
||||
QFont header_font = plc_data.headerFont;
|
||||
if (header_font.family().isEmpty()) {
|
||||
header_font = painter->font();
|
||||
header_font.setBold(true);
|
||||
}
|
||||
QFont cell_font = plc_data.cellFont;
|
||||
if (cell_font.family().isEmpty()) {
|
||||
cell_font = painter->font();
|
||||
}
|
||||
|
||||
for (const QPointF &pos : positions) {
|
||||
for (int block = 0; block < block_count; ++block) {
|
||||
qreal bx = pos.x() + block * (total_width + 3);
|
||||
qreal cx = bx;
|
||||
|
||||
// Draw header background
|
||||
if (plc_data.showHeaders) {
|
||||
painter->save();
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(QColor(220, 220, 220));
|
||||
painter->drawRect(QRectF(cx, pos.y(), total_width, header_h));
|
||||
painter->restore();
|
||||
|
||||
painter->setFont(header_font);
|
||||
painter->setPen(border_pen);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
for (int col : visible_cols) {
|
||||
QRectF hr(cx, pos.y(), col_widths[col], header_h);
|
||||
painter->drawRect(hr);
|
||||
QRectF text_rect = hr.adjusted(1, 0, -1, 0);
|
||||
painter->drawText(text_rect, Qt::AlignCenter, headers.value(col, QString()));
|
||||
cx += col_widths[col];
|
||||
}
|
||||
}
|
||||
|
||||
// Draw IO rows
|
||||
painter->setFont(cell_font);
|
||||
int start_idx = block_starts.at(block);
|
||||
int end_idx = (block + 1 < block_starts.size())
|
||||
? block_starts.at(block + 1) : total_ios;
|
||||
|
||||
for (int row = 0; row < (end_idx - start_idx); ++row) {
|
||||
int io_idx = start_idx + row;
|
||||
if (io_idx >= plc_data.ios.size()) break;
|
||||
const auto &io = plc_data.ios.at(io_idx);
|
||||
qreal ry = pos.y() + header_h + row * row_h;
|
||||
cx = bx;
|
||||
|
||||
for (int col : visible_cols) {
|
||||
QRectF cr(cx, ry, col_widths[col], row_h);
|
||||
painter->setPen(border_pen);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRect(cr);
|
||||
|
||||
QString cell_text;
|
||||
switch (col) {
|
||||
case COL_TYPE: cell_text = ElementData::translatedPlcIOType(io.type); break;
|
||||
case COL_ADDRESS: cell_text = io.address; break;
|
||||
case COL_FUNCTION: cell_text = io.functionText; break;
|
||||
case COL_COMMENT: cell_text = io.comment; break;
|
||||
case COL_CROSSREF:
|
||||
{
|
||||
cell_text = io.crossRef;
|
||||
for (auto it = m_group_index_map.constBegin();
|
||||
it != m_group_index_map.constEnd(); ++it) {
|
||||
if (it.value() == io_idx) {
|
||||
Element *slave = it.key();
|
||||
if (slave && slave->diagram() && diagram()
|
||||
&& diagram()->project())
|
||||
{
|
||||
XRefProperties xrp = diagram()->project()
|
||||
->defaultXRefProperties("plc");
|
||||
autonum::sequentialNumbers seq;
|
||||
cell_text = autonum::AssignVariables
|
||||
::formulaToLabel(
|
||||
xrp.masterLabel(),
|
||||
seq,
|
||||
slave->diagram(),
|
||||
slave);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QRectF text_rect = cr.adjusted(1, 0, -1, 0);
|
||||
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, cell_text);
|
||||
|
||||
cx += col_widths[col];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,20 @@ class Element : public QetGraphicsItem
|
||||
Thumbnail = 64,
|
||||
ConductorDefinition = 128};
|
||||
|
||||
/**
|
||||
* @brief The LinkInfo struct
|
||||
* Stores link data for element connections.
|
||||
* For slave elements, group_index indicates which contact group
|
||||
* of the master this slave is assigned to (-1 = no group assigned).
|
||||
*/
|
||||
struct LinkInfo {
|
||||
QUuid uuid;
|
||||
int group_index = -1; ///< Index into master's slaveContactGroups, -1 = not assigned
|
||||
|
||||
LinkInfo() = default;
|
||||
LinkInfo(const QUuid &u, int gi = -1) : uuid(u), group_index(gi) {}
|
||||
};
|
||||
|
||||
Element(const ElementsLocation &location,
|
||||
QGraphicsItem * = nullptr,
|
||||
int *state = nullptr,
|
||||
@@ -181,6 +195,9 @@ class Element : public QetGraphicsItem
|
||||
virtual void initLink(QETProject *);
|
||||
QList<Element *> linkedElements ();
|
||||
|
||||
int groupIndexForElement(Element *elmt) const;
|
||||
void setGroupIndexForElement(Element *elmt, int index);
|
||||
|
||||
/**
|
||||
* @brief linkType
|
||||
* use elementData function instead
|
||||
@@ -228,7 +245,8 @@ class Element : public QetGraphicsItem
|
||||
protected:
|
||||
//ATTRIBUTES related to linked element
|
||||
QList <Element *> connected_elements;
|
||||
QList <QUuid> tmp_uuids_link;
|
||||
QList <LinkInfo> tmp_uuids_link;
|
||||
QHash <Element *, int> m_group_index_map; ///< Maps linked elements to their group index (for slave->master links)
|
||||
QUuid m_uuid;
|
||||
kind m_link_type = Element::Simple;
|
||||
|
||||
@@ -238,11 +256,14 @@ class Element : public QetGraphicsItem
|
||||
bool m_freeze_label = false;
|
||||
QString m_F_str;
|
||||
|
||||
ElementsLocation m_location;
|
||||
QList <Terminal *> m_terminals;
|
||||
const QPicture m_picture;
|
||||
const QPicture m_low_zoom_picture;
|
||||
ElementData m_data;
|
||||
ElementsLocation m_location;
|
||||
QList <Terminal *> m_terminals;
|
||||
QPicture m_picture;
|
||||
QPicture m_low_zoom_picture;
|
||||
ElementData m_data;
|
||||
QList<QPointF> m_plc_table_positions; // Positions of plc_table parts in the element definition
|
||||
|
||||
void drawPlcTable(QPainter *painter);
|
||||
|
||||
private:
|
||||
bool m_must_highlight = false;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "elementtextitemgroup.h"
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
||||
#include "../diagram.h"
|
||||
#include "../qetapp.h"
|
||||
|
||||
@@ -76,6 +76,8 @@ class ElementTextItemGroup : public QObject, public QGraphicsItemGroup
|
||||
QList<DynamicElementTextItem *> texts() const;
|
||||
Diagram *diagram() const;
|
||||
Element *parentElement() const;
|
||||
/// PDF export: slave cross-reference text item of the group, if any.
|
||||
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||
|
||||
QDomElement toXml(QDomDocument &dom_document) const;
|
||||
void fromXml(QDomElement &dom_element);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "../diagramcommands.h"
|
||||
#include "../qet.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../utils/qetutils.h"
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QSettings>
|
||||
@@ -64,7 +65,7 @@ void IndependentTextItem::fromXml(const QDomElement &e) {
|
||||
if (e.hasAttribute("font"))
|
||||
{
|
||||
QFont font;
|
||||
font.fromString(e.attribute("font"));
|
||||
QETUtils::fontFromString(font, e.attribute("font"));
|
||||
setFont(font);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +81,7 @@ QDomElement IndependentTextItem::toXml(QDomDocument &document) const
|
||||
result.setAttribute("y", QString("%1").arg(pos().y()));
|
||||
result.setAttribute("text", toHtml());
|
||||
result.setAttribute("rotation", QString::number(QET::correctAngle(rotation())));
|
||||
result.setAttribute("font", font().toString());
|
||||
result.setAttribute("font", QETUtils::fontToString(font()));
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "masterelement.h"
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../diagram.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "crossrefitem.h"
|
||||
#include "dynamicelementtextitem.h"
|
||||
#include "../properties/elementdata.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
@@ -61,9 +63,14 @@ void MasterElement::linkToElement(Element *elmt)
|
||||
elmt->linkToElement(this);
|
||||
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(kindInformations()["type"].toString());
|
||||
if (!m_Xref_item && xrp.snapTo() == XRefProperties::Bottom)
|
||||
if (!m_Xref_item && (xrp.snapTo() == XRefProperties::Bottom ||
|
||||
m_data.m_master_type == ElementData::PLC))
|
||||
m_Xref_item = new CrossRefItem(this); //create cross ref item if not yet
|
||||
|
||||
|
||||
// For PLC masters, connect slave position changes to trigger master repaint
|
||||
if (m_data.m_master_type == ElementData::PLC)
|
||||
connectSlavePositionUpdates(elmt);
|
||||
|
||||
emit linkedElementChanged();
|
||||
aboutDeleteXref();
|
||||
}
|
||||
@@ -94,10 +101,28 @@ void MasterElement::unlinkElement(Element *elmt)
|
||||
//Ensure elmt is linked to this element
|
||||
if (connected_elements.contains(elmt))
|
||||
{
|
||||
// Clear PLC variables on slave before unlinking
|
||||
if (m_data.m_master_type == ElementData::PLC)
|
||||
{
|
||||
DiagramContext ctx = elmt->elementInformations();
|
||||
ctx.remove(QETInformation::ELMT_PLC_TYPE);
|
||||
ctx.remove(QETInformation::ELMT_PLC_ADDRESS);
|
||||
ctx.remove(QETInformation::ELMT_PLC_FUNCTION);
|
||||
ctx.remove(QETInformation::ELMT_PLC_COMMENT);
|
||||
ctx.remove(QETInformation::ELMT_PLC_CROSSREF);
|
||||
ctx.remove(QETInformation::ELMT_LABEL);
|
||||
elmt->setElementInformations(ctx);
|
||||
|
||||
setGroupIndexForElement(elmt, -1);
|
||||
}
|
||||
|
||||
connected_elements.removeOne(elmt);
|
||||
elmt -> unlinkElement (this);
|
||||
elmt -> setHighlighted (false);
|
||||
|
||||
// Disconnect slave position updates for PLC masters
|
||||
disconnectSlavePositionUpdates(elmt);
|
||||
|
||||
aboutDeleteXref();
|
||||
emit linkedElementChanged();
|
||||
}
|
||||
@@ -141,7 +166,7 @@ void MasterElement::xrefPropertiesChanged()
|
||||
{
|
||||
if(!diagram())
|
||||
return;
|
||||
|
||||
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(kindInformations()["type"].toString());
|
||||
if(xrp.snapTo() == XRefProperties::Bottom)
|
||||
{
|
||||
@@ -150,6 +175,12 @@ void MasterElement::xrefPropertiesChanged()
|
||||
if(!m_Xref_item)
|
||||
m_Xref_item = new CrossRefItem(this);
|
||||
}
|
||||
// Always create Xref for PLC master elements (they show IO table even without linked slaves)
|
||||
else if (m_data.m_master_type == ElementData::PLC)
|
||||
{
|
||||
if(!m_Xref_item)
|
||||
m_Xref_item = new CrossRefItem(this);
|
||||
}
|
||||
aboutDeleteXref();
|
||||
}
|
||||
|
||||
@@ -167,7 +198,11 @@ void MasterElement::aboutDeleteXref()
|
||||
{
|
||||
if(!m_Xref_item)
|
||||
return;
|
||||
|
||||
|
||||
// Never delete Xref for PLC master elements - they always show the IO table
|
||||
if (m_data.m_master_type == ElementData::PLC)
|
||||
return;
|
||||
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(kindInformations()["type"].toString());
|
||||
if (xrp.snapTo() != XRefProperties::Bottom && m_Xref_item)
|
||||
{
|
||||
@@ -175,7 +210,7 @@ void MasterElement::aboutDeleteXref()
|
||||
m_Xref_item = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_Xref_item->boundingRect().isNull())
|
||||
{
|
||||
delete m_Xref_item;
|
||||
@@ -207,3 +242,36 @@ bool MasterElement::isFull() const
|
||||
// Return true if current connected elements reached or exceeded the limit
|
||||
return connected_elements.size() >= max_slaves;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MasterElement::connectSlavePositionUpdates
|
||||
Connect slave xChanged/yChanged to master update() so the PLC table
|
||||
repaints in real-time when a slave moves on the diagram.
|
||||
@param slave the slave element
|
||||
*/
|
||||
void MasterElement::connectSlavePositionUpdates(Element *slave)
|
||||
{
|
||||
if (!slave)
|
||||
return;
|
||||
m_slave_x_conn[slave] = connect(slave, &QGraphicsObject::xChanged,
|
||||
[this]() { update(); });
|
||||
m_slave_y_conn[slave] = connect(slave, &QGraphicsObject::yChanged,
|
||||
[this]() { update(); });
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MasterElement::disconnectSlavePositionUpdates
|
||||
Disconnect slave position signals from master.
|
||||
@param slave the slave element
|
||||
*/
|
||||
void MasterElement::disconnectSlavePositionUpdates(Element *slave)
|
||||
{
|
||||
if (!slave)
|
||||
return;
|
||||
if (m_slave_x_conn.contains(slave)) {
|
||||
disconnect(m_slave_x_conn.take(slave));
|
||||
}
|
||||
if (m_slave_y_conn.contains(slave)) {
|
||||
disconnect(m_slave_y_conn.take(slave));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#define MASTERELEMENT_H
|
||||
|
||||
#include "element.h"
|
||||
#include <QHash>
|
||||
#include <QMetaObject>
|
||||
|
||||
class CrossRefItem;
|
||||
|
||||
@@ -55,10 +57,14 @@ class MasterElement : public Element
|
||||
private:
|
||||
void xrefPropertiesChanged();
|
||||
void aboutDeleteXref ();
|
||||
void connectSlavePositionUpdates(Element *slave);
|
||||
void disconnectSlavePositionUpdates(Element *slave);
|
||||
|
||||
private:
|
||||
CrossRefItem *m_Xref_item = nullptr;
|
||||
bool m_first_scene_change = true;
|
||||
QHash<Element*, QMetaObject::Connection> m_slave_x_conn;
|
||||
QHash<Element*, QMetaObject::Connection> m_slave_y_conn;
|
||||
};
|
||||
|
||||
#endif // MASTERELEMENT_H
|
||||
|
||||
@@ -594,7 +594,7 @@ void QetShapeItem::addHandler()
|
||||
{
|
||||
m_handler_vector = QetGraphicsHandlerItem::handlerForPoint(mapToScene(points_vector), QETUtils::graphicsHandlerSize(this));
|
||||
|
||||
for(const auto handler : qAsConst(m_handler_vector))
|
||||
for(const auto handler : std::as_const(m_handler_vector))
|
||||
{
|
||||
handler->setZValue(this->zValue()+1);
|
||||
handler->setColor(Qt::blue);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
|
||||
#include "../qetproject.h"
|
||||
#include "../conductorautonumerotation.h"
|
||||
#include "../diagram.h"
|
||||
#include "../undocommand/addgraphicsobjectcommand.h"
|
||||
@@ -86,6 +86,7 @@ Terminal::Terminal(TerminalData* data, Element* e) :
|
||||
*/
|
||||
Terminal::~Terminal() {
|
||||
qDeleteAll(m_conductors_list);
|
||||
delete d;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,6 +273,44 @@ void Terminal::paint(
|
||||
m_help_line_a -> setLine(line);
|
||||
}
|
||||
|
||||
// Draw label if show_name is enabled
|
||||
const QString display_name = name();
|
||||
if (d->m_show_name && !display_name.isEmpty()) {
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
painter->setRenderHint(QPainter::TextAntialiasing, true);
|
||||
painter->setFont(d->m_label_font);
|
||||
painter->setPen(d->m_label_color);
|
||||
|
||||
QPointF label_pos = d->m_pos + d->m_label_pos;
|
||||
QFontMetrics fm(d->m_label_font);
|
||||
QSizeF text_size = fm.size(Qt::TextSingleLine, display_name);
|
||||
|
||||
if (!qFuzzyIsNull(d->m_label_rotation)) {
|
||||
painter->save();
|
||||
painter->translate(label_pos);
|
||||
painter->rotate(d->m_label_rotation);
|
||||
QRectF text_rect(-text_size.width()/2.0, -text_size.height()/2.0,
|
||||
text_size.width(), text_size.height());
|
||||
painter->drawText(text_rect, static_cast<int>(d->m_label_halignment | d->m_label_valignment), display_name);
|
||||
painter->restore();
|
||||
} else {
|
||||
qreal dx = 0, dy = 0;
|
||||
if (d->m_label_halignment & Qt::AlignLeft) dx = 0;
|
||||
else if (d->m_label_halignment & Qt::AlignHCenter) dx = -text_size.width() / 2.0;
|
||||
else if (d->m_label_halignment & Qt::AlignRight) dx = -text_size.width();
|
||||
|
||||
if (d->m_label_valignment & Qt::AlignTop) dy = 0;
|
||||
else if (d->m_label_valignment & Qt::AlignVCenter) dy = -text_size.height() / 2.0;
|
||||
else if (d->m_label_valignment & Qt::AlignBottom) dy = -text_size.height();
|
||||
|
||||
QRectF text_rect(label_pos + QPointF(dx, dy), text_size);
|
||||
if (d->m_label_frame) {
|
||||
painter->drawRect(text_rect.adjusted(-1, -1, 1, 1));
|
||||
}
|
||||
painter->drawText(text_rect, static_cast<int>(Qt::AlignLeft | Qt::AlignTop), display_name);
|
||||
}
|
||||
}
|
||||
|
||||
painter -> restore();
|
||||
}
|
||||
|
||||
@@ -340,7 +379,28 @@ QLineF Terminal::HelpLine() const
|
||||
@return Le rectangle (en precision flottante) delimitant la borne et ses alentours.
|
||||
*/
|
||||
QRectF Terminal::boundingRect() const {
|
||||
return m_br;
|
||||
const QString display_name = name();
|
||||
if (!d->m_show_name || display_name.isEmpty()) {
|
||||
return m_br;
|
||||
}
|
||||
|
||||
QFontMetrics fm(d->m_label_font);
|
||||
QSizeF text_size = fm.size(Qt::TextSingleLine, display_name);
|
||||
QPointF label_pos = d->m_pos + d->m_label_pos;
|
||||
|
||||
qreal dx = 0, dy = 0;
|
||||
if (d->m_label_halignment & Qt::AlignLeft) dx = 0;
|
||||
else if (d->m_label_halignment & Qt::AlignHCenter) dx = -text_size.width() / 2.0;
|
||||
else if (d->m_label_halignment & Qt::AlignRight) dx = -text_size.width();
|
||||
|
||||
if (d->m_label_valignment & Qt::AlignTop) dy = 0;
|
||||
else if (d->m_label_valignment & Qt::AlignVCenter) dy = -text_size.height() / 2.0;
|
||||
else if (d->m_label_valignment & Qt::AlignBottom) dy = -text_size.height();
|
||||
|
||||
QRectF label_rect(label_pos + QPointF(dx, dy), text_size);
|
||||
label_rect = label_rect.adjusted(-2, -2, 2, 2);
|
||||
|
||||
return m_br.united(label_rect);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -750,6 +810,25 @@ QUuid Terminal::uuid() const
|
||||
|
||||
QString Terminal::name() const
|
||||
{
|
||||
if (d->m_use_master_label && parent_element_) {
|
||||
// Find the master element in the slave's linked elements
|
||||
for (Element *elmt : parent_element_->linkedElements()) {
|
||||
if (elmt->linkType() == Element::Master) {
|
||||
int group_idx = elmt->groupIndexForElement(parent_element_);
|
||||
if (group_idx >= 0) {
|
||||
const auto &groups = elmt->elementData().m_slave_contact_groups;
|
||||
if (group_idx < groups.size()) {
|
||||
int label_idx = d->m_master_label_index;
|
||||
const QStringList &labels = groups.at(group_idx).labels;
|
||||
if (label_idx >= 0 && label_idx < labels.size()) {
|
||||
return labels.at(label_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return d->m_name;
|
||||
}
|
||||
|
||||
@@ -762,6 +841,28 @@ TerminalData::Type Terminal::terminalType() const
|
||||
return d->m_type;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Terminal::setUseMasterLabel
|
||||
Set whether this terminal uses a label from the master's contact group
|
||||
@param use true to use master label
|
||||
*/
|
||||
void Terminal::setUseMasterLabel(bool use)
|
||||
{
|
||||
d->m_use_master_label = use;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Terminal::setMasterLabelIndex
|
||||
Set the index into the master's contact group labels
|
||||
@param index the label index (0-based)
|
||||
*/
|
||||
void Terminal::setMasterLabelIndex(int index)
|
||||
{
|
||||
d->m_master_label_index = index;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Conductor::relatedPotentialTerminal
|
||||
Return terminal at the same potential from the same
|
||||
@@ -788,6 +889,12 @@ QList<Terminal *> relatedPotentialTerminal (
|
||||
// If terminal parent element is a Terminal element.
|
||||
else if (terminal -> parentElement() -> linkType() & Element::Terminale)
|
||||
{
|
||||
// English: Check if the user activated the potential isolation checkbox for this terminal
|
||||
if (terminal->parentElement()->elementInformations().value(QStringLiteral("potential_isolating")).toString() == QLatin1String("true")) {
|
||||
// English: Potential is isolated. Return an empty list so it does not propagate to the other side.
|
||||
return QList<Terminal *>();
|
||||
}
|
||||
|
||||
QList <Terminal *> terminals = terminal->parentElement()->terminals();
|
||||
terminals.removeAll(const_cast<Terminal *>(terminal));
|
||||
return terminals;
|
||||
|
||||
@@ -77,6 +77,10 @@ class Terminal : public QGraphicsObject
|
||||
QUuid uuid () const;
|
||||
QString name () const;
|
||||
TerminalData::Type terminalType() const;
|
||||
bool useMasterLabel() const { return d->m_use_master_label; }
|
||||
void setUseMasterLabel(bool use);
|
||||
int masterLabelIndex() const { return d->m_master_label_index; }
|
||||
void setMasterLabelIndex(int index);
|
||||
|
||||
QList<Conductor *> conductors() const;
|
||||
Qet::Orientation orientation() const;
|
||||
|
||||
Reference in New Issue
Block a user