The qt, qml, and kivy GUIs have a first-start network-setup screen that allows the user customising the network settings before creating a wallet. Previously the daemon used to create the network and start it, before this screen, before the GUI even starts. If the user changed network settings, those would be set on the already running network, potentially including restarting the network. Now it becomes the responsibility of the GUI to start the network, allowing this first-start customisation to take place before starting the network at all. The qt and the qml GUIs are adapted to make use of this. Kivy, and the other prototype GUIs are not adapted and just start the network right away, as before.
141 lines
4.6 KiB
Python
141 lines
4.6 KiB
Python
from abc import abstractmethod
|
|
|
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
|
from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
|
|
|
|
from electrum.i18n import _
|
|
from electrum.logging import get_logger
|
|
from electrum.util import Satoshis, format_time
|
|
from electrum.interface import ServerAddr, PREFERRED_NETWORK_PROTOCOL
|
|
from electrum import blockchain
|
|
|
|
from .util import QtEventListener, qt_event_listener, event_listener
|
|
|
|
class QEServerListModel(QAbstractListModel, QtEventListener):
|
|
_logger = get_logger(__name__)
|
|
|
|
# define listmodel rolemap
|
|
_ROLE_NAMES=('name', 'address', 'is_connected', 'is_primary', 'is_tor', 'chain', 'height')
|
|
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
|
|
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES]))
|
|
_ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS))
|
|
|
|
def __init__(self, network, parent=None):
|
|
super().__init__(parent)
|
|
|
|
self._chaintips = 0
|
|
|
|
self.network = network
|
|
self.init_model()
|
|
self.register_callbacks()
|
|
self.destroyed.connect(lambda: self.unregister_callbacks())
|
|
|
|
@qt_event_listener
|
|
def on_event_network_updated(self):
|
|
self._logger.info(f'network updated')
|
|
self.init_model()
|
|
|
|
@qt_event_listener
|
|
def on_event_blockchain_updated(self):
|
|
self._logger.info(f'blockchain updated')
|
|
self.init_model()
|
|
|
|
@qt_event_listener
|
|
def on_event_default_server_changed(self):
|
|
self._logger.info(f'default server changed')
|
|
self.init_model()
|
|
|
|
def rowCount(self, index):
|
|
return len(self.servers)
|
|
|
|
def roleNames(self):
|
|
return self._ROLE_MAP
|
|
|
|
def data(self, index, role):
|
|
server = self.servers[index.row()]
|
|
role_index = role - Qt.UserRole
|
|
value = server[self._ROLE_NAMES[role_index]]
|
|
|
|
if isinstance(value, (bool, list, int, str)) or value is None:
|
|
return value
|
|
if isinstance(value, Satoshis):
|
|
return value.value
|
|
return str(value)
|
|
|
|
def clear(self):
|
|
self.beginResetModel()
|
|
self.servers = []
|
|
self.endResetModel()
|
|
|
|
chaintipsChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=chaintipsChanged)
|
|
def chaintips(self):
|
|
return self._chaintips
|
|
|
|
def get_chains(self):
|
|
chains = self.network.get_blockchains()
|
|
n_chains = len(chains)
|
|
if n_chains != self._chaintips:
|
|
self._chaintips = n_chains
|
|
self.chaintipsChanged.emit()
|
|
return chains
|
|
|
|
@pyqtSlot()
|
|
def init_model(self):
|
|
self.clear()
|
|
|
|
servers = []
|
|
|
|
chains = self.get_chains()
|
|
|
|
for chain_id, interfaces in chains.items():
|
|
self._logger.debug(f'chain {chain_id} has {len(interfaces)} interfaces')
|
|
b = blockchain.blockchains.get(chain_id)
|
|
if b is None:
|
|
continue
|
|
|
|
name = b.get_name()
|
|
|
|
self._logger.debug(f'chain {chain_id} has name={name}, max_forkpoint=@{b.get_max_forkpoint()}, height={b.height()}')
|
|
|
|
for i in interfaces:
|
|
server = {}
|
|
server['chain'] = name
|
|
server['chain_height'] = b.height()
|
|
server['is_primary'] = i == self.network.interface
|
|
server['is_connected'] = True
|
|
server['name'] = str(i.server)
|
|
server['address'] = i.server.to_friendly_name()
|
|
server['height'] = i.tip
|
|
|
|
#self._logger.debug(f'adding server: {repr(server)}')
|
|
servers.append(server)
|
|
|
|
# disconnected servers
|
|
all_servers = self.network.get_servers()
|
|
connected_hosts = set([iface.host for ifaces in chains.values() for iface in ifaces])
|
|
protocol = PREFERRED_NETWORK_PROTOCOL
|
|
for _host, d in sorted(all_servers.items()):
|
|
if _host in connected_hosts:
|
|
continue
|
|
if _host.endswith('.onion') and not self.network.tor_proxy:
|
|
continue
|
|
port = d.get(protocol)
|
|
if port:
|
|
s = ServerAddr(_host, port, protocol=protocol)
|
|
server = {}
|
|
server['chain'] = ''
|
|
server['chain_height'] = 0
|
|
server['height'] = 0
|
|
server['is_primary'] = False
|
|
server['is_connected'] = False
|
|
server['name'] = s.net_addr_str()
|
|
server['address'] = server['name']
|
|
|
|
# self._logger.debug(f'adding server: {repr(server)}')
|
|
servers.append(server)
|
|
|
|
self.beginInsertRows(QModelIndex(), 0, len(servers) - 1)
|
|
self.servers = servers
|
|
self.endInsertRows()
|