Merge pull request 'Solo mining' (#89) from solo-mining into master

Reviewed-on: #89
kryfi-block-explorer
dsc 2 years ago
commit 1b6f648a0b

@ -10,9 +10,7 @@ set(VERSION_REVISION "0")
set(VERSION "beta-4") set(VERSION "beta-4")
option(FETCH_DEPS "Download dependencies if they are not found" ON) option(FETCH_DEPS "Download dependencies if they are not found" ON)
option(XMRIG "Include XMRig module")
option(OPENVR "Include OpenVR support") option(OPENVR "Include OpenVR support")
option(QML "Include QtQuick (QML)")
option(ANDROID "Android deployment") option(ANDROID "Android deployment")
option(ANDROID_DEBUG "View the Android app on desktop") option(ANDROID_DEBUG "View the Android app on desktop")
option(TOR_BIN "Path to Tor binary to embed inside WOWlet") option(TOR_BIN "Path to Tor binary to embed inside WOWlet")
@ -33,9 +31,6 @@ set(BUILD_GUI_DEPS ON)
set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries") set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries")
set(INSTALL_VENDORED_LIBUNBOUND ${STATIC}) set(INSTALL_VENDORED_LIBUNBOUND ${STATIC})
set(USE_SINGLE_BUILDDIR ON) set(USE_SINGLE_BUILDDIR ON)
if(OPENVR OR ANDROID_DEBUG)
set(QML ON)
endif()
# Are we in debug mode? # Are we in debug mode?
set(_CMAKE_BUILD_TYPE "") set(_CMAKE_BUILD_TYPE "")

