diff --git a/src/widgets/axiscontrol.cpp b/src/widgets/axiscontrol.cpp index 84eb53c..f463e26 100644 --- a/src/widgets/axiscontrol.cpp +++ b/src/widgets/axiscontrol.cpp @@ -1,217 +1,123 @@ -/* Atelier KDE Printer Host for 3D Printing - Copyright (C) <2016> - Author: Lays Rodrigues - lays.rodrigues@kde.org - Chris Rizzitello - rizzitello@kde.org +/* AtCore Test Client + Copyright (C) <2016 - 2018> + + Authors: + Chris Rizzitello 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 "axiscontrol.h" -#include - -PieButton::PieButton(QLatin1Char &axis, int value, int size, int angle) : _axis(axis), _value(value) +#include +#include +#include +#include + +AxisControl::AxisControl(QWidget *parent) : + QWidget(parent) + , sbValue(new QDoubleSpinBox) { - const int delta = 16; // Qt Docs: angle is 16th of a degree. - setBrush(_palette.button()); - setStartAngle(angle * delta); - setSpanAngle(90 * delta); - setRect(QRect(QPoint(size * -1, size * -1), QPoint(size, size))); - setZValue(size * -1); - setAcceptHoverEvents(true); - setToolTip(tr("Move the hotend to the %1 by %2 units").arg(axis).arg(value)); -} + auto mainLayout = new QVBoxLayout; + auto newLabel = new QLabel(tr("Move Axis")); + sbValue->setSuffix(QStringLiteral(" mm")); + sbValue->setDecimals(3); + sbValue->setMaximum(100.0); + sbValue->setValue(1); + + auto comboUnits = new QComboBox(); + comboUnits->addItems(QStringList {QStringLiteral("Metric"), QStringLiteral("Imperial")}); + + connect(comboUnits, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int selection) { + if (selection == 0) { + sbValue->setSuffix(QStringLiteral(" mm")); + } else { + sbValue->setSuffix(QStringLiteral(" in")); + } + emit unitsChanged(selection); + }); -void PieButton::setPalette(QPalette palette) -{ - _palette = palette; -} + auto layout = new QHBoxLayout(); + layout->addWidget(newLabel); + layout->addWidget(sbValue); + layout->addWidget(comboUnits); -void PieButton::mousePressEvent(QGraphicsSceneMouseEvent *) -{ - emit clicked(_axis, _value); -} + auto newWidget = new QWidget(); + newWidget->setLayout(layout); + newWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + mainLayout->addWidget(newWidget); -void PieButton::hoverEnterEvent(QGraphicsSceneHoverEvent *) -{ - setBrush(_palette.highlight()); -} + QSize iconSize = QSize(fontMetrics().height(), fontMetrics().height()); + auto glayout = new QGridLayout(); + newLabel = new QLabel(QStringLiteral("X/Y")); + newLabel->setAlignment(Qt::AlignCenter); + glayout->addWidget(newLabel, 2, 1); -void PieButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *) -{ - setBrush(_palette.button()); -} + //Y-Axis + glayout->addWidget(makeButton(QLatin1Char('Y'), 1, iconSize, QStringLiteral("arrow-up"), QStringLiteral("↑")), 1, 1); + glayout->addWidget(makeButton(QLatin1Char('Y'), -1, iconSize, QStringLiteral("arrow-down"), QStringLiteral("↓")), 3, 1); -RectButton::RectButton(QLatin1Char &axis, int value, int size) : _axis(axis), _value(value) -{ - setBrush(_palette.button()); - setRect(QRect(QPoint(0, 0), QPoint(size, size))); - setAcceptHoverEvents(true); - setZValue(size * -1); - if (axis != QLatin1Char('E')) { - setToolTip(tr("Move the hotend to the %1 by %2 units").arg(axis).arg(value)); - } else { - setToolTip(tr("Extrude %1 Units").arg(value)); - } -} + //X-Axis + glayout->addWidget(makeButton(QLatin1Char('X'), -1, iconSize, QStringLiteral("arrow-left"), QStringLiteral("←")), 2, 0); + glayout->addWidget(makeButton(QLatin1Char('X'), 1, iconSize, QStringLiteral("arrow-right"), QStringLiteral("→")), 2, 3); -void RectButton::setPalette(QPalette palette) -{ - _palette = palette; -} + auto bottomLayout = new QHBoxLayout(); + bottomLayout->addItem(glayout); -void RectButton::mousePressEvent(QGraphicsSceneMouseEvent *) -{ - emit clicked(_axis, _value); -} + newWidget = makeSimpleAxis(QLatin1Char('Z'), iconSize); + bottomLayout->addWidget(newWidget); -void RectButton::hoverEnterEvent(QGraphicsSceneHoverEvent *) -{ - setBrush(_palette.highlight()); -} + newWidget = makeSimpleAxis(QLatin1Char('E'), iconSize); + bottomLayout->addWidget(newWidget); -void RectButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *) -{ - setBrush(_palette.button()); + mainLayout->addItem(bottomLayout); + setLayout(mainLayout); } -/* About the Magic Numbers - I don't have experience programming with QGraphicsScene, - Tomaz is helping me, but until we have a better solution, all the values - that are dividing or multiplying the items is based only in tests and errors. - Those values was chosen because it fit better on the alignment of the items - in the scene. If you have a better solution, please share with us. - Lays Rodrigues - Jan/2017 -*/ -AxisControl::AxisControl(const QList &movementValues, QWidget *parent) : - QGraphicsView(parent) -{ - setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - - setScene(new QGraphicsScene()); - - const int listSize = movementValues.size(); - int maxValue = *std::max_element(movementValues.begin(), movementValues.end()); - QList lessList = movementValues; - std::sort(lessList.begin(), lessList.end(), std::less()); - QList greaterList = movementValues; - std::sort(greaterList.begin(), greaterList.end(), std::greater()); - - auto createPie = [ this, maxValue ](QLatin1Char & axis, int value, int size, int angle) { - auto pie = new PieButton(axis, value, size, angle); - pie->setPalette(this->palette()); - connect(pie, &PieButton::clicked, this, &AxisControl::clicked); - if (abs(value) == maxValue) { - setLabels(pie, axis, value); - } - scene()->addItem(pie); - }; - - auto createRect = [ this, maxValue ](QLatin1Char & axis, int value, int size, int xPos, int yPos) { - auto z = new RectButton(axis, value, size); - z->setPalette(this->palette()); - z->setPos(xPos, yPos); - connect(z, &RectButton::clicked, this, &AxisControl::clicked); - if (abs(value) == maxValue) { - setLabels(z, axis, value); - } - scene()->addItem(z); - }; - - int currPieSize = 25; - auto xchar = QLatin1Char('X'); - auto ychar = QLatin1Char('Y'); - auto zchar = QLatin1Char('Z'); - auto echar = QLatin1Char('E'); - for (const int &value : lessList) { - createPie(xchar, value, currPieSize, -45); // Left - createPie(xchar, value * -1, currPieSize, 135); // Right - createPie(ychar, value, currPieSize, 45); // Top - createPie(ychar, value * -1, currPieSize, 225); // Bottom - currPieSize += 25; - } - - int currSize = 25; - int xPos = sceneRect().width() - 50; - int yPos = -(listSize * 25); //Align with the origin - - // Z+ - for (const int &value : greaterList) { - createRect(zchar, value, currSize, xPos, yPos); - yPos += currSize; - } - - // Z- - for (const int &value : lessList) { - createRect(zchar, -value, currSize, xPos, yPos); - yPos += currSize; - } - currSize = 25; - xPos = sceneRect().width() - 50; - yPos = -(listSize * 25); //Align with the origin - - // E- - for (const int &value : greaterList) { - createRect(echar, -value, currSize, xPos, yPos); - yPos += currSize; +QPushButton *AxisControl::makeButton(const QLatin1Char axis, int multiplier, const QSize &iconSize, const QString &themeIcon, const QString &fallbackText) +{ + auto button = new QPushButton(QIcon::fromTheme(themeIcon), QString()); + button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + if (button->icon().isNull()) { + button->setText(fallbackText); + } else { + button->setIconSize(iconSize); } - // E+ - for (const int &value : lessList) { - createRect(echar, value, currSize, xPos, yPos); - yPos += currSize; - } - setSceneRect(scene()->itemsBoundingRect()); + connect(button, &QPushButton::clicked, this, [this, axis, multiplier] { + emit clicked(axis, sbValue->value() *multiplier); + }); + return button; } -void AxisControl::resizeEvent(QResizeEvent *) +QWidget *AxisControl::makeSimpleAxis(const QLatin1Char axis, const QSize &iconSize) { - fitInView(sceneRect(), Qt::KeepAspectRatio); -} + int multiplier = 1; + if (axis == QLatin1Char('E')) { + multiplier = -1; + } -void AxisControl::setLabels(QGraphicsItem *item, QLatin1Char &axis, int value) -{ - auto *lb = new QGraphicsSimpleTextItem(); - lb->setBrush(palette().buttonText()); + auto vLayout = new QVBoxLayout; - if (this->logicalDpiX() <= 96) { - lb->setText((value < 0) ? QStringLiteral(" -") + axis : QStringLiteral(" ") + axis); - } else { - lb->setText((value < 0) ? QStringLiteral("-") + axis : QStringLiteral(" ") + axis); - } + vLayout->addWidget(makeButton(axis, multiplier, iconSize, QStringLiteral("arrow-up"), QStringLiteral("↑"))); - if (axis.toLatin1() == 'X') { - lb->setY(item->y() - lb->boundingRect().width()); - if (value < 0) { - lb->setX(item->x() - item->boundingRect().width() / 1.2 - lb->boundingRect().width() / 2); - } else { - lb->setX(item->x() + item->boundingRect().width() / 1.2 - lb->boundingRect().width() / 2); - } - } else if (axis.toLatin1() == 'Y') { - lb->setX(item->x() - lb->boundingRect().width() / 2); - if (value < 0) { - lb->setY(item->y() + item->boundingRect().height() / 1.5); - } else { - lb->setY(item->y() - item->boundingRect().height()); - } - } else { + auto label = new QLabel(QString(axis)); + label->setAlignment(Qt::AlignCenter); + vLayout->addWidget(label); - lb->setX(item->x() + lb->boundingRect().width() / fontMetrics().width(lb->text())); + multiplier *= -1; + vLayout->addWidget(makeButton(axis, multiplier, iconSize, QStringLiteral("arrow-down"), QStringLiteral("↓"))); -#ifndef Q_OS_WIN - lb->setY(item->y() - lb->boundingRect().height() / fontMetrics().xHeight()); -#else - lb->setY(item->y() - lb->boundingRect().height() / fontMetrics().height()); -#endif - } - scene()->addItem(lb); + auto widget = new QWidget(); + widget->setLayout(vLayout); + return widget; } diff --git a/src/widgets/axiscontrol.h b/src/widgets/axiscontrol.h index a0a0dc1..7624678 100644 --- a/src/widgets/axiscontrol.h +++ b/src/widgets/axiscontrol.h @@ -1,91 +1,80 @@ -/* Atelier KDE Printer Host for 3D Printing - Copyright (C) <2016> - Author: Lays Rodrigues - lays.rodrigues@kde.org - Chris Rizzitello - rizzitello@kde.org +/* AtCore Test Client + Copyright (C) <2016 - 2018> + + Authors: + Chris Rizzitello 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 "atcorewidgets_export.h" -class ATCOREWIDGETS_EXPORT PieButton : public QObject, public QGraphicsEllipseItem -{ - Q_OBJECT -public: - PieButton(QLatin1Char &axis, int value, int size, int angle); - void setPalette(QPalette palette); -protected: - void mousePressEvent(QGraphicsSceneMouseEvent *); - void hoverEnterEvent(QGraphicsSceneHoverEvent *); - void hoverLeaveEvent(QGraphicsSceneHoverEvent *); -signals: - void clicked(QLatin1Char axis, int value); -private: - QLatin1Char _axis; - int _value; - QPalette _palette; -}; - -class ATCOREWIDGETS_EXPORT RectButton : public QObject, public QGraphicsRectItem -{ - Q_OBJECT - -public: - RectButton(QLatin1Char &axis, int value, int size); - void setPalette(QPalette palette); -protected: - void mousePressEvent(QGraphicsSceneMouseEvent *); - void hoverEnterEvent(QGraphicsSceneHoverEvent *); - void hoverLeaveEvent(QGraphicsSceneHoverEvent *); -signals: - void clicked(QLatin1Char axis, int value); -private: - QLatin1Char _axis; - int _value; - QPalette _palette; -}; - /** * @brief AxisControl is a Widget to generate axis relative movements. * * Usage: * Create a instance of AxisControl and connect the clicked signal, it will give you the axis and value that was clicked. */ -class ATCOREWIDGETS_EXPORT AxisControl : public QGraphicsView +class ATCOREWIDGETS_EXPORT AxisControl : public QWidget { Q_OBJECT public: - explicit AxisControl(const QList &movementValues = {1, 10, 25}, QWidget *parent = nullptr); - -private: - void setLabels(QGraphicsItem *item, QLatin1Char &axis, int value); - -protected: - void resizeEvent(QResizeEvent *); + /** + * @brief Create a new AxisControl + * @param parent + */ + AxisControl(QWidget *parent = nullptr); + ~AxisControl() = default; signals: /** * @brief User has clicked to move an axis. * @param axis: Axis to move * @param value: Amount to move */ - void clicked(QLatin1Char axis, int value); + void clicked(const QLatin1Char axis, double value); + /** + * @brief User has changed the units. + * @param selection: Selection of Metric (0) or Imperial(1) + */ + void unitsChanged(int selection); + +private: + /** + * @brief Create A push button connected to the emit event + * @param axis: Single letter of the axis (X,Y,Z,E) + * @param multiplier: Used to set the move direction set to 1 or -1 + * @param iconSize: size to set the icon + * @param themeIcon: icon to use "fromTheme" + * @param fallbackText: Fallback text if theme fails + * @return The Created PushButton + */ + QPushButton *makeButton(const QLatin1Char axis, int multiplier, const QSize &iconSize, const QString &themeIcon, const QString &fallbackText); + + /** + * @brief makeSimpleAxis + * @param axis: Axis + * @param iconSize: Size of the icon + * @return Simple Axis Widget + */ + QWidget *makeSimpleAxis(const QLatin1Char axis, const QSize &iconSize); + QDoubleSpinBox *sbValue = nullptr; }; diff --git a/src/widgets/movementwidget.cpp b/src/widgets/movementwidget.cpp index 4e58aa7..a555593 100644 --- a/src/widgets/movementwidget.cpp +++ b/src/widgets/movementwidget.cpp @@ -1,98 +1,99 @@ /* AtCore Test Client Copyright (C) <2018> Author: 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 "axiscontrol.h" #include "movementwidget.h" MovementWidget::MovementWidget(bool showHomeAndDisableWidgets, QWidget *parent) : QWidget(parent) { auto mainLayout = new QVBoxLayout; auto hBoxLayout = new QHBoxLayout; auto newButton = new QPushButton; if (showHomeAndDisableWidgets) { newButton = new QPushButton(tr("Home All")); hBoxLayout->addWidget(newButton); connect(newButton, &QPushButton::clicked, this, [this] { emit homeAllPressed(); }); newButton = new QPushButton(tr("Home X")); hBoxLayout->addWidget(newButton); connect(newButton, &QPushButton::clicked, this, [this] { emit homeXPressed(); }); newButton = new QPushButton(tr("Home Y")); hBoxLayout->addWidget(newButton); connect(newButton, &QPushButton::clicked, this, [this] { emit homeYPressed(); }); newButton = new QPushButton(tr("Home Z")); hBoxLayout->addWidget(newButton); connect(newButton, &QPushButton::clicked, this, [this] { emit homeZPressed(); }); mainLayout->addLayout(hBoxLayout); newButton = new QPushButton(tr("Disable Motors")); mainLayout->addWidget(newButton); connect(newButton, &QPushButton::clicked, this, [this] { emit disableMotorsPressed(); }); } comboMoveAxis = new QComboBox; comboMoveAxis->addItem(tr("Move X Axis to")); comboMoveAxis->addItem(tr("Move Y Axis to")); comboMoveAxis->addItem(tr("Move Z Axis to")); sbMoveAxis = new QDoubleSpinBox; sbMoveAxis->setRange(0, 200); newButton = new QPushButton(tr("Go")); connect(newButton, &QPushButton::clicked, this, [this] { if (comboMoveAxis->currentIndex() == 0) { emit absoluteMove(QLatin1Char('X'), sbMoveAxis->value()); } else if (comboMoveAxis->currentIndex() == 1) { emit absoluteMove(QLatin1Char('Y'), sbMoveAxis->value()); } else if (comboMoveAxis->currentIndex() == 2) { emit absoluteMove(QLatin1Char('Z'), sbMoveAxis->value()); } }); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(comboMoveAxis); hBoxLayout->addWidget(sbMoveAxis); hBoxLayout->addWidget(newButton); mainLayout->addLayout(hBoxLayout); auto axisControl = new AxisControl; mainLayout->addWidget(axisControl); connect(axisControl, &AxisControl::clicked, this, &MovementWidget::relativeMove); + connect(axisControl, &AxisControl::unitsChanged, this, &MovementWidget::unitsChanged); setLayout(mainLayout); } diff --git a/src/widgets/movementwidget.h b/src/widgets/movementwidget.h index 6514838..a94a2d1 100644 --- a/src/widgets/movementwidget.h +++ b/src/widgets/movementwidget.h @@ -1,93 +1,100 @@ /* AtCore Test Client Copyright (C) <2018> Author: 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 "atcorewidgets_export.h" /** * @brief The MovementWidget class * This widget will provide Basic Movement Controls. Create it with "showHomeAndDisableWidgets" false if your client provides its own actions for homing and disabling the motors. */ class ATCOREWIDGETS_EXPORT MovementWidget : public QWidget { Q_OBJECT public: /** * @brief Create a Movement Widget * @param showHomeAndDisableWidgets: set False to hide the Home and Disable Motors buttons [default = true] * @param parent: Parent of this widget. */ MovementWidget(bool showHomeAndDisableWidgets = true, QWidget *parent = nullptr); signals: /** * @brief The Home All button was clicked. * This should be connected to AtCore::home() */ void homeAllPressed(); /** * @brief The Home X button was clicked. * This should be connected to AtCore::home(AtCore::X) */ void homeXPressed(); /** * @brief The Home Y button was clicked. * This should be connected to AtCore::home(AtCore::Y) */ void homeYPressed(); /** * @brief The Home Z button was clicked. * This should be connected to AtCore::home(AtCore::Z) */ void homeZPressed(); /** * @brief The Disable Motors button was clicked. * This should be connected to AtCore::disableMotors(0) */ void disableMotorsPressed(); /** * @brief An absoluteMove was requested * This should be connected to AtCore::move(axis,value) * @param axis: the axis to move * @param value: where to move */ void absoluteMove(const QLatin1Char &axis, const double value); /** * @brief A relativeMove was requested from the AxisControl * This should connect to a function that does the following * AtCore::setRelativePosition() * AtCore::move(axis, value) * AtCore::setAbsolutePosition() * @param axis: the axis to move. * @param value: the value to move it by. */ void relativeMove(const QLatin1Char &axis, const double value); + /** + * @brief A Change of units was requested from the AxisControl + * This should connect to a function that calls AtCore::setUnits + * @param units: 0=Metric 1=Imperial + */ + void unitsChanged(int units); + private: QComboBox *comboMoveAxis = nullptr; QDoubleSpinBox *sbMoveAxis = nullptr; }; diff --git a/testclient/mainwindow.cpp b/testclient/mainwindow.cpp index ce0e197..3716ace 100644 --- a/testclient/mainwindow.cpp +++ b/testclient/mainwindow.cpp @@ -1,586 +1,591 @@ /* AtCore Test Client Copyright (C) <2016 - 2018> Authors: Patrick José Pereira Lays Rodrigues Chris Rizzitello Tomaz Canabrava 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 "mainwindow.h" #include "seriallayer.h" #include "gcodecommands.h" #include "about.h" Q_LOGGING_CATEGORY(TESTCLIENT_MAINWINDOW, "org.kde.atelier.core") int MainWindow::fanCount = 4; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), core(new AtCore(this)) { setWindowTitle(tr("AtCore - Test Client")); setWindowIcon(QIcon(QStringLiteral(":/icon/windowIcon"))); QCoreApplication::setApplicationVersion(core->version()); initMenu(); initStatusBar(); initWidgets(); connect(core, &AtCore::atcoreMessage, logWidget, &LogWidget::appendLog); logWidget->appendLog(tr("Attempting to locate Serial Ports")); core->setSerialTimerInterval(1000); connect(core, &AtCore::stateChanged, this, &MainWindow::printerStateChanged); connect(core, &AtCore::portsChanged, this, &MainWindow::locateSerialPort); connect(core, &AtCore::sdCardFileListChanged, sdWidget, &SdWidget::updateFilelist); } void MainWindow::initMenu() { QMenu *menuFile = new QMenu(tr("File")); QAction *actionQuit = new QAction(style()->standardIcon(QStyle::SP_DialogCloseButton), tr("Quit")); connect(actionQuit, &QAction::triggered, this, &MainWindow::close); menuFile->addAction(actionQuit); menuView = new QMenu(tr("View")); QAction *actionShowDockTitles = new QAction(tr("Show Dock Titles")); actionShowDockTitles->setCheckable(true); actionShowDockTitles->setChecked(true); connect(actionShowDockTitles, &QAction::toggled, this, &MainWindow::toggleDockTitles); menuView->addAction(actionShowDockTitles); QMenu *menuHelp = new QMenu(tr("Help")); QAction *actionAbout = new QAction(tr("About")); actionAbout->setShortcut(QKeySequence(Qt::Key_F1)); connect(actionAbout, &QAction::triggered, this, [] { auto *dialog = new About; dialog->exec(); }); menuHelp->addAction(actionAbout); menuBar()->addMenu(menuFile); menuBar()->addMenu(menuView); menuBar()->addMenu(menuHelp); } void MainWindow::initStatusBar() { statusWidget = new StatusWidget; connect(statusWidget, &StatusWidget::stopPressed, core, &AtCore::stop); connect(core, &AtCore::printProgressChanged, statusWidget, &StatusWidget::updatePrintProgress); connect(core, &AtCore::sdMountChanged, statusWidget, &StatusWidget::setSD); statusBar()->addPermanentWidget(statusWidget, 100); } void MainWindow::initWidgets() { //Make the Docks makeCommandDock(); makePrintDock(); makeTempTimelineDock(); makeLogDock(); makeConnectDock(); makeMoveDock(); makeTempControlsDock(); makeSdDock(); setDangeriousDocksDisabled(true); setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North); setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North); tabifyDockWidget(moveDock, tempControlsDock); tabifyDockWidget(moveDock, sdDock); moveDock->raise(); tabifyDockWidget(connectDock, printDock); tabifyDockWidget(connectDock, commandDock); connectDock->raise(); setCentralWidget(nullptr); //More Gui stuff //hide the printing progress bar. statusWidget->showPrintArea(false); } void MainWindow::makeCommandDock() { commandWidget = new CommandWidget; //Connect the commandPressed signal connect(commandWidget, &CommandWidget::commandPressed, [this](const QString & command) { core->pushCommand(command.toUpper()); }); //Connect the messagePressed signal connect(commandWidget, &CommandWidget::messagePressed, [this](const QString & message) { core->showMessage(message); }); //Create the dock, and set the Widget. commandDock = new QDockWidget(tr("Commands"), this); commandDock->setWidget(commandWidget); //Push the toggle view action into our view menu menuView->insertAction(nullptr, commandDock->toggleViewAction()); //Place the Dock into a DockWidget Area. //Failure todo this will create some odd side effects at runtime addDockWidget(Qt::LeftDockWidgetArea, commandDock); } void MainWindow::makePrintDock() { printWidget = new PrintWidget; connect(printWidget, &PrintWidget::printPressed, this, &MainWindow::printPBClicked); connect(printWidget, &PrintWidget::emergencyStopPressed, core, &AtCore::emergencyStop); connect(printWidget, &PrintWidget::fanSpeedChanged, core, &AtCore::setFanSpeed); connect(printWidget, &PrintWidget::printSpeedChanged, this, [this](const int speed) { core->setPrinterSpeed(speed); }); connect(printWidget, &PrintWidget::flowRateChanged, [this](const int rate) { core->setFlowRate(rate); }); printDock = new QDockWidget(tr("Print"), this); printDock->setWidget(printWidget); menuView->insertAction(nullptr, printDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, printDock); } void MainWindow::makeTempTimelineDock() { plotWidget = new PlotWidget; //make and connect our plots in the widget. plotWidget->addPlot(tr("Actual Bed")); connect(&core->temperature(), &Temperature::bedTemperatureChanged, this, [this](float temp) { checkTemperature(0x00, 0, temp); plotWidget->appendPoint(tr("Actual Bed"), temp); }); plotWidget->addPlot(tr("Target Bed")); connect(&core->temperature(), &Temperature::bedTargetTemperatureChanged, this, [this](float temp) { checkTemperature(0x01, 0, temp); plotWidget->appendPoint(tr("Target Bed"), temp); }); plotWidget->addPlot(tr("Actual Ext.1")); connect(&core->temperature(), &Temperature::extruderTemperatureChanged, this, [this](float temp) { checkTemperature(0x02, 0, temp); plotWidget->appendPoint(tr("Actual Ext.1"), temp); }); plotWidget->addPlot(tr("Target Ext.1")); connect(&core->temperature(), &Temperature::extruderTargetTemperatureChanged, this, [this](float temp) { checkTemperature(0x03, 0, temp); plotWidget->appendPoint(tr("Target Ext.1"), temp); }); tempTimelineDock = new QDockWidget(tr("Temperature Timeline"), this); tempTimelineDock->setWidget(plotWidget); menuView->insertAction(nullptr, tempTimelineDock->toggleViewAction()); addDockWidget(Qt::RightDockWidgetArea, tempTimelineDock); } void MainWindow::makeLogDock() { logWidget = new LogWidget(new QTemporaryFile(QDir::tempPath() + QStringLiteral("/AtCore_"))); logDock = new QDockWidget(tr("Session Log"), this); logDock->setWidget(logWidget); menuView->insertAction(nullptr, logDock->toggleViewAction()); addDockWidget(Qt::RightDockWidgetArea, logDock); } void MainWindow::makeConnectDock() { auto *mainLayout = new QVBoxLayout; auto *newLabel = new QLabel(tr("Port:")); comboPort = new QComboBox; comboPort->setEditable(true); auto *hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(comboPort, 75); mainLayout->addLayout(hBoxLayout); newLabel = new QLabel(tr("Baud Rate:")); comboBAUD = new QComboBox; comboBAUD->addItems(core->portSpeeds()); comboBAUD->setCurrentIndex(9); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(comboBAUD, 75); mainLayout->addLayout(hBoxLayout); newLabel = new QLabel(tr("Use Plugin:")); comboPlugin = new QComboBox; comboPlugin->addItem(tr("Autodetect")); comboPlugin->addItems(core->availableFirmwarePlugins()); connect(comboPlugin, &QComboBox::currentTextChanged, this, &MainWindow::pluginCBChanged); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(comboPlugin, 75); mainLayout->addLayout(hBoxLayout); cbReset = new QCheckBox(tr("Attempt to stop Reset on connect")); cbReset->setHidden(true); mainLayout->addWidget(cbReset); connect(comboPlugin, &QComboBox::currentTextChanged, this, [this](const QString & currentText) { cbReset->setHidden(currentText == tr("Autodetect")); }); buttonConnect = new QPushButton(tr("Connect")); connect(buttonConnect, &QPushButton::clicked, this, &MainWindow::connectPBClicked); mainLayout->addWidget(buttonConnect); auto *dockContents = new QWidget; dockContents->setLayout(mainLayout); connectDock = new QDockWidget(tr("Connect"), this); connectDock->setWidget(dockContents); menuView->insertAction(nullptr, connectDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, connectDock); } void MainWindow::makeMoveDock() { movementWidget = new MovementWidget; connect(movementWidget, &MovementWidget::homeAllPressed, this, [this] { logWidget->appendLog(tr("Home All")); core->home(); }); connect(movementWidget, &MovementWidget::homeXPressed, this, [this] { logWidget->appendLog(tr("Home X")); core->home(AtCore::X); }); connect(movementWidget, &MovementWidget::homeYPressed, this, [this] { logWidget->appendLog(tr("Home Y")); core->home(AtCore::Y); }); connect(movementWidget, &MovementWidget::homeZPressed, this, [this] { logWidget->appendLog(tr("Home Z")); core->home(AtCore::Z); }); connect(movementWidget, &MovementWidget::absoluteMove, this, [this](const QLatin1Char & axis, const double & value) { logWidget->appendLog(GCode::description(GCode::G1)); core->move(axis, value); }); connect(movementWidget, &MovementWidget::disableMotorsPressed, this, [this] { core->disableMotors(0); }); connect(movementWidget, &MovementWidget::relativeMove, this, [this](const QLatin1Char & axis, const double & value) { core->setRelativePosition(); core->move(axis, value); core->setAbsolutePosition(); }); + connect(movementWidget, &MovementWidget::unitsChanged, this, [this](int units) { + auto selection = static_cast(units); + core->setUnits(selection); + }); + moveDock = new QDockWidget(tr("Movement"), this); moveDock->setWidget(movementWidget); menuView->insertAction(nullptr, moveDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, moveDock); } void MainWindow::makeTempControlsDock() { temperatureWidget = new TemperatureWidget; connect(temperatureWidget, &TemperatureWidget::bedTempChanged, core, &AtCore::setBedTemp); connect(temperatureWidget, &TemperatureWidget::extTempChanged, core, &AtCore::setExtruderTemp); tempControlsDock = new QDockWidget(tr("Temperatures"), this); tempControlsDock->setWidget(temperatureWidget); menuView->insertAction(nullptr, tempControlsDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, tempControlsDock); } void MainWindow::makeSdDock() { sdWidget = new SdWidget; connect(sdWidget, &SdWidget::requestSdList, core, &AtCore::sdFileList); connect(sdWidget, &SdWidget::printSdFile, this, [this](const QString & fileName) { if (fileName.isEmpty()) { QMessageBox::information(this, tr("Print Error"), tr("You must Select a file from the list")); } else { core->print(fileName, true); } }); connect(sdWidget, &SdWidget::deleteSdFile, this, [this](const QString & fileName) { if (fileName.isEmpty()) { QMessageBox::information(this, tr("Delete Error"), tr("You must Select a file from the list")); } else { core->sdDelete(fileName); } }); sdDock = new QDockWidget(tr("Sd Card"), this); sdDock->setWidget(sdWidget); menuView->insertAction(nullptr, sdDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, sdDock); } void MainWindow::closeEvent(QCloseEvent *event) { core->close(); event->accept(); } MainWindow::~MainWindow() { } void MainWindow::checkTemperature(uint sensorType, uint number, uint temp) { 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").arg( QString::number(number), QString::number(temp) )); logWidget->appendLog(msg); } /** * @brief MainWindow::locateSerialPort * Locate all active serial ports on the computer and add to the list * of serial ports */ void MainWindow::locateSerialPort(const QStringList &ports) { comboPort->clear(); if (!ports.isEmpty()) { comboPort->addItems(ports); logWidget->appendLog(tr("Found %1 Ports").arg(QString::number(ports.count()))); } else { QString portError(tr("No available ports! Please connect a serial device to continue!")); if (! logWidget->endsWith(portError)) { logWidget->appendLog(portError); } } } void MainWindow::connectPBClicked() { if (core->state() == AtCore::DISCONNECTED) { if (core->initSerial(comboPort->currentText(), comboBAUD->currentText().toInt(), cbReset->isChecked())) { connect(core, &AtCore::receivedMessage, logWidget, &LogWidget::appendRLog); connect(core->serial(), &SerialLayer::pushedCommand, logWidget, &LogWidget::appendSLog); logWidget->appendLog(tr("Serial connected")); if (!comboPlugin->currentText().contains(tr("Autodetect"))) { core->loadFirmwarePlugin(comboPlugin->currentText()); if (cbReset->isChecked()) { //Wait a few seconds after connect to avoid the normal errors QTimer::singleShot(5000, core, &AtCore::sdCardPrintStatus); } } } } else { disconnect(core, &AtCore::receivedMessage, logWidget, &LogWidget::appendRLog); disconnect(core->serial(), &SerialLayer::pushedCommand, logWidget, &LogWidget::appendSLog); core->closeConnection(); core->setState(AtCore::DISCONNECTED); logWidget->appendLog(tr("Disconnected")); } } void MainWindow::printPBClicked() { QString fileName; switch (core->state()) { case AtCore::DISCONNECTED: QMessageBox::information(this, tr("Error"), tr("Not Connected To a Printer")); break; case AtCore::CONNECTING: QMessageBox::information(this, tr("Error"), tr(" A Firmware Plugin was not loaded!\n Please send the command M115 and let us know what your firmware returns, so we can improve our firmware detection. We have loaded the most common plugin \"repetier\" for you. You may try to print again after this message")); comboPlugin->setCurrentText(QStringLiteral("repetier")); break; case AtCore::IDLE: fileName = QFileDialog::getOpenFileName(this, tr("Select a file to print"), QDir::homePath(), tr("*.gcode")); if (fileName.isNull()) { logWidget->appendLog(tr("No File Selected")); } else { logWidget->appendLog(tr("Print: %1").arg(fileName)); core->print(fileName); } break; case AtCore::BUSY: core->pause(printWidget->postPauseCommand()); break; case AtCore::PAUSE: core->resume(); break; default: qCDebug(TESTCLIENT_MAINWINDOW) << "ERROR / STOP unhandled."; } } void MainWindow::pluginCBChanged(QString currentText) { if (core->state() != AtCore::DISCONNECTED) { if (!currentText.contains(tr("Autodetect"))) { core->loadFirmwarePlugin(currentText); sdDock->setVisible(core->firmwarePlugin()->isSdSupported()); } } } void MainWindow::printerStateChanged(AtCore::STATES state) { QString stateString; switch (state) { case AtCore::IDLE: buttonConnect->setText(tr("Disconnect")); printWidget->setPrintText(tr("Print File")); stateString = tr("Connected to ") + core->connectedPort(); sdDock->setVisible(core->firmwarePlugin()->isSdSupported()); break; case AtCore::STARTPRINT: stateString = tr("START PRINT"); printWidget->setPrintText(tr("Pause Print")); statusWidget->showPrintArea(true); break; case AtCore::FINISHEDPRINT: stateString = tr("Finished Print"); printWidget->setPrintText(tr("Print File")); statusWidget->showPrintArea(false); break; case AtCore::PAUSE: stateString = tr("Paused"); printWidget->setPrintText(tr("Resume Print")); break; case AtCore::BUSY: stateString = tr("Printing"); printWidget->setPrintText(tr("Pause Print")); break; case AtCore::DISCONNECTED: stateString = QStringLiteral("Not Connected"); buttonConnect->setText(tr("Connect")); setDangeriousDocksDisabled(true); break; case AtCore::CONNECTING: stateString = QStringLiteral("Connecting"); setDangeriousDocksDisabled(false); break; case AtCore::STOP: stateString = tr("Stopping Print"); break; case AtCore::ERRORSTATE: stateString = tr("Command ERROR"); break; } statusWidget->setState(stateString); } void MainWindow::toggleDockTitles(bool checked) { if (checked) { delete connectDock->titleBarWidget(); delete logDock->titleBarWidget(); delete tempTimelineDock->titleBarWidget(); delete commandDock->titleBarWidget(); delete moveDock->titleBarWidget(); delete tempControlsDock->titleBarWidget(); delete printDock->titleBarWidget(); delete sdDock->titleBarWidget(); } else { connectDock->setTitleBarWidget(new QWidget()); logDock->setTitleBarWidget(new QWidget()); tempTimelineDock->setTitleBarWidget(new QWidget()); commandDock->setTitleBarWidget(new QWidget()); moveDock->setTitleBarWidget(new QWidget()); tempControlsDock->setTitleBarWidget(new QWidget()); printDock->setTitleBarWidget(new QWidget()); sdDock->setTitleBarWidget(new QWidget()); } } void MainWindow::setDangeriousDocksDisabled(bool disabled) { commandDock->widget()->setDisabled(disabled); moveDock->widget()->setDisabled(disabled); tempControlsDock->widget()->setDisabled(disabled); printDock->widget()->setDisabled(disabled); sdDock->widget()->setDisabled(disabled); if (!disabled) { temperatureWidget->updateExtruderCount(core->extruderCount()); printWidget->updateFanCount(fanCount); } else { printWidget->setPrintText(tr("Print File")); statusWidget->showPrintArea(false); } }