mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-13 16:34:14 +02:00
Add saving and loading the configuration to a file (discussion #610)
Revives #640, closed only to clear a review backlog. Two entries in the Configuration menu: "Enregistrer la configuration sous..." saves the settings to a .conf file, "Charger une configuration..." replaces them with one. The file is always ini format, so a profile made on one platform loads on the others. Changes from #640: - Window sizes, positions and dock layouts (".../geometry", ".../state") and the recent-files lists stay out of a profile and are kept on load: they describe this computer, not a preference. - A saved file carries a format marker; loading refuses files that QElectroTech did not save. - Loading closes QElectroTech and asks the user to start it again, instead of starting a second copy: a second copy started while this one is still running hands its arguments over and exits (main.cpp). - The colour dialog's custom colours are reloaded from the profile before quitting, since ~QETApp() saves them over the loaded ones. - Both actions are registered with ShortcutManager, so they appear in command search and can be given a shortcut. - The copy rules live in the header-only utils/configprofile.h and are unit tested (tst_configprofile). Loading still replaces rather than merges: a setting the profile does not have is removed, or switching profiles would keep settings from the previous one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -111,6 +111,13 @@ add_test(NAME tst_alignment COMMAND tst_alignment)
|
||||
target_include_directories(tst_alignment PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_alignment PRIVATE Qt::Test)
|
||||
|
||||
# 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)
|
||||
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)
|
||||
|
||||
# itemgroups.cpp keeps group membership and the selection rule apart from
|
||||
# Diagram, so it is tested here on a plain scene of rectangles.
|
||||
add_executable(tst_itemgroups tst_itemgroups.cpp ${QET_DIR}/sources/itemgroups.cpp)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#include <QtTest>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include "utils/configprofile.h"
|
||||
|
||||
class tst_configprofile : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
QString path(const QString &name) const { return m_dir.filePath(name); }
|
||||
|
||||
private slots:
|
||||
void localKeys()
|
||||
{
|
||||
QVERIFY(ConfigProfile::isLocalKey("diagrameditor/geometry"));
|
||||
QVERIFY(ConfigProfile::isLocalKey("elementeditor/state"));
|
||||
QVERIFY(ConfigProfile::isLocalKey("projects-recentfiles/file1"));
|
||||
QVERIFY(ConfigProfile::isLocalKey(ConfigProfile::marker_key));
|
||||
QVERIFY(!ConfigProfile::isLocalKey("diagrameditor/Xgrid"));
|
||||
QVERIFY(!ConfigProfile::isLocalKey("shortcuts/mainwindow.fullscreen"));
|
||||
QVERIFY(!ConfigProfile::isLocalKey("masterpropertieswidget/plc-table-header-state"));
|
||||
}
|
||||
|
||||
// Export leaves out window layout and recent files, and marks the file.
|
||||
void exportLeavesOutLocalKeys()
|
||||
{
|
||||
QSettings live(path("live1.conf"), QSettings::IniFormat);
|
||||
live.setValue("diagrameditor/Xgrid", 7);
|
||||
live.setValue("lang", "de");
|
||||
live.setValue("diagrameditor/geometry", QByteArray("xyz"));
|
||||
live.setValue("projects-recentfiles/file1", "/home/a/b.qet");
|
||||
|
||||
QSettings file(path("profile1.conf"), QSettings::IniFormat);
|
||||
QCOMPARE(ConfigProfile::exportTo(live, file), 2);
|
||||
QVERIFY(ConfigProfile::isProfile(file));
|
||||
QCOMPARE(file.value("diagrameditor/Xgrid").toInt(), 7);
|
||||
QVERIFY(!file.contains("diagrameditor/geometry"));
|
||||
QVERIFY(!file.contains("projects-recentfiles/file1"));
|
||||
}
|
||||
|
||||
// Import replaces every setting, removes the ones the profile does not
|
||||
// have, keeps window layout and recent files, and never copies the marker.
|
||||
void importReplacesButKeepsLocal()
|
||||
{
|
||||
QSettings file(path("profile2.conf"), QSettings::IniFormat);
|
||||
file.setValue("diagrameditor/Xgrid", 5);
|
||||
file.setValue("diagrameditor/geometry", QByteArray("from-file"));
|
||||
file.setValue(ConfigProfile::marker_key, 1);
|
||||
|
||||
QSettings live(path("live2.conf"), QSettings::IniFormat);
|
||||
live.setValue("diagrameditor/Xgrid", 10);
|
||||
live.setValue("lang", "fr");
|
||||
live.setValue("diagrameditor/geometry", QByteArray("mine"));
|
||||
live.setValue("projects-recentfiles/file1", "/home/a/b.qet");
|
||||
|
||||
ConfigProfile::importFrom(file, live);
|
||||
QCOMPARE(live.value("diagrameditor/Xgrid").toInt(), 5);
|
||||
QVERIFY(!live.contains("lang"));
|
||||
QCOMPARE(live.value("diagrameditor/geometry").toByteArray(), QByteArray("mine"));
|
||||
QCOMPARE(live.value("projects-recentfiles/file1").toString(), QString("/home/a/b.qet"));
|
||||
QVERIFY(!live.contains(ConfigProfile::marker_key));
|
||||
}
|
||||
|
||||
// Values survive the round trip with their types, including arrays.
|
||||
void roundTrip()
|
||||
{
|
||||
QSettings a(path("a.conf"), QSettings::IniFormat);
|
||||
a.setValue("print/default/fitinpage", true);
|
||||
a.setValue("diagrameditor/sheet_background_color", QColor(12, 34, 56));
|
||||
a.beginWriteArray("diagrameditor/defaultguides", 2);
|
||||
a.setArrayIndex(0); a.setValue("pos", 100);
|
||||
a.setArrayIndex(1); a.setValue("pos", 250);
|
||||
a.endArray();
|
||||
|
||||
QSettings file(path("profile3.conf"), QSettings::IniFormat);
|
||||
ConfigProfile::exportTo(a, file);
|
||||
QSettings b(path("b.conf"), QSettings::IniFormat);
|
||||
b.setValue("stray", 1);
|
||||
ConfigProfile::importFrom(file, b);
|
||||
|
||||
QStringList ka = a.allKeys(), kb = b.allKeys();
|
||||
ka.sort(); kb.sort();
|
||||
QCOMPARE(kb, ka);
|
||||
for (const QString &k : std::as_const(ka))
|
||||
QCOMPARE(b.value(k), a.value(k));
|
||||
}
|
||||
|
||||
void plainFileIsNotAProfile()
|
||||
{
|
||||
QSettings other(path("other.conf"), QSettings::IniFormat);
|
||||
other.setValue("some/key", 1);
|
||||
QVERIFY(!ConfigProfile::isProfile(other));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_configprofile)
|
||||
|
||||
#include "tst_configprofile.moc"
|
||||
Reference in New Issue
Block a user