From 0614221815df9aa02a10ec4fa96ddb4b30f0f680 Mon Sep 17 00:00:00 2001 From: Emily Ellis Date: Tue, 27 Feb 2024 21:49:31 -0500 Subject: [PATCH] support multiple machine-specific configurations ("profiles") This makes Candle2 usable for driving multiple machines from the same computer, by making separate copies of the entire config file per machine. By default, nothing will change; the file format is unchanged, and the current configuration is treated as a default profile. If multiple profiles are present at startup, the user is prompted to select one. If the environment variable $CANDLE_PROFILE is present and non-empty, that profile is used without prompting. Profiles are stored in the app config directory, as urlencode(profilename) + ".ini". (URL-encoding the names allows profiles to be named without regard for the platform's file naming rules.) There is currently no UI for managing profiles; they may be created by manually copying `settings.ini` to a new file in the same directory, or by setting $CANDLE_PROFILE to a profile that does not yet exist (it will be created with default settings). --- src/candle2.pro | 2 ++ src/frmmain.cpp | 41 ++++++++++++++++++++++++++++++---------- src/frmmain.h | 6 ++---- src/frmmain_settings.cpp | 4 ++-- src/utils/profiles.cpp | 20 ++++++++++++++++++++ src/utils/profiles.h | 11 +++++++++++ 6 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 src/utils/profiles.cpp create mode 100644 src/utils/profiles.h diff --git a/src/candle2.pro b/src/candle2.pro index 80d8a08..15ffc63 100644 --- a/src/candle2.pro +++ b/src/candle2.pro @@ -55,6 +55,7 @@ SOURCES += main.cpp\ parser/pointsegment.cpp \ tables/gcodetablemodel.cpp \ tables/heightmaptablemodel.cpp \ + utils/profiles.cpp \ widgets/colorpicker.cpp \ widgets/combobox.cpp \ widgets/groupbox.cpp \ @@ -96,6 +97,7 @@ HEADERS += frmmain.h \ tables/gcodetablemodel.h \ tables/heightmaptablemodel.h \ utils/interpolation.h \ + utils/profiles.h \ utils/util.h \ widgets/colorpicker.h \ widgets/combobox.h \ diff --git a/src/frmmain.cpp b/src/frmmain.cpp index 5347399..7d94dbf 100644 --- a/src/frmmain.cpp +++ b/src/frmmain.cpp @@ -33,12 +33,14 @@ #include #include #include +#include #include "frmmain.h" #include "ui_frmmain.h" #include "interface/SerialInterface.h" #include "GrIP/GrIP.h" +#include "utils/profiles.h" static const int ReceiveTimerInterval_ms = 10; @@ -113,14 +115,8 @@ frmMain::frmMain(QWidget *parent) : << "G58" << "G59"; - - // Create settings path - std::string path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation).toStdString(); - auto pos = path.rfind('/'); - if (pos != std::string::npos) { - path.erase(pos); - } - m_settingsFileName = QString::fromStdString(path) + "/Candle2" + settings_file; + m_settingsFilePath = findConfigPath(); + qDebug() << "settings file path: " << m_settingsFilePath; // Loading settings preloadSettings(); @@ -374,6 +370,31 @@ frmMain::frmMain(QWidget *parent) : m_timerSend.start(SendTimerInterval_ms); } +QString frmMain::findConfigPath() { + QString profileNameFromEnvironment = getenv("CANDLE_PROFILE"); + if (profileNameFromEnvironment.length() > 0) { + return configPathForProfile(profileNameFromEnvironment); + } + + const auto profiles = getProfileNames(); + if (profiles.length() == 0) { + // first run, no profiles at all yet + return configPathForProfile(default_profile_name); + } else if (profiles.length() == 1) { + // only one profile exists, no point asking which one + return configPathForProfile(profiles[0]); + } else { + bool ok; + const auto pickedItem = QInputDialog::getItem(this, "Choose Profile", "Choose a profile:", profiles, 0, false, &ok); + if (ok) { + return configPathForProfile(pickedItem); + } else { + // user cancelled + exit(0); + } + } +} + void frmMain::UpdateComPorts() { // Clear combobox @@ -381,7 +402,7 @@ void frmMain::UpdateComPorts() ui->comboHandwheel->clear(); // Add available ports to combobox - foreach (QSerialPortInfo info ,QSerialPortInfo::availablePorts()) + foreach (QSerialPortInfo info, QSerialPortInfo::availablePorts()) { ui->comboInterface->insertItem(0, info.portName()); ui->comboHandwheel->insertItem(0, info.portName()); @@ -468,7 +489,7 @@ double frmMain::toolZPosition() void frmMain::preloadSettings() { - QSettings set(m_settingsFileName, QSettings::IniFormat); + QSettings set(m_settingsFilePath, QSettings::IniFormat); set.setIniCodec("UTF-8"); qApp->setStyleSheet(QString(qApp->styleSheet()).replace(QRegExp("font-size:\\s*\\d+"), "font-size: " + set.value("fontSize", "8").toString())); diff --git a/src/frmmain.h b/src/frmmain.h index 082f3e1..fea0281 100644 --- a/src/frmmain.h +++ b/src/frmmain.h @@ -326,6 +326,7 @@ private slots: void jogStep(); void updateJogTitle(); + QString findConfigPath(); Ui::frmMain *ui; GcodeViewParse m_viewParser; @@ -358,7 +359,7 @@ private slots: frmSettings *m_settings; frmAbout m_frmAbout; - QString m_settingsFileName; + QString m_settingsFilePath; QString m_programFileName; QString m_heightMapFileName; QString m_lastFolder; @@ -458,9 +459,6 @@ private slots: // Size of internal GRBL buffer static const int GRBL_BUFFERLENGTH = 127; - // Name of setting file - const QString settings_file = "/settings.ini"; - QSerialPort m_serialHandWheel; }; diff --git a/src/frmmain_settings.cpp b/src/frmmain_settings.cpp index e410d68..4b2db7b 100644 --- a/src/frmmain_settings.cpp +++ b/src/frmmain_settings.cpp @@ -24,7 +24,7 @@ void frmMain::loadSettings() { - QSettings set(m_settingsFileName, QSettings::IniFormat); + QSettings set(m_settingsFilePath, QSettings::IniFormat); set.setIniCodec("UTF-8"); m_settingsLoading = true; @@ -194,7 +194,7 @@ void frmMain::loadSettings() void frmMain::saveSettings() { - QSettings set(m_settingsFileName, QSettings::IniFormat); + QSettings set(m_settingsFilePath, QSettings::IniFormat); set.setIniCodec("UTF-8"); set.setValue("ipaddress", m_settings->IPAddress()); diff --git a/src/utils/profiles.cpp b/src/utils/profiles.cpp new file mode 100644 index 0000000..b3830ea --- /dev/null +++ b/src/utils/profiles.cpp @@ -0,0 +1,20 @@ +#include "profiles.h" +#include + +// chosen for backward compatibility with pre-profiles versions, which always stored settings in `settings.ini` +const QString default_profile_name = "settings"; + +QStringList getProfileNames() { + const QDir configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + const QStringList configFiles = configDir.entryList({"*.ini"}, QDir::Files); + QStringList rv; + for (QString configFile : configFiles) { + rv << QUrl::fromPercentEncoding(configFile.replace(QRegExp("\\.ini$"), "").toUtf8()); + } + return rv; +} + +QString configPathForProfile(QString profile_name) { + const QDir configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + return configDir.filePath(QUrl::toPercentEncoding(profile_name) + ".ini"); +} diff --git a/src/utils/profiles.h b/src/utils/profiles.h new file mode 100644 index 0000000..90be7c0 --- /dev/null +++ b/src/utils/profiles.h @@ -0,0 +1,11 @@ +#ifndef PROFILES_H +#define PROFILES_H +#include +#include +#include + +extern const QString default_profile_name; +QStringList getProfileNames(); +QString configPathForProfile(QString profile_name); + +#endif // PROFILES_H