From 3fec15e4aa87224208eb612875c5b61673fc9748 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Oct 2021 14:45:06 +0200 Subject: [PATCH] Rework Mining UI --- src/utils/config.cpp | 5 + src/utils/config.h | 10 + src/utils/xmrig.cpp | 51 ++-- src/utils/xmrig.h | 10 +- src/widgets/XMRigWidget.cpp | 268 ++++++++++++-------- src/widgets/XMRigWidget.h | 14 +- src/widgets/XMRigWidget.ui | 483 ++++++++++++++++++------------------ 7 files changed, 473 insertions(+), 368 deletions(-) diff --git a/src/utils/config.cpp b/src/utils/config.cpp index 51f17ad2..dd0b838b 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -49,8 +49,13 @@ static const QHash configStrings = { {Config::showSearchbar,{QS("showSearchbar"), true}}, // Mining + {Config::miningMode,{QS("miningMode"), Config::MiningMode::Pool}}, {Config::xmrigPath,{QS("xmrigPath"), ""}}, + {Config::xmrigElevated,{QS("xmrigElevated"), false}}, + {Config::xmrigThreads,{QS("xmrigThreads"), 1}}, {Config::xmrigPool,{QS("xmrigPool"), "pool.xmr.pt:9000"}}, + {Config::xmrigNetworkTLS,{QS("xmrigNetworkTLS"), true}}, + {Config::xmrigNetworkTor,{QS("xmrigNetworkTor"), false}}, {Config::pools,{QS("pools"), {}}}, // Settings diff --git a/src/utils/config.h b/src/utils/config.h index 1dce5303..679b6913 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -53,8 +53,13 @@ public: showSearchbar, // Mining + miningMode, xmrigPath, + xmrigElevated, + xmrigThreads, xmrigPool, + xmrigNetworkTLS, + xmrigNetworkTor, pools, // Settings @@ -98,6 +103,11 @@ public: spendable }; + enum MiningMode { + Pool = 0, + Solo + }; + ~Config() override; QVariant get(ConfigKey key); QString getFileName(); diff --git a/src/utils/xmrig.cpp b/src/utils/xmrig.cpp index 2f8b613b..c80597d3 100644 --- a/src/utils/xmrig.cpp +++ b/src/utils/xmrig.cpp @@ -13,49 +13,58 @@ XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) { - this->rigDir = QDir(configDir).filePath("xmrig"); - m_process.setProcessChannelMode(QProcess::MergedChannels); connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput); connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError); - connect(&m_process, &QProcess::stateChanged, this, &XmRig::stateChanged); + connect(&m_process, &QProcess::stateChanged, this, &XmRig::onStateChanged); } void XmRig::stop() { - if(m_process.state() == QProcess::Running) { + qDebug() << m_process.processId(); + if (m_process.state() == QProcess::Running) { #if defined(Q_OS_WIN) m_process.kill(); // https://doc.qt.io/qt-5/qprocess.html#terminate -#else - m_process.terminate(); +#elif defined(Q_OS_LINUX) + if (m_elevated) { + m_killProcess.start("pkexec", QStringList() << "kill" << QString::number(m_process.processId())); + return; + } #endif + m_process.terminate(); } } void XmRig::start(const QString &path, int threads, const QString &address, const QString &username, - const QString &password, bool tor, bool tls) + const QString &password, bool tor, bool tls, bool elevated) { + m_elevated = elevated; + auto state = m_process.state(); if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting) { emit error("Can't start XMRig, already running or starting"); return; } - if(path.isEmpty()) { + if (path.isEmpty()) { emit error("XmRig->Start path parameter missing."); return; } - if(!Utils::fileExists(path)) { + if (!Utils::fileExists(path)) { emit error(QString("Path to XMRig binary invalid; file does not exist: %1").arg(path)); return; } QStringList arguments; + if (m_elevated) { + arguments << path; + } arguments << "-o" << address; arguments << "-a" << "rx/0"; arguments << "-u" << username; - if(!password.isEmpty()) + if (!password.isEmpty()) { arguments << "-p" << password; + } arguments << "--no-color"; arguments << "-t" << QString::number(threads); if (tor) { @@ -67,20 +76,30 @@ void XmRig::start(const QString &path, int threads, const QString &address, cons } arguments << "-x" << QString("%1:%2").arg(host, port); } - - if(tls) + if (tls) { arguments << "--tls"; + } arguments << "--donate-level" << "1"; QString cmd = QString("%1 %2").arg(path, arguments.join(" ")); emit output(cmd.toUtf8()); - m_process.start(path, arguments); + + if (m_elevated) { + m_process.start("pkexec", arguments); + } else { + m_process.start(path, arguments); + } } -void XmRig::stateChanged(QProcess::ProcessState state) { - if(state == QProcess::ProcessState::Running) +void XmRig::onStateChanged(QProcess::ProcessState state) { + emit stateChanged(state); + + if (state == QProcess::ProcessState::Running) { emit output("XMRig started"); - else if (state == QProcess::ProcessState::NotRunning) + } + + else if (state == QProcess::ProcessState::NotRunning) { emit output("XMRig stopped"); + } } void XmRig::handleProcessOutput() { diff --git a/src/utils/xmrig.h b/src/utils/xmrig.h index 2d89abc5..6b48c8f3 100644 --- a/src/utils/xmrig.h +++ b/src/utils/xmrig.h @@ -21,24 +21,24 @@ Q_OBJECT public: explicit XmRig(const QString &configDir, QObject *parent = nullptr); - void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true); + void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true, bool elevated = false); void stop(); - QString rigDir; - QString rigPath; - signals: void error(const QString &msg); void output(const QByteArray &data); void hashrate(const QString &rate); + void stateChanged(QProcess::ProcessState state); private slots: - void stateChanged(QProcess::ProcessState); + void onStateChanged(QProcess::ProcessState); void handleProcessOutput(); void handleProcessError(QProcess::ProcessError error); private: ChildProcess m_process; + QProcess m_killProcess; + bool m_elevated; }; #endif //FEATHER_XMRIG_H diff --git a/src/widgets/XMRigWidget.cpp b/src/widgets/XMRigWidget.cpp index 39cc22bd..7aa6e700 100644 --- a/src/widgets/XMRigWidget.cpp +++ b/src/widgets/XMRigWidget.cpp @@ -24,52 +24,54 @@ XMRigWidget::XMRigWidget(QSharedPointer ctx, QWidget *parent) { ui->setupUi(this); - QPixmap p(":assets/images/xmrig.svg"); - ui->lbl_logo->setPixmap(p.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - + connect(m_XMRig, &XmRig::stateChanged, this, &XMRigWidget::onXMRigStateChanged); connect(m_XMRig, &XmRig::output, this, &XMRigWidget::onProcessOutput); connect(m_XMRig, &XmRig::error, this, &XMRigWidget::onProcessError); connect(m_XMRig, &XmRig::hashrate, this, &XMRigWidget::onHashrate); - // table - ui->tableView->setModel(this->m_model); + // [Downloads] tab + ui->tableView->setModel(m_model); m_contextMenu->addAction(icons()->icon("network.png"), "Download file", this, &XMRigWidget::linkClicked); connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextMenu); connect(ui->tableView, &QTableView::doubleClicked, this, &XMRigWidget::linkClicked); - // threads - ui->threadSlider->setMinimum(1); - int threads = QThread::idealThreadCount(); - m_threads = threads / 2; - ui->threadSlider->setMaximum(threads); - ui->threadSlider->setValue(m_threads); - ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads)); - connect(ui->threadSlider, &QSlider::valueChanged, this, &XMRigWidget::onThreadsValueChanged); + // [Settings] tab + ui->poolFrame->show(); + ui->soloFrame->hide(); - // buttons - connect(ui->btn_start, &QPushButton::clicked, this, &XMRigWidget::onStartClicked); - connect(ui->btn_stop, &QPushButton::clicked, this, &XMRigWidget::onStopClicked); + // XMRig executable connect(ui->btn_browse, &QPushButton::clicked, this, &XMRigWidget::onBrowseClicked); - connect(ui->btn_clear, &QPushButton::clicked, this, &XMRigWidget::onClearClicked); + ui->lineEdit_path->setText(config()->get(Config::xmrigPath).toString()); - // defaults - ui->btn_stop->setEnabled(false); - ui->check_autoscroll->setChecked(true); - ui->relayTor->setChecked(false); - ui->check_tls->setChecked(true); - ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); - ui->label_status->hide(); - ui->soloFrame->hide(); - ui->poolFrame->hide(); - - // XMRig binary - auto path = config()->get(Config::xmrigPath).toString(); - if(!path.isEmpty()) { - ui->lineEdit_path->setText(path); + // Run as admin/root + bool elevated = config()->get(Config::xmrigElevated).toBool(); + if (elevated) { + ui->radio_elevateYes->setChecked(true); + } else { + ui->radio_elevateNo->setChecked(true); } + connect(ui->radio_elevateYes, &QRadioButton::toggled, this, &XMRigWidget::onXMRigElevationChanged); +#if defined(Q_OS_WIN) + ui->radio_elevateYes->setToolTip("Not supported on Windows, yet."); + ui->radio_elevateYes->setEnabled(false); + ui->radio_elevateNo->setChecked(true); +#endif + + // CPU threads + ui->threadSlider->setMinimum(1); + ui->threadSlider->setMaximum(QThread::idealThreadCount()); - // pools - ui->poolFrame->show(); + int threads = config()->get(Config::xmrigThreads).toInt(); + ui->threadSlider->setValue(threads); + ui->label_threads->setText(QString("CPU threads: %1").arg(threads)); + + connect(ui->threadSlider, &QSlider::valueChanged, this, &XMRigWidget::onThreadsValueChanged); + + // Mining mode + connect(ui->combo_miningMode, QOverload::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onMiningModeChanged); + ui->combo_miningMode->setCurrentIndex(config()->get(Config::miningMode).toInt()); + + // Pool/node address this->updatePools(); connect(ui->combo_pools, &QComboBox::currentTextChanged, this, &XMRigWidget::onPoolChanged); @@ -87,41 +89,45 @@ XMRigWidget::XMRigWidget(QSharedPointer ctx, QWidget *parent) this->updatePools(); }); - // info - ui->console->appendPlainText(QString("Detected %1 CPU threads.").arg(threads)); - if(!path.isEmpty() && !Utils::fileExists(path)) - ui->console->appendPlainText("Invalid path to XMRig binary detected. Please reconfigure on the Settings tab."); - else - ui->console->appendPlainText(QString("XMRig path set to %1").arg(path)); - - ui->console->appendPlainText("Ready to mine."); + // Network settings + connect(ui->check_tls, &QCheckBox::toggled, this, &XMRigWidget::onNetworkTLSToggled); + connect(ui->relayTor, &QCheckBox::toggled, this, &XMRigWidget::onNetworkTorToggled); + ui->check_tls->setChecked(config()->get(Config::xmrigNetworkTLS).toBool()); + ui->relayTor->setChecked(config()->get(Config::xmrigNetworkTor).toBool()); - // username/password - connect(ui->lineEdit_password, &QLineEdit::editingFinished, [=]() { - m_ctx->wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); - m_ctx->storeWallet(); - }); - connect(ui->lineEdit_address, &QLineEdit::editingFinished, [=]() { - m_ctx->wallet->setCacheAttribute("feather.xmrig_username", ui->lineEdit_address->text()); - m_ctx->storeWallet(); - }); - - // checkbox connects - connect(ui->check_solo, &QCheckBox::stateChanged, this, &XMRigWidget::onSoloChecked); - - // Xmrig username + // Receiving address auto username = m_ctx->wallet->getCacheAttribute("feather.xmrig_username"); - if(!username.isEmpty()) + if (!username.isEmpty()) { ui->lineEdit_address->setText(username); + } + connect(ui->lineEdit_address, &QLineEdit::textChanged, [=]() { + m_ctx->wallet->setCacheAttribute("feather.xmrig_username", ui->lineEdit_address->text()); + }); + connect(ui->btn_fillPrimaryAddress, &QPushButton::clicked, this, &XMRigWidget::onUsePrimaryAddressClicked); - // Xmrig passwd + // Password auto password = m_ctx->wallet->getCacheAttribute("feather.xmrig_password"); - if(!password.isEmpty()) { + if (!password.isEmpty()) { ui->lineEdit_password->setText(password); } else { ui->lineEdit_password->setText("featherwallet"); m_ctx->wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); } + connect(ui->lineEdit_password, &QLineEdit::textChanged, [=]() { + m_ctx->wallet->setCacheAttribute("feather.xmrig_password", ui->lineEdit_password->text()); + }); + + // [Status] tab + connect(ui->btn_start, &QPushButton::clicked, this, &XMRigWidget::onStartClicked); + connect(ui->btn_stop, &QPushButton::clicked, this, &XMRigWidget::onStopClicked); + connect(ui->btn_clear, &QPushButton::clicked, this, &XMRigWidget::onClearClicked); + + ui->btn_stop->setEnabled(false); + ui->check_autoscroll->setChecked(true); + ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); + ui->label_status->hide(); + + this->printConsoleInfo(); } bool XMRigWidget::isMining() { @@ -130,14 +136,11 @@ bool XMRigWidget::isMining() { void XMRigWidget::onWalletClosed() { this->onStopClicked(); - this->onClearClicked(); - ui->lineEdit_password->setText(""); - ui->lineEdit_address->setText(""); } void XMRigWidget::onThreadsValueChanged(int threads) { - m_threads = threads; - ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads)); + config()->set(Config::xmrigThreads, threads); + ui->label_threads->setText(QString("CPU threads: %1").arg(threads)); } void XMRigWidget::onPoolChanged(const QString &pool) { @@ -146,10 +149,15 @@ void XMRigWidget::onPoolChanged(const QString &pool) { } } +void XMRigWidget::onXMRigElevationChanged(bool elevated) { + config()->set(Config::xmrigElevated, elevated); +} + void XMRigWidget::onBrowseClicked() { - QString fileName = QFileDialog::getOpenFileName( - this, "Path to XMRig executable", QDir::homePath()); - if (fileName.isEmpty()) return; + QString fileName = QFileDialog::getOpenFileName(this, "Path to XMRig executable", QDir::homePath()); + if (fileName.isEmpty()) { + return; + } config()->set(Config::xmrigPath, fileName); ui->lineEdit_path->setText(fileName); } @@ -158,47 +166,50 @@ void XMRigWidget::onClearClicked() { ui->console->clear(); } +void XMRigWidget::onUsePrimaryAddressClicked() { + ui->lineEdit_address->setText(m_ctx->wallet->address(0, 0)); +} + void XMRigWidget::onStartClicked() { - QString xmrigPath; - bool solo = ui->check_solo->isChecked(); - xmrigPath = config()->get(Config::xmrigPath).toString(); + QString xmrigPath = config()->get(Config::xmrigPath).toString(); + if (!this->checkXMRigPath()) { + return; + } + + QString address = [this](){ + if (ui->combo_miningMode->currentIndex() == Config::MiningMode::Pool) { + return config()->get(Config::xmrigPool).toString(); + } else { + return ui->lineEdit_solo->text().trimmed(); + } + }(); + if (address.isEmpty()) { + ui->console->appendPlainText("No pool or node address set. Please configure on the Settings tab."); + return; + } // username is receiving address usually auto username = m_ctx->wallet->getCacheAttribute("feather.xmrig_username"); auto password = m_ctx->wallet->getCacheAttribute("feather.xmrig_password"); - if(username.isEmpty()) { - QString err = "Please specify a receiving address on the Settings screen"; - ui->console->appendPlainText(err); - QMessageBox::warning(this, "Error", err); + if (username.isEmpty()) { + ui->console->appendPlainText("Please specify a receiving address on the Settings screen."); return; } - QString address; - if(solo) - address = ui->lineEdit_solo->text().trimmed(); - else - address = config()->get(Config::xmrigPool).toString(); - - if(address.contains("cryptonote.social") && !username.contains(".")) { + if (address.contains("cryptonote.social") && !username.contains(".")) { // cryptonote social requires ., we'll just grab a few chars from primary addy username = QString("%1.%2").arg(username, m_ctx->wallet->address(0, 0).mid(0, 6)); } - m_XMRig->start(xmrigPath, m_threads, address, username, password, ui->relayTor->isChecked(), ui->check_tls->isChecked()); - ui->btn_start->setEnabled(false); - ui->btn_stop->setEnabled(true); - m_isMining = true; - emit miningStarted(); + int threads = ui->threadSlider->value(); + + m_XMRig->start(xmrigPath, threads, address, username, password, ui->relayTor->isChecked(), ui->check_tls->isChecked(), + ui->radio_elevateYes->isChecked()); } void XMRigWidget::onStopClicked() { m_XMRig->stop(); - ui->btn_start->setEnabled(true); - ui->btn_stop->setEnabled(false); - ui->label_status->hide(); - m_isMining = false; - emit miningEnded(); } void XMRigWidget::onProcessOutput(const QByteArray &data) { @@ -216,9 +227,7 @@ void XMRigWidget::onProcessError(const QString &msg) { ui->console->appendPlainText("\n" + msg); ui->btn_start->setEnabled(true); ui->btn_stop->setEnabled(false); - ui->label_status->hide(); - m_isMining = false; - emit miningEnded(); + this->setMiningStopped(); } void XMRigWidget::onHashrate(const QString &hashrate) { @@ -307,15 +316,78 @@ void XMRigWidget::updatePools() { } } -void XMRigWidget::onSoloChecked(int state) { - if(state == 2) { +void XMRigWidget::printConsoleInfo() { + ui->console->appendPlainText(QString("Detected %1 CPU threads.").arg(QThread::idealThreadCount())); + if (this->checkXMRigPath()) { + QString path = config()->get(Config::xmrigPath).toString(); + ui->console->appendPlainText(QString("XMRig path set to %1").arg(path)); + } +} + +void XMRigWidget::onMiningModeChanged(int mode) { + config()->set(Config::miningMode, mode); + + if (mode == Config::MiningMode::Pool) { + ui->poolFrame->show(); + ui->soloFrame->hide(); + ui->label_poolNodeAddress->setText("Pool address:"); + ui->check_tls->setChecked(true); + } else { // Solo mining ui->poolFrame->hide(); ui->soloFrame->show(); + ui->label_poolNodeAddress->setText("Node address:"); ui->check_tls->setChecked(false); } - else { - ui->poolFrame->show(); - ui->soloFrame->hide(); +} + +void XMRigWidget::onNetworkTLSToggled(bool checked) { + config()->set(Config::xmrigNetworkTLS, checked); +} + +void XMRigWidget::onNetworkTorToggled(bool checked) { + config()->set(Config::xmrigNetworkTor, checked); +} + +void XMRigWidget::onXMRigStateChanged(QProcess::ProcessState state) { + if (state == QProcess::ProcessState::Starting) { + ui->btn_start->setEnabled(false); + ui->btn_stop->setEnabled(false); + this->setMiningStarted(); + } + else if (state == QProcess::ProcessState::Running) { + ui->btn_start->setEnabled(false); + ui->btn_stop->setEnabled(true); + this->setMiningStarted(); + } + else if (state == QProcess::ProcessState::NotRunning) { + ui->btn_start->setEnabled(true); // todo + ui->btn_stop->setEnabled(false); + ui->label_status->hide(); + this->setMiningStopped(); + } +} + +void XMRigWidget::setMiningStopped() { + m_isMining = false; + emit miningEnded(); +} + +void XMRigWidget::setMiningStarted() { + m_isMining = true; + emit miningStarted(); +} + +bool XMRigWidget::checkXMRigPath() { + QString path = config()->get(Config::xmrigPath).toString(); + + if (path.isEmpty()) { + ui->console->appendPlainText("No XMRig executable is set. Please configure on the Settings tab."); + return false; + } else if (!Utils::fileExists(path)) { + ui->console->appendPlainText("Invalid path to XMRig executable detected. Please reconfigure on the Settings tab."); + return false; + } else { + return true; } } diff --git a/src/widgets/XMRigWidget.h b/src/widgets/XMRigWidget.h index d4b9fffc..bbe349f6 100644 --- a/src/widgets/XMRigWidget.h +++ b/src/widgets/XMRigWidget.h @@ -32,17 +32,22 @@ public slots: void onStartClicked(); void onStopClicked(); void onClearClicked(); + void onUsePrimaryAddressClicked(); void onDownloads(const QJsonObject &data); void linkClicked(); void onProcessError(const QString &msg); void onProcessOutput(const QByteArray &msg); void onHashrate(const QString &hashrate); - void onSoloChecked(int state); + void onMiningModeChanged(int mode); + void onNetworkTLSToggled(bool checked); + void onNetworkTorToggled(bool checked); + void onXMRigStateChanged(QProcess::ProcessState state); private slots: void onBrowseClicked(); void onThreadsValueChanged(int date); void onPoolChanged(const QString &pool); + void onXMRigElevationChanged(bool elevated); signals: void miningStarted(); @@ -51,15 +56,18 @@ signals: private: void showContextMenu(const QPoint &pos); void updatePools(); + void printConsoleInfo(); + void setMiningStopped(); + void setMiningStarted(); + bool checkXMRigPath(); QScopedPointer ui; QSharedPointer m_ctx; - XmRig * m_XMRig; + XmRig *m_XMRig; QStandardItemModel *m_model; QMenu *m_contextMenu; bool m_isMining = false; - int m_threads; QStringList m_urls; QStringList m_defaultPools{"pool.xmr.pt:9000", "pool.supportxmr.com:9000", "mine.xmrpool.net:443", "xmrpool.eu:9999", "xmr-eu1.nanopool.org:14433", "pool.minexmr.com:6666", "us-west.minexmr.com:6666", "monerohash.com:9999", "cryptonote.social:5555", "cryptonote.social:5556"}; }; diff --git a/src/widgets/XMRigWidget.ui b/src/widgets/XMRigWidget.ui index 8b1cb9f1..512a0ca3 100644 --- a/src/widgets/XMRigWidget.ui +++ b/src/widgets/XMRigWidget.ui @@ -33,7 +33,7 @@ - Mining + Status @@ -131,307 +131,298 @@ - - - + + + + + XMRig executable: + + + + + - - - Threads: + + + /path/to/xmrig - - - - - Qt::Horizontal - - - - + + + Browse + + - - - - Qt::Horizontal - - - - 0 - 20 - - - - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - + + - TLS + Run as admin/root: - - - - Tor - - + + + + + + Yes + + + + + + + No + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - + + - Solo mine + CPU threads: - - + + Qt::Horizontal - - - 40 - 20 - - - + - - - - - - - - QFrame::NoFrame - - - QFrame::Plain + + + + Mining mode: - - - 0 - - - 0 - - - 0 - - - - - Pool - - - - - - - - 0 - 0 - - - - - - - - Configure - - - - - - - Qt::Horizontal - - - - 414 - 20 - - - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - + + + + + - Node address + Pool mining - - - - + + - 127.0.0.1:18081 + Solo mining - - - - - - Qt::Horizontal - - - - 173 - 20 - - - - - - + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - - - + + - Receiving address + Pool/node address: - - + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + Configure + + + + + + + Qt::Horizontal + + + + 414 + 20 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + + + 127.0.0.1:18081 + + + + + + + Qt::Horizontal + + + + 173 + 20 + + + + + + + + - - + + - Password (optional) + Network settings: - - + + + + + + Secure connection (TLS) + + + + + + + Connect via Tor + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - + + - XMRig executable + Receiving address: - - + + - - - /path/to/xmrig - - + - + - Browse + Use primary address + + + + Password (optional): + + + + + + - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 24 - 20 - - - - - - - - - - logoimg - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - -- 2.52.0