Browse Source

Refactor Network and Blockchain dialogs in qt and kivy

2.9.x
ThomasV 8 years ago
parent
commit
568c14ca78
  1. 35
      gui/kivy/uix/dialogs/blockchain_dialog.py
  2. 6
      gui/kivy/uix/dialogs/settings.py
  3. 2
      gui/qt/main_window.py
  4. 241
      gui/qt/network_dialog.py
  5. 9
      lib/network.py

35
gui/kivy/uix/dialogs/checkpoint_dialog.py → gui/kivy/uix/dialogs/blockchain_dialog.py

@ -8,7 +8,7 @@ from electrum.i18n import _
Builder.load_string('''
#:import _ electrum_gui.kivy.i18n._
<CheckpointDialog@Popup>
<BlockchainDialog@Popup>
id: popup
title: _('Blockchain')
size_hint: 1, 1
@ -30,39 +30,12 @@ Builder.load_string('''
Widget:
size_hint: 1, 0.1
TopLabel:
text: _("In order to verify the history returned by your main server, Electrum downloads block headers from random nodes. These headers are then used to check that transactions sent by the server really are in the blockchain.")
text: _("Electrum connects to several nodes in order to download block headers and find out the longest blockchain.") + _("This blockchain is used to verify the transactions sent by your transaction server.")
font_size: '6pt'
Widget:
size_hint: 1, 0.1
GridLayout:
orientation: 'horizontal'
cols: 2
height: '36dp'
TopLabel:
text: _('Checkpoint') + ':'
height: '36dp'
TextInput:
id: height_input
multiline: False
input_type: 'number'
height: '36dp'
size_hint_y: None
text: '%d'%root.cp_height
TopLabel:
text: _('Block hash') + ':'
TxHashLabel:
data: root.cp_value
Widget:
size_hint: 1, 0.1
Label:
font_size: '6pt'
text: _('If there is a fork of the blockchain, you need to configure your checkpoint in order to make sure that you are on the correct side of the fork. Enter a block number to fetch a checkpoint from your main server, and check its value from independent sources.')
halign: 'left'
text_size: self.width, None
size: self.texture_size
Widget:
size_hint: 1, 0.3
BoxLayout:
orientation: 'horizontal'
size_hint: 1, 0.2
@ -80,9 +53,11 @@ Builder.load_string('''
popup.dismiss()
''')
class CheckpointDialog(Factory.Popup):
class BlockchainDialog(Factory.Popup):
def __init__(self, network, callback):
Factory.Popup.__init__(self)
self.network = network
self.callback = callback
self.is_split = len(self.network.blockchains) > 1
self.checkpoint_height = network.get_checkpoint()

6
gui/kivy/uix/dialogs/settings.py

@ -123,7 +123,7 @@ Builder.load_string('''
SettingsItem:
status: "%d blocks"% app.num_blocks
title: _('Blockchain') + ': ' + self.status
description: _("Configure checkpoints")
description: _("Blockchain status")
action: partial(root.blockchain_dialog, self)
''')
@ -192,12 +192,12 @@ class SettingsDialog(Factory.Popup):
self._coinselect_dialog.open()
def blockchain_dialog(self, item, dt):
from checkpoint_dialog import CheckpointDialog
from blockchain_dialog import BlockchainDialog
if self._blockchain_dialog is None:
def callback(height, value):
if value:
self.app.network.blockchain.set_checkpoint(height, value)
self._blockchain_dialog = CheckpointDialog(self.app.network, callback)
self._blockchain_dialog = BlockchainDialog(self.app.network, callback)
self._blockchain_dialog.open()
def proxy_status(self):

2
gui/qt/main_window.py

@ -265,6 +265,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def on_network(self, event, *args):
if event == 'updated':
self.need_update.set()
self.emit(QtCore.SIGNAL('updated'), event, *args)
elif event == 'new_transaction':
self.tx_notifications.append(args[0])
elif event in ['status', 'banner', 'verified', 'fee']:

241
gui/qt/network_dialog.py

@ -26,6 +26,7 @@ import socket
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import PyQt4.QtCore as QtCore
from electrum.i18n import _
from electrum.network import DEFAULT_PORTS
@ -43,14 +44,18 @@ class NetworkDialog(WindowModalDialog):
self.nlayout = NetworkChoiceLayout(network, config)
vbox = QVBoxLayout(self)
vbox.addLayout(self.nlayout.layout())
vbox.addLayout(Buttons(CancelButton(self), OkButton(self)))
vbox.addLayout(Buttons(CloseButton(self)))
self.connect(parent, QtCore.SIGNAL('updated'), self.on_update)
def do_exec(self):
result = self.exec_()
if result:
self.nlayout.accept()
#if result:
# self.nlayout.accept()
return result
def on_update(self):
self.nlayout.update()
class NodesListWidget(QTreeWidget):
@ -87,29 +92,47 @@ class NodesListWidget(QTreeWidget):
pt.setX(50)
self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt)
def update(self, network):
self.clear()
checkpoint = network.get_checkpoint()
n_chains = len(network.blockchains)
if n_chains > 1:
for b in network.blockchains.values():
name = network.get_blockchain_name(b)
x = QTreeWidgetItem([name, '%d'%checkpoint])
x.setData(0, Qt.UserRole, 1)
x.setData(1, Qt.UserRole, b.checkpoint)
for i in network.interfaces.values():
if i.blockchain == b:
item = QTreeWidgetItem([i.host, '%d'%i.tip])
item.setData(0, Qt.UserRole, 0)
item.setData(1, Qt.UserRole, i.server)
x.addChild(item)
self.addTopLevelItem(x)
x.setExpanded(True)
else:
for i in network.interfaces.values():
item = QTreeWidgetItem([i.host, '%d'%i.tip])
item.setData(0, Qt.UserRole, 0)
item.setData(1, Qt.UserRole, i.server)
self.addTopLevelItem(item)
h = self.header()
h.setStretchLastSection(False)
h.setResizeMode(0, QHeaderView.Stretch)
h.setResizeMode(1, QHeaderView.ResizeToContents)
class NetworkChoiceLayout(object):
def __init__(self, network, config, wizard=False):
self.network = network
self.config = config
self.protocol = None
self.tor_proxy = None
self.servers = network.get_servers()
host, port, protocol, proxy_config, auto_connect = network.get_parameters()
if not proxy_config:
proxy_config = { "mode":"none", "host":"localhost", "port":"9050"}
if not wizard:
if network.is_connected():
status = _("Server") + ": %s"%(host)
else:
status = _("Disconnected from server")
else:
status = _("Please choose a server.") + "\n" + _("Press 'Next' if you are offline.")
tabs = QTabWidget()
server_tab = QWidget()
proxy_tab = QWidget()
@ -122,22 +145,20 @@ class NetworkChoiceLayout(object):
grid = QGridLayout(server_tab)
grid.setSpacing(8)
# server
self.server_host = QLineEdit()
self.server_host.setFixedWidth(200)
self.server_port = QLineEdit()
self.server_port.setFixedWidth(60)
# use SSL
self.ssl_cb = QCheckBox(_('Use SSL'))
self.ssl_cb.setChecked(auto_connect)
self.ssl_cb.stateChanged.connect(self.change_protocol)
# auto connect
self.autoconnect_cb = QCheckBox(_('Select server automatically'))
self.autoconnect_cb.setChecked(auto_connect)
self.autoconnect_cb.setEnabled(self.config.is_modifiable('auto_connect'))
self.server_host.editingFinished.connect(self.set_server)
self.server_port.editingFinished.connect(self.set_server)
self.ssl_cb.stateChanged.connect(self.change_protocol)
self.autoconnect_cb.stateChanged.connect(self.set_server)
self.autoconnect_cb.clicked.connect(self.enable_set_server)
msg = _("Electrum sends your wallet addresses to a single server, in order to receive your transaction history.")
grid.addWidget(QLabel(_('Server') + ':'), 0, 0)
grid.addWidget(self.server_host, 0, 1, 1, 2)
@ -157,18 +178,6 @@ class NetworkChoiceLayout(object):
self.servers_list_widget.setColumnWidth(0, 240)
grid.addWidget(self.servers_list_widget, 3, 0, 1, 5)
def enable_set_server():
if config.is_modifiable('server'):
enabled = not self.autoconnect_cb.isChecked()
self.server_host.setEnabled(enabled)
self.server_port.setEnabled(enabled)
self.servers_list_widget.setEnabled(enabled)
else:
for w in [self.autoconnect_cb, self.server_host, self.server_port, self.ssl_cb, self.servers_list_widget]:
w.setEnabled(False)
self.autoconnect_cb.clicked.connect(enable_set_server)
enable_set_server()
# Proxy tab
grid = QGridLayout(proxy_tab)
@ -176,11 +185,11 @@ class NetworkChoiceLayout(object):
# proxy setting
self.proxy_mode = QComboBox()
self.proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
self.proxy_host = QLineEdit()
self.proxy_host.setFixedWidth(200)
self.proxy_port = QLineEdit()
self.proxy_port.setFixedWidth(60)
self.proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
self.proxy_user = QLineEdit()
self.proxy_user.setPlaceholderText(_("Proxy user"))
self.proxy_password = QLineEdit()
@ -188,21 +197,14 @@ class NetworkChoiceLayout(object):
self.proxy_password.setEchoMode(QLineEdit.Password)
self.proxy_password.setFixedWidth(60)
def check_for_disable(index = False):
if self.config.is_modifiable('proxy'):
for w in [self.proxy_host, self.proxy_port, self.proxy_user, self.proxy_password]:
w.setEnabled(self.proxy_mode.currentText() != 'NONE')
else:
for w in [self.proxy_host, self.proxy_port, self.proxy_mode]: w.setEnabled(False)
check_for_disable()
self.proxy_mode.connect(self.proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
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)
self.proxy_mode.setCurrentIndex(self.proxy_mode.findText(str(proxy_config.get("mode").upper())))
self.proxy_host.setText(proxy_config.get("host"))
self.proxy_port.setText(proxy_config.get("port"))
self.proxy_user.setText(proxy_config.get("user", ""))
self.proxy_password.setText(proxy_config.get("password", ""))
self.check_disable_proxy()
self.proxy_mode.connect(self.proxy_mode, SIGNAL('currentIndexChanged(int)'), self.check_disable_proxy)
self.proxy_mode.connect(self.proxy_mode, SIGNAL('currentIndexChanged(int)'), self.proxy_settings_changed)
self.proxy_host.connect(self.proxy_host, SIGNAL('textEdited(QString)'), self.proxy_settings_changed)
@ -224,62 +226,24 @@ class NetworkChoiceLayout(object):
grid.setRowStretch(6, 1)
# Blockchain Tab
from electrum import bitcoin
from amountedit import AmountEdit
grid = QGridLayout(blockchain_tab)
n = len(network.get_interfaces())
n_chains = len(network.blockchains)
self.checkpoint_height = network.get_checkpoint()
status = _("Connected to %d nodes.")%n if n else _("Not connected")
msg = ' '.join([
_("Electrum connects to several nodes in order to download block headers and find out the longest blockchain."),
_("This blockchain is used to verify the transactions sent by your transaction server.")
])
self.status_label = QLabel('')
grid.addWidget(QLabel(_('Status') + ':'), 0, 0)
grid.addWidget(QLabel(status), 0, 1, 1, 3)
grid.addWidget(self.status_label, 0, 1, 1, 3)
grid.addWidget(HelpButton(msg), 0, 4)
def short_hash(h): return h.lstrip('00')[0:10]
if n_chains == 1:
height_str = "%d "%(network.get_local_height()) + _("blocks")
msg = _('This is the height of your local copy of the blockchain.')
grid.addWidget(QLabel(_("Height") + ':'), 1, 0)
grid.addWidget(QLabel(height_str), 1, 1)
grid.addWidget(HelpButton(msg), 1, 4)
else:
checkpoint = network.get_checkpoint()
_hash = network.blockchain().get_hash(checkpoint)
grid.addWidget(QLabel(_('Chain split detected at block %d')%checkpoint), 1, 0, 1, 3)
grid.addWidget(QLabel(_('You are on branch') + ' ' + short_hash(_hash)), 2, 0, 1, 3)
nodes_list_widget = NodesListWidget(self)
grid.addWidget(nodes_list_widget, 5, 0, 1, 5)
if n_chains > 1:
for b in network.blockchains.values():
_hash = b.get_hash(checkpoint)
x = QTreeWidgetItem([short_hash(_hash), '%d'%checkpoint])
x.setData(0, Qt.UserRole, 1)
x.setData(1, Qt.UserRole, b.checkpoint)
for i in network.interfaces.values():
if i.blockchain == b:
item = QTreeWidgetItem([i.host, '%d'%i.tip])
item.setData(0, Qt.UserRole, 0)
item.setData(1, Qt.UserRole, i.server)
x.addChild(item)
nodes_list_widget.addTopLevelItem(x)
x.setExpanded(True)
else:
for i in network.interfaces.values():
item = QTreeWidgetItem([i.host, '%d'%i.tip])
item.setData(0, Qt.UserRole, 0)
item.setData(1, Qt.UserRole, i.server)
nodes_list_widget.addTopLevelItem(item)
h = nodes_list_widget.header()
h.setStretchLastSection(False)
h.setResizeMode(0, QHeaderView.Stretch)
h.setResizeMode(1, QHeaderView.ResizeToContents)
self.height_label = QLabel('')
msg = _('This is the height of your local copy of the blockchain.')
grid.addWidget(QLabel(_("Height") + ':'), 1, 0)
grid.addWidget(self.height_label, 1, 1)
grid.addWidget(HelpButton(msg), 1, 4)
self.split_label = QLabel('')
grid.addWidget(self.split_label, 2, 0, 1, 3)
self.nodes_list_widget = NodesListWidget(self)
grid.addWidget(self.nodes_list_widget, 5, 0, 1, 5)
grid.setRowStretch(7, 1)
vbox = QVBoxLayout()
vbox.addWidget(tabs)
@ -288,17 +252,69 @@ class NetworkChoiceLayout(object):
self.td = td = TorDetector()
td.found_proxy.connect(self.suggest_proxy)
td.start()
self.change_server(host, protocol)
self.set_protocol(protocol)
self.servers_list_widget.connect(
self.servers_list_widget,
SIGNAL('currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)'),
lambda x,y: self.server_changed(x))
# update
self.update()
def check_disable_proxy(self, index = False):
if self.config.is_modifiable('proxy'):
for w in [self.proxy_host, self.proxy_port, self.proxy_user, self.proxy_password]:
w.setEnabled(self.proxy_mode.currentText() != 'NONE')
else:
for w in [self.proxy_host, self.proxy_port, self.proxy_mode]: w.setEnabled(False)
def enable_set_server(self):
if self.config.is_modifiable('server'):
enabled = not self.autoconnect_cb.isChecked()
self.server_host.setEnabled(enabled)
self.server_port.setEnabled(enabled)
self.servers_list_widget.setEnabled(enabled)
else:
for w in [self.autoconnect_cb, self.server_host, self.server_port, self.ssl_cb, self.servers_list_widget]:
w.setEnabled(False)
def update(self):
host, port, protocol, proxy_config, auto_connect = self.network.get_parameters()
if not proxy_config:
proxy_config = { "mode":"none", "host":"localhost", "port":"9050"}
self.server_host.setText(host)
self.server_port.setText(port)
self.autoconnect_cb.setChecked(auto_connect)
#self.ssl_cb.setChecked(auto_connect)
#self.change_server(host, protocol)
self.set_protocol(protocol)
self.update_servers_list()
self.enable_set_server()
# proxy tab
self.proxy_mode.setCurrentIndex(self.proxy_mode.findText(str(proxy_config.get("mode").upper())))
self.proxy_host.setText(proxy_config.get("host"))
self.proxy_port.setText(proxy_config.get("port"))
self.proxy_user.setText(proxy_config.get("user", ""))
self.proxy_password.setText(proxy_config.get("password", ""))
height_str = "%d "%(self.network.get_local_height()) + _("blocks")
self.height_label.setText(height_str)
n = len(self.network.get_interfaces())
status = _("Connected to %d nodes.")%n if n else _("Not connected")
self.status_label.setText(status)
if len(self.network.blockchains)>1:
checkpoint = self.network.get_checkpoint()
name = self.network.get_blockchain_name(self.network.blockchain())
msg = _('Chain split detected at block %d')%checkpoint + '\n' + _('You are on branch') + ' ' + name
else:
msg = ''
self.split_label.setText(msg)
self.nodes_list_widget.update(self.network)
def layout(self):
return self.layout_
def init_servers_list(self):
def update_servers_list(self):
self.servers = self.network.get_servers()
self.servers_list_widget.clear()
for _host, d in sorted(self.servers.items()):
if d.get(self.protocol):
@ -308,7 +324,6 @@ class NetworkChoiceLayout(object):
def set_protocol(self, protocol):
if protocol != self.protocol:
self.protocol = protocol
self.init_servers_list()
def change_protocol(self, use_ssl):
p = 's' if use_ssl else 't'
@ -320,21 +335,20 @@ class NetworkChoiceLayout(object):
self.server_host.setText( host )
self.server_port.setText( port )
self.set_protocol(p)
self.set_server()
def server_changed(self, x):
if x:
self.change_server(str(x.text(0)), self.protocol)
def change_server(self, host, protocol):
pp = self.servers.get(host, DEFAULT_PORTS)
if protocol and protocol not in protocol_letters:
protocol = None
protocol = None
if protocol:
port = pp.get(protocol)
if port is None:
protocol = None
if not protocol:
if 's' in pp.keys():
protocol = 's'
@ -342,15 +356,24 @@ class NetworkChoiceLayout(object):
else:
protocol = pp.keys()[0]
port = pp.get(protocol)
self.server_host.setText( host )
self.server_port.setText( port )
self.ssl_cb.setChecked(protocol=='s')
self.set_server()
def accept(self):
pass
def set_server(self):
host, port, protocol, proxy, auto_connect = self.network.get_parameters()
host = str(self.server_host.text())
port = str(self.server_port.text())
protocol = 's' if self.ssl_cb.isChecked() else 't'
auto_connect = self.autoconnect_cb.isChecked()
self.network.set_parameters(host, port, protocol, proxy, auto_connect)
def set_proxy(self):
host, port, protocol, proxy, auto_connect = self.network.get_parameters()
if self.proxy_mode.currentText() != 'NONE':
proxy = { 'mode':str(self.proxy_mode.currentText()).lower(),
'host':str(self.proxy_host.text()),
@ -359,9 +382,7 @@ class NetworkChoiceLayout(object):
'password':str(self.proxy_password.text())}
else:
proxy = None
auto_connect = self.autoconnect_cb.isChecked()
self.network.set_parameters(host, port, protocol, proxy, auto_connect)
#self.network.blockchain.set_checkpoint(self.checkpoint_height, self.checkpoint_value)
def suggest_proxy(self, found_proxy):
self.tor_proxy = found_proxy

