File indexing completed on 2024-05-12 04:41:12

0001 /* AtCore KDE Libary for 3D Printers
0002     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0003     SPDX-FileCopyrightText: 2019-2020 Chris Rizzitello <rizzitello@kde.org>
0004 */
0005 
0006 #include "profilemanager.h"
0007 #include "atcore_default_folders.h"
0008 #include "machineinfo.h"
0009 
0010 #include <QButtonGroup>
0011 #include <QCheckBox>
0012 #include <QComboBox>
0013 #include <QCompleter>
0014 #include <QCoreApplication>
0015 #include <QDir>
0016 #include <QGroupBox>
0017 #include <QHBoxLayout>
0018 #include <QLabel>
0019 #include <QLineEdit>
0020 #include <QRadioButton>
0021 #include <QSpinBox>
0022 #include <QToolButton>
0023 
0024 ProfileManager::ProfileManager(QWidget *parent)
0025     : QWidget(parent)
0026 {
0027     auto newLabel = new QLabel(tr("Profile:"));
0028     cbProfile = new QComboBox();
0029     cbProfile->setEditable(true);
0030     cbProfile->addItems(MachineInfo::instance()->profileNames());
0031     cbProfile->setCompleter(new QCompleter(MachineInfo::instance()->profileNames()));
0032     connect(MachineInfo::instance(), &MachineInfo::profilesChanged, this, [this] {
0033         int index = cbProfile->currentIndex();
0034         cbProfile->clear();
0035         cbProfile->completer()->deleteLater();
0036         cbProfile->addItems(MachineInfo::instance()->profileNames());
0037         cbProfile->setCompleter(new QCompleter(MachineInfo::instance()->profileNames()));
0038         cbProfile->setCurrentIndex(std::min<int>(index, cbProfile->count() - 1));
0039     });
0040 
0041     connect(cbProfile, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this] {
0042         if (MachineInfo::instance()->profileNames().contains(cbProfile->currentText())) {
0043             loadProfile(cbProfile->currentText());
0044         }
0045     });
0046 
0047     connect(cbProfile->lineEdit(), &QLineEdit::editingFinished, this, &ProfileManager::onCbProfileEditingFinished);
0048 
0049     auto newHLayout = new QHBoxLayout();
0050     newHLayout->addWidget(newLabel);
0051     newHLayout->addWidget(cbProfile, 75);
0052 
0053     auto newButton = new QToolButton();
0054     newButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"), style()->standardIcon(QStyle::SP_DialogDiscardButton)));
0055     newButton->setToolTip(tr("Delete Current Profile"));
0056     newButton->setIconSize(QSize(fontMetrics().height(), fontMetrics().height()));
0057     connect(newButton, &QToolButton::clicked, this, [this] {
0058         if (!cbProfile->currentText().isEmpty()) {
0059             MachineInfo::instance()->removeProfile(cbProfile->currentText());
0060         }
0061     });
0062 
0063     auto mainLayout = new QVBoxLayout();
0064     newHLayout->addWidget(newButton);
0065     mainLayout->addLayout(newHLayout);
0066     auto profileLayout = new QVBoxLayout();
0067     auto boxLayout = new QVBoxLayout();
0068 
0069     newLabel = new QLabel(tr("Name"));
0070     lineName = new QLineEdit();
0071     connect(lineName, &QLineEdit::editingFinished, this, [this] {
0072         QString itemName = lineName->text();
0073         if (MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::NAME, itemName)) {
0074             loadProfile(itemName);
0075         }
0076     });
0077     newHLayout = new QHBoxLayout;
0078     newHLayout->addWidget(newLabel);
0079     newHLayout->addWidget(lineName);
0080     profileLayout->addLayout(newHLayout);
0081 
0082     newLabel = new QLabel(tr("Printer Type:"));
0083     radioDelta = new QRadioButton(tr("Delta"));
0084     radioCartesian = new QRadioButton(tr("Cartesian"));
0085     radioCartesian->setChecked(true);
0086     connect(radioCartesian, &QRadioButton::toggled, this, &ProfileManager::onRadioCartesianToggled);
0087 
0088     newHLayout = new QHBoxLayout;
0089     newHLayout->addWidget(newLabel);
0090     newHLayout->addWidget(radioCartesian);
0091     newHLayout->addWidget(radioDelta);
0092     boxLayout->addLayout(newHLayout);
0093 
0094     lblX = new QLabel(tr("Maximum X"));
0095     sbMaxX = new QSpinBox();
0096     sbMaxX->setMaximum(std::numeric_limits<int>::max());
0097     sbMaxX->setSuffix(QStringLiteral("mm"));
0098     connect(sbMaxX, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::XMAX, value); });
0099 
0100     newHLayout = new QHBoxLayout;
0101     newHLayout->addWidget(lblX);
0102     newHLayout->addWidget(sbMaxX);
0103     boxLayout->addLayout(newHLayout);
0104 
0105     newLabel = new QLabel(tr("Maximum Y"));
0106     sbMaxY = new QSpinBox();
0107     sbMaxY->setMaximum(std::numeric_limits<int>::max());
0108     sbMaxY->setSuffix(QStringLiteral("mm"));
0109     connect(sbMaxY, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::YMAX, value); });
0110 
0111     newHLayout = new QHBoxLayout;
0112     newHLayout->setContentsMargins(0, 0, 0, 0);
0113     newHLayout->addWidget(newLabel);
0114     newHLayout->addWidget(sbMaxY);
0115     axisY = new QWidget(this);
0116     axisY->setLayout(newHLayout);
0117     boxLayout->addWidget(axisY);
0118 
0119     lblZ = new QLabel(tr("Maximum Z"));
0120     sbMaxZ = new QSpinBox();
0121     sbMaxZ->setMaximum(std::numeric_limits<int>::max());
0122     sbMaxZ->setSuffix(QStringLiteral("mm"));
0123     connect(sbMaxZ, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::ZMAX, value); });
0124 
0125     newHLayout = new QHBoxLayout;
0126     newHLayout->addWidget(lblZ);
0127     newHLayout->addWidget(sbMaxZ);
0128     boxLayout->addLayout(newHLayout);
0129 
0130     auto groupBox = new QGroupBox(tr("Mechanics"));
0131     groupBox->setLayout(boxLayout);
0132     profileLayout->addWidget(groupBox);
0133 
0134     boxLayout = new QVBoxLayout;
0135     newLabel = new QLabel(tr("Bed Maximum"));
0136     sbMaxBedTemp = new QSpinBox();
0137     sbMaxBedTemp->setMaximum(999);
0138     sbMaxBedTemp->setSuffix(QStringLiteral(" ºC"));
0139     connect(sbMaxBedTemp, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::MAXBEDTEMP, value); });
0140 
0141     newHLayout = new QHBoxLayout;
0142     newHLayout->addWidget(newLabel);
0143     newHLayout->addWidget(sbMaxBedTemp);
0144     boxLayout->addLayout(newHLayout);
0145 
0146     sbMaxExtTemp = new QSpinBox();
0147     sbMaxExtTemp->setMaximum(999);
0148     newLabel = new QLabel(tr("Extruder Maximum"));
0149     sbMaxExtTemp->setSuffix(QStringLiteral(" ºC"));
0150     connect(sbMaxExtTemp, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::MAXEXTTEMP, value); });
0151 
0152     newHLayout = new QHBoxLayout();
0153     newHLayout->addWidget(newLabel);
0154     newHLayout->addWidget(sbMaxExtTemp);
0155     boxLayout->addLayout(newHLayout);
0156 
0157     checkAutoTempReport = new QCheckBox(tr("Auto Temperature Report"));
0158     checkAutoTempReport->setLayoutDirection(Qt::RightToLeft);
0159     connect(checkAutoTempReport, &QCheckBox::toggled, this, [this](bool checked) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::AUTOTEMPREPORT, checked); });
0160     boxLayout->addWidget(checkAutoTempReport, 0, Qt::AlignRight);
0161 
0162     groupBox = new QGroupBox(tr("Temperature"));
0163     groupBox->setLayout(boxLayout);
0164     profileLayout->addWidget(groupBox);
0165 
0166     boxLayout = new QVBoxLayout();
0167     cbBaud = new QComboBox();
0168     cbBaud->addItems(BAUDS);
0169     cbBaud->setCurrentText(QStringLiteral("115200"));
0170     connect(cbBaud, &QComboBox::currentTextChanged, this, [this](const QString &newText) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::BAUDRATE, newText); });
0171 
0172     newLabel = new QLabel(tr("Bit Rate"));
0173     newHLayout = new QHBoxLayout();
0174     newHLayout->addWidget(newLabel);
0175     newHLayout->addWidget(cbBaud);
0176     boxLayout->addLayout(newHLayout);
0177 
0178     newLabel = new QLabel(tr("Firmware"));
0179     cbFirmware = new QComboBox();
0180     cbFirmware->addItem(QStringLiteral("Auto-Detect"));
0181     cbFirmware->addItems(detectFWPlugins());
0182     connect(cbFirmware, &QComboBox::currentTextChanged, this, [this](const QString &newText) { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::FIRMWARE, newText); });
0183 
0184     newHLayout = new QHBoxLayout();
0185     newHLayout->addWidget(newLabel);
0186     newHLayout->addWidget(cbFirmware);
0187     boxLayout->addLayout(newHLayout);
0188 
0189     linePostPause = new QLineEdit();
0190     connect(linePostPause, &QLineEdit::editingFinished, this, [this] { MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::POSTPAUSE, linePostPause->text()); });
0191 
0192     newLabel = new QLabel(tr("PostPause"));
0193     newHLayout = new QHBoxLayout();
0194     newHLayout->addWidget(newLabel);
0195     newHLayout->addWidget(linePostPause);
0196     boxLayout->addLayout(newHLayout);
0197 
0198     groupBox = new QGroupBox(tr("Advanced"));
0199     groupBox->setLayout(boxLayout);
0200     profileLayout->addWidget(groupBox);
0201     groupBox = new QGroupBox(tr("Profile"));
0202     groupBox->setLayout(profileLayout);
0203     mainLayout->addWidget(groupBox);
0204     setLayout(mainLayout);
0205     loadProfile(cbProfile->currentText());
0206 }
0207 
0208 void ProfileManager::onCbProfileEditingFinished()
0209 {
0210     if (MachineInfo::instance()->profileNames().contains(cbProfile->currentText())) {
0211         loadProfile(cbProfile->currentText());
0212         return;
0213     }
0214     QMap<MachineInfo::KEY, QVariant> newProfile = {{MachineInfo::KEY::NAME, cbProfile->currentText()},
0215                                                    {MachineInfo::KEY::FIRMWARE, cbFirmware->currentText()},
0216                                                    {MachineInfo::KEY::BAUDRATE, cbBaud->currentText()},
0217                                                    {MachineInfo::KEY::POSTPAUSE, linePostPause->text()},
0218                                                    {MachineInfo::KEY::ISCARTESIAN, radioCartesian->isChecked()},
0219                                                    {MachineInfo::KEY::XMAX, sbMaxX->value()},
0220                                                    {MachineInfo::KEY::YMAX, sbMaxY->value()},
0221                                                    {MachineInfo::KEY::ZMAX, sbMaxZ->value()},
0222                                                    {MachineInfo::KEY::AUTOTEMPREPORT, checkAutoTempReport->isChecked()},
0223                                                    {MachineInfo::KEY::MAXBEDTEMP, sbMaxBedTemp->value()},
0224                                                    {MachineInfo::KEY::MAXEXTTEMP, sbMaxExtTemp->value()}};
0225     MachineInfo::instance()->storeProfile(newProfile);
0226     loadProfile(newProfile[MachineInfo::KEY::NAME].toString());
0227 }
0228 
0229 void ProfileManager::onRadioCartesianToggled(bool checked)
0230 {
0231     axisY->setVisible(checked);
0232     if (checked) {
0233         lblX->setText(tr("Maximum X"));
0234         lblZ->setText(tr("Maximum Z"));
0235     } else {
0236         lblX->setText(tr("Radius"));
0237         lblZ->setText(tr("Height"));
0238     }
0239     if (MachineInfo::instance()->profileNames().contains(cbProfile->currentText())) {
0240         MachineInfo::instance()->storeKey(cbProfile->currentText(), MachineInfo::KEY::ISCARTESIAN, checked);
0241     }
0242 }
0243 
0244 void ProfileManager::loadProfile(const QString &profileName)
0245 {
0246     if (profileName.isEmpty()) {
0247         return;
0248     }
0249     blockSignals(true);
0250     lineName->setText(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::NAME).toString());
0251     radioCartesian->setChecked(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::ISCARTESIAN).toBool());
0252     radioDelta->setChecked(!MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::ISCARTESIAN).toBool());
0253     sbMaxX->setValue(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::XMAX).toInt());
0254     sbMaxY->setValue(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::YMAX).toInt());
0255     sbMaxZ->setValue(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::ZMAX).toInt());
0256     checkAutoTempReport->setChecked(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::AUTOTEMPREPORT).toBool());
0257     sbMaxBedTemp->setValue(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::MAXBEDTEMP).toInt());
0258     sbMaxExtTemp->setValue(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::MAXEXTTEMP).toInt());
0259     cbFirmware->setCurrentText(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::FIRMWARE).toString());
0260     cbBaud->setCurrentText(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::BAUDRATE).toString());
0261     linePostPause->setText(MachineInfo::instance()->readKey(profileName, MachineInfo::KEY::POSTPAUSE).toString());
0262     cbProfile->setCurrentText(profileName);
0263     cbProfile->setCurrentIndex(MachineInfo::instance()->profileNames().indexOf(profileName));
0264     blockSignals(false);
0265 }
0266 
0267 QStringList ProfileManager::detectFWPlugins()
0268 {
0269     QStringList firmwares;
0270     for (const QString &path : AtCoreDirectories::pluginDir) {
0271         const auto pluginList = QDir(path).entryList({AtCoreDirectories::pluginExtFilter}, QDir::Files);
0272         for (QString file : pluginList) {
0273             file = file.split(QStringLiteral(".")).at(0).toLower().simplified();
0274             if (file.startsWith(QStringLiteral("lib")))
0275                 file = file.remove(QStringLiteral("lib"));
0276             firmwares.append(file);
0277         }
0278         if (!firmwares.isEmpty())
0279             break; //Use first path with valid files
0280     }
0281     return firmwares;
0282 }