Compare commits

...
Author SHA1 Message Date
dsc 6b2f8f847e Introduce the QML mining interface 2022-03-22 16:01:33 +02:00
dsc 917f8b5812 Make sure wownerod is stopped on application quit 2022-03-18 10:45:35 +02:00
dsc a62fb95fbf Fix Windows build 2022-03-18 10:45:35 +02:00
dsc c3b0d00a72 Move mining tab a few positions 2022-03-18 10:45:35 +02:00
dsc d6dfd678b8 Brings back the mining tab and adds support for solo mining.
- Automatically sets the receiving address and spendkey to the current wallet
- The ability to specify CPU threads
- The ability to download the latest wownerod release from within wowlet (via: https://git.wownero.com/wowlet/wowlet-backend/pulls/18)
- Explanation on how to mine via the 'How-To' tab
- Console has a buffer of 2000 lines (auto-clears)
2022-03-18 10:45:35 +02:00
dsc 8b215c1e73 Merge pull request 'hide on close' (#90) from hide-on-close into master
Reviewed-on: https://git.wownero.com/wowlet/wowlet/pulls/90
2022-03-18 08:40:29 +00:00
dsc 96295a52de Default disabled 2022-03-17 11:02:16 +02:00
dsc ccd0e8e64b hide on close 2022-03-16 20:32:00 +02:00
dscandSander 3b3ec89306 truncate the yellowpages suffix for contacts 2022-03-15 12:19:01 +02:00
dsc 373fe8e02a Merge pull request 'YellWOWpages integration' (#88) from yellowpages into master
Reviewed-on: https://git.wownero.com/wowlet/wowlet/pulls/88
2022-03-14 19:45:09 +00:00
dscandSander 14d9793193 Automatically populate the contacts widget via [YellWOWPages](yellow.wownero.com/). One may still add custom contacts - they will get saved normally like before.
Requires https://git.wownero.com/wowlet/wowlet-backend/pulls/17

![https://i.imgur.com/EYa1BBf.png](https://i.imgur.com/EYa1BBf.png)

Adds some new CLI arguments

- `backend-host`
- `backend-port`
- `backend-tls`

They refer to wowlet-backend
2022-03-14 19:11:27 +02:00
54 changed files with 1620 additions and 663 deletions
-5
View File
@@ -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 "")
+16 -39
View File
@@ -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}")
+34 -11
View File
@@ -100,8 +100,21 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged); connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes); connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
// Tor & socks proxy // init backend URLs
this->ws = new WSClient(this, wsUrl); if(cmdargs->isSet("backend-host"))
this->backendHost = cmdargs->value("backend-host");
if(cmdargs->isSet("backend-host"))
this->backendPort = cmdargs->value("backend-port").toUInt();
if(cmdargs->isSet("backend-tls"))
this->backendTLS = true;
backendWSUrl = this->backendTLS ? "wss://" : "ws://";
backendWSUrl += QString("%1:%2").arg(this->backendHost).arg(this->backendPort);
backendHTTPUrl = this->backendTLS ? "https://" : "http://";
backendHTTPUrl += QString("%1:%2").arg(this->backendHost).arg(this->backendPort);
// init websocket client
this->ws = new WSClient(this);
connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage); connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
connect(this->ws, &WSClient::connectionEstablished, this, &AppContext::wsConnected); connect(this->ws, &WSClient::connectionEstablished, this, &AppContext::wsConnected);
connect(this->ws, &WSClient::closed, this, &AppContext::wsDisconnected); connect(this->ws, &WSClient::closed, this, &AppContext::wsDisconnected);
@@ -150,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);
@@ -171,17 +182,25 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// libwallet connects // libwallet connects
connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened); connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened);
// hideOnClose
auto hideOnClose = config()->get(Config::hideOnClose).toBool();
if(hideOnClose)
QApplication::setQuitOnLastWindowClosed(false);
} }
void AppContext::initTor() { void AppContext::initTor() {
this->tor = new Tor(this, this); this->tor = new Tor(this, this);
this->tor->start(); this->tor->start();
if (!isWhonix && wsUrl.contains(".onion")) { if (!isWhonix && !backendHost.contains(".onion")) {
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); qDebug() << "'backend-host' did not contain '.onion' - running without Tor proxy.";
this->network->setProxy(*networkProxy); return;
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() {
@@ -421,15 +440,19 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
if(changed) if(changed)
emit blockHeightWSUpdated(this->heights); emit blockHeightWSUpdated(this->heights);
} }
else if(cmd == "yellwow") {
this->yellowPagesData = msg.value("data").toArray();
emit yellowUpdated();
}
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);
+10 -1
View File
@@ -66,7 +66,13 @@ public:
QString defaultWalletDir; QString defaultWalletDir;
QString defaultWalletDirRoot; QString defaultWalletDirRoot;
QString tmpTxDescription; QString tmpTxDescription;
QString wsUrl = "6wku2m4zrv6j666crlo7lzofv6ud6enzllyhou3ijeigpukymi37caad.onion";
// https://git.wownero.com/wowlet/wowlet-backend/
QString backendHost = "6wku2m4zrv6j666crlo7lzofv6ud6enzllyhou3ijeigpukymi37caad.onion";
unsigned int backendPort = 80;
bool backendTLS = false;
QString backendWSUrl;
QString backendHTTPUrl;
QString walletPath; QString walletPath;
QString walletPassword = ""; QString walletPassword = "";
@@ -106,6 +112,7 @@ public:
static QMap<QString, QString> txDescriptionCache; static QMap<QString, QString> txDescriptionCache;
static QMap<QString, QString> txCache; static QMap<QString, QString> txCache;
static TxFiatHistory *txFiatHistory; static TxFiatHistory *txFiatHistory;
QJsonArray yellowPagesData;
QJsonObject versionPending; QJsonObject versionPending;
// libwalletqt // libwalletqt
@@ -205,8 +212,10 @@ signals:
void nodesUpdated(QList<QSharedPointer<WowletNode>> &nodes); void nodesUpdated(QList<QSharedPointer<WowletNode>> &nodes);
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries); void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
void suchWowUpdated(const QJsonArray &such_data); void suchWowUpdated(const QJsonArray &such_data);
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);
+29
View File
@@ -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.
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 148 KiB

