|
|
@ -56,7 +56,7 @@ class NetworkDialog(QDialog): |
|
|
|
def __init__(self, network, config, network_updated_signal_obj): |
|
|
|
QDialog.__init__(self) |
|
|
|
self.setWindowTitle(_('Network')) |
|
|
|
self.setMinimumSize(500, 300) |
|
|
|
self.setMinimumSize(500, 500) |
|
|
|
self.nlayout = NetworkChoiceLayout(network, config) |
|
|
|
self.network_updated_signal_obj = network_updated_signal_obj |
|
|
|
vbox = QVBoxLayout(self) |
|
|
@ -79,12 +79,18 @@ class NodesListWidget(QTreeWidget): |
|
|
|
|
|
|
|
SERVER_ADDR_ROLE = Qt.UserRole + 100 |
|
|
|
CHAIN_ID_ROLE = Qt.UserRole + 101 |
|
|
|
IS_SERVER_ROLE = Qt.UserRole + 102 |
|
|
|
ITEMTYPE_ROLE = Qt.UserRole + 102 |
|
|
|
|
|
|
|
class ItemType(IntEnum): |
|
|
|
CHAIN = 0 |
|
|
|
CONNECTED_SERVER = 1 |
|
|
|
DISCONNECTED_SERVER = 2 |
|
|
|
TOPLEVEL = 3 |
|
|
|
|
|
|
|
def __init__(self, parent): |
|
|
|
QTreeWidget.__init__(self) |
|
|
|
self.parent = parent # type: NetworkChoiceLayout |
|
|
|
self.setHeaderLabels([_('Connected node'), _('Height')]) |
|
|
|
self.setHeaderLabels([_('Server'), _('Height')]) |
|
|
|
self.setContextMenuPolicy(Qt.CustomContextMenu) |
|
|
|
self.customContextMenuRequested.connect(self.create_menu) |
|
|
|
|
|
|
@ -92,14 +98,22 @@ class NodesListWidget(QTreeWidget): |
|
|
|
item = self.currentItem() |
|
|
|
if not item: |
|
|
|
return |
|
|
|
is_server = bool(item.data(0, self.IS_SERVER_ROLE)) |
|
|
|
item_type = item.data(0, self.ITEMTYPE_ROLE) |
|
|
|
menu = QMenu() |
|
|
|
if is_server: |
|
|
|
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)) |
|
|
|
else: |
|
|
|
elif item_type == self.ItemType.DISCONNECTED_SERVER: |
|
|
|
server = item.data(0, self.SERVER_ADDR_ROLE) # type: ServerAddr |
|
|
|
def func(): |
|
|
|
self.parent.server_e.setText(server.net_addr_str()) |
|
|
|
self.parent.set_server() |
|
|
|
menu.addAction(_("Use as server"), func) |
|
|
|
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)) |
|
|
|
else: |
|
|
|
return |
|
|
|
menu.exec_(self.viewport().mapToGlobal(position)) |
|
|
|
|
|
|
|
def keyPressEvent(self, event): |
|
|
@ -114,9 +128,12 @@ class NodesListWidget(QTreeWidget): |
|
|
|
pt.setX(50) |
|
|
|
self.customContextMenuRequested.emit(pt) |
|
|
|
|
|
|
|
def update(self, network: Network): |
|
|
|
def update(self, *, network: Network, servers: dict, use_tor: bool): |
|
|
|
self.clear() |
|
|
|
self.addChild = self.addTopLevelItem |
|
|
|
|
|
|
|
# connected servers |
|
|
|
connected_servers_item = QTreeWidgetItem([_("Connected nodes"), '']) |
|
|
|
connected_servers_item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.TOPLEVEL) |
|
|
|
chains = network.get_blockchains() |
|
|
|
n_chains = len(chains) |
|
|
|
for chain_id, interfaces in chains.items(): |
|
|
@ -125,87 +142,51 @@ class NodesListWidget(QTreeWidget): |
|
|
|
name = b.get_name() |
|
|
|
if n_chains > 1: |
|
|
|
x = QTreeWidgetItem([name + '@%d'%b.get_max_forkpoint(), '%d'%b.height()]) |
|
|
|
x.setData(0, self.IS_SERVER_ROLE, 0) |
|
|
|
x.setData(0, self.ITEMTYPE_ROLE, self.ItemType.CHAIN) |
|
|
|
x.setData(0, self.CHAIN_ID_ROLE, b.get_id()) |
|
|
|
else: |
|
|
|
x = self |
|
|
|
x = connected_servers_item |
|
|
|
for i in interfaces: |
|
|
|
star = ' *' if i == network.interface else '' |
|
|
|
item = QTreeWidgetItem([i.host + star, '%d'%i.tip]) |
|
|
|
item.setData(0, self.IS_SERVER_ROLE, 1) |
|
|
|
item = QTreeWidgetItem([f"{i.server.net_addr_str()}" + star, '%d'%i.tip]) |
|
|
|
item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.CONNECTED_SERVER) |
|
|
|
item.setData(0, self.SERVER_ADDR_ROLE, i.server) |
|
|
|
item.setToolTip(0, str(i.server)) |
|
|
|
x.addChild(item) |
|
|
|
if n_chains > 1: |
|
|
|
self.addTopLevelItem(x) |
|
|
|
x.setExpanded(True) |
|
|
|
connected_servers_item.addChild(x) |
|
|
|
|
|
|
|
h = self.header() |
|
|
|
h.setStretchLastSection(False) |
|
|
|
h.setSectionResizeMode(0, QHeaderView.Stretch) |
|
|
|
h.setSectionResizeMode(1, QHeaderView.ResizeToContents) |
|
|
|
|
|
|
|
super().update() |
|
|
|
|
|
|
|
|
|
|
|
class ServerListWidget(QTreeWidget): |
|
|
|
"""List of all known servers.""" |
|
|
|
|
|
|
|
class Columns(IntEnum): |
|
|
|
HOST = 0 |
|
|
|
PORT = 1 |
|
|
|
|
|
|
|
SERVER_ADDR_ROLE = Qt.UserRole + 100 |
|
|
|
|
|
|
|
def __init__(self, parent): |
|
|
|
QTreeWidget.__init__(self) |
|
|
|
self.parent = parent # type: NetworkChoiceLayout |
|
|
|
self.setHeaderLabels([_('Host'), _('Port')]) |
|
|
|
self.setContextMenuPolicy(Qt.CustomContextMenu) |
|
|
|
self.customContextMenuRequested.connect(self.create_menu) |
|
|
|
|
|
|
|
def create_menu(self, position): |
|
|
|
item = self.currentItem() |
|
|
|
if not item: |
|
|
|
return |
|
|
|
menu = QMenu() |
|
|
|
server = item.data(self.Columns.HOST, self.SERVER_ADDR_ROLE) |
|
|
|
menu.addAction(_("Use as server"), lambda: self.set_server(server)) |
|
|
|
menu.exec_(self.viewport().mapToGlobal(position)) |
|
|
|
|
|
|
|
def set_server(self, server: ServerAddr): |
|
|
|
self.parent.server_e.setText(server.net_addr_str()) |
|
|
|
self.parent.set_server() |
|
|
|
|
|
|
|
def keyPressEvent(self, event): |
|
|
|
if event.key() in [ Qt.Key_F2, Qt.Key_Return ]: |
|
|
|
self.on_activated(self.currentItem(), self.currentColumn()) |
|
|
|
else: |
|
|
|
QTreeWidget.keyPressEvent(self, event) |
|
|
|
|
|
|
|
def on_activated(self, item, column): |
|
|
|
# on 'enter' we show the menu |
|
|
|
pt = self.visualItemRect(item).bottomLeft() |
|
|
|
pt.setX(50) |
|
|
|
self.customContextMenuRequested.emit(pt) |
|
|
|
|
|
|
|
def update(self, servers, use_tor): |
|
|
|
self.clear() |
|
|
|
# disconnected servers |
|
|
|
disconnected_servers_item = QTreeWidgetItem([_("Other known servers"), ""]) |
|
|
|
disconnected_servers_item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.TOPLEVEL) |
|
|
|
connected_hosts = set([iface.host for ifaces in chains.values() for iface in ifaces]) |
|
|
|
protocol = PREFERRED_NETWORK_PROTOCOL |
|
|
|
for _host, d in sorted(servers.items()): |
|
|
|
if _host in connected_hosts: |
|
|
|
continue |
|
|
|
if _host.endswith('.onion') and not use_tor: |
|
|
|
continue |
|
|
|
port = d.get(protocol) |
|
|
|
if port: |
|
|
|
x = QTreeWidgetItem([_host, port]) |
|
|
|
server = ServerAddr(_host, port, protocol=protocol) |
|
|
|
x.setData(self.Columns.HOST, self.SERVER_ADDR_ROLE, server) |
|
|
|
self.addTopLevelItem(x) |
|
|
|
item = QTreeWidgetItem([server.net_addr_str(), ""]) |
|
|
|
item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.DISCONNECTED_SERVER) |
|
|
|
item.setData(0, self.SERVER_ADDR_ROLE, server) |
|
|
|
disconnected_servers_item.addChild(item) |
|
|
|
|
|
|
|
self.addTopLevelItem(connected_servers_item) |
|
|
|
self.addTopLevelItem(disconnected_servers_item) |
|
|
|
|
|
|
|
connected_servers_item.setExpanded(True) |
|
|
|
for i in range(connected_servers_item.childCount()): |
|
|
|
connected_servers_item.child(i).setExpanded(True) |
|
|
|
disconnected_servers_item.setExpanded(True) |
|
|
|
|
|
|
|
# headers |
|
|
|
h = self.header() |
|
|
|
h.setStretchLastSection(False) |
|
|
|
h.setSectionResizeMode(self.Columns.HOST, QHeaderView.Stretch) |
|
|
|
h.setSectionResizeMode(self.Columns.PORT, QHeaderView.ResizeToContents) |
|
|
|
h.setSectionResizeMode(0, QHeaderView.Stretch) |
|
|
|
h.setSectionResizeMode(1, QHeaderView.ResizeToContents) |
|
|
|
|
|
|
|
super().update() |
|
|
|
|
|
|
@ -218,44 +199,14 @@ class NetworkChoiceLayout(object): |
|
|
|
self.tor_proxy = None |
|
|
|
|
|
|
|
self.tabs = tabs = QTabWidget() |
|
|
|
server_tab = QWidget() |
|
|
|
proxy_tab = QWidget() |
|
|
|
blockchain_tab = QWidget() |
|
|
|
tabs.addTab(blockchain_tab, _('Overview')) |
|
|
|
tabs.addTab(server_tab, _('Server')) |
|
|
|
tabs.addTab(proxy_tab, _('Proxy')) |
|
|
|
|
|
|
|
fixed_width_hostname = 24 * char_width_in_lineedit() |
|
|
|
fixed_width_port = 6 * char_width_in_lineedit() |
|
|
|
|
|
|
|
# server tab |
|
|
|
grid = QGridLayout(server_tab) |
|
|
|
grid.setSpacing(8) |
|
|
|
|
|
|
|
self.server_e = QLineEdit() |
|
|
|
self.server_e.setFixedWidth(fixed_width_hostname + fixed_width_port) |
|
|
|
self.autoconnect_cb = QCheckBox(_('Select server automatically')) |
|
|
|
self.autoconnect_cb.setEnabled(self.config.is_modifiable('auto_connect')) |
|
|
|
|
|
|
|
self.server_e.editingFinished.connect(self.set_server) |
|
|
|
self.autoconnect_cb.clicked.connect(self.set_server) |
|
|
|
self.autoconnect_cb.clicked.connect(self.update) |
|
|
|
|
|
|
|
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.") |
|
|
|
]) |
|
|
|
grid.addWidget(self.autoconnect_cb, 0, 0, 1, 3) |
|
|
|
grid.addWidget(HelpButton(msg), 0, 4) |
|
|
|
|
|
|
|
grid.addWidget(QLabel(_('Server') + ':'), 1, 0) |
|
|
|
grid.addWidget(self.server_e, 1, 1, 1, 3) |
|
|
|
|
|
|
|
label = _('Server peers') if network.is_connected() else _('Default Servers') |
|
|
|
grid.addWidget(QLabel(label), 2, 0, 1, 5) |
|
|
|
self.servers_list = ServerListWidget(self) |
|
|
|
grid.addWidget(self.servers_list, 3, 0, 1, 5) |
|
|
|
|
|
|
|
# Proxy tab |
|
|
|
grid = QGridLayout(proxy_tab) |
|
|
|
grid.setSpacing(8) |
|
|
@ -315,23 +266,36 @@ class NetworkChoiceLayout(object): |
|
|
|
grid.addWidget(self.status_label, 0, 1, 1, 3) |
|
|
|
grid.addWidget(HelpButton(msg), 0, 4) |
|
|
|
|
|
|
|
self.server_label = QLabel('') |
|
|
|
msg = _("Electrum sends your wallet addresses to a single server, in order to receive your transaction history.") |
|
|
|
grid.addWidget(QLabel(_('Server') + ':'), 1, 0) |
|
|
|
grid.addWidget(self.server_label, 1, 1, 1, 3) |
|
|
|
self.autoconnect_cb = QCheckBox(_('Select server automatically')) |
|
|
|
self.autoconnect_cb.setEnabled(self.config.is_modifiable('auto_connect')) |
|
|
|
self.autoconnect_cb.clicked.connect(self.set_server) |
|
|
|
self.autoconnect_cb.clicked.connect(self.update) |
|
|
|
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.") |
|
|
|
]) |
|
|
|
grid.addWidget(self.autoconnect_cb, 1, 0, 1, 3) |
|
|
|
grid.addWidget(HelpButton(msg), 1, 4) |
|
|
|
|
|
|
|
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) |
|
|
|
grid.addWidget(HelpButton(msg), 2, 4) |
|
|
|
|
|
|
|
self.height_label = QLabel('') |
|
|
|
msg = _('This is the height of your local copy of the blockchain.') |
|
|
|
grid.addWidget(QLabel(_('Blockchain') + ':'), 2, 0) |
|
|
|
grid.addWidget(self.height_label, 2, 1) |
|
|
|
grid.addWidget(HelpButton(msg), 2, 4) |
|
|
|
grid.addWidget(QLabel(_('Blockchain') + ':'), 3, 0) |
|
|
|
grid.addWidget(self.height_label, 3, 1) |
|
|
|
grid.addWidget(HelpButton(msg), 3, 4) |
|
|
|
|
|
|
|
self.split_label = QLabel('') |
|
|
|
grid.addWidget(self.split_label, 3, 0, 1, 3) |
|
|
|
grid.addWidget(self.split_label, 4, 0, 1, 3) |
|
|
|
|
|
|
|
self.nodes_list_widget = NodesListWidget(self) |
|
|
|
grid.addWidget(self.nodes_list_widget, 5, 0, 1, 5) |
|
|
|
grid.addWidget(self.nodes_list_widget, 6, 0, 1, 5) |
|
|
|
|
|
|
|
vbox = QVBoxLayout() |
|
|
|
vbox.addWidget(tabs) |
|
|
@ -354,27 +318,18 @@ class NetworkChoiceLayout(object): |
|
|
|
if self.config.is_modifiable('server'): |
|
|
|
enabled = not self.autoconnect_cb.isChecked() |
|
|
|
self.server_e.setEnabled(enabled) |
|
|
|
self.servers_list.setEnabled(enabled) |
|
|
|
else: |
|
|
|
for w in [self.autoconnect_cb, self.server_e, self.servers_list]: |
|
|
|
for w in [self.autoconnect_cb, self.server_e, self.nodes_list_widget]: |
|
|
|
w.setEnabled(False) |
|
|
|
|
|
|
|
def update(self): |
|
|
|
net_params = self.network.get_parameters() |
|
|
|
server = net_params.server |
|
|
|
proxy_config, auto_connect = net_params.proxy, net_params.auto_connect |
|
|
|
auto_connect = net_params.auto_connect |
|
|
|
if not self.server_e.hasFocus(): |
|
|
|
self.server_e.setText(server.net_addr_str()) |
|
|
|
self.autoconnect_cb.setChecked(auto_connect) |
|
|
|
|
|
|
|
interface = self.network.interface |
|
|
|
host = interface.host if interface else _('None') |
|
|
|
self.server_label.setText(host) |
|
|
|
|
|
|
|
self.servers = self.network.get_servers() |
|
|
|
self.servers_list.update(self.servers, self.tor_cb.isChecked()) |
|
|
|
self.enable_set_server() |
|
|
|
|
|
|
|
height_str = "%d "%(self.network.get_local_height()) + _('blocks') |
|
|
|
self.height_label.setText(height_str) |
|
|
|
n = len(self.network.get_interfaces()) |
|
|
@ -391,7 +346,10 @@ class NetworkChoiceLayout(object): |
|
|
|
else: |
|
|
|
msg = '' |
|
|
|
self.split_label.setText(msg) |
|
|
|
self.nodes_list_widget.update(self.network) |
|
|
|
self.nodes_list_widget.update(network=self.network, |
|
|
|
servers=self.network.get_servers(), |
|
|
|
use_tor=self.tor_cb.isChecked()) |
|
|
|
self.enable_set_server() |
|
|
|
|
|
|
|
def fill_in_proxy_settings(self): |
|
|
|
proxy_config = self.network.get_parameters().proxy |
|
|
|