@ -39,11 +39,7 @@ file(GLOB SOURCE_FILES
"dialog/*.cpp" "dialog/*.cpp"
) )
if(QML) find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick QuickWidgets Qml QuickControls2 QuickCompiler QmlImportScanner Multimedia)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick Qml QuickControls2 QmlImportScanner Multimedia)
else()
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Multimedia)
endif()
if(OPENVR) if(OPENVR)
# include some extra files # include some extra files
@ -172,10 +168,6 @@ if(TOR_BIN)
target_compile_definitions(wowlet PRIVATE HAS_TOR_BIN=1) target_compile_definitions(wowlet PRIVATE HAS_TOR_BIN=1)
endif() endif()
if(XMRIG)
target_compile_definitions(wowlet PRIVATE HAS_XMRIG=1)
endif()
if(ANDROID) if(ANDROID)
target_compile_definitions(wowlet PRIVATE HAS_ANDROID=1) target_compile_definitions(wowlet PRIVATE HAS_ANDROID=1)
endif() endif()
@ -216,10 +208,7 @@ endif()
target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC) target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC)
if(QML) qt5_import_qml_plugins(${PROJECT_NAME})
qt5_import_qml_plugins(${PROJECT_NAME})
target_compile_definitions(wowlet PRIVATE HAS_QML=1)
endif()
target_compile_definitions(wowlet target_compile_definitions(wowlet
PUBLIC PUBLIC
@ -260,30 +249,20 @@ target_link_libraries(wowlet PUBLIC
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})
# Link Qt libraries # Link Qt libraries
if(QML) target_link_libraries(wowlet PUBLIC
target_link_libraries(wowlet PUBLIC Qt5::Core
Qt5::Core Qt5::Widgets
Qt5::Widgets Qt5::Gui
Qt5::Gui Qt5::Network
Qt5::Network Qt5::Svg
Qt5::Svg Qt5::QSvgPlugin
Qt5::QSvgPlugin Qt5::QSvgIconPlugin
Qt5::QSvgIconPlugin Qt5::Xml
Qt5::Xml Qt5::WebSockets
Qt5::WebSockets Qt5::Quick
Qt5::Quick Qt5::Qml
Qt5::Qml Qt5::QuickControls2
Qt5::QuickControls2) Qt5::QuickWidgets)
else()
target_link_libraries(wowlet PUBLIC
Qt5::Core
Qt5::Widgets
Qt5::Gui
Qt5::Network
Qt5::Svg
Qt5::Xml
Qt5::WebSockets)
endif()
if(ANDROID) if(ANDROID)
# yolo some hardcoded paths # yolo some hardcoded paths
@ -387,9 +366,7 @@ message(STATUS "VERSION_MAJOR: ${VERSION_MAJOR}")
message(STATUS "VERSION_MINOR: ${VERSION_MINOR}") message(STATUS "VERSION_MINOR: ${VERSION_MINOR}")
message(STATUS "VERSION_REVISION: ${VERSION_REVISION}") message(STATUS "VERSION_REVISION: ${VERSION_REVISION}")
message(STATUS "STATIC: ${STATIC}") message(STATUS "STATIC: ${STATIC}")
message(STATUS "Include QtQuick (QML): ${QML}")
message(STATUS "VERSION: ${VERSION}") message(STATUS "VERSION: ${VERSION}")
message(STATUS "Include the XMRIG tab: ${XMRIG}")
message(STATUS "Include Valve's OpenVR library: ${OPENVR}") message(STATUS "Include Valve's OpenVR library: ${OPENVR}")
message(STATUS "This build is for Android: ${ANDROID}") message(STATUS "This build is for Android: ${ANDROID}")
message(STATUS "This build is for testing the Android app on desktop: ${ANDROID_DEBUG}") message(STATUS "This build is for testing the Android app on desktop: ${ANDROID_DEBUG}")

@ -163,10 +163,8 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
AppContext::prices = new Prices(); AppContext::prices = new Prices();
// XMRig // XMRig
#ifdef HAS_XMRIG
this->XMRig = new XmRig(this->configDirectory, this); this->XMRig = new XmRig(this->configDirectory, this);
this->XMRig->prepare(); this->XMRig->prepare();
#endif
this->walletManager = WalletManager::instance(); this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDirectory); QString logPath = QString("%1/daemon.log").arg(configDirectory);
@ -195,12 +193,14 @@ void AppContext::initTor() {
this->tor = new Tor(this, this); this->tor = new Tor(this, this);
this->tor->start(); this->tor->start();
if (!isWhonix && backendHost.contains(".onion")) { if (!isWhonix && !backendHost.contains(".onion")) {
qDebug() << "'backend-host' did not contain '.onion' - running without Tor proxy."; qDebug() << "'backend-host' did not contain '.onion' - running without Tor proxy.";
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); return;
this->network->setProxy(*networkProxy);
this->ws->webSocket.setProxy(*networkProxy);
} }
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy);
this->ws->webSocket.setProxy(*networkProxy);
} }
void AppContext::initWS() { void AppContext::initWS() {
@ -447,11 +447,12 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
else if(cmd == "rpc_nodes") { else if(cmd == "rpc_nodes") {
this->onWSNodes(msg.value("data").toArray()); this->onWSNodes(msg.value("data").toArray());
} }
#if defined(HAS_XMRIG)
else if(cmd == "xmrig") { else if(cmd == "xmrig") {
this->XMRigDownloads(msg.value("data").toObject()); this->XMRigDownloads(msg.value("data").toObject());
} }
#endif else if(cmd == "wownerod_releases") {
emit WownerodDownloads(msg.value("data").toObject());
}
else if(cmd == "crypto_rates") { else if(cmd == "crypto_rates") {
QJsonArray crypto_rates = msg.value("data").toArray(); QJsonArray crypto_rates = msg.value("data").toArray();
AppContext::prices->cryptoPricesReceived(crypto_rates); AppContext::prices->cryptoPricesReceived(crypto_rates);

@ -215,6 +215,7 @@ signals:
void yellowUpdated(); void yellowUpdated();
void nodeSourceChanged(NodeSource nodeSource); void nodeSourceChanged(NodeSource nodeSource);
void XMRigDownloads(const QJsonObject &data); void XMRigDownloads(const QJsonObject &data);
void WownerodDownloads(const QJsonObject &data);
void pinLookupReceived(QString address, QString pin); void pinLookupReceived(QString address, QString pin);
void pinLookupErrorReceived(); void pinLookupErrorReceived();
void pinReceived(QString pin); void pinReceived(QString pin);

@ -28,6 +28,8 @@
<file>assets/images/confirmed.svg</file> <file>assets/images/confirmed.svg</file>
<file>assets/images/connect.svg</file> <file>assets/images/connect.svg</file>
<file>assets/images/copy.png</file> <file>assets/images/copy.png</file>
<file>assets/images/dog_running.gif</file>
<file>assets/images/dog_sitting.gif</file>
<file>assets/images/edit.png</file> <file>assets/images/edit.png</file>
<file>assets/images/exchange.png</file> <file>assets/images/exchange.png</file>
<file>assets/images/exchange_white.png</file> <file>assets/images/exchange_white.png</file>
@ -37,6 +39,7 @@
<file>assets/images/eye_blind.png</file> <file>assets/images/eye_blind.png</file>
<file>assets/images/wowlet.png</file> <file>assets/images/wowlet.png</file>
<file>assets/images/file.png</file> <file>assets/images/file.png</file>
<file>assets/images/fire.png</file>
<file>assets/images/gnome-calc.png</file> <file>assets/images/gnome-calc.png</file>
<file>assets/images/history.png</file> <file>assets/images/history.png</file>
<file>assets/images/info.png</file> <file>assets/images/info.png</file>
@ -225,5 +228,31 @@
<file>assets/images/zoom.png</file> <file>assets/images/zoom.png</file>
<file>assets/mnemonic_25_english.txt</file> <file>assets/mnemonic_25_english.txt</file>
<file>assets/restore_heights_wownero_mainnet.txt</file> <file>assets/restore_heights_wownero_mainnet.txt</file>
<file alias="mining/bottom_center_console.png">assets/images/mining/bottom_center_console.png</file>
<file alias="mining/intel.png">assets/images/mining/intel.png</file>
<file alias="mining/amd.png">assets/images/mining/amd.png</file>
<file alias="mining/lowerleft_circle.png">assets/images/mining/lowerleft_circle.png</file>
<file alias="mining/lowerleft.png">assets/images/mining/lowerleft.png</file>
<file alias="mining/lower_repeat.png">assets/images/mining/lower_repeat.png</file>
<file alias="mining/lowerright.png">assets/images/mining/lowerright.png</file>
<file alias="mining/r_bottom.png">assets/images/mining/r_bottom.png</file>
<file alias="mining/r_left.png">assets/images/mining/r_left.png</file>
<file alias="mining/r_right.png">assets/images/mining/r_right.png</file>
<file alias="mining/topleft.png">assets/images/mining/topleft.png</file>
<file alias="mining/topright_bar.png">assets/images/mining/topright_bar.png</file>
<file alias="mining/topright_left.png">assets/images/mining/topright_left.png</file>
<file alias="mining/topright_middle.png">assets/images/mining/topright_middle.png</file>
<file alias="mining/topright_right.png">assets/images/mining/topright_right.png</file>
<file alias="mining/warning.png">assets/images/mining/warning.png</file>
<file alias="mining/axe.png">assets/images/mining/axe.png</file>
<file alias="mining/lowerleft_btn.png">assets/images/mining/lowerleft_btn.png</file>
<file alias="mining/elmo.gif">assets/images/mining/elmo.gif</file>
<file alias="mining/bubble.png">assets/images/mining/bubble.png</file>
<file alias="mining/mining.webp">assets/images/mining/mining.webp</file>
<file alias="fonts/ComicMono.ttf">assets/fonts/ComicMono.ttf</file>
<file alias="fonts/ComicMono-Bold.ttf">assets/fonts/ComicMono-Bold.ttf</file>
<file alias="mining.qml">ui/qml/mining.qml</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

@ -9,6 +9,23 @@
namespace globals namespace globals
{ {
const qreal cdiv = 1e11; const qreal cdiv = 1e11;
enum Tabs {
HOME = 0,
HISTORY,
SEND,
RECEIVE,
COINS,
CALC,
XMRIG
};
enum TabsHome {
FORUM,
REDDIT,
SUCHWOW,
WFS
};
} }
#endif //WOWLET_GLOBALS_H #endif //WOWLET_GLOBALS_H

@ -31,6 +31,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
Q_INIT_RESOURCE(assets); Q_INIT_RESOURCE(assets);
qputenv("QML_DISABLE_DISK_CACHE", "1");
#if defined(Q_OS_MAC) && defined(HAS_TOR_BIN) #if defined(Q_OS_MAC) && defined(HAS_TOR_BIN)
Q_INIT_RESOURCE(assets_tor_macos); Q_INIT_RESOURCE(assets_tor_macos);
@ -140,16 +141,8 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
#endif #endif
qRegisterMetaType<QVector<QString>>(); qRegisterMetaType<QVector<QString>>();
#ifdef HAS_QML
qputenv("QML_DISABLE_DISK_CACHE", "1");
#endif
#ifdef __ANDROID__ #ifdef __ANDROID__
if(android || androidDebug) { if(android || androidDebug) {
#ifndef HAS_QML
qCritical() << "Wowlet compiled without QML support. Try -DQML=ON";
return 1;
#endif
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication mobile_app(argc, argv); QGuiApplication mobile_app(argc, argv);
auto *ctx = new AppContext(&parser); auto *ctx = new AppContext(&parser);

@ -171,22 +171,25 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage); connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage);
// XMRig // XMRig
#ifdef HAS_XMRIG
m_xmrig = new XMRigWidget(m_ctx, this); m_xmrig = new XMRigWidget(m_ctx, this);
ui->xmrRigLayout->addWidget(m_xmrig); ui->xmrRigLayout->addWidget(m_xmrig);
connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::onProcessOutput); connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::onProcessOutput);
connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::onProcessError); connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::onProcessError);
connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::daemonOutput);
connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::daemonOutput);
connect(m_ctx->XMRig, &XmRig::blockReward, m_xmrig, &XMRigWidget::onBlockReward);
connect(m_ctx->XMRig, &XmRig::hashrate, m_xmrig, &XMRigWidget::onHashrate); connect(m_ctx->XMRig, &XmRig::hashrate, m_xmrig, &XMRigWidget::onHashrate);
connect(m_ctx->XMRig, &XmRig::daemonStateChanged, m_xmrig, &XMRigWidget::onDaemonStateChanged);
connect(m_ctx->XMRig, &XmRig::syncStatus, m_xmrig, &XMRigWidget::onSyncStatus);
connect(m_ctx->XMRig, &XmRig::uptimeChanged, m_xmrig, &XMRigWidget::onUptimeChanged);
connect(m_ctx->XMRig, &XmRig::daemonStateChanged, [=](DaemonMiningState state) {
m_ctx->setWindowTitle(state >= DaemonMiningState::mining);
});
connect(m_ctx, &AppContext::walletClosed, m_xmrig, &XMRigWidget::onWalletClosed); connect(m_ctx, &AppContext::walletClosed, m_xmrig, &XMRigWidget::onWalletClosed);
connect(m_ctx, &AppContext::walletOpened, m_xmrig, &XMRigWidget::onWalletOpened); connect(m_ctx, &AppContext::walletOpened, m_xmrig, &XMRigWidget::onWalletOpened);
connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onDownloads); connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onRigDownloads);
connect(m_ctx, &AppContext::WownerodDownloads, m_xmrig, &XMRigWidget::onWownerodDownloads);
connect(m_xmrig, &XMRigWidget::miningStarted, [=]{ m_ctx->setWindowTitle(true); });
connect(m_xmrig, &XMRigWidget::miningEnded, [=]{ m_ctx->setWindowTitle(false); });
#else
ui->tabWidget->setTabVisible(Tabs::XMRIG, false);
#endif
connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen); connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen);
connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries); connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries);
@ -197,8 +200,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(ui->redditWidget, &RedditWidget::setStatusText, this, &MainWindow::setStatusText); connect(ui->redditWidget, &RedditWidget::setStatusText, this, &MainWindow::setStatusText);
connect(ui->tabWidget, &QTabWidget::currentChanged, m_xmrig, &XMRigWidget::onMenuTabChanged);
connect(ui->tabHomeWidget, &QTabWidget::currentChanged, [](int index){ connect(ui->tabHomeWidget, &QTabWidget::currentChanged, [](int index){
config()->set(Config::homeWidget, TabsHome(index)); config()->set(Config::homeWidget, globals::TabsHome(index));
}); });
connect(m_ctx, &AppContext::donationNag, [=]{ connect(m_ctx, &AppContext::donationNag, [=]{
@ -297,7 +301,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
}); });
connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) { connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) {
ui->historyWidget->setSearchText(text); ui->historyWidget->setSearchText(text);
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
}); });
// History // History
@ -334,9 +338,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_ctx, &AppContext::walletAboutToClose, [=]{ connect(m_ctx, &AppContext::walletAboutToClose, [=]{
if (!config()->get(Config::showTabHome).toBool()) if (!config()->get(Config::showTabHome).toBool())
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
else else
ui->tabWidget->setCurrentIndex(Tabs::HOME); ui->tabWidget->setCurrentIndex(globals::Tabs::HOME);
// Clear all tables when wallet is closed // Clear all tables when wallet is closed
ui->historyWidget->resetModel(); ui->historyWidget->resetModel();
@ -421,13 +425,9 @@ void MainWindow::initMenu() {
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
#if defined(HAS_XMRIG)
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["Mining"] = new ToggleTab(ui->tabXmrRig, "Mining", "Mining", ui->actionShow_XMRig, Config::showTabXMRig); m_tabShowHideMapper["Mining"] = new ToggleTab(ui->tabXmrRig, "Mining", "Mining", ui->actionShow_XMRig, Config::showTabXMRig);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "Mining"); m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "Mining");
#else
ui->actionShow_XMRig->setVisible(false);
#endif
for (const auto &key: m_tabShowHideMapper.keys()) { for (const auto &key: m_tabShowHideMapper.keys()) {
const auto toggleTab = m_tabShowHideMapper.value(key); const auto toggleTab = m_tabShowHideMapper.value(key);
@ -526,7 +526,7 @@ void MainWindow::menuToggleTabVisible(const QString &key){
void MainWindow::initWidgets() { void MainWindow::initWidgets() {
int homeWidget = config()->get(Config::homeWidget).toInt(); int homeWidget = config()->get(Config::homeWidget).toInt();
ui->tabHomeWidget->setCurrentIndex(TabsHome(homeWidget)); ui->tabHomeWidget->setCurrentIndex(globals::TabsHome(homeWidget));
} }
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){ WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
@ -1060,25 +1060,25 @@ void MainWindow::donateButtonClicked() {
donation = 0.1337; donation = 0.1337;
ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the WOWlet development team", donation); ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the WOWlet development team", donation);
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::showHistoryTab() { void MainWindow::showHistoryTab() {
this->raise(); this->raise();
this->show(); this->show();
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
} }
void MainWindow::showSendTab() { void MainWindow::showSendTab() {
this->raise(); this->raise();
this->show(); this->show();
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::showHomeWindow() { void MainWindow::showHomeWindow() {
this->raise(); this->raise();
this->show(); this->show();
ui->tabWidget->setCurrentIndex(Tabs::HOME); ui->tabWidget->setCurrentIndex(globals::Tabs::HOME);
} }
void MainWindow::showCalcWindow() { void MainWindow::showCalcWindow() {
@ -1088,7 +1088,7 @@ void MainWindow::showCalcWindow() {
} }
void MainWindow::payToMany() { void MainWindow::payToMany() {
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
ui->sendWidget->payToMany(); ui->sendWidget->payToMany();
QMessageBox::information(this, "Pay to many", "Enter a list of outputs in the 'Pay to' field.\n" QMessageBox::information(this, "Pay to many", "Enter a list of outputs in the 'Pay to' field.\n"
"One output per line.\n" "One output per line.\n"
@ -1098,7 +1098,7 @@ void MainWindow::payToMany() {
void MainWindow::showSendScreen(const CCSEntry &entry) { void MainWindow::showSendScreen(const CCSEntry &entry) {
ui->sendWidget->fill(entry); ui->sendWidget->fill(entry);
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::suchDonate(const QString address) { void MainWindow::suchDonate(const QString address) {
@ -1106,7 +1106,7 @@ void MainWindow::suchDonate(const QString address) {
QString preferredCurrency = config()->get(Config::preferredFiatCurrency).toString(); QString preferredCurrency = config()->get(Config::preferredFiatCurrency).toString();
double donation = AppContext::prices->convert(preferredCurrency, "WOW", tipAmount); double donation = AppContext::prices->convert(preferredCurrency, "WOW", tipAmount);
ui->sendWidget->fill(address, "SuchWow contribution :-)", donation); ui->sendWidget->fill(address, "SuchWow contribution :-)", donation);
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::onViewOnBlockExplorer(const QString &txid) { void MainWindow::onViewOnBlockExplorer(const QString &txid) {
@ -1283,6 +1283,7 @@ void MainWindow::importOutputs() {
void MainWindow::cleanupBeforeClose() { void MainWindow::cleanupBeforeClose() {
m_ctx->closeWallet(false, true); m_ctx->closeWallet(false, true);
m_ctx->tor->stop(); m_ctx->tor->stop();
m_ctx->XMRig->stop();
this->saveGeo(); this->saveGeo();
} }

@ -33,6 +33,7 @@
#include "utils/config.h" #include "utils/config.h"
#include "wizard/walletwizard.h" #include "wizard/walletwizard.h"
#include "settings.h" #include "settings.h"
#include "globals.h"
#include "dialog/aboutdialog.h" #include "dialog/aboutdialog.h"
#include "dialog/signverifydialog.h" #include "dialog/signverifydialog.h"
#include "dialog/verifyproofdialog.h" #include "dialog/verifyproofdialog.h"
@ -74,23 +75,6 @@ public:
qreal screenDpiPhysical; qreal screenDpiPhysical;
qreal screenRatio; qreal screenRatio;
enum Tabs {
HOME = 0,
HISTORY,
SEND,
RECEIVE,
COINS,
CALC,
XMRIG
};
enum TabsHome {
FORUM,
REDDIT,
SUCHWOW,
WFS
};
public slots: public slots:
void initWidgets(); void initWidgets();
void initMenu(); void initMenu();

@ -316,31 +316,31 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabCalc"> <widget class="QWidget" name="tabXmrRig">
<attribute name="icon"> <attribute name="icon">
<iconset resource="assets.qrc"> <iconset resource="assets.qrc">
<normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset> <normaloff>:/assets/images/mining.png</normaloff>:/assets/images/mining.png</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Calc</string> <string>Mining</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="CalcWidget" name="conversionWidget" native="true"/> <layout class="QGridLayout" name="xmrRigLayout"/>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabXmrRig"> <widget class="QWidget" name="tabCalc">
<attribute name="icon"> <attribute name="icon">
<iconset resource="assets.qrc"> <iconset resource="assets.qrc">
<normaloff>:/assets/images/mining.png</normaloff>:/assets/images/mining.png</iconset> <normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Mining</string> <string>Calc</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0"> <item row="0" column="0">
<layout class="QGridLayout" name="xmrRigLayout"/> <widget class="CalcWidget" name="conversionWidget" native="true"/>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -0,0 +1,678 @@
import QtQuick 2.7
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.3
Rectangle {
id: root
color: "#181725"
anchors.fill: parent
property variant buffer: []
property int bufferMaxLength: 12
// state: 0:idle 1:startup 2:syncing 3:mining
signal startMining();
signal stopMining();
Component.onCompleted: {
calcAvailableHeightConsoleLines();
}
onHeightChanged: {
calcAvailableHeightConsoleLines();
}
function calcAvailableHeightConsoleLines() {
var max_lines = parseInt(textContainer.height / 20);
if(root.bufferMaxLength != max_lines && max_lines >= 4)
root.bufferMaxLength = max_lines;
}
// width: 980
// height: 480
Column {
FontLoader { id: comicMono; source: "qrc:/fonts/ComicMono.ttf" }
FontLoader { id: comicMonoBold; source: "qrc:/fonts/ComicMono-Bold.ttf" }
}
ColumnLayout {
width: parent.width
height: parent.height
spacing: 0
RowLayout {
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: 128
Image {
source: "qrc:/mining/topleft.png"
Layout.preferredWidth: 435
Layout.preferredHeight: 128
smooth: false
// top-left monitors
ColumnLayout {
width: 102
height: 74
anchors.left: parent.left
anchors.leftMargin: 14
anchors.top: parent.top
anchors.topMargin: 42
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
text: "Hashrate"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 14
font.family: comicMonoBold.name;
antialiasing: false
color: "#41FF00"
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
id: hashRateText
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 16
font.family: comicMono.name;
antialiasing: false
color: "#41FF00"
}
}
}
ColumnLayout {
width: 102
height: 74
anchors.left: parent.left
anchors.leftMargin: 142
anchors.top: parent.top
anchors.topMargin: 42
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
text: "uptime"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 14
font.family: comicMonoBold.name;
antialiasing: false
color: "#41FF00"
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
id: miningUptime
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 12
font.family: comicMono.name;
antialiasing: false
color: "#41FF00"
}
}
}
AnimatedImage {
visible: mining.daemonMiningState !== 0
source: "qrc:/mining/mining.webp"
fillMode: Image.PreserveAspectCrop
width: 115
height: 86
anchors.left: parent.left
anchors.leftMargin: 263
anchors.top: parent.top
anchors.topMargin: 34
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.preferredHeight: 128
spacing: 0
Image {
source: "qrc:/mining/warning.png"
Layout.fillWidth: true
Layout.preferredHeight: 15
fillMode: Image.TileHorizontally
smooth: false
}
Image {
source: "qrc:/mining/topright_bar.png"
Layout.fillWidth: true
Layout.preferredHeight: 4
fillMode: Image.TileHorizontally
smooth: false
}
RowLayout {
spacing: 0
Layout.fillHeight: true
Layout.preferredHeight: 102
Image {
Layout.preferredWidth: 5
Layout.preferredHeight: parent.height
source: "qrc:/mining/topright_left.png"
smooth: false
}
Image {
Layout.fillWidth: true
Layout.preferredHeight: parent.height
source: "qrc:/mining/topright_middle.png"
fillMode: Image.TileHorizontally
smooth: false
RowLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 6
anchors.right: parent.right
anchors.rightMargin: 8
anchors.topMargin: 12
height: 78
spacing: 16
ColumnLayout {
Layout.minimumWidth: 200
Layout.maximumWidth: 260
Layout.fillHeight: true
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
text: "Block Height"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 8
font.pointSize: 20
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
id: heightText
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 8
font.pointSize: 18
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
}
ColumnLayout {
Layout.minimumWidth: 200
Layout.maximumWidth: 260
Layout.fillHeight: true
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
text: "Sync Progress"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 8
font.pointSize: 20
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
id: syncPctText
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 18
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}
Image {
Layout.preferredWidth: 5
Layout.preferredHeight: parent.height
source: "qrc:/mining/topright_right.png"
smooth: false
}
}
Item {
Layout.preferredHeight: 7 // 15 + 4 + 102 + 7 = 128
Layout.fillWidth: true
}
}
}
RowLayout {
spacing: 0
//Layout.preferredHeight: 128
Layout.fillHeight: true
Layout.fillWidth: true
Image {
Layout.preferredWidth: 6
Layout.fillHeight: true
source: "qrc:/mining/r_left.png"
fillMode: Image.TileVertically
smooth: false
}
Item {
// text container
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
id: textContainer
color: "transparent"
height: parent.height - 20
width: parent.width - 32
clip: true
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Text {
id: cons
anchors.margins: 4
anchors.fill: parent
text: "Miner is idle."
font.pointSize: 12
font.family: comicMono.name;
wrapMode: Text.WordWrap
color: "white"
}
}
}
Image {
Layout.preferredWidth: 6
Layout.fillHeight: true
source: "qrc:/mining/r_right.png"
fillMode: Image.TileVertically
smooth: false
}
}
RowLayout {
spacing: 0
Layout.preferredHeight: 140
Layout.fillWidth: true
Image {
Layout.preferredWidth: 306
Layout.preferredHeight: 140
source: "qrc:/mining/lowerleft.png"
smooth: false
AnimatedImage {
source: mining.daemonMiningState === 0 ? "qrc:/assets/images/dog_sitting.gif" : "qrc:/assets/images/dog_running.gif"
width: 80
height: 60
anchors.bottom: parent.bottom
anchors.bottomMargin: 22
anchors.left: parent.left
anchors.leftMargin: 22
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
showBubble();
}
onExited: {
hideBubble();
}
}
}
}
Image {
Layout.fillWidth: true
Layout.preferredHeight: 140
source: "qrc:/mining/lower_repeat.png"
fillMode: Image.TileHorizontally
smooth: false
}
Image {
Layout.preferredWidth: 236
Layout.preferredHeight: 140
source: "qrc:/mining/bottom_center_console.png"
smooth: false
Rectangle {
// middle console clock
anchors.left: parent.left
anchors.leftMargin: 100
anchors.top: parent.top
anchors.topMargin: 8
color: "transparent"
width: 54
height: 16
Text {
id: clock
text: ""
antialiasing: false
font.pointSize: 9
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.family: comicMonoBold.name;
color: "#41FF00";
Component.onCompleted: {
root.setClock();
}
}
}
Image {
source: {
var imgs = ["qrc:/mining/amd.png", "qrc:/mining/intel.png"];
return imgs[Math.floor(Math.random()*imgs.length)];
}
width: 100
height: 100
fillMode: Image.Pad
smooth: false
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
Image {
Layout.fillWidth: true
Layout.preferredHeight: 140
source: "qrc:/mining/lower_repeat.png"
fillMode: Image.TileHorizontally
smooth: false
}
Image {
Layout.preferredWidth: 308
Layout.preferredHeight: 140
source: "qrc:/mining/lowerright.png"
smooth: false
Rectangle {
// lower-right button container
color: "transparent"
width: 106
height: 100
anchors.right: parent.right
anchors.rightMargin: 11
anchors.top: parent.top
anchors.topMargin: 34
Image {
id: imgAxe
visible: mining.daemonMiningState === 0
source: "qrc:/mining/axe.png"
width: 73
height: 75
smooth: false
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
AnimatedImage {
visible: mining.daemonMiningState !== 0
source: "qrc:/mining/elmo.gif"
width: 106
height: 100
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: stopMiningBtn
visible: true
text: "Stop Mining"
font.pointSize: 10
z: parent.z + 1
color: "black"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 28
font.family: comicMonoBold.name;
antialiasing: false
}
}
MouseArea {
anchors.fill: parent
z: parent.z + 1
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if(mining.daemonMiningState === 0) {
root.startMining();
root.calcAvailableHeightConsoleLines();
}
else
root.stopMining();
}
onEntered: {
imgAxe.height = 64
imgAxe.width = 64
}
onExited: {
imgAxe.height = 75
imgAxe.width = 73
}
}
}
}
}
}
Image {
id: bubble
visible: false
source: "qrc:/mining/bubble.png"
width: 200
height: 60
anchors.bottom: parent.bottom
anchors.bottomMargin: 64
anchors.left: parent.left
anchors.leftMargin: 48
Rectangle {
anchors.top: parent.top
anchors.topMargin: 6
anchors.left: parent.left
anchors.leftMargin: 10
height: 26
color: "transparent"
width: 183
z: parent.z + 1
Text {
id: bubbleText
text: ""
color: "black"
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
//font.family: ComicMonoBold.name;
}
}
}
Timer {
id: setClockTimer
interval: 1000*60
running: true
repeat: true
onTriggered: setClock()
}
Timer {
id: dogBubbleTimer
interval: 1000*30
running: true
repeat: true
onTriggered: {
if(Math.random() >= 0.5) return;
if(bubble.visible) return;
root.dogBubbleRemoval.stop();
root.dogBubbleRemoval.start();
var msg = root.bubbleMessage();
bubbleText.text = msg;
bubble.visible = true;
}
}
Timer {
id: dogBubbleRemoval
interval: 2500
running: false
repeat: false
onTriggered: bubble.visible = false;
}
function setClock() {
var now = new Date();
var hours = now.getHours();
var minutes = ('0'+now.getMinutes()).slice(-2);
clock.text = hours + ":" + minutes;
}
function resetComponents() {
hashRateText.text = "-";
miningUptime.text = "-";
syncPctText.text = "-";
heightText.text = "-";
}
function consoleAppend(line) {
if(root.buffer.length >= root.bufferMaxLength)
root.buffer.shift()
root.buffer.push(line);
cons.text = "";
for(var i = 0; i != root.bufferMaxLength; i++) {
if(root.buffer[i])
cons.text += root.buffer[i] + "\n";
}
}
Connections {
target: mining
function onDaemonOutput(line) {
root.consoleAppend(line);
}
function onSyncStatus(from, to, pct) {
syncPctText.text = pct + "%";
heightText.text = from + "/" + to;
}
function onUptimeChanged(uptime) {
miningUptime.text = uptime;
}
function onHashrate(hashrate) {
hashRateText.text = hashrate;
}
}
function showBubble() {
if(bubble.visible) return;
var msg = root.bubbleMessage();
bubbleText.text = msg;
bubble.visible = true;
}
function hideBubble() {
bubble.visible = false;
}
function bubbleMessage() {
var active = ["such work!", "mining WOW!", "woof woof!", "I am tired!", "mining :@", "weeeee", "blocks everywhere!", "wooohoo", "working, twerkin'", "looking4blocks", "mining blocks"];
var inactive = ["doing nothing!", "ZzZZzzZ", "wen mining?!", "ETA mining?!", "zZZzzZZz", "omg so bored", "so bored!!", "much idle, many zZz", "lets go!", "i'm ready!"];
var syncing = ["wen 1gbit", "syncin'", "zZzz", "ETA sync ready?!", "downloading blocks", "fetching blocks"];
var msg = "";
if(mining.daemonMiningState === 0) {
return inactive[Math.floor(Math.random()*inactive.length)];
} else if (mining.daemonMiningState === 2) {
return syncing[Math.floor(Math.random()*syncing.length)];
} else {
return active[Math.floor(Math.random()*active.length)];
}
}
}

@ -33,20 +33,21 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}}, {Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}},
{Config::walletPath,{QS("walletPath"), ""}}, {Config::walletPath,{QS("walletPath"), ""}},
{Config::xmrigPath,{QS("xmrigPath"), ""}}, {Config::xmrigPath,{QS("xmrigPath"), ""}},
{Config::xmrigPool,{QS("xmrigPool"), "cryptonote.social:2223"}}, {Config::wownerodPath, {QS("wownerodPath"), ""}},
{Config::nodes,{QS("nodes"), "{}"}}, {Config::nodes,{QS("nodes"), "{}"}},
{Config::websocketEnabled,{QS("websocketEnabled"), true}}, {Config::websocketEnabled,{QS("websocketEnabled"), true}},
{Config::nodeSource,{QS("nodeSource"), 0}}, {Config::nodeSource,{QS("nodeSource"), 0}},
{Config::useOnionNodes,{QS("useOnionNodes"), false}}, {Config::useOnionNodes,{QS("useOnionNodes"), false}},
{Config::showTabHome,{QS("showTabHome"), true}}, {Config::showTabHome,{QS("showTabHome"), true}},
{Config::showTabCoins,{QS("showTabCoins"), false}}, {Config::showTabCoins,{QS("showTabCoins"), false}},
{Config::showTabXMRig,{QS("showTabXMRig"), false}}, {Config::showTabXMRig,{QS("showTabXMRig"), true}},
{Config::showTabCalc,{QS("showTabCalc"), false}}, {Config::showTabCalc,{QS("showTabCalc"), false}},
{Config::geometry, {QS("geometry"), {}}}, {Config::geometry, {QS("geometry"), {}}},
{Config::windowState, {QS("windowState"), {}}}, {Config::windowState, {QS("windowState"), {}}},
{Config::firstRun,{QS("firstRun"), false}}, {Config::firstRun,{QS("firstRun"), false}},
{Config::hideBalance, {QS("hideBalance"), false}}, {Config::hideBalance, {QS("hideBalance"), false}},
{Config::hideOnClose, {QS("hideOnClose"), false}}, {Config::hideOnClose, {QS("hideOnClose"), false}},
{Config::simplifiedMiningInterface, {QS("simplifiedMiningInterface"), false}},
{Config::hideFiatBalance, {QS("hideFiatBalance"), false}}, {Config::hideFiatBalance, {QS("hideFiatBalance"), false}},
{Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}}, {Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}},
{Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}}, {Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}},

@ -35,7 +35,7 @@ public:
walletDirectory, walletDirectory,
walletPath, walletPath,
xmrigPath, xmrigPath,
xmrigPool, wownerodPath,
nodes, nodes,
websocketEnabled, websocketEnabled,
nodeSource, nodeSource,
@ -50,6 +50,7 @@ public:
hideBalance, hideBalance,
hideFiatBalance, hideFiatBalance,
hideOnClose, hideOnClose,
simplifiedMiningInterface,
redditFrontend, redditFrontend,
showHistorySyncNotice, showHistorySyncNotice,
ignoreUpdateWarning, ignoreUpdateWarning,

@ -7,17 +7,24 @@
#include "utils/utils.h" #include "utils/utils.h"
#include "utils/xmrig.h" #include "utils/xmrig.h"
#include "appcontext.h" #include "mainwindow.h"
XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) { XmRig::XmRig(const QString &configDir, QObject *parent) :
this->rigDir = QDir(configDir).filePath("xmrig"); QObject(parent),
m_statusTimer(new QTimer(this))
{
m_statusTimer->setInterval(5000);
connect(m_statusTimer, &QTimer::timeout, [this]{
if(daemonMiningState == DaemonMiningState::mining && m_process.state() == QProcess::Running)
m_process.write("status\n");
});
} }
void XmRig::prepare() { void XmRig::prepare() {
m_process.setProcessChannelMode(QProcess::MergedChannels); m_process.setProcessChannelMode(QProcess::MergedChannels);
connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput); connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::onHandleProcessOutput);
connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError); connect(&m_process, &QProcess::errorOccurred, this, &XmRig::onHandleProcessError);
connect(&m_process, &QProcess::stateChanged, this, &XmRig::stateChanged); connect(&m_process, &QProcess::stateChanged, this, &XmRig::onProcessStateChanged);
} }
void XmRig::stop() { void XmRig::stop() {
@ -28,74 +35,145 @@ void XmRig::stop() {
m_process.terminate(); m_process.terminate();
#endif #endif
} }
m_statusTimer->stop();
} }
void XmRig::start(const QString &path, bool XmRig::start(const QString &path, int threads) {
int threads, m_ctx = MainWindow::getContext();
const QString &address,
const QString &username,
const QString &password,
bool tor, bool tls) {
auto state = m_process.state(); auto state = m_process.state();
if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting) { if (state == QProcess::ProcessState::Running ||
emit error("Can't start XMRig, already running or starting"); state == QProcess::ProcessState::Starting ||
return; daemonMiningState != DaemonMiningState::idle) {
emit error("Can't start wownerod, already running or starting");
return false;
} }
if(path.isEmpty()) { if(path.isEmpty()) {
emit error("XmRig->Start path parameter missing."); emit error("wownerod path seems to be empty.");
return; return false;
} }
if(!Utils::fileExists(path)) { if(!Utils::fileExists(path)) {
emit error(QString("Path to XMRig binary invalid; file does not exist: %1").arg(path)); emit error(QString("Path to wownerod binary is invalid; file does not exist: %1").arg(path));
return; return false;
} }
auto privateSpendKey = m_ctx->currentWallet->getSecretSpendKey();
QStringList arguments; QStringList arguments;
arguments << "-o" << address; arguments << "--mining-threads" << QString::number(threads);
arguments << "-a" << "rx/wow"; arguments << "--start-mining" << m_ctx->currentWallet->address(0, 0);
arguments << "-u" << username; arguments << "--spendkey" << privateSpendKey;
if(!password.isEmpty())
arguments << "-p" << password;
arguments << "--no-color";
arguments << "-t" << QString::number(threads);
if(tor)
arguments << "-x" << QString("%1:%2").arg(Tor::torHost).arg(Tor::torPort);
if(tls)
arguments << "--tls";
arguments << "--donate-level" << "1";
QString cmd = QString("%1 %2").arg(path, arguments.join(" ")); QString cmd = QString("%1 %2").arg(path, arguments.join(" "));
cmd = cmd.replace(privateSpendKey, "[redacted]");
emit output(cmd.toUtf8()); emit output(cmd.toUtf8());
m_process.start(path, arguments); m_process.start(path, arguments);
m_statusTimer->start();
return true;
} }
void XmRig::stateChanged(QProcess::ProcessState state) { void XmRig::onProcessStateChanged(QProcess::ProcessState state) {
if(state == QProcess::ProcessState::Running) if(state == QProcess::ProcessState::Running) {
emit output("XMRig started"); emit output("wownerod started");
else if (state == QProcess::ProcessState::NotRunning) changeDaemonState(DaemonMiningState::startup);
emit output("XMRig stopped"); }
else if (state == QProcess::ProcessState::NotRunning) {
emit output("wownerod stopped");
changeDaemonState(DaemonMiningState::idle);
}
} }
void XmRig::handleProcessOutput() { void XmRig::onHandleProcessOutput() {
QByteArray _output = m_process.readAllStandardOutput(); QByteArray data = m_process.readAllStandardOutput();
if(_output.contains("miner") && _output.contains("speed")) {
// detect hashrate for(auto &line: data.split('\n')) {
auto str = Utils::barrayToString(_output); // remove timestamp
auto spl = str.mid(str.indexOf("speed")).split(" "); if(line.indexOf("\tI") >= 20)
auto rate = spl.at(2) + "H/s"; line = line.remove(0, line.indexOf("\tI") + 2);
qDebug() << "mining hashrate: " << rate; line = line.trimmed();
emit hashrate(rate);
// sad attempt at removing ANSI color codes
// yes this is stupid, no i dont care
// works remarkably well so far lmao
auto ansi_start = QByteArray("\x1b\x5b");
line = line.replace(ansi_start, "");
line = line.replace("0;36m", "");
line = line.replace("0;35m", "");
line = line.replace("0;34m", "");
line = line.replace("0;33m", "");
line = line.replace("0;32m", "");
line = line.replace("1;32m", "");
line = line.replace("1;33m", "");
line = line.replace("1;34m", "");
line = line.replace("1;35m", "");
line = line.replace("1;36m", "");
if(line.startsWith("0m")) continue;
auto lower = line.toLower();
if(lower.isEmpty() || lower.startsWith("status")) continue;
if(lower.startsWith("the daemon will start synchronizing")) {
changeDaemonState(DaemonMiningState::startup);
} else if(lower.startsWith("synchronization started")) {
changeDaemonState(DaemonMiningState::syncing);
} else if(lower.startsWith("synced") && lower.contains("left")) {
if(daemonMiningState < DaemonMiningState::syncing) changeDaemonState(DaemonMiningState::syncing);
QRegularExpression re("synced (\\d+)\\/(\\d+) \\((\\d+)%, (\\d+) left");
QRegularExpressionMatch match = re.match(lower);
if (match.hasMatch()) {
auto from = match.captured(1);
auto to = match.captured(2);
auto pct = match.captured(3);
m_from = from.toUInt();
m_to = to.toUInt();
emit syncStatus(m_from, m_to, pct.toInt());
}
} else if(lower.contains("mining started. good luck")) {
emit syncStatus(m_to, m_to, 100);
changeDaemonState(DaemonMiningState::mining);
}
else if(lower.contains("you won a block reward"))
emit blockReward();
else if(lower.contains("mining at")) {
QRegularExpression re("Height\\: (\\d+)\\/(\\d+) \\((.*)\\) on mainnet, mining at (.*), net hash .*, uptime (.*)");
QRegularExpressionMatch match = re.match(line);
if (match.hasMatch()) {
m_from = match.captured(1).toUInt();
m_to = match.captured(2).toUInt();
unsigned int pct = match.captured(3).replace("%", "").toDouble();
auto rate = match.captured(4);
auto uptime = match.captured(5).replace(" ", "");
emit uptimeChanged(uptime);
emit syncStatus(m_to, m_to, pct);
emit hashrate(rate);
line = line.remove(0, line.indexOf("mining at"));
}
}
emit output(line.trimmed());
} }
}
emit output(_output); void XmRig::changeDaemonState(const DaemonMiningState state) {
if(daemonMiningState == state) return;
daemonMiningState = state;
emit daemonStateChanged(daemonMiningState);
} }
void XmRig::handleProcessError(QProcess::ProcessError err) { void XmRig::onHandleProcessError(QProcess::ProcessError err) {
if (err == QProcess::ProcessError::Crashed) if (err == QProcess::ProcessError::Crashed)
emit error("XMRig crashed or killed"); emit error("wownerod crashed or killed");
else if (err == QProcess::ProcessError::FailedToStart) { else if (err == QProcess::ProcessError::FailedToStart) {
auto path = config()->get(Config::xmrigPath).toString(); auto path = config()->get(Config::wownerodPath).toString();
emit error(QString("XMRig binary failed to start: %1").arg(path)); emit error(QString("wownerod binary failed to start: %1").arg(path));
} }
changeDaemonState(DaemonMiningState::idle);
} }

@ -14,6 +14,14 @@
#include "utils/childproc.h" #include "utils/childproc.h"
enum DaemonMiningState {
idle = 0,
startup,
syncing,
mining
};
class AppContext;
class XmRig : public QObject class XmRig : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -22,24 +30,33 @@ public:
explicit XmRig(const QString &configDir, QObject *parent = nullptr); explicit XmRig(const QString &configDir, QObject *parent = nullptr);
void prepare(); void prepare();
void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true); bool start(const QString &path, int threads);
void stop(); void stop();
QString rigDir; DaemonMiningState daemonMiningState = DaemonMiningState::idle;
QString rigPath;
signals: signals:
void error(const QString &msg); void error(const QString &msg);
void output(const QByteArray &data); void output(const QByteArray &data);
void blockReward();
void syncStatus(unsigned int from, unsigned int to, unsigned int pct);
void hashrate(const QString &rate); void hashrate(const QString &rate);
void daemonStateChanged(DaemonMiningState state);
void uptimeChanged(QString &uptime);
private slots: private slots:
void stateChanged(QProcess::ProcessState); void onProcessStateChanged(QProcess::ProcessState);
void handleProcessOutput(); void onHandleProcessOutput();
void handleProcessError(QProcess::ProcessError error); void onHandleProcessError(QProcess::ProcessError error);
private: private:
void changeDaemonState(DaemonMiningState state);
ChildProcess m_process; ChildProcess m_process;
AppContext *m_ctx;
QTimer *m_statusTimer;
unsigned int m_to;
unsigned int m_from;
}; };
#endif //WOWLET_XMRIG_H #endif //WOWLET_XMRIG_H

