diff --git a/CMakeLists.txt b/CMakeLists.txt index c6474b3..095d698 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,73 +1,73 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(atelier) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(KDECompilerSettings) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMInstallIcons) if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() set(CMAKE_CXX_STANDARD 11) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(QT_MIN_VERSION "5.9.0") set(KF5_DEP_VERSION "5.30.0") -set(KDE_APPLICATIONS_VERSION_MAJOR "1") -set(KDE_APPLICATIONS_VERSION_MINOR "0") +set(KDE_APPLICATIONS_VERSION_MAJOR "0") +set(KDE_APPLICATIONS_VERSION_MINOR "70") set(KDE_APPLICATIONS_VERSION_MICRO "0") set(KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") #Atelier Dependencies find_package(AtCore REQUIRED COMPONENTS AtCore AtCoreWidgets ) find_package(Qwt6 REQUIRED) find_package(KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS I18n XmlGui ConfigWidgets TextEditor ) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Widgets SerialPort Charts Quick Qml 3DCore 3DExtras 3DRender 3DInput Multimedia MultimediaWidgets ) if(BUILD_TESTING) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG REQUIRED) endif() # config.h configure_file (config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/src/config.h) include(ECMPoQmTools) include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(src) add_subdirectory(deploy) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/dialogs/profilesdialog.cpp b/src/dialogs/profilesdialog.cpp index 540ea55..3798005 100644 --- a/src/dialogs/profilesdialog.cpp +++ b/src/dialogs/profilesdialog.cpp @@ -1,284 +1,281 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2016> Author: Lays Rodrigues - lays.rodrigues@kde.org Chris Rizzitello - rizzitello@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #include #include #include #include #include "profilesdialog.h" #include "ui_profilesdialog.h" ProfilesDialog::ProfilesDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::ProfilesDialog) , m_modified(false) { ui->setupUi(this); ui->firmwareCB->addItem(QStringLiteral("Auto-Detect")); ui->firmwareCB->addItems(detectFWPlugins()); ui->baudCB->addItems(SERIAL::BAUDS); ui->baudCB->setCurrentText(QLatin1String("115200")); ui->profileCB->setAutoCompletion(true); connect(ui->profileCB, static_cast(&QComboBox::currentIndexChanged), this, [this] { askToSave(); loadSettings(); }); updateCBProfiles(); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ProfilesDialog::buttonBoxClicked); connect(ui->heatedBedCK, &QCheckBox::clicked, this, [this](const bool & status) { ui->bedTempSB->setEnabled(status); }); connect(ui->cartesianRB, &QRadioButton::clicked, this, [this] { ui->printerTypeStack->setCurrentIndex(1); }); connect(ui->deltaRB, &QRadioButton::clicked, this, [this] { ui->printerTypeStack->setCurrentIndex(0); }); connect(ui->removeProfilePB, &QPushButton::clicked, this, &ProfilesDialog::removeProfile); -#ifdef Q_OS_LINUX - ui->removeProfilePB->setIcon(QIcon::fromTheme("edit-delete")); -#else - ui->removeProfilePB->setIcon(style()->standardIcon(QStyle::SP_TrashIcon)); -#endif + ui->removeProfilePB->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"), style()->standardIcon(QStyle::SP_TrashIcon))); + //if any control is modifed and no load / save has happend contents are not saved. auto modify = [this] {setModified(true);}; connect(ui->baudCB, &QComboBox::currentTextChanged, modify); connect(ui->radiusSB, &QSpinBox::editingFinished, modify); connect(ui->z_delta_dimensionSB, &QSpinBox::editingFinished, modify); connect(ui->x_dimensionSB, &QSpinBox::editingFinished, modify); connect(ui->y_dimensionSB, &QSpinBox::editingFinished, modify); connect(ui->z_dimensionSB, &QSpinBox::editingFinished, modify); connect(ui->heatedBedCK, &QCheckBox::stateChanged, modify); connect(ui->bedTempSB, &QSpinBox::editingFinished, modify); connect(ui->extruderTempSB, &QSpinBox::editingFinished, modify); connect(ui->postPauseLE, &QLineEdit::editingFinished, modify); connect(ui->firmwareCB, &QComboBox::currentTextChanged, modify); } ProfilesDialog::~ProfilesDialog() { delete ui; } void ProfilesDialog::buttonBoxClicked(QAbstractButton *btn) { switch (ui->buttonBox->buttonRole(btn)) { case QDialogButtonBox::ResetRole: askToSave(); loadSettings(); break; case QDialogButtonBox::RejectRole: askToSave(); close(); break; case QDialogButtonBox::AcceptRole: saveSettings(); break; default: break; } } void ProfilesDialog::saveSettings() { m_settings.beginGroup(QStringLiteral("Profiles")); QStringList groups = m_settings.childGroups(); m_settings.endGroup(); if (groups.contains(ui->profileCB->currentText())) { int ret = QMessageBox::warning( this , i18n("Overwrite Profile?") , i18n("A profile with this name already exists. \n Are you sure you want to overwrite it?") , QMessageBox::Save , QMessageBox::Cancel ); if (ret == QMessageBox::Cancel) { return; } } save(); } void ProfilesDialog::save() { QString currentProfile = ui->profileCB->currentText(); //Add indent to better view of the data m_settings.beginGroup(QStringLiteral("Profiles")); m_settings.beginGroup(currentProfile); //BED if (ui->cartesianRB->isChecked()) { m_settings.setValue(QStringLiteral("isCartesian"), true); m_settings.setValue(QStringLiteral("dimensionX"), ui->x_dimensionSB->value()); m_settings.setValue(QStringLiteral("dimensionY"), ui->y_dimensionSB->value()); m_settings.setValue(QStringLiteral("dimensionZ"), ui->z_dimensionSB->value()); } else { m_settings.setValue(QStringLiteral("isCartesian"), false); m_settings.setValue(QStringLiteral("radius"), ui->radiusSB->value()); m_settings.setValue(QStringLiteral("z_delta_dimension"), ui->z_dimensionSB->value()); } m_settings.setValue(QStringLiteral("heatedBed"), ui->heatedBedCK->isChecked()); m_settings.setValue(QStringLiteral("maximumTemperatureBed"), ui->bedTempSB->value()); //HOTEND m_settings.setValue(QStringLiteral("maximumTemperatureExtruder"), ui->extruderTempSB->value()); //Baud m_settings.setValue(QStringLiteral("bps"), ui->baudCB->currentText()); m_settings.setValue(QStringLiteral("firmware"), ui->firmwareCB->currentText()); m_settings.setValue(QStringLiteral("postPause"), ui->postPauseLE->text()); m_settings.endGroup(); m_settings.endGroup(); //Load new profile setModified(false); updateCBProfiles(); loadSettings(currentProfile); emit updateProfiles(); } void ProfilesDialog::loadSettings(const QString ¤tProfile) { m_settings.beginGroup(QStringLiteral("Profiles")); const QString profileName = currentProfile.isEmpty() ? ui->profileCB ->currentText() : currentProfile; ui->profileCB->setCurrentText(profileName); m_settings.beginGroup(profileName); //BED if (m_settings.value(QStringLiteral("isCartesian")).toBool()) { ui->printerTypeStack->setCurrentIndex(1); ui->cartesianRB->setChecked(true); ui->deltaRB->setChecked(false); ui->x_dimensionSB->setValue(m_settings.value(QStringLiteral("dimensionX"), QStringLiteral("0")).toInt()); ui->y_dimensionSB->setValue(m_settings.value(QStringLiteral("dimensionY"), QStringLiteral("0")).toInt()); ui->z_dimensionSB->setValue(m_settings.value(QStringLiteral("dimensionZ"), QStringLiteral("0")).toInt()); } else { ui->printerTypeStack->setCurrentIndex(0); ui->deltaRB->setChecked(true); ui->cartesianRB->setChecked(false); - ui->radiusSB->setValue(m_settings.value(QStringLiteral("radius"), QStringLiteral("0")).toFloat()); - ui->z_delta_dimensionSB->setValue(m_settings.value(QStringLiteral("z_delta_dimension"), QStringLiteral("0")).toFloat()); + ui->radiusSB->setValue(m_settings.value(QStringLiteral("radius"), QStringLiteral("0")).toInt()); + ui->z_delta_dimensionSB->setValue(m_settings.value(QStringLiteral("z_delta_dimension"), QStringLiteral("0")).toInt()); } ui->heatedBedCK->setChecked(m_settings.value(QStringLiteral("heatedBed"), QStringLiteral("true")).toBool()); ui->bedTempSB->setEnabled(ui->heatedBedCK->isChecked()); ui->bedTempSB->setValue(m_settings.value(QStringLiteral("maximumTemperatureBed"), QStringLiteral("0")).toInt()); //HOTEND ui->extruderTempSB->setValue(m_settings.value(QStringLiteral("maximumTemperatureExtruder"), QStringLiteral("0")).toInt()); //Baud ui->baudCB->setCurrentText(m_settings.value(QStringLiteral("bps"), QStringLiteral("115200")).toString()); ui->firmwareCB->setCurrentText(m_settings.value(QStringLiteral("firmware"), QStringLiteral("Auto-Detect")).toString()); ui->postPauseLE->setText(m_settings.value(QStringLiteral("postPause"), QStringLiteral("")).toString()); m_settings.endGroup(); m_settings.endGroup(); setModified(false); } void ProfilesDialog::updateCBProfiles() { m_settings.beginGroup(QStringLiteral("Profiles")); QStringList groups = m_settings.childGroups(); m_settings.endGroup(); if (groups.isEmpty()) { ui->printerTypeStack->setCurrentIndex(1); } ui->profileCB->clear(); ui->profileCB->addItems(groups); } void ProfilesDialog::removeProfile() { QString currentProfile = ui->profileCB->currentText(); m_settings.beginGroup(QStringLiteral("Profiles")); m_settings.beginGroup(currentProfile); m_settings.remove(""); m_settings.endGroup(); m_settings.remove(currentProfile); m_settings.endGroup(); updateCBProfiles(); } QStringList ProfilesDialog::detectFWPlugins() { QStringList firmwares; QStringList paths = AtCoreDirectories::pluginDir; //Add our runtime paths paths.prepend(qApp->applicationDirPath() + QStringLiteral("/../Plugins/AtCore")); paths.prepend(qApp->applicationDirPath() + QStringLiteral("/AtCore")); paths.prepend(qApp->applicationDirPath() + QStringLiteral("/plugins")); for (const QString &path : paths) { firmwares = firmwaresInPath(path); if (!firmwares.isEmpty()) { //use path where plugins were detected. break; } } return firmwares; } QStringList ProfilesDialog::firmwaresInPath(const QString &path) { QStringList firmwares; QStringList files = QDir(path).entryList(QDir::Files); for (const QString &f : files) { QString file = f; #if defined(Q_OS_WIN) if (file.endsWith(QStringLiteral(".dll"))) #elif defined(Q_OS_MAC) if (file.endsWith(QStringLiteral(".dylib"))) #else if (file.endsWith(QStringLiteral(".so"))) #endif file = file.split(QChar::fromLatin1('.')).at(0); else { continue; } if (file.startsWith(QStringLiteral("lib"))) { file = file.remove(QStringLiteral("lib")); } file = file.toLower().simplified(); firmwares.append(file); } return firmwares; } void ProfilesDialog::setModified(bool modified) { m_modified = modified; } void ProfilesDialog::askToSave() { if (m_modified) { int ret = QMessageBox::question( this , i18n("Save?") , i18n("This Profile has been modified, Would you like to Save it?") , QMessageBox::Save , QMessageBox::No ); if (ret == QMessageBox::Save) { save(); } } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b51cbcf..d087ec6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,475 +1,475 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2016> Author: Lays Rodrigues - lays.rodrigues@kde.org Chris Rizzitello - rizzitello@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "dialogs/choosefiledialog.h" #include "dialogs/profilesdialog.h" #include "mainwindow.h" #include "widgets/3dview/viewer3d.h" #include "widgets/atcoreinstancewidget.h" #include "widgets/videomonitorwidget.h" #include "widgets/welcomewidget.h" MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent) , m_currEditorView(nullptr) , m_theme(getTheme()) , m_instances(new QTabWidget(this)) { initWidgets(); setupActions(); setAcceptDrops(true); connect(m_instances, &QTabWidget::tabCloseRequested, this, [this](int index) { auto tempWidget = qobject_cast(m_instances->widget(index)); if (tempWidget->isPrinting()) { if (askToClose()) { delete tempWidget; } else { return; } } else { delete tempWidget; } if (m_instances->count() == 1) { m_instances->setTabsClosable(false); m_instances->setMovable(false); } }); } void MainWindow::closeEvent(QCloseEvent *event) { if (!askToSave(m_gcodeEditor->modifiedFiles())) { event->ignore(); } bool closePrompt = false; for (int i = 0; i < m_instances->count(); i++) { AtCoreInstanceWidget *instance = qobject_cast(m_instances->widget(i)); if (instance->isPrinting()) { closePrompt = true; break; } } if (closePrompt) { if (askToClose()) { event->accept(); } else { event->ignore(); } } } void MainWindow::dragEnterEvent(QDragEnterEvent *event) { event->accept(); } void MainWindow::dropEvent(QDropEvent *event) { const QMimeData *mimeData = event->mimeData(); if (mimeData->hasUrls()) { processDropEvent(mimeData->urls()); } } void MainWindow::processDropEvent(const QList &fileList) { for (const auto &url : fileList) { //Loop thru the urls and only load ones ending our "supported" formats QString ext = url.toLocalFile().split('.').last(); if (ext.contains("gcode", Qt::CaseInsensitive) || ext.contains("gco", Qt::CaseInsensitive)) { loadFile(url); } } } void MainWindow::initWidgets() { setupLateralArea(); newAtCoreInstance(); // View: // Sidebar, Sidebar Controls, Printer Tabs. // Sidebar Controls and Printer Tabs can be resized, Sidebar can't. auto splitter = new QSplitter(); splitter->addWidget(m_lateral.m_stack); splitter->addWidget(m_instances); auto addTabBtn = new QToolButton(); addTabBtn->setIconSize(QSize(fontMetrics().lineSpacing(), fontMetrics().lineSpacing())); addTabBtn->setIcon(QIcon::fromTheme("list-add", QIcon(QString(":/%1/addTab").arg(m_theme)))); addTabBtn->setToolTip(i18n("Create new instance")); addTabBtn->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T)); connect(addTabBtn, &QToolButton::clicked, this, &MainWindow::newAtCoreInstance); m_instances->setCornerWidget(addTabBtn, Qt::TopLeftCorner); auto *centralLayout = new QHBoxLayout(); centralLayout->addWidget(m_lateral.m_toolBar); centralLayout->addWidget(splitter); auto *centralWidget = new QWidget(); centralWidget->setLayout(centralLayout); setCentralWidget(centralWidget); } void MainWindow::newAtCoreInstance() { auto newInstanceWidget = new AtCoreInstanceWidget(); QString name = QString::number(m_instances->addTab(newInstanceWidget, i18n("Connect a printer"))); newInstanceWidget->setObjectName(name); newInstanceWidget->setFileCount(m_openFiles.size()); connect(this, &MainWindow::profilesChanged, newInstanceWidget, &AtCoreInstanceWidget::updateProfileData); connect(newInstanceWidget, &AtCoreInstanceWidget::requestProfileDialog, this, [this] { std::unique_ptr pd(new ProfilesDialog); pd->exec(); emit(profilesChanged()); }); connect(newInstanceWidget, &AtCoreInstanceWidget::requestFileChooser, this, [newInstanceWidget, this] { QUrl file; switch (m_openFiles.size()) { case 0: QMessageBox::warning(this, i18n("Error"), i18n("There's no GCode File open. \n Please select a file and try again."), QMessageBox::Ok); break; case 1: file = m_openFiles.at(0); break; default: ChooseFileDialog dialog(this, m_openFiles); if (dialog.exec() == QDialog::Accepted) { file = dialog.choosenFile(); } break; } if (m_gcodeEditor->modifiedFiles().contains(file)) { int result = QMessageBox::question( this , i18n("Document Modified") , i18n("%1 \n Contains Unsaved Changes That will not be in the print.\n Would you like to Save before printing?", file.toLocalFile()) , QMessageBox::Save , QMessageBox::Cancel , QMessageBox::Ignore ); if (result == QMessageBox::Cancel) { return; } else if (result == QMessageBox::Save) { m_gcodeEditor->saveFile(file); } } newInstanceWidget->printFile(file); }); connect(newInstanceWidget, &AtCoreInstanceWidget::connectionChanged, this, &MainWindow::atCoreInstanceNameChange); if (m_instances->count() > 1) { m_instances->setTabsClosable(true); m_instances->setMovable(true); m_instances->setCurrentIndex(m_instances->count() - 1); } } // Move to LateralArea. void MainWindow::setupLateralArea() { m_lateral.m_toolBar = new QWidget(); m_lateral.m_stack = new QStackedWidget(); auto buttonLayout = new QVBoxLayout(); auto setupButton = [this, buttonLayout](const QString & key, const QString & text, const QIcon & icon, QWidget * w) { auto *btn = new QPushButton(m_lateral.m_toolBar); btn->setToolTip(text); btn->setAutoExclusive(true); btn->setCheckable(true); //3d view is on top set it checked so users see its selected. btn->setChecked(key == QStringLiteral("welcome")); btn->setIcon(icon); //Set an iconSize based on the DPI. //96 was considered to be the "standard" DPI for years. //Hi-dpi monitors have a higher DPI //Tiny or old screen could have a lower DPI. //Start our iconSize at 16 so with a DPI less then 96 we get a sane iconsize. int iconSize = 16 + ((logicalDpiX() / 96) * 16); btn->setIconSize(QSize(iconSize, iconSize)); btn->setFixedSize(btn->iconSize()); btn->setFlat(true); m_lateral.m_stack->addWidget(w); m_lateral.m_map[key] = {btn, w}; buttonLayout->addWidget(btn); connect(btn, &QPushButton::clicked, this, [this, w, btn] { if (m_lateral.m_stack->currentWidget() == w) { m_lateral.m_stack->setHidden(m_lateral.m_stack->isVisible()); if (m_lateral.m_stack->isHidden()) { btn->setCheckable(false); btn->setCheckable(true); } } else { m_lateral.m_stack->setHidden(false); m_lateral.m_stack->setCurrentWidget(w); } toggleGCodeActions(); }); }; m_gcodeEditor = new GCodeEditorWidget(this); connect(m_gcodeEditor, &GCodeEditorWidget::updateClientFactory, this, &MainWindow::updateClientFactory); connect(m_gcodeEditor, &GCodeEditorWidget::droppedUrls, this, &MainWindow::processDropEvent); connect(m_gcodeEditor, &GCodeEditorWidget::fileClosed, this, [this](const QUrl & file) { m_openFiles.removeAll(file); }); auto *viewer3D = new Viewer3D(this); connect(viewer3D, &Viewer3D::droppedUrls, this, &MainWindow::processDropEvent); - connect(m_gcodeEditor, &GCodeEditorWidget::currentFileChanged, this, [this, viewer3D](const QUrl & url) { + connect(m_gcodeEditor, &GCodeEditorWidget::currentFileChanged, this, [viewer3D](const QUrl & url) { viewer3D->drawModel(url.toString()); }); setupButton("welcome", i18n("&Welcome"), QIcon::fromTheme("go-home", QIcon(QString(":/%1/home").arg(m_theme))), new WelcomeWidget(this)); setupButton("3d", i18n("&3D"), QIcon::fromTheme("draw-cuboid", QIcon(QString(":/%1/3d").arg(m_theme))), viewer3D); setupButton("gcode", i18n("&GCode"), QIcon::fromTheme("accessories-text-editor", QIcon(":/icon/edit")), m_gcodeEditor); setupButton("video", i18n("&Video"), QIcon::fromTheme("camera-web", QIcon(":/icon/video")), new VideoMonitorWidget(this)); buttonLayout->addStretch(); m_lateral.m_toolBar->setLayout(buttonLayout); } void MainWindow::setupActions() { // Actions for the Toolbar QAction *action; action = actionCollection()->addAction(QStringLiteral("open")); action->setIcon(QIcon::fromTheme("document-open", QIcon(QString(":/%1/open").arg(m_theme)))); action->setText(i18n("&Open")); actionCollection()->setDefaultShortcut(action, QKeySequence::Open); connect(action, &QAction::triggered, this, &MainWindow::openActionTriggered); action = actionCollection()->addAction(QStringLiteral("new_instance")); action->setIcon(QIcon::fromTheme("list-add", QIcon(QString(":/%1/addTab").arg(m_theme)))); action->setText(i18n("&New Connection")); actionCollection()->setDefaultShortcut(action, QKeySequence::AddTab); connect(action, &QAction::triggered, this, &MainWindow::newAtCoreInstance); action = actionCollection()->addAction(QStringLiteral("profiles")); action->setIcon(QIcon::fromTheme("document-properties", QIcon(QString(":/%1/configure").arg(m_theme)))); action->setText(i18n("&Profiles")); connect(action, &QAction::triggered, this, [this] { std::unique_ptr pd(new ProfilesDialog); pd->exec(); emit(profilesChanged()); }); action = actionCollection()->addAction(QStringLiteral("quit")); action->setIcon(QIcon::fromTheme("application-exit", QIcon(":/icon/exit"))); action->setText(i18n("&Quit")); actionCollection()->setDefaultShortcut(action, QKeySequence::Quit); connect(action, &QAction::triggered, this, &MainWindow::close); setupGUI(Default, "atelierui"); } void MainWindow::openActionTriggered() { QList fileList = QFileDialog::getOpenFileUrls( this , i18n("Open GCode") , QUrl::fromLocalFile(QDir::homePath()) , i18n("GCode(*.gco *.gcode);;All Files(*.*)") ); for (const auto &url : fileList) { loadFile(url); } } void MainWindow::loadFile(const QUrl &fileName) { if (!fileName.isEmpty()) { m_lateral.get("gcode")->loadFile(fileName); m_lateral.get("3d")->drawModel(fileName.toString()); // Make 3dview focused when opening a file if (m_openFiles.isEmpty() && m_lateral.m_stack->currentWidget() == m_lateral.get("welcome")) { m_lateral.getButton("3d")->setChecked(true); m_lateral.m_stack->setCurrentWidget(m_lateral.get("3d")); } const int tabs = m_instances->count(); if (!m_openFiles.contains(fileName)) { m_openFiles.append(fileName); } for (int i = 0; i < tabs; ++i) { auto instance = qobject_cast(m_instances->widget(i)); instance->setFileCount(m_openFiles.size()); } } } void MainWindow::atCoreInstanceNameChange(const QString &name) { m_instances->setTabText(sender()->objectName().toInt(), name); } QString MainWindow::getTheme() { return palette().text().color().value() >= QColor(Qt::lightGray).value() ? \ QString("dark") : QString("light"); } bool MainWindow::askToClose() { bool rtn = false; int result = QMessageBox::question( this , i18n("Printing") , i18n("Currently printing! \nAre you sure you want to close?") , QMessageBox::Close , QMessageBox::Cancel ); switch (result) { case QMessageBox::Close: rtn = true; break; default: break; } return rtn; } void MainWindow::toggleGCodeActions() { if (m_lateral.m_stack->currentWidget() == m_lateral.m_map["gcode"].second && m_lateral.m_stack->isVisible()) { if (m_currEditorView) { guiFactory()->addClient(m_currEditorView); } } else { guiFactory()->removeClient(m_currEditorView); } } void MainWindow::updateClientFactory(KTextEditor::View *view) { if (m_lateral.m_stack->currentWidget() == m_lateral.m_map["gcode"].second) { if (m_currEditorView) { guiFactory()->removeClient(m_currEditorView); } if (view) { guiFactory()->addClient(view); } } m_currEditorView = view; } bool MainWindow::askToSave(const QVector &fileList) { if (fileList.isEmpty()) { return true; } QSize iconSize = QSize(fontMetrics().lineSpacing(), fontMetrics().lineSpacing()); auto dialog = new QDialog(); const int padding = 30; auto listWidget = new QListWidget(); listWidget->setMinimumWidth(fontMetrics().height() / 2 * padding); for (const auto &url : fileList) { listWidget->addItem(url.toLocalFile() + " [*]"); } auto hLayout = new QHBoxLayout(); auto saveBtn = new QPushButton(QIcon::fromTheme("document-save", QIcon(QStringLiteral(":/%1/save").arg(m_theme))), i18n("Save Selected")); saveBtn->setIconSize(iconSize); connect(saveBtn, &QPushButton::clicked, this, [this, &listWidget, &fileList, &dialog] { if (!m_gcodeEditor->saveFile(fileList.at(listWidget->currentRow()))) { QMessageBox::information(this, i18n("Save Failed"), i18n("Failed to save file: %1").arg(fileList.at(listWidget->currentRow()).toLocalFile())); } else { QString txt = listWidget->item(listWidget->currentRow())->text(); txt.remove(" [*]"); listWidget->item(listWidget->currentRow())->setText(txt); for (int i = 0; i < listWidget->count(); i++) { QString string = listWidget->item(i)->text(); if (string.endsWith(" [*]")) { return; } } dialog->accept(); } }); hLayout->addWidget(saveBtn); auto saveAllBtn = new QPushButton(QIcon::fromTheme("document-save-all", QIcon(QStringLiteral(":/%1/saveAll").arg(m_theme))), i18n("Save All")); saveAllBtn->setIconSize(iconSize); connect(saveAllBtn, &QPushButton::clicked, this, [this, &listWidget, &fileList, &dialog] { for (int i = 0; i < listWidget->count(); i++) { if (!m_gcodeEditor->saveFile(fileList.at(i))) { QMessageBox::information(this, i18n("Save Failed"), i18n("Failed to save file: %1").arg(fileList.at(i).toLocalFile())); dialog->reject(); } else { QString txt = listWidget->item(listWidget->currentRow())->text(); txt.remove(" [*]"); listWidget->item(listWidget->currentRow())->setText(txt); } } dialog->accept(); }); hLayout->addWidget(saveAllBtn); auto cancelBtn = new QPushButton(QIcon::fromTheme("dialog-cancel", QIcon(QStringLiteral(":/%1/cancel").arg(m_theme))), i18n("Cancel")); cancelBtn->setIconSize(iconSize); connect(cancelBtn, &QPushButton::clicked, this, [&dialog] { dialog->reject(); }); hLayout->addWidget(cancelBtn); auto ignoreBtn = new QPushButton(QIcon::fromTheme("window-close", QIcon(QStringLiteral(":/icon/close"))), i18n("Ignore")); ignoreBtn->setIconSize(iconSize); connect(ignoreBtn, &QPushButton::clicked, this, [&dialog] { dialog->accept(); }); hLayout->addWidget(ignoreBtn); auto layout = new QVBoxLayout; auto label = new QLabel(i18n("Files with Unsaved Changes.")); layout->addWidget(label); layout->addWidget(listWidget); layout->addItem(hLayout); dialog->setLayout(layout); return dialog->exec(); } diff --git a/src/widgets/3dview/fileloader.cpp b/src/widgets/3dview/fileloader.cpp index d603247..f8c5e07 100644 --- a/src/widgets/3dview/fileloader.cpp +++ b/src/widgets/3dview/fileloader.cpp @@ -1,129 +1,129 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2017-2018> Author: Patrick José Pereira - patrickjp@kde.org Kevin Ottens - ervin@kde.org This program 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 3 of the License or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 this program. If not, see . */ #include #include #include #include #include #include "fileloader.h" namespace { const static QString _commentChar = QStringLiteral(";"); const static QStringList _moveCommands = {QStringLiteral("G0"), QStringLiteral("G1")}; const static QString _space = QStringLiteral(" "); const static QString _E = QStringLiteral("E"); const static QString _X = QStringLiteral("X"); const static QString _Y = QStringLiteral("Y"); const static QString _Z = QStringLiteral("Z"); } FileLoader::FileLoader(QString &fileName, QObject *parent) : QObject(parent) , _file(fileName) { } FileLoader::~FileLoader() { } void FileLoader::run() { QVector pos; qint64 totalSize = _file.bytesAvailable(); qint64 stillSize = totalSize; if (_file.open(QIODevice::ReadOnly)) { - float lastPerc = 0.0; + int lastPerc = 0; QTextStream in(&_file); while (!in.atEnd()) { //Get each line QString line = in.readLine(); stillSize -= line.size() + 1; // +1 endl - const float perc = (totalSize - stillSize) * 100.0 / totalSize; + const int perc = int((totalSize - stillSize) * 100.0 / totalSize); if (perc - lastPerc > 1) { - emit percentUpdate((int)perc); + emit percentUpdate(perc); lastPerc = perc; } line = line.simplified(); //Is it a comment ? Drop it if (line.isEmpty()) { continue; } //Remove comment in the end of command if (line.indexOf(_commentChar) != -1) { line.resize(line.indexOf(_commentChar)); //Remove trailing spaces line = line.simplified(); } //Split command and args QStringList commAndArgs = line.split(_space); if (_moveCommands.contains(commAndArgs[0])) { QVector4D actualPos; //Compute args commAndArgs.removeFirst(); for (QString element : commAndArgs) { if (element.contains(_X)) { actualPos.setX(element.remove(0, 1).toFloat() / 10); } if (element.contains(_Y)) { actualPos.setY(element.remove(0, 1).toFloat() / 10); } if (element.contains(_Z)) { actualPos.setZ(element.remove(0, 1).toFloat() / 10); } if (element.contains(_E)) { actualPos.setW(element.remove(0, 1).toFloat() / 10); } } if (!pos.isEmpty()) { if (!line.contains(_X)) { actualPos.setX(pos.last().x()); } if (!line.contains(_Y)) { actualPos.setY(pos.last().y()); } if (!line.contains(_Z)) { actualPos.setZ(pos.last().z()); } if (!line.contains(_E)) { actualPos.setW(pos.last().w()); } } pos.append(actualPos); } } } emit percentUpdate(100); emit posFinished(pos); }; diff --git a/src/widgets/3dview/linemesh.cpp b/src/widgets/3dview/linemesh.cpp index 28569a6..e36e63c 100644 --- a/src/widgets/3dview/linemesh.cpp +++ b/src/widgets/3dview/linemesh.cpp @@ -1,71 +1,69 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2017-2018> Author: Patrick José Pereira - patrickjp@kde.org Kevin Ottens - ervin@kde.org This program 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 3 of the License or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 this program. If not, see . */ #include #include #include #include "gcodeto4d.h" #include "linemesh.h" #include "linemeshgeometry.h" LineMesh::LineMesh(Qt3DCore::QNode *parent) : Qt3DRender::QGeometryRenderer(parent) , _lineMeshGeo(nullptr) { setInstanceCount(1); setIndexOffset(0); setFirstInstance(0); setPrimitiveType(Qt3DRender::QGeometryRenderer::LineStrip); qRegisterMetaType>("QVector"); connect(&_gcode, &GcodeTo4D::posFinished, this, &LineMesh::posUpdate); } LineMesh::~LineMesh() { } void LineMesh::readAndRun(const QString &path) { _gcode.read(path); } void LineMesh::read(const QString &path) { emit run(path); } void LineMesh::posUpdate(const QVector &pos) { QVector vertices; vertices.reserve(pos.size()); - std::transform(pos.cbegin(), pos.cend(), - std::back_inserter(vertices), - [](const QVector4D & x) { - return x.toVector3D(); - }); + std::transform(pos.cbegin(), pos.cend(), std::back_inserter(vertices), [](const QVector4D & x) { + return x.toVector3D(); + }); _lineMeshGeo = new LineMeshGeometry(vertices, this); setVertexCount(_lineMeshGeo->vertexCount()); setGeometry(_lineMeshGeo); emit finished(); } diff --git a/src/widgets/atcoreinstancewidget.cpp b/src/widgets/atcoreinstancewidget.cpp index 380f039..d0d5730 100644 --- a/src/widgets/atcoreinstancewidget.cpp +++ b/src/widgets/atcoreinstancewidget.cpp @@ -1,628 +1,628 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2017> Author: Lays Rodrigues - lays.rodrigues@kde.org Chris Rizzitello - rizzitello@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #include #include #include #include #include "atcoreinstancewidget.h" AtCoreInstanceWidget::AtCoreInstanceWidget(QWidget *parent): QWidget(parent) , m_fileCount(0) , m_printAction(nullptr) , m_stopAction(nullptr) , m_toolBar(nullptr) { m_theme = palette().text().color().value() >= QColor(Qt::lightGray).value() ? QString("dark") : QString("light") ; m_iconSize = QSize(fontMetrics().lineSpacing(), fontMetrics().lineSpacing()); QHBoxLayout *HLayout = new QHBoxLayout; m_bedExtWidget = new BedExtruderWidget; HLayout->addWidget(m_bedExtWidget); m_movementWidget = new MovementWidget(false); m_movementWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); HLayout->addWidget(m_movementWidget); QVBoxLayout *VLayout = new QVBoxLayout; VLayout->addLayout(HLayout); m_plotWidget = new PlotWidget(); VLayout->addWidget(m_plotWidget, 80); QWidget *controlTab = new QWidget(); controlTab->setLayout(VLayout); //AdvancedTab VLayout = new QVBoxLayout; m_printWidget = new PrintWidget(false); VLayout->addWidget(m_printWidget); m_commandWidget = new CommandWidget; VLayout->addWidget(m_commandWidget); m_logWidget = new LogWidget(new QTemporaryFile(QDir::tempPath() + QStringLiteral("/Atelier_"))); VLayout->addWidget(m_logWidget); m_advancedTab = new QWidget; m_advancedTab->setLayout(VLayout); m_sdWidget = new SdWidget; VLayout = new QVBoxLayout(); buildToolbar(); buildConnectionToolbar(); HLayout = new QHBoxLayout; HLayout->addWidget(m_toolBar); HLayout->addWidget(m_connectToolBar); HLayout->addWidget(m_connectButton); VLayout->addLayout(HLayout); m_toolBar->setHidden(true); m_tabWidget = new QTabWidget; m_tabWidget->addTab(controlTab, i18n("Controls")); m_tabWidget->addTab(m_advancedTab, i18n("Advanced")); m_tabWidget->addTab(m_sdWidget, i18n("Sd Card")); VLayout->addWidget(m_tabWidget); m_statusWidget = new StatusWidget(false); m_statusWidget->showPrintArea(false); VLayout->addWidget(m_statusWidget); setLayout(VLayout); enableControls(false); updateProfileData(); initConnectsToAtCore(); } AtCoreInstanceWidget::~AtCoreInstanceWidget() { m_core.closeConnection(); } void AtCoreInstanceWidget::buildToolbar() { m_toolBar = new QToolBar(); m_toolBar->setIconSize(m_iconSize); m_toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); auto lb = new QLabel; QIcon icon = QIcon::fromTheme("go-home", QIcon(QString(":/%1/home").arg(m_theme))); lb->setPixmap(icon.pixmap(m_iconSize)); m_toolBar->addWidget(lb); lb = new QLabel(i18n("Home:")); m_toolBar->addWidget(lb); auto homeAll = new QAction(i18n("All")); connect(homeAll, &QAction::triggered, this, [this] { m_core.home(); }); m_toolBar->addAction(homeAll); for (auto homes : std::map {{"X", AtCore::X}, {"Y", AtCore::Y}, {"Z", AtCore::Z}}) { auto home = new QAction(homes.first); connect(home, &QAction::triggered, this, [this, homes] { - m_core.home(homes.second); + m_core.home(uchar(homes.second)); }); m_toolBar->addAction(home); } m_toolBar->addSeparator(); m_printAction = new QAction(QIcon::fromTheme("media-playback-start", style()->standardIcon(QStyle::SP_MediaPlay)), i18n("Print")); connect(m_printAction, &QAction::triggered, this, [this] { if (m_core.state() == AtCore::BUSY) { m_logWidget->appendLog(i18n("Pause Print")); pausePrint(); return; } if (m_core.state() == AtCore::IDLE) { print(); } else if (m_core.state() == AtCore::PAUSE) { m_logWidget->appendLog(i18n("Resume Print")); m_core.resume(); } }); m_toolBar->addAction(m_printAction); m_stopAction = new QAction(QIcon::fromTheme("media-playback-stop", QIcon(QString(":/%1/stop").arg(m_theme))), i18n("Stop")); connect(m_stopAction, &QAction::triggered, this, &AtCoreInstanceWidget::stopPrint); connect(m_stopAction, &QAction::triggered, this, [this] { m_printAction->setText(i18n("Print")); m_printAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(QString(":/%1/start").arg(m_theme)))); }); m_toolBar->addAction(m_stopAction); auto disableMotorsAction = new QAction(style()->standardIcon(QStyle::SP_MediaStop), i18n("Disable Motors")); connect(disableMotorsAction, &QAction::triggered, this, &AtCoreInstanceWidget::disableMotors); m_toolBar->addAction(disableMotorsAction); togglePrintButtons(m_fileCount); } void AtCoreInstanceWidget::buildConnectionToolbar() { m_connectToolBar = new QToolBar(); m_comboPort = new QComboBox; m_comboPort->setEditable(true); QLabel *deviceLabel = new QLabel(i18n("Device")); QHBoxLayout *deviceLayout = new QHBoxLayout; deviceLayout->addWidget(deviceLabel); deviceLayout->addWidget(m_comboPort, 100); m_comboProfile = new QComboBox; m_comboProfile->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QHBoxLayout *profileLayout = new QHBoxLayout; QLabel *profileLabel = new QLabel(i18n("Profile")); profileLayout->addWidget(profileLabel); profileLayout->addWidget(m_comboProfile, 100); QHBoxLayout *connectLayout = new QHBoxLayout; connectLayout->addLayout(deviceLayout, 50); connectLayout->addLayout(profileLayout, 50); m_connectWidget = new QWidget(); m_connectWidget->setLayout(connectLayout); m_connectToolBar->addWidget(m_connectWidget); m_connectButton = new QPushButton(QIcon::fromTheme("network-connect", QIcon(QString(":/%1/connect").arg(m_theme))), i18n("Connect")); m_connectButton->setIconSize(m_iconSize); m_connectButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(this, &AtCoreInstanceWidget::disableDisconnect, m_connectButton, &QPushButton::setDisabled); connect(m_connectButton, &QPushButton::clicked, this, &AtCoreInstanceWidget::connectButtonClicked); } void AtCoreInstanceWidget::connectButtonClicked() { if (m_core.state() == AtCore::DISCONNECTED) { if (m_comboProfile->currentText().isEmpty()) { QMessageBox::information( this , i18n("No Profiles!") , i18n("Connecting Requires creating a profile for your printer. Create a profile in the next dialog then try again.") ); emit(requestProfileDialog()); return; } if (m_comboPort->currentText().isEmpty()) { QMessageBox::critical( this , i18n("Error") , i18n("Please, connect a serial device to continue!") ); return; } //Get profile data before connecting. m_profileData = readProfile(); //then connect if (m_core.initSerial(m_comboPort->currentText(), m_profileData["bps"].toInt())) { QString fw = m_profileData["firmware"].toString(); m_logWidget->appendLog(i18n("Firmware: %1", fw)); if (fw != QString("Auto-Detect")) { m_core.loadFirmwarePlugin(fw); } emit(connectionChanged(m_profileData["name"].toString())); m_profileData["heatedBed"].toBool() ? m_bedExtWidget->setBedMaxTemperature(m_profileData["bedTemp"].toInt()) : m_bedExtWidget->setBedThermoHidden(true); m_bedExtWidget->setExtruderMaxTemperature(m_profileData["hotendTemp"].toInt()); //AddFan Support to profile m_printWidget->updateFanCount(2); } } else { m_core.closeConnection(); emit(connectionChanged(i18n("Connect a Printer"))); } } void AtCoreInstanceWidget::initConnectsToAtCore() { //connect log to atcoreMessages connect(&m_core, &AtCore::atcoreMessage, m_logWidget, &LogWidget::appendLog); m_core.setSerialTimerInterval(100); // Handle device changes connect(&m_core, &AtCore::portsChanged, this, &AtCoreInstanceWidget::updateSerialPort); // Handle AtCore status change connect(&m_core, &AtCore::stateChanged, this, &AtCoreInstanceWidget::handlePrinterStatusChanged); // If the number of extruders from the printer change, we need to update the radiobuttons on the widget connect(this, &AtCoreInstanceWidget::extruderCountChanged, m_bedExtWidget, &BedExtruderWidget::setExtruderCount); // Bed and Extruder temperatures management connect(m_bedExtWidget, &BedExtruderWidget::bedTemperatureChanged, &m_core, &AtCore::setBedTemp); connect(m_bedExtWidget, &BedExtruderWidget::extTemperatureChanged, &m_core, &AtCore::setExtruderTemp); //command Widget connect(m_commandWidget, &CommandWidget::commandPressed, this, [this](const QString & command) { m_logWidget->appendLog(i18n("Push: %1", command)); m_core.pushCommand(command); }); connect(m_commandWidget, &CommandWidget::messagePressed, [this](const QString & message) { m_logWidget->appendLog(i18n("Display: %1", message)); m_core.showMessage(message); }); // Fan, Flow and Speed management connect(m_printWidget, &PrintWidget::fanSpeedChanged, &m_core, &AtCore::setFanSpeed); connect(m_printWidget, &PrintWidget::flowRateChanged, &m_core, &AtCore::setFlowRate); connect(m_printWidget, &PrintWidget::printSpeedChanged, &m_core, &AtCore::setPrinterSpeed); //Movement Widget connect(m_movementWidget, &MovementWidget::absoluteMove, this, [this](const QLatin1Char & axis, const double value) { m_logWidget->appendLog(GCode::description(GCode::G1)); m_core.move(axis, value); }); connect(m_movementWidget, &MovementWidget::unitsChanged, this, [this](int units) { auto selection = static_cast(units); m_core.setUnits(selection); }); connect(m_movementWidget, &MovementWidget::relativeMove, this, [this](const QLatin1Char & axis, const double value) { m_logWidget->appendLog(i18n("Relative Move: %1, %2", axis, QString::number(value))); m_core.setRelativePosition(); m_core.move(axis, value); m_core.setAbsolutePosition(); }); //Sd Card Stuff connect(&m_core, &AtCore::sdCardFileListChanged, m_sdWidget, &SdWidget::updateFilelist); connect(m_sdWidget, &SdWidget::requestSdList, &m_core, &AtCore::sdFileList); connect(&m_core, &AtCore::sdMountChanged, m_statusWidget, &StatusWidget::setSD); connect(m_sdWidget, &SdWidget::printSdFile, this, [this](const QString & fileName) { if (fileName.isEmpty()) { QMessageBox::information( this , i18n("Print Error") , i18n("You must Select a file from the list") ); } else { m_core.print(fileName, true); togglePrintButtons(true); } }); connect(m_sdWidget, &SdWidget::deleteSdFile, this, [this](const QString & fileName) { if (fileName.isEmpty()) { QMessageBox::information( this , i18n("Delete Error") , i18n("You must Select a file from the list") ); } else { m_core.sdDelete(fileName); } }); } void AtCoreInstanceWidget::printFile(const QUrl &fileName) { if (fileName.isEmpty()) { QMessageBox::critical( this , i18n("Filename Empty") , i18n("No filename sent from calling method, please check and try again.") ); return; } else if (!QFileInfo(fileName.toString()).isReadable()) { QMessageBox::critical( this , i18n("File not found") , i18n("%1 \nIs not readable, please check and try again.", fileName.toString()) ); return; } if (m_core.state() == AtCore::IDLE) { m_logWidget->appendLog(i18n("Printing:%1", fileName.toLocalFile())); m_core.print(fileName.toLocalFile()); } } void AtCoreInstanceWidget::print() { emit(requestFileChooser()); } void AtCoreInstanceWidget::pausePrint() { if (m_core.state() == AtCore::BUSY) { m_core.pause(m_profileData["postPause"].toString()); } else if (m_core.state() == AtCore::PAUSE) { m_core.resume(); } } void AtCoreInstanceWidget::stopPrint() { m_core.stop(); } void AtCoreInstanceWidget::disableMotors() { m_core.disableMotors(0); } void AtCoreInstanceWidget::handlePrinterStatusChanged(AtCore::STATES newState) { static QString stateString; switch (newState) { case AtCore::CONNECTING: { m_core.setSerialTimerInterval(0); m_connectButton->setText(i18n("Disconnect")); m_connectButton->setIcon(QIcon::fromTheme("network-disconnect", QIcon(QString(":/%1/disconnect").arg(m_theme)))); m_connectToolBar->setHidden(true); m_toolBar->setHidden(false); stateString = i18n("Connecting..."); m_logWidget->appendLog(i18n("Attempting to Connect")); connect(&m_core, &AtCore::receivedMessage, m_logWidget, &LogWidget::appendRLog); connect(m_core.serial(), &SerialLayer::pushedCommand, m_logWidget, &LogWidget::appendSLog); } break; case AtCore::IDLE: { stateString = i18n("Connected to %1", m_core.serial()->portName()); emit extruderCountChanged(m_core.extruderCount()); m_logWidget->appendLog(stateString); emit disableDisconnect(false); enableControls(true); connectExtruderTemperatureData(true); if (m_profileData["heatedBed"].toBool()) { connectBedTemperatureData(true); } } break; case AtCore::DISCONNECTED: { stateString = i18n("Not Connected"); disconnect(&m_core, &AtCore::receivedMessage, m_logWidget, &LogWidget::appendRLog); disconnect(m_core.serial(), &SerialLayer::pushedCommand, m_logWidget, &LogWidget::appendSLog); m_logWidget->appendLog(i18n("Serial disconnected")); m_core.setSerialTimerInterval(100); m_connectButton->setText(i18n("Connect")); m_connectButton->setIcon(QIcon::fromTheme("network-connect", QIcon(QString(":/%1/connect").arg(m_theme)))); m_connectToolBar->setHidden(false); m_toolBar->setHidden(true); enableControls(false); connectExtruderTemperatureData(false); if (m_profileData["heatedBed"].toBool()) { connectBedTemperatureData(false); } } break; case AtCore::STARTPRINT: { stateString = i18n("Starting Print"); m_statusWidget->showPrintArea(true); connect(&m_core, &AtCore::printProgressChanged, m_statusWidget, &StatusWidget::updatePrintProgress); } break; case AtCore::FINISHEDPRINT: { stateString = i18n("Finished Print"); m_statusWidget->showPrintArea(false); disconnect(&m_core, &AtCore::printProgressChanged, m_statusWidget, &StatusWidget::updatePrintProgress); m_printAction->setText(i18n("Print")); m_printAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(QString(":/%1/start").arg(m_theme)))); m_logWidget->appendLog(i18n("Finished Print Job")); } break; case AtCore::BUSY: { stateString = i18n("Printing"); emit disableDisconnect(true); m_printAction->setText(i18n("Pause")); m_printAction->setIcon(QIcon::fromTheme("media-playback-pause", QIcon(QString(":/%1/pause").arg(m_theme)))); } break; case AtCore::PAUSE: { stateString = i18n("Paused"); m_printAction->setText(i18n("Resume")); m_printAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(QString(":/%1/start").arg(m_theme)))); } break; case AtCore::STOP: { stateString = i18n("Stoping Print"); m_logWidget->appendLog(stateString); } break; case AtCore::ERRORSTATE: { stateString = i18n("Error"); } break; default: m_logWidget->appendLog(i18n("Unknown AtCore State, %1", newState)); qWarning("AtCore State not Recognized."); break; } m_statusWidget->setState(stateString); } -void AtCoreInstanceWidget::checkTemperature(uint sensorType, uint number, uint temp) +void AtCoreInstanceWidget::checkTemperature(uint sensorType, uint number, float temp) { static QString msg; switch (sensorType) { case 0x00: // bed msg = QString::fromLatin1("Bed Temperature "); break; case 0x01: // bed target msg = QString::fromLatin1("Bed Target Temperature "); break; case 0x02: // extruder msg = QString::fromLatin1("Extruder Temperature "); break; case 0x03: // extruder target msg = QString::fromLatin1("Extruder Target Temperature "); break; case 0x04: // enclosure msg = QString::fromLatin1("Enclosure Temperature "); break; case 0x05: // enclosure target msg = QString::fromLatin1("Enclosure Target Temperature "); break; } msg.append(QString::fromLatin1("[%1] : %2")); msg = msg.arg(QString::number(number)) - .arg(QString::number(temp)); + .arg(QString::number(temp, 'f', 2)); m_logWidget->appendLog(msg); } void AtCoreInstanceWidget::enableControls(bool b) { if (b) { layout()->removeWidget(m_logWidget); layout()->removeWidget(m_statusWidget); layout()->addWidget(m_statusWidget); m_advancedTab->layout()->addWidget(m_logWidget); } else { m_advancedTab->layout()->removeWidget(m_logWidget); layout()->addWidget(m_logWidget); layout()->removeWidget(m_statusWidget); layout()->addWidget(m_statusWidget); } m_tabWidget->setHidden(!b); m_toolBar->setEnabled(b); } bool AtCoreInstanceWidget::connected() { return (m_core.state() != AtCore::DISCONNECTED); } void AtCoreInstanceWidget::setFileCount(int count) { m_fileCount = count; togglePrintButtons(m_fileCount); } void AtCoreInstanceWidget::updateSerialPort(QStringList ports) { m_comboPort->clear(); //Remove any strings that match ttyS## from the port list. ports = ports.filter(QRegularExpression("^((?!ttyS\\d+).)*$")); if (!ports.isEmpty()) { m_comboPort->addItems(ports); m_logWidget->appendLog(i18n("Found %1 Ports", QString::number(ports.count()))); } else { QString portError(i18n("No available ports! Please connect a serial device to continue!")); if (!m_logWidget->endsWith(portError)) { m_logWidget->appendLog(portError); } } } void AtCoreInstanceWidget::updateProfileData() { m_settings.beginGroup("Profiles"); QStringList profiles = m_settings.childGroups(); m_settings.endGroup(); m_comboProfile->clear(); m_comboProfile->addItems(profiles); if (m_core.state() != AtCore::DISCONNECTED) { m_profileData = readProfile(); bool hBed = m_profileData["heatedBed"].toBool(); m_bedExtWidget->setBedThermoHidden(!hBed); connectBedTemperatureData(hBed); m_bedExtWidget->setBedMaxTemperature(m_profileData["bedTemp"].toInt()); m_bedExtWidget->setExtruderMaxTemperature(m_profileData["hotendTemp"].toInt()); } } void AtCoreInstanceWidget::togglePrintButtons(bool shown) { m_printAction->setVisible(shown); m_stopAction->setVisible(shown); } bool AtCoreInstanceWidget::isPrinting() { return (m_core.state() == AtCore::BUSY); } QMap AtCoreInstanceWidget::readProfile() { QString profile = m_comboProfile->currentText(); m_settings.beginGroup("Profiles"); m_settings.beginGroup(profile); QMap data{ {"bps", m_settings.value(QStringLiteral("bps"), QStringLiteral("115200"))} , {"bedTemp", m_settings.value(QStringLiteral("maximumTemperatureBed"), QStringLiteral("0"))} , {"hotendTemp", m_settings.value(QStringLiteral("maximumTemperatureExtruder"), QStringLiteral("0"))} , {"firmware", m_settings.value(QStringLiteral("firmware"), QStringLiteral("Auto-Detect"))} , {"postPause", m_settings.value(QStringLiteral("postPause"), QStringLiteral(""))} , {"heatedBed", m_settings.value(QStringLiteral("heatedBed"), true)} , {"name", profile} }; m_settings.endGroup(); m_settings.endGroup(); return data; } void AtCoreInstanceWidget::connectBedTemperatureData(bool connected) { if (connected) { if (m_plotWidget->plots().contains((i18n("Actual Bed")))) { return; } m_plotWidget->addPlot(i18n("Actual Bed")); connect(&m_core.temperature(), &Temperature::bedTemperatureChanged, [this](const float & temp) { checkTemperature(0x00, 0, temp); m_plotWidget->appendPoint(i18n("Actual Bed"), temp); m_bedExtWidget->updateBedTemp(temp); }); m_plotWidget->addPlot(i18n("Target Bed")); connect(&m_core.temperature(), &Temperature::bedTargetTemperatureChanged, [this](const float & temp) { checkTemperature(0x01, 0, temp); m_plotWidget->appendPoint(i18n("Target Bed"), temp); m_bedExtWidget->updateBedTargetTemp(temp); }); } else { if (m_plotWidget->plots().contains(i18n("Actual Bed"))) { m_plotWidget->removePlot(i18n("Actual Bed")); disconnect(&m_core.temperature(), &Temperature::bedTemperatureChanged, this, nullptr); m_plotWidget->removePlot(i18n("Target Bed")); disconnect(&m_core.temperature(), &Temperature::bedTargetTemperatureChanged, this, nullptr); } } } void AtCoreInstanceWidget::connectExtruderTemperatureData(bool connected) { if (connected) { if (m_plotWidget->plots().contains((i18n("Actual Ext.1")))) { return; } //Add Extruder. m_plotWidget->addPlot(i18n("Actual Ext.1")); connect(&m_core.temperature(), &Temperature::extruderTemperatureChanged, this, [this](const float & temp) { checkTemperature(0x02, 0, temp); m_plotWidget->appendPoint(i18n("Actual Ext.1"), temp); m_bedExtWidget->updateExtTemp(temp); }); m_plotWidget->addPlot(i18n("Target Ext.1")); connect(&m_core.temperature(), &Temperature::extruderTargetTemperatureChanged, this, [this](const float & temp) { checkTemperature(0x03, 0, temp); m_plotWidget->appendPoint(i18n("Target Ext.1"), temp); m_bedExtWidget->updateExtTargetTemp(temp); }); } else { if (m_plotWidget->plots().contains(i18n("Actual Ext.1"))) { m_plotWidget->removePlot(i18n("Actual Ext.1")); disconnect(&m_core.temperature(), &Temperature::extruderTemperatureChanged, this, nullptr); m_plotWidget->removePlot(i18n("Target Ext.1")); disconnect(&m_core.temperature(), &Temperature::extruderTargetTemperatureChanged, this, nullptr); } } } diff --git a/src/widgets/atcoreinstancewidget.h b/src/widgets/atcoreinstancewidget.h index 91716ce..c8b3aee 100644 --- a/src/widgets/atcoreinstancewidget.h +++ b/src/widgets/atcoreinstancewidget.h @@ -1,105 +1,105 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2017> Author: Lays Rodrigues - lays.rodrigues@kde.org Chris Rizzitello - rizzitello@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bedextruderwidget.h" /** * @todo write docs */ class AtCoreInstanceWidget : public QWidget { Q_OBJECT public: AtCoreInstanceWidget(QWidget *parent = nullptr); ~AtCoreInstanceWidget(); bool connected(); void setFileCount(int count); void startConnection(const QString &serialPort, const QMap &profile); public slots: bool isPrinting(); void printFile(const QUrl &fileName); void updateProfileData(); private: AtCore m_core; BedExtruderWidget *m_bedExtWidget; CommandWidget *m_commandWidget; int m_fileCount; LogWidget *m_logWidget; MovementWidget *m_movementWidget; PlotWidget *m_plotWidget; PrintWidget *m_printWidget; SdWidget *m_sdWidget; StatusWidget *m_statusWidget; QAction *m_printAction; QAction *m_stopAction; QComboBox *m_comboPort; QComboBox *m_comboProfile; QMap m_profileData; QPushButton *m_connectButton; QSettings m_settings; QSize m_iconSize; QString m_theme; QTabWidget *m_tabWidget; QToolBar *m_connectToolBar; QToolBar *m_toolBar; QWidget *m_advancedTab; QWidget *m_connectWidget; void buildConnectionToolbar(); void buildToolbar(); - void checkTemperature(uint sensorType, uint number, uint temp); + void checkTemperature(uint sensorType, uint number, float temp); void connectButtonClicked(); void connectBedTemperatureData(bool connected); void connectExtruderTemperatureData(bool connected); void disableMotors(); void enableControls(bool b); void handlePrinterStatusChanged(AtCore::STATES newState); void initConnectsToAtCore(); void stopPrint(); QMap readProfile(); void pausePrint(); void print(); void updateSerialPort(QStringList ports); void togglePrintButtons(bool shown); signals: void connectionChanged(QString name); void disableDisconnect(bool b); void extruderCountChanged(int count); void requestProfileDialog(); void requestFileChooser(); }; diff --git a/src/widgets/bedextruderwidget.cpp b/src/widgets/bedextruderwidget.cpp index 7dcba87..110a65b 100644 --- a/src/widgets/bedextruderwidget.cpp +++ b/src/widgets/bedextruderwidget.cpp @@ -1,130 +1,130 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2016> Author: Lays Rodrigues - lays.rodrigues@kde.org Tomaz Canabraza - tcanabrava@kde.org Chris Rizzitello - rizzitello@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #include #include #include #include #include "bedextruderwidget.h" #include "thermowidget.h" BedExtruderWidget::BedExtruderWidget(QWidget *parent) : QWidget(parent) , m_bedThermo(new ThermoWidget(this, QString(i18n("Bed")))) , m_extruderThermo(new ThermoWidget(this, QString(i18n("HotEnd")))) , m_extrudersLayout(new QHBoxLayout) , m_extruderBox(new QWidget(this)) { m_bedThermo->setScale(0, 150); m_extruderThermo->setScale(0, 250); m_extruderBox->setLayout(m_extrudersLayout); auto *label = new QLabel(i18n("Active Extruder:")); m_extrudersLayout->addWidget(label); auto *layout = new QHBoxLayout; layout->addWidget(m_extruderBox); layout->addWidget(m_bedThermo); layout->addWidget(m_extruderThermo); setLayout(layout); //Add Default Extruder setExtruderCount(1); - connect(m_bedThermo, &ThermoWidget::targetTemperatureChanged, this, [this](double v) { + connect(m_bedThermo, &ThermoWidget::targetTemperatureChanged, this, [this](int v) { qDebug() << "Receiving the temperature change for bed"; - emit bedTemperatureChanged((int)v, false); + emit bedTemperatureChanged(v, false); }); - connect(m_extruderThermo, &ThermoWidget::targetTemperatureChanged, this, [this](double v) { + connect(m_extruderThermo, &ThermoWidget::targetTemperatureChanged, this, [this](int v) { qDebug() << "Receiving the temperature changed for thermo"; - emit extTemperatureChanged((int)v, currentExtruder(), false); + emit extTemperatureChanged(v, currentExtruder(), false); }); } void BedExtruderWidget::setExtruderCount(int value) { value > 1 ? m_extruderBox->setVisible(true) : m_extruderBox->setVisible(false); if (value == m_extruderCount) { return; } else if (m_extruderCount < value) { //loop for the new buttons for (int i = m_extruderCount; i < value; i++) { auto *rb = new QRadioButton(QString::number(i + 1)); m_extrudersLayout->addWidget(rb); extruderMap.insert(i, rb); } } else { //remove buttons - need to test it! for (int i = m_extruderCount; i >= value; i--) { auto *rb = extruderMap.value(i); m_extrudersLayout->removeWidget(rb); extruderMap.remove(i); delete (rb); } } m_extruderCount = value; } void BedExtruderWidget::updateBedTemp(const float temp) { - m_bedThermo->setCurrentTemperature(temp); + m_bedThermo->setCurrentTemperature(double(temp)); } void BedExtruderWidget::updateExtTemp(const float temp) { - m_extruderThermo->setCurrentTemperature(temp); + m_extruderThermo->setCurrentTemperature(double(temp)); } -void BedExtruderWidget::updateBedTargetTemp(const float temp) +void BedExtruderWidget::updateBedTargetTemp(const int temp) { m_bedThermo->setTargetTemperature(temp); } -void BedExtruderWidget::updateExtTargetTemp(const float temp) +void BedExtruderWidget::updateExtTargetTemp(const int temp) { m_extruderThermo->setTargetTemperature(temp); } int BedExtruderWidget::currentExtruder() { int currExt = 0; for (int i = 0; i < extruderMap.size(); i++) { if (extruderMap.value(i)->isChecked()) { currExt = i; break; } } return currExt; } void BedExtruderWidget::setBedMaxTemperature(int value) { m_bedThermo->setScale(0, value); } void BedExtruderWidget::setExtruderMaxTemperature(int value) { m_extruderThermo->setScale(0, value); } void BedExtruderWidget::setBedThermoHidden(bool hidden) { m_bedThermo->setHidden(hidden); } diff --git a/src/widgets/bedextruderwidget.h b/src/widgets/bedextruderwidget.h index 08b5425..7db671a 100644 --- a/src/widgets/bedextruderwidget.h +++ b/src/widgets/bedextruderwidget.h @@ -1,56 +1,56 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2016> Author: Lays Rodrigues - lays.rodrigues@kde.org Tomaz Canabraza - tcanabrava@kde.org Chris Rizzitello - rizzitello@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #pragma once #include #include #include #include class ThermoWidget; class BedExtruderWidget : public QWidget { Q_OBJECT public: explicit BedExtruderWidget(QWidget *parent = nullptr); void setExtruderCount(int value); void setBedMaxTemperature(int value); void setExtruderMaxTemperature(int value); void updateBedTemp(const float temp); void updateExtTemp(const float temp); - void updateBedTargetTemp(const float temp); - void updateExtTargetTemp(const float temp); + void updateBedTargetTemp(const int temp); + void updateExtTargetTemp(const int temp); void setBedThermoHidden(bool hidden); private: int m_extruderCount = 0; ThermoWidget *m_bedThermo = nullptr; ThermoWidget *m_extruderThermo = nullptr; QMap extruderMap; QHBoxLayout *m_extrudersLayout = nullptr; QWidget *m_extruderBox = nullptr; int currentExtruder(); signals: void bedTemperatureChanged(int tmp, bool andWait); void extTemperatureChanged(int tmp, int currExt, bool andWait); }; diff --git a/src/widgets/thermowidget.cpp b/src/widgets/thermowidget.cpp index cf6ec73..2f4ba0e 100644 --- a/src/widgets/thermowidget.cpp +++ b/src/widgets/thermowidget.cpp @@ -1,288 +1,306 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2018> Author: Tomaz Canabrava - tcanabrava@kde.org Chris Rizzitello - rizzitello@kde.org Lays Rodrigues - lays.rodrigues@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #include #include #include #include #include #include #include #include #include "thermowidget.h" ThermoWidget::ThermoWidget(QWidget *parent, QString name) : QwtDial(parent) - , m_targetTemperatureNeedle(new QwtDialSimpleNeedle(QwtDialSimpleNeedle::Arrow, Qt::red, Qt::darkRed)) + , m_targetTemperatureNeedle(new QwtDialSimpleNeedle(QwtDialSimpleNeedle::Arrow, false, Qt::red, Qt::darkRed)) , m_name(name) , m_tempChangedTimer(new QTimer()) , m_currentTemperature(0) , m_targetTemperature(0) { setScaleArc(40, 320); //make our current temperature needle here so we can set it to match text color. m_currentTemperatureNeedle = new QwtDialSimpleNeedle(QwtDialSimpleNeedle::Arrow, true, palette().text().color()); setNeedle(m_currentTemperatureNeedle); setReadOnly(false); setFocusPolicy(Qt::StrongFocus); m_cursorTimer = new QTimer(); connect(m_cursorTimer, &QTimer::timeout, this, [this] { m_paintCursor = !m_paintCursor; update(); }); m_tempChangedTimer->setSingleShot(true); connect(m_tempChangedTimer, &QTimer::timeout, this, [this] { emit targetTemperatureChanged(m_targetTemperature); }); } void ThermoWidget::keyPressEvent(QKeyEvent *event) { //set our target text length. int slen = m_currentTemperatureTextFromEditor.length() - 1; // be sure our cursor posistion is valid. if (slen < 0) { m_currentTemperatureTextFromEditor = '-'; m_cursorPos = 0; } else if (slen > 2) { m_cursorPos = 2; } //parse the key events. if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) { auto tmp = m_currentTemperatureTextFromEditor; if (m_cursorPos == slen) { tmp.append(event->key()); } else { tmp.insert(m_cursorPos, event->key()); } if (tmp.startsWith('0')) { tmp.remove(0, 1); } if (tmp.contains('-')) { tmp.remove('-'); //push the cursor back to negate advancement; m_cursorPos--; } if (tmp.toInt() <= upperBound() && tmp.toInt() >= lowerBound()) { m_currentTemperatureTextFromEditor = tmp; if (m_cursorPos <= slen) { m_cursorPos++; } } } else if (event->key() == Qt::Key_Delete && m_currentTemperatureTextFromEditor.count()) { m_currentTemperatureTextFromEditor.remove(m_cursorPos, 1); if (m_cursorPos < slen) { m_cursorPos = slen; } m_cursorPos--; } else if (event->key() == Qt::Key_Backspace && m_currentTemperatureTextFromEditor.count()) { if (m_cursorPos <= slen) { m_cursorPos--; m_currentTemperatureTextFromEditor.remove(m_cursorPos, 1); } } else if (event->key() == Qt::Key_Enter) { m_targetTemperature = m_currentTemperatureTextFromEditor.toInt(); } else if (event->key() == Qt::Key_Escape) { m_currentTemperatureTextFromEditor = '0'; } else if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Plus) { - if (m_targetTemperature != upperBound()) { + if (!isEqual(m_targetTemperature, upperBound())) { m_currentTemperatureTextFromEditor = QString::number(m_targetTemperature + 1); } } else if (event->key() == Qt::Key_Down || event->key() == Qt::Key_Minus) { - if (m_targetTemperature != lowerBound()) { + if (!isEqual(m_targetTemperature, lowerBound())) { m_currentTemperatureTextFromEditor = QString::number(m_targetTemperature - 1); } } else if (event->key() == Qt::Key_PageUp) { if (m_targetTemperature + 10 > upperBound()) { m_currentTemperatureTextFromEditor = QString::number(upperBound()); } else { m_currentTemperatureTextFromEditor = QString::number(m_targetTemperature + 10); } } else if (event->key() == Qt::Key_PageDown) { if (m_targetTemperature - 10 < lowerBound()) { m_currentTemperatureTextFromEditor = QString::number(lowerBound()); } else { m_currentTemperatureTextFromEditor = QString::number(m_targetTemperature - 10); } - } - - else if (event->key() == Qt::Key_Right) { + } else if (event->key() == Qt::Key_Right) { if (m_cursorPos < slen) { m_cursorPos++; } } else if (event->key() == Qt::Key_Left) { if (m_cursorPos > 0) { m_cursorPos--; } } else { QwtDial::keyPressEvent(event); return; } if (m_targetTemperature != m_currentTemperatureTextFromEditor.toInt()) { m_targetTemperature = m_currentTemperatureTextFromEditor.toInt(); resetTimer(); update(); event->accept(); } } void ThermoWidget::wheelEvent(QWheelEvent *event) { if (event->angleDelta().y() > 0) { if (m_targetTemperature + 10 > upperBound()) { m_currentTemperatureTextFromEditor = QString::number(upperBound()); } else { m_currentTemperatureTextFromEditor = QString::number(m_targetTemperature + 10); } } else if (event->angleDelta().y() < 0) { if (m_targetTemperature - 10 < lowerBound()) { m_currentTemperatureTextFromEditor = QString::number(lowerBound()); } else { m_currentTemperatureTextFromEditor = QString::number(m_targetTemperature - 10); } } if (m_targetTemperature != m_currentTemperatureTextFromEditor.toInt()) { m_targetTemperature = m_currentTemperatureTextFromEditor.toInt(); resetTimer(); update(); } event->accept(); } void ThermoWidget::focusOutEvent(QFocusEvent *event) { if (m_targetTemperature != m_currentTemperatureTextFromEditor.toInt()) { m_targetTemperature = m_currentTemperatureTextFromEditor.toInt(); resetTimer(); event->accept(); } m_cursorTimer->stop(); m_paintCursor = false; update(); } void ThermoWidget::focusInEvent(QFocusEvent *event) { m_cursorTimer->start(1000); event->accept(); } void ThermoWidget::paintEvent(QPaintEvent *event) { QwtDial::paintEvent(event); const QString currentText = QString::number(m_currentTemperature); QFontMetrics fm(font()); - const double targetWidth = fm.width(m_currentTemperatureTextFromEditor); - const double currentWidth = fm.width(currentText); - const double nameWidth = fm.width(m_name); - const double wWidth = fm.width('W'); - const double cursorWidth = fm.width('0'); - const double height = fm.height(); - const double halfWidth = geometry().width() / 2; - const double xposTarget = halfWidth - (targetWidth / 2); - const double xposCurrent = halfWidth - (currentWidth / 2); - const double xposName = halfWidth - (nameWidth / 2); - const double xposCursor = xposTarget + (cursorWidth * m_cursorPos); - double ypos = geometry().height() / 2 + height * 2; + const int targetWidth = fm.width(m_currentTemperatureTextFromEditor); + const int currentWidth = fm.width(currentText); + const int nameWidth = fm.width(m_name); + const int wWidth = fm.width('W'); + const int cursorWidth = fm.width('0'); + const int height = fm.height(); + const int halfWidth = geometry().width() / 2; + const int xposTarget = int(halfWidth - (targetWidth / 2)); + const int xposCurrent = int(halfWidth - (currentWidth / 2)); + const int xposName = halfWidth - (nameWidth / 2); + const int xposCursor = xposTarget + (cursorWidth * m_cursorPos); + int ypos = geometry().height() / 2 + height * 2; QPainter p(this); QColor color = palette().color(QPalette::Text); //draw a box to put our target into as a user hint. - p.fillRect(QRect(halfWidth - wWidth, ypos - (height * 0.66), wWidth * 2, (height * 0.9)), palette().color(QPalette::AlternateBase)); + p.fillRect(QRect(int(halfWidth - wWidth), int(ypos - (height * 0.66)), int(wWidth * 2), int(height * 0.9)), palette().color(QPalette::AlternateBase)); if (m_paintCursor) { p.setPen(palette().color(QPalette::Text)); p.drawText(xposCursor, ypos, QChar('_')); } p.setPen(Qt::red); p.drawText(xposTarget, ypos, m_currentTemperatureTextFromEditor); ypos += height + 2; p.setPen(color); p.drawText(xposCurrent, ypos, QString::number(m_currentTemperature)); p.setPen(color); if (size().height() <= height * 6.5 && innerRect().width() <= nameWidth * 4) { p.drawText(xposName, 0 + height, m_name); } else if (size().height() >= height * 8) { p.drawText(xposName, ypos + height + 2, m_name); } else { p.drawText(xposName, geometry().height() / 2 - 12, m_name); } } void ThermoWidget::drawNeedle(QPainter *painter, const QPointF ¢er, double radius, double dir, QPalette::ColorGroup colorGroup) const { Q_UNUSED(dir); + //save a copy of radius as int to avoid casting it several times. + int radiusAsInt = int(radius); const double relativePercent = upperBound() - lowerBound(); const double currentTemperaturePercent = (m_currentTemperature - lowerBound()) / relativePercent; const double targetTemperaturePercent = (m_targetTemperature - lowerBound()) / relativePercent; const double currentTemperatureAngle = (maxScaleArc() - minScaleArc()) * currentTemperaturePercent + minScaleArc(); const double targetTemperatureAngle = (maxScaleArc() - minScaleArc()) * targetTemperaturePercent + minScaleArc(); // Qt coordinates and Qwt coordinates differ. // the "begin" of our coordinates in Qt: -130 // the "span" of our coordinates in Qt: -180 // Negative values means clockwise in Qt dialect. - const double qtBeginAngle = -130; - const double coolZone = - (targetTemperatureAngle - minScaleArc()); - int yPos = geometry().height() / 2 - radius; - int xPos = geometry().width() / 2 - radius; + const int qtBeginAngle = -130; + const int coolZone = int (- (targetTemperatureAngle - minScaleArc())); + int yPos = geometry().height() / 2 - radiusAsInt; + int xPos = geometry().width() / 2 - radiusAsInt; QRadialGradient grad(center, radius); grad.setColorAt(0.75, QColor(0, 0, 0, 0)); grad.setColorAt(0.85, QColor(255, 0, 0, 196)); grad.setColorAt(0.95, QColor(255, 110, 60, 196)); painter->setBrush(grad); - painter->drawPie(xPos, yPos, radius * 2, radius * 2, qtBeginAngle * 16, coolZone * 16); + painter->drawPie(xPos, yPos, radiusAsInt * 2, radiusAsInt * 2, qtBeginAngle * 16, coolZone * 16); m_targetTemperatureNeedle->draw(painter, center, radius * 1.3, 360 - targetTemperatureAngle - origin(), colorGroup); m_currentTemperatureNeedle->draw(painter, center, radius, 360 - currentTemperatureAngle - origin(), colorGroup); } void ThermoWidget::setCurrentTemperature(double temperature) { - if (m_currentTemperature != temperature) { + if (!isEqual(m_currentTemperature, temperature)) { m_currentTemperature = temperature; update(); } } -void ThermoWidget::setTargetTemperature(double temperature) +void ThermoWidget::setTargetTemperature(int temperature) { if (m_targetTemperature != temperature) { m_currentTemperatureTextFromEditor = QString::number(temperature); m_targetTemperature = temperature; resetTimer(); update(); } } void ThermoWidget::resetTimer() { m_tempChangedTimer->start(500); } + +bool ThermoWidget::isEqual(double a, double b) +{ +//qFuzzyCompare always returns false if a || b ==0 + if (qFuzzyIsNull(a) || qFuzzyIsNull(b)) { + if (a < 0.0 || b < 0.0) { + //One number is 0 and the other negative + //to prevent a issue if a or b == -1 and the other 0 + //we will subtract one from each value + a -= 1; + b -= 1; + } else { + a += 1; + b += 1; + } + } + return qFuzzyCompare(a, b); +} diff --git a/src/widgets/thermowidget.h b/src/widgets/thermowidget.h index e3f1f97..0da80de 100644 --- a/src/widgets/thermowidget.h +++ b/src/widgets/thermowidget.h @@ -1,65 +1,66 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2018> Author: Tomaz Canabrava - tcanabrava@kde.org Chris Rizzitello - rizzitello@kde.org Lays Rodrigues - lays.rodrigues@kde.org This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . */ #pragma once #include #include class QKeyEvent; class QPaintEvent; class QFocusEvent; class QWheelEvent; class ThermoWidget : public QwtDial { Q_OBJECT public: ThermoWidget(QWidget *parent, QString name); void drawNeedle(QPainter *painter, const QPointF ¢er, double radius, double dir, QPalette::ColorGroup colorGroup) const; void setCurrentTemperature(double temperature); - void setTargetTemperature(double temperature); + void setTargetTemperature(int temperature); signals: void targetTemperatureChanged(double targetTemperature); protected: void focusInEvent(QFocusEvent *event); void focusOutEvent(QFocusEvent *event); void keyPressEvent(QKeyEvent *event); void paintEvent(QPaintEvent *event); void wheelEvent(QWheelEvent *event); private: + bool isEqual(double a = 0, double b = 0); QwtDialSimpleNeedle *m_currentTemperatureNeedle; QwtDialSimpleNeedle *m_targetTemperatureNeedle; QString m_currentTemperatureTextFromEditor = QString("-"); QString m_name; QTimer *m_cursorTimer = nullptr; QTimer *m_tempChangedTimer = nullptr; bool m_paintCursor = false; int m_cursorPos = 0; double m_currentTemperature; - double m_targetTemperature; + int m_targetTemperature; void resetTimer(); };