qt: implement server picker in server connect wizard
This commit is contained in:
@@ -95,9 +95,12 @@ class NodesListWidget(QTreeWidget):
|
||||
DISCONNECTED_SERVER = 2
|
||||
TOPLEVEL = 3
|
||||
|
||||
def __init__(self, parent):
|
||||
followServer = pyqtSignal([object], arguments=['server'])
|
||||
followChain = pyqtSignal([str], arguments=['chain_id'])
|
||||
setServer = pyqtSignal([str], arguments=['server'])
|
||||
|
||||
def __init__(self):
|
||||
QTreeWidget.__init__(self)
|
||||
self.parent = parent # type: NetworkChoiceLayout
|
||||
self.setHeaderLabels([_('Server'), _('Height')])
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self.create_menu)
|
||||
@@ -110,16 +113,19 @@ class NodesListWidget(QTreeWidget):
|
||||
menu = QMenu()
|
||||
if item_type == self.ItemType.CONNECTED_SERVER:
|
||||
server = item.data(0, self.SERVER_ADDR_ROLE) # type: ServerAddr
|
||||
menu.addAction(_("Use as server"), lambda: self.parent.follow_server(server))
|
||||
def do_follow_server():
|
||||
self.followServer.emit(server)
|
||||
menu.addAction(_("Use as server"), do_follow_server)
|
||||
elif item_type == self.ItemType.DISCONNECTED_SERVER:
|
||||
server = item.data(0, self.SERVER_ADDR_ROLE) # type: ServerAddr
|
||||
def func():
|
||||
self.parent.server_e.setText(str(server))
|
||||
self.parent.set_server()
|
||||
menu.addAction(_("Use as server"), func)
|
||||
def do_set_server():
|
||||
self.setServer.emit(str(server))
|
||||
menu.addAction(_("Use as server"), do_set_server)
|
||||
elif item_type == self.ItemType.CHAIN:
|
||||
chain_id = item.data(0, self.CHAIN_ID_ROLE)
|
||||
menu.addAction(_("Follow this branch"), lambda: self.parent.follow_branch(chain_id))
|
||||
def do_follow_chain():
|
||||
self.followChain.emit(chain_id)
|
||||
menu.addAction(_("Follow this branch"), do_follow_chain)
|
||||
else:
|
||||
return
|
||||
menu.exec_(self.viewport().mapToGlobal(position))
|
||||
@@ -136,9 +142,11 @@ class NodesListWidget(QTreeWidget):
|
||||
pt.setX(50)
|
||||
self.customContextMenuRequested.emit(pt)
|
||||
|
||||
def update(self, *, network: Network, servers: dict, use_tor: bool):
|
||||
def update(self, *, network: Network, servers: dict):
|
||||
self.clear()
|
||||
|
||||
use_tor = network.tor_proxy
|
||||
|
||||
# connected servers
|
||||
connected_servers_item = QTreeWidgetItem([_("Connected nodes"), ''])
|
||||
connected_servers_item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.TOPLEVEL)
|
||||
@@ -146,7 +154,8 @@ class NodesListWidget(QTreeWidget):
|
||||
n_chains = len(chains)
|
||||
for chain_id, interfaces in chains.items():
|
||||
b = blockchain.blockchains.get(chain_id)
|
||||
if b is None: continue
|
||||
if b is None:
|
||||
continue
|
||||
name = b.get_name()
|
||||
if n_chains > 1:
|
||||
x = QTreeWidgetItem([name + '@%d'%b.get_max_forkpoint(), '%d'%b.height()])
|
||||
@@ -201,7 +210,9 @@ class NodesListWidget(QTreeWidget):
|
||||
|
||||
|
||||
class NetworkChoiceLayout(object):
|
||||
|
||||
# TODO consolidate to ProxyWidget+ServerWidget
|
||||
# TODO TorDetector is unnecessary, Network tests socks5 peer and detects Tor
|
||||
# TODO apply on editingFinished is not ideal, separate Apply button and on Close?
|
||||
def __init__(self, network: Network, config: 'SimpleConfig', wizard=False):
|
||||
self.network = network
|
||||
self.config = config
|
||||
@@ -304,7 +315,14 @@ class NetworkChoiceLayout(object):
|
||||
self.split_label = QLabel('')
|
||||
grid.addWidget(self.split_label, 4, 0, 1, 3)
|
||||
|
||||
self.nodes_list_widget = NodesListWidget(self)
|
||||
self.nodes_list_widget = NodesListWidget()
|
||||
self.nodes_list_widget.followServer.connect(self.follow_server)
|
||||
self.nodes_list_widget.followChain.connect(self.follow_branch)
|
||||
|
||||
def do_set_server(server):
|
||||
self.server_e.setText(server)
|
||||
self.set_server()
|
||||
self.nodes_list_widget.setServer.connect(do_set_server)
|
||||
grid.addWidget(self.nodes_list_widget, 6, 0, 1, 5)
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
@@ -361,8 +379,7 @@ class NetworkChoiceLayout(object):
|
||||
msg = ''
|
||||
self.split_label.setText(msg)
|
||||
self.nodes_list_widget.update(network=self.network,
|
||||
servers=self.network.get_servers(),
|
||||
use_tor=self.tor_cb.isChecked())
|
||||
servers=self.network.get_servers())
|
||||
self.enable_set_server()
|
||||
|
||||
def fill_in_proxy_settings(self):
|
||||
@@ -499,13 +516,11 @@ class ProxyWidget(QWidget):
|
||||
grid = QGridLayout(self)
|
||||
grid.setSpacing(8)
|
||||
|
||||
# proxy setting
|
||||
# proxy setting.
|
||||
self.proxy_cb = QCheckBox(_('Use proxy'))
|
||||
# self.proxy_cb.clicked.connect(self.check_disable_proxy)
|
||||
# self.proxy_cb.clicked.connect(self.set_proxy)
|
||||
|
||||
self.proxy_mode = QComboBox()
|
||||
self.proxy_mode.addItems(['SOCKS4', 'SOCKS5'])
|
||||
self.proxy_mode.setCurrentIndex(1)
|
||||
self.proxy_host = QLineEdit()
|
||||
self.proxy_host.setFixedWidth(fixed_width_hostname)
|
||||
self.proxy_port = QLineEdit()
|
||||
@@ -516,43 +531,37 @@ class ProxyWidget(QWidget):
|
||||
self.proxy_password.setPlaceholderText(_("Password"))
|
||||
self.proxy_password.setFixedWidth(fixed_width_port)
|
||||
|
||||
# self.proxy_mode.currentIndexChanged.connect(self.set_proxy)
|
||||
# self.proxy_host.editingFinished.connect(self.set_proxy)
|
||||
# self.proxy_port.editingFinished.connect(self.set_proxy)
|
||||
# self.proxy_user.editingFinished.connect(self.set_proxy)
|
||||
# self.proxy_password.editingFinished.connect(self.set_proxy)
|
||||
grid.addWidget(self.proxy_cb, 0, 0, 1, 3)
|
||||
grid.addWidget(HelpButton(_('Proxy settings apply to all connections: with Electrum servers, but also with third-party services.')), 0, 4)
|
||||
grid.addWidget(self.proxy_mode, 1, 1)
|
||||
grid.addWidget(self.proxy_host, 1, 2)
|
||||
grid.addWidget(self.proxy_port, 1, 3)
|
||||
grid.addWidget(self.proxy_user, 2, 2)
|
||||
grid.addWidget(self.proxy_password, 2, 3)
|
||||
|
||||
# self.proxy_mode.currentIndexChanged.connect(self.proxy_settings_changed)
|
||||
# self.proxy_host.textEdited.connect(self.proxy_settings_changed)
|
||||
# self.proxy_port.textEdited.connect(self.proxy_settings_changed)
|
||||
# self.proxy_user.textEdited.connect(self.proxy_settings_changed)
|
||||
# self.proxy_password.textEdited.connect(self.proxy_settings_changed)
|
||||
|
||||
self.tor_cb = QCheckBox(_("Use Tor Proxy"))
|
||||
self.tor_cb.setIcon(read_QIcon("tor_logo.png"))
|
||||
self.tor_cb.hide()
|
||||
# self.tor_cb.clicked.connect(self.use_tor_proxy)
|
||||
|
||||
grid.addWidget(self.tor_cb, 1, 0, 1, 3)
|
||||
grid.addWidget(self.proxy_cb, 2, 0, 1, 3)
|
||||
grid.addWidget(HelpButton(_('Proxy settings apply to all connections: with Electrum servers, but also with third-party services.')), 2, 4)
|
||||
grid.addWidget(self.proxy_mode, 4, 1)
|
||||
grid.addWidget(self.proxy_host, 4, 2)
|
||||
grid.addWidget(self.proxy_port, 4, 3)
|
||||
grid.addWidget(self.proxy_user, 5, 2)
|
||||
grid.addWidget(self.proxy_password, 5, 3)
|
||||
grid.setRowStretch(7, 1)
|
||||
def get_proxy_settings(self):
|
||||
return {
|
||||
'enabled': self.proxy_cb.isChecked(),
|
||||
'mode': ['socks4', 'socks5'][self.proxy_mode.currentIndex()],
|
||||
'host': self.proxy_host.text(),
|
||||
'port': self.proxy_port.text(),
|
||||
'user': self.proxy_user.text(),
|
||||
'password': self.proxy_password.text()
|
||||
}
|
||||
|
||||
|
||||
class ServerWidget(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
class ServerWidget(QWidget, QtEventListener):
|
||||
def __init__(self, network, parent=None):
|
||||
super().__init__(parent)
|
||||
self.network = network
|
||||
self.config = network.config
|
||||
|
||||
fixed_width_hostname = 24 * char_width_in_lineedit()
|
||||
fixed_width_port = 6 * char_width_in_lineedit()
|
||||
|
||||
self.setLayout(QVBoxLayout())
|
||||
|
||||
grid = QGridLayout(self)
|
||||
# self.setLayout(grid)
|
||||
|
||||
msg = ' '.join([
|
||||
_("Electrum connects to several nodes in order to download block headers and find out the longest blockchain."),
|
||||
@@ -564,9 +573,7 @@ class ServerWidget(QWidget):
|
||||
grid.addWidget(HelpButton(msg), 0, 4)
|
||||
|
||||
self.autoconnect_cb = QCheckBox(_('Select server automatically'))
|
||||
# self.autoconnect_cb.setEnabled(self.config.cv.NETWORK_AUTO_CONNECT.is_modifiable())
|
||||
# self.autoconnect_cb.clicked.connect(self.set_server)
|
||||
# self.autoconnect_cb.clicked.connect(self.update)
|
||||
self.autoconnect_cb.setEnabled(self.config.cv.NETWORK_AUTO_CONNECT.is_modifiable())
|
||||
msg = ' '.join([
|
||||
_("If auto-connect is enabled, Electrum will always use a server that is on the longest blockchain."),
|
||||
_("If it is disabled, you have to choose a server you want to use. Electrum will warn you if your server is lagging.")
|
||||
@@ -576,7 +583,6 @@ class ServerWidget(QWidget):
|
||||
|
||||
self.server_e = QLineEdit()
|
||||
self.server_e.setFixedWidth(fixed_width_hostname + fixed_width_port)
|
||||
# self.server_e.editingFinished.connect(self.set_server)
|
||||
msg = _("Electrum sends your wallet addresses to a single server, in order to receive your transaction history.")
|
||||
grid.addWidget(QLabel(_('Server') + ':'), 2, 0)
|
||||
grid.addWidget(self.server_e, 2, 1, 1, 3)
|
||||
@@ -591,16 +597,46 @@ class ServerWidget(QWidget):
|
||||
self.split_label = QLabel('')
|
||||
grid.addWidget(self.split_label, 4, 0, 1, 3)
|
||||
|
||||
self.nodes_list_widget = NodesListWidget(self)
|
||||
grid.addWidget(self.nodes_list_widget, 6, 0, 1, 5)
|
||||
self.layout().addLayout(grid)
|
||||
|
||||
# vbox = QVBoxLayout()
|
||||
# vbox.addWidget(tabs)
|
||||
# self.layout_ = vbox
|
||||
# # tor detector
|
||||
# self.td = td = TorDetector()
|
||||
# td.found_proxy.connect(self.suggest_proxy)
|
||||
# td.start()
|
||||
self.nodes_list_widget = NodesListWidget()
|
||||
self.nodes_list_widget.followServer.connect(self.follow_server)
|
||||
self.nodes_list_widget.followChain.connect(self.follow_branch)
|
||||
|
||||
# self.fill_in_proxy_settings()
|
||||
# self.update()
|
||||
def do_set_server(server):
|
||||
self.server_e.setText(server)
|
||||
self.set_server()
|
||||
self.nodes_list_widget.setServer.connect(do_set_server)
|
||||
|
||||
|
||||
self.layout().addWidget(self.nodes_list_widget)
|
||||
self.nodes_list_widget.update(network=self.network,
|
||||
servers=self.network.get_servers())
|
||||
|
||||
self.register_callbacks()
|
||||
self.destroyed.connect(lambda: self.unregister_callbacks())
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_network_updated(self):
|
||||
self.nodes_list_widget.update(network=self.network, servers=self.network.get_servers())
|
||||
|
||||
def follow_branch(self, chain_id):
|
||||
self.network.run_from_another_thread(self.network.follow_chain_given_id(chain_id))
|
||||
self.update()
|
||||
|
||||
def follow_server(self, server: ServerAddr):
|
||||
self.server_e.setText(str(server))
|
||||
self.network.run_from_another_thread(self.network.follow_chain_given_server(server))
|
||||
self.update()
|
||||
|
||||
def set_server(self):
|
||||
net_params = self.network.get_parameters()
|
||||
try:
|
||||
server = ServerAddr.from_str_with_inference(str(self.server_e.text()))
|
||||
if not server:
|
||||
raise Exception("failed to parse server")
|
||||
except Exception:
|
||||
return
|
||||
net_params = net_params._replace(server=server,
|
||||
auto_connect=self.autoconnect_cb.isChecked())
|
||||
self.network.run_from_another_thread(self.network.set_parameters(net_params))
|
||||
|
||||
@@ -46,16 +46,7 @@ class WCAutoConnect(WizardComponent):
|
||||
self._valid = True
|
||||
|
||||
def apply(self):
|
||||
r = self.choice_w.selected_index
|
||||
self.wizard_data['autoconnect'] = (r == 0)
|
||||
# if r == 1:
|
||||
# nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
|
||||
# if self.exec_layout(nlayout.layout()):
|
||||
# nlayout.accept()
|
||||
# self.config.NETWORK_AUTO_CONNECT = network.auto_connect
|
||||
# else:
|
||||
# network.auto_connect = True
|
||||
# self.config.NETWORK_AUTO_CONNECT = True
|
||||
self.wizard_data['autoconnect'] = (self.choice_w.selected_item[0] == 'autoconnect')
|
||||
|
||||
|
||||
class WCProxyAsk(WizardComponent):
|
||||
@@ -70,29 +61,31 @@ class WCProxyAsk(WizardComponent):
|
||||
self._valid = True
|
||||
|
||||
def apply(self):
|
||||
r = self.choice_w.selected_index
|
||||
self.wizard_data['want_proxy'] = (r == 0)
|
||||
self.wizard_data['want_proxy'] = (self.choice_w.selected_item[0] == 'yes')
|
||||
|
||||
|
||||
class WCProxyConfig(WizardComponent):
|
||||
def __init__(self, parent, wizard):
|
||||
WizardComponent.__init__(self, parent, wizard, title=_("Proxy"))
|
||||
pw = ProxyWidget(self)
|
||||
self.layout().addWidget(pw)
|
||||
self.pw = ProxyWidget(self)
|
||||
self.pw.proxy_cb.setChecked(True)
|
||||
self.pw.proxy_host.setText('localhost')
|
||||
self.pw.proxy_port.setText('9050')
|
||||
self.layout().addWidget(self.pw)
|
||||
self.layout().addStretch(1)
|
||||
self._valid = True
|
||||
|
||||
def apply(self):
|
||||
# TODO
|
||||
pass
|
||||
self.wizard_data['proxy'] = self.pw.get_proxy_settings()
|
||||
|
||||
|
||||
class WCServerConfig(WizardComponent):
|
||||
def __init__(self, parent, wizard):
|
||||
WizardComponent.__init__(self, parent, wizard, title=_("Server"))
|
||||
sw = ServerWidget(self)
|
||||
self.layout().addWidget(sw)
|
||||
self.layout().addStretch(1)
|
||||
self.sw = ServerWidget(wizard._daemon.network, self)
|
||||
self.layout().addWidget(self.sw)
|
||||
self._valid = True
|
||||
|
||||
def apply(self):
|
||||
# TODO
|
||||
pass
|
||||
self.wizard_data['autoconnect'] = self.sw.autoconnect_cb.isChecked()
|
||||
self.wizard_data['server'] = self.sw.server_e.text()
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
from typing import List, NamedTuple, Any, Dict, Optional, Tuple
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.interface import ServerAddr
|
||||
from electrum.keystore import hardware_keystore
|
||||
from electrum.logging import get_logger
|
||||
from electrum.plugin import run_hook
|
||||
@@ -647,25 +648,57 @@ class ServerConnectWizard(AbstractWizard):
|
||||
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
def __init__(self, daemon):
|
||||
def __init__(self, daemon: 'Daemon'):
|
||||
AbstractWizard.__init__(self)
|
||||
self.navmap = {
|
||||
'autoconnect': {
|
||||
'next': 'server_config',
|
||||
'accept': self.do_configure_autoconnect,
|
||||
'last': lambda d: d['autoconnect']
|
||||
},
|
||||
'proxy_ask': {
|
||||
'next': lambda d: 'proxy_config' if d['want_proxy'] else 'autoconnect'
|
||||
},
|
||||
'proxy_config': {
|
||||
'next': 'autoconnect'
|
||||
'next': 'autoconnect',
|
||||
'accept': self.do_configure_proxy
|
||||
},
|
||||
'server_config': {
|
||||
'accept': self.do_configure_server,
|
||||
'last': True
|
||||
}
|
||||
}
|
||||
self._daemon = daemon
|
||||
|
||||
def do_configure_proxy(self, wizard_data):
|
||||
proxy_settings = wizard_data['proxy']
|
||||
if not self._daemon.network:
|
||||
self._logger.debug('not configuring proxy, electrum config wants offline mode')
|
||||
return
|
||||
self._logger.debug(f'configuring proxy: {proxy_settings!r}')
|
||||
net_params = self._daemon.network.get_parameters()
|
||||
if not proxy_settings['enabled']:
|
||||
proxy_settings = None
|
||||
net_params = net_params._replace(proxy=proxy_settings)
|
||||
self._daemon.network.run_from_another_thread(self._daemon.network.set_parameters(net_params))
|
||||
|
||||
def do_configure_server(self, wizard_data):
|
||||
self._logger.debug(f'configuring server: {wizard_data!r}')
|
||||
net_params = self._daemon.network.get_parameters()
|
||||
try:
|
||||
server = ServerAddr.from_str_with_inference(wizard_data['server'])
|
||||
if not server:
|
||||
raise Exception('failed to parse server %s' % wizard_data['server'])
|
||||
except Exception:
|
||||
return
|
||||
net_params = net_params._replace(server=server, auto_connect=wizard_data['autoconnect'])
|
||||
self._daemon.network.run_from_another_thread(self._daemon.network.set_parameters(net_params))
|
||||
|
||||
def do_configure_autoconnect(self, wizard_data):
|
||||
self._logger.debug(f'configuring autoconnect: {wizard_data!r}')
|
||||
if self._daemon.config.cv.NETWORK_AUTO_CONNECT.is_modifiable():
|
||||
self._daemon.config.NETWORK_AUTO_CONNECT = wizard_data['autoconnect']
|
||||
|
||||
def start(self, initial_data=None):
|
||||
if initial_data is None:
|
||||
initial_data = {}
|
||||
|
||||
Reference in New Issue
Block a user