Save/open preferences

This commit is contained in:
Jan Käberich 2022-01-15 17:00:57 +01:00
parent 5d8efd4336
commit fa481e2062
6 changed files with 257 additions and 196 deletions

View File

@ -25,7 +25,7 @@ Calkit::Calkit()
{
// set default values
for(auto e : json_descr) {
for(auto e : descr) {
e.var.setValue(e.def);
}
}
@ -40,24 +40,7 @@ void Calkit::toFile(QString filename)
TransformPathsToRelative(filename);
json j;
for(auto e : json_descr) {
auto list = e.name.split("/");
auto *json_entry = &j;
while(list.size() > 0) {
json_entry = &(*json_entry)[list.takeFirst().toStdString()];
}
// json library does not now about QVariant, handle used cases
auto val = e.var.value();
switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: *json_entry = val.toDouble(); break;
case QMetaType::Int: *json_entry = val.toInt(); break;
case QMetaType::Bool: *json_entry = val.toBool(); break;
case QMetaType::QString: *json_entry = val.toString().toStdString(); break;
default:
throw runtime_error("Unimplemented metatype");
}
}
json j = Savable::createJSON(descr);
ofstream file;
file.open(filename.toStdString());
file << setw(4) << j << endl;
@ -90,58 +73,19 @@ Calkit Calkit::fromFile(QString filename)
throw runtime_error("JSON parsing error: " + string(e.what()));
}
if(j.contains("SOLT")) {
// older file versions specify Z0 for resistance. If resistance entry is missing,
// set it to Z0 later
bool loadResistanceMissing_m = false;
bool loadResistanceMissing_f = false;
// older file versions specify Z0 for resistance. Set resistance to Nan to detect missing values later
c.SOLT.load_m.resistance = std::numeric_limits<double>::quiet_NaN();
c.SOLT.load_f.resistance = std::numeric_limits<double>::quiet_NaN();
qDebug() << "JSON format detected";
// calkit file uses json format, parse
for(auto e : c.json_descr) {
auto list = e.name.split("/");
auto *json_entry = &j;
bool entry_exists = true;
while(list.size() > 0) {
auto key = list.takeFirst().toStdString();
if((*json_entry).contains(key)) {
json_entry = &(*json_entry)[key];
} else {
entry_exists = false;
break;
}
}
if(!entry_exists) {
// missing entry in json file, nothing to do (default values already set in constructor)
qWarning() << "Entry" << e.name << "not present in file, assuming default value";
if(e.name == "SOLT/Load/Param/Resistance") {
loadResistanceMissing_m = true;
} else if(e.name == "SOLT/Load/Param/Resistance_Female") {
loadResistanceMissing_f = true;
}
continue;
}
// json library does not now about QVariant, handle used cases
auto val = e.var.value();
switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: e.var.setValue((*json_entry).get<double>()); break;
case QMetaType::Int: e.var.setValue((*json_entry).get<int>()); break;
case QMetaType::Bool: e.var.setValue((*json_entry).get<bool>()); break;
case QMetaType::QString: {
auto s = QString::fromStdString((*json_entry).get<string>());
e.var.setValue(s);
}
break;
default:
throw runtime_error("Unimplemented metatype");
}
}
// adjust Z0/resistance in case of older calkit file version
if(loadResistanceMissing_f) {
Savable::parseJSON(j, c.descr);
// adjust Z0/resistance in case of older calkit file version with missing resistance entries
if(isnan(c.SOLT.load_f.resistance)) {
c.SOLT.load_f.resistance = c.SOLT.load_f.Z0;
c.SOLT.load_f.Z0 = 50.0;
}
if(loadResistanceMissing_m) {
if(isnan(c.SOLT.load_m.resistance)) {
c.SOLT.load_m.resistance = c.SOLT.load_m.Z0;
c.SOLT.load_m.Z0 = 50.0;
}

View File

@ -105,90 +105,85 @@ private:
Touchstone *ts_through;
bool ts_cached;
using JSONDescription = struct _jsondescr {
QPointerVariant var;
QString name;
QVariant def;
};
const std::array<JSONDescription, 73> json_descr = {{
const std::vector<Savable::SettingDescription> descr = {{
{&manufacturer, "Manufacturer", ""},
{&serialnumber, "Serialnumber", ""},
{&description, "Description", ""},
{&SOLT.open_m.Z0, "SOLT/Open/Param/Z0", 50.0},
{&SOLT.open_m.delay, "SOLT/Open/Param/Delay", 0.0},
{&SOLT.open_m.loss, "SOLT/Open/Param/Loss", 0.0},
{&SOLT.open_m.C0, "SOLT/Open/Param/C0", 0.0},
{&SOLT.open_m.C1, "SOLT/Open/Param/C1", 0.0},
{&SOLT.open_m.C2, "SOLT/Open/Param/C2", 0.0},
{&SOLT.open_m.C3, "SOLT/Open/Param/C3", 0.0},
{&SOLT.open_m.useMeasurements, "SOLT/Open/Measurements/Use", false},
{&SOLT.open_m.file, "SOLT/Open/Measurements/File", ""},
{&SOLT.open_m.Sparam, "SOLT/Open/Measurements/Port", 0},
{&SOLT.open_f.Z0, "SOLT/Open/Param/Z0_Female", 50.0},
{&SOLT.open_f.delay, "SOLT/Open/Param/Delay_Female", 0.0},
{&SOLT.open_f.loss, "SOLT/Open/Param/Loss_Female", 0.0},
{&SOLT.open_f.C0, "SOLT/Open/Param/C0_Female", 0.0},
{&SOLT.open_f.C1, "SOLT/Open/Param/C1_Female", 0.0},
{&SOLT.open_f.C2, "SOLT/Open/Param/C2_Female", 0.0},
{&SOLT.open_f.C3, "SOLT/Open/Param/C3_Female", 0.0},
{&SOLT.open_f.useMeasurements, "SOLT/Open/Measurements/Use_Female", false},
{&SOLT.open_f.file, "SOLT/Open/Measurements/File_Female", ""},
{&SOLT.open_f.Sparam, "SOLT/Open/Measurements/Port_Female", 0},
{&SOLT.open_m.Z0, "SOLT.Open.Param.Z0", 50.0},
{&SOLT.open_m.delay, "SOLT.Open.Param.Delay", 0.0},
{&SOLT.open_m.loss, "SOLT.Open.Param.Loss", 0.0},
{&SOLT.open_m.C0, "SOLT.Open.Param.C0", 0.0},
{&SOLT.open_m.C1, "SOLT.Open.Param.C1", 0.0},
{&SOLT.open_m.C2, "SOLT.Open.Param.C2", 0.0},
{&SOLT.open_m.C3, "SOLT.Open.Param.C3", 0.0},
{&SOLT.open_m.useMeasurements, "SOLT.Open.Measurements.Use", false},
{&SOLT.open_m.file, "SOLT.Open.Measurements.File", ""},
{&SOLT.open_m.Sparam, "SOLT.Open.Measurements.Port", 0},
{&SOLT.open_f.Z0, "SOLT.Open.Param.Z0_Female", 50.0},
{&SOLT.open_f.delay, "SOLT.Open.Param.Delay_Female", 0.0},
{&SOLT.open_f.loss, "SOLT.Open.Param.Loss_Female", 0.0},
{&SOLT.open_f.C0, "SOLT.Open.Param.C0_Female", 0.0},
{&SOLT.open_f.C1, "SOLT.Open.Param.C1_Female", 0.0},
{&SOLT.open_f.C2, "SOLT.Open.Param.C2_Female", 0.0},
{&SOLT.open_f.C3, "SOLT.Open.Param.C3_Female", 0.0},
{&SOLT.open_f.useMeasurements, "SOLT.Open.Measurements.Use_Female", false},
{&SOLT.open_f.file, "SOLT.Open.Measurements.File_Female", ""},
{&SOLT.open_f.Sparam, "SOLT.Open.Measurements.Port_Female", 0},
{&SOLT.short_m.Z0, "SOLT/Short/Param/Z0", 50.0},
{&SOLT.short_m.delay, "SOLT/Short/Param/Delay", 0.0},
{&SOLT.short_m.loss, "SOLT/Short/Param/Loss", 0.0},
{&SOLT.short_m.L0, "SOLT/Short/Param/L0", 0.0},
{&SOLT.short_m.L1, "SOLT/Short/Param/L1", 0.0},
{&SOLT.short_m.L2, "SOLT/Short/Param/L2", 0.0},
{&SOLT.short_m.L3, "SOLT/Short/Param/L3", 0.0},
{&SOLT.short_m.useMeasurements, "SOLT/Short/Measurements/Use", false},
{&SOLT.short_m.file, "SOLT/Short/Measurements/File", ""},
{&SOLT.short_m.Sparam, "SOLT/Short/Measurements/Port", 0},
{&SOLT.short_f.Z0, "SOLT/Short/Param/Z0_Female", 50.0},
{&SOLT.short_f.delay, "SOLT/Short/Param/Delay_Female", 0.0},
{&SOLT.short_f.loss, "SOLT/Short/Param/Loss_Female", 0.0},
{&SOLT.short_f.L0, "SOLT/Short/Param/L0_Female", 0.0},
{&SOLT.short_f.L1, "SOLT/Short/Param/L1_Female", 0.0},
{&SOLT.short_f.L2, "SOLT/Short/Param/L2_Female", 0.0},
{&SOLT.short_f.L3, "SOLT/Short/Param/L3_Female", 0.0},
{&SOLT.short_f.useMeasurements, "SOLT/Short/Measurements/Use_Female", false},
{&SOLT.short_f.file, "SOLT/Short/Measurements/File_Female", ""},
{&SOLT.short_f.Sparam, "SOLT/Short/Measurements/Port_Female", 0},
{&SOLT.short_m.Z0, "SOLT.Short.Param.Z0", 50.0},
{&SOLT.short_m.delay, "SOLT.Short.Param.Delay", 0.0},
{&SOLT.short_m.loss, "SOLT.Short.Param.Loss", 0.0},
{&SOLT.short_m.L0, "SOLT.Short.Param.L0", 0.0},
{&SOLT.short_m.L1, "SOLT.Short.Param.L1", 0.0},
{&SOLT.short_m.L2, "SOLT.Short.Param.L2", 0.0},
{&SOLT.short_m.L3, "SOLT.Short.Param.L3", 0.0},
{&SOLT.short_m.useMeasurements, "SOLT.Short.Measurements.Use", false},
{&SOLT.short_m.file, "SOLT.Short.Measurements.File", ""},
{&SOLT.short_m.Sparam, "SOLT.Short.Measurements.Port", 0},
{&SOLT.short_f.Z0, "SOLT.Short.Param.Z0_Female", 50.0},
{&SOLT.short_f.delay, "SOLT.Short.Param.Delay_Female", 0.0},
{&SOLT.short_f.loss, "SOLT.Short.Param.Loss_Female", 0.0},
{&SOLT.short_f.L0, "SOLT.Short.Param.L0_Female", 0.0},
{&SOLT.short_f.L1, "SOLT.Short.Param.L1_Female", 0.0},
{&SOLT.short_f.L2, "SOLT.Short.Param.L2_Female", 0.0},
{&SOLT.short_f.L3, "SOLT.Short.Param.L3_Female", 0.0},
{&SOLT.short_f.useMeasurements, "SOLT.Short.Measurements.Use_Female", false},
{&SOLT.short_f.file, "SOLT.Short.Measurements.File_Female", ""},
{&SOLT.short_f.Sparam, "SOLT.Short.Measurements.Port_Female", 0},
{&SOLT.load_m.resistance, "SOLT/Load/Param/Resistance", 50.0},
{&SOLT.load_m.Z0, "SOLT/Load/Param/Z0", 50.0},
{&SOLT.load_m.delay, "SOLT/Load/Param/Delay", 0.0},
{&SOLT.load_m.Cparallel, "SOLT/Load/Param/C", 0.0},
{&SOLT.load_m.Lseries, "SOLT/Load/Param/L", 0.0},
{&SOLT.load_m.useMeasurements, "SOLT/Load/Measurements/Use", false},
{&SOLT.load_m.file, "SOLT/Load/Measurements/File", ""},
{&SOLT.load_m.Sparam, "SOLT/Load/Measurements/Port", 0},
{&SOLT.load_f.resistance, "SOLT/Load/Param/Resistance_Female", 50.0},
{&SOLT.load_f.Z0, "SOLT/Load/Param/Z0_Female", 50.0},
{&SOLT.load_f.delay, "SOLT/Load/Param/Delay_Female", 0.0},
{&SOLT.load_f.Cparallel, "SOLT/Load/Param/C_Female", 0.0},
{&SOLT.load_f.Lseries, "SOLT/Load/Param/L_Female", 0.0},
{&SOLT.load_f.useMeasurements, "SOLT/Load/Measurements/Use_Female", false},
{&SOLT.load_f.file, "SOLT/Load/Measurements/File_Female", ""},
{&SOLT.load_f.Sparam, "SOLT/Load/Measurements/Port_Female", 0},
{&SOLT.load_m.resistance, "SOLT.Load.Param.Resistance", 50.0},
{&SOLT.load_m.Z0, "SOLT.Load.Param.Z0", 50.0},
{&SOLT.load_m.delay, "SOLT.Load.Param.Delay", 0.0},
{&SOLT.load_m.Cparallel, "SOLT.Load.Param.C", 0.0},
{&SOLT.load_m.Lseries, "SOLT.Load.Param.L", 0.0},
{&SOLT.load_m.useMeasurements, "SOLT.Load.Measurements.Use", false},
{&SOLT.load_m.file, "SOLT.Load.Measurements.File", ""},
{&SOLT.load_m.Sparam, "SOLT.Load.Measurements.Port", 0},
{&SOLT.load_f.resistance, "SOLT.Load.Param.Resistance_Female", 50.0},
{&SOLT.load_f.Z0, "SOLT.Load.Param.Z0_Female", 50.0},
{&SOLT.load_f.delay, "SOLT.Load.Param.Delay_Female", 0.0},
{&SOLT.load_f.Cparallel, "SOLT.Load.Param.C_Female", 0.0},
{&SOLT.load_f.Lseries, "SOLT.Load.Param.L_Female", 0.0},
{&SOLT.load_f.useMeasurements, "SOLT.Load.Measurements.Use_Female", false},
{&SOLT.load_f.file, "SOLT.Load.Measurements.File_Female", ""},
{&SOLT.load_f.Sparam, "SOLT.Load.Measurements.Port_Female", 0},
{&SOLT.Through.Z0, "SOLT/Through/Param/Z0", 50.0},
{&SOLT.Through.delay, "SOLT/Through/Param/Delay", 0.0},
{&SOLT.Through.loss, "SOLT/Through/Param/Loss", 0.0},
{&SOLT.Through.useMeasurements, "SOLT/Through/Measurements/Use", false},
{&SOLT.Through.file, "SOLT/Through/Measurements/File", ""},
{&SOLT.Through.Sparam1, "SOLT/Through/Measurements/Port1", 0},
{&SOLT.Through.Sparam2, "SOLT/Through/Measurements/Port2", 1},
{&SOLT.Through.Z0, "SOLT.Through.Param.Z0", 50.0},
{&SOLT.Through.delay, "SOLT.Through.Param.Delay", 0.0},
{&SOLT.Through.loss, "SOLT.Through.Param.Loss", 0.0},
{&SOLT.Through.useMeasurements, "SOLT.Through.Measurements.Use", false},
{&SOLT.Through.file, "SOLT.Through.Measurements.File", ""},
{&SOLT.Through.Sparam1, "SOLT.Through.Measurements.Port1", 0},
{&SOLT.Through.Sparam2, "SOLT.Through.Measurements.Port2", 1},
{&SOLT.separate_male_female, "SOLT/SeparateMaleFemale", false},
{&SOLT.separate_male_female, "SOLT.SeparateMaleFemale", false},
{&TRL.Through.Z0, "TRL/Through/Z0", 50.0},
{&TRL.Reflection.isShort, "TRL/Reflect/isShort", false},
{&TRL.Line.delay, "TRL/Line/Delay", 74.0},
{&TRL.Line.minFreq, "TRL/Line/minFreq", 751000000.0},
{&TRL.Line.maxFreq, "TRL/Line/maxFreq", 6000000000.0},
{&TRL.Through.Z0, "TRL.Through.Z0", 50.0},
{&TRL.Reflection.isShort, "TRL.Reflect.isShort", false},
{&TRL.Line.delay, "TRL.Line.Delay", 74.0},
{&TRL.Line.minFreq, "TRL.Line.minFreq", 751000000.0},
{&TRL.Line.maxFreq, "TRL.Line.maxFreq", 6000000000.0},
{&startDialogWithSOLT, "StartDialogWithSOLT", true}
}};

View File

@ -6,8 +6,12 @@
#include <QSettings>
#include <QPushButton>
#include <QMessageBox>
#include <map>
#include <QDebug>
#include <QFileDialog>
#include <map>
#include <fstream>
#include <iomanip>
using namespace std;
@ -128,55 +132,34 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
});
connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [=](){
// apply GUI state to settings
p->Startup.ConnectToFirstDevice = ui->StartupAutoconnect->isChecked();
p->Startup.RememberSweepSettings = ui->StartupSweepLastUsed->isChecked();
p->Startup.DefaultSweep.type = ui->StartupSweepType->currentText();
p->Startup.DefaultSweep.f_start = ui->StartupSweepStart->value();
p->Startup.DefaultSweep.f_stop = ui->StartupSweepStop->value();
p->Startup.DefaultSweep.f_excitation = ui->StartupSweepLevel->value();
p->Startup.DefaultSweep.dbm_start = ui->StartupSweepPowerStart->value();
p->Startup.DefaultSweep.dbm_stop = ui->StartupSweepPowerStop->value();
p->Startup.DefaultSweep.dbm_freq = ui->StartupSweepPowerFrequency->value();
p->Startup.DefaultSweep.bandwidth = ui->StartupSweepBandwidth->value();
p->Startup.DefaultSweep.points = ui->StartupSweepPoints->value();
p->Startup.DefaultSweep.averaging = ui->StartupSweepAveraging->value();
p->Startup.Generator.frequency = ui->StartupGeneratorFrequency->value();
p->Startup.Generator.level = ui->StartupGeneratorLevel->value();
p->Startup.SA.start = ui->StartupSAStart->value();
p->Startup.SA.stop = ui->StartupSAStop->value();
p->Startup.SA.RBW = ui->StartupSARBW->value();
p->Startup.SA.window = ui->StartupSAWindow->currentIndex();
p->Startup.SA.detector = ui->StartupSADetector->currentIndex();
p->Startup.SA.signalID = ui->StartupSASignalID->isChecked();
p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked();
p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked();
p->Acquisition.adjustPowerLevel = ui->AcquisitionAdjustPowerLevel->isChecked();
p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked();
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1;
p->Acquisition.IF1 = ui->AcquisitionIF1->value();
p->Acquisition.ADCprescaler = ui->AcquisitionADCpresc->value();
p->Acquisition.DFTPhaseInc = ui->AcquisitionADCphaseInc->value();
p->Graphs.showUnits = ui->GraphsShowUnit->isChecked();
p->Graphs.Color.background = ui->GraphsColorBackground->getColor();
p->Graphs.Color.axis = ui->GraphsColorAxis->getColor();
p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked();
p->Graphs.Color.Ticks.Background.background = ui->GraphsColorTicksBackground->getColor();
p->Graphs.Color.Ticks.divisions = ui->GraphsColorTicksDivisions->getColor();
p->Graphs.domainChangeBehavior = (GraphDomainChangeBehavior) ui->GraphsDomainChangeBehavior->currentIndex();
p->Graphs.lineWidth = ui->GraphsLineWidth->value();
p->Marker.defaultBehavior.showDataOnGraphs = ui->MarkerShowMarkerData->isChecked();
p->Marker.defaultBehavior.showAllData = ui->MarkerShowAllMarkerData->isChecked();
p->Marker.interpolatePoints = ui->MarkerInterpolate->currentIndex() == 1;
p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked();
p->SCPIServer.port = ui->SCPIServerPort->value();
updateFromGUI();
accept();
});
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getSaveFileName(this, "Save preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) {
if(!filename.toLower().endsWith(".vnapref")) {
filename.append(".vnapref");
}
ofstream file;
file.open(filename.toStdString());
updateFromGUI();
file << setw(1) << p->toJSON();
file.close();
}
});
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getOpenFileName(this, "Load preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) {
ifstream file;
file.open(filename.toStdString());
nlohmann::json j;
file >> j;
file.close();
p->fromJSON(j);
setInitialGUIState();
}
});
setInitialGUIState();
updateADCRate();
@ -259,6 +242,57 @@ void PreferencesDialog::setInitialGUIState()
}
}
void PreferencesDialog::updateFromGUI()
{
p->Startup.ConnectToFirstDevice = ui->StartupAutoconnect->isChecked();
p->Startup.RememberSweepSettings = ui->StartupSweepLastUsed->isChecked();
p->Startup.DefaultSweep.type = ui->StartupSweepType->currentText();
p->Startup.DefaultSweep.f_start = ui->StartupSweepStart->value();
p->Startup.DefaultSweep.f_stop = ui->StartupSweepStop->value();
p->Startup.DefaultSweep.f_excitation = ui->StartupSweepLevel->value();
p->Startup.DefaultSweep.dbm_start = ui->StartupSweepPowerStart->value();
p->Startup.DefaultSweep.dbm_stop = ui->StartupSweepPowerStop->value();
p->Startup.DefaultSweep.dbm_freq = ui->StartupSweepPowerFrequency->value();
p->Startup.DefaultSweep.bandwidth = ui->StartupSweepBandwidth->value();
p->Startup.DefaultSweep.points = ui->StartupSweepPoints->value();
p->Startup.DefaultSweep.averaging = ui->StartupSweepAveraging->value();
p->Startup.Generator.frequency = ui->StartupGeneratorFrequency->value();
p->Startup.Generator.level = ui->StartupGeneratorLevel->value();
p->Startup.SA.start = ui->StartupSAStart->value();
p->Startup.SA.stop = ui->StartupSAStop->value();
p->Startup.SA.RBW = ui->StartupSARBW->value();
p->Startup.SA.window = ui->StartupSAWindow->currentIndex();
p->Startup.SA.detector = ui->StartupSADetector->currentIndex();
p->Startup.SA.signalID = ui->StartupSASignalID->isChecked();
p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked();
p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked();
p->Acquisition.adjustPowerLevel = ui->AcquisitionAdjustPowerLevel->isChecked();
p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked();
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1;
p->Acquisition.IF1 = ui->AcquisitionIF1->value();
p->Acquisition.ADCprescaler = ui->AcquisitionADCpresc->value();
p->Acquisition.DFTPhaseInc = ui->AcquisitionADCphaseInc->value();
p->Graphs.showUnits = ui->GraphsShowUnit->isChecked();
p->Graphs.Color.background = ui->GraphsColorBackground->getColor();
p->Graphs.Color.axis = ui->GraphsColorAxis->getColor();
p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked();
p->Graphs.Color.Ticks.Background.background = ui->GraphsColorTicksBackground->getColor();
p->Graphs.Color.Ticks.divisions = ui->GraphsColorTicksDivisions->getColor();
p->Graphs.domainChangeBehavior = (GraphDomainChangeBehavior) ui->GraphsDomainChangeBehavior->currentIndex();
p->Graphs.lineWidth = ui->GraphsLineWidth->value();
p->Marker.defaultBehavior.showDataOnGraphs = ui->MarkerShowMarkerData->isChecked();
p->Marker.defaultBehavior.showAllData = ui->MarkerShowAllMarkerData->isChecked();
p->Marker.interpolatePoints = ui->MarkerInterpolate->currentIndex() == 1;
p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked();
p->SCPIServer.port = ui->SCPIServerPort->value();
}
void Preferences::load()
{
QSettings settings;
@ -296,3 +330,13 @@ void Preferences::setDefault()
d.var.setValue(d.def);
}
}
void Preferences::fromJSON(nlohmann::json j)
{
parseJSON(j, descr);
}
nlohmann::json Preferences::toJSON()
{
return createJSON(descr);
}