@ -15,19 +15,28 @@ XMRigWidget::XMRigWidget(AppContext *ctx, QWidget *parent) :
QWidget(parent), QWidget(parent),
ui(new Ui::XMRigWidget), ui(new Ui::XMRigWidget),
m_ctx(ctx), m_ctx(ctx),
m_model(new QStandardItemModel(this)), m_modelRig(new QStandardItemModel(this)),
m_contextMenu(new QMenu(this)) m_modelWownerod(new QStandardItemModel(this)),
m_contextMenuRig(new QMenu(this)),
m_contextMenuWownerod(new QMenu(this))
{ {
ui->setupUi(this); ui->setupUi(this);
this->resetUI();
QPixmap p(":assets/images/mining.png"); QPixmap p(":assets/images/fire.png");
ui->lbl_logo->setPixmap(p.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); ui->lbl_logo->setPixmap(p.scaled(268, 271, Qt::KeepAspectRatio, Qt::SmoothTransformation));
// table // table XMRig
ui->tableView->setModel(this->m_model); ui->tableRig->setModel(this->m_modelRig);
m_contextMenu->addAction(QIcon(":/assets/images/network.png"), "Download file", this, &XMRigWidget::linkClicked); m_contextMenuRig->addAction(QIcon(":/assets/images/network.png"), "Download file", this, &XMRigWidget::rigLinkClicked);
connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextMenu); connect(ui->tableRig, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextRigMenu);
connect(ui->tableView, &QTableView::doubleClicked, this, &XMRigWidget::linkClicked); connect(ui->tableRig, &QTableView::doubleClicked, this, &XMRigWidget::rigLinkClicked);
// table wownerod
ui->tableWownerod->setModel(this->m_modelWownerod);
m_contextMenuWownerod->addAction(QIcon(":/assets/images/network.png"), "Download file", this, &XMRigWidget::wownerodLinkClicked);
connect(ui->tableWownerod, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextWownerodMenu);
connect(ui->tableWownerod, &QTableView::doubleClicked, this, &XMRigWidget::wownerodLinkClicked);
// threads // threads
ui->threadSlider->setMinimum(1); ui->threadSlider->setMinimum(1);
@ -44,78 +53,107 @@ XMRigWidget::XMRigWidget(AppContext *ctx, QWidget *parent) :
connect(ui->btn_browse, &QPushButton::clicked, this, &XMRigWidget::onBrowseClicked); connect(ui->btn_browse, &QPushButton::clicked, this, &XMRigWidget::onBrowseClicked);
connect(ui->btn_clear, &QPushButton::clicked, this, &XMRigWidget::onClearClicked); connect(ui->btn_clear, &QPushButton::clicked, this, &XMRigWidget::onClearClicked);
// defaults // graphics
ui->btn_stop->setEnabled(false); bool simplifiedUI = config()->get(Config::simplifiedMiningInterface).toBool();
ui->comboBox_gfx->setCurrentIndex(simplifiedUI ? 1 : 0);
connect(ui->comboBox_gfx, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onSimplifiedMiningChanged);
// wownerod binary
auto path = config()->get(Config::wownerodPath).toString();
if(!path.isEmpty())
ui->lineEdit_path->setText(path);
connect(ui->lineEdit_path, &QLineEdit::textChanged, [=] {
config()->set(Config::wownerodPath, ui->lineEdit_path->text().trimmed());
});
// info
this->appendText(QString("Detected %1 CPU threads.").arg(threads));
if(path.isEmpty())
this->appendText(QString("wownerod path is empty - please point towards the wownerod executable.").arg(path));
else if(!Utils::fileExists(path))
this->appendText("Invalid path to the wownerod executable detected. Please set the correct path.");
else {
this->appendText(QString("wownerod path set to '%1'").arg(path));
this->appendText("Ready to mine.");
}
}
void XMRigWidget::resetUI() {
ui->consoleFrame->hide();
ui->qmlFrame->hide();
ui->qmlFrameTxt->hide();
ui->check_autoscroll->setChecked(true); ui->check_autoscroll->setChecked(true);
ui->relayTor->setChecked(false);
ui->check_tls->setChecked(true);
ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse);
ui->label_status->hide(); ui->label_status->hide();
ui->soloFrame->hide(); ui->console->clear();
ui->poolFrame->hide();
// XMRig binary this->destroyQml();
auto path = config()->get(Config::xmrigPath).toString(); }
if(!path.isEmpty()) {
ui->lineEdit_path->setText(path);
}
// pools void XMRigWidget::startUI() {
ui->poolFrame->show(); this->resetUI();
ui->combo_pools->insertItems(0, m_pools); bool simplifiedUI = config()->get(Config::simplifiedMiningInterface).toBool();
auto preferredPool = config()->get(Config::xmrigPool).toString();
if (m_pools.contains(preferredPool)) if(simplifiedUI) {
ui->combo_pools->setCurrentIndex(m_pools.indexOf(preferredPool)); this->initConsole();
else { } else {
preferredPool = m_pools.at(0); this->initQML();
config()->set(Config::xmrigPool, preferredPool);
} }
connect(ui->combo_pools, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onPoolChanged); }
// info void XMRigWidget::initConsole() {
ui->console->appendPlainText(QString("Detected %1 CPU threads.").arg(threads)); ui->consoleFrame->show();
if(!path.isEmpty() && !Utils::fileExists(path)) }
ui->console->appendPlainText("Invalid path to XMRig binary detected. Please reconfigure on the Settings tab.");
else void XMRigWidget::initQML() {
ui->console->appendPlainText(QString("XMRig path set to %1").arg(path)); if(m_quickWidget != nullptr) return;
m_quickWidget = new QQuickWidget(this);
ui->console->appendPlainText("Ready to mine.");
auto *qctx = m_quickWidget->rootContext();
// username/password qctx->setContextProperty("cfg", config());
connect(ui->lineEdit_password, &QLineEdit::editingFinished, [=]() { qctx->setContextProperty("ctx", m_ctx);
m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_password", ui->lineEdit_password->text()); qctx->setContextProperty("mining", this);
m_ctx->storeWallet();
});
connect(ui->lineEdit_address, &QLineEdit::editingFinished, [=]() {
m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_username", ui->lineEdit_address->text());
m_ctx->storeWallet();
});
// checkbox connects m_quickWidget->setSource(QUrl("qrc:/mining.qml"));
connect(ui->check_solo, &QCheckBox::stateChanged, this, &XMRigWidget::onSoloChecked); m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect((QObject*)m_quickWidget->rootObject(), SIGNAL(startMining()),
this, SLOT(onStartClicked()));
connect((QObject*)m_quickWidget->rootObject(), SIGNAL(stopMining()),
this, SLOT(onStopClicked()));
ui->qmlFrame->layout()->addWidget(m_quickWidget);
ui->qmlFrame->show();
qDebug() << "created QML mining widget";
}
void XMRigWidget::destroyQml() {
if(m_quickWidget == nullptr) return;
m_quickWidget->disconnect();
m_quickWidget->deleteLater();
m_quickWidget = nullptr;
qDebug() << "destroyed QML mining widget";
}
void XMRigWidget::appendText(const QString &line) {
ui->console->appendPlainText(line);
m_consoleBuffer += 1;
if(m_consoleBuffer >= m_consoleBufferMax) {
ui->console->clear();
m_consoleBuffer = 0;
}
} }
void XMRigWidget::onWalletClosed() { void XMRigWidget::onWalletClosed() {
this->onStopClicked(); this->onStopClicked();
this->onClearClicked(); this->onClearClicked();
ui->lineEdit_password->setText("");
ui->lineEdit_address->setText("");
} }
void XMRigWidget::onWalletOpened(Wallet *wallet){ void XMRigWidget::onWalletOpened(Wallet *wallet){
// Xmrig username int egiwoge = 1;
auto username = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_username");
if(!username.isEmpty())
ui->lineEdit_address->setText(username);
// Xmrig passwd
auto password = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_password");
if(!password.isEmpty()) {
ui->lineEdit_password->setText(password);
} else {
ui->lineEdit_password->setText("wowlet");
m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_password", ui->lineEdit_password->text());
}
} }
void XMRigWidget::onThreadsValueChanged(int threads) { void XMRigWidget::onThreadsValueChanged(int threads) {
@ -123,99 +161,94 @@ void XMRigWidget::onThreadsValueChanged(int threads) {
ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads)); ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads));
} }
void XMRigWidget::onPoolChanged(int pos) {
config()->set(Config::xmrigPool, m_pools.at(pos));
}
void XMRigWidget::onBrowseClicked() { void XMRigWidget::onBrowseClicked() {
QString fileName = QFileDialog::getOpenFileName( QString fileName = QFileDialog::getOpenFileName(
this, "Path to XMRig executable", QDir::homePath()); this, "Path to wownerod executable", QDir::homePath());
if (fileName.isEmpty()) return; if (fileName.isEmpty()) return;
config()->set(Config::xmrigPath, fileName); config()->set(Config::wownerodPath, fileName);
ui->lineEdit_path->setText(fileName); ui->lineEdit_path->setText(fileName);
} }
void XMRigWidget::onClearClicked() { void XMRigWidget::onSyncStatus(unsigned int from, unsigned int to, unsigned int pct) {
ui->console->clear(); emit syncStatus(from, to, pct);
} }
void XMRigWidget::onStartClicked() { void XMRigWidget::onDaemonStateChanged(DaemonMiningState state) {
QString xmrigPath; if(state == DaemonMiningState::idle) {
bool solo = ui->check_solo->isChecked(); ui->btn_stop->setEnabled(false);
xmrigPath = config()->get(Config::xmrigPath).toString(); ui->btn_start->setEnabled(true);
ui->label_status->hide();
// username is receiving address usually } else {
auto username = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_username"); ui->btn_stop->setEnabled(true);
auto password = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_password"); ui->btn_start->setEnabled(false);
ui->label_status->show();
if(username.isEmpty()) {
QString err = "Please specify a receiving address on the Settings screen";
ui->console->appendPlainText(err);
QMessageBox::warning(this, "Error", err);
return;
} }
QString address; m_daemonMiningState = state;
if(solo) emit daemonMiningStateChanged();
address = ui->lineEdit_solo->text().trimmed(); }
else
address = config()->get(Config::xmrigPool).toString();
if(address.contains("cryptonote.social") && !username.contains(".")) { void XMRigWidget::onUptimeChanged(const QString &uptime) {
// cryptonote social requires <addr>.<username>, we'll just grab a few chars from primary addy emit uptimeChanged(uptime);
username = QString("%1.%2").arg(username, m_ctx->currentWallet->address(0, 0).mid(0, 6)); }
}
m_ctx->XMRig->start(xmrigPath, m_threads, address, username, password, ui->relayTor->isChecked(), ui->check_tls->isChecked()); void XMRigWidget::onBlockReward() {
QDateTime date = QDateTime::currentDateTime();
QString formattedTime = date.toString("yyyy/MM/dd hh:mm");
auto reward = QString("Congrats: new block found at %1").arg(formattedTime);
// @TODO: this might be blocking, what if multiple rewards happen?
QMessageBox::information(this, "Reward found", reward);
}
void XMRigWidget::onClearClicked() {
ui->console->clear();
}
void XMRigWidget::onStartClicked() {
auto binPath = config()->get(Config::wownerodPath).toString();
if(!m_ctx->XMRig->start(binPath, m_threads)) return;
ui->btn_start->setEnabled(false); ui->btn_start->setEnabled(false);
ui->btn_stop->setEnabled(true); ui->btn_stop->setEnabled(true);
emit miningStarted();
} }
void XMRigWidget::onStopClicked() { void XMRigWidget::onStopClicked() {
m_ctx->XMRig->stop(); if(m_ctx->XMRig->daemonMiningState != DaemonMiningState::idle)
ui->btn_start->setEnabled(true); m_ctx->XMRig->stop();
ui->btn_stop->setEnabled(false);
ui->label_status->hide();
emit miningEnded();
} }
void XMRigWidget::onProcessOutput(const QByteArray &data) { void XMRigWidget::onProcessOutput(const QByteArray &data) {
auto output = Utils::barrayToString(data); auto line = Utils::barrayToString(data);
if(output.endsWith("\n")) line = line.trimmed();
output = output.trimmed(); this->appendText(line);
ui->console->appendPlainText(output);
if(ui->check_autoscroll->isChecked()) if(ui->check_autoscroll->isChecked())
ui->console->verticalScrollBar()->setValue(ui->console->verticalScrollBar()->maximum()); ui->console->verticalScrollBar()->setValue(ui->console->verticalScrollBar()->maximum());
} }
void XMRigWidget::onProcessError(const QString &msg) { void XMRigWidget::onProcessError(const QString &msg) {
ui->console->appendPlainText("\n" + msg); this->appendText(msg);
ui->btn_start->setEnabled(true);
ui->btn_stop->setEnabled(false);
ui->label_status->hide();
emit miningEnded();
} }
void XMRigWidget::onHashrate(const QString &hashrate) { void XMRigWidget::onSimplifiedMiningChanged(int idx) {
ui->label_status->show(); config()->set(Config::simplifiedMiningInterface, idx == 1);
ui->label_status->setText(QString("Mining at %1").arg(hashrate)); this->startUI();
} }
void XMRigWidget::onDownloads(const QJsonObject &data) { void XMRigWidget::onHashrate(const QString &rate) {
// For the downloads table we'll manually update the table ui->label_status->show();
// with items once, as opposed to creating a class in ui->label_status->setText(QString("Mining at %1").arg(rate));
// src/models/. Saves effort; full-blown model emit hashrate(rate);
// is unnecessary in this case. }
m_model->clear(); void XMRigWidget::onWownerodDownloads(const QJsonObject &data) {
m_urls.clear(); m_modelWownerod->clear();
m_urlsWownerod.clear();
auto version = data.value("version").toString(); auto version = data.value("version").toString();
ui->label_latest_version->setText(QString("Latest version: %1").arg(version)); ui->label_latest_version_wownerod->setText(QString("Latest version: %1").arg(version));
QJsonObject assets = data.value("assets").toObject(); QJsonObject assets = data.value("assets").toObject();
const auto _linux = assets.value("linux").toArray(); const auto _linux = assets.value("linux").toArray();
@ -240,54 +273,112 @@ void XMRigWidget::onDownloads(const QJsonObject &data) {
auto _url = _obj.value("url").toString(); auto _url = _obj.value("url").toString();
auto _created_at = _obj.value("created_at").toString(); auto _created_at = _obj.value("created_at").toString();
m_urls.append(_url); m_urlsWownerod.append(_url);
auto download_count = _obj.value("download_count").toInt(); auto download_count = _obj.value("download_count").toInt();
m_model->setItem(i, 0, Utils::qStandardItem(_name)); m_modelWownerod->setItem(i, 0, Utils::qStandardItem(_name));
m_model->setItem(i, 1, Utils::qStandardItem(_created_at)); m_modelWownerod->setItem(i, 1, Utils::qStandardItem(_created_at));
m_model->setItem(i, 2, Utils::qStandardItem(QString::number(download_count))); m_modelWownerod->setItem(i, 2, Utils::qStandardItem(QString::number(download_count)));
i++; i++;
} }
m_model->setHeaderData(0, Qt::Horizontal, tr("Filename"), Qt::DisplayRole); m_modelWownerod->setHeaderData(0, Qt::Horizontal, tr("Filename"), Qt::DisplayRole);
m_model->setHeaderData(1, Qt::Horizontal, tr("Date"), Qt::DisplayRole); m_modelWownerod->setHeaderData(1, Qt::Horizontal, tr("Date"), Qt::DisplayRole);
m_model->setHeaderData(2, Qt::Horizontal, tr("Downloads"), Qt::DisplayRole); m_modelWownerod->setHeaderData(2, Qt::Horizontal, tr("Downloads"), Qt::DisplayRole);
ui->tableView->verticalHeader()->setVisible(false); ui->tableWownerod->verticalHeader()->setVisible(false);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); ui->tableWownerod->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->tableWownerod->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); ui->tableWownerod->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->tableView->setColumnWidth(2, 100); ui->tableWownerod->setColumnWidth(2, 100);
} }
void XMRigWidget::showContextMenu(const QPoint &pos) { void XMRigWidget::onMenuTabChanged(int index) {
QModelIndex index = ui->tableView->indexAt(pos); if(m_tabIndex == globals::Tabs::XMRIG && index != m_tabIndex)
if (!index.isValid()) { this->resetUI();
return; else if(globals::Tabs(index + 1) == globals::Tabs::XMRIG)
} this->startUI();
m_contextMenu->exec(ui->tableView->viewport()->mapToGlobal(pos)); m_tabIndex = index + 1;
} }
void XMRigWidget::onSoloChecked(int state) { void XMRigWidget::onRigDownloads(const QJsonObject &data) {
if(state == 2) { m_modelRig->clear();
ui->poolFrame->hide(); m_urlsRig.clear();
ui->soloFrame->show();
ui->check_tls->setChecked(false); auto version = data.value("version").toString();
ui->label_latest_version_rig->setText(QString("Latest version: %1").arg(version));
QJsonObject assets = data.value("assets").toObject();
const auto _linux = assets.value("linux").toArray();
const auto macos = assets.value("macos").toArray();
const auto windows = assets.value("windows").toArray();
auto info = QSysInfo::productType();
QJsonArray *os_assets;
if(info == "osx") {
os_assets = const_cast<QJsonArray *>(&macos);
} else if (info == "windows") {
os_assets = const_cast<QJsonArray *>(&windows);
} else {
// assume linux
os_assets = const_cast<QJsonArray *>(&_linux);
} }
else {
ui->poolFrame->show(); int i = 0;
ui->soloFrame->hide(); for(const auto &entry: *os_assets) {
auto _obj = entry.toObject();
auto _name = _obj.value("name").toString();
auto _url = _obj.value("url").toString();
auto _created_at = _obj.value("created_at").toString();
m_urlsRig.append(_url);
auto download_count = _obj.value("download_count").toInt();
m_modelRig->setItem(i, 0, Utils::qStandardItem(_name));
m_modelRig->setItem(i, 1, Utils::qStandardItem(_created_at));
m_modelRig->setItem(i, 2, Utils::qStandardItem(QString::number(download_count)));
i++;
} }
m_modelRig->setHeaderData(0, Qt::Horizontal, tr("Filename"), Qt::DisplayRole);
m_modelRig->setHeaderData(1, Qt::Horizontal, tr("Date"), Qt::DisplayRole);
m_modelRig->setHeaderData(2, Qt::Horizontal, tr("Downloads"), Qt::DisplayRole);
ui->tableRig->verticalHeader()->setVisible(false);
ui->tableRig->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableRig->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableRig->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->tableRig->setColumnWidth(2, 100);
}
void XMRigWidget::showContextRigMenu(const QPoint &pos) {
QModelIndex index = ui->tableRig->indexAt(pos);
if (!index.isValid())
return;
m_contextMenuRig->exec(ui->tableRig->viewport()->mapToGlobal(pos));
}
void XMRigWidget::showContextWownerodMenu(const QPoint &pos) {
QModelIndex index = ui->tableWownerod->indexAt(pos);
if (!index.isValid())
return;
m_contextMenuRig->exec(ui->tableWownerod->viewport()->mapToGlobal(pos));
}
void XMRigWidget::wownerodLinkClicked() {
QModelIndex index = ui->tableRig->currentIndex();
auto download_link = m_urlsRig.at(index.row());
Utils::externalLinkWarning(this, download_link);
} }
void XMRigWidget::linkClicked() { void XMRigWidget::rigLinkClicked() {
QModelIndex index = ui->tableView->currentIndex(); QModelIndex index = ui->tableRig->currentIndex();
auto download_link = m_urls.at(index.row()); auto download_link = m_urlsRig.at(index.row());
Utils::externalLinkWarning(this, download_link); Utils::externalLinkWarning(this, download_link);
} }
QStandardItemModel *XMRigWidget::model() { QStandardItemModel *XMRigWidget::model() {
return m_model; return m_modelRig;
} }
XMRigWidget::~XMRigWidget() { XMRigWidget::~XMRigWidget() {

@ -4,6 +4,10 @@
#ifndef XMRIGWIDGET_H #ifndef XMRIGWIDGET_H
#define XMRIGWIDGET_H #define XMRIGWIDGET_H
#include <QObject>
#include <QQuickWidget>
#include <QQuickView>
#include <QQmlContext>
#include <QMenu> #include <QMenu>
#include <QWidget> #include <QWidget>
#include <QItemSelection> #include <QItemSelection>
@ -11,6 +15,7 @@
#include "utils/xmrig.h" #include "utils/xmrig.h"
#include "utils/config.h" #include "utils/config.h"
#include "appcontext.h" #include "appcontext.h"
#include "globals.h"
namespace Ui { namespace Ui {
class XMRigWidget; class XMRigWidget;
@ -23,6 +28,10 @@ class XMRigWidget : public QWidget
public: public:
explicit XMRigWidget(AppContext *ctx, QWidget *parent = nullptr); explicit XMRigWidget(AppContext *ctx, QWidget *parent = nullptr);
~XMRigWidget() override; ~XMRigWidget() override;
Q_PROPERTY(int daemonMiningState READ daemonMiningState NOTIFY daemonMiningStateChanged);
int daemonMiningState() const { return m_daemonMiningState; }
QStandardItemModel *model(); QStandardItemModel *model();
public slots: public slots:
@ -31,32 +40,61 @@ public slots:
void onStartClicked(); void onStartClicked();
void onStopClicked(); void onStopClicked();
void onClearClicked(); void onClearClicked();
void onDownloads(const QJsonObject &data); void onBlockReward();
void linkClicked(); void onRigDownloads(const QJsonObject &data);
void onWownerodDownloads(const QJsonObject &data);
void rigLinkClicked();
void wownerodLinkClicked();
void onProcessError(const QString &msg); void onProcessError(const QString &msg);
void onProcessOutput(const QByteArray &msg); void onProcessOutput(const QByteArray &msg);
void onHashrate(const QString &hashrate); void onHashrate(const QString &rate);
void onSoloChecked(int state); void onDaemonStateChanged(DaemonMiningState state);
void onSyncStatus(unsigned int from, unsigned int to, unsigned int pct);
void onUptimeChanged(const QString &uptime);
void onMenuTabChanged(int index);
private slots: private slots:
void onBrowseClicked(); void onBrowseClicked();
void onThreadsValueChanged(int date); void onThreadsValueChanged(int date);
void onPoolChanged(int pos); void onSimplifiedMiningChanged(int idx);
signals: signals:
void miningStarted(); void daemonOutput(const QString &line);
void miningEnded(); void syncStatus(unsigned int from, unsigned int to, unsigned int pct);
void hashrate(const QString &rate);
void daemonMiningStateChanged();
void uptimeChanged(const QString &uptime);
//protected:
// void resizeEvent(QResizeEvent *event) override;
private: private:
void showContextMenu(const QPoint &pos); void showContextRigMenu(const QPoint &pos);
void showContextWownerodMenu(const QPoint &pos);
void appendText(const QString &line);
AppContext *m_ctx; AppContext *m_ctx;
Ui::XMRigWidget *ui; Ui::XMRigWidget *ui;
QStandardItemModel *m_model; QStandardItemModel *m_modelRig;
QMenu *m_contextMenu; QStandardItemModel *m_modelWownerod;
QMenu *m_contextMenuRig;
QMenu *m_contextMenuWownerod;
int m_threads; int m_threads;
QStringList m_urls; QStringList m_urlsRig;
QStringList m_pools{"cryptonote.social:2223", "pool.hashvault.pro:8888"}; QStringList m_urlsWownerod;
unsigned int m_tabIndex = 0;
unsigned int m_consoleBuffer = 0;
unsigned int m_consoleBufferMax = 2000;
int m_daemonMiningState = 0;
QQuickWidget *m_quickWidget = nullptr;
void resetUI();
void startUI();
void initConsole();
void initQML();
void destroyQml();
}; };
#endif // REDDITWIDGET_H #endif

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1329</width> <width>854</width>
<height>540</height> <height>431</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -29,22 +29,25 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">
<string>Mining</string> <string>Mining</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item> <item row="0" column="0">
<widget class="QFrame" name="outputFrame"> <widget class="QFrame" name="consoleFrame">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Plain</enum> <enum>QFrame::Plain</enum>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -57,14 +60,14 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item row="0" column="0">
<widget class="QPlainTextEdit" name="console"> <widget class="QPlainTextEdit" name="console">
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QPushButton" name="btn_clear"> <widget class="QPushButton" name="btn_clear">
@ -119,105 +122,128 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QFrame" name="qmlFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="qmlFrameTxt">
<property name="text">
<string>QML area here</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_3"> <widget class="QWidget" name="tab_3">
<attribute name="title"> <attribute name="title">
<string>Settings</string> <string>Settings</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_9"> <layout class="QGridLayout" name="gridLayout_6">
<item> <item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_6"> <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>386</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_6"> <layout class="QFormLayout" name="formLayout">
<item> <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_11"> <widget class="QLabel" name="label">
<property name="text">
<string>Graphics</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBox_gfx">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <property name="text">
<item> <string>Ultra</string>
<widget class="QLabel" name="label_threads"> </property>
<property name="text">
<string>Threads: </string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSlider" name="threadSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_8"> <property name="text">
<property name="orientation"> <string>Potato</string>
<enum>Qt::Horizontal</enum> </property>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item> </item>
</layout> </widget>
</item> </item>
<item> <item row="1" column="0">
<widget class="QFrame" name="pathFrame"> <widget class="QLabel" name="label_8">
<property name="frameShape"> <property name="text">
<enum>QFrame::NoFrame</enum> <string>Executable</string>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget> </widget>
</item> </item>
<item> <item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QCheckBox" name="check_tls"> <widget class="QLineEdit" name="lineEdit_path">
<property name="text"> <property name="placeholderText">
<string>TLS</string> <string>Path to wownerod...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="relayTor"> <widget class="QPushButton" name="btn_browse">
<property name="text"> <property name="text">
<string>Tor</string> <string>Browse</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_threads">
<property name="text">
<string>CPU threads</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item> <item>
<widget class="QCheckBox" name="check_solo"> <widget class="QSlider" name="threadSlider">
<property name="text"> <property name="orientation">
<string>Solo mine</string> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
@ -231,209 +257,32 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QFrame" name="poolFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Pool</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combo_pools">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="soloFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Node address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_solo">
<property name="text">
<string>127.0.0.1:18081</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Receiving address</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_address"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Password (optional)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_password"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>XMRig executable</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="lineEdit_path">
<property name="placeholderText">
<string>/path/to/xmrig</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_browse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_4"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>24</width> <width>40</width>
<height>20</height> <height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="lbl_logo"> <widget class="QLabel" name="lbl_logo">
<property name="text"> <property name="text">
<string>logoimg</string> <string>img</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
@ -449,30 +298,70 @@
</item> </item>
</layout> </layout>
</item> </item>
<item> </layout>
<spacer name="verticalSpacer_3"> </widget>
<property name="orientation"> <widget class="QWidget" name="tab_4">
<enum>Qt::Vertical</enum> <attribute name="title">
<string>wownerod</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="label_latest_version_wownerod">
<property name="text">
<string>Latest version:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_version_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>(right-click to download)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QTableView" name="tableWownerod">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="editTriggers">
<size> <set>QAbstractItemView::NoEditTriggers</set>
<width>20</width>
<height>0</height>
</size>
</property> </property>
</spacer> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_2"> <widget class="QWidget" name="tab_2">
<attribute name="title"> <attribute name="title">
<string>Downloads</string> <string>XMRig</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item> <item>
<widget class="QLabel" name="label_latest_version"> <widget class="QLabel" name="label_latest_version_rig">
<property name="text"> <property name="text">
<string>Latest version:</string> <string>Latest version:</string>
</property> </property>
@ -494,7 +383,7 @@
</layout> </layout>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QTableView" name="tableView"> <widget class="QTableView" name="tableRig">
<property name="contextMenuPolicy"> <property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum> <enum>Qt::CustomContextMenu</enum>
</property> </property>

Loading…
Cancel
Save