mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 09:14:14 +02:00
Merge upstream/master: translation wave plus generic device
Upstream moved 56 commits ahead of the last merge. Six files conflicted,
all of them files where upstream's French -> English tr() translation wave
touched lines we had also touched with the cable feature. Resolution rule
applied everywhere: take upstream's English source strings, keep our cable
additions.
Resolved:
- sources/ui/xrefpropertieswidget.ui : kept our structure (m_labels_gb
pulled out of the display group, m_slave_row, the m_font_pb "Police..."
button of the cable cross-reference) and mechanically applied upstream's
20 English string replacements on top of it.
- sources/ui/xrefpropertieswidget.cpp : our version plus upstream's 7
translated combo entries; the tr("Cable") item we add for the cable
cross-reference type stays.
- sources/autoNum/ui/numparteditorw.cpp : upstream's English part names
("number format 1", "Sheet no.", "Plant", "Location", ...) for the
number-format list, with our comment explaining why type 3 (cable) takes
the conductor kinds of variable.
- sources/ui/configpage/configpages.cpp : upstream's English tab titles
(Conductors / Elements / Sheets) plus our Cables tab.
- sources/ui/configpage/projectconfigpages.cpp : upstream's "Sheets" tab
title plus our Cables tab block (single-rule SelectAutonumW(3)).
- sources/qetdiagrameditor.cpp : union of both sides - upstream's new
"Add a generic device" action (icon, status tip, data id) and its
English menu names ("File"/"Edit"/"Project"/"Display"), together with our
cable action, the Numbering and Lists menus, the cable list actions and
the CSV export in the Project menu.
Kept French: our own cable tr() source strings stay French for now. That
is the smaller change and the translation pass can follow separately.
Verified: full build 1065/1065, ctest 91/92 (only the pre-existing
headless tst_menubarkeyboard fails: no window manager, so Alt+F and F10
cannot open a menu bar), smoke start exit 124, no conflict marker left in
the tree.
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>
|
||||
@@ -178,12 +185,24 @@ add_test(NAME tst_symbolscale COMMAND tst_symbolscale)
|
||||
target_include_directories(tst_symbolscale PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_symbolscale PRIVATE Qt::Test)
|
||||
|
||||
# genericdevice.cpp is pure: the generic device wizard's layout, pattern
|
||||
# lines, groups and pin numbers, and the symbol it writes.
|
||||
add_executable(tst_genericdevice tst_genericdevice.cpp
|
||||
${QET_DIR}/sources/genericdevice/genericdevice.cpp)
|
||||
add_test(NAME tst_genericdevice COMMAND tst_genericdevice)
|
||||
set_tests_properties(tst_genericdevice PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
target_include_directories(tst_genericdevice PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_genericdevice PRIVATE Qt::Test Qt::Gui Qt::Xml)
|
||||
|
||||
# configprofile.h is header-only: which settings a saved configuration
|
||||
# profile carries, and how loading one replaces the live settings.
|
||||
add_executable(tst_configprofile tst_configprofile.cpp)
|
||||
# borderproperties.cpp: the real writer of the folio size defaults, to check
|
||||
# that what it writes falls in the part for new projects.
|
||||
add_executable(tst_configprofile tst_configprofile.cpp
|
||||
${QET_DIR}/sources/borderproperties.cpp)
|
||||
add_test(NAME tst_configprofile COMMAND tst_configprofile)
|
||||
target_include_directories(tst_configprofile PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_configprofile PRIVATE Qt::Test Qt::Gui)
|
||||
target_link_libraries(tst_configprofile PRIVATE Qt::Test Qt::Gui Qt::Xml)
|
||||
|
||||
# itemgroups.cpp keeps group membership and the selection rule apart from
|
||||
# Diagram, so it is tested here on a plain scene of rectangles.
|
||||
@@ -208,6 +227,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(
|
||||
@@ -621,6 +648,17 @@ target_link_libraries(tst_dxfcurves PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_dxfcurves PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# A static text with anchor="alignment" has its x at the edge or centre its
|
||||
# Halignment selects, not at its left edge (#1251).
|
||||
add_executable(
|
||||
tst_statictextanchor
|
||||
tst_statictextanchor.cpp)
|
||||
add_test(NAME tst_statictextanchor COMMAND tst_statictextanchor)
|
||||
add_dependencies(tst_statictextanchor qelectrotech)
|
||||
target_link_libraries(tst_statictextanchor PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_statictextanchor PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# --export-dxf --dxf-blocks: a symbol placed twice is one BLOCK and two
|
||||
# INSERTs (issue #1339).
|
||||
add_executable(
|
||||
@@ -922,6 +960,18 @@ target_compile_definitions(tst_derivedwireuuid PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# Symbols sharing one uuid on a folio keep their wires through save and
|
||||
# reopen (#1408): nets of examples/Habitat-Schemas_developpes.qet.
|
||||
add_executable(
|
||||
tst_sharedsymboluuid
|
||||
tst_sharedsymboluuid.cpp)
|
||||
add_test(NAME tst_sharedsymboluuid COMMAND tst_sharedsymboluuid)
|
||||
add_dependencies(tst_sharedsymboluuid qelectrotech)
|
||||
target_link_libraries(tst_sharedsymboluuid PRIVATE Qt::Test Qt::Xml)
|
||||
target_compile_definitions(tst_sharedsymboluuid PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# Element numbering schemes carry an id and elements follow them by it:
|
||||
# a legacy file gets stable ids on --resave; through --run, rename, edit,
|
||||
# refused removal and undo/redo keep the elements following the scheme.
|
||||
@@ -1081,6 +1131,17 @@ 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)
|
||||
|
||||
# Picture files dropped on a folio: which files are taken, the size and
|
||||
# pixel limits before a picture is embedded, fitting into the view and the
|
||||
# cascade of several pictures.
|
||||
add_executable(
|
||||
tst_imagedrop
|
||||
tst_imagedrop.cpp
|
||||
${QET_DIR}/sources/imagedrop.cpp)
|
||||
add_test(NAME tst_imagedrop COMMAND tst_imagedrop)
|
||||
target_include_directories(tst_imagedrop PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_imagedrop PRIVATE Qt::Test Qt::Gui)
|
||||
|
||||
# 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,685 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project title="Habitat-Schemas_developpes" uuid="{947a7459-9d3d-57ef-896f-ccacfb38ab71}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">17/04/2021</property>
|
||||
<property name="saveddate-eu" show="1">17-04-2021</property>
|
||||
<property name="saveddate-us" show="1">2021-04-17</property>
|
||||
<property name="savedfilename" show="1">Habitat-Schemas_developpes</property>
|
||||
<property name="savedfilepath" show="1">C:/Users/VD5385/Documents/Github/qelectrotech-source-mirror/qelectrotech-source-mirror/examples/Habitat-Schemas_developpes.qet</property>
|
||||
<property name="savedtime" show="1">14:45</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="%id/%total" 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="9" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="0" />
|
||||
<report label="%f-%l%c" />
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="contacts" master_label="%f-%l%c" offset="40" powerprefix="" showallconfiguredslaves="false" showpowerctc="false" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="bottom" stackoverlapping="true" switchprefix="" type="coil" xrefpos="AlignBaseline" />
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="false" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" 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" stackoverlapping="true" switchprefix="" type="plc" xrefpos="AlignBottom" />
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="40" powerprefix="" showallconfiguredslaves="false" showpowerctc="false" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" switchprefix="" type="protection" xrefpos="AlignBaseline" />
|
||||
</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="QElectroTech Team" auto_page_num="" cols="10" colsize="60" date="20130601" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="%id/%total" freezeNewConductor="false" freezeNewElement="false" height="420" indexrev="" locmach="" order="1" plant="" rows="5" rowsize="80" title="Eclairage en simple allumage" uuid="{88807a77-e299-5e27-a274-103fe6bb51ae}" 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="9" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="0" />
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="1" prefix="" type="embed://import/sources/multifilaire/src_p.elmt" uuid="{7c229e90-c025-4c01-bbb7-7af8fd90aefc}" x="90" y="80" z="10">
|
||||
<terminals>
|
||||
<terminal id="0" orientation="1" x="6" y="0" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">L</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-90" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{7ffba7b0-932b-47e7-9fa5-d8c5a56a3c27}" x="-26.0822" y="6.73678">
|
||||
<text>L</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/sources/multifilaire/src_p.elmt" uuid="{d001d8d3-e0b8-4f8c-bad3-2dc564ddbb1d}" x="90" y="400" z="10">
|
||||
<terminals>
|
||||
<terminal id="1" orientation="1" x="6" y="0" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{de902e74-cc32-4c39-991b-d8b633aed3f8}" x="-2.0425" y="16.5674">
|
||||
<text />
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/protections/disjoncteurs/disjonct-m.elmt" uuid="{1a0c9ea9-bfac-4816-97a4-d1676abe08ff}" x="140" y="160" z="10">
|
||||
<terminals>
|
||||
<terminal id="2" orientation="0" x="10" y="-16" />
|
||||
<terminal id="3" orientation="2" x="10" y="26" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">16A</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{9bf813a9-2a95-4f13-9baf-1906771e0040}" x="43.45" y="-13.3834">
|
||||
<text>16A</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/protections/disjoncteurs/disjonct-m.elmt" uuid="{34153d81-c75e-4269-85b1-541d16982ad9}" x="140" y="350" z="10">
|
||||
<terminals>
|
||||
<terminal id="4" orientation="0" x="10" y="-16" />
|
||||
<terminal id="5" orientation="2" x="10" y="26" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">20A</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{bccf3627-f134-41c6-a00f-952c5fa75c53}" x="43.45" y="-13.3834">
|
||||
<text>20A</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/contacts/interrupteurs/interrupteur.elmt" uuid="{3534f52e-ebe0-4510-9706-3f7e2734080f}" x="230" y="150" z="10">
|
||||
<terminals>
|
||||
<terminal id="6" orientation="0" x="0" y="-17" />
|
||||
<terminal id="7" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">S1</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{52f55b0e-243a-43e6-9cd8-e07e105413ce}" x="21.7" y="-5.55">
|
||||
<text>S1</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/contacts/interrupteurs/interrupteur.elmt" uuid="{0f7a1074-334f-4173-a9e9-25a601ef4c52}" x="230" y="210" z="10">
|
||||
<terminals>
|
||||
<terminal id="8" orientation="0" x="0" y="-17" />
|
||||
<terminal id="9" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">S2</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{dc88b1bd-21e2-43b6-b53e-f25099be0f4b}" x="21.7" y="-5.55">
|
||||
<text>S2</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="" type="embed://import/pc.elmt" uuid="{92274d83-04c4-49eb-83b6-7c8d7d89e638}" x="300" y="340" z="10">
|
||||
<terminals>
|
||||
<terminal id="10" orientation="3" x="-25" y="0" />
|
||||
<terminal id="11" orientation="1" x="1" y="0" />
|
||||
<terminal id="12" orientation="2" x="-12" y="6" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">PC1</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{210108db-89bf-40e7-8c8d-6acfd1bedc8d}" x="-20" y="-33.5">
|
||||
<text>PC1</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/lampe_pe.elmt" uuid="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" x="320" y="150" z="10">
|
||||
<terminals>
|
||||
<terminal id="13" orientation="0" x="0" y="-17" />
|
||||
<terminal id="14" orientation="2" x="-10" y="12" />
|
||||
<terminal id="15" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">L1</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{8570e0e6-41ec-47a3-b629-c58e8c4ccc57}" x="32.95" y="-11.55">
|
||||
<text>L1</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/lampe_pe.elmt" uuid="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" x="320" y="210" z="10">
|
||||
<terminals>
|
||||
<terminal id="16" orientation="0" x="0" y="-17" />
|
||||
<terminal id="17" orientation="2" x="-10" y="12" />
|
||||
<terminal id="18" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">L2</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{5e1942f3-39a6-4b77-a862-5c2a9712a63a}" x="32.95" y="-11.55">
|
||||
<text>L2</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/lampe_pe.elmt" uuid="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" x="320" y="270" z="10">
|
||||
<terminals>
|
||||
<terminal id="19" orientation="0" x="0" y="-17" />
|
||||
<terminal id="20" orientation="2" x="-10" y="12" />
|
||||
<terminal id="21" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">L3</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{29ef9f5b-b2ac-4ad7-b315-f5a5226549d3}" x="32.95" y="-11.55">
|
||||
<text>L3</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/disj_neutre.elmt" uuid="{4c068000-8007-4a6d-82d1-21c157209335}" x="430" y="150" z="10">
|
||||
<terminals>
|
||||
<terminal id="22" orientation="0" x="0" y="-17" />
|
||||
<terminal id="23" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">10A</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{e1dcd9cb-38a2-41d4-a54a-f137a4bce8be}" x="28.5161" y="-16.2484">
|
||||
<text>10A</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/disj_neutre.elmt" uuid="{7c25df98-6c99-4dde-9080-a08214224b06}" x="430" y="340" z="10">
|
||||
<terminals>
|
||||
<terminal id="24" orientation="0" x="0" y="-17" />
|
||||
<terminal id="25" orientation="2" x="0" y="17" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">20A</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{aa9eeb7b-9f43-46f9-adf3-dd3f161c0782}" x="28.5161" y="-16.2484">
|
||||
<text>20A</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="1" prefix="" type="embed://import/sources/multifilaire/src_n.elmt" uuid="{0b635509-0349-4517-93fd-1c602d4cd8dd}" x="510" y="80" z="10">
|
||||
<terminals>
|
||||
<terminal id="26" orientation="1" x="6" y="0" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">N</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-90" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{1dc02080-1b95-4b54-9aeb-58dfa7289744}" x="-28.7219" y="7.23678">
|
||||
<text>N</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/sources/multifilaire/src_n.elmt" uuid="{6eb3bd36-847c-403f-87b0-c7a4b7e8327f}" x="510" y="390" z="10">
|
||||
<terminals>
|
||||
<terminal id="27" orientation="1" x="6" y="0" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{b1001bac-77a1-45a5-9d79-0c0bde88ac15}" x="-6.8219" y="-9.76322">
|
||||
<text />
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="1" prefix="" type="embed://import/sources/multifilaire/pe.elmt" uuid="{d6d0d1d9-21cc-4757-8fbf-d44726eaf18d}" x="560" y="80" z="10">
|
||||
<terminals>
|
||||
<terminal id="28" orientation="1" x="6" y="0" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">PE</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-90" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{ce539f0c-0df5-42c2-a6a4-01feb89d4a4e}" x="-27.369" y="12.45">
|
||||
<text>PE</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="3" prefix="" type="embed://import/sources/multifilaire/pe.elmt" uuid="{4d7f799d-2263-448a-9350-35237b5345f0}" x="560" y="390" z="10">
|
||||
<terminals>
|
||||
<terminal id="29" orientation="1" x="6" y="0" />
|
||||
</terminals>
|
||||
<inputs />
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="MS Shell Dlg 2,9,-1,5,50,0,0,0,0,0,Normal" frame="false" keep_visual_rotation="true" rotation="-270" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{04862e60-07c2-4d6b-a271-fe77ba0ad07e}" x="-5.469" y="-13.55">
|
||||
<text />
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups />
|
||||
</element>
|
||||
</elements>
|
||||
<conductors>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff0000" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{d001d8d3-e0b8-4f8c-bad3-2dc564ddbb1d}" element1_label="" element1_name="Phase source" element2="{7c229e90-c025-4c01-bbb7-7af8fd90aefc}" element2_label="L" element2_name="Phase source" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{affdf717-0a37-5ccf-b9ae-61ca395e0f82}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff0000" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{1a0c9ea9-bfac-4816-97a4-d1676abe08ff}" element1_label="16A" element1_name="Circuit-breaker" element2="{7c229e90-c025-4c01-bbb7-7af8fd90aefc}" element2_label="L" element2_name="Phase source" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{ab69f5f1-346f-5514-b8f4-e23282dd76ad}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{8423eab0-c93d-5cb7-b670-ed915025647e}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff0000" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{34153d81-c75e-4269-85b1-541d16982ad9}" element1_label="20A" element1_name="Circuit-breaker" element2="{7c229e90-c025-4c01-bbb7-7af8fd90aefc}" element2_label="L" element2_name="Phase source" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{ab69f5f1-346f-5514-b8f4-e23282dd76ad}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{6fd96e4d-6d22-551f-afed-d0d28ab3d026}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff0000" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{1a0c9ea9-bfac-4816-97a4-d1676abe08ff}" element1_label="16A" element1_name="Circuit-breaker" element2="{3534f52e-ebe0-4510-9706-3f7e2734080f}" element2_label="S1" element2_name="Switch" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{3a9a1158-fcda-555c-ad99-d6b92a4f04a7}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" userx="160" usery="120" uuid="{4170dcca-a770-5e1c-abdc-00460596271a}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff0000" color2="#000000" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="2" displaytext="1" element1="{34153d81-c75e-4269-85b1-541d16982ad9}" element1_label="20A" element1_name="Circuit-breaker" element2="{92274d83-04c4-49eb-83b6-7c8d7d89e638}" element2_label="PC1" element2_name="c" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="2,5mm²" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{3a9a1158-fcda-555c-ad99-d6b92a4f04a7}" terminal2="{0c6b6b61-7e22-53c9-8e34-25f816145de3}" terminalname1="" terminalname2="" text_color="#000000" type="multi" userx="200" usery="350" uuid="{3d91adfc-5ea3-5a8f-8c84-a32c43c725c6}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff0000" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{3534f52e-ebe0-4510-9706-3f7e2734080f}" element1_label="S1" element1_name="Switch" element2="{0f7a1074-334f-4173-a9e9-25a601ef4c52}" element2_label="S2" element2_name="Switch" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="1,5mm²" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" userx="160" usery="210" uuid="{683fa12e-0275-5cfb-ae1e-1d5bb293660c}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="-9" orientation="horizontal" />
|
||||
<segment length="-10" orientation="horizontal" />
|
||||
<segment length="60" orientation="vertical" />
|
||||
<segment length="10" orientation="horizontal" />
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{3534f52e-ebe0-4510-9706-3f7e2734080f}" element1_label="S1" element1_name="Switch" element2="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element2_label="L1" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{995071b4-5237-5328-b1fe-f37115b72d60}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff5500" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{0f7a1074-334f-4173-a9e9-25a601ef4c52}" element1_label="S2" element1_name="Switch" element2="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element2_label="L2" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{e23364f3-f08b-50dd-8f6e-6b2d0533d24e}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#ff5500" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{0f7a1074-334f-4173-a9e9-25a601ef4c52}" element1_label="S2" element1_name="Switch" element2="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element2_label="L3" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{68ef76c3-03b2-5d9e-a377-d3067d3f12e0}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="2" displaytext="1" element1="{7c25df98-6c99-4dde-9080-a08214224b06}" element1_label="20A" element1_name="Circuit-breaker" element2="{92274d83-04c4-49eb-83b6-7c8d7d89e638}" element2_label="PC1" element2_name="c" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminal2="{5e7df160-501d-5ae1-9dc2-59d2ba0daf9a}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{95ef461f-72e0-51d4-bb0f-e6f012928528}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="-9" orientation="horizontal" />
|
||||
<segment length="-50" orientation="horizontal" />
|
||||
<segment length="0" orientation="horizontal" />
|
||||
<segment length="-36" orientation="horizontal" />
|
||||
<segment length="0" orientation="horizontal" />
|
||||
<segment length="-9" orientation="horizontal" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="true" bus="" cable="" color="#00aa00" color2="#ffff00" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="4" displaytext="1" element1="{92274d83-04c4-49eb-83b6-7c8d7d89e638}" element1_label="PC1" element1_name="c" element2="{4d7f799d-2263-448a-9350-35237b5345f0}" element2_label="" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" style="line-style: dashdotted" tension_protocol="" terminal1="{7c6f5395-9daf-5fe6-89e0-c5c4145e0f9c}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{521bfde2-4240-5095-8317-4234f14b1b50}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="9" orientation="vertical" />
|
||||
<segment length="11" orientation="vertical" />
|
||||
<segment length="272" orientation="horizontal" />
|
||||
<segment length="1" orientation="vertical" />
|
||||
<segment length="9" orientation="vertical" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="true" bus="" cable="" color="#00aa00" color2="#ffff00" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="4" displaytext="1" element1="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element1_label="L1" element1_name="e" element2="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element2_label="L2" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" style="line-style: dashdotted" tension_protocol="" terminal1="{696f18f9-9973-56d7-ad54-543740e0d6e1}" terminal2="{696f18f9-9973-56d7-ad54-543740e0d6e1}" terminalname1="" terminalname2="" text_color="#000000" type="multi" userx="360" usery="190" uuid="{e42f0149-8853-50e6-9097-264036a5dde0}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<segment length="15" orientation="horizontal" />
|
||||
<segment length="60" orientation="vertical" />
|
||||
<segment length="-15" orientation="horizontal" />
|
||||
<segment length="-9" orientation="horizontal" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element1_label="L1" element1_name="e" element2="{4c068000-8007-4a6d-82d1-21c157209335}" element2_label="10A" element2_name="Circuit-breaker" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{cf7af2d6-2fce-52b1-856e-78ae7db37426}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="true" bus="" cable="" color="#00aa00" color2="#ffff00" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="4" displaytext="1" element1="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element1_label="L2" element1_name="e" element2="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element2_label="L3" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" style="line-style: dashdotted" tension_protocol="" terminal1="{696f18f9-9973-56d7-ad54-543740e0d6e1}" terminal2="{696f18f9-9973-56d7-ad54-543740e0d6e1}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{c53823e4-42d6-590e-a52a-52c98efa1be9}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<segment length="15" orientation="horizontal" />
|
||||
<segment length="60" orientation="vertical" />
|
||||
<segment length="-15" orientation="horizontal" />
|
||||
<segment length="-9" orientation="horizontal" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element1_label="L2" element1_name="e" element2="{4c068000-8007-4a6d-82d1-21c157209335}" element2_label="10A" element2_name="Circuit-breaker" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" userx="380" usery="180" uuid="{74cc2956-3b6d-5f78-be23-ddeffa4e1907}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<segment length="30" orientation="horizontal" />
|
||||
<segment length="-60" orientation="vertical" />
|
||||
<segment length="20" orientation="horizontal" />
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element1_label="L3" element1_name="e" element2="{4c068000-8007-4a6d-82d1-21c157209335}" element2_label="10A" element2_name="Circuit-breaker" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" terminalname1="" terminalname2="" text_color="#000000" type="multi" userx="380" usery="190" uuid="{ff5bed0e-6cda-5f72-b711-e13a957a027d}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<segment length="30" orientation="horizontal" />
|
||||
<segment length="-120" orientation="vertical" />
|
||||
<segment length="20" orientation="horizontal" />
|
||||
<segment length="9" orientation="horizontal" />
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="true" bus="" cable="" color="#00aa00" color2="#ffff00" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="4" displaytext="1" element1="{3ab3ddf0-c969-4e96-9981-d40d63f92306}" element1_label="L3" element1_name="e" element2="{d6d0d1d9-21cc-4757-8fbf-d44726eaf18d}" element2_label="PE" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" style="line-style: dashdotted" tension_protocol="" terminal1="{696f18f9-9973-56d7-ad54-543740e0d6e1}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{7a6815bf-d956-52a4-b270-bb8aa3df8e89}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{4c068000-8007-4a6d-82d1-21c157209335}" element1_label="10A" element1_name="Circuit-breaker" element2="{0b635509-0349-4517-93fd-1c602d4cd8dd}" element2_label="N" element2_name="Neutral source" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{daea0830-ba4a-5e08-8fdc-751bcfe6f85f}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{7c25df98-6c99-4dde-9080-a08214224b06}" element1_label="20A" element1_name="Circuit-breaker" element2="{6eb3bd36-847c-403f-87b0-c7a4b7e8327f}" element2_label="" element2_name="Neutral source" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" tension_protocol="" terminal1="{6c830253-af7a-5723-9fc2-304c81fe739f}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{97bc19fc-b224-5693-ba45-38f4ae9ed1bd}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color="#0000ff" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{6eb3bd36-847c-403f-87b0-c7a4b7e8327f}" element1_label="" element1_name="Neutral source" element2="{0b635509-0349-4517-93fd-1c602d4cd8dd}" element2_label="N" element2_name="Neutral source" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" tension_protocol="" terminal1="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{a1214aa3-69d5-5490-8e86-3150665bd6b8}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
<conductor bicolor="true" bus="" cable="" color="#00aa00" color2="#ffff00" condsize="1" conductor_color="" conductor_section="2,5mm²" dash-size="4" displaytext="1" element1="{4d7f799d-2263-448a-9350-35237b5345f0}" element1_label="" element1_name="e" element2="{d6d0d1d9-21cc-4757-8fbf-d44726eaf18d}" element2_label="PE" element2_name="e" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="" numsize="9" onetextperfolio="0" rotation="270" style="line-style: dashdotted" tension_protocol="" terminal1="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminal2="{cf882328-3321-5cf9-ad83-ff420cb81927}" terminalname1="" terminalname2="" text_color="#000000" type="multi" uuid="{9998cb90-3fb4-54af-b79a-069918ca8c8f}" vertical-alignment="AlignRight" vertirotatetext="0" x="0" y="0">
|
||||
<sequentialNumbers />
|
||||
</conductor>
|
||||
</conductors>
|
||||
<inputs>
|
||||
<input font="MS Shell Dlg 2,8,-1,5,50,0,0,0,0,0,Normal" rotation="0" text="<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:9pt;">L1 en simple allumage par S1</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:9pt;">L2 et L3 en simple allumage par S2</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:9pt;">1 PC 2p+T</span></p></body></html>" uuid="{946b5b47-981a-5743-b84e-d0a9fdb49584}" x="210" y="30" />
|
||||
</inputs>
|
||||
</diagram>
|
||||
|
||||
|
||||
|
||||
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="ru">Импортированные элементы</name>
|
||||
<name lang="en">Imported elements</name>
|
||||
<name lang="it">Elementi importati</name>
|
||||
<name lang="fr">Éléments importés</name>
|
||||
<name lang="pl">Elementy importowane</name>
|
||||
<name lang="es">Elementos importados</name>
|
||||
<name lang="cs">Zavedené prvky</name>
|
||||
</names>
|
||||
<category name="sources">
|
||||
<names>
|
||||
<name lang="ar">مصادر</name>
|
||||
<name lang="de">Quellen</name>
|
||||
<name lang="ru">Источники</name>
|
||||
<name lang="pt">Fontes</name>
|
||||
<name lang="en">Sources</name>
|
||||
<name lang="it">Sorgenti</name>
|
||||
<name lang="fr">Sources</name>
|
||||
<name lang="pl">Źródła zasilania</name>
|
||||
<name lang="es">Fuentes</name>
|
||||
<name lang="cs">Zdroje</name>
|
||||
</names>
|
||||
<category name="multifilaire">
|
||||
<names>
|
||||
<name lang="ar">متعددة الأسلاك</name>
|
||||
<name lang="de">Mehradrig</name>
|
||||
<name lang="ru">Многолинейные</name>
|
||||
<name lang="pt">Multifilar</name>
|
||||
<name lang="en">Multiline</name>
|
||||
<name lang="it">Multifilari</name>
|
||||
<name lang="fr">Multifilaire</name>
|
||||
<name lang="pl">Schemat wielokreskowyowy</name>
|
||||
<name lang="es">Multihilo</name>
|
||||
<name lang="cs">Několik vedení</name>
|
||||
</names>
|
||||
<element name="pe.elmt">
|
||||
<definition height="30" hotspot_x="25" hotspot_y="15" type="element" version="0.3" width="40">
|
||||
<names>
|
||||
<name lang="ru">Нейтраль</name>
|
||||
<name lang="fr">Protection Electrique</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<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="10" y1="0" y2="0" />
|
||||
<input size="9" tagg="label" text="PE" x="-23" y="0" />
|
||||
<terminal orientation="e" uuid="{cf882328-3321-5cf9-ad83-ff420cb81927}" x="10" y="0" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
<element name="src_p.elmt">
|
||||
<definition height="30" hotspot_x="15" hotspot_y="15" type="element" version="0.3" width="30">
|
||||
<names>
|
||||
<name lang="ar">مصدر وجه</name>
|
||||
<name lang="de">Phase</name>
|
||||
<name lang="ru">Фаза</name>
|
||||
<name lang="pt">Fonte de fase</name>
|
||||
<name lang="en">Phase source</name>
|
||||
<name lang="it">Sorgente fase</name>
|
||||
<name lang="fr">Source phase</name>
|
||||
<name lang="pl">Przewód liniowy</name>
|
||||
<name lang="es">Fuente fase</name>
|
||||
<name lang="cs">Fázový zdroj</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<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="10" y1="0" y2="0" />
|
||||
<input size="9" tagg="label" text="L" x="-16" y="0" />
|
||||
<terminal orientation="e" uuid="{cf882328-3321-5cf9-ad83-ff420cb81927}" x="10" y="0" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
<element name="src_n.elmt">
|
||||
<definition height="30" hotspot_x="15" hotspot_y="15" type="element" version="0.3" width="30">
|
||||
<names>
|
||||
<name lang="ar">مصدر محايد</name>
|
||||
<name lang="de">Neutralleiter</name>
|
||||
<name lang="ru">Нейтраль</name>
|
||||
<name lang="pt">Fonte de neutro</name>
|
||||
<name lang="en">Neutral source</name>
|
||||
<name lang="it">Sorgente neutro</name>
|
||||
<name lang="fr">Source neutre</name>
|
||||
<name lang="pl">Przewód neutralny</name>
|
||||
<name lang="es">Fuente neutro</name>
|
||||
<name lang="cs">Nulový zdroj</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<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="10" y1="0" y2="0" />
|
||||
<input size="9" tagg="label" text="N" x="-17" y="0" />
|
||||
<terminal orientation="e" uuid="{cf882328-3321-5cf9-ad83-ff420cb81927}" x="10" y="0" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
<category name="protections">
|
||||
<names>
|
||||
<name lang="ar">حمايات</name>
|
||||
<name lang="de">Sicherheit</name>
|
||||
<name lang="ru">Защитные уст-ва</name>
|
||||
<name lang="pt">Protecções</name>
|
||||
<name lang="en">Protections</name>
|
||||
<name lang="it">Protezioni</name>
|
||||
<name lang="fr">Protections</name>
|
||||
<name lang="pl">Zabezpieczenia</name>
|
||||
<name lang="es">Protecciones</name>
|
||||
<name lang="cs">Ochrany</name>
|
||||
</names>
|
||||
<category name="disjoncteurs">
|
||||
<names>
|
||||
<name lang="ar">قواطع</name>
|
||||
<name lang="de">Lastschalter</name>
|
||||
<name lang="ru">Выключатели</name>
|
||||
<name lang="pt">Disjuntores</name>
|
||||
<name lang="en">Circuit-breakers</name>
|
||||
<name lang="it">Interruttori</name>
|
||||
<name lang="fr">Disjoncteurs</name>
|
||||
<name lang="pl">Wyłączniki</name>
|
||||
<name lang="es">Disyuntores</name>
|
||||
<name lang="cs">Jističe</name>
|
||||
</names>
|
||||
<element name="disjonct-m.elmt">
|
||||
<definition height="60" hotspot_x="0" hotspot_y="25" orientation="dnny" type="element" version="0.3" width="30">
|
||||
<names>
|
||||
<name lang="ar">قاطع أحدي القطب</name>
|
||||
<name lang="en">Circuit-breaker</name>
|
||||
<name lang="fr">Disjoncteur unipolaire</name>
|
||||
<name lang="pl">Wyłącznik</name>
|
||||
<name lang="cs">Jednopólový jistič</name>
|
||||
<name lang="it">Int. Aut. Magneto-termico 1P</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<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="10" x2="10" y1="26" y2="30" />
|
||||
<polygon antialias="true" closed="false" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="10" x2="10" x3="5" y1="13" y2="10" y3="-10" />
|
||||
<input size="9" tagg="label" text="_" x="16" y="3.6666" />
|
||||
<arc angle="-180" antialias="true" height="5" start="90" style="line-style:normal;line-weight:normal;filling:none;color:black" width="8" x="6" y="20.5" />
|
||||
<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="10" x2="10" y1="18" y2="20" />
|
||||
<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="10" x2="15" y1="18" y2="18" />
|
||||
<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="15" x2="15" y1="13" y2="18" />
|
||||
<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="10" x2="15" y1="13" y2="13" />
|
||||
<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="10" x2="10" y1="-20" y2="-10" />
|
||||
<line antialias="true" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="12" x2="8" y1="-12" y2="-8" />
|
||||
<line antialias="true" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="8" x2="12" y1="-12" y2="-8" />
|
||||
<terminal orientation="s" uuid="{3a9a1158-fcda-555c-ad99-d6b92a4f04a7}" x="10" y="30" />
|
||||
<terminal orientation="n" uuid="{ab69f5f1-346f-5514-b8f4-e23282dd76ad}" x="10" y="-20" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
<category name="contacts">
|
||||
<names>
|
||||
<name lang="ar">ملامسات</name>
|
||||
<name lang="de">Kontakte</name>
|
||||
<name lang="ru">Контакты</name>
|
||||
<name lang="pt">Contactos</name>
|
||||
<name lang="en">Contacts</name>
|
||||
<name lang="it">Contatti</name>
|
||||
<name lang="fr">Contacts</name>
|
||||
<name lang="pl">Zestyki</name>
|
||||
<name lang="es">Terminales</name>
|
||||
<name lang="cs">Kontakty</name>
|
||||
</names>
|
||||
<category name="interrupteurs">
|
||||
<names>
|
||||
<name lang="ar">مفاتيح</name>
|
||||
<name lang="de">Schalter</name>
|
||||
<name lang="ru">переключателей</name>
|
||||
<name lang="pt">Interruptores</name>
|
||||
<name lang="en">Switches</name>
|
||||
<name lang="it">Interruttori</name>
|
||||
<name lang="fr">Interrupteurs</name>
|
||||
<name lang="pl">Przełącznik</name>
|
||||
<name lang="es">Interruptores</name>
|
||||
<name lang="cs">Spínače</name>
|
||||
</names>
|
||||
<element name="interrupteur.elmt">
|
||||
<definition height="40" hotspot_x="10" hotspot_y="20" orientation="dnny" type="element" version="0.3" width="20">
|
||||
<names>
|
||||
<name lang="ar">مفتاح</name>
|
||||
<name lang="de">Schalter</name>
|
||||
<name lang="ru">Переключатель</name>
|
||||
<name lang="pt">Interruptor</name>
|
||||
<name lang="en">Switch</name>
|
||||
<name lang="it">Interruttore</name>
|
||||
<name lang="fr">Interrupteur</name>
|
||||
<name lang="pl">Łącznik</name>
|
||||
<name lang="es">Interruptor</name>
|
||||
<name lang="cs">Spínač</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<polygon antialias="true" closed="false" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="-5" x2="0" x3="0" y1="-10" y2="10" y3="20" />
|
||||
<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="-20" y2="-12.5" />
|
||||
<input size="9" tagg="label" text="_" x="2.75" y="3" />
|
||||
<circle antialias="true" diameter="3.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x="-1.75" y="-11.75" />
|
||||
<terminal orientation="n" uuid="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" x="0" y="-21" />
|
||||
<terminal orientation="s" uuid="{6c830253-af7a-5723-9fc2-304c81fe739f}" x="0" y="21" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
<element name="lampe_pe.elmt">
|
||||
<definition height="50" hotspot_x="15" hotspot_y="25" orientation="dnny" type="element" version="0.3" width="50">
|
||||
<names>
|
||||
<name lang="fr">Point Lumineux</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<circle antialias="true" diameter="20" style="line-style:normal;line-weight:normal;filling:none;color:black" x="-10" y="-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="7.03" x2="-7.03" y1="-7.03" y2="7.03" />
|
||||
<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="-7.03" x2="7.03" y1="-7.03" y2="7.03" />
|
||||
<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="-20" y2="-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="10" y2="20" />
|
||||
<input size="9" tagg="label" text="L_" x="10" y="1" />
|
||||
<arc angle="-90" antialias="true" height="16" start="270" style="line-style:normal;line-weight:normal;filling:none;color:black" width="15" x="-13" y="-3" />
|
||||
<terminal orientation="n" uuid="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" x="0" y="-21" />
|
||||
<terminal orientation="s" uuid="{6c830253-af7a-5723-9fc2-304c81fe739f}" x="0" y="21" />
|
||||
<terminal orientation="s" uuid="{696f18f9-9973-56d7-ad54-543740e0d6e1}" x="-10" y="16" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
<element name="disj_neutre.elmt">
|
||||
<definition height="50" hotspot_x="10" hotspot_y="25" orientation="dnny" type="element" version="0.3" width="30">
|
||||
<names>
|
||||
<name lang="ar">قاطع</name>
|
||||
<name lang="de">Lastschalter</name>
|
||||
<name lang="ru">Выключатель</name>
|
||||
<name lang="pt">Disjuntor</name>
|
||||
<name lang="en">Circuit-breaker</name>
|
||||
<name lang="it">Sezionatore 1P</name>
|
||||
<name lang="fr">Disjoncteur</name>
|
||||
<name lang="pl">Wyłącznik</name>
|
||||
<name lang="es">Disyuntor</name>
|
||||
<name lang="cs">Jistič</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<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="-20" y2="-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="10" y2="20" />
|
||||
<line antialias="true" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="-5" y1="-10" y2="10" />
|
||||
<input size="9" tagg="label" text="_" x="6" y="3" />
|
||||
<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="2" x2="-2" y1="14" y2="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="-2" x2="2" y1="14" y2="10" />
|
||||
<terminal orientation="n" uuid="{cef8a446-540b-53f6-a474-6cfcb20d1abb}" x="0" y="-21" />
|
||||
<terminal orientation="s" uuid="{6c830253-af7a-5723-9fc2-304c81fe739f}" x="0" y="21" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
<element name="pc.elmt">
|
||||
<definition height="60" hotspot_x="35" hotspot_y="40" type="element" version="0.3" width="50">
|
||||
<names>
|
||||
<name lang="ru">Нейтраль</name>
|
||||
<name lang="fr">Protection Electrique</name>
|
||||
</names>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<arc angle="-90" antialias="true" height="18" start="270" style="line-style:normal;line-weight:normal;filling:none;color:black" width="18" x="-24" y="-9" />
|
||||
<arc angle="-90" antialias="true" height="18" start="180" style="line-style:normal;line-weight:normal;filling:none;color:black" width="18" x="-24" y="-9" />
|
||||
<arc angle="-90" antialias="true" height="18" start="90" style="line-style:normal;line-weight:normal;filling:none;color:black" width="18" x="-18" y="-9" />
|
||||
<input size="9" tagg="label" text="_" x="-20" y="-22" />
|
||||
<arc angle="-90" antialias="true" height="18" start="0" style="line-style:normal;line-weight:normal;filling:none;color:black" width="18" x="-18" y="-9" />
|
||||
<rect antialias="false" height="10" style="line-style:normal;line-weight:normal;filling:black;color:black" width="4" x="-14" y="-5" />
|
||||
<terminal orientation="e" uuid="{5e7df160-501d-5ae1-9dc2-59d2ba0daf9a}" x="5" y="0" />
|
||||
<terminal orientation="w" uuid="{0c6b6b61-7e22-53c9-8e34-25f816145de3}" x="-29" y="0" />
|
||||
<terminal orientation="s" uuid="{7c6f5395-9daf-5fe6-89e0-c5c4145e0f9c}" x="-12" y="10" />
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,59 @@
|
||||
<project title="Anchored static texts" uuid="{6c1a2b3d-1251-4c1e-9e57-3f0d0f0a7b10}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">static_text_anchor</property>
|
||||
<property name="savedtime" show="1">18:39</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="{6c1a2b3d-1251-4c1e-9e57-3f0d0f0a7a01}" version="0.200.1-dev">
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="" type="embed://import/anchor.elmt" uuid="{6c1a2b3d-1251-4c1e-9e57-3f0d0f0a7a02}" x="300" y="300" z="10">
|
||||
<terminals/>
|
||||
<inputs/>
|
||||
<dynamic_texts/>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
<element name="anchor.elmt">
|
||||
<definition height="80" hotspot_x="100" hotspot_y="10" link_type="simple" type="element" version="0.100.0" width="200">
|
||||
<uuid uuid="{6c1a2b3d-1251-4c1e-9e57-3f0d0f0a7a05}"/>
|
||||
<names>
|
||||
<name lang="en">Anchored static texts</name>
|
||||
</names>
|
||||
<description>
|
||||
<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="0" y2="60"/>
|
||||
<text text="GPIO21 PCM_DOUT" x="0" y="10" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" color="#000000" rotation="0"/>
|
||||
<text text="GPIO21 PCM_DOUT" x="0" y="20" Halignment="AlignRight" anchor="alignment" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" color="#000000" rotation="0"/>
|
||||
<text text="GPIO21 PCM_DOUT" x="0" y="30" Halignment="AlignHCenter" anchor="alignment" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" color="#000000" rotation="0"/>
|
||||
<text text="GPIO21 PCM_DOUT" x="0" y="40" Halignment="AlignRight" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" color="#000000" rotation="0"/>
|
||||
<text text="GPIO21 PCM_DOUT" x="0" y="50" anchor="alignment" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" color="#000000" rotation="0"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</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);
|
||||
}
|
||||
@@ -66,8 +66,8 @@ int main(int argc, char **argv)
|
||||
const QByteArray pristine=png(image->pixmap()); const auto transform=image->transform(); const auto bounds=image->boundingRect();
|
||||
ImagePropertiesWidget widget(image); auto *box=widget.findChild<QCheckBox *>("m_adapt_to_dark_theme_cb");
|
||||
check(box && !box->isChecked(),"unchecked properties default");
|
||||
check(box->text()==QString::fromUtf8("Adapter l’image au thème sombre"),"checkbox wording");
|
||||
check(box->toolTip()==QString::fromUtf8("Adapte les couleurs à l’affichage sombre uniquement. L’image originale, les exports et les impressions restent inchangés."),"tooltip wording");
|
||||
check(box->text()==QStringLiteral("Adapt the picture to the dark theme"),"checkbox wording");
|
||||
check(box->toolTip()==QStringLiteral("Adapts the colors for the dark display only. The original picture, exports and printouts stay unchanged."),"tooltip wording");
|
||||
box->setChecked(true); check(image->adaptToDarkTheme(),"preview"); widget.reset(); check(!image->adaptToDarkTheme(),"reset cancels preview");
|
||||
diagram->undoStack().clear(); box->setChecked(true); widget.apply(); check(image->adaptToDarkTheme(),"apply");
|
||||
diagram->undoStack().undo(); check(!image->adaptToDarkTheme()&&!box->isChecked(),"undo updates checkbox");
|
||||
|
||||
@@ -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)
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include "utils/configprofile.h"
|
||||
#include "borderproperties.h"
|
||||
|
||||
class tst_configprofile : public QObject
|
||||
{
|
||||
@@ -17,6 +18,7 @@ private slots:
|
||||
QVERIFY(ConfigProfile::isLocalKey("diagrameditor/geometry"));
|
||||
QVERIFY(ConfigProfile::isLocalKey("elementeditor/state"));
|
||||
QVERIFY(ConfigProfile::isLocalKey("projects-recentfiles/file1"));
|
||||
QVERIFY(ConfigProfile::isLocalKey("dialoggeometry/ConfigDialog"));
|
||||
QVERIFY(ConfigProfile::isLocalKey(ConfigProfile::marker_key));
|
||||
QVERIFY(!ConfigProfile::isLocalKey("diagrameditor/Xgrid"));
|
||||
QVERIFY(!ConfigProfile::isLocalKey("shortcuts/mainwindow.fullscreen"));
|
||||
@@ -31,6 +33,7 @@ private slots:
|
||||
live.setValue("lang", "de");
|
||||
live.setValue("diagrameditor/geometry", QByteArray("xyz"));
|
||||
live.setValue("projects-recentfiles/file1", "/home/a/b.qet");
|
||||
live.setValue("dialoggeometry/ConfigDialog", QByteArray("xyz"));
|
||||
|
||||
QSettings file(path("profile1.conf"), QSettings::IniFormat);
|
||||
QCOMPARE(ConfigProfile::exportTo(live, file), 2);
|
||||
@@ -38,6 +41,7 @@ private slots:
|
||||
QCOMPARE(file.value("diagrameditor/Xgrid").toInt(), 7);
|
||||
QVERIFY(!file.contains("diagrameditor/geometry"));
|
||||
QVERIFY(!file.contains("projects-recentfiles/file1"));
|
||||
QVERIFY(!file.contains("dialoggeometry/ConfigDialog"));
|
||||
}
|
||||
|
||||
// Import replaces every setting, removes the ones the profile does not
|
||||
@@ -87,6 +91,134 @@ private slots:
|
||||
QCOMPARE(b.value(k), a.value(k));
|
||||
}
|
||||
|
||||
// Every key belongs to one part; keys nobody listed are Other.
|
||||
void partOfKeys()
|
||||
{
|
||||
using P = ConfigProfile::Part;
|
||||
QCOMPARE(ConfigProfile::partOf("shortcuts/mainwindow.fullscreen"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("toolbars/icon_size"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/toolbars/diagram"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/custom_toolbars/names"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/shortcut_bar/canvas"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/gestures/directions"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/mouse_gestures"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/context_toolbar"), P::Controls);
|
||||
QCOMPARE(ConfigProfile::partOf("diagramview/gestures"), P::Controls);
|
||||
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/defaultconductortype"), P::NewProject);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/defaultreportlabel"), P::NewProject);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/defaultxrefcoil"), P::NewProject);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/defaultguides/1/position"), P::NewProject);
|
||||
QCOMPARE(ConfigProfile::partOf("autonum/conductor/current"), P::NewProject);
|
||||
|
||||
QCOMPARE(ConfigProfile::partOf("elements-collections/custom-collection-path"), P::Folders);
|
||||
QCOMPARE(ConfigProfile::partOf("elements-collections/macros-path"), P::Folders);
|
||||
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/Xgrid"), P::Other);
|
||||
QCOMPARE(ConfigProfile::partOf("diagrameditor/auto_break_conductor"), P::Other);
|
||||
QCOMPARE(ConfigProfile::partOf("lang"), P::Other);
|
||||
QCOMPARE(ConfigProfile::partOf("customColors"), P::Other);
|
||||
}
|
||||
|
||||
// What the New project page writes for the folio size is the
|
||||
// NewProject part, whatever key names BorderProperties uses.
|
||||
void folioDefaultsAreNewProject()
|
||||
{
|
||||
QSettings live(path("live_border.conf"), QSettings::IniFormat);
|
||||
BorderProperties().toSettings(live, "diagrameditor/default");
|
||||
const QStringList keys = live.allKeys();
|
||||
QVERIFY(!keys.isEmpty());
|
||||
for (const QString &key : keys)
|
||||
QCOMPARE(ConfigProfile::partOf(key), ConfigProfile::Part::NewProject);
|
||||
}
|
||||
|
||||
// A file of some parts holds only their keys, never the folders, and
|
||||
// is format 2 so that an older QElectroTech refuses it.
|
||||
void exportSomeParts()
|
||||
{
|
||||
QSettings live(path("live4.conf"), QSettings::IniFormat);
|
||||
live.setValue("shortcuts/mainwindow.fullscreen", "F11");
|
||||
live.setValue("diagrameditor/defaultconductortype", "Multi");
|
||||
live.setValue("diagrameditor/Xgrid", 7);
|
||||
live.setValue("elements-collections/custom-collection-path", "/home/a/sym");
|
||||
|
||||
QSettings file(path("profile4.conf"), QSettings::IniFormat);
|
||||
QCOMPARE(ConfigProfile::exportTo(live, file, {ConfigProfile::Part::NewProject}), 1);
|
||||
QCOMPARE(file.value(ConfigProfile::marker_key).toInt(), 2);
|
||||
QVERIFY(ConfigProfile::isProfile(file));
|
||||
QCOMPARE(ConfigProfile::partsOf(file), QList<ConfigProfile::Part>{ConfigProfile::Part::NewProject});
|
||||
QVERIFY(file.contains("diagrameditor/defaultconductortype"));
|
||||
QVERIFY(!file.contains("shortcuts/mainwindow.fullscreen"));
|
||||
QVERIFY(!file.contains("diagrameditor/Xgrid"));
|
||||
QVERIFY(!file.contains("elements-collections/custom-collection-path"));
|
||||
|
||||
//Other without the two others: still no folders
|
||||
QSettings file2(path("profile4b.conf"), QSettings::IniFormat);
|
||||
ConfigProfile::exportTo(live, file2, {ConfigProfile::Part::Other});
|
||||
QVERIFY(file2.contains("diagrameditor/Xgrid"));
|
||||
QVERIFY(!file2.contains("elements-collections/custom-collection-path"));
|
||||
}
|
||||
|
||||
// Every choosable part is a complete profile: format 1, folders
|
||||
// included, readable by the QElectroTech that only knows format 1.
|
||||
void exportEveryPartIsComplete()
|
||||
{
|
||||
QSettings live(path("live5.conf"), QSettings::IniFormat);
|
||||
live.setValue("diagrameditor/Xgrid", 7);
|
||||
live.setValue("elements-collections/custom-collection-path", "/home/a/sym");
|
||||
|
||||
QSettings file(path("profile5.conf"), QSettings::IniFormat);
|
||||
ConfigProfile::exportTo(live, file, ConfigProfile::choosableParts());
|
||||
QCOMPARE(file.value(ConfigProfile::marker_key).toInt(), 1);
|
||||
QVERIFY(!file.contains(ConfigProfile::parts_key));
|
||||
QVERIFY(file.contains("elements-collections/custom-collection-path"));
|
||||
QCOMPARE(ConfigProfile::partsOf(file).size(), 4);
|
||||
}
|
||||
|
||||
// Loading some parts replaces those parts only, removes their keys the
|
||||
// file lacks, and keeps every other setting, folders included.
|
||||
void importSomePartsKeepsTheRest()
|
||||
{
|
||||
QSettings file(path("profile6.conf"), QSettings::IniFormat);
|
||||
file.setValue("diagrameditor/defaultconductortype", "Single");
|
||||
file.setValue("diagrameditor/Xgrid", 99); // not in its parts: ignored
|
||||
file.setValue(ConfigProfile::marker_key, 2);
|
||||
file.setValue(ConfigProfile::parts_key, "newproject");
|
||||
|
||||
QSettings live(path("live6.conf"), QSettings::IniFormat);
|
||||
live.setValue("diagrameditor/defaultconductortype", "Multi");
|
||||
live.setValue("autonum/folio/current", "mine");
|
||||
live.setValue("diagrameditor/Xgrid", 10);
|
||||
live.setValue("shortcuts/mainwindow.fullscreen", "F11");
|
||||
live.setValue("elements-collections/custom-collection-path", "/home/me/sym");
|
||||
|
||||
ConfigProfile::importFrom(file, live);
|
||||
QCOMPARE(live.value("diagrameditor/defaultconductortype").toString(), QString("Single"));
|
||||
QVERIFY(!live.contains("autonum/folio/current"));
|
||||
QCOMPARE(live.value("diagrameditor/Xgrid").toInt(), 10);
|
||||
QCOMPARE(live.value("shortcuts/mainwindow.fullscreen").toString(), QString("F11"));
|
||||
QCOMPARE(live.value("elements-collections/custom-collection-path").toString(),
|
||||
QString("/home/me/sym"));
|
||||
QVERIFY(!live.contains(ConfigProfile::parts_key));
|
||||
}
|
||||
|
||||
void restartOnlyWhenNeeded()
|
||||
{
|
||||
using P = ConfigProfile::Part;
|
||||
QVERIFY(!ConfigProfile::needsRestart({P::NewProject}));
|
||||
QVERIFY(ConfigProfile::needsRestart({P::NewProject, P::Controls}));
|
||||
QVERIFY(ConfigProfile::needsRestart({P::Other}));
|
||||
}
|
||||
|
||||
// A file of format 2 naming no known part is refused by the caller.
|
||||
void unknownPartsGiveNothing()
|
||||
{
|
||||
QSettings file(path("profile7.conf"), QSettings::IniFormat);
|
||||
file.setValue(ConfigProfile::marker_key, 2);
|
||||
file.setValue(ConfigProfile::parts_key, "colours");
|
||||
QVERIFY(ConfigProfile::partsOf(file).isEmpty());
|
||||
}
|
||||
|
||||
void plainFileIsNotAProfile()
|
||||
{
|
||||
QSettings other(path("other.conf"), QSettings::IniFormat);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
@@ -86,6 +87,11 @@ private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
//A scope of this test's own. Without an organization name,
|
||||
//QSettings on Windows has no registry key: it drops every
|
||||
//write and reads only defaults.
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("QElectroTech-tst_diagramtoolbarsettings"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("tst_diagramtoolbarsettings"));
|
||||
clearSettings();
|
||||
//A few of the diagram editor's commands, as it registers them
|
||||
for (const QString &id : {QStringLiteral("diagrameditor.copy"),
|
||||
|
||||
@@ -0,0 +1,502 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include "genericdevice/genericdevice.h"
|
||||
|
||||
using namespace GenericDevice;
|
||||
|
||||
namespace {
|
||||
|
||||
/// QElectroTech's default diagram font, at the sizes the wizard uses
|
||||
Fonts fonts()
|
||||
{
|
||||
QFont names(QStringLiteral("Liberation Sans"));
|
||||
names.setPointSizeF(7);
|
||||
QFont label(QStringLiteral("Liberation Sans"));
|
||||
label.setPointSizeF(9);
|
||||
return {names, label};
|
||||
}
|
||||
|
||||
QStringList names(const Slots &slot_list)
|
||||
{
|
||||
QStringList list;
|
||||
for (const Slot &s : slot_list)
|
||||
list << (s.kind == Slot::Terminal ? s.name : s.kind == Slot::Gap ? QStringLiteral("_")
|
||||
: QStringLiteral("~"));
|
||||
return list;
|
||||
}
|
||||
|
||||
Slots parse(const QString &pattern)
|
||||
{
|
||||
const PatternResult r = expandPattern(pattern);
|
||||
Q_ASSERT(r.ok());
|
||||
return r.list;
|
||||
}
|
||||
|
||||
/// The VSD of the scope document, §3
|
||||
Spec vsd()
|
||||
{
|
||||
Spec spec;
|
||||
spec.name = QStringLiteral("VSD");
|
||||
spec.label = QStringLiteral("-U1");
|
||||
spec.manufacturer = QStringLiteral("ACME");
|
||||
spec.reference = QStringLiteral("VF-400");
|
||||
spec.sides[Left] = parse(QStringLiteral("L1,L2,L3,,PE"));
|
||||
spec.sides[Right] = parse(QStringLiteral("U,V,W,,PE"));
|
||||
spec.sides[Bottom] = parse(QStringLiteral("DI{4},COM,+24V"));
|
||||
return spec;
|
||||
}
|
||||
|
||||
QString toText(const QDomElement &e)
|
||||
{
|
||||
QString out;
|
||||
QTextStream stream(&out);
|
||||
e.save(stream, 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class tst_genericdevice : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void expandPattern_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
QTest::addColumn<QStringList>("expected");
|
||||
QTest::newRow("plain") << "L1" << QStringList{"L1"};
|
||||
QTest::newRow("gap") << "L3,,PE" << QStringList{"L3", "_", "PE"};
|
||||
QTest::newRow("count") << "In{5}" << QStringList{"In1", "In2", "In3", "In4", "In5"};
|
||||
QTest::newRow("from zero") << "Q{z5}" << QStringList{"Q0", "Q1", "Q2", "Q3", "Q4"};
|
||||
QTest::newRow("range") << "X{3-8}" << QStringList{"X3", "X4", "X5", "X6", "X7", "X8"};
|
||||
QTest::newRow("prefix") << "X1:{4}" << QStringList{"X1:1", "X1:2", "X1:3", "X1:4"};
|
||||
QTest::newRow("suffix") << "{2}.1" << QStringList{"1.1", "2.1"};
|
||||
QTest::newRow("spaces") << " L1 , L2 " << QStringList{"L1", "L2"};
|
||||
QTest::newRow("empty") << "" << QStringList{};
|
||||
QTest::newRow("vsd bottom") << "DI{4},COM,+24V"
|
||||
<< QStringList{"DI1", "DI2", "DI3", "DI4", "COM", "+24V"};
|
||||
}
|
||||
void expandPattern()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
QFETCH(QStringList, expected);
|
||||
const PatternResult r = GenericDevice::expandPattern(pattern);
|
||||
QVERIFY2(r.ok(), qPrintable(r.error));
|
||||
QCOMPARE(names(r.list), expected);
|
||||
}
|
||||
|
||||
void patternErrors_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
QTest::newRow("two braces") << "X{2}{3}";
|
||||
QTest::newRow("unclosed") << "X{2";
|
||||
QTest::newRow("letters") << "X{a}";
|
||||
QTest::newRow("z with range") << "X{z1-3}";
|
||||
QTest::newRow("backwards range") << "X{5-3}";
|
||||
QTest::newRow("zero count") << "X{0}";
|
||||
QTest::newRow("over the cap") << "X{201}";
|
||||
}
|
||||
void patternErrors()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
const PatternResult r = GenericDevice::expandPattern(pattern);
|
||||
QVERIFY(!r.ok());
|
||||
QVERIFY(r.list.isEmpty());
|
||||
}
|
||||
|
||||
void capIsInclusive()
|
||||
{
|
||||
QCOMPARE(GenericDevice::expandPattern(QStringLiteral("X{200}")).list.size(), 200);
|
||||
}
|
||||
|
||||
void toPatternFoldsRuns()
|
||||
{
|
||||
bool ok = false;
|
||||
QCOMPARE(toPattern(parse(QStringLiteral("In{5},COM")), &ok), QStringLiteral("In{5},COM"));
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(toPattern(parse(QStringLiteral("Q{z4}"))), QStringLiteral("Q{z4}"));
|
||||
QCOMPARE(toPattern(parse(QStringLiteral("X{3-8}"))), QStringLiteral("X{3-8}"));
|
||||
QCOMPARE(toPattern(parse(QStringLiteral("L1,L2,L3,,PE"))), QStringLiteral("L{3},,PE"));
|
||||
//Two in a row stay as written
|
||||
QCOMPARE(toPattern(parse(QStringLiteral("A1,A2"))), QStringLiteral("A1,A2"));
|
||||
//Leading zeros are not numbers to fold
|
||||
QCOMPARE(toPattern(parse(QStringLiteral("01,02,03"))), QStringLiteral("01,02,03"));
|
||||
}
|
||||
|
||||
void toPatternRoundTrips_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
for (const char *p : {"L1,L2,L3,,PE", "DI{4},COM,+24V", "Q{z5}", "X1:{4}", ",A,,B,",
|
||||
"1,2,3,4,5", "X9,X10,X11", "A,A,A"})
|
||||
QTest::newRow(p) << QString::fromLatin1(p);
|
||||
}
|
||||
void toPatternRoundTrips()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
const Slots original = parse(pattern);
|
||||
QCOMPARE(names(parse(toPattern(original))), names(original));
|
||||
}
|
||||
|
||||
void toPatternFlagsWhatItCannotSay()
|
||||
{
|
||||
bool ok = true;
|
||||
Slots s{Slot::terminal(QStringLiteral("A,B"))};
|
||||
toPattern(s, &ok);
|
||||
QVERIFY(!ok);
|
||||
ok = true;
|
||||
toPattern({Slot::terminal(QStringLiteral("{x}"))}, &ok);
|
||||
QVERIFY(!ok);
|
||||
}
|
||||
|
||||
void applyPatternKeepsWhatTheTableHolds()
|
||||
{
|
||||
Slots current = parse(QStringLiteral("1,2"));
|
||||
current[0].function = QStringLiteral("OUT1");
|
||||
current[0].uuid = QUuid::createUuid();
|
||||
current[1].type = QStringLiteral("Inner");
|
||||
const Slots result = applyPattern(current, parse(QStringLiteral("A,,B,C")));
|
||||
QCOMPARE(names(result), (QStringList{"A", "_", "B", "C"}));
|
||||
QCOMPARE(result[0].function, QStringLiteral("OUT1"));
|
||||
QCOMPARE(result[0].uuid, current[0].uuid);
|
||||
QCOMPARE(result[2].type, QStringLiteral("Inner"));
|
||||
QVERIFY(result[3].uuid.isNull());
|
||||
}
|
||||
|
||||
void groupEvery_data()
|
||||
{
|
||||
QTest::addColumn<int>("count");
|
||||
QTest::addColumn<int>("n");
|
||||
QTest::addColumn<QString>("expected");
|
||||
QTest::newRow("9 by 4") << 9 << 4 << "1 2 3 4 _ 5 6 7 8 _ 9";
|
||||
QTest::newRow("8 by 4") << 8 << 4 << "1 2 3 4 _ 5 6 7 8";
|
||||
QTest::newRow("none") << 3 << 0 << "1 2 3";
|
||||
QTest::newRow("by 1") << 3 << 1 << "1 _ 2 _ 3";
|
||||
QTest::newRow("n = count") << 4 << 4 << "1 2 3 4";
|
||||
QTest::newRow("n > count") << 3 << 8 << "1 2 3";
|
||||
}
|
||||
void groupEvery()
|
||||
{
|
||||
QFETCH(int, count);
|
||||
QFETCH(int, n);
|
||||
QFETCH(QString, expected);
|
||||
const Slots s = parse(QStringLiteral("{%1}").arg(count));
|
||||
QCOMPARE(names(GenericDevice::groupEvery(s, n)).join(' '), expected);
|
||||
}
|
||||
|
||||
void groupEveryReplacesHandGaps()
|
||||
{
|
||||
QCOMPARE(names(GenericDevice::groupEvery(parse(QStringLiteral("1,,2,3,,,4")), 2)).join(' '),
|
||||
QStringLiteral("1 2 _ 3 4"));
|
||||
}
|
||||
|
||||
void resizeKeepsTypedNames()
|
||||
{
|
||||
const Slots s = parse(QStringLiteral("L1,L2,,PE"));
|
||||
QCOMPARE(names(resize(s, 5, 0)).join(' '), QStringLiteral("L1 L2 _ PE "));
|
||||
QCOMPARE(names(resize(s, 2, 0)).join(' '), QStringLiteral("L1 L2"));
|
||||
QCOMPARE(names(resize(s, 0, 0)), QStringList());
|
||||
QCOMPARE(names(resize(s, 5, 2)).join(' '), QStringLiteral("L1 L2 _ PE _ "));
|
||||
}
|
||||
|
||||
void lineUpGroups()
|
||||
{
|
||||
Slots a = parse(QStringLiteral("L1,L2,L3,N,,PE"));
|
||||
Slots b = parse(QStringLiteral("U,V,W,,PE"));
|
||||
GenericDevice::lineUpGroups(a, b);
|
||||
QCOMPARE(names(a).join(' '), QStringLiteral("L1 L2 L3 N _ PE"));
|
||||
QCOMPARE(names(b).join(' '), QStringLiteral("U V W ~ _ PE"));
|
||||
QCOMPARE(a.size(), b.size());
|
||||
}
|
||||
|
||||
void lineUpLeavesEqualAndUngroupedSidesAlone()
|
||||
{
|
||||
Slots a = parse(QStringLiteral("L1,L2,L3,,PE"));
|
||||
Slots b = parse(QStringLiteral("U,V,W,,PE"));
|
||||
GenericDevice::lineUpGroups(a, b);
|
||||
QCOMPARE(names(b).join(' '), QStringLiteral("U V W _ PE"));
|
||||
|
||||
//No gap on one side: nothing faces the other's groups
|
||||
Slots c = parse(QStringLiteral("1,2,3,4,5"));
|
||||
Slots d = parse(QStringLiteral("A,,B"));
|
||||
GenericDevice::lineUpGroups(c, d);
|
||||
QCOMPARE(names(c).join(' '), QStringLiteral("1 2 3 4 5"));
|
||||
QCOMPARE(names(d).join(' '), QStringLiteral("A _ B"));
|
||||
|
||||
//More groups on one side: the extra ones simply follow
|
||||
Slots e = parse(QStringLiteral("A,,B,B2,,C"));
|
||||
Slots f = parse(QStringLiteral("X,X2,,Y"));
|
||||
GenericDevice::lineUpGroups(e, f);
|
||||
QCOMPARE(names(e).join(' '), QStringLiteral("A ~ _ B B2 _ C"));
|
||||
QCOMPARE(names(f).join(' '), QStringLiteral("X X2 _ Y"));
|
||||
}
|
||||
|
||||
void numberPinsCounterClockwise()
|
||||
{
|
||||
Spec spec;
|
||||
spec.sides[Left] = resize({}, 3, 0);
|
||||
spec.sides[Bottom] = {Slot::terminal({}), Slot::gap(), Slot::terminal({})};
|
||||
spec.sides[Right] = resize({}, 2, 0);
|
||||
spec.sides[Top] = resize({}, 2, 0);
|
||||
numberPins(spec);
|
||||
QCOMPARE(names(spec.sides[Left]).join(' '), QStringLiteral("1 2 3"));
|
||||
QCOMPARE(names(spec.sides[Bottom]).join(' '), QStringLiteral("4 _ 5"));
|
||||
QCOMPARE(names(spec.sides[Right]).join(' '), QStringLiteral("7 6"));
|
||||
QCOMPARE(names(spec.sides[Top]).join(' '), QStringLiteral("9 8"));
|
||||
QVERIFY(hasNumberedPins(spec));
|
||||
spec.sides[Top][0].name = QStringLiteral("PE");
|
||||
QVERIFY(!hasNumberedPins(spec));
|
||||
}
|
||||
|
||||
void nameUnnamedLeavesTypedNames()
|
||||
{
|
||||
Spec spec;
|
||||
spec.sides[Left] = {Slot::terminal(QStringLiteral("L1")), Slot::terminal({}), Slot::gap(),
|
||||
Slot::terminal({})};
|
||||
nameUnnamed(spec);
|
||||
QCOMPARE(names(spec.sides[Left]).join(' '), QStringLiteral("L1 2 _ 3"));
|
||||
}
|
||||
|
||||
void pasteTabsAndCommasReadAlike()
|
||||
{
|
||||
const QString tabs = QStringLiteral(
|
||||
"side\tname\tfunction\n"
|
||||
"L\tL1\n" "L\tL2\n" "L\tL3\n" "L\t\n" "L\tPE\n"
|
||||
"right\tU\tmotor\n" "R\tV\n" "R\tW\n" "R\t\n" "R\tPE\n"
|
||||
"bottom\tDI1\t\tInner\n");
|
||||
QString commas = tabs;
|
||||
commas.replace('\t', ',');
|
||||
for (const QString &text : {tabs, commas})
|
||||
{
|
||||
const PasteResult r = parsePastedTable(text);
|
||||
QVERIFY2(r.bad_rows.isEmpty(), qPrintable(r.bad_rows.join('|')));
|
||||
QCOMPARE(r.rows, 11);
|
||||
QCOMPARE(names(r.sides[Left]).join(' '), QStringLiteral("L1 L2 L3 _ PE"));
|
||||
QCOMPARE(r.sides[Right][0].function, QStringLiteral("motor"));
|
||||
QCOMPARE(r.sides[Bottom][0].type, QStringLiteral("Inner"));
|
||||
QVERIFY(r.has_side[Left] && r.has_side[Right] && r.has_side[Bottom]);
|
||||
QVERIFY(!r.has_side[Top]);
|
||||
}
|
||||
}
|
||||
|
||||
void pasteListsBadRows()
|
||||
{
|
||||
const PasteResult r = parsePastedTable(QStringLiteral(
|
||||
"left,A\r\nmiddle,B\r\ntop,C,,Sideways\r\n\r\nbottom,D,f,Generic,extra\r\nT,E"));
|
||||
QCOMPARE(r.rows, 2);
|
||||
QCOMPARE(r.bad_rows, (QStringList{"middle,B", "top,C,,Sideways", "bottom,D,f,Generic,extra"}));
|
||||
QCOMPARE(names(r.sides[Top]), QStringList{"E"});
|
||||
}
|
||||
|
||||
/// Scope §3: the VSD body is 160 × 140 with the terminals packed
|
||||
/// from the top and clear of the corners
|
||||
void vsdSize()
|
||||
{
|
||||
const Layout l = layout(vsd(), fonts());
|
||||
QCOMPARE(l.body_width, 160);
|
||||
QCOMPARE(l.body_height, 140);
|
||||
QCOMPARE(l.terminals.size(), 14);
|
||||
QCOMPARE(l.dividers.size(), 2);
|
||||
QVERIFY(l.warnings.join(' ').contains(QStringLiteral("PE")));
|
||||
}
|
||||
|
||||
void everythingOnTheGrid()
|
||||
{
|
||||
Spec spec = vsd();
|
||||
spec.sides[Top] = parse(QStringLiteral("X{3}"));
|
||||
spec.extra_width = 3;
|
||||
QDomDocument doc;
|
||||
const QDomElement def = toDefinition(spec, fonts(), doc);
|
||||
for (const char *a : {"width", "height", "hotspot_x", "hotspot_y"})
|
||||
QCOMPARE(def.attribute(a).toInt() % Grid, 0);
|
||||
const QDomNodeList terminals = def.elementsByTagName(QStringLiteral("terminal"));
|
||||
QCOMPARE(terminals.size(), 17);
|
||||
for (int i = 0 ; i < terminals.size() ; ++i) {
|
||||
const QDomElement t = terminals.at(i).toElement();
|
||||
QCOMPARE(qRound(t.attribute("x").toDouble()) % Grid, 0);
|
||||
QCOMPARE(qRound(t.attribute("y").toDouble()) % Grid, 0);
|
||||
QVERIFY(!QUuid(t.attribute("uuid")).isNull());
|
||||
}
|
||||
}
|
||||
|
||||
void lineUpPutsPeOnOneRow()
|
||||
{
|
||||
Spec spec;
|
||||
spec.sides[Left] = parse(QStringLiteral("L1,L2,L3,N,,PE"));
|
||||
spec.sides[Right] = parse(QStringLiteral("U,V,W,,PE"));
|
||||
Layout l = layout(spec, fonts());
|
||||
QList<qreal> pe;
|
||||
for (const auto &t : l.terminals)
|
||||
if (t.slot.name == QLatin1String("PE")) pe << t.point.y();
|
||||
QCOMPARE(pe.size(), 2);
|
||||
QCOMPARE(pe[0], pe[1]);
|
||||
//Both gaps marked, the padding not
|
||||
QCOMPARE(l.dividers.size(), 2);
|
||||
QCOMPARE(l.dividers[0].y1(), l.dividers[1].y1());
|
||||
|
||||
spec.line_up = false;
|
||||
l = layout(spec, fonts());
|
||||
pe.clear();
|
||||
for (const auto &t : l.terminals)
|
||||
if (t.slot.name == QLatin1String("PE")) pe << t.point.y();
|
||||
QVERIFY(pe[0] != pe[1]);
|
||||
|
||||
spec.mark_gaps = false;
|
||||
QVERIFY(layout(spec, fonts()).dividers.isEmpty());
|
||||
}
|
||||
|
||||
void edgeCases()
|
||||
{
|
||||
Spec empty;
|
||||
Layout l = layout(empty, fonts());
|
||||
//No terminals: the minimum, or as wide as the label needs
|
||||
QVERIFY(l.body_width >= MinWidth);
|
||||
QCOMPARE(l.body_width % Grid, 0);
|
||||
QCOMPARE(l.body_height, MinHeight);
|
||||
QVERIFY(l.terminals.isEmpty());
|
||||
|
||||
Spec one;
|
||||
one.sides[Top] = parse(QStringLiteral("A"));
|
||||
l = layout(one, fonts());
|
||||
QCOMPARE(l.terminals.size(), 1);
|
||||
QCOMPARE(l.terminals[0].point.y(), qreal(-Stub));
|
||||
|
||||
Spec many;
|
||||
many.sides[Left] = parse(QStringLiteral("{50}"));
|
||||
l = layout(many, fonts());
|
||||
QCOMPARE(l.terminals.size(), 50);
|
||||
QVERIFY(l.body_height >= 50 * many.pitch);
|
||||
}
|
||||
|
||||
void extraSizeOnlyAdds()
|
||||
{
|
||||
Spec spec = vsd();
|
||||
const Layout base = layout(spec, fonts());
|
||||
spec.extra_width = 2;
|
||||
spec.extra_height = 1;
|
||||
const Layout grown = layout(spec, fonts());
|
||||
QCOMPARE(grown.body_width, base.body_width + 20);
|
||||
QCOMPARE(grown.body_height, base.body_height + 10);
|
||||
spec.extra_width = -5;
|
||||
QCOMPARE(layout(spec, fonts()).body_width, base.body_width);
|
||||
}
|
||||
|
||||
void definitionRoundTrip()
|
||||
{
|
||||
Spec spec = vsd();
|
||||
spec.sides[Right][0].function = QStringLiteral("motor");
|
||||
spec.sides[Left][1].type = QStringLiteral("Inner");
|
||||
spec.sides[Top] = parse(QStringLiteral("A,B"));
|
||||
spec.extra_width = 2;
|
||||
spec.extra_height = 1;
|
||||
spec.pitch = 30;
|
||||
|
||||
QDomDocument doc;
|
||||
const QDomElement def = toDefinition(spec, fonts(), doc, QStringLiteral("0.100.0"));
|
||||
Spec back;
|
||||
QVERIFY(fromDefinition(def, fonts(), &back));
|
||||
|
||||
QCOMPARE(back.name, spec.name);
|
||||
QCOMPARE(back.label, spec.label);
|
||||
QCOMPARE(back.manufacturer, spec.manufacturer);
|
||||
QCOMPARE(back.reference, spec.reference);
|
||||
QCOMPARE(back.show_reference, true);
|
||||
QCOMPARE(back.pitch, 30);
|
||||
QCOMPARE(back.extra_width, 2);
|
||||
QCOMPARE(back.extra_height, 1);
|
||||
QVERIFY(back.mark_gaps);
|
||||
QVERIFY(back.line_up);
|
||||
for (int s = 0 ; s < SideCount ; ++s)
|
||||
QCOMPARE(names(back.sides[s]), names(spec.sides[s]));
|
||||
QCOMPARE(back.sides[Right][0].function, QStringLiteral("motor"));
|
||||
QCOMPARE(back.sides[Left][1].type, QStringLiteral("Inner"));
|
||||
|
||||
//Written again, the terminals keep their uuids and positions
|
||||
QDomDocument doc2;
|
||||
const QDomElement def2 = toDefinition(back, fonts(), doc2, QStringLiteral("0.100.0"));
|
||||
const QDomNodeList t1 = def.elementsByTagName(QStringLiteral("terminal"));
|
||||
const QDomNodeList t2 = def2.elementsByTagName(QStringLiteral("terminal"));
|
||||
QCOMPARE(t2.size(), t1.size());
|
||||
for (int i = 0 ; i < t1.size() ; ++i)
|
||||
for (const char *a : {"x", "y", "uuid", "name", "orientation", "type"})
|
||||
QCOMPARE(t2.at(i).toElement().attribute(a), t1.at(i).toElement().attribute(a));
|
||||
for (const char *a : {"width", "height", "hotspot_x", "hotspot_y"})
|
||||
QCOMPARE(def2.attribute(a), def.attribute(a));
|
||||
}
|
||||
|
||||
void roundTripWithPaddingAndNoMarks()
|
||||
{
|
||||
Spec spec;
|
||||
spec.name = QStringLiteral("X");
|
||||
spec.sides[Left] = parse(QStringLiteral("L1,L2,L3,N,,PE"));
|
||||
spec.sides[Right] = parse(QStringLiteral("U,V,W,,PE"));
|
||||
for (bool marks : {true, false})
|
||||
{
|
||||
spec.mark_gaps = marks;
|
||||
QDomDocument doc;
|
||||
const QDomElement def = toDefinition(spec, fonts(), doc);
|
||||
Spec back;
|
||||
QVERIFY(fromDefinition(def, fonts(), &back));
|
||||
QDomDocument doc2;
|
||||
const QDomElement def2 = toDefinition(back, fonts(), doc2);
|
||||
const QDomNodeList t1 = def.elementsByTagName(QStringLiteral("terminal"));
|
||||
const QDomNodeList t2 = def2.elementsByTagName(QStringLiteral("terminal"));
|
||||
QCOMPARE(t2.size(), t1.size());
|
||||
for (int i = 0 ; i < t1.size() ; ++i)
|
||||
QCOMPARE(t2.at(i).toElement().attribute("y"), t1.at(i).toElement().attribute("y"));
|
||||
QCOMPARE(def2.elementsByTagName("line").size(), def.elementsByTagName("line").size());
|
||||
}
|
||||
}
|
||||
|
||||
void foreignDefinitionsAreRefused()
|
||||
{
|
||||
QDomDocument doc;
|
||||
QDomElement def = toDefinition(vsd(), fonts(), doc);
|
||||
Spec back;
|
||||
QVERIFY(fromDefinition(def, fonts(), &back));
|
||||
|
||||
//A circle drawn on it in the symbol editor
|
||||
QDomElement description = def.firstChildElement(QStringLiteral("description"));
|
||||
QDomElement extra = doc.createElement(QStringLiteral("ellipse"));
|
||||
description.appendChild(extra);
|
||||
QVERIFY(!fromDefinition(def, fonts(), &back));
|
||||
description.removeChild(extra);
|
||||
|
||||
//A terminal moved off its edge
|
||||
QDomElement t = description.firstChildElement(QStringLiteral("terminal"));
|
||||
t.setAttribute(QStringLiteral("x"), 5);
|
||||
QVERIFY(!fromDefinition(def, fonts(), &back));
|
||||
}
|
||||
|
||||
/// The output is stable apart from the uuids
|
||||
void definitionIsDeterministic()
|
||||
{
|
||||
Spec spec = vsd();
|
||||
for (Slots &side : spec.sides)
|
||||
for (Slot &s : side)
|
||||
s.uuid = QUuid::createUuid();
|
||||
QDomDocument a, b;
|
||||
QString one = toText(toDefinition(spec, fonts(), a));
|
||||
QString two = toText(toDefinition(spec, fonts(), b));
|
||||
static const QRegularExpression uuid(QStringLiteral("uuid=\"\\{[^}]+\\}\""));
|
||||
static const QRegularExpression part_uuid(QStringLiteral("<uuid uuid=\"[^\"]+\"/>"));
|
||||
//Only parts and the definition get fresh uuids; the terminals keep theirs
|
||||
QCOMPARE(one.count(QStringLiteral("<terminal ")), 14);
|
||||
for (QString *s : {&one, &two}) {
|
||||
const QStringList lines = s->split('\n');
|
||||
QStringList kept;
|
||||
for (QString line : lines) {
|
||||
if (!line.contains(QLatin1String("<terminal")))
|
||||
line.replace(uuid, QStringLiteral("uuid=\"\""));
|
||||
kept << line;
|
||||
}
|
||||
*s = kept.join('\n');
|
||||
}
|
||||
QCOMPARE(one, two);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_genericdevice)
|
||||
|
||||
#include "tst_genericdevice.moc"
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QDropEvent>
|
||||
#include <QListWidget>
|
||||
#include <QMainWindow>
|
||||
@@ -64,6 +65,11 @@ private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
//A scope of this test's own. Without an organization name,
|
||||
//QSettings on Windows has no registry key: it drops every
|
||||
//write and reads only defaults.
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("QElectroTech-tst_gesturesettings"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("tst_gesturesettings"));
|
||||
QSettings().remove(QStringLiteral("diagrameditor"));
|
||||
addCommand(QStringLiteral("diagrameditor.copy"), QStringLiteral("Copier"));
|
||||
addCommand(QStringLiteral("diagrameditor.cut"), QStringLiteral("Couper"));
|
||||
|
||||
@@ -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,254 @@
|
||||
/*
|
||||
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 "imagedrop.h"
|
||||
|
||||
#include <QtEndian>
|
||||
#include <QtTest>
|
||||
|
||||
#include <QImage>
|
||||
#include <QMimeData>
|
||||
#include <QTemporaryDir>
|
||||
#include <QUrl>
|
||||
|
||||
/**
|
||||
Picture files dropped on a folio: which files are taken, the limits a
|
||||
file must meet before it is embedded in the project, and where the
|
||||
pictures of one drop land.
|
||||
*/
|
||||
class tst_imagedrop : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
QString path(const QString &name) const { return m_dir.filePath(name); }
|
||||
|
||||
static QMimeData *urls(const QStringList &paths)
|
||||
{
|
||||
auto *mime = new QMimeData();
|
||||
QList<QUrl> list;
|
||||
for (const QString &p : paths)
|
||||
list << (p.startsWith("http") ? QUrl(p) : QUrl::fromLocalFile(p));
|
||||
mime->setUrls(list);
|
||||
return mime;
|
||||
}
|
||||
|
||||
// A BMP header announcing width x height pixels, without the pixels.
|
||||
static QByteArray bmpHeader(qint32 width, qint32 height)
|
||||
{
|
||||
QByteArray b(54, '\0');
|
||||
b[0] = 'B'; b[1] = 'M';
|
||||
qToLittleEndian<quint32>(54, b.data() + 2);
|
||||
qToLittleEndian<quint32>(54, b.data() + 10);
|
||||
qToLittleEndian<quint32>(40, b.data() + 14);
|
||||
qToLittleEndian<qint32>(width, b.data() + 18);
|
||||
qToLittleEndian<qint32>(height, b.data() + 22);
|
||||
qToLittleEndian<quint16>(1, b.data() + 26);
|
||||
qToLittleEndian<quint16>(24, b.data() + 28);
|
||||
return b;
|
||||
}
|
||||
|
||||
static void write(const QString &p, const QByteArray &data)
|
||||
{
|
||||
QFile f(p);
|
||||
QVERIFY(f.open(QIODevice::WriteOnly));
|
||||
f.write(data);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QImage img(40, 20, QImage::Format_ARGB32);
|
||||
img.fill(Qt::red);
|
||||
QVERIFY(img.save(path("a.png")));
|
||||
QVERIFY(img.save(path("b.JPG"), "JPG"));
|
||||
write(path("plan.qet"), "<project/>");
|
||||
write(path("fake.png"), "this is not a picture");
|
||||
write(path("bomb.bmp"), bmpHeader(10000, 10000));
|
||||
QFile big(path("big.png"));
|
||||
QVERIFY(big.open(QIODevice::WriteOnly));
|
||||
QVERIFY(big.resize(ImageDrop::maxFileBytes + 1));
|
||||
}
|
||||
|
||||
// Only local files with a picture suffix are taken, in drop order and
|
||||
// whatever the case of the suffix.
|
||||
void picturesAreTakenInOrder()
|
||||
{
|
||||
std::unique_ptr<QMimeData> mime(urls({path("b.JPG"), path("plan.qet"), path("a.png"),
|
||||
"https://example.org/remote.png"}));
|
||||
QCOMPARE(ImageDrop::imageFiles(mime.get()), QStringList({path("b.JPG"), path("a.png")}));
|
||||
QVERIFY(!ImageDrop::hasOnlyOtherUrls(mime.get()));
|
||||
}
|
||||
|
||||
// A drop holding only a project is left to the main window, which
|
||||
// opens it; plain text is not a URL drop at all.
|
||||
void otherFilesAreLeftAlone()
|
||||
{
|
||||
std::unique_ptr<QMimeData> project(urls({path("plan.qet")}));
|
||||
QVERIFY(ImageDrop::imageFiles(project.get()).isEmpty());
|
||||
QVERIFY(ImageDrop::hasOnlyOtherUrls(project.get()));
|
||||
|
||||
QMimeData text;
|
||||
text.setText("K1");
|
||||
QVERIFY(!ImageDrop::hasOnlyOtherUrls(&text));
|
||||
QVERIFY(ImageDrop::imageFiles(&text).isEmpty());
|
||||
QVERIFY(ImageDrop::imageFiles(nullptr).isEmpty());
|
||||
}
|
||||
|
||||
void validPictureLoads()
|
||||
{
|
||||
QString error;
|
||||
const QImage image = ImageDrop::load(path("a.png"), &error);
|
||||
QCOMPARE(image.size(), QSize(40, 20));
|
||||
QVERIFY(error.isEmpty());
|
||||
}
|
||||
|
||||
void unusableFilesAreRefusedWithAReason_data()
|
||||
{
|
||||
QTest::addColumn<QString>("file");
|
||||
QTest::addColumn<QString>("reason");
|
||||
QTest::newRow("missing") << path("missing.png") << "file";
|
||||
QTest::newRow("directory") << m_dir.path() << "file";
|
||||
QTest::newRow("not a picture") << path("fake.png") << "read";
|
||||
QTest::newRow("over 10 MB") << path("big.png") << "10 MB";
|
||||
// refused on its header, before the 300 MB of pixels are allocated
|
||||
QTest::newRow("too many pixels") << path("bomb.bmp") << "pixels";
|
||||
}
|
||||
|
||||
void unusableFilesAreRefusedWithAReason()
|
||||
{
|
||||
QFETCH(QString, file);
|
||||
QFETCH(QString, reason);
|
||||
QString error;
|
||||
QVERIFY(ImageDrop::load(file, &error).isNull());
|
||||
QVERIFY2(error.contains(reason), qPrintable(error));
|
||||
}
|
||||
|
||||
// A picture too large for the folio is scaled down until it leaves 20 %
|
||||
// of the frame free on every side, i.e. into 60 % of its width and
|
||||
// height; a picture that already fits keeps its size.
|
||||
void largePicturesLeaveAMarginToTheFrame_data()
|
||||
{
|
||||
QTest::addColumn<QSizeF>("size");
|
||||
QTest::addColumn<qreal>("scale");
|
||||
// frame: 1000 x 800, so the inner frame is 600 x 480
|
||||
QTest::newRow("small, kept") << QSizeF(100, 50) << 1.0;
|
||||
QTest::newRow("exactly the inner frame") << QSizeF(600, 480) << 1.0;
|
||||
QTest::newRow("wide") << QSizeF(3000, 1000) << 0.2;
|
||||
QTest::newRow("tall photo") << QSizeF(3024, 4032) << 480.0 / 4032;
|
||||
}
|
||||
|
||||
void largePicturesLeaveAMarginToTheFrame()
|
||||
{
|
||||
QFETCH(QSizeF, size);
|
||||
QFETCH(qreal, scale);
|
||||
const QRectF frame(50, 30, 1000, 800);
|
||||
QCOMPARE(ImageDrop::innerFrame(frame), QRectF(250, 190, 600, 480));
|
||||
QCOMPARE(ImageDrop::fitScale(size, frame), scale);
|
||||
const QSizeF scaled = size * ImageDrop::fitScale(size, frame);
|
||||
QVERIFY(scaled.width() <= 600.0001 && scaled.height() <= 480.0001);
|
||||
}
|
||||
|
||||
void noFrameMeansNoScaling()
|
||||
{
|
||||
QCOMPARE(ImageDrop::fitScale(QSizeF(4000, 3000), QRectF()), 1.0);
|
||||
}
|
||||
|
||||
// A picture dropped near an edge is moved back inside, by the least
|
||||
// amount; one larger than the area is centred on it.
|
||||
void picturesAreKeptInsideTheArea_data()
|
||||
{
|
||||
QTest::addColumn<QRectF>("picture");
|
||||
QTest::addColumn<QRectF>("expected");
|
||||
const QRectF inside(300, 300, 100, 50);
|
||||
QTest::newRow("already inside") << inside << inside;
|
||||
QTest::newRow("over the top edge") << QRectF(300, -20, 100, 50) << QRectF(300, 0, 100, 50);
|
||||
QTest::newRow("past the right and bottom") << QRectF(980, 790, 100, 50)
|
||||
<< QRectF(900, 750, 100, 50);
|
||||
QTest::newRow("wider than the area") << QRectF(-50, 100, 1200, 50)
|
||||
<< QRectF(-100, 100, 1200, 50);
|
||||
}
|
||||
|
||||
void picturesAreKeptInsideTheArea()
|
||||
{
|
||||
QFETCH(QRectF, picture);
|
||||
QFETCH(QRectF, expected);
|
||||
QCOMPARE(ImageDrop::keepInside(picture, QRectF(0, 0, 1000, 800)), expected);
|
||||
}
|
||||
|
||||
// Several pictures dropped together are spread side by side: a
|
||||
// roughly square grid over the area, each shrunk into its cell.
|
||||
void severalPicturesAreSpreadInAGrid_data()
|
||||
{
|
||||
QTest::addColumn<int>("count");
|
||||
QTest::addColumn<int>("columns");
|
||||
QTest::addColumn<int>("rows");
|
||||
QTest::newRow("2") << 2 << 2 << 1;
|
||||
QTest::newRow("3") << 3 << 2 << 2;
|
||||
QTest::newRow("4") << 4 << 2 << 2;
|
||||
QTest::newRow("5") << 5 << 3 << 2;
|
||||
QTest::newRow("9") << 9 << 3 << 3;
|
||||
}
|
||||
|
||||
void severalPicturesAreSpreadInAGrid()
|
||||
{
|
||||
QFETCH(int, count);
|
||||
QFETCH(int, columns);
|
||||
QFETCH(int, rows);
|
||||
const QRectF area(100, 50, 600, 480);
|
||||
const QList<QSizeF> sizes(count, QSizeF(3024, 4032));
|
||||
const QList<QRectF> rects = ImageDrop::gridLayout(sizes, area);
|
||||
QCOMPARE(rects.size(), count);
|
||||
|
||||
const QSizeF cell(area.width() / columns, area.height() / rows);
|
||||
for (int i = 0 ; i < count ; ++i)
|
||||
{
|
||||
const QRectF r = rects.at(i);
|
||||
// inside the area, proportions kept
|
||||
QVERIFY(area.contains(r));
|
||||
QVERIFY(qAbs(r.width() / r.height() - 3024.0 / 4032.0) < 1e-6);
|
||||
// centred in its own cell, row by row
|
||||
const QPointF centre = area.topLeft() + QPointF(cell.width() * (i % columns + 0.5),
|
||||
cell.height() * (i / columns + 0.5));
|
||||
QVERIFY(qAbs(r.center().x() - centre.x()) < 1e-6);
|
||||
QVERIFY(qAbs(r.center().y() - centre.y()) < 1e-6);
|
||||
// and overlapping no other
|
||||
for (int j = i + 1 ; j < count ; ++j)
|
||||
QVERIFY2(!r.intersects(rects.at(j)), qPrintable(QString("%1 and %2").arg(i).arg(j)));
|
||||
}
|
||||
}
|
||||
|
||||
void smallPicturesAreNotEnlargedInTheGrid()
|
||||
{
|
||||
const QList<QRectF> rects = ImageDrop::gridLayout({QSizeF(40, 20), QSizeF(30, 30)},
|
||||
QRectF(0, 0, 600, 480));
|
||||
QCOMPARE(rects.at(0).size(), QSizeF(40, 20));
|
||||
QCOMPARE(rects.at(1).size(), QSizeF(30, 30));
|
||||
QCOMPARE(rects.at(0).center(), QPointF(150, 240));
|
||||
QCOMPARE(rects.at(1).center(), QPointF(450, 240));
|
||||
}
|
||||
|
||||
void emptyDropHasNoLayout()
|
||||
{
|
||||
QVERIFY(ImageDrop::gridLayout({}, QRectF(0, 0, 600, 480)).isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_imagedrop)
|
||||
#include "tst_imagedrop.moc"
|
||||
@@ -191,6 +191,48 @@ private slots:
|
||||
r.value(QStringLiteral("v")).toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
Mirroring a symbol twice the same way puts it back where it was,
|
||||
for every symbol of the project, including those that do not sit
|
||||
on the grid. Before, the second mirror snapped such a symbol onto
|
||||
the grid, a few pixels away. Which symbol mirrorBackAndSave()
|
||||
picks changes from run to run, so this checks them all.
|
||||
*/
|
||||
void mirrorTwiceComesBack_data()
|
||||
{
|
||||
QTest::addColumn<bool>("vertical");
|
||||
QTest::newRow("horizontal") << false;
|
||||
QTest::newRow("vertical") << true;
|
||||
}
|
||||
|
||||
void mirrorTwiceComesBack()
|
||||
{
|
||||
QFETCH(bool, vertical);
|
||||
const QJsonObject r = run(script(QStringLiteral(
|
||||
"var out = {count: 0, moved: []};\n"
|
||||
"for (var f = 0; f < qet.folioCount(); f++) {\n"
|
||||
" var els = qet.elementUuids(f);\n"
|
||||
" for (var e = 0; e < els.length; e++) {\n"
|
||||
" var before = JSON.stringify(terms(f, els[e]));\n"
|
||||
" qet.mirrorElement(f, els[e], %1);\n"
|
||||
" qet.mirrorElement(f, els[e], %1);\n"
|
||||
" var after = JSON.stringify(terms(f, els[e]));\n"
|
||||
" out.count++;\n"
|
||||
" if (after !== before) out.moved.push(els[e] + ' ' + before + ' -> ' + after);\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"qet.log('PROBE ' + JSON.stringify(out));\n")
|
||||
.arg(vertical ? QStringLiteral("true") : QStringLiteral("false"))),
|
||||
QStringLiteral(QET_EXAMPLES_DIR "/perceuse.qet"));
|
||||
|
||||
QVERIFY2(!r.isEmpty(), "the script logged nothing");
|
||||
QVERIFY(r.value(QStringLiteral("count")).toInt() > 0);
|
||||
const QJsonArray moved = r.value(QStringLiteral("moved")).toArray();
|
||||
for (const QJsonValue &m : moved)
|
||||
qWarning("moved: %s", qPrintable(m.toString()));
|
||||
QCOMPARE(moved.size(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
On a turned symbol a mirror of the folio is the other mirror of the
|
||||
symbol itself, and its rotation does not change: a label kept
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
// Symbols copied in old versions can share one uuid on a folio. Since every
|
||||
// terminal got a uuid (#1118), the wires on them were saved by symbol uuid
|
||||
// and reopened on the first symbol carrying it (#1408).
|
||||
//
|
||||
// Runs the real binary on examples/Habitat-Schemas_developpes.qet, whose
|
||||
// folio 1 has three lamps L1, L2, L3, and compares the nets QElectroTech
|
||||
// exports with those of the unedited example:
|
||||
// - the lamps given one uuid, saved twice;
|
||||
// - fixtures/shared_symbol_uuid_0200.qet: that folio as a build with #1118
|
||||
// saved it, its wires naming the lamps by the shared uuid.
|
||||
namespace {
|
||||
|
||||
const QString example = QStringLiteral(QET_EXAMPLES_DIR "/Habitat-Schemas_developpes.qet");
|
||||
|
||||
QDomDocument load(const QString &path)
|
||||
{
|
||||
QDomDocument doc;
|
||||
QFile file(path);
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
doc.setContent(&file);
|
||||
return doc;
|
||||
}
|
||||
|
||||
QDomElement firstDiagram(const QDomDocument &doc)
|
||||
{
|
||||
return doc.documentElement().firstChildElement(QStringLiteral("diagram"));
|
||||
}
|
||||
|
||||
QList<QDomElement> childElements(const QDomElement &diagram, const QString &block,
|
||||
const QString &tag)
|
||||
{
|
||||
QList<QDomElement> out;
|
||||
for (QDomElement e = diagram.firstChildElement(block).firstChildElement(tag);
|
||||
!e.isNull(); e = e.nextSiblingElement(tag))
|
||||
out << e;
|
||||
return out;
|
||||
}
|
||||
|
||||
QList<QDomElement> lamps(const QDomDocument &doc)
|
||||
{
|
||||
QList<QDomElement> out;
|
||||
for (const QDomElement &e : childElements(firstDiagram(doc), QStringLiteral("elements"),
|
||||
QStringLiteral("element")))
|
||||
if (e.attribute(QStringLiteral("type")).endsWith(QStringLiteral("lampe_pe.elmt")))
|
||||
out << e;
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class tst_sharedsymboluuid : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
int m_run = 0;
|
||||
|
||||
// Run the binary in a sandbox of its own, so a running QElectroTech
|
||||
// cannot answer instead.
|
||||
bool run(const QStringList &args)
|
||||
{
|
||||
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run++));
|
||||
QDir().mkpath(home);
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), args);
|
||||
return proc.waitForFinished(120000) && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
QString write(const QDomDocument &doc)
|
||||
{
|
||||
const QString path = m_dir.filePath(QStringLiteral("in%1.qet").arg(m_run));
|
||||
QFile f(path);
|
||||
if (f.open(QIODevice::WriteOnly))
|
||||
f.write(doc.toByteArray());
|
||||
return path;
|
||||
}
|
||||
|
||||
QString resave(const QString &in)
|
||||
{
|
||||
const QString out = m_dir.filePath(QStringLiteral("out%1.qet").arg(m_run));
|
||||
return run({QStringLiteral("--resave"), in, out}) ? out : QString();
|
||||
}
|
||||
|
||||
// Folio 1's nets, each as its sorted symbol labels, sorted: the order
|
||||
// the export numbers them in follows the file and does not matter.
|
||||
QStringList nets(const QString &project)
|
||||
{
|
||||
const QString out = m_dir.filePath(QStringLiteral("nets%1.json").arg(m_run));
|
||||
if (!run({QStringLiteral("--export-nets"), project, out}))
|
||||
return {};
|
||||
QFile f(out);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
return {};
|
||||
QStringList result;
|
||||
const QJsonArray list = QJsonDocument::fromJson(f.readAll())
|
||||
.object().value(QStringLiteral("list")).toArray();
|
||||
for (const QJsonValue &net : list) {
|
||||
QStringList ends;
|
||||
for (const QJsonValue &t : net.toObject().value(QStringLiteral("terminals")).toArray()) {
|
||||
const QJsonObject end = t.toObject();
|
||||
if (end.value(QStringLiteral("folio")).toInt() == 1)
|
||||
ends << end.value(QStringLiteral("element")).toString();
|
||||
}
|
||||
ends.sort();
|
||||
if (!ends.isEmpty())
|
||||
result << ends.join(QLatin1Char(','));
|
||||
}
|
||||
result.sort();
|
||||
return result;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
QCOMPARE(lamps(load(example)).size(), 3);
|
||||
}
|
||||
|
||||
void sharedUuidSurvivesSaves()
|
||||
{
|
||||
const QStringList expected = nets(example);
|
||||
QVERIFY(expected.contains(QStringLiteral("L2,L3,S2")));
|
||||
|
||||
QDomDocument doc = load(example);
|
||||
const QList<QDomElement> l = lamps(doc);
|
||||
const QString shared = l.first().attribute(QStringLiteral("uuid"));
|
||||
for (QDomElement e : l)
|
||||
e.setAttribute(QStringLiteral("uuid"), shared);
|
||||
|
||||
const QString twice = resave(resave(write(doc)));
|
||||
QVERIFY(!twice.isEmpty());
|
||||
QCOMPARE(nets(twice), expected);
|
||||
|
||||
//No wire names a lamp by the uuid they share
|
||||
for (const QDomElement &c : childElements(firstDiagram(load(twice)),
|
||||
QStringLiteral("conductors"),
|
||||
QStringLiteral("conductor"))) {
|
||||
QVERIFY(c.attribute(QStringLiteral("element1")) != shared);
|
||||
QVERIFY(c.attribute(QStringLiteral("element2")) != shared);
|
||||
}
|
||||
}
|
||||
|
||||
void savedByUuidReopensRight()
|
||||
{
|
||||
const QString fixture = QFINDTESTDATA("fixtures/shared_symbol_uuid_0200.qet");
|
||||
QVERIFY(!fixture.isEmpty());
|
||||
QCOMPARE(nets(fixture), nets(example));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_sharedsymboluuid)
|
||||
#include "tst_sharedsymboluuid.moc"
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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 static text with anchor="alignment" has its x at the edge or centre
|
||||
its Halignment selects, instead of its left edge (#1251). The symbol in
|
||||
fixtures/static_text_anchor.qet has the same text five times, all at
|
||||
x = 0: left, right and centre anchored, right aligned without anchor,
|
||||
and anchored without Halignment. --export-dxf writes where each is drawn.
|
||||
*/
|
||||
class tst_statictextanchor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void anchoredX()
|
||||
{
|
||||
const QString project = QFINDTESTDATA("fixtures/static_text_anchor.qet");
|
||||
QVERIFY2(!project.isEmpty(), "fixture project not found");
|
||||
QTemporaryDir dir;
|
||||
QVERIFY(dir.isValid());
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--export-dxf"), project, dir.path()});
|
||||
QVERIFY2(proc.waitForFinished(60000), "--export-dxf timed out");
|
||||
QCOMPARE(proc.exitCode(), 0);
|
||||
|
||||
QFile file(dir.filePath(QStringLiteral("01_diagram.dxf")));
|
||||
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QStringList values = QString::fromUtf8(file.readAll()).split(QLatin1Char('\n'));
|
||||
|
||||
//x (group code 10) of each TEXT entity of the symbol, in order
|
||||
QList<qreal> xs;
|
||||
for (int i = 0 ; i + 1 < values.size() ; i += 2) {
|
||||
if (values.at(i).trimmed() != QLatin1String("0") || values.at(i + 1).trimmed() != QLatin1String("TEXT"))
|
||||
continue;
|
||||
qreal x = 0;
|
||||
QString text;
|
||||
for (int j = i + 2 ; j + 1 < values.size() && values.at(j).trimmed() != QLatin1String("0") ; j += 2) {
|
||||
if (values.at(j).trimmed() == QLatin1String("10"))
|
||||
x = values.at(j + 1).trimmed().toDouble();
|
||||
else if (values.at(j).trimmed() == QLatin1String("1"))
|
||||
text = values.at(j + 1).trimmed();
|
||||
}
|
||||
if (text == QLatin1String("GPIO21 PCM_DOUT"))
|
||||
xs << x;
|
||||
}
|
||||
QCOMPARE(xs.size(), 5);
|
||||
|
||||
const qreal left = xs.at(0), right = xs.at(1), centre = xs.at(2);
|
||||
//anchored on the right edge: drawn one text width to the left
|
||||
QVERIFY2(left - right > 20, qPrintable(QStringLiteral("left %1 right %2").arg(left).arg(right)));
|
||||
//anchored on the centre: half as far
|
||||
QVERIFY(qAbs(2 * (left - centre) - (left - right)) < 0.01);
|
||||
//without anchor, x stays the left edge whatever the alignment
|
||||
QCOMPARE(xs.at(3), left);
|
||||
//anchor without Halignment is the left edge too
|
||||
QCOMPARE(xs.at(4), left);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_statictextanchor)
|
||||
#include "tst_statictextanchor.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);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QDockWidget>
|
||||
#include <QMainWindow>
|
||||
#include <QSettings>
|
||||
@@ -48,6 +49,11 @@ private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
//A scope of this test's own. Without an organization name,
|
||||
//QSettings on Windows has no registry key: it drops every
|
||||
//write and reads only defaults.
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("QElectroTech-tst_toolbarsettings"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("tst_toolbarsettings"));
|
||||
QSettings().remove(QStringLiteral("toolbars"));
|
||||
|
||||
m_window = new QMainWindow();
|
||||
|
||||
Reference in New Issue
Block a user