setup improvements (#21505)

* getting started

* downloading

* some more

* nice checkmark

* nice triangle

* fixup style

* wait for internet

* more responsive

* waiting for internet
old-commit-hash: ddd1342fc5
commatwo_master
Adeeb Shihadeh 4 years ago committed by GitHub
parent 454cdf2d25
commit 65002c7b53
  1. 3
      selfdrive/assets/img_circled_check.svg
  2. 3
      selfdrive/assets/img_continue_triangle.svg
  3. 12
      selfdrive/ui/qt/api.cc
  4. 4
      selfdrive/ui/qt/api.h
  5. 308
      selfdrive/ui/qt/setup/setup.cc
  6. 6
      selfdrive/ui/qt/setup/setup.h

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c5687faf4cb22bece0405893671651eb13e8eda393987610f493ee5e05eac61d
size 439

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1764c0c93703481b2ced63bc35c5a23a6c12388ca690ce4cf577b3b478a08b69
size 197

@ -67,18 +67,23 @@ QString create_jwt(const QJsonObject &payloads, int expiry) {
} // namespace CommaApi } // namespace CommaApi
HttpRequest::HttpRequest(QObject *parent, const QString &requestURL, bool create_jwt_) : create_jwt(create_jwt_), QObject(parent) { HttpRequest::HttpRequest(QObject *parent, const QString &requestURL, bool create_jwt_,
int timeout) : create_jwt(create_jwt_), QObject(parent) {
networkAccessManager = new QNetworkAccessManager(this); networkAccessManager = new QNetworkAccessManager(this);
reply = NULL; reply = NULL;
networkTimer = new QTimer(this); networkTimer = new QTimer(this);
networkTimer->setSingleShot(true); networkTimer->setSingleShot(true);
networkTimer->setInterval(20000); networkTimer->setInterval(timeout);
connect(networkTimer, &QTimer::timeout, this, &HttpRequest::requestTimeout); connect(networkTimer, &QTimer::timeout, this, &HttpRequest::requestTimeout);
sendRequest(requestURL); sendRequest(requestURL);
} }
bool HttpRequest::active() {
return reply != NULL;
}
void HttpRequest::sendRequest(const QString &requestURL) { void HttpRequest::sendRequest(const QString &requestURL) {
QString token; QString token;
if(create_jwt) { if(create_jwt) {
@ -105,11 +110,13 @@ void HttpRequest::requestTimeout() {
// This function should always emit something // This function should always emit something
void HttpRequest::requestFinished() { void HttpRequest::requestFinished() {
bool success = false;
if (reply->error() != QNetworkReply::OperationCanceledError) { if (reply->error() != QNetworkReply::OperationCanceledError) {
networkTimer->stop(); networkTimer->stop();
QString response = reply->readAll(); QString response = reply->readAll();
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
success = true;
emit receivedResponse(response); emit receivedResponse(response);
} else { } else {
qDebug() << reply->errorString(); qDebug() << reply->errorString();
@ -118,6 +125,7 @@ void HttpRequest::requestFinished() {
} else { } else {
emit timeoutResponse("timeout"); emit timeoutResponse("timeout");
} }
emit requestDone(success);
reply->deleteLater(); reply->deleteLater();
reply = NULL; reply = NULL;
} }

@ -20,8 +20,9 @@ class HttpRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit HttpRequest(QObject* parent, const QString &requestURL, bool create_jwt_ = true); explicit HttpRequest(QObject* parent, const QString &requestURL, bool create_jwt_ = true, int timeout = 20000);
void sendRequest(const QString &requestURL); void sendRequest(const QString &requestURL);
bool active();
protected: protected:
QNetworkReply *reply; QNetworkReply *reply;
@ -36,6 +37,7 @@ private slots:
void requestFinished(); void requestFinished();
signals: signals:
void requestDone(bool success);
void receivedResponse(const QString &response); void receivedResponse(const QString &response);
void failedResponse(const QString &errorString); void failedResponse(const QString &errorString);
void timeoutResponse(const QString &errorString); void timeoutResponse(const QString &errorString);

@ -10,20 +10,21 @@
#include <curl/curl.h> #include <curl/curl.h>
#include "selfdrive/hardware/hw.h" #include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/api.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/offroad/networking.h" #include "selfdrive/ui/qt/offroad/networking.h"
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/qt_window.h"
#define USER_AGENT "AGNOSSetup-0.1" const char* USER_AGENT = "AGNOSSetup-0.1";
const QString DASHCAM_URL = "https://dashcam.comma.ai";
void Setup::download(QString url) { const QString TEST_URL = "https://api.commadotai.com/v1/me";
QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
setCurrentIndex(count() - 2);
void Setup::download(QString url) {
CURL *curl = curl_easy_init(); CURL *curl = curl_easy_init();
if (!curl) { if (!curl) {
emit downloadFailed(); emit finished(false);
return;
} }
char tmpfile[] = "/tmp/installer_XXXXXX"; char tmpfile[] = "/tmp/installer_XXXXXX";
@ -38,121 +39,245 @@ void Setup::download(QString url) {
int ret = curl_easy_perform(curl); int ret = curl_easy_perform(curl);
if (ret != CURLE_OK) { if (ret != CURLE_OK) {
emit downloadFailed(); emit finished(false);
return;
} }
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
fclose(fp); fclose(fp);
rename(tmpfile, "/tmp/installer"); rename(tmpfile, "/tmp/installer");
emit finished(true);
} }
QLabel * title_label(QString text) { QWidget * Setup::getting_started() {
QLabel *l = new QLabel(text);
l->setStyleSheet(R"(
font-size: 100px;
font-weight: 500;
)");
return l;
}
QWidget * Setup::build_page(QString title, QWidget *content, bool next, bool prev) {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->setMargin(50); QHBoxLayout *main_layout = new QHBoxLayout(widget);
main_layout->addWidget(title_label(title), 0, Qt::AlignLeft | Qt::AlignTop); main_layout->setMargin(0);
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setContentsMargins(165, 280, 100, 0);
main_layout->addLayout(vlayout);
QLabel *title = new QLabel("Getting Started");
title->setStyleSheet("font-size: 90px; font-weight: 500;");
vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
vlayout->addSpacing(90);
QLabel *desc = new QLabel("Before we get on the road, let’s finish installation and cover some details.");
desc->setWordWrap(true);
desc->setStyleSheet("font-size: 80px; font-weight: 300;");
vlayout->addWidget(desc, 0, Qt::AlignTop | Qt::AlignLeft);
main_layout->addWidget(content); vlayout->addStretch();
QHBoxLayout *nav_layout = new QHBoxLayout(); QPushButton *btn = new QPushButton();
btn->setIcon(QIcon("../../../assets/img_continue_triangle.svg"));
btn->setIconSize(QSize(54, 106));
btn->setFixedSize(310, 1080);
btn->setProperty("primary", true);
btn->setStyleSheet("border: none;");
main_layout->addWidget(btn, 0, Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, this, &Setup::nextPage);
if (prev) { return widget;
QPushButton *back_btn = new QPushButton("Back");
nav_layout->addWidget(back_btn, 1, Qt::AlignBottom | Qt::AlignLeft);
QObject::connect(back_btn, &QPushButton::released, this, &Setup::prevPage);
} }
if (next) { QWidget * Setup::network_setup() {
QPushButton *continue_btn = new QPushButton("Continue"); QWidget *widget = new QWidget();
nav_layout->addWidget(continue_btn, 0, Qt::AlignBottom | Qt::AlignRight); QVBoxLayout *main_layout = new QVBoxLayout(widget);
QObject::connect(continue_btn, &QPushButton::released, this, &Setup::nextPage); main_layout->setContentsMargins(55, 50, 55, 50);
// title
QLabel *title = new QLabel("Connect to WiFi");
title->setStyleSheet("font-size: 90px; font-weight: 500;");
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
// wifi widget
Networking *wifi = new Networking(this, false);
main_layout->addWidget(wifi, 1);
// back + continue buttons
QHBoxLayout *blayout = new QHBoxLayout;
main_layout->addLayout(blayout);
blayout->setSpacing(50);
QPushButton *back = new QPushButton("Back");
back->setObjectName("navBtn");
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
blayout->addWidget(back);
QPushButton *cont = new QPushButton();
cont->setObjectName("navBtn");
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
blayout->addWidget(cont);
// setup timer for testing internet connection
HttpRequest *request = new HttpRequest(this, TEST_URL, false, 2500);
QObject::connect(request, &HttpRequest::requestDone, [=](bool success) {
cont->setEnabled(success);
cont->setText(success ? "Continue" : "Waiting for internet");
repaint();
});
QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, [=]() {
if (!request->active() && cont->isVisible()) {
request->sendRequest(TEST_URL);
} }
});
timer->start(1000);
main_layout->addLayout(nav_layout, 0);
return widget; return widget;
} }
QWidget * Setup::getting_started() { QWidget * radio_button(QString title, QButtonGroup *group) {
QLabel *body = new QLabel("Before we get on the road, let's finish\ninstallation and cover some details."); QPushButton *btn = new QPushButton(title);
body->setAlignment(Qt::AlignHCenter); btn->setCheckable(true);
body->setStyleSheet(R"(font-size: 80px;)"); group->addButton(btn);
return build_page("Getting Started", body, true, false); btn->setStyleSheet(R"(
QPushButton {
height: 230;
padding-left: 100px;
padding-right: 100px;
text-align: left;
font-size: 80px;
font-weight: 400;
border-radius: 10px;
background-color: #4F4F4F;
}
QPushButton:checked {
background-color: #465BEA;
} }
)");
QWidget * Setup::network_setup() { // checkmark icon
Networking *wifi = new Networking(this, false); QPixmap pix("../../../assets/img_circled_check.svg");
return build_page("Connect to WiFi", wifi, true, true); btn->setIcon(pix);
btn->setIconSize(QSize(0, 0));
btn->setLayoutDirection(Qt::RightToLeft);
QObject::connect(btn, &QPushButton::toggled, [=](bool checked) {
btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0));
});
return btn;
} }
QWidget * Setup::software_selection() { QWidget * Setup::software_selection() {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget); QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->setContentsMargins(55, 50, 55, 50);
main_layout->setSpacing(0);
QPushButton *dashcam_btn = new QPushButton("Dashcam"); // title
main_layout->addWidget(dashcam_btn); QLabel *title = new QLabel("Choose Software to Install");
QObject::connect(dashcam_btn, &QPushButton::released, this, [=]() { title->setStyleSheet("font-size: 90px; font-weight: 500;");
this->download("https://dashcam.comma.ai"); main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
});
main_layout->addSpacing(50); main_layout->addSpacing(50);
QPushButton *custom_btn = new QPushButton("Custom"); // dashcam + custom radio buttons
main_layout->addWidget(custom_btn); QButtonGroup *group = new QButtonGroup(widget);
QObject::connect(custom_btn, &QPushButton::released, this, [=]() { group->setExclusive(true);
QString input_url = InputDialog::getText("Enter URL", this);
if (input_url.size()) { QWidget *dashcam = radio_button("Dashcam", group);
this->download(input_url); main_layout->addWidget(dashcam);
main_layout->addSpacing(30);
QWidget *custom = radio_button("Custom Software", group);
main_layout->addWidget(custom);
main_layout->addStretch();
// back + continue buttons
QHBoxLayout *blayout = new QHBoxLayout;
main_layout->addLayout(blayout);
blayout->setSpacing(50);
QPushButton *back = new QPushButton("Back");
back->setObjectName("navBtn");
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
blayout->addWidget(back);
QPushButton *cont = new QPushButton("Continue");
cont->setObjectName("navBtn");
cont->setEnabled(false);
blayout->addWidget(cont);
QObject::connect(cont, &QPushButton::clicked, [=]() {
QString url = DASHCAM_URL;
if (group->checkedButton() != dashcam) {
url = InputDialog::getText("Enter URL", this);
}
if (!url.isEmpty()) {
setCurrentWidget(downloading_widget);
QTimer::singleShot(100, this, [=]() {
download(url);
});
} }
}); });
return build_page("Choose Software", widget, false, true);
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) {
btn->setChecked(true);
cont->setEnabled(true);
});
return widget;
} }
QWidget * Setup::downloading() { QWidget * Setup::downloading() {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget); QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->addWidget(title_label("Downloading..."), 0, Qt::AlignCenter); QLabel *txt = new QLabel("Downloading...");
txt->setStyleSheet("font-size: 90px; font-weight: 500;");
main_layout->addWidget(txt, 0, Qt::AlignCenter);
return widget; return widget;
} }
QWidget * Setup::download_failed() { QWidget * Setup::download_failed() {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget); QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->setContentsMargins(50, 50, 50, 50); main_layout->setContentsMargins(55, 225, 55, 55);
main_layout->addWidget(title_label("Download Failed"), 0, Qt::AlignLeft | Qt::AlignTop); main_layout->setSpacing(0);
QLabel *title = new QLabel("Download Failed");
title->setStyleSheet("font-size: 90px; font-weight: 500;");
main_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
QLabel *body = new QLabel("Ensure the entered URL is valid, and the device's network connection is good."); main_layout->addSpacing(67);
QLabel *body = new QLabel("Ensure the entered URL is valid, and the device’s internet connection is good.");
body->setWordWrap(true); body->setWordWrap(true);
body->setAlignment(Qt::AlignHCenter); body->setAlignment(Qt::AlignTop | Qt::AlignLeft);
body->setStyleSheet(R"(font-size: 80px;)"); body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;");
main_layout->addWidget(body); main_layout->addWidget(body);
QHBoxLayout *nav_layout = new QHBoxLayout(); main_layout->addStretch();
QPushButton *reboot_btn = new QPushButton("Reboot"); // reboot + start over buttons
nav_layout->addWidget(reboot_btn, 0, Qt::AlignBottom | Qt::AlignLeft); QHBoxLayout *blayout = new QHBoxLayout();
QObject::connect(reboot_btn, &QPushButton::released, this, [=]() { blayout->setSpacing(50);
if (Hardware::TICI()) { main_layout->addLayout(blayout, 0);
std::system("sudo reboot");
} QPushButton *reboot = new QPushButton("Reboot device");
reboot->setObjectName("navBtn");
blayout->addWidget(reboot);
QObject::connect(reboot, &QPushButton::released, this, [=]() {
Hardware::reboot();
}); });
QPushButton *restart_btn = new QPushButton("Start over"); QPushButton *restart = new QPushButton("Start over");
nav_layout->addWidget(restart_btn, 0, Qt::AlignBottom | Qt::AlignRight); restart->setObjectName("navBtn");
QObject::connect(restart_btn, &QPushButton::released, this, [=]() { restart->setProperty("primary", true);
blayout->addWidget(restart);
QObject::connect(restart, &QPushButton::released, this, [=]() {
setCurrentIndex(0); setCurrentIndex(0);
}); });
main_layout->addLayout(nav_layout, 0); widget->setStyleSheet(R"(
QLabel {
margin-left: 117;
}
)");
return widget; return widget;
} }
@ -168,24 +293,47 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
addWidget(getting_started()); addWidget(getting_started());
addWidget(network_setup()); addWidget(network_setup());
addWidget(software_selection()); addWidget(software_selection());
addWidget(downloading());
addWidget(download_failed());
QObject::connect(this, &Setup::downloadFailed, this, &Setup::nextPage); downloading_widget = downloading();
addWidget(downloading_widget);
failed_widget = download_failed();
addWidget(failed_widget);
QObject::connect(this, &Setup::finished, [=](bool success) {
// hide setup on success
qDebug() << "finished" << success;
setVisible(!success);
setCurrentWidget(failed_widget);
});
// TODO: revisit pressed bg color
setStyleSheet(R"( setStyleSheet(R"(
* { * {
font-family: Inter;
color: white; color: white;
font-family: Inter;
}
Setup {
background-color: black; background-color: black;
} }
QPushButton { QPushButton#navBtn {
padding: 50px; height: 160;
padding-right: 100px; font-size: 55px;
padding-left: 100px; font-weight: 400;
border: 7px solid white; border-radius: 10px;
border-radius: 20px; background-color: #333333;
font-size: 50px; }
QPushButton#navBtn:disabled {
color: #808080;
}
QPushButton#navBtn:pressed {
background-color: #444444;
}
QPushButton[primary='true'], #navBtn[primary='true'] {
background-color: #465BEA;
}
QPushButton[primary='true']:pressed, #navBtn:pressed[primary='true'] {
background-color: #3049F4;
} }
)"); )");
} }

@ -14,14 +14,14 @@ private:
QWidget *getting_started(); QWidget *getting_started();
QWidget *network_setup(); QWidget *network_setup();
QWidget *software_selection(); QWidget *software_selection();
QWidget *custom_software();
QWidget *downloading(); QWidget *downloading();
QWidget *download_failed(); QWidget *download_failed();
QWidget *build_page(QString title, QWidget *content, bool next, bool prev); QWidget *failed_widget;
QWidget *downloading_widget;
signals: signals:
void downloadFailed(); void finished(bool success);
public slots: public slots:
void nextPage(); void nextPage();

Loading…
Cancel
Save