qml: add serverlistmodel.py, add server list to ServerConfigDialog.qml
This commit is contained in:
@@ -154,6 +154,7 @@ Pane {
|
|||||||
text: qsTr('disabled');
|
text: qsTr('disabled');
|
||||||
visible: !Config.useGossip
|
visible: !Config.useGossip
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,14 +30,44 @@ ElDialog {
|
|||||||
height: parent.height
|
height: parent.height
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
ServerConfig {
|
ColumnLayout {
|
||||||
id: serverconfig
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
Layout.leftMargin: constants.paddingLarge
|
Layout.leftMargin: constants.paddingLarge
|
||||||
Layout.rightMargin: constants.paddingLarge
|
Layout.rightMargin: constants.paddingLarge
|
||||||
}
|
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; Layout.preferredWidth: 1 }
|
ServerConfig {
|
||||||
|
id: serverconfig
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr('Servers')
|
||||||
|
font.pixelSize: constants.fontSizeLarge
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 1
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame {
|
||||||
|
background: PaneInsetBackground { baseColor: Material.dialogColor }
|
||||||
|
|
||||||
|
verticalPadding: 0
|
||||||
|
horizontalPadding: 0
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
model: Network.serverListModel
|
||||||
|
delegate: ServerDelegate { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -2,25 +2,26 @@ import QtQuick 2.6
|
|||||||
import QtQuick.Controls.Material 2.0
|
import QtQuick.Controls.Material 2.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
property color baseColor: Material.background
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||||
height: 1
|
height: 1
|
||||||
color: Qt.darker(Material.background, 1.50)
|
color: Qt.darker(baseColor, 1.50)
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
|
anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
|
||||||
width: 1
|
width: 1
|
||||||
color: Qt.darker(Material.background, 1.50)
|
color: Qt.darker(baseColor, 1.50)
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
|
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
|
||||||
height: 1
|
height: 1
|
||||||
color: Qt.lighter(Material.background, 1.50)
|
color: Qt.lighter(baseColor, 1.50)
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors { right: parent.right; top: parent.top; bottom: parent.bottom }
|
anchors { right: parent.right; top: parent.top; bottom: parent.bottom }
|
||||||
width: 1
|
width: 1
|
||||||
color: Qt.lighter(Material.background, 1.50)
|
color: Qt.lighter(baseColor, 1.50)
|
||||||
}
|
}
|
||||||
color: Qt.darker(Material.background, 1.15)
|
color: Qt.darker(baseColor, 1.15)
|
||||||
}
|
}
|
||||||
|
|||||||
29
electrum/gui/qml/components/controls/ServerDelegate.qml
Normal file
29
electrum/gui/qml/components/controls/ServerDelegate.qml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import QtQuick 2.6
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
import QtQuick.Controls.Material 2.0
|
||||||
|
|
||||||
|
import org.electrum 1.0
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
id: root
|
||||||
|
height: itemLayout.height
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: itemLayout
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: constants.paddingSmall
|
||||||
|
rightMargin: constants.paddingSmall
|
||||||
|
}
|
||||||
|
columns: 2
|
||||||
|
Label {
|
||||||
|
text: model.address
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: model.chain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,14 @@ from electrum import constants
|
|||||||
from electrum.interface import ServerAddr
|
from electrum.interface import ServerAddr
|
||||||
|
|
||||||
from .util import QtEventListener, event_listener
|
from .util import QtEventListener, event_listener
|
||||||
|
from .qeserverlistmodel import QEServerListModel
|
||||||
|
|
||||||
class QENetwork(QObject, QtEventListener):
|
class QENetwork(QObject, QtEventListener):
|
||||||
def __init__(self, network, qeconfig, parent=None):
|
def __init__(self, network, qeconfig, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.network = network
|
self.network = network
|
||||||
self._qeconfig = qeconfig
|
self._qeconfig = qeconfig
|
||||||
|
self._serverListModel = None
|
||||||
self._height = network.get_local_height() # init here, update event can take a while
|
self._height = network.get_local_height() # init here, update event can take a while
|
||||||
self.register_callbacks()
|
self.register_callbacks()
|
||||||
|
|
||||||
@@ -186,3 +188,10 @@ class QENetwork(QObject, QtEventListener):
|
|||||||
'db_channels': self._gossipDbChannels ,
|
'db_channels': self._gossipDbChannels ,
|
||||||
'db_policies': self._gossipDbPolicies
|
'db_policies': self._gossipDbPolicies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serverListModelChanged = pyqtSignal()
|
||||||
|
@pyqtProperty(QEServerListModel, notify=serverListModelChanged)
|
||||||
|
def serverListModel(self):
|
||||||
|
if self._serverListModel is None:
|
||||||
|
self._serverListModel = QEServerListModel(self.network)
|
||||||
|
return self._serverListModel
|
||||||
|
|||||||
133
electrum/gui/qml/qeserverlistmodel.py
Normal file
133
electrum/gui/qml/qeserverlistmodel.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
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__)
|
||||||
|
_chaintips = 0
|
||||||
|
|
||||||
|
# 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.network = network
|
||||||
|
self.init_model()
|
||||||
|
self.register_callbacks()
|
||||||
|
self.destroyed.connect(lambda: self.unregister_callbacks())
|
||||||
|
|
||||||
|
@event_listener
|
||||||
|
def on_event_network_updated(self):
|
||||||
|
self._logger.info(f'network updated')
|
||||||
|
self.init_model()
|
||||||
|
|
||||||
|
@event_listener
|
||||||
|
def on_event_blockchain_updated(self):
|
||||||
|
self._logger.info(f'blockchain updated')
|
||||||
|
self.init_model()
|
||||||
|
|
||||||
|
@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()
|
||||||
|
|
||||||
|
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)}')
|
||||||
|
self.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
|
||||||
|
use_tor = True
|
||||||
|
for _host, d in sorted(all_servers.items()):
|
||||||
|
if _host in connected_hosts:
|
||||||
|
continue
|
||||||
|
if _host.endswith('.onion') and not use_tor:
|
||||||
|
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.servers.append(server)
|
||||||
|
|
||||||
Reference in New Issue
Block a user