Qt UI refactoring + improvements (#20033)

albatross
Adeeb Shihadeh 2021-02-07 17:33:48 -08:00 committed by GitHub
parent bb0f91791a
commit f9d8652cbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 350 additions and 437 deletions

View File

@ -18,7 +18,7 @@ else:
qt_libs = qt_env["LIBS"] + libs + ["pthread", "ssl", "crypto"]
widgets = qt_env.Library("qt_widgets",
["qt/qt_window.cc", "qt/qt_sound.cc", "qt/widgets/keyboard.cc", "qt/widgets/input_field.cc", "qt/widgets/drive_stats.cc", "qt/widgets/ssh_keys.cc",
["qt/qt_sound.cc", "qt/widgets/keyboard.cc", "qt/widgets/input_field.cc", "qt/widgets/drive_stats.cc", "qt/widgets/ssh_keys.cc",
"qt/offroad/networking.cc", "qt/offroad/wifiManager.cc", "qt/widgets/toggle.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc"],
LIBS=qt_libs)
qt_libs.append(widgets)
@ -43,10 +43,16 @@ else:
installers = [
("openpilot", "master"),
("openpilot_test", "master"),
#("dashcam", "dashcam"),
#("dashcam_test", "dashcam"),
#("openpilot_test", "release3-staging"),
#("openpilot_internal", "master"),
#("dashcam", "dashcam3-staging"),
#("dashcam_test", "dashcam3-staging"),
]
for name, branch in installers:
flags = qt_env["CXXFLAGS"] + [f"-D{branch}"]
qt_env.Program(f"qt/setup/installer_{name}", ["qt/setup/installer.cc"], LIBS=qt_libs)
d = {'BRANCH': f"'\"{branch}\"'"}
if "internal" in name:
import requests
r = requests.get("https://github.com/commaci2.keys")
r.raise_for_status()
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
qt_env.Program(f"qt/setup/installer_{name}", ["qt/setup/installer.cc"], LIBS=qt_libs, CPPDEFINES=d)

View File

@ -29,21 +29,21 @@ QWidget* layoutToWidget(QLayout* l, QWidget* parent){
// https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
// Networking functions
Networking::Networking(QWidget* parent) : QWidget(parent){
Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent){
try {
wifi = new WifiManager(this);
} catch (std::exception &e) {
@ -56,25 +56,20 @@ Networking::Networking(QWidget* parent) : QWidget(parent){
return;
}
connect(wifi, SIGNAL(wrongPassword(QString)), this, SLOT(wrongPassword(QString)));
connect(wifi, SIGNAL(successfulConnection(QString)), this, SLOT(successfulConnection(QString)));
s = new QStackedLayout;
inputField = new InputField(this, 8);
connect(inputField, SIGNAL(emitText(QString)), this, SLOT(receiveText(QString)));
connect(inputField, SIGNAL(cancel()), this, SLOT(abortTextInput()));
inputField->setContentsMargins(100,0,100,0);
s->addWidget(inputField);
QVBoxLayout* vlayout = new QVBoxLayout;
QPushButton* advancedSettings = new QPushButton("Advanced");
advancedSettings->setStyleSheet(R"(margin-right: 30px)");
advancedSettings->setFixedSize(300, 100);
connect(advancedSettings, &QPushButton::released, [=](){s->setCurrentIndex(2);});
vlayout->addSpacing(10);
vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
vlayout->addSpacing(10);
if (show_advanced) {
QPushButton* advancedSettings = new QPushButton("Advanced");
advancedSettings->setStyleSheet(R"(margin-right: 30px)");
advancedSettings->setFixedSize(300, 100);
connect(advancedSettings, &QPushButton::released, [=](){s->setCurrentIndex(1);});
vlayout->addSpacing(10);
vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
vlayout->addSpacing(10);
}
wifiWidget = new WifiUI(0, 5, wifi);
connect(wifiWidget, SIGNAL(connectToNetwork(Network)), this, SLOT(connectToNetwork(Network)));
@ -83,13 +78,9 @@ Networking::Networking(QWidget* parent) : QWidget(parent){
s->addWidget(layoutToWidget(vlayout, this));
an = new AdvancedNetworking(this, wifi);
connect(an, &AdvancedNetworking::backPress, [=](){s->setCurrentIndex(1);});
connect(an, &AdvancedNetworking::openKeyboard, [=](){emit openKeyboard();});
connect(an, &AdvancedNetworking::closeKeyboard, [=](){emit closeKeyboard();});
connect(an, &AdvancedNetworking::backPress, [=](){s->setCurrentIndex(0);});
s->addWidget(an);
s->setCurrentIndex(1);
// Update network status
QTimer* timer = new QTimer(this);
QObject::connect(timer, SIGNAL(timeout()), this, SLOT(refresh()));
@ -100,7 +91,8 @@ Networking::Networking(QWidget* parent) : QWidget(parent){
font-size: 50px;
margin: 0px;
padding: 15px;
border-radius: 25px;
border-width: 0;
border-radius: 7px;
color: #dddddd;
background-color: #444444;
}
@ -124,39 +116,14 @@ void Networking::connectToNetwork(Network n) {
if (n.security_type == SecurityType::OPEN) {
wifi->connect(n);
} else if (n.security_type == SecurityType::WPA) {
inputField->setPromptText("Enter password for \"" + n.ssid + "\"");
s->setCurrentIndex(0);
selectedNetwork = n;
emit openKeyboard();
QString pass = InputDialog::getText("Enter password for \"" + n.ssid + "\"");
wifi->connect(n, pass);
}
}
void Networking::abortTextInput(){
s->setCurrentIndex(1);
emit closeKeyboard();
}
void Networking::receiveText(QString text) {
wifi->disconnect();
wifi->connect(selectedNetwork, text);
s->setCurrentIndex(1);
emit closeKeyboard();
}
void Networking::wrongPassword(QString ssid) {
if(s->currentIndex()==0){
qDebug()<<"Wrong password, but we are already trying a new network";
return;
}
if(s->currentIndex()==2){
qDebug()<<"Wrong password, but we are in advanced settings";
return;
}
if(!this->isVisible()){
qDebug()<<"Wrong password, but we are not visible";
return;
}
return; // TODO: add this back
/*
for (Network n : wifi->seen_networks) {
if (n.ssid == ssid) {
inputField->setPromptText("Wrong password for \"" + n.ssid +"\"");
@ -165,20 +132,7 @@ void Networking::wrongPassword(QString ssid) {
return;
}
}
}
void Networking::successfulConnection(QString ssid) {
//Maybe we will want to do something here in the future.
}
void Networking::sidebarChange(){
if (s == nullptr || an == nullptr){
return;
}
s->setCurrentIndex(1);
an->s->setCurrentIndex(1);
refresh();
*/
}
QFrame* hline(QWidget* parent = 0){
@ -192,24 +146,19 @@ QFrame* hline(QWidget* parent = 0){
// AdvancedNetworking functions
AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi){
s = new QStackedLayout;// inputField, mainPage, SSH settings
inputField = new InputField(this, 8);
connect(inputField, SIGNAL(emitText(QString)), this, SLOT(receiveText(QString)));
connect(inputField, SIGNAL(cancel()), this, SLOT(abortTextInput()));
inputField->setContentsMargins(100,0,100,0);
s->addWidget(inputField);
s = new QStackedLayout; // mainPage, SSH settings
QVBoxLayout* vlayout = new QVBoxLayout;
//Back button
// Back button
QHBoxLayout* backLayout = new QHBoxLayout;
QPushButton* back = new QPushButton("BACK");
QPushButton* back = new QPushButton("Back");
back->setFixedSize(500, 100);
connect(back, &QPushButton::released, [=](){emit backPress();});
backLayout->addWidget(back, 0, Qt::AlignLeft);
vlayout->addWidget(layoutToWidget(backLayout, this), 0, Qt::AlignLeft);
//Enable tethering layout
// Enable tethering layout
QHBoxLayout* tetheringToggleLayout = new QHBoxLayout;
tetheringToggleLayout->addWidget(new QLabel("Enable tethering"));
Toggle* toggle_switch = new Toggle;
@ -223,17 +172,22 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
vlayout->addWidget(layoutToWidget(tetheringToggleLayout, this), 0);
vlayout->addWidget(hline(), 0);
//Change tethering password
// Change tethering password
QHBoxLayout *tetheringPassword = new QHBoxLayout;
tetheringPassword->addWidget(new QLabel("Edit tethering password"), 1);
editPasswordButton = new QPushButton("EDIT");
editPasswordButton->setFixedWidth(500);
connect(editPasswordButton, &QPushButton::released, [=](){inputField->setPromptText("Enter the new hotspot password"); s->setCurrentIndex(0); emit openKeyboard();});
connect(editPasswordButton, &QPushButton::released, [=](){
QString pass = InputDialog::getText("Enter new tethering password");
if (pass.size()) {
wifi->changeTetheringPassword(pass);
}
});
tetheringPassword->addWidget(editPasswordButton, 1, Qt::AlignRight);
vlayout->addWidget(layoutToWidget(tetheringPassword, this), 0);
vlayout->addWidget(hline(), 0);
//IP adress
// IP adress
QHBoxLayout* IPlayout = new QHBoxLayout;
IPlayout->addWidget(new QLabel("IP address"), 0);
ipLabel = new QLabel(wifi->ipv4_address);
@ -242,7 +196,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
vlayout->addWidget(layoutToWidget(IPlayout, this), 0);
vlayout->addWidget(hline(), 0);
//Enable SSH
// Enable SSH
QHBoxLayout* enableSSHLayout = new QHBoxLayout(this);
enableSSHLayout->addWidget(new QLabel("Enable SSH", this));
toggle_switch_SSH = new Toggle(this);
@ -255,12 +209,12 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
vlayout->addWidget(layoutToWidget(enableSSHLayout, this));
vlayout->addWidget(hline(), 0);
//Authorized SSH keys
// SSH keys
QHBoxLayout* authSSHLayout = new QHBoxLayout(this);
authSSHLayout->addWidget(new QLabel("Authorized SSH keys", this));
authSSHLayout->addWidget(new QLabel("SSH keys", this));
QPushButton* editAuthSSHButton = new QPushButton("EDIT", this);
editAuthSSHButton->setFixedWidth(500);
connect(editAuthSSHButton, &QPushButton::released, [=](){s->setCurrentIndex(2);});
connect(editAuthSSHButton, &QPushButton::released, [=](){s->setCurrentWidget(ssh);});
authSSHLayout->addWidget(editAuthSSHButton);
vlayout->addWidget(layoutToWidget(authSSHLayout, this));
vlayout->addSpacing(50);
@ -273,14 +227,13 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
// dangerZone->addWidget(deleteAll);
// vlayout->addWidget(layoutToWidget(dangerZone, this));
//vlayout to widget
// vlayout to widget
QWidget* settingsWidget = layoutToWidget(vlayout, this);
settingsWidget->setStyleSheet("margin-left: 40px; margin-right: 40px;");
s->addWidget(settingsWidget);
s->setCurrentIndex(1);
ssh = new SSH;
connect(ssh, &SSH::closeSSHSettings, [=](){s->setCurrentIndex(1);});
connect(ssh, &SSH::closeSSHSettings, [=](){s->setCurrentIndex(0);});
s->addWidget(ssh);
setLayout(s);
@ -300,7 +253,7 @@ void AdvancedNetworking::refresh(){
if (toggle_switch_SSH->on != isSSHEnabled()) {
toggle_switch_SSH->togglePosition();
}
//Qt can be lazy
// Qt can be lazy
repaint();
}
@ -334,16 +287,6 @@ void AdvancedNetworking::toggleSSH(int enable) {
}
}
void AdvancedNetworking::receiveText(QString text){
wifi->changeTetheringPassword(text);
s->setCurrentIndex(1);
emit closeKeyboard();
}
void AdvancedNetworking::abortTextInput(){
s->setCurrentIndex(1);
emit closeKeyboard();
}
// WifiUI functions
@ -370,7 +313,6 @@ void WifiUI::refresh() {
int i = 0;
int countWidgets = 0;
int button_height = static_cast<int>(this->height() / (networks_per_page + 1) * 0.6);
for (Network &network : wifi->seen_networks) {
QHBoxLayout *hlayout = new QHBoxLayout;
if (page * networks_per_page <= i && i < (page + 1) * networks_per_page) {
@ -380,30 +322,30 @@ void WifiUI::refresh() {
if(ssid.length() > 30){
ssid = ssid.left(30)+"";
}
hlayout->addWidget(new QLabel(ssid));
QLabel *ssid_label = new QLabel(ssid);
ssid_label->setStyleSheet(R"(
font-size: 55px;
)");
hlayout->addWidget(ssid_label);
// TODO: don't use images for this
// strength indicator
unsigned int strength_scale = network.strength / 17;
QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png");
QLabel *icon = new QLabel();
icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
hlayout->addWidget(icon);
hlayout->addSpacing(20);
hlayout->addWidget(icon, 0, Qt::AlignRight);
// connect button
QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect")));
btn->setFixedWidth(400);
btn->setFixedHeight(button_height);
btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED);
hlayout->addWidget(btn);
hlayout->addSpacing(20);
btn->setFixedWidth(350);
hlayout->addWidget(btn, 0, Qt::AlignRight);
connectButtons->addButton(btn, i);
QWidget * w = new QWidget;
w->setLayout(hlayout);
vlayout->addWidget(w, 1);
vlayout->addLayout(hlayout, 1);
// Don't add the last horizontal line
if (page * networks_per_page <= i+1 && i+1 < (page + 1) * networks_per_page && i+1 < wifi->seen_networks.size()) {
vlayout->addWidget(hline(), 0);
@ -412,34 +354,23 @@ void WifiUI::refresh() {
}
i++;
}
vlayout->addStretch(3);
// Pad vlayout to prevert oversized network widgets in case of low visible network count
for (int i = countWidgets; i < networks_per_page; i++) {
QWidget *w = new QWidget;
// That we need to add w twice was determined empiricaly
vlayout->addWidget(w, 1);
vlayout->addWidget(w, 1);
}
QHBoxLayout *prev_next_buttons = new QHBoxLayout;//Adding constructor exposes the qt bug
// Setup buttons for pagination
QHBoxLayout *prev_next_buttons = new QHBoxLayout;
QPushButton* prev = new QPushButton("Previous");
prev->setEnabled(page);
prev->setFixedSize(400, button_height);
QObject::connect(prev, SIGNAL(released()), this, SLOT(prevPage()));
prev_next_buttons->addWidget(prev);
QPushButton* next = new QPushButton("Next");
next->setFixedSize(400, button_height);
// If there are more visible networks then we can show, enable going to next page
next->setEnabled(wifi->seen_networks.size() > (page + 1) * networks_per_page);
QObject::connect(prev, SIGNAL(released()), this, SLOT(prevPage()));
QObject::connect(next, SIGNAL(released()), this, SLOT(nextPage()));
prev_next_buttons->addWidget(prev);
prev_next_buttons->addWidget(next);
QWidget *w = new QWidget;
w->setLayout(prev_next_buttons);
vlayout->addWidget(w, 1, Qt::AlignBottom);
vlayout->addLayout(prev_next_buttons, 2);
}
void WifiUI::handleButton(QAbstractButton* button) {
@ -452,6 +383,7 @@ void WifiUI::prevPage() {
page--;
refresh();
}
void WifiUI::nextPage() {
page++;
refresh();

View File

@ -47,7 +47,6 @@ public:
QStackedLayout* s;
private:
InputField* inputField;
QLabel* ipLabel;
QPushButton* editPasswordButton;
SSH* ssh;
@ -56,14 +55,11 @@ private:
WifiManager* wifi = nullptr;
bool isSSHEnabled();
signals:
void openKeyboard();
void closeKeyboard();
void backPress();
public slots:
void receiveText(QString text);
void abortTextInput();
void toggleTethering(int enable);
void toggleSSH(int enable);
void refresh();
@ -73,29 +69,20 @@ class Networking : public QWidget {
Q_OBJECT
public:
explicit Networking(QWidget* parent = 0);
explicit Networking(QWidget* parent = 0, bool show_advanced = true);
private:
QStackedLayout* s = nullptr;// keyboard, wifiScreen, advanced
QStackedLayout* s = nullptr; // keyboard, wifiScreen, advanced
AdvancedNetworking* an = nullptr;
Network selectedNetwork;
WifiUI* wifiWidget;
WifiManager* wifi = nullptr;
InputField* inputField;
signals:
void openKeyboard();
void closeKeyboard();
private slots:
void connectToNetwork(Network n);
void refresh();
void receiveText(QString text);
void abortTextInput();
void wrongPassword(QString ssid);
void successfulConnection(QString ssid);
void sidebarChange();
};

View File

@ -225,9 +225,6 @@ QWidget * developer_panel() {
QWidget * network_panel(QWidget * parent) {
Networking *w = new Networking(parent);
QObject::connect(parent, SIGNAL(sidebarPressed()), w, SLOT(sidebarChange()));
QObject::connect(w, SIGNAL(openKeyboard()), parent, SLOT(closeSidebar()));
QObject::connect(w, SIGNAL(closeKeyboard()), parent, SLOT(openSidebar()));
return w;
}
@ -276,7 +273,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
border: none;
background: none;
font-size: 65px;
font-weight: 600;
font-weight: 500;
padding-top: 35px;
padding-bottom: 35px;
}
@ -327,11 +324,3 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
}
)");
}
void SettingsWindow::closeSidebar() {
sidebar_widget->setFixedWidth(0);
}
void SettingsWindow::openSidebar() {
sidebar_widget->setFixedWidth(500);
}

View File

@ -48,6 +48,4 @@ private:
public slots:
void setActivePanel();
void closeSidebar();
void openSidebar();
};

View File

@ -1,28 +0,0 @@
#include <string>
#include <QFontDatabase>
#include <QApplication>
#ifdef QCOM2
#include <qpa/qplatformnativeinterface.h>
#include <QPlatformSurfaceEvent>
#include <wayland-client-protocol.h>
#endif
#include "qt_window.hpp"
void setMainWindow(QWidget *w) {
float scale = getenv("SCALE") != NULL ? std::stof(getenv("SCALE")) : 1.0;
w->setFixedSize(vwp_w*scale, vwp_h*scale);
w->show();
#ifdef QCOM2
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
wl_surface *s = reinterpret_cast<wl_surface*>(native->nativeResourceForWindow("surface", w->windowHandle()));
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
w->showFullScreen();
#endif
}

View File

@ -1,4 +1,15 @@
#include <string>
#include <QWidget>
#include <QFontDatabase>
#include <QApplication>
#ifdef QCOM2
#include <qpa/qplatformnativeinterface.h>
#include <QPlatformSurfaceEvent>
#include <wayland-client-protocol.h>
#endif
#ifdef QCOM2
const int vwp_w = 2160, vwp_h = 1080;
@ -6,4 +17,16 @@
const int vwp_w = 1920, vwp_h = 1080;
#endif
void setMainWindow(QWidget *w);
inline void setMainWindow(QWidget *w) {
const float scale = getenv("SCALE") != NULL ? std::stof(getenv("SCALE")) : 1.0;
w->setFixedSize(vwp_w*scale, vwp_h*scale);
w->show();
#ifdef QCOM2
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
wl_surface *s = reinterpret_cast<wl_surface*>(native->nativeResourceForWindow("surface", w->windowHandle()));
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
w->showFullScreen();
#endif
}

View File

@ -1,6 +1,7 @@
#include <time.h>
#include <unistd.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
#ifndef BRANCH
@ -24,9 +25,7 @@ int fresh_clone() {
int err;
// Cleanup
err = std::system("rm -rf /tmp/openpilot");
if (err) return 1;
err = std::system("rm -rf /data/openpilot");
err = std::system("rm -rf /tmp/openpilot /data/openpilot");
if (err) return 1;
// Clone
@ -40,14 +39,22 @@ int fresh_clone() {
err = std::system("mv /tmp/openpilot /data");
if (err) return 1;
#ifdef SSH_KEYS
err = std::system("mkdir -p /data/params/d/");
if (err) return 1;
std::ofstream param;
param.open("/data/params/d/GithubSshKeys");
param << SSH_KEYS;
param.close();
#endif
return 0;
}
int install() {
int err;
// TODO: Disable SSH after install done
// Wait for valid time
while (!time_valid()) {
usleep(500 * 1000);

View File

@ -7,19 +7,20 @@
#include <QApplication>
#include "setup.hpp"
#include "offroad/wifi.hpp"
#include "offroad/networking.hpp"
#include "widgets/input_field.hpp"
#include "qt_window.hpp"
#define USER_AGENT "AGNOSSetup-0.1"
void Setup::download(QString url) {
setCurrentIndex(count() - 1);
setCurrentIndex(count() - 2);
QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
CURL *curl;
curl = curl_easy_init();
// TODO: exit with return code
if (!curl) return;
CURL *curl = curl_easy_init();
if (!curl) {
emit downloadFailed();
}
char tmpfile[] = "/tmp/installer_XXXXXX";
FILE *fp = fdopen(mkstemp(tmpfile), "w");
@ -30,7 +31,11 @@ void Setup::download(QString url) {
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_perform(curl);
int ret = curl_easy_perform(curl);
if (ret != CURLE_OK) {
emit downloadFailed();
}
curl_easy_cleanup(curl);
fclose(fp);
@ -41,68 +46,55 @@ QLabel * title_label(QString text) {
QLabel *l = new QLabel(text);
l->setStyleSheet(R"(
font-size: 100px;
font-weight: bold;
font-weight: 500;
)");
return l;
}
QWidget * Setup::getting_started() {
QWidget * Setup::build_page(QString title, QWidget *content, bool next, bool prev) {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->setContentsMargins(200, 100, 200, 100);
main_layout->setContentsMargins(50, 50, 50, 50);
main_layout->addWidget(title_label(title), 0, Qt::AlignLeft | Qt::AlignTop);
main_layout->addWidget(title_label("Getting Started"), 0, Qt::AlignCenter);
main_layout->addWidget(content);
QLabel *body = new QLabel("Before we get on the road, let's finish\ninstallation and cover some details.");
body->setStyleSheet(R"(font-size: 65px;)");
main_layout->addWidget(body, 0, Qt::AlignCenter);
QHBoxLayout *nav_layout = new QHBoxLayout();
main_layout->addSpacing(100);
QPushButton *back_btn = new QPushButton("Back");
nav_layout->addWidget(back_btn, 0, Qt::AlignBottom | Qt::AlignLeft);
QObject::connect(back_btn, SIGNAL(released()), this, SLOT(prevPage()));
back_btn->setVisible(prev);
QPushButton *btn = new QPushButton("Continue");
main_layout->addWidget(btn);
QObject::connect(btn, SIGNAL(released()), this, SLOT(nextPage()));
QPushButton *continue_btn = new QPushButton("Continue");
nav_layout->addWidget(continue_btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(continue_btn, SIGNAL(released()), this, SLOT(nextPage()));
continue_btn->setVisible(next);
main_layout->addLayout(nav_layout, 0);
QWidget *widget = new QWidget();
widget->setLayout(main_layout);
return widget;
}
QWidget * Setup::getting_started() {
QLabel *body = new QLabel("Before we get on the road, let's finish\ninstallation and cover some details.");
body->setAlignment(Qt::AlignHCenter);
body->setStyleSheet(R"(font-size: 80px;)");
return build_page("Getting Started", body, true, false);
}
QWidget * Setup::network_setup() {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->setContentsMargins(50, 50, 50, 50);
main_layout->addWidget(title_label("Connect to WiFi"), 0, Qt::AlignTop);
WifiUI *wifi = new WifiUI(this, 6);
main_layout->addWidget(wifi);
QObject::connect(wifi, &WifiUI::openKeyboard, this, [=]() {
this->continue_btn->setVisible(false);
});
QObject::connect(wifi, &WifiUI::closeKeyboard, this, [=]() {
this->continue_btn->setVisible(true);
});
continue_btn = new QPushButton("Continue");
main_layout->addWidget(continue_btn);
QObject::connect(continue_btn, SIGNAL(released()), this, SLOT(nextPage()));
QWidget *widget = new QWidget();
widget->setLayout(main_layout);
return widget;
Networking *wifi = new Networking(this, false);
return build_page("Connect to WiFi", wifi, true, true);
}
QWidget * Setup::software_selection() {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->setMargin(100);
main_layout->addWidget(title_label("Choose Software"), 0, Qt::AlignCenter);
main_layout->addSpacing(50);
QPushButton *dashcam_btn = new QPushButton("Dashcam");
main_layout->addWidget(dashcam_btn);
QObject::connect(dashcam_btn, &QPushButton::released, this, [=]() {
QObject::connect(dashcam_btn, &QPushButton::released, this, [=]() {
this->download("https://dashcam.comma.ai");
});
@ -110,39 +102,20 @@ QWidget * Setup::software_selection() {
QPushButton *custom_btn = new QPushButton("Custom");
main_layout->addWidget(custom_btn);
QObject::connect(custom_btn, SIGNAL(released()), this, SLOT(nextPage()));
main_layout->addSpacing(100);
QPushButton *prev_btn = new QPushButton("Back");
main_layout->addWidget(prev_btn);
QObject::connect(prev_btn, SIGNAL(released()), this, SLOT(prevPage()));
QObject::connect(custom_btn, &QPushButton::released, this, [=]() {
QString input_url = InputDialog::getText("Enter URL");
if (input_url.size()) {
this->download(input_url);
}
});
QWidget *widget = new QWidget();
widget->setLayout(main_layout);
return widget;
}
QWidget * Setup::custom_software() {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->setMargin(50);
main_layout->addWidget(title_label("Custom Software"), Qt::AlignTop | Qt::AlignHCenter);
InputField *input = new InputField();
input->setPromptText("Enter URL");
main_layout->addWidget(input);
QObject::connect(input, SIGNAL(emitText(QString)), this, SLOT(download(QString)));
QWidget *widget = new QWidget();
widget->setLayout(main_layout);
return widget;
return build_page("Choose Software", widget, false, true);
}
QWidget * Setup::downloading() {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->addWidget(title_label("Downloading..."), 0, Qt::AlignCenter);
QWidget *widget = new QWidget();
@ -150,6 +123,40 @@ QWidget * Setup::downloading() {
return widget;
}
QWidget * Setup::download_failed() {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->setContentsMargins(50, 50, 50, 50);
main_layout->addWidget(title_label("Download Failed"), 0, Qt::AlignLeft | Qt::AlignTop);
QLabel *body = new QLabel("Ensure the entered URL is valid, and the device's network connection is good.");
body->setWordWrap(true);
body->setAlignment(Qt::AlignHCenter);
body->setStyleSheet(R"(font-size: 80px;)");
main_layout->addWidget(body);
QHBoxLayout *nav_layout = new QHBoxLayout();
QPushButton *reboot_btn = new QPushButton("Reboot");
nav_layout->addWidget(reboot_btn, 0, Qt::AlignBottom | Qt::AlignLeft);
QObject::connect(reboot_btn, &QPushButton::released, this, [=]() {
#ifdef QCOM2
std::system("sudo reboot");
#endif
});
QPushButton *restart_btn = new QPushButton("Start over");
nav_layout->addWidget(restart_btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(restart_btn, &QPushButton::released, this, [=]() {
setCurrentIndex(0);
});
main_layout->addLayout(nav_layout, 0);
QWidget *widget = new QWidget();
widget->setLayout(main_layout);
return widget;
}
void Setup::prevPage() {
setCurrentIndex(currentIndex() - 1);
}
@ -162,23 +169,24 @@ Setup::Setup(QWidget *parent) {
addWidget(getting_started());
addWidget(network_setup());
addWidget(software_selection());
addWidget(custom_software());
addWidget(downloading());
addWidget(download_failed());
QObject::connect(this, SIGNAL(downloadFailed()), this, SLOT(nextPage()));
setStyleSheet(R"(
* {
font-family: Inter;
}
QWidget {
color: white;
background-color: black;
}
QPushButton {
font-size: 60px;
padding: 60px;
width: 800px;
color: white;
background-color: blue;
padding: 50px;
padding-right: 100px;
padding-left: 100px;
border: 7px solid white;
border-radius: 20px;
font-size: 50px;
}
)");
}

View File

@ -11,14 +11,17 @@ public:
explicit Setup(QWidget *parent = 0);
private:
QLineEdit *url_input;
QPushButton *continue_btn;
QWidget *getting_started();
QWidget *network_setup();
QWidget *software_selection();
QWidget *custom_software();
QWidget *downloading();
QWidget *download_failed();
QWidget *build_page(QString title, QWidget *content, bool next, bool prev);
signals:
void downloadFailed();
public slots:
void nextPage();

View File

@ -1,61 +1,86 @@
#include <QPushButton>
#include "input_field.hpp"
#include "qt_window.hpp"
InputField::InputField(QWidget *parent, int minTextLength): QWidget(parent), minTextLength(minTextLength) {
layout = new QGridLayout();
layout->setSpacing(30);
InputDialog::InputDialog(QString prompt_text, QWidget *parent): QDialog(parent) {
layout = new QVBoxLayout();
layout->setContentsMargins(50, 50, 50, 50);
layout->setSpacing(20);
label = new QLabel(this);
label->setStyleSheet(R"(font-size: 70px; font-weight: 500;)");
layout->addWidget(label, 0, 0,Qt::AlignLeft);
layout->setColumnStretch(0, 1);
// build header
QHBoxLayout *header_layout = new QHBoxLayout();
QPushButton* cancel = new QPushButton("Cancel");
cancel->setFixedSize(300, 150);
cancel->setStyleSheet(R"(padding: 0;)");
layout->addWidget(cancel, 0, 1, Qt::AlignRight);
QObject::connect(cancel, SIGNAL(released()), this, SLOT(emitEmpty()));
label = new QLabel(prompt_text, this);
label->setStyleSheet(R"(font-size: 75px; font-weight: 500;)");
header_layout->addWidget(label, 1, Qt::AlignLeft);
QPushButton* cancel_btn = new QPushButton("Cancel");
cancel_btn->setStyleSheet(R"(
padding: 30px;
padding-right: 45px;
padding-left: 45px;
border-radius: 7px;
font-size: 45px;
background-color: #444444;
)");
header_layout->addWidget(cancel_btn, 0, Qt::AlignRight);
QObject::connect(cancel_btn, SIGNAL(released()), this, SLOT(reject()));
layout->addLayout(header_layout);
// text box
layout->addSpacing(20);
line = new QLineEdit();
line->setStyleSheet(R"(
color: white;
border: none;
background-color: #444444;
font-size: 80px;
font-weight: 500;
padding: 10px;
)");
layout->addWidget(line, 1, 0, 1, -1);
layout->addWidget(line, 1, Qt::AlignTop);
k = new Keyboard(this);
QObject::connect(k, SIGNAL(emitButton(QString)), this, SLOT(getText(QString)));
layout->addWidget(k, 2, 0, 1, -1);
QObject::connect(k, SIGNAL(emitButton(QString)), this, SLOT(handleInput(QString)));
layout->addWidget(k, 2, Qt::AlignBottom);
setStyleSheet(R"(
* {
color: white;
background-color: black;
}
)");
setLayout(layout);
}
void InputField::setPromptText(QString text) {
label->setText(text);
QString InputDialog::getText(const QString prompt) {
InputDialog d = InputDialog(prompt);
const int ret = d.exec();
if (ret) {
return d.text();
} else {
return QString();
}
}
void InputField::emitEmpty() {
line->setText("");
emit cancel();
QString InputDialog::text() {
return line->text();
}
void InputField::getText(QString s) {
int InputDialog::exec() {
setMainWindow(this);
return QDialog::exec();
}
void InputDialog::handleInput(QString s) {
if (!QString::compare(s,"")) {
line->backspace();
}
if (!QString::compare(s,"")) {
if(line->text().length()<minTextLength){
setPromptText("Need at least "+QString::number(minTextLength)+" characters!");
return;
}
emitText(line->text());
line->setText("");
done(QDialog::Accepted);
}
QVector<QString> control_buttons {"", "", "ABC", "", "#+=", "", "123"};

View File

@ -3,30 +3,29 @@
#include <QLabel>
#include <QString>
#include <QWidget>
#include <QDialog>
#include <QLineEdit>
#include <QGridLayout>
#include <QVBoxLayout>
#include "keyboard.hpp"
class InputField : public QWidget {
class InputDialog : public QDialog {
Q_OBJECT
public:
explicit InputField(QWidget* parent = 0, int minTextLength = 0);
void setPromptText(QString text);
int minTextLength;
explicit InputDialog(QString prompt_text, QWidget* parent = 0);
static QString getText(QString prompt);
QString text();
private:
QLineEdit *line;
Keyboard *k;
QLabel *label;
QGridLayout *layout;
QVBoxLayout *layout;
public slots:
void getText(QString s);
void emitEmpty();
int exec() override;
signals:
void cancel();
void emitText(QString s);
private slots:
void handleInput(QString s);
};

View File

@ -7,20 +7,22 @@
#include "keyboard.hpp"
const int DEFAULT_WIDTH = 1;
const int SPACEBAR_WIDTH = 3;
const int DEFAULT_STRETCH = 1;
const int SPACEBAR_STRETCH = 3;
KeyboardLayout::KeyboardLayout(QWidget *parent, std::vector<QVector<QString>> layout) : QWidget(parent) {
QVBoxLayout* vlayout = new QVBoxLayout;
QButtonGroup* btn_group = new QButtonGroup(this);
vlayout->setMargin(0);
vlayout->setSpacing(15);
QButtonGroup* btn_group = new QButtonGroup(this);
QObject::connect(btn_group, SIGNAL(buttonClicked(QAbstractButton*)), parent, SLOT(handleButton(QAbstractButton*)));
int i = 0;
for (const auto &s : layout) {
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setSpacing(30);
if (i == 1) {
if (vlayout->count() == 1) {
hlayout->addSpacing(90);
}
@ -28,28 +30,32 @@ KeyboardLayout::KeyboardLayout(QWidget *parent, std::vector<QVector<QString>> la
QPushButton* btn = new QPushButton(p);
btn->setFixedHeight(120);
btn_group->addButton(btn);
hlayout->addSpacing(30);
if (p == QString(" ")) {
hlayout->addWidget(btn, SPACEBAR_WIDTH);
} else {
hlayout->addWidget(btn, DEFAULT_WIDTH);
}
hlayout->addWidget(btn, p == QString(" ") ? SPACEBAR_STRETCH : DEFAULT_STRETCH);
}
if (i == 1) {
if (vlayout->count() == 1) {
hlayout->addSpacing(90);
}
vlayout->addLayout(hlayout);
i++;
}
setStyleSheet(R"(
QPushButton {
font-size: 65px;
margin: 0px;
padding: 0px;
border-radius: 7px;
color: #dddddd;
background-color: #444444;
}
)");
setLayout(vlayout);
}
Keyboard::Keyboard(QWidget *parent) : QWidget(parent) {
Keyboard::Keyboard(QWidget *parent) : QFrame(parent) {
main_layout = new QStackedLayout;
main_layout->setMargin(0);
// lowercase
std::vector<QVector<QString>> lowercase = {
@ -69,7 +75,7 @@ Keyboard::Keyboard(QWidget *parent) : QWidget(parent) {
};
main_layout->addWidget(new KeyboardLayout(this, uppercase));
// 1234567890
// numbers + specials
std::vector<QVector<QString>> numbers = {
{"1","2","3","4","5","6","7","8","9","0"},
{"-","/",":",";","(",")","$","&&","@","\""},
@ -78,7 +84,7 @@ Keyboard::Keyboard(QWidget *parent) : QWidget(parent) {
};
main_layout->addWidget(new KeyboardLayout(this, numbers));
// Special characters
// extra specials
std::vector<QVector<QString>> specials = {
{"[","]","{","}","#","%","^","*","+","="},
{"_","\\","|","~","<",">","","£","¥",""},
@ -89,19 +95,8 @@ Keyboard::Keyboard(QWidget *parent) : QWidget(parent) {
setLayout(main_layout);
main_layout->setCurrentIndex(0);
setStyleSheet(R"(
QPushButton {
padding: 0;
font-size: 50px;
}
* {
background-color: #99777777;
}
)");
}
void Keyboard::handleButton(QAbstractButton* m_button) {
QString id = m_button->text();
if (!QString::compare(m_button->text(), "") || !QString::compare(m_button->text(), "ABC")) {

View File

@ -2,6 +2,7 @@
#include <vector>
#include <QFrame>
#include <QString>
#include <QWidget>
#include <QStackedLayout>
@ -14,7 +15,7 @@ public:
explicit KeyboardLayout(QWidget *parent, std::vector<QVector<QString>> layout);
};
class Keyboard : public QWidget {
class Keyboard : public QFrame {
Q_OBJECT
public:

View File

@ -1,20 +1,12 @@
#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QState>
#include <QStateMachine>
#include <QNetworkReply>
#include "common/params.h"
#include "widgets/ssh_keys.hpp"
#include "widgets/input_field.hpp"
#include "common/params.h"
QWidget* layout_to_widget(QLayout* l){
QWidget* q = new QWidget;
q->setLayout(l);
return q;
}
SSH::SSH(QWidget* parent) : QWidget(parent){
// init variables
@ -24,40 +16,25 @@ SSH::SSH(QWidget* parent) : QWidget(parent){
networkTimer->setInterval(5000);
connect(networkTimer, SIGNAL(timeout()), this, SLOT(timeout()));
// Layout on entering
QVBoxLayout* main_layout = new QVBoxLayout;
main_layout->setMargin(50);
// Construct the layouts to display
slayout = new QStackedLayout(this); // Initial screen, input, waiting for response
//Layout on entering
QVBoxLayout* initialLayout = new QVBoxLayout;
initialLayout->setContentsMargins(80, 80, 80, 80);
QHBoxLayout* header = new QHBoxLayout;
QPushButton* exitButton = new QPushButton("BACK", this);
exitButton->setFixedSize(500, 100);
header->addWidget(exitButton, 0, Qt::AlignLeft | Qt::AlignTop);
initialLayout->addWidget(layout_to_widget(header));
main_layout->addWidget(exitButton, 0, Qt::AlignLeft | Qt::AlignTop);
connect(exitButton, SIGNAL(released()), this, SIGNAL(closeSSHSettings()));
QLabel* title = new QLabel("Authorize SSH keys");
title->setStyleSheet(R"(font-size: 75px;)");
header->addWidget(title, 0, Qt::AlignRight | Qt::AlignTop);
QLabel* wallOfText = new QLabel("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A Comma employee will NEVER ask you to add their GitHub username.");
QLabel* wallOfText = new QLabel("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own.");
wallOfText->setAlignment(Qt::AlignHCenter);
wallOfText->setWordWrap(true);
wallOfText->setStyleSheet(R"(font-size: 60px;)");
initialLayout->addWidget(wallOfText, 0);
main_layout->addWidget(wallOfText, 0);
QPushButton* actionButton = new QPushButton;
actionButton->setFixedHeight(100);
initialLayout->addWidget(actionButton, 0, Qt::AlignBottom);
main_layout->addWidget(actionButton, 0, Qt::AlignBottom);
slayout->addWidget(layout_to_widget(initialLayout));
InputField* input = new InputField;
slayout->addWidget(input);
QLabel* loading = new QLabel("Loading SSH keys from GitHub.");
slayout->addWidget(loading);
setStyleSheet(R"(
QPushButton {
font-size: 60px;
@ -68,59 +45,61 @@ SSH::SSH(QWidget* parent) : QWidget(parent){
background-color: #444444;
}
)");
setLayout(slayout);
setLayout(main_layout);
//Initialize the state machine and states
// Initialize the state machine and states
QStateMachine* state = new QStateMachine(this);
QState* initialState = new QState(); //State when entering the widget
QState* initialStateNoGithub = new QState(); //Starting state, key not connected
QState* initialStateConnected = new QState(); //Starting state, ssh connected
QState* quitState = new QState(); // State when exiting the widget
QState* removeSSH_State = new QState(); // State when user wants to remove the SSH keys
QState* defaultInputFieldState = new QState(); // State when we want the user to give us the username
QState* removeSSH_State = new QState(); // State when user wants to remove the SSH keys
QState* loadingState = new QState(); // State while waiting for the network response
// Adding states to the state machine and adding the transitions
state->addState(initialState);
connect(initialState, &QState::entered, [=](){checkForSSHKey(); slayout->setCurrentIndex(0);});
connect(initialState, &QState::entered, [=](){
checkForSSHKey();
});
initialState->addTransition(this, &SSH::NoSSHAdded, initialStateNoGithub);
initialState->addTransition(this, &SSH::SSHAdded, initialStateConnected);
state->addState(quitState);
connect(quitState, &QState::entered, [=](){emit closeSSHSettings();});
quitState->addTransition(quitState, &QState::entered, initialState);
state->addState(initialStateConnected);
connect(initialStateConnected, &QState::entered, [=](){actionButton->setText("Remove GitHub SSH keys"); actionButton->setStyleSheet(R"(background-color: #750c0c;)");});
initialStateConnected->addTransition(exitButton, &QPushButton::released, quitState);
connect(initialStateConnected, &QState::entered, [=](){
actionButton->setText("Clear SSH keys");
actionButton->setStyleSheet(R"(background-color: #750c0c;)");
});
initialStateConnected->addTransition(actionButton, &QPushButton::released, removeSSH_State);
state->addState(removeSSH_State);
connect(removeSSH_State, &QState::entered, [=](){Params().delete_db_value("GithubSshKeys");});
connect(removeSSH_State, &QState::entered, [=](){
Params().delete_db_value("GithubSshKeys");
});
removeSSH_State->addTransition(removeSSH_State, &QState::entered, initialState);
state->addState(initialStateNoGithub);
connect(initialStateNoGithub, &QState::entered, [=](){actionButton->setText("Link GitHub SSH keys"); actionButton->setStyleSheet(R"(background-color: #444444;)");});
initialStateNoGithub->addTransition(exitButton, &QPushButton::released, quitState);
initialStateNoGithub->addTransition(actionButton, &QPushButton::released, defaultInputFieldState);
connect(actionButton, &QPushButton::released, [=](){input->setPromptText("Enter your GitHub username");});
state->addState(defaultInputFieldState);
connect(defaultInputFieldState, &QState::entered, [=](){slayout->setCurrentIndex(1);});
connect(input, &InputField::emitText, [=](QString a){usernameGitHub = a;}); // Store the string the user provided
defaultInputFieldState->addTransition(input, &InputField::cancel, initialState);
defaultInputFieldState->addTransition(input, &InputField::emitText, loadingState);
connect(initialStateNoGithub, &QState::entered, [=](){
actionButton->setText("Link GitHub SSH keys");
actionButton->setStyleSheet(R"(background-color: #444444;)");
});
initialStateNoGithub->addTransition(actionButton, &QPushButton::released, loadingState);
state->addState(loadingState);
connect(loadingState, &QState::entered, [=](){slayout->setCurrentIndex(2); getSSHKeys();});
connect(this, &SSH::failedResponse, [=](QString message){input->setPromptText(message);});
loadingState->addTransition(this, &SSH::failedResponse, defaultInputFieldState);
connect(loadingState, &QState::entered, [=](){
QString user = InputDialog::getText("Enter your GitHub username");
if (user.size()) {
getSSHKeys(user);
}
});
connect(this, &SSH::failedResponse, [=](QString message){
QString user = InputDialog::getText(message);
if (user.size()) {
getSSHKeys(user);
}
});
loadingState->addTransition(loadingState, &QState::entered, initialState);
loadingState->addTransition(this, &SSH::failedResponse, initialState);
loadingState->addTransition(this, &SSH::gotSSHKeys, initialState);
state->setInitialState(initialState);
state->start();
}
@ -134,8 +113,8 @@ void SSH::checkForSSHKey(){
}
}
void SSH::getSSHKeys(){
QString url = "https://github.com/" + usernameGitHub + ".keys";
void SSH::getSSHKeys(QString username){
QString url = "https://github.com/" + username + ".keys";
aborted = false;
reply = manager->get(QNetworkRequest(QUrl(url)));
connect(reply, SIGNAL(finished()), this, SLOT(parseResponse()));
@ -155,9 +134,9 @@ void SSH::parseResponse(){
Params().write_db_value("GithubSshKeys", response.toStdString());
emit gotSSHKeys();
} else {
emit failedResponse("Username "+usernameGitHub+" doesn't exist");
emit failedResponse("Username doesn't exist");
}
}else{
} else {
emit failedResponse("Request timed out");
}
reply->deleteLater();

View File

@ -1,15 +1,9 @@
#pragma once
#include <QWidget>
#include <QButtonGroup>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QPushButton>
#include <QTimer>
#include <QWidget>
#include <QNetworkAccessManager>
#include "widgets/input_field.hpp"
class SSH : public QWidget {
Q_OBJECT
@ -17,27 +11,22 @@ public:
explicit SSH(QWidget* parent = 0);
private:
InputField* inputField;
QStackedLayout* slayout;
QString usernameGitHub;
QNetworkAccessManager* manager;
QNetworkReply* reply;
QTimer* networkTimer;
bool aborted;
void getSSHKeys(QString user);
signals:
void closeSSHSettings();
void openKeyboard();
void closeKeyboard();
void NoSSHAdded();
void SSHAdded();
void failedResponse(QString errorString);
void gotSSHKeys();
void closeSSHSettings();
private slots:
void checkForSSHKey();
void getSSHKeys();
void timeout();
void parseResponse();
};