View File

@ -2,6 +2,7 @@
#define PREFERENCESDIALOG_H
#include "Util/qpointervariant.h"
#include "savable.h"
#include <QDialog>
#include <QVariant>
@ -16,7 +17,7 @@ enum GraphDomainChangeBehavior {
Q_DECLARE_METATYPE(GraphDomainChangeBehavior);
class Preferences {
class Preferences : public Savable {
public:
static Preferences& getInstance() {
return instance;
@ -104,16 +105,16 @@ public:
} SCPIServer;
bool TCPoverride; // in case of manual port specification via command line
void fromJSON(nlohmann::json j) override;
nlohmann::json toJSON() override;
private:
Preferences() :
TCPoverride(false) {};
static Preferences instance;
using SettingDescription = struct {
QPointerVariant var;
QString name;
QVariant def;
};
const std::array<SettingDescription, 45> descr = {{
const std::vector<Savable::SettingDescription> descr = {{
{&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true},
{&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false},
{&Startup.DefaultSweep.type, "Startup.DefaultSweep.type", "Frequency"},
@ -176,6 +177,7 @@ public:
private:
void setInitialGUIState();
void updateFromGUI();
Ui::PreferencesDialog *ui;
Preferences *p;
};

View File

@ -1144,7 +1144,7 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
<set>QDialogButtonBox::Ok|QDialogButtonBox::Open|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
</property>
<property name="centerButtons">
<bool>false</bool>

View File

@ -2,11 +2,87 @@
#define SAVABLE_H
#include "json.hpp"
#include "Util/qpointervariant.h"
#include <QVariant>
#include <QDebug>
#include <QColor>
#include <array>
#include <exception>
class Savable {
public:
virtual nlohmann::json toJSON() = 0;
virtual void fromJSON(nlohmann::json j) = 0;
using SettingDescription = struct {
QPointerVariant var;
QString name;
QVariant def;
};
static void parseJSON(nlohmann::json j, std::vector<SettingDescription> descr) {
for(auto e : descr) {
auto list = e.name.split(".");
auto *json_entry = &j;
bool entry_exists = true;
while(list.size() > 0) {
auto key = list.takeFirst().toStdString();
if((*json_entry).contains(key)) {
json_entry = &(*json_entry)[key];
} else {
entry_exists = false;
break;
}
}
if(!entry_exists) {
// missing entry in json file, nothing to do (default values already set in constructor)
qWarning() << "Entry" << e.name << "not present in file";
continue;
}
// json library does not now about QVariant, handle used cases
auto val = e.var.value();
switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: e.var.setValue((*json_entry).get<double>()); break;
case QMetaType::Int: e.var.setValue((*json_entry).get<int>()); break;
case QMetaType::Bool: e.var.setValue((*json_entry).get<bool>()); break;
case QMetaType::QString: {
auto s = QString::fromStdString((*json_entry).get<std::string>());
e.var.setValue(s);
}
break;
case QMetaType::QColor: {
auto s = QString::fromStdString((*json_entry).get<std::string>());
e.var.setValue(QColor(s));
}
break;
default:
throw std::runtime_error("Unimplemented metatype:"+std::string(val.typeName()));
}
}
}
static nlohmann::json createJSON(std::vector<SettingDescription> descr) {
nlohmann::json j;
for(auto e : descr) {
auto list = e.name.split(".");
auto *json_entry = &j;
while(list.size() > 0) {
json_entry = &(*json_entry)[list.takeFirst().toStdString()];
}
// json library does not now about QVariant, handle used cases
auto val = e.var.value();
switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: *json_entry = val.toDouble(); break;
case QMetaType::Int: *json_entry = val.toInt(); break;
case QMetaType::Bool: *json_entry = val.toBool(); break;
case QMetaType::QString: *json_entry = val.toString().toStdString(); break;
case QMetaType::QColor: *json_entry = val.value<QColor>().name().toStdString(); break;
default:
throw std::runtime_error("Unimplemented metatype:"+std::string(val.typeName()));
}
}
return j;
}
};
#endif // SAVABLE_H