mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-23 07:04:14 +02:00
Regenerate the English source conversion on master 8b5a09f3fc
Generated by tools/qet-en-source/regenerate.sh, not edited by hand. The tree is the fresh conversion; this commit only joins it to the PR's history so the branch is never force-pushed.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(handle_refresh_probe LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||
add_executable(handle_refresh_probe main.cpp
|
||||
${QET_SOURCE}/sources/QetGraphicsItemModeler/qetgraphicshandleritem.cpp
|
||||
${QET_SOURCE}/sources/QetGraphicsItemModeler/qetgraphicshandleritem.h)
|
||||
target_include_directories(handle_refresh_probe PRIVATE ${QET_SOURCE}/sources)
|
||||
target_link_libraries(handle_refresh_probe PRIVATE Qt6::Widgets)
|
||||
if(WIN32)
|
||||
target_link_libraries(handle_refresh_probe PRIVATE psapi)
|
||||
endif()
|
||||
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Synthetic raster viewport workload, not a full editor latency benchmark.
|
||||
#include <QApplication>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QElapsedTimer>
|
||||
#include <QPainter>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#endif
|
||||
#include "QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
||||
|
||||
static double cpuMilliseconds() {
|
||||
#ifdef Q_OS_WIN
|
||||
FILETIME created,exited,kernel,user;
|
||||
if(GetProcessTimes(GetCurrentProcess(),&created,&exited,&kernel,&user)) {
|
||||
ULARGE_INTEGER k,u; k.LowPart=kernel.dwLowDateTime;k.HighPart=kernel.dwHighDateTime;
|
||||
u.LowPart=user.dwLowDateTime;u.HighPart=user.dwHighDateTime;
|
||||
return double(k.QuadPart+u.QuadPart)/10000;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return double(std::clock())*1000/CLOCKS_PER_SEC;
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc,char **argv) {
|
||||
QApplication app(argc,argv);
|
||||
const qreal size=QString::fromLocal8Bit(argv[1]).toDouble();
|
||||
const int count=QString::fromLocal8Bit(argv[2]).toInt();
|
||||
const QString operation=QString::fromLocal8Bit(argv[3]);
|
||||
QGraphicsScene scene; scene.setSceneRect(-2000,-2000,4000,4000);
|
||||
QList<QGraphicsRectItem *> objects;
|
||||
for(int i=0;i<count/4;++i) {
|
||||
auto *object=scene.addRect(0,0,32,24,QPen(Qt::black),QBrush(Qt::lightGray));
|
||||
object->setPos((i%32)*36-576,(i/32)*28-224); objects<<object;
|
||||
for(auto *handle:QetGraphicsHandlerItem::handlerForPoint({{0,0},{32,0},{32,24},{0,24}},size)) {
|
||||
handle->setParentItem(object); handle->setColor(Qt::darkGreen);
|
||||
}
|
||||
}
|
||||
QGraphicsView view(&scene); view.resize(1280,800); view.setSceneRect(scene.sceneRect());
|
||||
view.show(); QApplication::processEvents();
|
||||
QImage image(1280,800,QImage::Format_ARGB32_Premultiplied);
|
||||
auto frame=[&](int i) {
|
||||
if(operation=="zoom") {view.resetTransform();view.scale(0.75+0.005*(i%60),0.75+0.005*(i%60));}
|
||||
if(operation=="pan") view.centerOn((i%60)*2,0);
|
||||
if(operation=="manipulate") for(auto *object:objects) {
|
||||
object->setRotation(i%30); object->setRect(0,0,32+(i%8),24); object->moveBy(i%2?1:-1,0);
|
||||
}
|
||||
image.fill(Qt::white); QPainter painter(&image); view.render(&painter);
|
||||
};
|
||||
for(int i=0;i<20;++i) frame(i);
|
||||
std::puts("size,handles,operation,repeat,ms_per_frame,cpu_ms_per_frame,working_set_bytes");
|
||||
for(int repeat=0;repeat<7;++repeat) {
|
||||
const auto cpu=cpuMilliseconds(); QElapsedTimer timer; timer.start();
|
||||
for(int i=0;i<120;++i) frame(i);
|
||||
const double elapsed=timer.nsecsElapsed()/1e6/120;
|
||||
const double cpuElapsed=(cpuMilliseconds()-cpu)/120;
|
||||
unsigned long long memory=0;
|
||||
#ifdef Q_OS_WIN
|
||||
PROCESS_MEMORY_COUNTERS counters{}; counters.cb=sizeof(counters);
|
||||
if(GetProcessMemoryInfo(GetCurrentProcess(),&counters,sizeof(counters))) memory=counters.WorkingSetSize;
|
||||
#endif
|
||||
std::printf("%.2f,%d,%s,%d,%.6f,%.6f,%llu\n",size,count,qPrintable(operation),repeat,elapsed,cpuElapsed,memory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run identical Release component workloads; preserve samples and variability."""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
import pathlib
|
||||
import statistics
|
||||
import subprocess
|
||||
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument('--reference', required=True)
|
||||
p.add_argument('--modified', required=True)
|
||||
p.add_argument('--output', required=True)
|
||||
a = p.parse_args()
|
||||
env = os.environ.copy()
|
||||
env['QT_QPA_PLATFORM'] = 'offscreen'
|
||||
rows = []
|
||||
# Compare x1 first, then exercise every remaining size on the modified code.
|
||||
for size in [10, 2.5, 5, 7.5, 20, 30]:
|
||||
for count in [16, 2048]:
|
||||
for operation in ['zoom', 'pan', 'manipulate']:
|
||||
variants = [('reference', a.reference), ('modified', a.modified)] if size == 10 else [('modified', a.modified)]
|
||||
for variant, binary in variants:
|
||||
result = subprocess.run([binary, str(size), str(count), operation], env=env,
|
||||
capture_output=True, text=True, check=True, timeout=120)
|
||||
samples = list(csv.DictReader(io.StringIO(result.stdout)))
|
||||
assert len(samples) == 7
|
||||
rows.extend(dict(variant=variant, **sample) for sample in samples)
|
||||
output = pathlib.Path(a.output)
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
with (output / 'samples.csv').open('w', newline='', encoding='utf-8') as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=list(rows[0]))
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
groups = {}
|
||||
for row in rows:
|
||||
key = (row['variant'], row['size'], row['handles'], row['operation'])
|
||||
groups.setdefault(key, []).append(row)
|
||||
lines = ['# Release handle refresh measurements', '',
|
||||
'Synthetic QGraphicsView raster rendering at 1280×800, offscreen, on the same PC.',
|
||||
'Each sample comprises 120 frames; 20 warm-up frames precede seven samples.',
|
||||
'16 or 2048 handles; same scenes and workload code for both builds.',
|
||||
'Manipulate changes position, rectangle size and rotation of all scene objects.',
|
||||
'This is not full-editor mouse latency, GPU frame presentation or human usability.', '',
|
||||
'| Code | Factor | Handles | Workload | Mean ms/frame ± SD | Min–max | CPU ms/frame | Working set MiB |',
|
||||
'|---|---:|---:|---|---:|---:|---:|---:|']
|
||||
for (variant, size, count, operation), samples in groups.items():
|
||||
wall = [float(s['ms_per_frame']) for s in samples]
|
||||
cpu = [float(s['cpu_ms_per_frame']) for s in samples]
|
||||
memory = [int(s['working_set_bytes']) / 1048576 for s in samples]
|
||||
lines.append(f'| {variant} | {float(size)/10:g} | {count} | {operation} | '
|
||||
f'{statistics.mean(wall):.3f} ± {statistics.stdev(wall):.3f} | '
|
||||
f'{min(wall):.3f}–{max(wall):.3f} | {statistics.mean(cpu):.3f} | '
|
||||
f'{statistics.median(memory):.1f} |')
|
||||
(output / 'measurements.md').write_text('\n'.join(lines)+'\n', encoding='utf-8')
|
||||
print(f'{len(rows)} samples saved in {output}')
|
||||
@@ -74,6 +74,13 @@ target_include_directories(tst_darkimagelayers PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_darkimagelayers PRIVATE Qt::Test Qt::Widgets)
|
||||
find_package(Python3 QUIET COMPONENTS Interpreter)
|
||||
if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
add_test(NAME tst_handlersizeintegration
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_handle_size_probe.py
|
||||
--build ${CMAKE_BINARY_DIR} --binary $<TARGET_FILE:qelectrotech>
|
||||
--source ${CMAKE_CURRENT_SOURCE_DIR}/handle_size_probe.cpp
|
||||
--fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/unlinked_contact_label.qet)
|
||||
set_tests_properties(tst_handlersizeintegration PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE
|
||||
ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
add_test(NAME tst_attachedplainpaste
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_attached_plain_paste_probe.py
|
||||
--build ${CMAKE_BINARY_DIR} --binary $<TARGET_FILE:qelectrotech>
|
||||
@@ -108,6 +115,13 @@ add_test(NAME tst_diagramsortkeys COMMAND tst_diagramsortkeys)
|
||||
target_include_directories(tst_diagramsortkeys PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_diagramsortkeys PRIVATE Qt::Test)
|
||||
|
||||
# positionorder.h is header-only: the position comparisons behind element
|
||||
# renumbering and terminal numbering, as strict weak orderings for std::sort.
|
||||
add_executable(tst_positionorder tst_positionorder.cpp)
|
||||
target_include_directories(tst_positionorder PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_positionorder PRIVATE Qt::Test Qt::Core)
|
||||
add_test(NAME tst_positionorder COMMAND tst_positionorder)
|
||||
|
||||
# bordercelllabels.h is header-only too.
|
||||
add_executable(tst_bordercelllabels tst_bordercelllabels.cpp)
|
||||
add_test(NAME tst_bordercelllabels COMMAND tst_bordercelllabels)
|
||||
@@ -128,6 +142,13 @@ add_test(NAME tst_textgrid COMMAND tst_textgrid)
|
||||
target_include_directories(tst_textgrid PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textgrid PRIVATE Qt::Test)
|
||||
|
||||
# foliogrid.h is header-only: the folio grid step as read from the
|
||||
# settings, never 0 or negative, so no reader can divide by it or loop on it.
|
||||
add_executable(tst_foliogrid tst_foliogrid.cpp)
|
||||
target_include_directories(tst_foliogrid PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_foliogrid PRIVATE Qt::Test Qt::Core)
|
||||
add_test(NAME tst_foliogrid COMMAND tst_foliogrid)
|
||||
|
||||
# textlines.h is header-only: the lines of a text as they are laid out,
|
||||
# for the exports that write a text line by line.
|
||||
add_executable(tst_textlines tst_textlines.cpp)
|
||||
@@ -194,6 +215,14 @@ set_tests_properties(tst_textanchor PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offs
|
||||
target_include_directories(tst_textanchor PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textanchor PRIVATE Qt::Test Qt::Widgets)
|
||||
|
||||
# Shared handle geometry and picking, including fractional sizes.
|
||||
add_executable(tst_handlersize tst_handlersize.cpp
|
||||
${QET_DIR}/sources/QetGraphicsItemModeler/qetgraphicshandleritem.cpp)
|
||||
add_test(NAME tst_handlersize COMMAND tst_handlersize)
|
||||
set_tests_properties(tst_handlersize PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
target_include_directories(tst_handlersize PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_handlersize PRIVATE Qt::Test Qt::Widgets)
|
||||
|
||||
# TextResizeHandles: the corner handles that change the width of a text box,
|
||||
# on a plain QGraphicsTextItem with the "textWidth" property QET texts have.
|
||||
add_executable(
|
||||
@@ -557,6 +586,18 @@ target_compile_definitions(tst_wiringlistexport PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# A label formula with ten or more sequential numbers gets the tenth value
|
||||
# for %sequ_10 (not the first one and a "0"): the real binary's
|
||||
# --export-bom on fixtures/ten_sequential_numbers.qet.
|
||||
add_executable(
|
||||
tst_tensequentialnumbers
|
||||
tst_tensequentialnumbers.cpp)
|
||||
add_test(NAME tst_tensequentialnumbers COMMAND tst_tensequentialnumbers)
|
||||
add_dependencies(tst_tensequentialnumbers qelectrotech)
|
||||
target_link_libraries(tst_tensequentialnumbers PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_tensequentialnumbers PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# --export-dxf writes a wrapped symbol text line by line, as it is drawn.
|
||||
add_executable(
|
||||
tst_dxfwrappedtext
|
||||
@@ -894,6 +935,20 @@ if(QET_HAS_SCRIPTING)
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
endif()
|
||||
|
||||
# A picture whose saved crop does not match the picture shown (an undone
|
||||
# crop, saved before #1310) loads without that crop; a valid crop is kept.
|
||||
if(QET_HAS_SCRIPTING)
|
||||
add_executable(
|
||||
tst_imagestalecrop
|
||||
tst_imagestalecrop.cpp)
|
||||
add_test(NAME tst_imagestalecrop COMMAND tst_imagestalecrop)
|
||||
add_dependencies(tst_imagestalecrop qelectrotech)
|
||||
target_link_libraries(tst_imagestalecrop PRIVATE Qt::Test Qt::Gui)
|
||||
target_compile_definitions(tst_imagestalecrop PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
endif()
|
||||
|
||||
# Saving a project that was just saved changes nothing: runs the real
|
||||
# binary's --resave twice on every project in examples/, and on one whose
|
||||
# title block holds a single-space value (#973).
|
||||
@@ -908,6 +963,18 @@ target_compile_definitions(tst_resaveunchanged PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# A folio's "freeze new elements/conductors" flags, saved as the words
|
||||
# "true"/"false", are read back as words: the real binary's --resave on
|
||||
# fixtures/folio_freeze_flags.qet, one folio per combination.
|
||||
add_executable(
|
||||
tst_foliofreezeflags
|
||||
tst_foliofreezeflags.cpp)
|
||||
add_test(NAME tst_foliofreezeflags COMMAND tst_foliofreezeflags)
|
||||
add_dependencies(tst_foliofreezeflags qelectrotech)
|
||||
target_link_libraries(tst_foliofreezeflags PRIVATE Qt::Test Qt::Xml)
|
||||
target_compile_definitions(tst_foliofreezeflags PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# Replacing a symbol's definition in a project keeps its terminal uuids, so
|
||||
# the wires saved against them still load: TerminalUuids::keep() on its own,
|
||||
# then the real binary's --info on an example whose symbols were replaced.
|
||||
@@ -922,6 +989,19 @@ target_compile_definitions(tst_terminaluuids PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# Sequential numbers saved the old way, as sequ_1/.../seqhf_1 attributes,
|
||||
# are all read: the real binary's --resave on
|
||||
# fixtures/legacy_sequential_attributes.qet, whose element and conductor
|
||||
# carry only seqhf_1.
|
||||
add_executable(
|
||||
tst_legacysequentialattributes
|
||||
tst_legacysequentialattributes.cpp)
|
||||
add_test(NAME tst_legacysequentialattributes COMMAND tst_legacysequentialattributes)
|
||||
add_dependencies(tst_legacysequentialattributes qelectrotech)
|
||||
target_link_libraries(tst_legacysequentialattributes PRIVATE Qt::Test Qt::Xml)
|
||||
target_compile_definitions(tst_legacysequentialattributes PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# ConductorProperties::applyChanges() -- the Selection properties panel
|
||||
# writes only the fields the user edited.
|
||||
add_executable(
|
||||
@@ -935,6 +1015,19 @@ add_test(NAME tst_conductorapplychanges COMMAND tst_conductorapplychanges)
|
||||
target_include_directories(tst_conductorapplychanges PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_conductorapplychanges PRIVATE Qt::Test Qt::Widgets Qt::Xml pugixml::pugixml)
|
||||
|
||||
# ConductorProperties::fromSettings() -- the default conductor width is a
|
||||
# decimal and comes back as saved (1.4 used to be read as 0).
|
||||
add_executable(
|
||||
tst_conductorsizesetting
|
||||
tst_conductorsizesetting.cpp
|
||||
${QET_DIR}/sources/conductorproperties.cpp
|
||||
${QET_DIR}/sources/qet.cpp
|
||||
${QET_DIR}/sources/qeticons.cpp
|
||||
${QET_DIR}/sources/shortcutmanager.cpp)
|
||||
add_test(NAME tst_conductorsizesetting COMMAND tst_conductorsizesetting)
|
||||
target_include_directories(tst_conductorsizesetting PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_conductorsizesetting PRIVATE Qt::Test Qt::Widgets Qt::Xml pugixml::pugixml)
|
||||
|
||||
# ConductorMultiEdit -- editing several wires at once in the Selection
|
||||
# properties panel: order, which wires an edit reaches, one undo step for
|
||||
# all of them, and fields shown blank because the wires disagree.
|
||||
@@ -976,6 +1069,16 @@ add_test(NAME tst_xrefpos COMMAND tst_xrefpos)
|
||||
target_include_directories(tst_xrefpos PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_xrefpos PRIVATE Qt::Test Qt::Widgets Qt::Xml pugixml::pugixml)
|
||||
|
||||
# BorderProperties::fromXml() -- the project's <border> for new folios:
|
||||
# column width and row height are decimals and are read as such.
|
||||
add_executable(
|
||||
tst_borderpropertiesxml
|
||||
tst_borderpropertiesxml.cpp
|
||||
${QET_DIR}/sources/borderproperties.cpp)
|
||||
add_test(NAME tst_borderpropertiesxml COMMAND tst_borderpropertiesxml)
|
||||
target_include_directories(tst_borderpropertiesxml PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_borderpropertiesxml PRIVATE Qt::Test Qt::Xml)
|
||||
|
||||
# Qt 6.4.2 writes the attributes of an element in an order that changes from
|
||||
# one process to the next, so a project saved twice by two runs of the binary
|
||||
# differs in bytes only (seen on projects without any numbering, too). A fixed
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<project title="Folio freeze flags" uuid="{7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e00}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="savedfilename" show="1">folio_freeze_flags</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs/>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="true" freezeNewElement="true" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="both frozen" uuid="{7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e01}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
</diagram>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="true" height="660" indexrev="" locmach="" order="2" plant="" rows="8" rowsize="80" title="elements frozen" uuid="{7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e02}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
</diagram>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="3" plant="" rows="8" rowsize="80" title="not frozen" uuid="{7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e03}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
</diagram>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" height="660" indexrev="" locmach="" order="4" plant="" rows="8" rowsize="80" title="no attributes" uuid="{7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e04}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,351 @@
|
||||
<project title="Legacy sequential attributes - i1280" uuid="{abc27554-b301-5651-8055-41e0ce4599cb}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/4/26</property>
|
||||
<property name="saveddate-eu" show="1">04-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-04</property>
|
||||
<property name="savedfilename" show="1">skeleton</property>
|
||||
<property name="savedtime" show="1">10:28</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{a94486ed-a490-4ae0-9698-b075035de54d}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" seqhf_1="5" uuid="{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" x="100" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="0" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="1" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{1b01d6c6-86f7-44e4-8e76-eba4b3a53dea}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{65cedcc2-1a82-4168-858c-8751bc1561d9}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{91920aeb-94ef-423e-a282-206489f04e5e}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" sequ_1="3" uuid="{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" x="200" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="2" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="3" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{827f6a67-cf32-448a-b33a-e52fbbb9cee7}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{8fe1bb6c-bd54-429f-b8b9-2b1ca789ed2c}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{15fa6b73-1702-4e17-b821-3bd12f81e7ba}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" uuid="{b6bceef1-5f5c-4119-bc0c-4ea629eb0baa}" x="300" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="4" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="5" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{93d83639-c2c1-4af5-9048-c0a5e0f241b8}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{c14602f0-24e8-463c-a183-3dac7f78e009}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{662e7f9d-9f6d-40b4-b517-7b584f6b3604}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" uuid="{5d8752c5-1f02-4ef1-a24f-c7844ede38c2}" x="400" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="6" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="7" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{3c604f1b-1498-4b2b-9f2d-0063e2bcb78c}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{dbb98cf7-9d66-4d99-91d9-2149077d6ba2}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{a8efc4c9-bfb3-4bde-a501-6b1bd5f93ae5}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
<conductors>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" element1_label="" element1_name="Normally open counter" element2="{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" element2_label="" element2_name="Normally open counter" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{77224246-2278-498a-88e5-1ecd84e938e8}" terminal2="{77224246-2278-498a-88e5-1ecd84e938e8}" terminalname1="" terminalname2="" text_color="#000000" type="multi" seqhf_1="7" uuid="{f5162c59-0c59-4e94-b6a6-4cf259a52d91}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" element1_label="" element1_name="Normally open counter" element2="{5d8752c5-1f02-4ef1-a24f-c7844ede38c2}" element2_label="" element2_name="Normally open counter" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{77224246-2278-498a-88e5-1ecd84e938e8}" terminal2="{77224246-2278-498a-88e5-1ecd84e938e8}" terminalname1="" terminalname2="" text_color="#000000" type="multi" sequ_1="4" uuid="{89cacce1-b12a-4ec3-aadf-d3f8feb7494d}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" element1_label="" element1_name="Normally open counter" element2="{b6bceef1-5f5c-4119-bc0c-4ea629eb0baa}" element2_label="" element2_name="Normally open counter" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{77224246-2278-498a-88e5-1ecd84e938e8}" terminal2="{77224246-2278-498a-88e5-1ecd84e938e8}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{944a0069-bad6-4d92-91ad-7aedf3961c8a}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
</conductors>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="ca">Elements importats</name>
|
||||
<name lang="cs">Zavedené prvky</name>
|
||||
<name lang="da">Importerede elementer</name>
|
||||
<name lang="de">Importierte Elemente</name>
|
||||
<name lang="el">Εισηγμένα στοιχεία</name>
|
||||
<name lang="en">Imported elements</name>
|
||||
<name lang="es">Elementos importados</name>
|
||||
<name lang="fr">Éléments importés</name>
|
||||
<name lang="hr">Uvezeni elementi</name>
|
||||
<name lang="it">Elementi importati</name>
|
||||
<name lang="ja">インバートされた要素</name>
|
||||
<name lang="ko">전기 심볼 요소</name>
|
||||
<name lang="ko_KR">전기 심볼 요소</name>
|
||||
<name lang="nl">Elementen geïmporteerd</name>
|
||||
<name lang="nl_BE">Elementen geïmporteerd</name>
|
||||
<name lang="pl">Elementy importowane</name>
|
||||
<name lang="pt">Elementos importados</name>
|
||||
<name lang="pt_BR">Elementos importados</name>
|
||||
<name lang="ro">Elemente importate</name>
|
||||
<name lang="ru">Импортированные элементы</name>
|
||||
<name lang="sl">Uvoženi elementi</name>
|
||||
<name lang="sv">Importerade element</name>
|
||||
<name lang="tr">İthal öğeler</name>
|
||||
<name lang="uk">Імпортовані елементи</name>
|
||||
<name lang="zh">导入元件</name>
|
||||
</names>
|
||||
<category name="10_electric">
|
||||
<names>
|
||||
<name lang="ar">كهربائي</name>
|
||||
<name lang="ca">Elèctric</name>
|
||||
<name lang="cs">Elektrotechnika</name>
|
||||
<name lang="da">Elektrisk</name>
|
||||
<name lang="de">Elektrik</name>
|
||||
<name lang="el">Ηλεκτρικά</name>
|
||||
<name lang="en">Electric</name>
|
||||
<name lang="es">Eléctrica</name>
|
||||
<name lang="fi">Sähkö</name>
|
||||
<name lang="fr">Electrique</name>
|
||||
<name lang="hr">Električno</name>
|
||||
<name lang="hu">Elektromos</name>
|
||||
<name lang="it">Elettrica</name>
|
||||
<name lang="ja">電気</name>
|
||||
<name lang="ko">전기</name>
|
||||
<name lang="mn">Цахилгаан</name>
|
||||
<name lang="nb">Elektrisk</name>
|
||||
<name lang="nl">Elektrotechniek</name>
|
||||
<name lang="nl_BE">Elektrisch</name>
|
||||
<name lang="pl">Elektrotechnika</name>
|
||||
<name lang="pt">Elétrico</name>
|
||||
<name lang="pt_BR">Elétrica</name>
|
||||
<name lang="ro">Electric</name>
|
||||
<name lang="ru">Электротехника</name>
|
||||
<name lang="sk">Elektrické</name>
|
||||
<name lang="sl">Elektrika</name>
|
||||
<name lang="sr">Електрика</name>
|
||||
<name lang="sv">El</name>
|
||||
<name lang="tr">Elektrik</name>
|
||||
<name lang="uk">Електрика</name>
|
||||
<name lang="zh">10_电气</name>
|
||||
<name lang="zn">电气工程</name>
|
||||
</names>
|
||||
<category name="10_allpole">
|
||||
<names>
|
||||
<name lang="ar">متعدد الأقطاب</name>
|
||||
<name lang="ca">Multifilar</name>
|
||||
<name lang="cs">Vícežilový</name>
|
||||
<name lang="da">Flere ledere</name>
|
||||
<name lang="de">Allpolig</name>
|
||||
<name lang="el">Πολυγραμμικό</name>
|
||||
<name lang="en">All-pole</name>
|
||||
<name lang="es">Multifilar</name>
|
||||
<name lang="fi">Yleisnapa</name>
|
||||
<name lang="fr">Multifilaire</name>
|
||||
<name lang="hr">Univerzalni</name>
|
||||
<name lang="hu">Többvonalas szimbólumok</name>
|
||||
<name lang="it">Multifilare</name>
|
||||
<name lang="ja">複線</name>
|
||||
<name lang="ko">다-극</name>
|
||||
<name lang="mn">Бүх туйл</name>
|
||||
<name lang="nb">Alle poler</name>
|
||||
<name lang="nl">Veel polig</name>
|
||||
<name lang="nl_BE">All-pole</name>
|
||||
<name lang="pl">Schematy wieloliniowe</name>
|
||||
<name lang="pt">Multipolar</name>
|
||||
<name lang="pt_BR">Multifilar</name>
|
||||
<name lang="ro">Bipolar</name>
|
||||
<name lang="ru">Многополюсные</name>
|
||||
<name lang="sk">Všepólové</name>
|
||||
<name lang="sl">Vsi poli</name>
|
||||
<name lang="sr">Свe-полни</name>
|
||||
<name lang="sv">Allpolig</name>
|
||||
<name lang="tr">Çok kutuplu</name>
|
||||
<name lang="uk">Багатополюсні</name>
|
||||
<name lang="zh">10_全极</name>
|
||||
</names>
|
||||
<category name="310_relays_contactors_contacts">
|
||||
<names>
|
||||
<name lang="ar">المرحّلات والموصّلات وجهات الاتصال</name>
|
||||
<name lang="ca">Relés, contactors i contactes</name>
|
||||
<name lang="cs">Relé, stykače a kontakty</name>
|
||||
<name lang="da">Relæer, kontaktorer og kontakter</name>
|
||||
<name lang="de">Relais, Schütze und Kontakte</name>
|
||||
<name lang="el">Ηλεκτρονόμοι και επαφές</name>
|
||||
<name lang="en">Relays, contactors and contacts</name>
|
||||
<name lang="es">Relevadores, contactores y contactos</name>
|
||||
<name lang="fi">Releet, kontaktorit ja koskettimet</name>
|
||||
<name lang="fr">Relais, contacteurs et contacts</name>
|
||||
<name lang="hr">Releji, kontaktori i kontakti</name>
|
||||
<name lang="hu">Relék, kontaktorok és érintkezők</name>
|
||||
<name lang="it">Relè, contattori e contatti</name>
|
||||
<name lang="ja">継電器、接触器、接点</name>
|
||||
<name lang="ko">릴레이, 커넥터및 접점</name>
|
||||
<name lang="mn">Реле, контактор болон контакт</name>
|
||||
<name lang="nb">Reléer, kontaktorere og kontakter</name>
|
||||
<name lang="nl">Relais, contactoren en contacten</name>
|
||||
<name lang="nl_BE">Relais, contacteurs en contacten</name>
|
||||
<name lang="pl">Przekaźniki i styczniki</name>
|
||||
<name lang="pt">Relés, contatores e contactos</name>
|
||||
<name lang="pt_BR">Relés, Contatores e contatos</name>
|
||||
<name lang="ro">Relee, contactoare și contacte</name>
|
||||
<name lang="ru">Реле, пускатели и контакты</name>
|
||||
<name lang="sk">Relé, stykače a kontakty</name>
|
||||
<name lang="sl">Releji, kontaktorji in kontakti</name>
|
||||
<name lang="sr">Релеји, контактори и контакти</name>
|
||||
<name lang="sv">Reläer, kontaktorer och kontakter</name>
|
||||
<name lang="tr">Röleler, kontaktörler ve kontaklar</name>
|
||||
<name lang="uk">Реле, контактори та контакти</name>
|
||||
<name lang="zh">继电器、接触器与触点</name>
|
||||
</names>
|
||||
<category name="03_contacts">
|
||||
<names>
|
||||
<name lang="ar">جهات الاتصال</name>
|
||||
<name lang="ca">Contactes</name>
|
||||
<name lang="cs">Kontakty</name>
|
||||
<name lang="da">Kontaktpersoner</name>
|
||||
<name lang="de">Kontakte</name>
|
||||
<name lang="el">Επαφές</name>
|
||||
<name lang="en">Contacts</name>
|
||||
<name lang="es">Contactos</name>
|
||||
<name lang="fi">Kosketimet</name>
|
||||
<name lang="fr">Contacts</name>
|
||||
<name lang="hr">Kontakti</name>
|
||||
<name lang="hu">Kapcsolatok</name>
|
||||
<name lang="it">Contatti</name>
|
||||
<name lang="ja">連絡先</name>
|
||||
<name lang="ko">접점</name>
|
||||
<name lang="mn">Харилцагчид</name>
|
||||
<name lang="nb">Kontakter</name>
|
||||
<name lang="nl">Contacten</name>
|
||||
<name lang="nl_BE">Contacten</name>
|
||||
<name lang="pl">Łączność</name>
|
||||
<name lang="pt">Contatos</name>
|
||||
<name lang="pt_BR">Contatos</name>
|
||||
<name lang="ro">Contacte</name>
|
||||
<name lang="ru">Контакты</name>
|
||||
<name lang="sk">Kontakty</name>
|
||||
<name lang="sl">Stiki</name>
|
||||
<name lang="sr">Контакти</name>
|
||||
<name lang="sv">Kontakter</name>
|
||||
<name lang="tr">Kişiler</name>
|
||||
<name lang="uk">Контакти</name>
|
||||
<name lang="zh">触点</name>
|
||||
</names>
|
||||
<element name="act_comptage_no.elmt">
|
||||
<definition height="60" hotspot_x="22" hotspot_y="41" link_type="simple" type="element" version="0.100.0" width="30">
|
||||
<uuid uuid="{19fdfdc7-c1fe-4604-cb14-da967d1a5252}"/>
|
||||
<names>
|
||||
<name lang="ar">عدّاد مفتوح عادة</name>
|
||||
<name lang="ca">Taulell normalment obert</name>
|
||||
<name lang="cs">Kontakt čítače (NO)</name>
|
||||
<name lang="da">Normalt åben tæller</name>
|
||||
<name lang="de">Öffner Zähler betätigt</name>
|
||||
<name lang="el">Ανοικτή επαφή μετρητή</name>
|
||||
<name lang="en">Normally open counter</name>
|
||||
<name lang="es">Contador NA</name>
|
||||
<name lang="fi">Normaalisti avoin laskuri</name>
|
||||
<name lang="fr">Actionné par comptage NO</name>
|
||||
<name lang="hr">Brojač normalno otvoren</name>
|
||||
<name lang="hu">Alaphelyzetben nyitott számláló</name>
|
||||
<name lang="it">Contatto azionato da un contatore (NO)</name>
|
||||
<name lang="ja">常開カウンター</name>
|
||||
<name lang="ko">평상시 열림 카운터</name>
|
||||
<name lang="mn">Ихэвчлэн нээлттэй тоолуур</name>
|
||||
<name lang="nb">Normalt åpen teller</name>
|
||||
<name lang="nl">Teller contact (NO)</name>
|
||||
<name lang="nl_BE">Normaal open teller</name>
|
||||
<name lang="pl">Zestyk zwierny licznika</name>
|
||||
<name lang="pt">Contador normalmente aberto</name>
|
||||
<name lang="pt_BR">Contato NA acionado por contador</name>
|
||||
<name lang="ro">Contor normal deschis</name>
|
||||
<name lang="ru">Счётчик с нормально открытым контактом</name>
|
||||
<name lang="sk">Počítadlo s kontaktom NO</name>
|
||||
<name lang="sl">Števec normalno odprt</name>
|
||||
<name lang="sr">Бројач нормално отворен</name>
|
||||
<name lang="sv">Räknare med slutande kontakt</name>
|
||||
<name lang="tr">Normalde açık sayaç</name>
|
||||
<name lang="uk">Лічильник нормально розімкнений</name>
|
||||
<name lang="zh">计数驱动常开触点</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<rect antialias="false" height="10" rx="0" ry="0" style="line-style:normal;line-weight:thin;filling:none;color:black" width="10" x="-20" y="-15"/>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:dashed;line-weight:thin;filling:none;color:black" x1="-10" x2="-3" y1="-10" y2="-10"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="UserText" text_width="-1" uuid="{a3321491-1dac-42e2-a9d1-1a43604116a9}" x="-13" y="-2" z="3">
|
||||
<text>14</text>
|
||||
</dynamic_text>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="UserText" text_width="-1" uuid="{a82a123f-f3cf-4dd7-93c0-44bbdd940429}" x="-13" y="-34" z="4">
|
||||
<text>11</text>
|
||||
</dynamic_text>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="ElementInfo" text_width="-1" uuid="{960d8065-c520-4d45-aa36-4bb600c0e6db}" x="1" y="-22" z="5">
|
||||
<text/>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_text>
|
||||
<polygon antialias="true" closed="false" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="-5" x2="0" x3="0" y1="-20" y2="0" y3="10"/>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="0" y1="-30" y2="-20"/>
|
||||
<ellipse antialias="true" height="4" style="line-style:normal;line-weight:thin;filling:none;color:black" width="4" x="-17" y="-12"/>
|
||||
<terminal name="" orientation="s" type="Generic" uuid="{572018e1-9843-49dd-964f-836ff27b8240}" x="0" y="10"/>
|
||||
<terminal name="" orientation="n" type="Generic" uuid="{77224246-2278-498a-88e5-1ecd84e938e8}" x="0" y="-30"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,359 @@
|
||||
<project title="Ten sequential numbers - i1280" uuid="{abc27554-b301-5651-8055-41e0ce4599cb}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/4/26</property>
|
||||
<property name="saveddate-eu" show="1">04-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-04</property>
|
||||
<property name="savedfilename" show="1">skeleton</property>
|
||||
<property name="savedtime" show="1">10:28</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{a94486ed-a490-4ae0-9698-b075035de54d}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" uuid="{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" x="100" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="0" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="1" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<elementInformations>
|
||||
<elementInformation name="formula" show="1">%sequ_10-%sequ_1</elementInformation>
|
||||
</elementInformations>
|
||||
<sequentialNumbers>
|
||||
<unit>A;B;C;D;E;F;G;H;I;J</unit>
|
||||
</sequentialNumbers>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{1b01d6c6-86f7-44e4-8e76-eba4b3a53dea}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{65cedcc2-1a82-4168-858c-8751bc1561d9}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{91920aeb-94ef-423e-a282-206489f04e5e}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" uuid="{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" x="200" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="2" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="3" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{827f6a67-cf32-448a-b33a-e52fbbb9cee7}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{8fe1bb6c-bd54-429f-b8b9-2b1ca789ed2c}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{15fa6b73-1702-4e17-b821-3bd12f81e7ba}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" uuid="{b6bceef1-5f5c-4119-bc0c-4ea629eb0baa}" x="300" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="4" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="5" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{93d83639-c2c1-4af5-9048-c0a5e0f241b8}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{c14602f0-24e8-463c-a183-3dac7f78e009}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{662e7f9d-9f6d-40b4-b517-7b584f6b3604}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/03_contacts/act_comptage_no.elmt" uuid="{5d8752c5-1f02-4ef1-a24f-c7844ede38c2}" x="400" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="6" orientation="0" x="0" y="-26"/>
|
||||
<terminal id="7" orientation="2" x="0" y="6"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{3c604f1b-1498-4b2b-9f2d-0063e2bcb78c}" x="-13" y="-2">
|
||||
<text>14</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{dbb98cf7-9d66-4d99-91d9-2149077d6ba2}" x="-13" y="-34">
|
||||
<text>11</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{a8efc4c9-bfb3-4bde-a501-6b1bd5f93ae5}" x="1" y="-22">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
<conductors>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" element1_label="" element1_name="Normally open counter" element2="{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" element2_label="" element2_name="Normally open counter" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{77224246-2278-498a-88e5-1ecd84e938e8}" terminal2="{77224246-2278-498a-88e5-1ecd84e938e8}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{f5162c59-0c59-4e94-b6a6-4cf259a52d91}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" element1_label="" element1_name="Normally open counter" element2="{5d8752c5-1f02-4ef1-a24f-c7844ede38c2}" element2_label="" element2_name="Normally open counter" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{77224246-2278-498a-88e5-1ecd84e938e8}" terminal2="{77224246-2278-498a-88e5-1ecd84e938e8}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{89cacce1-b12a-4ec3-aadf-d3f8feb7494d}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" element1_label="" element1_name="Normally open counter" element2="{b6bceef1-5f5c-4119-bc0c-4ea629eb0baa}" element2_label="" element2_name="Normally open counter" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{77224246-2278-498a-88e5-1ecd84e938e8}" terminal2="{77224246-2278-498a-88e5-1ecd84e938e8}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{944a0069-bad6-4d92-91ad-7aedf3961c8a}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
</conductors>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="ca">Elements importats</name>
|
||||
<name lang="cs">Zavedené prvky</name>
|
||||
<name lang="da">Importerede elementer</name>
|
||||
<name lang="de">Importierte Elemente</name>
|
||||
<name lang="el">Εισηγμένα στοιχεία</name>
|
||||
<name lang="en">Imported elements</name>
|
||||
<name lang="es">Elementos importados</name>
|
||||
<name lang="fr">Éléments importés</name>
|
||||
<name lang="hr">Uvezeni elementi</name>
|
||||
<name lang="it">Elementi importati</name>
|
||||
<name lang="ja">インバートされた要素</name>
|
||||
<name lang="ko">전기 심볼 요소</name>
|
||||
<name lang="ko_KR">전기 심볼 요소</name>
|
||||
<name lang="nl">Elementen geïmporteerd</name>
|
||||
<name lang="nl_BE">Elementen geïmporteerd</name>
|
||||
<name lang="pl">Elementy importowane</name>
|
||||
<name lang="pt">Elementos importados</name>
|
||||
<name lang="pt_BR">Elementos importados</name>
|
||||
<name lang="ro">Elemente importate</name>
|
||||
<name lang="ru">Импортированные элементы</name>
|
||||
<name lang="sl">Uvoženi elementi</name>
|
||||
<name lang="sv">Importerade element</name>
|
||||
<name lang="tr">İthal öğeler</name>
|
||||
<name lang="uk">Імпортовані елементи</name>
|
||||
<name lang="zh">导入元件</name>
|
||||
</names>
|
||||
<category name="10_electric">
|
||||
<names>
|
||||
<name lang="ar">كهربائي</name>
|
||||
<name lang="ca">Elèctric</name>
|
||||
<name lang="cs">Elektrotechnika</name>
|
||||
<name lang="da">Elektrisk</name>
|
||||
<name lang="de">Elektrik</name>
|
||||
<name lang="el">Ηλεκτρικά</name>
|
||||
<name lang="en">Electric</name>
|
||||
<name lang="es">Eléctrica</name>
|
||||
<name lang="fi">Sähkö</name>
|
||||
<name lang="fr">Electrique</name>
|
||||
<name lang="hr">Električno</name>
|
||||
<name lang="hu">Elektromos</name>
|
||||
<name lang="it">Elettrica</name>
|
||||
<name lang="ja">電気</name>
|
||||
<name lang="ko">전기</name>
|
||||
<name lang="mn">Цахилгаан</name>
|
||||
<name lang="nb">Elektrisk</name>
|
||||
<name lang="nl">Elektrotechniek</name>
|
||||
<name lang="nl_BE">Elektrisch</name>
|
||||
<name lang="pl">Elektrotechnika</name>
|
||||
<name lang="pt">Elétrico</name>
|
||||
<name lang="pt_BR">Elétrica</name>
|
||||
<name lang="ro">Electric</name>
|
||||
<name lang="ru">Электротехника</name>
|
||||
<name lang="sk">Elektrické</name>
|
||||
<name lang="sl">Elektrika</name>
|
||||
<name lang="sr">Електрика</name>
|
||||
<name lang="sv">El</name>
|
||||
<name lang="tr">Elektrik</name>
|
||||
<name lang="uk">Електрика</name>
|
||||
<name lang="zh">10_电气</name>
|
||||
<name lang="zn">电气工程</name>
|
||||
</names>
|
||||
<category name="10_allpole">
|
||||
<names>
|
||||
<name lang="ar">متعدد الأقطاب</name>
|
||||
<name lang="ca">Multifilar</name>
|
||||
<name lang="cs">Vícežilový</name>
|
||||
<name lang="da">Flere ledere</name>
|
||||
<name lang="de">Allpolig</name>
|
||||
<name lang="el">Πολυγραμμικό</name>
|
||||
<name lang="en">All-pole</name>
|
||||
<name lang="es">Multifilar</name>
|
||||
<name lang="fi">Yleisnapa</name>
|
||||
<name lang="fr">Multifilaire</name>
|
||||
<name lang="hr">Univerzalni</name>
|
||||
<name lang="hu">Többvonalas szimbólumok</name>
|
||||
<name lang="it">Multifilare</name>
|
||||
<name lang="ja">複線</name>
|
||||
<name lang="ko">다-극</name>
|
||||
<name lang="mn">Бүх туйл</name>
|
||||
<name lang="nb">Alle poler</name>
|
||||
<name lang="nl">Veel polig</name>
|
||||
<name lang="nl_BE">All-pole</name>
|
||||
<name lang="pl">Schematy wieloliniowe</name>
|
||||
<name lang="pt">Multipolar</name>
|
||||
<name lang="pt_BR">Multifilar</name>
|
||||
<name lang="ro">Bipolar</name>
|
||||
<name lang="ru">Многополюсные</name>
|
||||
<name lang="sk">Všepólové</name>
|
||||
<name lang="sl">Vsi poli</name>
|
||||
<name lang="sr">Свe-полни</name>
|
||||
<name lang="sv">Allpolig</name>
|
||||
<name lang="tr">Çok kutuplu</name>
|
||||
<name lang="uk">Багатополюсні</name>
|
||||
<name lang="zh">10_全极</name>
|
||||
</names>
|
||||
<category name="310_relays_contactors_contacts">
|
||||
<names>
|
||||
<name lang="ar">المرحّلات والموصّلات وجهات الاتصال</name>
|
||||
<name lang="ca">Relés, contactors i contactes</name>
|
||||
<name lang="cs">Relé, stykače a kontakty</name>
|
||||
<name lang="da">Relæer, kontaktorer og kontakter</name>
|
||||
<name lang="de">Relais, Schütze und Kontakte</name>
|
||||
<name lang="el">Ηλεκτρονόμοι και επαφές</name>
|
||||
<name lang="en">Relays, contactors and contacts</name>
|
||||
<name lang="es">Relevadores, contactores y contactos</name>
|
||||
<name lang="fi">Releet, kontaktorit ja koskettimet</name>
|
||||
<name lang="fr">Relais, contacteurs et contacts</name>
|
||||
<name lang="hr">Releji, kontaktori i kontakti</name>
|
||||
<name lang="hu">Relék, kontaktorok és érintkezők</name>
|
||||
<name lang="it">Relè, contattori e contatti</name>
|
||||
<name lang="ja">継電器、接触器、接点</name>
|
||||
<name lang="ko">릴레이, 커넥터및 접점</name>
|
||||
<name lang="mn">Реле, контактор болон контакт</name>
|
||||
<name lang="nb">Reléer, kontaktorere og kontakter</name>
|
||||
<name lang="nl">Relais, contactoren en contacten</name>
|
||||
<name lang="nl_BE">Relais, contacteurs en contacten</name>
|
||||
<name lang="pl">Przekaźniki i styczniki</name>
|
||||
<name lang="pt">Relés, contatores e contactos</name>
|
||||
<name lang="pt_BR">Relés, Contatores e contatos</name>
|
||||
<name lang="ro">Relee, contactoare și contacte</name>
|
||||
<name lang="ru">Реле, пускатели и контакты</name>
|
||||
<name lang="sk">Relé, stykače a kontakty</name>
|
||||
<name lang="sl">Releji, kontaktorji in kontakti</name>
|
||||
<name lang="sr">Релеји, контактори и контакти</name>
|
||||
<name lang="sv">Reläer, kontaktorer och kontakter</name>
|
||||
<name lang="tr">Röleler, kontaktörler ve kontaklar</name>
|
||||
<name lang="uk">Реле, контактори та контакти</name>
|
||||
<name lang="zh">继电器、接触器与触点</name>
|
||||
</names>
|
||||
<category name="03_contacts">
|
||||
<names>
|
||||
<name lang="ar">جهات الاتصال</name>
|
||||
<name lang="ca">Contactes</name>
|
||||
<name lang="cs">Kontakty</name>
|
||||
<name lang="da">Kontaktpersoner</name>
|
||||
<name lang="de">Kontakte</name>
|
||||
<name lang="el">Επαφές</name>
|
||||
<name lang="en">Contacts</name>
|
||||
<name lang="es">Contactos</name>
|
||||
<name lang="fi">Kosketimet</name>
|
||||
<name lang="fr">Contacts</name>
|
||||
<name lang="hr">Kontakti</name>
|
||||
<name lang="hu">Kapcsolatok</name>
|
||||
<name lang="it">Contatti</name>
|
||||
<name lang="ja">連絡先</name>
|
||||
<name lang="ko">접점</name>
|
||||
<name lang="mn">Харилцагчид</name>
|
||||
<name lang="nb">Kontakter</name>
|
||||
<name lang="nl">Contacten</name>
|
||||
<name lang="nl_BE">Contacten</name>
|
||||
<name lang="pl">Łączność</name>
|
||||
<name lang="pt">Contatos</name>
|
||||
<name lang="pt_BR">Contatos</name>
|
||||
<name lang="ro">Contacte</name>
|
||||
<name lang="ru">Контакты</name>
|
||||
<name lang="sk">Kontakty</name>
|
||||
<name lang="sl">Stiki</name>
|
||||
<name lang="sr">Контакти</name>
|
||||
<name lang="sv">Kontakter</name>
|
||||
<name lang="tr">Kişiler</name>
|
||||
<name lang="uk">Контакти</name>
|
||||
<name lang="zh">触点</name>
|
||||
</names>
|
||||
<element name="act_comptage_no.elmt">
|
||||
<definition height="60" hotspot_x="22" hotspot_y="41" link_type="simple" type="element" version="0.100.0" width="30">
|
||||
<uuid uuid="{19fdfdc7-c1fe-4604-cb14-da967d1a5252}"/>
|
||||
<names>
|
||||
<name lang="ar">عدّاد مفتوح عادة</name>
|
||||
<name lang="ca">Taulell normalment obert</name>
|
||||
<name lang="cs">Kontakt čítače (NO)</name>
|
||||
<name lang="da">Normalt åben tæller</name>
|
||||
<name lang="de">Öffner Zähler betätigt</name>
|
||||
<name lang="el">Ανοικτή επαφή μετρητή</name>
|
||||
<name lang="en">Normally open counter</name>
|
||||
<name lang="es">Contador NA</name>
|
||||
<name lang="fi">Normaalisti avoin laskuri</name>
|
||||
<name lang="fr">Actionné par comptage NO</name>
|
||||
<name lang="hr">Brojač normalno otvoren</name>
|
||||
<name lang="hu">Alaphelyzetben nyitott számláló</name>
|
||||
<name lang="it">Contatto azionato da un contatore (NO)</name>
|
||||
<name lang="ja">常開カウンター</name>
|
||||
<name lang="ko">평상시 열림 카운터</name>
|
||||
<name lang="mn">Ихэвчлэн нээлттэй тоолуур</name>
|
||||
<name lang="nb">Normalt åpen teller</name>
|
||||
<name lang="nl">Teller contact (NO)</name>
|
||||
<name lang="nl_BE">Normaal open teller</name>
|
||||
<name lang="pl">Zestyk zwierny licznika</name>
|
||||
<name lang="pt">Contador normalmente aberto</name>
|
||||
<name lang="pt_BR">Contato NA acionado por contador</name>
|
||||
<name lang="ro">Contor normal deschis</name>
|
||||
<name lang="ru">Счётчик с нормально открытым контактом</name>
|
||||
<name lang="sk">Počítadlo s kontaktom NO</name>
|
||||
<name lang="sl">Števec normalno odprt</name>
|
||||
<name lang="sr">Бројач нормално отворен</name>
|
||||
<name lang="sv">Räknare med slutande kontakt</name>
|
||||
<name lang="tr">Normalde açık sayaç</name>
|
||||
<name lang="uk">Лічильник нормально розімкнений</name>
|
||||
<name lang="zh">计数驱动常开触点</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<rect antialias="false" height="10" rx="0" ry="0" style="line-style:normal;line-weight:thin;filling:none;color:black" width="10" x="-20" y="-15"/>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:dashed;line-weight:thin;filling:none;color:black" x1="-10" x2="-3" y1="-10" y2="-10"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="UserText" text_width="-1" uuid="{a3321491-1dac-42e2-a9d1-1a43604116a9}" x="-13" y="-2" z="3">
|
||||
<text>14</text>
|
||||
</dynamic_text>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="UserText" text_width="-1" uuid="{a82a123f-f3cf-4dd7-93c0-44bbdd940429}" x="-13" y="-34" z="4">
|
||||
<text>11</text>
|
||||
</dynamic_text>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="ElementInfo" text_width="-1" uuid="{960d8065-c520-4d45-aa36-4bb600c0e6db}" x="1" y="-22" z="5">
|
||||
<text/>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_text>
|
||||
<polygon antialias="true" closed="false" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="-5" x2="0" x3="0" y1="-20" y2="0" y3="10"/>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="0" y1="-30" y2="-20"/>
|
||||
<ellipse antialias="true" height="4" style="line-style:normal;line-weight:thin;filling:none;color:black" width="4" x="-17" y="-12"/>
|
||||
<terminal name="" orientation="s" type="Generic" uuid="{572018e1-9843-49dd-964f-836ff27b8240}" x="0" y="10"/>
|
||||
<terminal name="" orientation="n" type="Generic" uuid="{77224246-2278-498a-88e5-1ecd84e938e8}" x="0" y="-30"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
#include <QTemporaryDir>
|
||||
#include <QSettings>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsSceneHoverEvent>
|
||||
#include <QStandardItemModel>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include "../../sources/qetapp.h"
|
||||
#include "../../sources/qetdiagrameditor.h"
|
||||
#include "../../sources/qetproject.h"
|
||||
#include "../../sources/qetresult.h"
|
||||
#include "../../sources/qetmessagebox.h"
|
||||
#include "../../sources/projectview.h"
|
||||
#include "../../sources/diagram.h"
|
||||
#include "../../sources/utils/qetutils.h"
|
||||
#include "../../sources/ui/diagrameditorhandlersizewidget.h"
|
||||
#include "../../sources/QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
||||
#include "../../sources/qetgraphicsitem/independenttextitem.h"
|
||||
#include "../../sources/qetgraphicsitem/dynamicelementtextitem.h"
|
||||
#include "../../sources/qetgraphicsitem/diagramimageitem.h"
|
||||
#include "../../sources/qetgraphicsitem/qetshapeitem.h"
|
||||
#include "../../sources/editor/ui/qetelementeditor.h"
|
||||
#include "../../sources/editor/elementscene.h"
|
||||
#include "../../sources/editor/graphicspart/partrectangle.h"
|
||||
#include "../../sources/editor/graphicspart/partdynamictextfield.h"
|
||||
#include "../../sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.h"
|
||||
#include "../../sources/qetgraphicsitem/conductor.h"
|
||||
static void check(bool value,const char *why){if(!value)throw std::runtime_error(why);}
|
||||
static QString canonicalProjectXml(QDomDocument document) {
|
||||
// Scene index updates can reorder equal-z conductors. Compare their
|
||||
// complete serialized content in UUID order, rather than scene order.
|
||||
const auto groups=document.elementsByTagName("conductors");
|
||||
for(int i=0;i<groups.size();++i) {
|
||||
auto group=groups.at(i).toElement();QList<QDomElement> wires;
|
||||
for(auto wire=group.firstChildElement("conductor");!wire.isNull();wire=wire.nextSiblingElement("conductor"))wires<<wire;
|
||||
std::sort(wires.begin(),wires.end(),[](const QDomElement &a,const QDomElement &b){return a.attribute("uuid")<b.attribute("uuid");});
|
||||
for(auto wire:wires)group.appendChild(wire);
|
||||
}
|
||||
return document.toString();
|
||||
}
|
||||
static void mouse(Diagram *scene,QGraphicsItem *item,QEvent::Type type,const QPointF &point,const QPointF *press=nullptr) {
|
||||
QGraphicsSceneMouseEvent event(type);event.setButton(Qt::LeftButton);
|
||||
event.setButtons(type==QEvent::GraphicsSceneMouseRelease?Qt::NoButton:Qt::LeftButton);
|
||||
event.setScenePos(point);event.setPos(item->mapFromScene(point));
|
||||
event.setButtonDownScenePos(Qt::LeftButton,press?*press:point);scene->sendEvent(item,&event);
|
||||
}
|
||||
static QetGraphicsHandlerItem *bottomRightHandle(Diagram *scene) {
|
||||
QetGraphicsHandlerItem *found=nullptr;
|
||||
for(auto *item:scene->items())if(item->type()==QetGraphicsHandlerItem::Type&&item->isVisible()) {
|
||||
auto *handle=qgraphicsitem_cast<QetGraphicsHandlerItem *>(item);
|
||||
if(!found||handle->scenePos().x()+handle->scenePos().y()>found->scenePos().x()+found->scenePos().y())found=handle;
|
||||
}
|
||||
check(found,"mouse gesture handle present");return found;
|
||||
}
|
||||
static void drag(Diagram *scene,QGraphicsItem *item,const QPointF &end) {
|
||||
const auto start=item->scenePos();
|
||||
mouse(scene,item,QEvent::GraphicsSceneMousePress,start);
|
||||
mouse(scene,item,QEvent::GraphicsSceneMouseMove,end,&start);
|
||||
mouse(scene,item,QEvent::GraphicsSceneMouseRelease,end,&start);
|
||||
}
|
||||
int main(int argc,char **argv) {
|
||||
const QString fixture=QString::fromLocal8Bit(argv[1]);
|
||||
const QString report=QString::fromLocal8Bit(argv[2]);
|
||||
QTemporaryDir isolated;
|
||||
QByteArray config="--config-dir="+isolated.path().toUtf8();
|
||||
QByteArray data="--data-dir="+isolated.path().toUtf8();
|
||||
QByteArray collection="--common-elements-dir="+isolated.path().toUtf8();
|
||||
char *args[]={argv[0],config.data(),data.data(),collection.data()};int count=4;
|
||||
QApplication app(count,args);
|
||||
std::freopen(report.toLocal8Bit().constData(),"w",stdout);
|
||||
std::setbuf(stdout,nullptr);
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
QSettings::setPath(QSettings::IniFormat,QSettings::UserScope,isolated.path());
|
||||
QETProject::setBackupEnabled(false);QET::QetMessageBox::setNonInteractive(true);
|
||||
try {
|
||||
auto *qet=new QETApp; Q_UNUSED(qet);
|
||||
auto editors=QETApp::diagramEditors();check(!editors.isEmpty(),"editor created");
|
||||
auto *editor=editors.first();
|
||||
auto *project=new QETProject(fixture);check(project->state()==QETProject::Ok,"fixture loaded");
|
||||
editor->addProjectView(new ProjectView(project));
|
||||
auto *scene=project->diagrams().first();
|
||||
auto widgets=editor->findChildren<DiagramEditorHandlerSizeWidget *>();check(!widgets.isEmpty(),"toolbar widget present");
|
||||
auto *combo=widgets.first()->findChild<QComboBox *>();check(combo&&combo->count()==6,"six choices");
|
||||
check(combo->currentIndex()==3&&editor->property("graphics_handler_size").toReal()==10,"default x1");
|
||||
auto *free=new IndependentTextItem;free->setPlainText("Mouse placement");scene->addItem(free);free->setSelected(true);
|
||||
QPixmap pixmap(30,20);pixmap.fill(Qt::red);auto *image=new DiagramImageItem(pixmap);scene->addItem(image);image->setSelected(true);
|
||||
auto *shape=new QetShapeItem({100,100},{150,140},QetShapeItem::Rectangle);scene->addItem(shape);shape->setSelected(true);
|
||||
auto *attached=scene->elements().first()->dynamicTextItems().first();attached->setSelected(true);
|
||||
auto *background=project->addNewDiagram();
|
||||
auto *backgroundHandle=new QetGraphicsHandlerItem;
|
||||
background->addItem(backgroundHandle);
|
||||
QApplication::processEvents();
|
||||
const QString xml=project->toXml().toString();
|
||||
const int undoCount=scene->undoStack().count();const int undoIndex=scene->undoStack().index();
|
||||
const bool dirty=project->projectWasModified();
|
||||
const qreal sizes[]={2.5,5,7.5,10,20,30};
|
||||
for(int i=0;i<6;++i) {
|
||||
combo->setCurrentIndex(i);QApplication::processEvents();
|
||||
check(editor->property("graphics_handler_size").toReal()==sizes[i],"fractional editor property");
|
||||
check(QETUtils::graphicsHandlerSize(image)==sizes[i],"new handle size lookup");
|
||||
check(backgroundHandle->property("currentSize").toReal()==sizes[i],"inactive folio handles updated");
|
||||
int handles=0;for(auto *item:scene->items())if(item->type()==QetGraphicsHandlerItem::Type){
|
||||
++handles;auto *handle=qgraphicsitem_cast<QetGraphicsHandlerItem *>(item);
|
||||
check(handle->property("currentSize").toReal()==sizes[i],"existing handles updated immediately");
|
||||
}
|
||||
check(handles>=8,"multiple selected object handles tested");
|
||||
image->setSelected(false);image->setSelected(true);
|
||||
for(auto *item:scene->items())if(item->type()==QetGraphicsHandlerItem::Type)
|
||||
check(qgraphicsitem_cast<QetGraphicsHandlerItem *>(item)->property("currentSize").toReal()==sizes[i],"recreated handles retain fractions");
|
||||
DiagramEditorHandlerSizeWidget recreated(editor);
|
||||
check(recreated.findChild<QComboBox *>()->currentIndex()==i,"toolbar recreation preserves chosen size");
|
||||
check(project->toXml().toString()==xml,"handle size leaves project XML unchanged");
|
||||
check(scene->undoStack().count()==undoCount&&scene->undoStack().index()==undoIndex,"handle size leaves undo stack unchanged");
|
||||
check(project->projectWasModified()==dirty,"handle size leaves dirty state unchanged");
|
||||
}
|
||||
for(int i=0;i<6;++i) {
|
||||
combo->setCurrentIndex(i);
|
||||
scene->clearSelection();
|
||||
auto *picture=new DiagramImageItem(pixmap);scene->addItem(picture);picture->setPos(400,400);picture->setSelected(true);
|
||||
auto *handle=bottomRightHandle(scene);
|
||||
check(handle->property("currentSize").toReal()==sizes[i],"image mouse handle size");
|
||||
drag(scene,handle,handle->scenePos()+QPointF(20,20));
|
||||
check(picture->scaleFactorX()!=1,"image resized by mouse events");
|
||||
const qreal changed=picture->scaleFactorX();scene->undoStack().undo();
|
||||
check(picture->scaleFactorX()==1,"image resize undo");scene->undoStack().redo();
|
||||
check(picture->scaleFactorX()==changed,"image resize redo");scene->undoStack().undo();
|
||||
const auto centre=picture->mapToScene(picture->imageRect().center());
|
||||
mouse(scene,picture,QEvent::GraphicsSceneMousePress,centre);
|
||||
mouse(scene,picture,QEvent::GraphicsSceneMouseRelease,centre);
|
||||
handle=bottomRightHandle(scene);
|
||||
const auto pivot=picture->mapToScene(picture->pivot());QTransform turn;turn.rotate(30);
|
||||
drag(scene,handle,pivot+turn.map(handle->scenePos()-pivot));
|
||||
check(qAbs(picture->rotationAngle())>1,"image rotated by mouse events");
|
||||
const qreal angle=picture->rotationAngle();scene->undoStack().undo();
|
||||
check(qAbs(picture->rotationAngle())<0.001,"image rotation undo");scene->undoStack().redo();
|
||||
check(qAbs(picture->rotationAngle()-angle)<0.001,"image rotation redo");scene->undoStack().undo();
|
||||
scene->undoStack().clear();delete picture;
|
||||
auto *rectangle=new QetShapeItem({0,0},{50,40},QetShapeItem::Rectangle);scene->addItem(rectangle);rectangle->setPos(400,400);rectangle->setSelected(true);
|
||||
const auto rect=rectangle->rect();handle=bottomRightHandle(scene);
|
||||
drag(scene,handle,handle->scenePos()+QPointF(20,20));check(rectangle->rect()!=rect,"shape resized by mouse events");
|
||||
const auto resized=rectangle->rect();scene->undoStack().undo();check(rectangle->rect()==rect,"shape resize undo");
|
||||
scene->undoStack().redo();check(rectangle->rect()==resized,"shape resize redo");scene->undoStack().undo();
|
||||
for(int click=0;click<2;++click) {
|
||||
const auto middle=rectangle->mapToScene(rect.center());
|
||||
mouse(scene,rectangle,QEvent::GraphicsSceneMousePress,middle);mouse(scene,rectangle,QEvent::GraphicsSceneMouseRelease,middle);
|
||||
}
|
||||
handle=bottomRightHandle(scene);const auto shapePivot=rectangle->mapToScene(rectangle->pivot());
|
||||
drag(scene,handle,shapePivot+turn.map(handle->scenePos()-shapePivot));
|
||||
check(qAbs(rectangle->rotation())>1,"shape rotated by mouse events");
|
||||
const qreal shapeAngle=rectangle->rotation();scene->undoStack().undo();check(qAbs(rectangle->rotation())<0.001,"shape rotation undo");
|
||||
scene->undoStack().redo();check(qAbs(rectangle->rotation()-shapeAngle)<0.001,"shape rotation redo");scene->undoStack().undo();
|
||||
scene->undoStack().clear();
|
||||
auto *companion=new DiagramImageItem(pixmap);scene->addItem(companion);companion->setPos(600,400);companion->setSelected(true);
|
||||
rectangle->setSelected(true);
|
||||
const auto rectanglePos=rectangle->pos();const auto companionPos=companion->pos();
|
||||
const auto start=companion->mapToScene(companion->imageRect().center());const auto end=start+QPointF(20,20);
|
||||
mouse(scene,companion,QEvent::GraphicsSceneMousePress,start);
|
||||
mouse(scene,companion,QEvent::GraphicsSceneMouseMove,end,&start);
|
||||
mouse(scene,companion,QEvent::GraphicsSceneMouseRelease,end,&start);
|
||||
check(companion->pos()==companionPos+QPointF(20,20)&&rectangle->pos()==rectanglePos+QPointF(20,20),"multiple objects moved by mouse events");
|
||||
scene->undoStack().undo();check(companion->pos()==companionPos&&rectangle->pos()==rectanglePos,"multiple object movement undo");
|
||||
scene->undoStack().redo();check(companion->pos()==companionPos+QPointF(20,20)&&rectangle->pos()==rectanglePos+QPointF(20,20),"multiple object movement redo");
|
||||
scene->undoStack().undo();scene->undoStack().clear();delete companion;delete rectangle;
|
||||
}
|
||||
std::puts("PASS: image and rectangle mouse resize/rotation, multiple object movement, and undo/redo at all six sizes");
|
||||
project->setFilePath(QString::fromLocal8Bit(argv[3])+"/handles.qet");
|
||||
check(project->write().isOk(),"save after handle size changes");
|
||||
QETProject reopened(project->filePath());check(reopened.state()==QETProject::Ok,"reopen after handle size changes");
|
||||
bool imageFound=false,textFound=false;
|
||||
for(auto *item:reopened.diagrams().first()->items()) {
|
||||
if(auto *loaded=qgraphicsitem_cast<DiagramImageItem *>(item))imageFound|=loaded->pixmap().size()==pixmap.size();
|
||||
if(auto *loaded=qgraphicsitem_cast<IndependentTextItem *>(item))textFound|=loaded->toPlainText()==free->toPlainText();
|
||||
}
|
||||
check(imageFound&&textFound,"object content survives reopen");
|
||||
auto *elementEditor=new QETElementEditor;elementEditor->show();
|
||||
auto *elementScene=elementEditor->elementScene();
|
||||
auto *part=new PartRectangle(elementEditor);part->setRect({0,0,50,40});elementScene->addItems({part});part->setSelected(true);
|
||||
QApplication::processEvents();
|
||||
auto checkElementHandles=[&] {
|
||||
int count=0;
|
||||
for(auto *item:elementScene->items())if(item->type()==QetGraphicsHandlerItem::Type&&item->isVisible()) {
|
||||
++count;check(qgraphicsitem_cast<QetGraphicsHandlerItem *>(item)->property("currentSize").toReal()==10,"element editor retains default handle size");
|
||||
}
|
||||
check(count>=4,"element editor shared handles present");
|
||||
};
|
||||
for(int i=0;i<6;++i){combo->setCurrentIndex(i);checkElementHandles();check(part->rect()==QRectF(0,0,50,40),"element primitive geometry unchanged");}
|
||||
elementScene->clearSelection();
|
||||
auto *field=new PartDynamicTextField(elementEditor);field->setPlainText("Element field");elementScene->addItems({field});field->setSelected(true);
|
||||
QApplication::processEvents();checkElementHandles();
|
||||
std::puts("PASS: element editor rectangle and dynamic field keep their default shared handles");
|
||||
QStandardItemModel model(2,2);model.setData(model.index(0,0),"Table");
|
||||
auto *table=new QetGraphicsTableItem;table->setModel(&model);scene->addItem(table);
|
||||
const auto tableSize=table->size();
|
||||
for(int i=0;i<6;++i) {
|
||||
combo->setCurrentIndex(i);
|
||||
QGraphicsSceneHoverEvent enter(QEvent::GraphicsSceneHoverEnter);scene->sendEvent(table,&enter);
|
||||
check(bottomRightHandle(scene)->property("currentSize").toReal()==sizes[i],"table hover handle retains fraction");
|
||||
check(table->size()==tableSize,"table dimensions unchanged");
|
||||
QGraphicsSceneHoverEvent leave(QEvent::GraphicsSceneHoverLeave);scene->sendEvent(table,&leave);
|
||||
}
|
||||
delete table;std::puts("PASS: table hover handles retain all six sizes without changing the table");
|
||||
const auto wireFixture=QFileInfo(fixture).dir().absoluteFilePath("../../../examples/tremie_vibrante.qet");
|
||||
auto *wireProject=new QETProject(wireFixture);check(wireProject->state()==QETProject::Ok,"conductor fixture opens");
|
||||
editor->addProjectView(new ProjectView(wireProject));
|
||||
Diagram *wireScene=nullptr;for(auto *diagram:wireProject->diagrams())if(!diagram->conductors().isEmpty()){wireScene=diagram;break;}
|
||||
check(wireScene,"conductor scene present");auto *wire=wireScene->conductors().first();
|
||||
wire->setSelected(true);QApplication::processEvents();
|
||||
const auto wireXml=canonicalProjectXml(wireProject->toXml());
|
||||
for(int i=0;i<6;++i) {
|
||||
combo->setCurrentIndex(i);wire->setSelected(false);wire->setSelected(true);
|
||||
int count=0;for(auto *item:wireScene->items())if(item->type()==QetGraphicsHandlerItem::Type) {
|
||||
++count;check(qgraphicsitem_cast<QetGraphicsHandlerItem *>(item)->property("currentSize").toReal()==sizes[i],"conductor handles retain fractions");
|
||||
}
|
||||
check(count>0,"conductor handles present");
|
||||
const auto after=canonicalProjectXml(wireProject->toXml());
|
||||
check(after==wireXml,"conductor project content unchanged");
|
||||
}
|
||||
std::puts("PASS: conductor handles retain all six sizes without changing their project");
|
||||
std::puts("PASS: six toolbar choices, default, fractions, existing/recreated handles, multi-selection, toolbar recreation, unchanged XML/undo/dirty state");
|
||||
}catch(const std::exception &error){std::printf("FAIL: %s\n",error.what());std::fflush(stdout);std::_Exit(1);}
|
||||
std::fflush(stdout);std::_Exit(0);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Link the handle-size integration probe using an existing Ninja app build.
|
||||
|
||||
Reuses app objects without duplicating the app or introducing a core-library
|
||||
refactor. No application source or existing executable is replaced. The
|
||||
generated manifest, probe binary, reports and export fixtures stay in build/.
|
||||
"""
|
||||
import argparse
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--build', required=True)
|
||||
parser.add_argument('--binary', required=True)
|
||||
parser.add_argument('--source', required=True)
|
||||
parser.add_argument('--fixture', required=True)
|
||||
args = parser.parse_args()
|
||||
build = pathlib.Path(args.build).resolve()
|
||||
binary = pathlib.Path(args.binary).resolve()
|
||||
source = pathlib.Path(args.source).resolve()
|
||||
fixture = pathlib.Path(args.fixture).resolve()
|
||||
manifest = (build / 'build.ninja').read_text(encoding='utf-8')
|
||||
match = re.search(r'^build (\S*?/sources/main\.cpp\.(?:obj|o)):', manifest, re.MULTILINE)
|
||||
if not match:
|
||||
sys.exit('Cannot find the application main object in the Ninja build.')
|
||||
main_object = match.group(1)
|
||||
probe_object = main_object.replace('main.cpp.', 'handle_size_probe.cpp.')
|
||||
probe_binary = binary.with_name('handle_size_probe' + binary.suffix)
|
||||
def escape(path):
|
||||
return pathlib.Path(path).as_posix().replace('$', '$$').replace(':', '$:').replace(' ', '$ ')
|
||||
main_source = source.parents[2] / 'sources' / 'main.cpp'
|
||||
manifest = manifest.replace(main_object, probe_object)
|
||||
manifest = manifest.replace(escape(main_source), escape(source))
|
||||
if binary.suffix:
|
||||
manifest = manifest.replace(binary.name, probe_binary.name)
|
||||
else:
|
||||
old_target = escape(binary.name if binary.parent == build else binary)
|
||||
new_target = escape(probe_binary.name if probe_binary.parent == build else probe_binary)
|
||||
manifest = manifest.replace('build ' + old_target + ':', 'build ' + new_target + ':')
|
||||
old_file = binary.name if binary.parent == build else binary.as_posix()
|
||||
new_file = probe_binary.name if probe_binary.parent == build else probe_binary.as_posix()
|
||||
manifest = re.sub(r'^ TARGET_FILE = ' + re.escape(old_file) + r'$',
|
||||
' TARGET_FILE = ' + new_file, manifest, flags=re.MULTILINE)
|
||||
probe_manifest = build / 'handle-size-probe.ninja'
|
||||
probe_manifest.write_text(manifest, encoding='utf-8')
|
||||
ninja = shutil.which('ninja')
|
||||
if not ninja:
|
||||
sys.exit('Ninja is required for the integration probe.')
|
||||
target = probe_binary.name if probe_binary.parent == build else str(probe_binary)
|
||||
subprocess.run([ninja, '-C', str(build), '-f', probe_manifest.name, target], check=True)
|
||||
output = build / 'handle-size'
|
||||
output.mkdir(exist_ok=True)
|
||||
report = output / 'results.txt'
|
||||
result = subprocess.run([str(probe_binary), str(fixture), str(report), str(output)], timeout=90)
|
||||
if report.exists():
|
||||
print(report.read_text(encoding='utf-8', errors='replace'))
|
||||
sys.exit(result.returncode)
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "borderproperties.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <QDomDocument>
|
||||
|
||||
/**
|
||||
BorderProperties::fromXml() -- the <border> element a project keeps for
|
||||
its new folios. Its column width and row height are decimals, written
|
||||
by toXml() as "%1"; they used to be read with toInt(), which is 0 for
|
||||
"60.5", and 0 is then clamped to the 5 px minimum by the folio.
|
||||
*/
|
||||
class tst_borderpropertiesxml : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
static QDomElement border(const QString &attributes)
|
||||
{
|
||||
static QDomDocument doc;
|
||||
doc.setContent(QStringLiteral("<border %1/>").arg(attributes));
|
||||
return doc.documentElement();
|
||||
}
|
||||
|
||||
static BorderProperties base()
|
||||
{
|
||||
BorderProperties p;
|
||||
p.columns_count = 17;
|
||||
p.columns_width = 60;
|
||||
p.rows_count = 8;
|
||||
p.rows_height = 80;
|
||||
p.display_columns = true;
|
||||
p.display_rows = true;
|
||||
return p;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void sizesAreReadAsDecimals_data()
|
||||
{
|
||||
QTest::addColumn<QString>("colsize");
|
||||
QTest::addColumn<QString>("rowsize");
|
||||
QTest::addColumn<qreal>("width");
|
||||
QTest::addColumn<qreal>("height");
|
||||
QTest::newRow("whole numbers") << "50" << "100" << 50.0 << 100.0;
|
||||
QTest::newRow("decimals") << "60.5" << "80.25" << 60.5 << 80.25;
|
||||
QTest::newRow("rounds down") << "61.3" << "61.3" << 61.3 << 61.3;
|
||||
QTest::newRow("rounds up") << "61.7" << "61.7" << 61.7 << 61.7;
|
||||
}
|
||||
|
||||
void sizesAreReadAsDecimals()
|
||||
{
|
||||
QFETCH(QString, colsize);
|
||||
QFETCH(QString, rowsize);
|
||||
QFETCH(qreal, width);
|
||||
QFETCH(qreal, height);
|
||||
|
||||
BorderProperties p = base();
|
||||
QDomElement e = border(QStringLiteral("cols=\"17\" colsize=\"%1\" rows=\"8\" rowsize=\"%2\"")
|
||||
.arg(colsize, rowsize));
|
||||
p.fromXml(e);
|
||||
QCOMPARE(p.columns_width, width);
|
||||
QCOMPARE(p.rows_height, height);
|
||||
QCOMPARE(p.columns_count, 17);
|
||||
QCOMPARE(p.rows_count, 8);
|
||||
}
|
||||
|
||||
void savedSizesComeBack()
|
||||
{
|
||||
BorderProperties saved = base();
|
||||
saved.columns_width = 60.5;
|
||||
saved.rows_height = 80.25;
|
||||
QDomDocument doc;
|
||||
QDomElement e = doc.createElement(QStringLiteral("border"));
|
||||
saved.toXml(e);
|
||||
|
||||
BorderProperties loaded = base();
|
||||
loaded.fromXml(e);
|
||||
QCOMPARE(loaded.columns_width, 60.5);
|
||||
QCOMPARE(loaded.rows_height, 80.25);
|
||||
}
|
||||
|
||||
// An unreadable size leaves the previous value, as a missing one does.
|
||||
void unreadableSizeIsLeftAlone_data()
|
||||
{
|
||||
QTest::addColumn<QString>("attributes");
|
||||
QTest::newRow("missing") << "cols=\"17\" rows=\"8\"";
|
||||
QTest::newRow("text") << "colsize=\"wide\" rowsize=\"tall\"";
|
||||
QTest::newRow("nan") << "colsize=\"nan\" rowsize=\"nan\"";
|
||||
QTest::newRow("inf") << "colsize=\"inf\" rowsize=\"-inf\"";
|
||||
}
|
||||
|
||||
void unreadableSizeIsLeftAlone()
|
||||
{
|
||||
QFETCH(QString, attributes);
|
||||
BorderProperties p = base();
|
||||
QDomElement e = border(attributes);
|
||||
p.fromXml(e);
|
||||
QCOMPARE(p.columns_width, 60.0);
|
||||
QCOMPARE(p.rows_height, 80.0);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_borderpropertiesxml)
|
||||
#include "tst_borderpropertiesxml.moc"
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "conductorproperties.h"
|
||||
#include "qetapp.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <QSettings>
|
||||
|
||||
// qet.cpp, compiled in for ConductorProperties, refers to this static.
|
||||
QString QETApp::m_interface_language;
|
||||
|
||||
/**
|
||||
ConductorProperties::toSettings()/fromSettings() keep the conductor
|
||||
width (cond_size) as it was saved. The width is a decimal, written with
|
||||
QString::number(); reading it with toInt() gave 0 for every value that
|
||||
was not a whole number, so a default width of 1.4 came back as 0 after
|
||||
a restart.
|
||||
|
||||
This test owns its own QSettings scope (organization + application
|
||||
name), so it cannot read or write the configuration of whoever runs it.
|
||||
*/
|
||||
class tst_conductorsizesetting : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QCoreApplication::setOrganizationName(
|
||||
QStringLiteral("QElectroTech-tst_conductorsizesetting"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("tst_conductorsizesetting"));
|
||||
QSettings settings;
|
||||
settings.clear();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
QSettings settings;
|
||||
settings.clear();
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
void widthSurvivesARoundTrip_data()
|
||||
{
|
||||
QTest::addColumn<qreal>("width");
|
||||
QTest::newRow("whole number") << 2.0;
|
||||
QTest::newRow("decimal") << 1.4;
|
||||
QTest::newRow("minimum") << 0.4;
|
||||
QTest::newRow("rounds down") << 61.3;
|
||||
QTest::newRow("rounds up") << 61.7;
|
||||
}
|
||||
|
||||
void widthSurvivesARoundTrip()
|
||||
{
|
||||
QFETCH(qreal, width);
|
||||
QSettings settings;
|
||||
ConductorProperties saved;
|
||||
saved.cond_size = width;
|
||||
saved.toSettings(settings, QStringLiteral("test/"));
|
||||
|
||||
ConductorProperties loaded;
|
||||
loaded.fromSettings(settings, QStringLiteral("test/"));
|
||||
QCOMPARE(loaded.cond_size, width);
|
||||
}
|
||||
|
||||
// A hand-edited or truncated settings file must not give a width of 0.
|
||||
void unusableValueFallsBackToOne_data()
|
||||
{
|
||||
QTest::addColumn<QString>("value");
|
||||
QTest::newRow("text") << QStringLiteral("wide");
|
||||
QTest::newRow("zero") << QStringLiteral("0");
|
||||
QTest::newRow("negative") << QStringLiteral("-1");
|
||||
QTest::newRow("nan") << QStringLiteral("nan");
|
||||
QTest::newRow("inf") << QStringLiteral("inf");
|
||||
}
|
||||
|
||||
void unusableValueFallsBackToOne()
|
||||
{
|
||||
QFETCH(QString, value);
|
||||
QSettings settings;
|
||||
settings.setValue(QStringLiteral("test/size"), value);
|
||||
|
||||
ConductorProperties loaded;
|
||||
loaded.fromSettings(settings, QStringLiteral("test/"));
|
||||
QCOMPARE(loaded.cond_size, 1.0);
|
||||
}
|
||||
|
||||
void missingValueIsOne()
|
||||
{
|
||||
QSettings settings;
|
||||
ConductorProperties loaded;
|
||||
loaded.fromSettings(settings, QStringLiteral("test/"));
|
||||
QCOMPARE(loaded.cond_size, 1.0);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_conductorsizesetting)
|
||||
#include "tst_conductorsizesetting.moc"
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QDomDocument>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
A folio's "freeze new elements" and "freeze new conductors" flags are
|
||||
saved as freezeNewElement="true" and freezeNewConductor="true" on its
|
||||
<diagram>. Loading must read the words back: the flags used to be read
|
||||
with toInt(), which is 0 for "true" as well as for "false", so both were
|
||||
off again after every reload. Runs the real binary's --resave on a
|
||||
project with one folio per combination and reads the saved attributes.
|
||||
*/
|
||||
class tst_foliofreezeflags : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
static bool runQet(const QStringList &arguments)
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), arguments);
|
||||
return proc.waitForFinished(60000)
|
||||
&& proc.exitStatus() == QProcess::NormalExit
|
||||
&& proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
/// The <diagram> elements of a saved project, in file order.
|
||||
static QList<QDomElement> diagrams(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
QDomDocument doc;
|
||||
if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) return {};
|
||||
QList<QDomElement> list;
|
||||
for (QDomElement e = doc.documentElement().firstChildElement(QStringLiteral("diagram"));
|
||||
!e.isNull();
|
||||
e = e.nextSiblingElement(QStringLiteral("diagram")))
|
||||
list << e;
|
||||
return list;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found");
|
||||
QVERIFY(m_dir.isValid());
|
||||
}
|
||||
|
||||
// Folio 1 has both flags, folio 2 only the element one, folio 3 none,
|
||||
// and folio 4 is an older file without the attributes at all.
|
||||
void flagsSurviveASave_data()
|
||||
{
|
||||
QTest::addColumn<int>("folio");
|
||||
QTest::addColumn<QString>("element");
|
||||
QTest::addColumn<QString>("conductor");
|
||||
QTest::newRow("both frozen") << 0 << "true" << "true";
|
||||
QTest::newRow("elements frozen") << 1 << "true" << "false";
|
||||
QTest::newRow("not frozen") << 2 << "false" << "false";
|
||||
QTest::newRow("no attributes") << 3 << "false" << "false";
|
||||
}
|
||||
|
||||
void flagsSurviveASave()
|
||||
{
|
||||
QFETCH(int, folio);
|
||||
QFETCH(QString, element);
|
||||
QFETCH(QString, conductor);
|
||||
|
||||
const QString fixture = QFINDTESTDATA("fixtures/folio_freeze_flags.qet");
|
||||
QVERIFY2(!fixture.isEmpty(), "fixture project not found");
|
||||
const QString out = m_dir.filePath(QStringLiteral("resaved%1.qet").arg(folio));
|
||||
QVERIFY2(runQet({QStringLiteral("--resave"), fixture, out}), "--resave failed");
|
||||
|
||||
const QList<QDomElement> list = diagrams(out);
|
||||
QCOMPARE(list.size(), 4);
|
||||
const QDomElement e = list.at(folio);
|
||||
QCOMPARE(e.attribute(QStringLiteral("freezeNewElement")), element);
|
||||
QCOMPARE(e.attribute(QStringLiteral("freezeNewConductor")), conductor);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_foliofreezeflags)
|
||||
#include "tst_foliofreezeflags.moc"
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "foliogrid.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <QSettings>
|
||||
|
||||
/**
|
||||
FolioGrid::step() -- the folio grid step as read from the settings.
|
||||
Every reader of diagrameditor/Xgrid and Ygrid goes through it, so a
|
||||
settings file holding 0 (a division by zero in Diagram::snapToGrid()
|
||||
and an endless "% 0" in drawBackground()), a negative number (an
|
||||
endless loop) or text can no longer reach the code that uses the step.
|
||||
*/
|
||||
class tst_foliogrid : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void usableValuesAreKept_data()
|
||||
{
|
||||
QTest::addColumn<QVariant>("value");
|
||||
QTest::addColumn<int>("expected");
|
||||
QTest::newRow("one") << QVariant(1) << 1;
|
||||
QTest::newRow("default") << QVariant(10) << 10;
|
||||
QTest::newRow("text number") << QVariant(QStringLiteral("7")) << 7;
|
||||
QTest::newRow("large") << QVariant(100000) << 100000;
|
||||
QTest::newRow("int max") << QVariant(2147483647) << 2147483647;
|
||||
}
|
||||
|
||||
void usableValuesAreKept()
|
||||
{
|
||||
QFETCH(QVariant, value);
|
||||
QFETCH(int, expected);
|
||||
QCOMPARE(FolioGrid::step(value, 10), expected);
|
||||
}
|
||||
|
||||
void unusableValuesFallBack_data()
|
||||
{
|
||||
QTest::addColumn<QVariant>("value");
|
||||
QTest::newRow("missing") << QVariant();
|
||||
QTest::newRow("zero") << QVariant(0);
|
||||
QTest::newRow("negative") << QVariant(-5);
|
||||
QTest::newRow("text") << QVariant(QStringLiteral("ten"));
|
||||
QTest::newRow("empty") << QVariant(QString());
|
||||
QTest::newRow("nan") << QVariant(QStringLiteral("nan"));
|
||||
QTest::newRow("too large") << QVariant(QStringLiteral("99999999999"));
|
||||
QTest::newRow("too large number") << QVariant(qlonglong(99999999999));
|
||||
QTest::newRow("int max + 1") << QVariant(qlonglong(2147483648));
|
||||
}
|
||||
|
||||
void unusableValuesFallBack()
|
||||
{
|
||||
QFETCH(QVariant, value);
|
||||
QCOMPARE(FolioGrid::step(value, 10), 10);
|
||||
QCOMPARE(FolioGrid::step(value, 20), 20);
|
||||
}
|
||||
|
||||
// A decimal in the file is rounded to a whole number, as the plain
|
||||
// QVariant::toInt() read did before.
|
||||
void decimalIsRoundedLikeBefore()
|
||||
{
|
||||
QCOMPARE(FolioGrid::step(QVariant(7.9), 10), 8);
|
||||
QCOMPARE(FolioGrid::step(QVariant(0.4), 10), 10);
|
||||
}
|
||||
|
||||
// Through a real settings file, in a scope of this test's own.
|
||||
void readsTheSettingsKeys()
|
||||
{
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("QElectroTech-tst_foliogrid"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("tst_foliogrid"));
|
||||
QSettings settings;
|
||||
settings.clear();
|
||||
QCOMPARE(FolioGrid::step(settings.value(FolioGrid::x_key), 10), 10);
|
||||
settings.setValue(FolioGrid::x_key, 0);
|
||||
settings.setValue(FolioGrid::y_key, 15);
|
||||
QCOMPARE(FolioGrid::step(settings.value(FolioGrid::x_key), 10), 10);
|
||||
QCOMPARE(FolioGrid::step(settings.value(FolioGrid::y_key), 10), 15);
|
||||
settings.clear();
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_foliogrid)
|
||||
#include "tst_foliogrid.moc"
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QGraphicsSceneHoverEvent>
|
||||
#include "QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
||||
|
||||
class tst_handlersize : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void sizeChangeCancelsOldHoverAnimation() {
|
||||
QGraphicsScene scene;
|
||||
auto *handle=new QetGraphicsHandlerItem(30);scene.addItem(handle);
|
||||
QGraphicsSceneHoverEvent event(QEvent::GraphicsSceneHoverEnter);
|
||||
scene.sendEvent(handle,&event);
|
||||
handle->setSize(2.5);
|
||||
QTest::qWait(250);
|
||||
QCOMPARE(handle->property("currentSize").toReal(),qreal(2.5));
|
||||
}
|
||||
void fractionalGeometryAndPicking() {
|
||||
for (qreal size : {2.5, 5.0, 7.5, 10.0, 20.0, 30.0}) {
|
||||
QGraphicsScene scene;
|
||||
auto *object = scene.addRect(QRectF(0,0,100,60));
|
||||
object->setPos(80,40); object->setRotation(37); object->setSelected(true);
|
||||
const QRectF geometry=object->rect(); const auto transform=object->sceneTransform();
|
||||
auto handles=QetGraphicsHandlerItem::handlerForPoint({QPointF(120,80)},size);
|
||||
auto *handle=handles.first(); scene.addItem(handle); handle->setColor(Qt::darkGreen);
|
||||
QCOMPARE(handle->property("currentSize").toReal(),size);
|
||||
QCOMPARE(handle->boundingRect().width(),size+2);
|
||||
const auto position=handle->pos();
|
||||
for (qreal next : {2.5,5.0,7.5,10.0,20.0,30.0}) {
|
||||
handle->setSize(next);
|
||||
QCOMPARE(handle->property("currentSize").toReal(),next);
|
||||
QCOMPARE(handle->pos(),position); QVERIFY(handle->isVisible());
|
||||
QCOMPARE(object->rect(),geometry); QCOMPARE(object->sceneTransform(),transform);
|
||||
for (qreal zoom : {0.25,1.0,4.0}) {
|
||||
QGraphicsView view(&scene); view.scale(zoom,zoom);
|
||||
const auto device=handle->deviceTransform(view.viewportTransform());
|
||||
QCOMPARE(device.mapRect(handle->boundingRect()).width(),next+2);
|
||||
const QPointF centre=handle->scenePos();
|
||||
QVERIFY(scene.items(centre,Qt::IntersectsItemShape,Qt::DescendingOrder,view.viewportTransform()).contains(handle));
|
||||
const QPointF outside=centre+QPointF((next/2+2)/zoom,0);
|
||||
QVERIFY(!scene.items(outside,Qt::IntersectsItemShape,Qt::DescendingOrder,view.viewportTransform()).contains(handle));
|
||||
}
|
||||
}
|
||||
handle->hide(); QVERIFY(!scene.items(position).contains(handle));
|
||||
}
|
||||
}
|
||||
};
|
||||
QTEST_MAIN(tst_handlersize)
|
||||
#include "tst_handlersize.moc"
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QBuffer>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
Before #1310, a project saved after a crop was undone kept the undone
|
||||
<crop>, with the uncropped picture shown. Loading such a file has to
|
||||
drop that crop: the picture shown is always exactly the crop's size,
|
||||
so a picture of another size means the crop is stale. Otherwise the
|
||||
next edit of the picture applies the crop again.
|
||||
|
||||
Runs the real binary: a script adds and crops a picture and saves;
|
||||
the test then puts another picture where the shown one is, and lets
|
||||
the binary load and save that file again.
|
||||
*/
|
||||
class tst_imagestalecrop : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
// Runs the binary on @p project with a script that saves it to @p out.
|
||||
bool run(const QString &script, const QString &project)
|
||||
{
|
||||
const QString path = m_dir.filePath(QStringLiteral("probe.js"));
|
||||
const QString home = m_dir.filePath(QStringLiteral("home"));
|
||||
QDir().mkpath(home);
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) return false;
|
||||
f.write(script.toUtf8());
|
||||
f.close();
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
env.insert(QStringLiteral("TMPDIR"), m_dir.path());
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), path, project});
|
||||
return proc.waitForFinished(120000) && proc.exitStatus() == QProcess::NormalExit;
|
||||
}
|
||||
|
||||
// Opens @p in and saves it to @p out.
|
||||
bool resave(const QString &in, const QString &out)
|
||||
{
|
||||
return run(QStringLiteral("qet.save('%1');\n").arg(out), in) && QFile::exists(out);
|
||||
}
|
||||
|
||||
static QByteArray read(const QString &path)
|
||||
{
|
||||
QFile f(path);
|
||||
return f.open(QIODevice::ReadOnly) ? f.readAll() : QByteArray();
|
||||
}
|
||||
|
||||
static QByteArray png(const QSize &size)
|
||||
{
|
||||
QImage img(size, QImage::Format_RGB32);
|
||||
img.fill(Qt::darkGreen);
|
||||
QByteArray bytes;
|
||||
QBuffer buffer(&bytes);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
img.save(&buffer, "PNG");
|
||||
return bytes.toBase64();
|
||||
}
|
||||
|
||||
// The size of the saved original (<image_base>), or an empty size.
|
||||
static QSize baseSize(const QByteArray &xml)
|
||||
{
|
||||
const QRegularExpression base(QStringLiteral("<image_base>\\s*([A-Za-z0-9+/=]+)"));
|
||||
const QRegularExpressionMatch m = base.match(QString::fromLatin1(xml));
|
||||
if (!m.hasMatch()) return {};
|
||||
return QImage::fromData(QByteArray::fromBase64(m.captured(1).toLatin1())).size();
|
||||
}
|
||||
|
||||
// @p xml with the shown picture (the <image> element's first text)
|
||||
// replaced by a PNG of @p size.
|
||||
static QByteArray withShown(QByteArray xml, const QSize &size)
|
||||
{
|
||||
const QRegularExpression shown(QStringLiteral("(<image\\b[^>]*>\\s*)([A-Za-z0-9+/=]+)"));
|
||||
const QRegularExpressionMatch m = shown.match(QString::fromLatin1(xml));
|
||||
if (!m.hasMatch()) return {};
|
||||
return xml.replace(m.capturedStart(2), m.capturedLength(2), png(size));
|
||||
}
|
||||
|
||||
QString m_cropped; // a picture of 40 x 30, cropped to 10,5,20,10
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
QImage img(40, 30, QImage::Format_RGB32);
|
||||
img.fill(Qt::darkGreen);
|
||||
const QString pic = m_dir.filePath(QStringLiteral("pic.png"));
|
||||
QVERIFY(img.save(pic));
|
||||
|
||||
m_cropped = m_dir.filePath(QStringLiteral("cropped.qet"));
|
||||
QVERIFY(run(QStringLiteral(R"JS(
|
||||
var i = qet.addImage(0, '%1', 100, 100);
|
||||
qet.cropImage(0, i, 10, 5, 20, 10);
|
||||
qet.save('%2');
|
||||
)JS").arg(pic, m_cropped), QStringLiteral(QET_EXAMPLES_DIR "/741.qet")));
|
||||
const QByteArray xml = read(m_cropped);
|
||||
QCOMPARE(xml.count("<image "), 1);
|
||||
QVERIFY(xml.contains("<crop "));
|
||||
QVERIFY(xml.contains("<image_base>"));
|
||||
}
|
||||
|
||||
// A valid crop is kept, byte for byte.
|
||||
void validCropIsKept()
|
||||
{
|
||||
const QString out = m_dir.filePath(QStringLiteral("valid-out.qet"));
|
||||
QVERIFY(resave(m_cropped, out));
|
||||
const QByteArray xml = read(out);
|
||||
// Attributes in any order: Qt before 6.5 writes them unordered.
|
||||
const QRegularExpression crop(QStringLiteral("<crop (?=[^>]*\\bx=\"10\")(?=[^>]*\\by=\"5\")"
|
||||
"(?=[^>]*\\bw=\"20\")(?=[^>]*\\bh=\"10\")"));
|
||||
QVERIFY2(crop.match(QString::fromLatin1(xml)).hasMatch(), "the valid crop was lost");
|
||||
QVERIFY(xml.contains("<image_base>"));
|
||||
}
|
||||
|
||||
// The shown picture is the whole original (40 x 30): the crop was
|
||||
// undone before the file was saved. It is dropped, and with nothing
|
||||
// else to remember the original is not saved twice any more.
|
||||
void staleCropOfWholeOriginalIsDropped()
|
||||
{
|
||||
const QString stale = m_dir.filePath(QStringLiteral("stale.qet"));
|
||||
const QByteArray xml = withShown(read(m_cropped), QSize(40, 30));
|
||||
QVERIFY(!xml.isEmpty());
|
||||
QFile f(stale);
|
||||
QVERIFY(f.open(QIODevice::WriteOnly));
|
||||
f.write(xml);
|
||||
f.close();
|
||||
|
||||
const QString out = m_dir.filePath(QStringLiteral("stale-out.qet"));
|
||||
QVERIFY(resave(stale, out));
|
||||
const QByteArray saved = read(out);
|
||||
QCOMPARE(saved.count("<image "), 1);
|
||||
QVERIFY2(!saved.contains("<crop "), "the stale crop was kept");
|
||||
QVERIFY(!saved.contains("<image_base>"));
|
||||
}
|
||||
|
||||
// The shown picture matches neither the crop nor the original: it is
|
||||
// what the user saw, so it becomes the original -- a crop made after
|
||||
// loading cuts it, not the old original.
|
||||
void shownPictureOfOtherSizeBecomesTheOriginal()
|
||||
{
|
||||
const QString odd = m_dir.filePath(QStringLiteral("odd.qet"));
|
||||
const QByteArray xml = withShown(read(m_cropped), QSize(25, 25));
|
||||
QVERIFY(!xml.isEmpty());
|
||||
QFile f(odd);
|
||||
QVERIFY(f.open(QIODevice::WriteOnly));
|
||||
f.write(xml);
|
||||
f.close();
|
||||
|
||||
const QString out = m_dir.filePath(QStringLiteral("odd-out.qet"));
|
||||
QVERIFY(resave(odd, out));
|
||||
const QByteArray saved = read(out);
|
||||
QVERIFY2(!saved.contains("<crop "), "the stale crop was kept");
|
||||
QVERIFY(!saved.contains("<image_base>"));
|
||||
|
||||
const QString recropped = m_dir.filePath(QStringLiteral("odd-recropped.qet"));
|
||||
QVERIFY(run(QStringLiteral("qet.cropImage(0, 0, 0, 0, 10, 10);\nqet.save('%1');\n")
|
||||
.arg(recropped), odd));
|
||||
QCOMPARE(baseSize(read(recropped)), QSize(25, 25));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_imagestalecrop)
|
||||
#include "tst_imagestalecrop.moc"
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QDomDocument>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
Projects saved before the <sequentialNumbers> element keep an element's
|
||||
or conductor's sequential numbers as attributes: sequ_1, sequf_1, seqt_1,
|
||||
seqtf_1, seqh_1 and seqhf_1. The loaders look for one of them to take
|
||||
the old route; the list named sequf_1 twice and seqhf_1 never, so a
|
||||
file whose only sequence was the hundred-folio one lost it. Runs the
|
||||
real binary's --resave on fixtures/legacy_sequential_attributes.qet
|
||||
and reads the <sequentialNumbers> it writes.
|
||||
*/
|
||||
class tst_legacysequentialattributes : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
QDomDocument m_resaved;
|
||||
|
||||
static bool runQet(const QStringList &arguments)
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), arguments);
|
||||
return proc.waitForFinished(60000)
|
||||
&& proc.exitStatus() == QProcess::NormalExit
|
||||
&& proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
/// The first @p tag element of the saved project with this uuid.
|
||||
QDomElement item(const QString &tag, const QString &uuid) const
|
||||
{
|
||||
const QDomNodeList list = m_resaved.elementsByTagName(tag);
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
const QDomElement e = list.at(i).toElement();
|
||||
if (e.attribute(QStringLiteral("uuid")) == uuid) return e;
|
||||
}
|
||||
return QDomElement();
|
||||
}
|
||||
|
||||
/// The text of <sequentialNumbers><@p part> under @p e.
|
||||
static QString sequence(const QDomElement &e, const QString &part)
|
||||
{
|
||||
return e.firstChildElement(QStringLiteral("sequentialNumbers"))
|
||||
.firstChildElement(part).text();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found");
|
||||
QVERIFY(m_dir.isValid());
|
||||
const QString fixture = QFINDTESTDATA("fixtures/legacy_sequential_attributes.qet");
|
||||
QVERIFY2(!fixture.isEmpty(), "fixture project not found");
|
||||
const QString out = m_dir.filePath(QStringLiteral("resaved.qet"));
|
||||
QVERIFY2(runQet({QStringLiteral("--resave"), fixture, out}), "--resave failed");
|
||||
QFile file(out);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
QVERIFY(m_resaved.setContent(&file));
|
||||
}
|
||||
|
||||
// The element and the conductor whose only old attribute is seqhf_1.
|
||||
void hundredFolioSequenceIsKept_data()
|
||||
{
|
||||
QTest::addColumn<QString>("tag");
|
||||
QTest::addColumn<QString>("uuid");
|
||||
QTest::addColumn<QString>("value");
|
||||
QTest::newRow("element") << "element" << "{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" << "5";
|
||||
QTest::newRow("conductor") << "conductor" << "{f5162c59-0c59-4e94-b6a6-4cf259a52d91}" << "7";
|
||||
}
|
||||
|
||||
void hundredFolioSequenceIsKept()
|
||||
{
|
||||
QFETCH(QString, tag);
|
||||
QFETCH(QString, uuid);
|
||||
QFETCH(QString, value);
|
||||
const QDomElement e = item(tag, uuid);
|
||||
QVERIFY2(!e.isNull(), "item not found in the resaved project");
|
||||
QVERIFY2(!e.hasAttribute(QStringLiteral("seqhf_1")), "the old attribute is not written back");
|
||||
QCOMPARE(sequence(e, QStringLiteral("hundredFolio")), value);
|
||||
}
|
||||
|
||||
// The unit sequence, whose attribute was already in the list, as before.
|
||||
void unitSequenceIsKept_data()
|
||||
{
|
||||
QTest::addColumn<QString>("tag");
|
||||
QTest::addColumn<QString>("uuid");
|
||||
QTest::addColumn<QString>("value");
|
||||
QTest::newRow("element") << "element" << "{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" << "3";
|
||||
QTest::newRow("conductor") << "conductor" << "{89cacce1-b12a-4ec3-aadf-d3f8feb7494d}" << "4";
|
||||
}
|
||||
|
||||
void unitSequenceIsKept()
|
||||
{
|
||||
QFETCH(QString, tag);
|
||||
QFETCH(QString, uuid);
|
||||
QFETCH(QString, value);
|
||||
const QDomElement e = item(tag, uuid);
|
||||
QVERIFY2(!e.isNull(), "item not found in the resaved project");
|
||||
QCOMPARE(sequence(e, QStringLiteral("unit")), value);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_legacysequentialattributes)
|
||||
#include "tst_legacysequentialattributes.moc"
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "positionorder.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
PositionOrder -- the position comparisons behind renumbering elements
|
||||
(comparPos) and numbering terminals. std::sort needs a strict weak
|
||||
ordering; the old comparisons used "<=" and a 1 px tolerance, which
|
||||
are not one, so two elements at the same position (or three terminals
|
||||
a fraction apart) were undefined behaviour.
|
||||
*/
|
||||
class tst_positionorder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
using Less = std::function<bool(const QPointF &, const QPointF &)>;
|
||||
|
||||
static QList<QPair<QString, Less>> orders()
|
||||
{
|
||||
return {{QStringLiteral("xThenY"), PositionOrder::xThenY},
|
||||
{QStringLiteral("yThenX"), PositionOrder::yThenX},
|
||||
{QStringLiteral("roundedXThenY"), PositionOrder::roundedXThenY},
|
||||
{QStringLiteral("roundedYThenX"), PositionOrder::roundedYThenX}};
|
||||
}
|
||||
|
||||
private slots:
|
||||
// Nothing is before itself, and equal points are before each other
|
||||
// in neither direction: what "<=" broke.
|
||||
void equalPointsAreNotOrdered()
|
||||
{
|
||||
const QPointF p(100, 200), q(100, 200);
|
||||
for (const auto &order : orders()) {
|
||||
QVERIFY2(!order.second(p, p), qPrintable(order.first));
|
||||
QVERIFY2(!order.second(p, q), qPrintable(order.first));
|
||||
QVERIFY2(!order.second(q, p), qPrintable(order.first));
|
||||
}
|
||||
}
|
||||
|
||||
void leftToRightThenTopToBottom()
|
||||
{
|
||||
QVERIFY(PositionOrder::xThenY(QPointF(10, 500), QPointF(20, 0)));
|
||||
QVERIFY(!PositionOrder::xThenY(QPointF(20, 0), QPointF(10, 500)));
|
||||
QVERIFY(PositionOrder::xThenY(QPointF(10, 0), QPointF(10, 5)));
|
||||
QVERIFY(!PositionOrder::xThenY(QPointF(10, 5), QPointF(10, 0)));
|
||||
}
|
||||
|
||||
void topToBottomThenLeftToRight()
|
||||
{
|
||||
QVERIFY(PositionOrder::yThenX(QPointF(500, 10), QPointF(0, 20)));
|
||||
QVERIFY(!PositionOrder::yThenX(QPointF(0, 20), QPointF(500, 10)));
|
||||
QVERIFY(PositionOrder::yThenX(QPointF(0, 10), QPointF(5, 10)));
|
||||
QVERIFY(!PositionOrder::yThenX(QPointF(5, 10), QPointF(0, 10)));
|
||||
}
|
||||
|
||||
// Items a fraction of a pixel apart (placed with Ctrl, or after a
|
||||
// rotation) count as aligned, and are then ordered on the other axis.
|
||||
void roundedTreatsFractionsAsAligned()
|
||||
{
|
||||
QVERIFY(PositionOrder::roundedXThenY(QPointF(10.2, 0), QPointF(9.8, 5)));
|
||||
QVERIFY(!PositionOrder::roundedXThenY(QPointF(9.8, 5), QPointF(10.2, 0)));
|
||||
QVERIFY(PositionOrder::roundedYThenX(QPointF(0, 10.2), QPointF(5, 9.8)));
|
||||
QVERIFY(!PositionOrder::roundedYThenX(QPointF(5, 9.8), QPointF(0, 10.2)));
|
||||
}
|
||||
|
||||
// The three terminals that broke the tolerance comparison: with
|
||||
// "within 1 px counts as aligned", a < b (by y), b < c (by y) and
|
||||
// c < a (by x), a cycle. Rounded, they are simply ordered by x.
|
||||
void roundedIsTransitiveWhereToleranceWasNot()
|
||||
{
|
||||
const QPointF a(2, 0), b(1.1, 1), c(0.2, 2);
|
||||
QVERIFY(PositionOrder::roundedXThenY(c, b));
|
||||
QVERIFY(PositionOrder::roundedXThenY(b, a));
|
||||
QVERIFY(PositionOrder::roundedXThenY(c, a));
|
||||
QVERIFY(!PositionOrder::roundedXThenY(a, c));
|
||||
}
|
||||
|
||||
// Every order is a strict weak ordering on a grid of awkward points:
|
||||
// irreflexive, asymmetric, transitive, and with transitive
|
||||
// equivalence. Checked by brute force on the whole set.
|
||||
void strictWeakOrdering_data()
|
||||
{
|
||||
QTest::addColumn<QString>("name");
|
||||
for (const auto &order : orders())
|
||||
QTest::newRow(qPrintable(order.first)) << order.first;
|
||||
}
|
||||
|
||||
void strictWeakOrdering()
|
||||
{
|
||||
QFETCH(QString, name);
|
||||
Less less;
|
||||
for (const auto &order : orders())
|
||||
if (order.first == name) less = order.second;
|
||||
QVERIFY(less);
|
||||
|
||||
QList<QPointF> points;
|
||||
const QList<qreal> values{-1, -0.6, -0.4, 0, 0.2, 0.5, 0.9, 1, 1.1, 2, 10.5, 61.3, 61.7};
|
||||
for (qreal x : values)
|
||||
for (qreal y : values)
|
||||
points << QPointF(x, y);
|
||||
points << points.first() << QPointF(2, 0) << QPointF(1.1, 1) << QPointF(0.2, 2);
|
||||
|
||||
auto equiv = [&](const QPointF &p, const QPointF &q) { return !less(p, q) && !less(q, p); };
|
||||
for (const QPointF &p : points) {
|
||||
QVERIFY(!less(p, p));
|
||||
for (const QPointF &q : points) {
|
||||
if (less(p, q)) QVERIFY(!less(q, p));
|
||||
for (const QPointF &r : points) {
|
||||
if (less(p, q) && less(q, r)) QVERIFY2(less(p, r), "transitive");
|
||||
if (equiv(p, q) && equiv(q, r)) QVERIFY2(equiv(p, r), "equivalence transitive");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And std::sort on it ends sorted, with the list intact.
|
||||
QList<QPointF> sorted = points;
|
||||
std::sort(sorted.begin(), sorted.end(), less);
|
||||
QCOMPARE(sorted.size(), points.size());
|
||||
for (int i = 1; i < sorted.size(); ++i)
|
||||
QVERIFY(!less(sorted.at(i), sorted.at(i - 1)));
|
||||
}
|
||||
|
||||
// Twenty items at one position, as pasting at the origin leaves
|
||||
// them: a list std::sort could scan past the end of with "<=".
|
||||
void manyEqualPositionsSort()
|
||||
{
|
||||
QList<QPointF> points(20, QPointF(0, 0));
|
||||
points << QPointF(-10, 0) << QPointF(10, 0);
|
||||
std::sort(points.begin(), points.end(), PositionOrder::xThenY);
|
||||
QCOMPARE(points.size(), 22);
|
||||
QCOMPARE(points.first(), QPointF(-10, 0));
|
||||
QCOMPARE(points.last(), QPointF(10, 0));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_positionorder)
|
||||
#include "tst_positionorder.moc"
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
A label formula with ten or more sequential numbers: %sequ_10 must get
|
||||
the tenth value, not the first value followed by a "0". The values are
|
||||
put in by AssignVariables::assignSequence(), which the bill of
|
||||
materials uses for every label, so the real binary's --export-bom on
|
||||
fixtures/ten_sequential_numbers.qet shows the result: its element has
|
||||
the formula %sequ_10-%sequ_1 and the unit values A to J.
|
||||
*/
|
||||
class tst_tensequentialnumbers : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
static bool runQet(const QStringList &arguments)
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), arguments);
|
||||
return proc.waitForFinished(60000)
|
||||
&& proc.exitStatus() == QProcess::NormalExit
|
||||
&& proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found");
|
||||
QVERIFY(m_dir.isValid());
|
||||
}
|
||||
|
||||
void tenthValueIsUsed()
|
||||
{
|
||||
const QString fixture = QFINDTESTDATA("fixtures/ten_sequential_numbers.qet");
|
||||
QVERIFY2(!fixture.isEmpty(), "fixture project not found");
|
||||
const QString out = m_dir.filePath(QStringLiteral("bom.csv"));
|
||||
QVERIFY2(runQet({QStringLiteral("--export-bom"), fixture, out}), "--export-bom failed");
|
||||
|
||||
QFile file(out);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QStringList lines = QString::fromUtf8(file.readAll()).split(QLatin1Char('\n'));
|
||||
QStringList labels;
|
||||
for (const QString &line : lines) {
|
||||
if (line.startsWith(QLatin1Char('"')))
|
||||
labels << line.section(QLatin1Char(';'), 0, 0);
|
||||
}
|
||||
QVERIFY2(labels.contains(QStringLiteral("\"J-A\"")),
|
||||
qPrintable(QStringLiteral("labels: %1").arg(labels.join(QLatin1Char(' ')))));
|
||||
QVERIFY2(!labels.contains(QStringLiteral("\"A0-A\"")), "the first value with a 0 appended");
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_tensequentialnumbers)
|
||||
#include "tst_tensequentialnumbers.moc"
|
||||
@@ -176,6 +176,7 @@ private slots:
|
||||
// and reports the change once, on release.
|
||||
void dragKeepsTheOppositeCorner()
|
||||
{
|
||||
for (qreal size : {2.5, 5.0, 7.5, 10.0, 20.0, 30.0})
|
||||
for (int c = TextResize::TopLeft ; c <= TextResize::BottomLeft ; ++c)
|
||||
for (qreal angle : {0.0, 90.0, 37.0}) {
|
||||
QGraphicsScene scene;
|
||||
@@ -183,7 +184,7 @@ private slots:
|
||||
scene.addItem(text);
|
||||
text->setPos(100, 100);
|
||||
text->setRotation(angle);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
auto *handles = new TextResizeHandles(text, size);
|
||||
QSignalSpy finished(handles, &TextResizeHandles::resizeFinished);
|
||||
|
||||
const Corner corner = Corner(c);
|
||||
|
||||
Reference in New Issue
Block a user