Merge pull request #217

3752ec7 send page: check daemon status after every refresh (Jaquee)
01549a3 walletManager: coding conventions (Jaquee)
0ae3d67 daemon console -> log + adjusted height (Jaquee)
0e8cd14 Wallet: add m_initialized (Jaquee)
c7232e1 set wallet connection status before querying sync status (Jaquee)
3f8e05d Wallet: Cache connection status query (Jaquee)
36a6b89 connect onWalletConnectionStatusChanged() (Jaquee)
93a8200 Transfer: new connectionStatus handling + show status msg (Jaquee)
76e6ae8 remove reference to old pw-dialog (Jaquee)
18b7a67 Wallet: add connectionStatusChanged signal (Jaquee)
d9f4ab4 daemonManager: wait for daemon stop on app close (Jaquee)
f62bb68 daemonManagerDialog: use new ConnectionStatus enum (Jaquee)
dd01f59 hide daemon sync progress after disconnecting wallet (Jaquee)
8d19a03 DaemonManager: add stateChanged() (Jaquee)
760e01b daemonManagerDialog: show processdialog when starting (Jaquee)
bb881d9 show processingSplash when starting/stopping daemon (Jaquee)
8361dda resolve rebase conflict (Jaquee)
8dfa79e Shutdown daemon and close wallet properly on app exit (Jaquee)
7876957 DaemonManager::closing() (Jacob Brydolf)
065b060 main: debug messages (Jacob Brydolf)
b4eb489 DaemonManager: forward command line arguments to monerod (Jacob Brydolf)
752ff26 forward command line arguments to DaemonManager (Jaquee)
7840dab DaemonManager: console debug output (Jacob Brydolf)
14a5bd5 settings: added daemon console (Jacob Brydolf)
b53ef00 history tx details: resized popup (Jacob Brydolf)
b4f29b2 StandardDialog: changed default sizes (Jacob Brydolf)
5855700 daemonManagerDialog: added starting signal (Jacob Brydolf)
3a43154 DaemonManager: added console updated signals (Jacob Brydolf)
3df9e44 DaemonManager: windows support (Jacob Brydolf)
5da9f8f standardDialog: close window before continue (Jacob Brydolf)
5a110f4 replace message dialog with custom dialog (Jacob Brydolf)
d465780 resized standard dialog (Jacob Brydolf)
482bd30 settings: enable/disable daemon start/stop buttons (Jacob Brydolf)
4e7de8c proper daemon shutdown on app exit (Jaquee)
48471f3 onDaemonStarted/stopped signals/slots (Jaquee)
de635cb pw dialog: close popup before continue (Jacob Brydolf)
86772be added standardDialog component (Jacob Brydolf)
ae977af settings: add daemon manager (Jacob Brydolf)
2775124 small error in daemon manager dialog (Jacob Brydolf)
82c39e0 WalletManager: include dependencies (Jaquee)
1c6884e Show daemon manager dialog if daemon isnt started (Jacob Brydolf)
9fbfbc4 daemonManager: embed as context property (Jaquee)
4cdc258 WalletManager: basic functionality (Jacob Brydolf)
pull/2/head
Riccardo Spagni 8 years ago
commit bfcdf113f9
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD

@ -32,6 +32,7 @@ import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.0
import moneroComponents.Wallet 1.0
import "./pages"
@ -56,9 +57,6 @@ Rectangle {
signal generatePaymentIdInvoked()
signal checkPaymentClicked(string address, string txid, string txkey);
// Disable transfer page if daemon isnt fully synced
enabled: (currentView !== transferView || appWindow.daemonSynced)
color: "#F0EEEE"
onCurrentViewChanged: {
@ -72,6 +70,10 @@ Rectangle {
}
}
function updateStatus(){
transferView.updateStatus();
}
// XXX: just for memo, to be removed
// states: [
@ -292,14 +294,6 @@ Rectangle {
color: "#DBDBDB"
}
Rectangle {
id:desaturate
color:"black"
anchors.fill: parent
opacity: 0.1
visible: (root.enabled)? 0 : 1;
}
/* connect "payment" click */
Connections {

@ -0,0 +1,120 @@
// Copyright (c) 2014-2015, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.0
import "../components" as MoneroComponents
Window {
id: root
modality: Qt.ApplicationModal
flags: Qt.Window | Qt.FramelessWindowHint
signal rejected()
signal started();
function open() {
show()
}
// TODO: implement without hardcoding sizes
width: 480
height: 200
ColumnLayout {
id: mainLayout
spacing: 10
anchors { fill: parent; margins: 35 }
ColumnLayout {
id: column
//anchors {fill: parent; margins: 16 }
Layout.alignment: Qt.AlignHCenter
Label {
text: qsTr("Daemon doesn't appear to be running")
Layout.alignment: Qt.AlignHCenter
Layout.columnSpan: 2
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 24
font.family: "Arial"
color: "#555555"
}
}
RowLayout {
id: buttons
spacing: 60
Layout.alignment: Qt.AlignHCenter
MoneroComponents.StandardButton {
id: okButton
width: 120
fontSize: 14
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
text: qsTr("Start daemon")
KeyNavigation.tab: cancelButton
onClicked: {
root.close()
appWindow.startDaemon();
root.started()
}
}
MoneroComponents.StandardButton {
id: cancelButton
width: 120
fontSize: 14
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
text: qsTr("Cancel")
onClicked: {
root.close()
root.rejected()
}
}
}
}
}

@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.0
import moneroComponents.Wallet 1.0
Item {
id: item
@ -45,7 +46,7 @@ Item {
// TODO: lower daemon block height cache, ttl and refresh interval?
item.visible = (currentBlock < targetBlock)
item.visible = (currentWallet.connected !== Wallet.ConnectionStatus_Disconnected) && (currentBlock < targetBlock)
}
}

@ -69,6 +69,7 @@ ListView {
StandardDialog {
id: detailsPopup
width:600
cancelVisible: false
okVisible: true
}

@ -152,8 +152,8 @@ Window {
text: qsTr("Cancel")
KeyNavigation.tab: passwordInput
onClicked: {
root.rejected()
root.close()
root.rejected()
}
}
MoneroComponents.StandardButton {
@ -167,8 +167,8 @@ Window {
text: qsTr("Ok")
KeyNavigation.tab: cancelButton
onClicked: {
root.accepted()
root.close()
root.accepted()
}
}
}

@ -40,9 +40,12 @@ Window {
modality: Qt.ApplicationModal
flags: Qt.Window | Qt.FramelessWindowHint
property alias title: dialogTitle.text
property alias content: dialogContent.text
property alias text: dialogContent.text
property alias content: root.text
property alias cancelVisible: cancelButton.visible
property alias okVisible: okButton.visible
property alias textArea: dialogContent
property var icon
// same signals as Dialog has
signal accepted()
@ -54,8 +57,8 @@ Window {
}
// TODO: implement without hardcoding sizes
width: 600
height: 480
width: 480
height: 280
ColumnLayout {
id: mainLayout
@ -81,6 +84,7 @@ Window {
TextArea {
id : dialogContent
Layout.fillWidth: true
Layout.fillHeight: true
font.family: "Arial"
textFormat: TextEdit.AutoText
readOnly: true
@ -105,8 +109,9 @@ Window {
text: qsTr("Ok")
KeyNavigation.tab: cancelButton
onClicked: {
root.accepted()
root.close()
root.accepted()
}
}
@ -120,8 +125,8 @@ Window {
pressedColor: "#FF4304"
text: qsTr("Cancel")
onClicked: {
root.rejected()
root.close()
root.rejected()
}
}
}

@ -31,6 +31,7 @@
#include <QtQml>
#include <QStandardPaths>
#include <QDebug>
#include <QObject>
#include "clipboardAdapter.h"
#include "filter.h"
#include "oscursor.h"
@ -44,6 +45,7 @@
#include "TransactionHistory.h"
#include "model/TransactionHistoryModel.h"
#include "model/TransactionHistorySortFilterModel.h"
#include "daemon/DaemonManager.h"
int main(int argc, char *argv[])
@ -88,6 +90,8 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<TransactionInfo>("moneroComponents.TransactionInfo", 1, 0, "TransactionInfo",
"TransactionHistory can't be instantiated directly");
qmlRegisterUncreatableType<DaemonManager>("moneroComponents.DaemonManager", 1, 0, "DaemonManager",
"DaemonManager can't be instantiated directly");
qRegisterMetaType<PendingTransaction::Priority>();
qRegisterMetaType<TransactionInfo::Direction>();
qRegisterMetaType<TransactionHistoryModel::TransactionInfoRole>();
@ -104,6 +108,10 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("translationManager", TranslationManager::instance());
engine.addImageProvider(QLatin1String("qrcode"), new QRCodeImageProvider());
const QStringList arguments = QCoreApplication::arguments();
DaemonManager * daemonManager = DaemonManager::instance(&arguments);
QObject::connect(&app, SIGNAL(aboutToQuit()), daemonManager, SLOT(closing()));
engine.rootContext()->setContextProperty("daemonManager", daemonManager);
// export to QML monero accounts root directory
// wizard is talking about where

@ -59,6 +59,7 @@ ApplicationWindow {
property int restoreHeight:0
property bool daemonSynced: false
property int maxWindowHeight: (Screen.height < 900)? 720 : 800;
property bool daemonRunning: false
// true if wallet ever synchronized
property bool walletInitialized : false
@ -212,6 +213,7 @@ ApplicationWindow {
currentWallet.moneySpent.disconnect(onWalletMoneySent)
currentWallet.moneyReceived.disconnect(onWalletMoneyReceived)
currentWallet.transactionCreated.disconnect(onTransactionCreated)
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
currentWallet.refreshed.connect(onWalletRefresh)
currentWallet.updated.connect(onWalletUpdate)
@ -219,6 +221,7 @@ ApplicationWindow {
currentWallet.moneySpent.connect(onWalletMoneySent)
currentWallet.moneyReceived.connect(onWalletMoneyReceived)
currentWallet.transactionCreated.connect(onTransactionCreated)
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
console.log("initializing with daemon address: ", persistentSettings.daemon_address)
@ -232,6 +235,11 @@ ApplicationWindow {
return wallet_path;
}
function onWalletConnectionStatusChanged(){
console.log("Wallet connection status changed")
middlePanel.updateStatus();
}
function onWalletOpened(wallet) {
console.log(">>> wallet opened: " + wallet)
if (wallet.status !== Wallet.Status_Ok) {
@ -280,18 +288,23 @@ ApplicationWindow {
hideProcessingSplash()
}
// Daemon connected
leftPanel.networkStatus.connected = currentWallet.connected
// Check daemon status
var dCurrentBlock = currentWallet.daemonBlockChainHeight();
var dTargetBlock = currentWallet.daemonBlockChainTargetHeight();
leftPanel.daemonProgress.updateProgress(dCurrentBlock,dTargetBlock);
// Daemon connected
leftPanel.networkStatus.connected = currentWallet.connected
// Daemon fully synced
// TODO: implement onDaemonSynced or similar in wallet API and don't start refresh thread before daemon is synced
daemonSynced = (currentWallet.connected != Wallet.ConnectionStatus_Disconnected && dCurrentBlock >= dTargetBlock)
leftPanel.daemonProgress.updateProgress(dCurrentBlock,dTargetBlock);
middlePanel.updateStatus();
// If wallet isnt connected and no daemon is running - Ask
if(currentWallet.connected === Wallet.ConnectionStatus_Disconnected && !daemonManager.running() && !walletInitialized){
daemonManagerDialog.open();
}
// Refresh is succesfull if blockchain height > 1
if (currentWallet.blockChainHeight() > 1){
@ -317,10 +330,29 @@ ApplicationWindow {
walletInitialized = true
}
onWalletUpdate();
}
function startDaemon(){
appWindow.showProcessingSplash(qsTr("Waiting for daemon to start..."))
daemonManager.start();
}
function stopDaemon(){
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."))
daemonManager.stop();
}
function onDaemonStarted(){
console.log("daemon started");
daemonRunning = true;
}
function onDaemonStopped(){
console.log("daemon stopped");
daemonRunning = false;
}
function onWalletNewBlock(blockHeight) {
if (splash.visible) {
var currHeight = blockHeight
@ -633,6 +665,9 @@ ApplicationWindow {
walletManager.walletOpened.connect(onWalletOpened);
walletManager.walletClosed.connect(onWalletClosed);
daemonManager.daemonStarted.connect(onDaemonStarted);
daemonManager.daemonStopped.connect(onDaemonStopped);
if(!walletsFound()) {
rootItem.state = "wizard"
} else {
@ -668,11 +703,11 @@ ApplicationWindow {
// TODO: replace with customized popups
// Information dialog
MessageDialog {
StandardDialog {
// dynamically change onclose handler
property var onCloseCallback
id: informationPopup
standardButtons: StandardButton.Ok
cancelVisible: false
onAccepted: {
if (onCloseCallback) {
onCloseCallback()
@ -681,10 +716,10 @@ ApplicationWindow {
}
// Confrirmation aka question dialog
MessageDialog {
StandardDialog {
id: transactionConfirmationPopup
standardButtons: StandardButton.Ok + StandardButton.Cancel
onAccepted: {
close();
handleTransactionConfirmed()
}
}
@ -721,6 +756,10 @@ ApplicationWindow {
}
DaemonManagerDialog {
id: daemonManagerDialog
}
ProcessingSplash {
id: splash
@ -1032,10 +1071,13 @@ ApplicationWindow {
}
}
onClosing: {
// Close and save to disk on app close
if (currentWallet != undefined) {
walletManager.closeWallet(currentWallet);
currentWallet = undefined
}
// Make sure wallet is closed before app exit (~Wallet() isn't always invoked)
// Daemon shutdown is handled with signal/slot in main.cpp
if (currentWallet != undefined) {
walletManager.closeWallet(currentWallet);
currentWallet = undefined
}
// Stop daemon
daemonManager.stop();
}
}

@ -30,7 +30,8 @@ HEADERS += \
src/model/TransactionHistorySortFilterModel.h \
src/QR-Code-generator/BitBuffer.hpp \
src/QR-Code-generator/QrCode.hpp \
src/QR-Code-generator/QrSegment.hpp
src/QR-Code-generator/QrSegment.hpp \
src/daemon/DaemonManager.h
SOURCES += main.cpp \
@ -49,7 +50,8 @@ SOURCES += main.cpp \
src/model/TransactionHistorySortFilterModel.cpp \
src/QR-Code-generator/BitBuffer.cpp \
src/QR-Code-generator/QrCode.cpp \
src/QR-Code-generator/QrSegment.cpp
src/QR-Code-generator/QrSegment.cpp \
src/daemon/DaemonManager.cpp
lupdate_only {
SOURCES = *.qml \

@ -37,7 +37,6 @@ import "../components"
import moneroComponents.Clipboard 1.0
Rectangle {
property var daemonAddress
color: "#F0EEEE"
@ -272,15 +271,94 @@ Rectangle {
}
}
RowLayout {
Label {
id: manageDaemonLabel
color: "#4A4949"
text: qsTr("Manage daemon") + translationManager.emptyString
fontSize: 16
}
StandardButton {
visible: true
enabled: !appWindow.daemonRunning
id: startDaemonButton
width: 110
text: qsTr("Start daemon") + translationManager.emptyString
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
onClicked: {
appWindow.startDaemon()
}
}
StandardButton {
visible: true
enabled: appWindow.daemonRunning
id: stopDaemonButton
width: 110
text: qsTr("Stop daemon") + translationManager.emptyString
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
onClicked: {
appWindow.stopDaemon()
}
}
StandardButton {
visible: true
// enabled: appWindow.daemonRunning
id: daemonConsolePopupButton
width: 110
text: qsTr("Show log") + translationManager.emptyString
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
onClicked: {
daemonConsolePopup.open();
}
}
}
}
// Daemon console
StandardDialog {
id: daemonConsolePopup
height:500
width:800
cancelVisible: false
title: qsTr("Daemon log")
onAccepted: {
close();
}
}
// fires on every page load
function onPageCompleted() {
console.log("Settings page loaded");
initSettings();
}
// fires only once
Component.onCompleted: {
daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated)
}
function onDaemonConsoleUpdated(message){
// Update daemon console
daemonConsolePopup.textArea.append(message)
}
}

@ -29,6 +29,7 @@
import QtQuick 2.0
import moneroComponents.PendingTransaction 1.0
import "../components"
import moneroComponents.Wallet 1.0
Rectangle {
@ -331,4 +332,69 @@ Rectangle {
}
}
Rectangle {
id:desaturate
color:"black"
anchors.fill: parent
opacity: 0.1
visible: (root.enabled)? 0 : 1;
}
Rectangle {
x: root.width/2 - width/2
y: root.height/2 - height/2
height:statusText.paintedHeight + 50
width:statusText.paintedWidth + 40
visible: statusText.text != ""
opacity: 0.9
Text {
id: statusText
anchors.fill:parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Component.onCompleted: {
//Disable password page until enabled by updateStatus
root.enabled = false
}
// fires on every page load
function onPageCompleted() {
console.log("transfer page loaded")
updateStatus();
}
//TODO: Add daemon sync status
//TODO: enable send page when we're connected and daemon is synced
function updateStatus() {
console.log("updated transfer page status")
if(typeof currentWallet === "undefined") {
statusText.text = qsTr("Wallet is not connected to daemon.")
return;
}
switch (currentWallet.connected) {
case Wallet.ConnectionStatus_Disconnected:
statusText.text = qsTr("Wallet is not connected to daemon.")
break
case Wallet.ConnectionStatus_WrongVersion:
statusText.text = qsTr("Connected daemon is not compatible with GUI. \n" +
"Please upgrade or connect to another daemon")
break
default:
if(!appWindow.daemonSynced){
statusText.text = qsTr("Waiting on daemon synchronization to finish")
} else {
// everything OK, enable transfer page
root.enabled = true;
statusText.text = "";
}
}
}
}

@ -120,5 +120,7 @@
<file>components/DaemonProgress.qml</file>
<file>components/StandardDialog.qml</file>
<file>pages/Sign.qml</file>
<file>components/DaemonManagerDialog.qml</file>
<file>components/StandardDialog.qml</file>
</qresource>
</RCC>

@ -0,0 +1,142 @@
#include "DaemonManager.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QUrl>
#include <QtConcurrent/QtConcurrent>
#include <QApplication>
#include <QProcess>
DaemonManager * DaemonManager::m_instance = nullptr;
QStringList DaemonManager::m_clArgs;
DaemonManager *DaemonManager::instance(const QStringList *args)
{
if (!m_instance) {
m_instance = new DaemonManager;
// store command line arguments for later use
m_clArgs = *args;
m_clArgs.removeFirst();
}
return m_instance;
}
bool DaemonManager::start()
{
//
QString process;
#ifdef Q_OS_WIN
process = QApplication::applicationDirPath() + "/monerod.exe";
#elif defined(Q_OS_UNIX)
process = QApplication::applicationDirPath() + "/monerod";
#endif
if (process.length() == 0) {
qDebug() << "no daemon binary defined for current platform";
return false;
}
// prepare command line arguments and pass to monerod
QStringList arguments;
foreach (const QString &str, m_clArgs) {
qDebug() << QString(" [%1] ").arg(str);
arguments << str;
}
qDebug() << "starting monerod " + process;
qDebug() << "With command line arguments " << arguments;
m_daemon = new QProcess();
initialized = true;
// Connect output slots
connect (m_daemon, SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
connect (m_daemon, SIGNAL(readyReadStandardError()), this, SLOT(printError()));
// Start monerod
m_daemon->start(process,arguments);
bool started = m_daemon->waitForStarted();
// add state changed listener
connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState)));
if (!started) {
qDebug() << "Daemon start error: " + m_daemon->errorString();
} else {
emit daemonStarted();
}
return started;
}
bool DaemonManager::stop()
{
if (initialized) {
qDebug() << "stopping daemon";
// we can't use QProcess::terminate() on windows console process
// write exit command to stdin
m_daemon->write("exit\n");
}
return true;
}
void DaemonManager::stateChanged(QProcess::ProcessState state)
{
qDebug() << "STATE CHANGED: " << state;
if (state == QProcess::NotRunning) {
emit daemonStopped();
}
}
void DaemonManager::printOutput()
{
QByteArray byteArray = m_daemon->readAllStandardOutput();
QStringList strLines = QString(byteArray).split("\n");
foreach (QString line, strLines) {
emit daemonConsoleUpdated(line);
qDebug() << "Daemon: " + line;
}
}
void DaemonManager::printError()
{
QByteArray byteArray = m_daemon->readAllStandardError();
QStringList strLines = QString(byteArray).split("\n");
foreach (QString line, strLines) {
emit daemonConsoleUpdated(line);
qDebug() << "Daemon ERROR: " + line;
}
}
bool DaemonManager::running() const
{
if (initialized) {
qDebug() << m_daemon->state();
qDebug() << QProcess::NotRunning;
// m_daemon->write("status\n");
return m_daemon->state() > QProcess::NotRunning;
}
return false;
}
DaemonManager::DaemonManager(QObject *parent)
: QObject(parent)
{
}
void DaemonManager::closing()
{
qDebug() << __FUNCTION__;
stop();
// Wait for daemon to stop before exiting (max 10 secs)
if (initialized) {
m_daemon->waitForFinished(10000);
}
}

@ -0,0 +1,43 @@
#ifndef DAEMONMANAGER_H
#define DAEMONMANAGER_H
#include <QObject>
#include <QUrl>
#include <QProcess>
class DaemonManager : public QObject
{
Q_OBJECT
public:
static DaemonManager * instance(const QStringList *args);
Q_INVOKABLE bool start();
Q_INVOKABLE bool stop();
// return true if daemon process is started
Q_INVOKABLE bool running() const;
signals:
void daemonStarted();
void daemonStopped();
void daemonConsoleUpdated(QString message);
public slots:
void printOutput();
void printError();
void closing();
void stateChanged(QProcess::ProcessState state);
private:
explicit DaemonManager(QObject *parent = 0);
static DaemonManager * m_instance;
static QStringList m_clArgs;
QProcess *m_daemon;
bool initialized = false;
};
#endif // DAEMONMANAGER_H

@ -15,6 +15,7 @@
namespace {
static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 10;
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 60;
static const int WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS = 5;
}
class WalletListenerImpl : public Bitmonero::WalletListener
@ -89,7 +90,18 @@ Wallet::Status Wallet::status() const
Wallet::ConnectionStatus Wallet::connected() const
{
return static_cast<ConnectionStatus>(m_walletImpl->connected());
// cache connection status
if (!m_initialized || m_connectionStatusTime.elapsed() / 1000 > m_connectionStatusTtl) {
m_initialized = true;
ConnectionStatus newStatus = static_cast<ConnectionStatus>(m_walletImpl->connected());
if (newStatus != m_connectionStatus) {
m_connectionStatus = newStatus;
emit connectionStatusChanged();
}
m_connectionStatusTime.restart();
}
return m_connectionStatus;
}
bool Wallet::synchronized() const
@ -424,9 +436,16 @@ Wallet::Wallet(Bitmonero::Wallet *w, QObject *parent)
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
, m_daemonBlockChainTargetHeight(0)
, m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS)
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
{
m_history = new TransactionHistory(m_walletImpl->history(), this);
m_walletImpl->setListener(new WalletListenerImpl(this));
m_connectionStatus = Wallet::ConnectionStatus_Disconnected;
// start cache timers
m_connectionStatusTime.restart();
m_daemonBlockChainHeightTime.restart();
m_daemonBlockChainTargetHeightTime.restart();
m_initialized = false;
}
Wallet::~Wallet()

@ -200,6 +200,8 @@ signals:
// emitted when transaction is created async
void transactionCreated(PendingTransaction * transaction, QString address, QString paymentId, quint32 mixinCount);
void connectionStatusChanged() const;
private:
Wallet(QObject * parent = nullptr);
Wallet(Bitmonero::Wallet *w, QObject * parent = 0);
@ -221,6 +223,10 @@ private:
mutable QTime m_daemonBlockChainTargetHeightTime;
mutable quint64 m_daemonBlockChainTargetHeight;
int m_daemonBlockChainTargetHeightTtl;
mutable ConnectionStatus m_connectionStatus;
int m_connectionStatusTtl;
mutable QTime m_connectionStatusTime;
mutable bool m_initialized;
};