9
lib/network.py

@ -77,6 +77,7 @@ def set_testnet():
global DEFAULT_PORTS, DEFAULT_SERVERS
DEFAULT_PORTS = {'t':'51001', 's':'51002'}
DEFAULT_SERVERS = {
'testnetnode.arihanc.com': DEFAULT_PORTS,
'testnet1.bauerj.eu': DEFAULT_PORTS,
'14.3.140.101': DEFAULT_PORTS,
'testnet.hsmiths.com': {'t':'53011', 's':'53012'},
@ -527,7 +528,8 @@ class Network(util.DaemonThread):
if self.interface != i:
self.print_error("switching to", server)
# stop any current interface in order to terminate subscriptions
self.close_interface(self.interface)
# fixme: we don't want to close headers sub
#self.close_interface(self.interface)
self.interface = i
self.send_subscriptions()
self.set_status('connected')
@ -1016,6 +1018,11 @@ class Network(util.DaemonThread):
return self.blockchains[self.blockchain_index]
def get_blockchain_name(self, blockchain):
checkpoint = self.get_checkpoint()
_hash = blockchain.get_hash(checkpoint)
return _hash.lstrip('00')[0:10]
def follow_chain(self, index):
blockchain = self.blockchains.get(index)
if blockchain:

Loading…
Cancel
Save