qml: add LabelSync toggle
This commit is contained in:
@@ -41,6 +41,7 @@ class ElectrumTranslator(QTranslator):
|
|||||||
def translate(self, context, source_text, disambiguation, n):
|
def translate(self, context, source_text, disambiguation, n):
|
||||||
return _(source_text, context=context)
|
return _(source_text, context=context)
|
||||||
|
|
||||||
|
|
||||||
class ElectrumGui(BaseElectrumGui, Logger):
|
class ElectrumGui(BaseElectrumGui, Logger):
|
||||||
|
|
||||||
@profiler
|
@profiler
|
||||||
@@ -91,7 +92,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
|||||||
Exception_Hook.maybe_setup(config=config, slot=self.app.appController.crash)
|
Exception_Hook.maybe_setup(config=config, slot=self.app.appController.crash)
|
||||||
|
|
||||||
# Initialize any QML plugins
|
# Initialize any QML plugins
|
||||||
run_hook('init_qml', self)
|
run_hook('init_qml', self.app)
|
||||||
self.app.engine.load('electrum/gui/qml/components/main.qml')
|
self.app.engine.load('electrum/gui/qml/components/main.qml')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|||||||
@@ -217,6 +217,25 @@ Pane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: -constants.paddingSmall
|
||||||
|
spacing: 0
|
||||||
|
Switch {
|
||||||
|
id: syncLabels
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (activeFocus)
|
||||||
|
AppController.setPluginEnabled('labels', checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr('Synchronize labels')
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PrefsHeading {
|
PrefsHeading {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
text: qsTr('Wallet behavior')
|
text: qsTr('Wallet behavior')
|
||||||
@@ -384,5 +403,6 @@ Pane {
|
|||||||
useFallbackAddress.checked = Config.useFallbackAddress
|
useFallbackAddress.checked = Config.useFallbackAddress
|
||||||
enableDebugLogs.checked = Config.enableDebugLogs
|
enableDebugLogs.checked = Config.enableDebugLogs
|
||||||
useRecoverableChannels.checked = Config.useRecoverableChannels
|
useRecoverableChannels.checked = Config.useRecoverableChannels
|
||||||
|
syncLabels.checked = AppController.isPluginEnabled('labels')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from electrum.logging import Logger, get_logger
|
|||||||
from electrum.bip21 import BITCOIN_BIP21_URI_SCHEME, LIGHTNING_URI_SCHEME
|
from electrum.bip21 import BITCOIN_BIP21_URI_SCHEME, LIGHTNING_URI_SCHEME
|
||||||
from electrum.base_crash_reporter import BaseCrashReporter, EarlyExceptionsQueue
|
from electrum.base_crash_reporter import BaseCrashReporter, EarlyExceptionsQueue
|
||||||
from electrum.network import Network
|
from electrum.network import Network
|
||||||
|
from electrum.plugin import run_hook
|
||||||
|
|
||||||
from .qeconfig import QEConfig
|
from .qeconfig import QEConfig
|
||||||
from .qedaemon import QEDaemon
|
from .qedaemon import QEDaemon
|
||||||
@@ -70,10 +71,11 @@ class QEAppController(BaseCrashReporter, QObject):
|
|||||||
sendingBugreportFailure = pyqtSignal(str)
|
sendingBugreportFailure = pyqtSignal(str)
|
||||||
secureWindowChanged = pyqtSignal()
|
secureWindowChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, qedaemon: 'QEDaemon', plugins: 'Plugins'):
|
def __init__(self, qeapp: 'ElectrumQmlApplication', qedaemon: 'QEDaemon', plugins: 'Plugins'):
|
||||||
BaseCrashReporter.__init__(self, None, None, None)
|
BaseCrashReporter.__init__(self, None, None, None)
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
|
|
||||||
|
self._app = qeapp
|
||||||
self._qedaemon = qedaemon
|
self._qedaemon = qedaemon
|
||||||
self._plugins = plugins
|
self._plugins = plugins
|
||||||
self.config = qedaemon.daemon.config
|
self.config = qedaemon.daemon.config
|
||||||
@@ -224,12 +226,18 @@ class QEAppController(BaseCrashReporter, QObject):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
@pyqtSlot(str, bool)
|
@pyqtSlot(str, bool)
|
||||||
def setPluginEnabled(self, plugin, enabled):
|
def setPluginEnabled(self, plugin: str, enabled: bool):
|
||||||
if enabled:
|
if enabled:
|
||||||
self._plugins.enable(plugin)
|
self._plugins.enable(plugin)
|
||||||
|
# note: all enabled plugins will receive this hook:
|
||||||
|
run_hook('init_qml', self._app)
|
||||||
else:
|
else:
|
||||||
self._plugins.disable(plugin)
|
self._plugins.disable(plugin)
|
||||||
|
|
||||||
|
@pyqtSlot(str, result=bool)
|
||||||
|
def isPluginEnabled(self, plugin: str):
|
||||||
|
return bool(self._plugins.get(plugin))
|
||||||
|
|
||||||
@pyqtSlot(result=bool)
|
@pyqtSlot(result=bool)
|
||||||
def isAndroid(self):
|
def isAndroid(self):
|
||||||
return 'ANDROID_DATA' in os.environ
|
return 'ANDROID_DATA' in os.environ
|
||||||
@@ -369,7 +377,7 @@ class ElectrumQmlApplication(QGuiApplication):
|
|||||||
self._qeconfig = QEConfig(config)
|
self._qeconfig = QEConfig(config)
|
||||||
self._qenetwork = QENetwork(daemon.network, self._qeconfig)
|
self._qenetwork = QENetwork(daemon.network, self._qeconfig)
|
||||||
self.daemon = QEDaemon(daemon)
|
self.daemon = QEDaemon(daemon)
|
||||||
self.appController = QEAppController(self.daemon, self.plugins)
|
self.appController = QEAppController(self, self.daemon, self.plugins)
|
||||||
self._maxAmount = QEAmount(is_max=True)
|
self._maxAmount = QEAmount(is_max=True)
|
||||||
self.context.setContextProperty('AppController', self.appController)
|
self.context.setContextProperty('AppController', self.appController)
|
||||||
self.context.setContextProperty('Config', self._qeconfig)
|
self.context.setContextProperty('Config', self._qeconfig)
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ def hook(func):
|
|||||||
hook_names.add(func.__name__)
|
hook_names.add(func.__name__)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
def run_hook(name, *args):
|
def run_hook(name, *args):
|
||||||
results = []
|
results = []
|
||||||
f_list = hooks.get(name, [])
|
f_list = hooks.get(name, [])
|
||||||
|
|||||||
@@ -134,9 +134,11 @@ class LabelsPlugin(BasePlugin):
|
|||||||
response = await self.do_get("/labels/since/%d/for/%s" % (nonce, wallet_id))
|
response = await self.do_get("/labels/since/%d/for/%s" % (nonce, wallet_id))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ErrorConnectingServer(e) from e
|
raise ErrorConnectingServer(e) from e
|
||||||
if response["labels"] is None:
|
if response["labels"] is None or len(response["labels"]) == 0:
|
||||||
self.logger.info('no new labels')
|
self.logger.info('no new labels')
|
||||||
return
|
return
|
||||||
|
self.logger.debug(f"labels received {response!r}")
|
||||||
|
self.logger.info(f'received {len(response["labels"])} labels')
|
||||||
result = {}
|
result = {}
|
||||||
for label in response["labels"]:
|
for label in response["labels"]:
|
||||||
try:
|
try:
|
||||||
@@ -157,7 +159,6 @@ class LabelsPlugin(BasePlugin):
|
|||||||
if force or not wallet._get_label(key):
|
if force or not wallet._get_label(key):
|
||||||
wallet._set_label(key, value)
|
wallet._set_label(key, value)
|
||||||
|
|
||||||
self.logger.info(f"received {len(response)} labels")
|
|
||||||
self.set_nonce(wallet, response["nonce"] + 1)
|
self.set_nonce(wallet, response["nonce"] + 1)
|
||||||
self.on_pulled(wallet)
|
self.on_pulled(wallet)
|
||||||
|
|
||||||
@@ -173,15 +174,18 @@ class LabelsPlugin(BasePlugin):
|
|||||||
self.logger.info(repr(e))
|
self.logger.info(repr(e))
|
||||||
|
|
||||||
def pull(self, wallet: 'Abstract_Wallet', force: bool):
|
def pull(self, wallet: 'Abstract_Wallet', force: bool):
|
||||||
if not wallet.network: raise Exception(_('You are offline.'))
|
if not wallet.network:
|
||||||
|
raise Exception(_('You are offline.'))
|
||||||
return asyncio.run_coroutine_threadsafe(self.pull_thread(wallet, force), wallet.network.asyncio_loop).result()
|
return asyncio.run_coroutine_threadsafe(self.pull_thread(wallet, force), wallet.network.asyncio_loop).result()
|
||||||
|
|
||||||
def push(self, wallet: 'Abstract_Wallet'):
|
def push(self, wallet: 'Abstract_Wallet'):
|
||||||
if not wallet.network: raise Exception(_('You are offline.'))
|
if not wallet.network:
|
||||||
|
raise Exception(_('You are offline.'))
|
||||||
return asyncio.run_coroutine_threadsafe(self.push_thread(wallet), wallet.network.asyncio_loop).result()
|
return asyncio.run_coroutine_threadsafe(self.push_thread(wallet), wallet.network.asyncio_loop).result()
|
||||||
|
|
||||||
def start_wallet(self, wallet: 'Abstract_Wallet'):
|
def start_wallet(self, wallet: 'Abstract_Wallet'):
|
||||||
if not wallet.network: return # 'offline' mode
|
if not wallet.network:
|
||||||
|
return # 'offline' mode
|
||||||
mpk = wallet.get_fingerprint()
|
mpk = wallet.get_fingerprint()
|
||||||
if not mpk:
|
if not mpk:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot
|
||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.plugin import hook
|
from electrum.plugin import hook
|
||||||
@@ -10,6 +10,7 @@ from electrum.gui.qml.plugins import PluginQObject
|
|||||||
|
|
||||||
from .labels import LabelsPlugin
|
from .labels import LabelsPlugin
|
||||||
|
|
||||||
|
|
||||||
class Plugin(LabelsPlugin):
|
class Plugin(LabelsPlugin):
|
||||||
|
|
||||||
class QSignalObject(PluginQObject):
|
class QSignalObject(PluginQObject):
|
||||||
@@ -63,6 +64,8 @@ class Plugin(LabelsPlugin):
|
|||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
LabelsPlugin.__init__(self, *args)
|
LabelsPlugin.__init__(self, *args)
|
||||||
|
self._app = None
|
||||||
|
self.so = None
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def load_wallet(self, wallet):
|
def load_wallet(self, wallet):
|
||||||
@@ -77,9 +80,9 @@ class Plugin(LabelsPlugin):
|
|||||||
|
|
||||||
wallet = self._app.daemon.currentWallet.wallet
|
wallet = self._app.daemon.currentWallet.wallet
|
||||||
|
|
||||||
def push_thread(wallet):
|
def push_thread(_wallet):
|
||||||
try:
|
try:
|
||||||
self.push(wallet)
|
self.push(_wallet)
|
||||||
self.so.upload_finished(True)
|
self.so.upload_finished(True)
|
||||||
self._app.appController.userNotify.emit(_('Labels uploaded'))
|
self._app.appController.userNotify.emit(_('Labels uploaded'))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -87,7 +90,7 @@ class Plugin(LabelsPlugin):
|
|||||||
self.so.upload_finished(False)
|
self.so.upload_finished(False)
|
||||||
self._app.appController.userNotify.emit(repr(e))
|
self._app.appController.userNotify.emit(repr(e))
|
||||||
|
|
||||||
threading.Thread(target=push_thread,args=[wallet]).start()
|
threading.Thread(target=push_thread, args=[wallet]).start()
|
||||||
|
|
||||||
def pull_async(self):
|
def pull_async(self):
|
||||||
if not self._app.daemon.currentWallet:
|
if not self._app.daemon.currentWallet:
|
||||||
@@ -96,9 +99,10 @@ class Plugin(LabelsPlugin):
|
|||||||
return
|
return
|
||||||
|
|
||||||
wallet = self._app.daemon.currentWallet.wallet
|
wallet = self._app.daemon.currentWallet.wallet
|
||||||
def pull_thread(wallet):
|
|
||||||
|
def pull_thread(_wallet):
|
||||||
try:
|
try:
|
||||||
self.pull(wallet, True)
|
self.pull(_wallet, True)
|
||||||
self.so.download_finished(True)
|
self.so.download_finished(True)
|
||||||
self._app.appController.userNotify.emit(_('Labels downloaded'))
|
self._app.appController.userNotify.emit(_('Labels downloaded'))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -106,8 +110,7 @@ class Plugin(LabelsPlugin):
|
|||||||
self.so.download_finished(False)
|
self.so.download_finished(False)
|
||||||
self._app.appController.userNotify.emit(repr(e))
|
self._app.appController.userNotify.emit(repr(e))
|
||||||
|
|
||||||
threading.Thread(target=pull_thread,args=[wallet]).start()
|
threading.Thread(target=pull_thread, args=[wallet]).start()
|
||||||
|
|
||||||
|
|
||||||
def on_pulled(self, wallet):
|
def on_pulled(self, wallet):
|
||||||
self.logger.info('on pulled')
|
self.logger.info('on pulled')
|
||||||
@@ -117,9 +120,15 @@ class Plugin(LabelsPlugin):
|
|||||||
_wallet.labelsUpdated.emit()
|
_wallet.labelsUpdated.emit()
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def init_qml(self, gui):
|
def init_qml(self, app):
|
||||||
self.logger.debug(f'init_qml hook called, gui={str(type(gui))}')
|
self.logger.debug(f'init_qml hook called, gui={str(type(app))}')
|
||||||
self._app = gui.app
|
self.logger.debug(f'app={self._app!r}, so={self.so!r}')
|
||||||
|
self._app = app
|
||||||
# important: QSignalObject needs to be parented, as keeping a ref
|
# important: QSignalObject needs to be parented, as keeping a ref
|
||||||
# in the plugin is not enough to avoid gc
|
# in the plugin is not enough to avoid gc
|
||||||
self.so = Plugin.QSignalObject(self, self._app)
|
self.so = Plugin.QSignalObject(self, self._app)
|
||||||
|
|
||||||
|
# If the user just enabled the plugin, the 'load_wallet' hook would not
|
||||||
|
# get called for already loaded wallets, hence we call it manually for those:
|
||||||
|
for wallet_name, wallet in app.daemon.daemon._wallets.items():
|
||||||
|
self.load_wallet(wallet)
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ from .trustedcoin import (TrustedCoinPlugin, server, ErrorConnectingServer,
|
|||||||
TrustedCoinException, make_xpub)
|
TrustedCoinException, make_xpub)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from electrum.gui.qml import ElectrumGui
|
from electrum.gui.qml import ElectrumQmlApplication
|
||||||
from electrum.wallet import Abstract_Wallet
|
from electrum.wallet import Abstract_Wallet
|
||||||
|
|
||||||
|
|
||||||
class Plugin(TrustedCoinPlugin):
|
class Plugin(TrustedCoinPlugin):
|
||||||
|
|
||||||
class QSignalObject(PluginQObject):
|
class QSignalObject(PluginQObject):
|
||||||
@@ -287,9 +288,9 @@ class Plugin(TrustedCoinPlugin):
|
|||||||
self.start_request_thread(wallet)
|
self.start_request_thread(wallet)
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def init_qml(self, gui: 'ElectrumGui'):
|
def init_qml(self, app: 'ElectrumQmlApplication'):
|
||||||
self.logger.debug(f'init_qml hook called, gui={str(type(gui))}')
|
self.logger.debug(f'init_qml hook called, gui={str(type(app))}')
|
||||||
self._app = gui.app
|
self._app = app
|
||||||
# important: QSignalObject needs to be parented, as keeping a ref
|
# important: QSignalObject needs to be parented, as keeping a ref
|
||||||
# in the plugin is not enough to avoid gc
|
# in the plugin is not enough to avoid gc
|
||||||
self.so = Plugin.QSignalObject(self, self._app)
|
self.so = Plugin.QSignalObject(self, self._app)
|
||||||
|
|||||||
Reference in New Issue
Block a user