URI: 
       tMerge pull request #6114 from SomberNight/202004_qt_network_dialog - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 853f42dbbb8a7fb9506e11740c039c5b78b921b2
   DIR parent bf223470ce607a762700f144a6a0f4cb0205cf54
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sat, 25 Apr 2020 10:33:55 +0200
       
       Merge pull request #6114 from SomberNight/202004_qt_network_dialog
       
       qt network dialog: merge "Overview" and "Servers" tabs
       Diffstat:
         M electrum/gui/qt/network_dialog.py   |     200 ++++++++++++-------------------
       
       1 file changed, 79 insertions(+), 121 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/network_dialog.py b/electrum/gui/qt/network_dialog.py
       t@@ -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)
       t@@ -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)
        
       t@@ -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):
       t@@ -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():
       t@@ -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()
        
       t@@ -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)
       t@@ -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)
       t@@ -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())
       t@@ -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