+74 -7
View File
@@ -32,17 +32,22 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
this->newContact(); this->newContact();
}); });
// row context menu
m_rowMenu = new QMenu(ui->contacts);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){ connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
QModelIndex index = ui->contacts->indexAt(point); QModelIndex index = ui->contacts->indexAt(point);
if (index.isValid()) { if (index.isValid()) {
auto username = index.model()->data(index.siblingAtColumn(AddressBookModel::Description), Qt::UserRole).toString();
m_rowMenu = new QMenu(ui->contacts);
if(username.contains("(yp)"))
m_rowMenu->addAction(QIcon(":/assets/images/network.png"), "Visit user's YellWOWPage", this, &ContactsWidget::visitYellowPage);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
m_rowMenu->exec(ui->contacts->viewport()->mapToGlobal(point)); m_rowMenu->exec(ui->contacts->viewport()->mapToGlobal(point));
m_rowMenu->deleteLater();
} }
else { else {
m_contextMenu->exec(ui->contacts->viewport()->mapToGlobal(point)); m_contextMenu->exec(ui->contacts->viewport()->mapToGlobal(point));
@@ -52,6 +57,68 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
connect(ui->search, &QLineEdit::textChanged, this, &ContactsWidget::setSearchFilter); connect(ui->search, &QLineEdit::textChanged, this, &ContactsWidget::setSearchFilter);
} }
QMap<QString, QString> ContactsWidget::data() {
auto rtn = QMap<QString, QString>();
for (int i = 0; i < m_ctx->currentWallet->addressBook()->count(); i++) {
m_ctx->currentWallet->addressBook()->getRow(i, [&rtn](const AddressBookInfo &entry) {
rtn[entry.description()] = entry.address();
});
}
return rtn;
}
unsigned int ContactsWidget::rowIndex(const QString &name) {
// name -> row index lookup
int result = -1;
for (int i = 0; i < m_ctx->currentWallet->addressBook()->count(); i++) {
m_ctx->currentWallet->addressBook()->getRow(i, [i, name, &result](const AddressBookInfo &entry) {
if(entry.description() == name) result = i;
return;
});
if(result != -1)
return result;
}
return result;
}
void ContactsWidget::loadYellowPages() {
if (m_ctx->currentWallet == nullptr || m_ctx->yellowPagesData.empty())
return;
auto contacts = this->data();
for (auto item: m_ctx->yellowPagesData) {
auto obj = item.toObject();
const auto username = QString("%1 (yp)").arg(obj.value("username").toString());
const auto address = obj.value("address").toString();
if(contacts.contains(username)) {
if(contacts[username] == address) continue;
// update the address
auto idx = this->rowIndex(username);
if(idx == -1) continue;
m_model->deleteRow((int)idx);
}
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
if (!addressValid) {
continue;
}
m_ctx->currentWallet->addressBook()->addRow(address, "", username);
}
}
void ContactsWidget::visitYellowPage() {
auto index = ui->contacts->currentIndex();
auto username = index.model()->data(
index.siblingAtColumn(AddressBookModel::Description),
Qt::UserRole).toString();
username = username.replace(" (yp)", "").trimmed();
Utils::externalLinkWarning(this, QString("https://yellow.wownero.com/user/%1").arg(username));
}
void ContactsWidget::copyAddress() { void ContactsWidget::copyAddress() {
QModelIndex index = ui->contacts->currentIndex(); QModelIndex index = ui->contacts->currentIndex();
ModelUtils::copyColumn(&index, AddressBookModel::Address); ModelUtils::copyColumn(&index, AddressBookModel::Address);
+5
View File
@@ -22,6 +22,7 @@ class ContactsWidget : public QWidget
public: public:
explicit ContactsWidget(QWidget *parent = nullptr); explicit ContactsWidget(QWidget *parent = nullptr);
void setModel(AddressBookModel * model); void setModel(AddressBookModel * model);
QMap<QString, QString> data();
~ContactsWidget() override; ~ContactsWidget() override;
public slots: public slots:
@@ -30,9 +31,11 @@ public slots:
void payTo(); void payTo();
void newContact(QString address = "", QString name = ""); void newContact(QString address = "", QString name = "");
void deleteContact(); void deleteContact();
void visitYellowPage();
void setShowFullAddresses(bool show); void setShowFullAddresses(bool show);
void setSearchFilter(const QString &filter); void setSearchFilter(const QString &filter);
void resetModel(); void resetModel();
void loadYellowPages();
signals: signals:
void fillAddress(QString &address); void fillAddress(QString &address);
@@ -50,6 +53,8 @@ private:
QMenu *m_headerMenu; QMenu *m_headerMenu;
AddressBookModel * m_model; AddressBookModel * m_model;
AddressBookProxyModel * m_proxyModel; AddressBookProxyModel * m_proxyModel;
unsigned int rowIndex(const QString &name);
}; };
#endif // CONTACTSWIDGET_H #endif // CONTACTSWIDGET_H
+2
View File
@@ -50,6 +50,7 @@ void DebugInfoDialog::updateInfo() {
ui->label_synchronized->setText(m_ctx->currentWallet->synchronized() ? "True" : "False"); ui->label_synchronized->setText(m_ctx->currentWallet->synchronized() ? "True" : "False");
auto node = m_ctx->nodes->connection(); auto node = m_ctx->nodes->connection();
ui->label_websocketURL->setText(m_ctx->backendWSUrl);
ui->label_remoteNode->setText(node.full); ui->label_remoteNode->setText(node.full);
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus())); ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
ui->label_torStatus->setText(torStatus); ui->label_torStatus->setText(torStatus);
@@ -101,6 +102,7 @@ void DebugInfoDialog::copyToClipboad() {
text += QString("Remote node: %1 \n").arg(ui->label_remoteNode->text()); text += QString("Remote node: %1 \n").arg(ui->label_remoteNode->text());
text += QString("Wallet status: %1 \n").arg(ui->label_walletStatus->text()); text += QString("Wallet status: %1 \n").arg(ui->label_walletStatus->text());
text += QString("Tor status: %1 \n").arg(ui->label_torStatus->text()); text += QString("Tor status: %1 \n").arg(ui->label_torStatus->text());
text += QString("Websocket URL: %1 \n").arg(ui->label_websocketURL->text());
text += QString("Websocket status: %1 \n").arg(ui->label_websocketStatus->text()); text += QString("Websocket status: %1 \n").arg(ui->label_websocketStatus->text());
text += QString("Network type: %1 \n").arg(ui->label_netType->text()); text += QString("Network type: %1 \n").arg(ui->label_netType->text());
+30 -16
View File
@@ -183,14 +183,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0"> <item row="13" column="0">
<widget class="QLabel" name="label_19"> <widget class="QLabel" name="label_19">
<property name="text"> <property name="text">
<string>Websocket status:</string> <string>Websocket status:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="13" column="1">
<widget class="QLabel" name="label_websocketStatus"> <widget class="QLabel" name="label_websocketStatus">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -200,21 +200,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="14" column="1">
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="0"> <item row="15" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Network type:</string> <string>Network type:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="15" column="1">
<widget class="QLabel" name="label_netType"> <widget class="QLabel" name="label_netType">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -224,14 +224,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="0"> <item row="16" column="0">
<widget class="QLabel" name="label_23"> <widget class="QLabel" name="label_23">
<property name="text"> <property name="text">
<string>Seed type:</string> <string>Seed type:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="1"> <item row="16" column="1">
<widget class="QLabel" name="label_seedType"> <widget class="QLabel" name="label_seedType">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -241,14 +241,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="0"> <item row="17" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>View only:</string> <string>View only:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="17" column="1">
<widget class="QLabel" name="label_viewOnly"> <widget class="QLabel" name="label_viewOnly">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -258,14 +258,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="20" column="0"> <item row="21" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Timestamp:</string> <string>Timestamp:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="20" column="1"> <item row="21" column="1">
<widget class="QLabel" name="label_timestamp"> <widget class="QLabel" name="label_timestamp">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -275,14 +275,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="0"> <item row="20" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Operating system:</string> <string>Operating system:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="1"> <item row="20" column="1">
<widget class="QLabel" name="label_OS"> <widget class="QLabel" name="label_OS">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@@ -292,7 +292,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="1"> <item row="19" column="1">
<widget class="Line" name="line_4"> <widget class="Line" name="line_4">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@@ -316,20 +316,34 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="1"> <item row="18" column="1">
<widget class="QLabel" name="label_primaryOnly"> <widget class="QLabel" name="label_primaryOnly">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="0"> <item row="18" column="0">
<widget class="QLabel" name="label_11"> <widget class="QLabel" name="label_11">
<property name="text"> <property name="text">
<string>Primary only:</string> <string>Primary only:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Websocket URL:</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="label_websocketURL">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
+17
View File
@@ -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
+10 -8
View File
@@ -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);
@@ -107,6 +108,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
QCommandLineOption androidDebugOption(QStringList() << "android-debug", "Start the Android interface without actually running on Android - for debugging purposes. Requires -DANDROID_DEBUG=ON CMake definition."); QCommandLineOption androidDebugOption(QStringList() << "android-debug", "Start the Android interface without actually running on Android - for debugging purposes. Requires -DANDROID_DEBUG=ON CMake definition.");
parser.addOption(androidDebugOption); parser.addOption(androidDebugOption);
QCommandLineOption backendHostOption(QStringList() << "backend-host", "specify your own `wowlet-backend` host", "backend-host");
parser.addOption(backendHostOption);
QCommandLineOption backendPortOption(QStringList() << "backend-port", "specify your own `wowlet-backend` port", "backend-port");
parser.addOption(backendPortOption);
QCommandLineOption backendTLS(QStringList() << "backend-tls", "`wowlet-backend` is running via TLS? 'wss://' and 'https://' will be used.", "backend-tls");
parser.addOption(backendTLS);
auto parsed = parser.parse(argv_); auto parsed = parser.parse(argv_);
if(!parsed) { if(!parsed) {
qCritical() << parser.errorText(); qCritical() << parser.errorText();
@@ -131,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);
+50 -26
View File
@@ -108,11 +108,14 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png")); m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png"));
m_trayIcon->show(); m_trayIcon->show();
m_trayActionHome = new QAction("Show", this);
m_trayActionHome->setStatusTip("Show");
m_trayActionCalc = new QAction("Calc", this); m_trayActionCalc = new QAction("Calc", this);
m_trayActionCalc->setStatusTip("Calculator"); m_trayActionCalc->setStatusTip("Calculator");
m_trayActionSend = new QAction("Send", this); m_trayActionSend = new QAction("Send", this);
m_trayActionSend->setStatusTip("Send WOW payment"); m_trayActionSend->setStatusTip("Send a WOW payment");
m_trayActionHistory = new QAction("History", this); m_trayActionHistory = new QAction("History", this);
m_trayActionHistory->setStatusTip("View incoming transfers"); m_trayActionHistory->setStatusTip("View incoming transfers");
@@ -120,6 +123,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
m_trayActionExit = new QAction("Quit", this); m_trayActionExit = new QAction("Quit", this);
m_trayActionExit->setStatusTip("Exit application"); m_trayActionExit->setStatusTip("Exit application");
m_trayMenu.addAction(m_trayActionHome);
m_trayMenu.addAction(m_trayActionSend); m_trayMenu.addAction(m_trayActionSend);
m_trayMenu.addAction(m_trayActionHistory); m_trayMenu.addAction(m_trayActionHistory);
m_trayMenu.addAction(m_trayActionCalc); m_trayMenu.addAction(m_trayActionCalc);
@@ -127,6 +131,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
m_trayIcon->setContextMenu(&m_trayMenu); m_trayIcon->setContextMenu(&m_trayMenu);
// @TODO: only init tray *after* boot // @TODO: only init tray *after* boot
connect(m_trayActionHome, &QAction::triggered, this, &MainWindow::showHomeWindow);
connect(m_trayActionCalc, &QAction::triggered, this, &MainWindow::showCalcWindow); connect(m_trayActionCalc, &QAction::triggered, this, &MainWindow::showCalcWindow);
connect(m_trayActionSend, &QAction::triggered, this, &MainWindow::showSendTab); connect(m_trayActionSend, &QAction::triggered, this, &MainWindow::showSendTab);
connect(m_trayActionHistory, &QAction::triggered, this, &MainWindow::showHistoryTab); connect(m_trayActionHistory, &QAction::triggered, this, &MainWindow::showHistoryTab);
@@ -166,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);
@@ -192,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, [=]{
@@ -292,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
@@ -303,6 +312,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
// Contacts // Contacts
connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress); connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress);
connect(m_ctx, &AppContext::yellowUpdated, ui->contactWidget, &ContactsWidget::loadYellowPages);
// Open alias // Open alias
connect(ui->sendWidget, &SendWidget::resolveOpenAlias, m_ctx, &AppContext::onOpenAliasResolve); connect(ui->sendWidget, &SendWidget::resolveOpenAlias, m_ctx, &AppContext::onOpenAliasResolve);
@@ -328,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();
@@ -415,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);
@@ -520,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){
@@ -632,6 +638,7 @@ void MainWindow::onWalletOpened(Wallet *wallet) {
// contacts widget // contacts widget
ui->contactWidget->setModel(m_ctx->currentWallet->addressBookModel()); ui->contactWidget->setModel(m_ctx->currentWallet->addressBookModel());
ui->contactWidget->loadYellowPages();
// coins page // coins page
m_ctx->currentWallet->coins()->refresh(m_ctx->currentWallet->currentSubaddressAccount()); m_ctx->currentWallet->coins()->refresh(m_ctx->currentWallet->currentSubaddressAccount());
@@ -966,7 +973,6 @@ void MainWindow::menuNewRestoreClicked() {
void MainWindow::menuQuitClicked() { void MainWindow::menuQuitClicked() {
cleanupBeforeClose(); cleanupBeforeClose();
QCoreApplication::quit(); QCoreApplication::quit();
} }
@@ -1036,9 +1042,16 @@ void MainWindow::skinChanged(const QString &skinName) {
} }
void MainWindow::closeEvent(QCloseEvent *event) { void MainWindow::closeEvent(QCloseEvent *event) {
cleanupBeforeClose(); auto hideOnClose = config()->get(Config::hideOnClose).toBool();
if(hideOnClose && !this->isHidden()) {
this->hide();
event->ignore();
return;
}
cleanupBeforeClose();
QWidget::closeEvent(event); QWidget::closeEvent(event);
QApplication::exit();
} }
void MainWindow::donateButtonClicked() { void MainWindow::donateButtonClicked() {
@@ -1047,25 +1060,35 @@ 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();
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); this->show();
ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
} }
void MainWindow::showSendTab() { void MainWindow::showSendTab() {
this->raise(); this->raise();
ui->tabWidget->setCurrentIndex(Tabs::SEND); this->show();
ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
}
void MainWindow::showHomeWindow() {
this->raise();
this->show();
ui->tabWidget->setCurrentIndex(globals::Tabs::HOME);
} }
void MainWindow::showCalcWindow() { void MainWindow::showCalcWindow() {
this->raise();
this->show();
m_windowCalc->show(); m_windowCalc->show();
} }
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"
@@ -1075,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) {
@@ -1083,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) {
@@ -1260,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();
} }
+3 -17
View File
@@ -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();
@@ -109,6 +93,7 @@ public slots:
void showViewOnlyDialog(); void showViewOnlyDialog();
void donateButtonClicked(); void donateButtonClicked();
void showCalcWindow(); void showCalcWindow();
void showHomeWindow();
void payToMany(); void payToMany();
void showWalletCacheDebugDialog(); void showWalletCacheDebugDialog();
void showSendTab(); void showSendTab();
@@ -200,6 +185,7 @@ private:
QSystemTrayIcon *m_trayIcon; QSystemTrayIcon *m_trayIcon;
QMenu m_trayMenu; QMenu m_trayMenu;
QAction *m_trayActionHome;
QAction *m_trayActionCalc; QAction *m_trayActionCalc;
QAction *m_trayActionExit; QAction *m_trayActionExit;
QAction *m_trayActionSend; QAction *m_trayActionSend;
+14 -14
View File
@@ -316,20 +316,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabCalc">
<attribute name="icon">
<iconset resource="assets.qrc">
<normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset>
</attribute>
<attribute name="title">
<string>Calc</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="CalcWidget" name="conversionWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabXmrRig"> <widget class="QWidget" name="tabXmrRig">
<attribute name="icon"> <attribute name="icon">
<iconset resource="assets.qrc"> <iconset resource="assets.qrc">
@@ -344,6 +330,20 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabCalc">
<attribute name="icon">
<iconset resource="assets.qrc">
<normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset>
</attribute>
<attribute name="title">
<string>Calc</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="CalcWidget" name="conversionWidget" native="true"/>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
+6
View File
@@ -30,6 +30,11 @@ Settings::Settings(QWidget *parent) :
m_ctx->updateBalance(); m_ctx->updateBalance();
}); });
connect(ui->checkBox_hideOnClose, &QCheckBox::toggled, [this](bool toggled){
config()->set(Config::hideOnClose, toggled);
QApplication::setQuitOnLastWindowClosed(toggled);
});
connect(ui->closeButton, &QDialogButtonBox::accepted, this, &Settings::close); connect(ui->closeButton, &QDialogButtonBox::accepted, this, &Settings::close);
// nodes // nodes
@@ -40,6 +45,7 @@ Settings::Settings(QWidget *parent) :
// setup checkboxes // setup checkboxes
ui->checkBox_externalLink->setChecked(config()->get(Config::warnOnExternalLink).toBool()); ui->checkBox_externalLink->setChecked(config()->get(Config::warnOnExternalLink).toBool());
ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool()); ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool());
ui->checkBox_hideOnClose->setChecked(config()->get(Config::hideOnClose).toBool());
// setup comboboxes // setup comboboxes
this->setupSkinCombobox(); this->setupSkinCombobox();
+7
View File
@@ -198,6 +198,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkBox_hideOnClose">
<property name="text">
<string>Hide application on close</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_node"> <widget class="QWidget" name="tab_node">
+678
View File
@@ -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)];
}
}
}
+4 -2
View File
@@ -33,19 +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::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}},
+3 -1
View File
@@ -35,7 +35,7 @@ public:
walletDirectory, walletDirectory,
walletPath, walletPath,
xmrigPath, xmrigPath,
xmrigPool, wownerodPath,
nodes, nodes,
websocketEnabled, websocketEnabled,
nodeSource, nodeSource,
@@ -49,6 +49,8 @@ public:
firstRun, firstRun,
hideBalance, hideBalance,
hideFiatBalance, hideFiatBalance,
hideOnClose,
simplifiedMiningInterface,
redditFrontend, redditFrontend,
showHistorySyncNotice, showHistorySyncNotice,
ignoreUpdateWarning, ignoreUpdateWarning,
+5 -5
View File
@@ -8,18 +8,18 @@
#include "wsclient.h" #include "wsclient.h"
#include "appcontext.h" #include "appcontext.h"
WSClient::WSClient(AppContext *ctx, const QString &url, QObject *parent) : WSClient::WSClient(AppContext *ctx, QObject *parent) :
QObject(parent), QObject(parent),
m_ctx(ctx) { m_ctx(ctx) {
// this class connects to `https://git.wownero.com/wowlet/wowlet-backend/`
connect(&this->webSocket, &QWebSocket::binaryMessageReceived, this, &WSClient::onbinaryMessageReceived); connect(&this->webSocket, &QWebSocket::binaryMessageReceived, this, &WSClient::onbinaryMessageReceived);
connect(&this->webSocket, &QWebSocket::connected, this, &WSClient::onConnected); connect(&this->webSocket, &QWebSocket::connected, this, &WSClient::onConnected);
connect(&this->webSocket, &QWebSocket::disconnected, this, &WSClient::closed); connect(&this->webSocket, &QWebSocket::disconnected, this, &WSClient::closed);
connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError); connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
m_tor = url.contains(".onion"); m_tor = m_ctx->backendHost.contains(".onion");
this->url = QString("ws://%1/ws").arg(url);
// Keep websocket connection alive // keep-alive
connect(&m_pingTimer, &QTimer::timeout, [this]{ connect(&m_pingTimer, &QTimer::timeout, [this]{
if (this->webSocket.state() == QAbstractSocket::ConnectedState) if (this->webSocket.state() == QAbstractSocket::ConnectedState)
this->webSocket.ping(); this->webSocket.ping();
@@ -40,7 +40,7 @@ void WSClient::start() {
qDebug() << "WebSocket connect:" << url.url(); qDebug() << "WebSocket connect:" << url.url();
#endif #endif
if((m_tor && this->m_ctx->tor->torConnected) || !m_tor) if((m_tor && this->m_ctx->tor->torConnected) || !m_tor)
this->webSocket.open(QUrl(this->url)); this->webSocket.open(QString("%1/ws").arg(m_ctx->backendWSUrl));
if(!this->m_connectionTimer.isActive()) { if(!this->m_connectionTimer.isActive()) {
connect(&this->m_connectionTimer, &QTimer::timeout, this, &WSClient::checkConnection); connect(&this->m_connectionTimer, &QTimer::timeout, this, &WSClient::checkConnection);
+1 -2
View File
@@ -14,11 +14,10 @@ class WSClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit WSClient(AppContext *ctx, const QString &url, QObject *parent = nullptr); explicit WSClient(AppContext *ctx, QObject *parent = nullptr);
void start(); void start();
void sendMsg(const QByteArray &data); void sendMsg(const QByteArray &data);
QWebSocket webSocket; QWebSocket webSocket;
QString url;
signals: signals:
void closed(); void closed();
+131 -53
View File
@@ -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");
void XmRig::handleProcessOutput() { changeDaemonState(DaemonMiningState::idle);
QByteArray _output = m_process.readAllStandardOutput();
if(_output.contains("miner") && _output.contains("speed")) {
// detect hashrate
auto str = Utils::barrayToString(_output);
auto spl = str.mid(str.indexOf("speed")).split(" ");
auto rate = spl.at(2) + "H/s";
qDebug() << "mining hashrate: " << rate;
emit hashrate(rate);
} }
emit output(_output);
} }
void XmRig::handleProcessError(QProcess::ProcessError err) { void XmRig::onHandleProcessOutput() {
QByteArray data = m_process.readAllStandardOutput();
for(auto &line: data.split('\n')) {
// remove timestamp
if(line.indexOf("\tI") >= 20)
line = line.remove(0, line.indexOf("\tI") + 2);
line = line.trimmed();
// 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());
}
}
void XmRig::changeDaemonState(const DaemonMiningState state) {
if(daemonMiningState == state) return;
daemonMiningState = state;
emit daemonStateChanged(daemonMiningState);
}
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);
} }
+23 -6
View File
@@ -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
+1 -1
View File
@@ -19,7 +19,7 @@ SuchWowPost::SuchWowPost(AppContext *ctx, QObject *parent) :
m_ctx(ctx) { m_ctx(ctx) {
m_networkImg = new UtilsNetworking(m_ctx->network, this); m_networkImg = new UtilsNetworking(m_ctx->network, this);
m_networkThumb = new UtilsNetworking(m_ctx->network, this); m_networkThumb = new UtilsNetworking(m_ctx->network, this);
m_weburl = QString("http://%1/suchwow").arg(this->m_ctx->wsUrl); m_weburl = QString("%1/suchwow").arg(this->m_ctx->backendHTTPUrl);
} }
void SuchWowPost::onThumbReceived(QByteArray data) { void SuchWowPost::onThumbReceived(QByteArray data) {
+247 -156
View File
@@ -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->check_autoscroll->setChecked(true); ui->comboBox_gfx->setCurrentIndex(simplifiedUI ? 1 : 0);
ui->relayTor->setChecked(false); connect(ui->comboBox_gfx, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onSimplifiedMiningChanged);
ui->check_tls->setChecked(true);
ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse);
ui->label_status->hide();
ui->soloFrame->hide();
ui->poolFrame->hide();
// XMRig binary // wownerod binary
auto path = config()->get(Config::xmrigPath).toString(); auto path = config()->get(Config::wownerodPath).toString();
if(!path.isEmpty()) { if(!path.isEmpty())
ui->lineEdit_path->setText(path); ui->lineEdit_path->setText(path);
}
// pools connect(ui->lineEdit_path, &QLineEdit::textChanged, [=] {
ui->poolFrame->show(); config()->set(Config::wownerodPath, ui->lineEdit_path->text().trimmed());
ui->combo_pools->insertItems(0, m_pools); });
auto preferredPool = config()->get(Config::xmrigPool).toString();
if (m_pools.contains(preferredPool))
ui->combo_pools->setCurrentIndex(m_pools.indexOf(preferredPool));
else {
preferredPool = m_pools.at(0);
config()->set(Config::xmrigPool, preferredPool);
}
connect(ui->combo_pools, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onPoolChanged);
// info // info
ui->console->appendPlainText(QString("Detected %1 CPU threads.").arg(threads)); this->appendText(QString("Detected %1 CPU threads.").arg(threads));
if(!path.isEmpty() && !Utils::fileExists(path)) if(path.isEmpty())
ui->console->appendPlainText("Invalid path to XMRig binary detected. Please reconfigure on the Settings tab."); this->appendText(QString("wownerod path is empty - please point towards the wownerod executable.").arg(path));
else else if(!Utils::fileExists(path))
ui->console->appendPlainText(QString("XMRig path set to %1").arg(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.");
}
}
ui->console->appendPlainText("Ready to mine."); void XMRigWidget::resetUI() {
ui->consoleFrame->hide();
ui->qmlFrame->hide();
ui->qmlFrameTxt->hide();
// username/password ui->check_autoscroll->setChecked(true);
connect(ui->lineEdit_password, &QLineEdit::editingFinished, [=]() { ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse);
m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_password", ui->lineEdit_password->text()); ui->label_status->hide();
m_ctx->storeWallet(); ui->console->clear();
});
connect(ui->lineEdit_address, &QLineEdit::editingFinished, [=]() {
m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_username", ui->lineEdit_address->text());
m_ctx->storeWallet();
});
// checkbox connects this->destroyQml();
connect(ui->check_solo, &QCheckBox::stateChanged, this, &XMRigWidget::onSoloChecked); }
void XMRigWidget::startUI() {
this->resetUI();
bool simplifiedUI = config()->get(Config::simplifiedMiningInterface).toBool();
if(simplifiedUI) {
this->initConsole();
} else {
this->initQML();
}
}
void XMRigWidget::initConsole() {
ui->consoleFrame->show();
}
void XMRigWidget::initQML() {
if(m_quickWidget != nullptr) return;
m_quickWidget = new QQuickWidget(this);
auto *qctx = m_quickWidget->rootContext();
qctx->setContextProperty("cfg", config());
qctx->setContextProperty("ctx", m_ctx);
qctx->setContextProperty("mining", this);
m_quickWidget->setSource(QUrl("qrc:/mining.qml"));
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::onSyncStatus(unsigned int from, unsigned int to, unsigned int pct) {
emit syncStatus(from, to, pct);
}
void XMRigWidget::onDaemonStateChanged(DaemonMiningState state) {
if(state == DaemonMiningState::idle) {
ui->btn_stop->setEnabled(false);
ui->btn_start->setEnabled(true);
ui->label_status->hide();
} else {
ui->btn_stop->setEnabled(true);
ui->btn_start->setEnabled(false);
ui->label_status->show();
}
m_daemonMiningState = state;
emit daemonMiningStateChanged();
}
void XMRigWidget::onUptimeChanged(const QString &uptime) {
emit uptimeChanged(uptime);
}
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() { void XMRigWidget::onClearClicked() {
ui->console->clear(); ui->console->clear();
} }
void XMRigWidget::onStartClicked() { void XMRigWidget::onStartClicked() {
QString xmrigPath; auto binPath = config()->get(Config::wownerodPath).toString();
bool solo = ui->check_solo->isChecked(); if(!m_ctx->XMRig->start(binPath, m_threads)) return;
xmrigPath = config()->get(Config::xmrigPath).toString();
// username is receiving address usually
auto username = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_username");
auto password = m_ctx->currentWallet->getCacheAttribute("wowlet.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);
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(".")) {
// cryptonote social requires <addr>.<username>, we'll just grab a few chars from primary addy
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());
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) {
config()->set(Config::simplifiedMiningInterface, idx == 1);
this->startUI();
}
void XMRigWidget::onHashrate(const QString &rate) {
ui->label_status->show(); ui->label_status->show();
ui->label_status->setText(QString("Mining at %1").arg(hashrate)); ui->label_status->setText(QString("Mining at %1").arg(rate));
emit hashrate(rate);
} }
void XMRigWidget::onDownloads(const QJsonObject &data) { void XMRigWidget::onWownerodDownloads(const QJsonObject &data) {
// For the downloads table we'll manually update the table m_modelWownerod->clear();
// with items once, as opposed to creating a class in m_urlsWownerod.clear();
// src/models/. Saves effort; full-blown model
// is unnecessary in this case.
m_model->clear();
m_urls.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();
else if(globals::Tabs(index + 1) == globals::Tabs::XMRIG)
this->startUI();
m_tabIndex = index + 1;
}
void XMRigWidget::onRigDownloads(const QJsonObject &data) {
m_modelRig->clear();
m_urlsRig.clear();
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);
}
int i = 0;
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; return;
} m_contextMenuRig->exec(ui->tableRig->viewport()->mapToGlobal(pos));
m_contextMenu->exec(ui->tableView->viewport()->mapToGlobal(pos));
} }
void XMRigWidget::onSoloChecked(int state) { void XMRigWidget::showContextWownerodMenu(const QPoint &pos) {
if(state == 2) { QModelIndex index = ui->tableWownerod->indexAt(pos);
ui->poolFrame->hide(); if (!index.isValid())
ui->soloFrame->show(); return;
ui->check_tls->setChecked(false); m_contextMenuRig->exec(ui->tableWownerod->viewport()->mapToGlobal(pos));
}
else {
ui->poolFrame->show();
ui->soloFrame->hide();
}
} }
void XMRigWidget::linkClicked() { void XMRigWidget::wownerodLinkClicked() {
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);
}
void XMRigWidget::rigLinkClicked() {
QModelIndex index = ui->tableRig->currentIndex();
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() {
+51 -13
View File
@@ -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
+169 -280
View File
@@ -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>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Executable</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit_path">
<property name="placeholderText">
<string>Path to wownerod...</string>
</property> </property>
<property name="sizeHint" stdset="0"> </widget>
<size> </item>
<width>0</width> <item>
<height>20</height> <widget class="QPushButton" name="btn_browse">
</size> <property name="text">
<string>Browse</string>
</property> </property>
</spacer> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item row="2" column="0">
<widget class="QFrame" name="pathFrame"> <widget class="QLabel" name="label_threads">
<property name="frameShape"> <property name="text">
<enum>QFrame::NoFrame</enum> <string>CPU threads</string>
</property> </property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</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="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<item> <item>
<widget class="QCheckBox" name="check_tls"> <widget class="QSlider" name="threadSlider">
<property name="text"> <property name="orientation">
<string>TLS</string> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="relayTor"> <spacer name="horizontalSpacer_3">
<property name="text">
<string>Tor</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="check_solo">
<property name="text">
<string>Solo mine</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<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>