electrum-bitcoinprivate/gui/gtk.py

1368 lines
49 KiB
Python
Raw Normal View History

2011-11-04 10:00:37 -07:00
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2011 thomasv@gitorious
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2013-09-28 04:51:25 -07:00
2011-11-04 10:00:37 -07:00
import datetime
2012-02-03 06:45:41 -08:00
import thread, time, ast, sys, re
2011-11-10 13:21:40 -08:00
import socket, traceback
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, cairo
2011-12-06 12:05:46 -08:00
from decimal import Decimal
from electrum.util import print_error
2013-09-11 03:05:28 -07:00
from electrum.bitcoin import is_valid
2014-08-29 03:27:10 -07:00
from electrum import WalletStorage, Wallet
Gdk.threads_init()
2011-11-04 10:00:37 -07:00
APP_NAME = "Electrum"
2012-03-31 07:46:32 -07:00
import platform
MONOSPACE_FONT = 'Lucida Console' if platform.system() == 'Windows' else 'monospace'
2011-11-04 10:00:37 -07:00
2014-06-13 07:02:30 -07:00
from electrum.util import format_satoshis, parse_URI
2013-09-12 05:58:42 -07:00
from electrum.network import DEFAULT_SERVERS
2013-03-24 03:25:17 -07:00
from electrum.bitcoin import MIN_RELAY_TX_FEE
2011-11-04 10:00:37 -07:00
def numbify(entry, is_int = False):
text = entry.get_text().strip()
2011-12-07 13:38:31 -08:00
chars = '0123456789'
if not is_int: chars +='.'
s = ''.join([i for i in text if i in chars])
if not is_int:
2011-12-07 13:38:31 -08:00
if '.' in s:
p = s.find('.')
s = s.replace('.','')
s = s[:p] + '.' + s[p:p+8]
try:
2011-12-07 10:24:04 -08:00
amount = int( Decimal(s) * 100000000 )
2013-11-10 12:30:57 -08:00
except Exception:
2011-12-07 13:11:35 -08:00
amount = None
2011-12-07 13:38:31 -08:00
else:
try:
amount = int( s )
2013-11-10 12:30:57 -08:00
except Exception:
2011-12-07 13:38:31 -08:00
amount = None
2011-12-07 10:24:04 -08:00
entry.set_text(s)
return amount
2011-11-08 04:56:35 -08:00
2014-04-30 00:31:20 -07:00
def show_seed_dialog(seed, parent):
if not seed:
2012-05-12 15:43:22 -07:00
show_message("No seed")
return
2014-04-30 00:31:20 -07:00
dialog = Gtk.MessageDialog(
parent = parent,
flags = Gtk.DialogFlags.MODAL,
buttons = Gtk.ButtonsType.OK,
2014-04-30 00:31:20 -07:00
message_format = "Your wallet generation seed is:\n\n" + '"' + seed + '"'\
+ "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" )
dialog.set_title("Seed")
2011-11-08 04:56:35 -08:00
dialog.show()
dialog.run()
dialog.destroy()
def restore_create_dialog():
2011-11-04 10:00:37 -07:00
2012-02-11 04:14:12 -08:00
# ask if the user wants to create a new wallet, or recover from a seed.
# if he wants to recover, and nothing is found, do not create wallet
dialog = Gtk.Dialog("electrum", parent=None,
2014-04-30 00:31:20 -07:00
flags=Gtk.DialogFlags.MODAL,
2012-02-11 04:14:12 -08:00
buttons= ("create", 0, "restore",1, "cancel",2) )
label = Gtk.Label("Wallet file not found.\nDo you want to create a new wallet,\n or to restore an existing one?" )
2012-02-11 04:14:12 -08:00
label.show()
dialog.vbox.pack_start(label, True, True, 0)
2012-02-11 04:14:12 -08:00
dialog.show()
r = dialog.run()
dialog.destroy()
2012-02-14 00:52:03 -08:00
if r==2: return False
return 'restore' if r==1 else 'create'
2012-02-14 00:52:03 -08:00
2011-11-04 10:00:37 -07:00
def run_recovery_dialog():
message = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet."
dialog = Gtk.MessageDialog(
parent = None,
flags = Gtk.DialogFlags.MODAL,
buttons = Gtk.ButtonsType.OK_CANCEL,
message_format = message)
vbox = dialog.vbox
dialog.set_default_response(Gtk.ResponseType.OK)
# ask seed, server and gap in the same dialog
seed_box = Gtk.HBox()
seed_label = Gtk.Label(label='Seed or mnemonic:')
seed_label.set_size_request(150,-1)
seed_box.pack_start(seed_label, False, False, 10)
seed_label.show()
seed_entry = Gtk.Entry()
seed_entry.show()
seed_entry.set_size_request(450,-1)
seed_box.pack_start(seed_entry, False, False, 10)
add_help_button(seed_box, '.')
seed_box.show()
vbox.pack_start(seed_box, False, False, 5)
dialog.show()
r = dialog.run()
2012-01-14 16:39:16 -08:00
seed = seed_entry.get_text()
dialog.destroy()
if r==Gtk.ResponseType.CANCEL:
return False
2014-04-30 00:31:20 -07:00
if Wallet.is_seed(seed):
return seed
show_message("no seed")
return False
2013-10-07 01:05:15 -07:00
def run_settings_dialog(self):
2012-02-14 00:52:03 -08:00
message = "Here are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field."
dialog = Gtk.MessageDialog(
2013-10-07 01:05:15 -07:00
parent = self.window,
flags = Gtk.DialogFlags.MODAL,
buttons = Gtk.ButtonsType.OK_CANCEL,
message_format = message)
2011-11-04 10:00:37 -07:00
image = Gtk.Image()
image.set_from_stock(Gtk.STOCK_PREFERENCES, Gtk.IconSize.DIALOG)
2011-12-03 05:08:21 -08:00
image.show()
dialog.set_image(image)
dialog.set_title("Settings")
2011-12-03 05:08:21 -08:00
vbox = dialog.vbox
dialog.set_default_response(Gtk.ResponseType.OK)
2011-11-04 10:00:37 -07:00
fee = Gtk.HBox()
fee_entry = Gtk.Entry()
fee_label = Gtk.Label(label='Transaction fee:')
fee_label.set_size_request(150,10)
fee_label.show()
fee.pack_start(fee_label,False, False, 10)
2014-09-07 09:45:06 -07:00
fee_entry.set_text( str( Decimal(self.wallet.fee_per_kb) /100000000 ) )
fee_entry.connect('changed', numbify, False)
fee_entry.show()
fee.pack_start(fee_entry,False,False, 10)
add_help_button(fee, 'Fee per kilobyte of transaction. Recommended value:0.0001')
fee.show()
vbox.pack_start(fee, False,False, 5)
nz = Gtk.HBox()
nz_entry = Gtk.Entry()
nz_label = Gtk.Label(label='Display zeros:')
nz_label.set_size_request(150,10)
nz_label.show()
nz.pack_start(nz_label,False, False, 10)
2013-10-07 01:05:15 -07:00
nz_entry.set_text( str( self.num_zeros ))
nz_entry.connect('changed', numbify, True)
nz_entry.show()
nz.pack_start(nz_entry,False,False, 10)
add_help_button(nz, "Number of zeros displayed after the decimal point.\nFor example, if this number is 2, then '5.' is displayed as '5.00'")
nz.show()
vbox.pack_start(nz, False,False, 5)
2011-11-04 10:00:37 -07:00
dialog.show()
r = dialog.run()
fee = fee_entry.get_text()
nz = nz_entry.get_text()
2011-11-04 10:00:37 -07:00
dialog.destroy()
if r==Gtk.ResponseType.CANCEL:
return
2011-11-08 04:56:35 -08:00
2011-12-02 07:40:48 -08:00
try:
fee = int( 100000000 * Decimal(fee) )
2013-11-10 12:30:57 -08:00
except Exception:
2011-12-02 07:40:48 -08:00
show_message("error")
return
2013-10-07 01:05:15 -07:00
self.wallet.set_fee(fee)
2011-12-02 07:40:48 -08:00
try:
nz = int( nz )
if nz>8: nz = 8
2013-11-10 12:30:57 -08:00
except Exception:
show_message("error")
return
2011-12-02 07:40:48 -08:00
2013-10-07 01:05:15 -07:00
if self.num_zeros != nz:
self.num_zeros = nz
self.config.set_key('num_zeros',nz,True)
self.update_history_tab()
2012-10-12 08:04:16 -07:00
2011-12-02 07:40:48 -08:00
2011-11-04 10:00:37 -07:00
2012-03-31 05:20:25 -07:00
def run_network_dialog( network, parent ):
image = Gtk.Image()
image.set_from_stock(Gtk.STOCK_NETWORK, Gtk.IconSize.DIALOG)
2014-07-28 00:28:02 -07:00
host, port, protocol, proxy_config, auto_connect = network.get_parameters()
2014-08-27 04:48:10 -07:00
server = "%s:%s:%s"%(host, port, protocol)
if parent:
2013-10-07 01:05:15 -07:00
if network.is_connected():
2014-08-27 04:48:10 -07:00
status = "Connected to %s\n%d blocks"%(host, network.get_local_height())
else:
status = "Not connected"
else:
import random
status = "Please choose a server.\nSelect cancel if you are offline."
servers = network.get_servers()
2012-03-31 05:46:00 -07:00
dialog = Gtk.MessageDialog( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, status)
dialog.set_title("Server")
dialog.set_image(image)
image.show()
vbox = dialog.vbox
host_box = Gtk.HBox()
host_label = Gtk.Label(label='Connect to:')
host_label.set_size_request(100,-1)
host_label.show()
host_box.pack_start(host_label, False, False, 10)
host_entry = Gtk.Entry()
host_entry.set_size_request(200,-1)
if network.is_connected():
host_entry.set_text(server)
else:
host_entry.set_text("Not Connected")
host_entry.show()
host_box.pack_start(host_entry, False, False, 10)
2013-10-07 01:05:15 -07:00
add_help_button(host_box, 'The name, port number and protocol of your Electrum server, separated by a colon. Example: "ecdsa.org:50002:s". Some servers allow you to connect through http (port 80) or https (port 443)')
host_box.show()
2012-03-31 05:46:00 -07:00
p_box = Gtk.HBox(False, 10)
2012-03-31 05:46:00 -07:00
p_box.show()
p_label = Gtk.Label(label='Protocol:')
2012-03-31 05:46:00 -07:00
p_label.set_size_request(100,-1)
p_label.show()
p_box.pack_start(p_label, False, False, 10)
combobox = Gtk.ComboBoxText()
2013-10-07 01:05:15 -07:00
combobox.show()
combobox.append_text("TCP")
combobox.append_text("SSL")
combobox.append_text("HTTP")
combobox.append_text("HTTPS")
p_box.pack_start(combobox, True, True, 0)
2012-03-31 05:46:00 -07:00
def current_line():
return unicode(host_entry.get_text()).split(':')
2013-10-07 01:05:15 -07:00
def set_combobox(protocol):
combobox.set_active('tshg'.index(protocol))
2012-03-31 05:46:00 -07:00
def set_protocol(protocol):
host = current_line()[0]
pp = servers[host]
2012-03-31 05:46:00 -07:00
if protocol not in pp.keys():
protocol = pp.keys()[0]
2013-10-07 01:05:15 -07:00
set_combobox(protocol)
2012-03-31 05:46:00 -07:00
port = pp[protocol]
host_entry.set_text( host + ':' + port + ':' + protocol)
2013-10-07 01:05:15 -07:00
combobox.connect("changed", lambda x:set_protocol('tshg'[combobox.get_active()]))
if network.is_connected():
set_combobox(protocol)
server_list = Gtk.ListStore(str)
for host in servers.keys():
2012-03-31 05:46:00 -07:00
server_list.append([host])
treeview = Gtk.TreeView(model=server_list)
treeview.show()
2014-07-28 00:28:02 -07:00
label = 'Active Servers' if network.is_connected() else 'Default Servers'
tvcolumn = Gtk.TreeViewColumn(label)
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
tvcolumn.pack_start(cell, False)
tvcolumn.add_attribute(cell, 'text', 0)
vbox.pack_start(host_box, False,False, 5)
2012-03-31 05:46:00 -07:00
vbox.pack_start(p_box, True, True, 0)
2013-10-07 01:05:15 -07:00
#scroll = Gtk.ScrolledWindow()
#scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
2013-10-07 01:05:15 -07:00
#scroll.add_with_viewport(treeview)
#scroll.show()
#vbox.pack_start(scroll, True)
vbox.pack_start(treeview, True, True, 0)
def my_treeview_cb(treeview):
path, view_column = treeview.get_cursor()
2012-03-31 05:46:00 -07:00
host = server_list.get_value( server_list.get_iter(path), 0)
pp = servers[host]
2012-03-31 05:46:00 -07:00
if 't' in pp.keys():
protocol = 't'
else:
protocol = pp.keys()[0]
port = pp[protocol]
host_entry.set_text( host + ':' + port + ':' + protocol)
2013-10-07 01:05:15 -07:00
set_combobox(protocol)
2012-03-31 05:46:00 -07:00
treeview.connect('cursor-changed', my_treeview_cb)
2013-10-07 01:05:15 -07:00
dialog.show_all()
r = dialog.run()
2012-03-30 14:44:54 -07:00
server = host_entry.get_text()
dialog.destroy()
if r==Gtk.ResponseType.CANCEL:
2012-02-14 06:42:13 -08:00
return False
try:
2013-10-07 01:05:15 -07:00
host, port, protocol = server.split(':')
2013-11-10 12:30:57 -08:00
except Exception:
2012-03-30 14:44:54 -07:00
show_message("error:" + server)
2012-02-14 06:42:13 -08:00
return False
2014-08-27 04:48:10 -07:00
network.set_parameters(host, port, protocol, proxy_config, auto_connect)
2013-10-07 01:05:15 -07:00
2011-11-04 10:00:37 -07:00
def show_message(message, parent=None):
dialog = Gtk.MessageDialog(
parent = parent,
flags = Gtk.DialogFlags.MODAL,
buttons = Gtk.ButtonsType.CLOSE,
2011-11-04 10:00:37 -07:00
message_format = message )
dialog.show()
dialog.run()
dialog.destroy()
def password_line(label):
password = Gtk.HBox()
password_label = Gtk.Label(label=label)
2011-11-04 10:00:37 -07:00
password_label.set_size_request(120,10)
password_label.show()
password.pack_start(password_label,False, False, 10)
password_entry = Gtk.Entry()
2011-11-11 02:12:40 -08:00
password_entry.set_size_request(300,-1)
2011-11-04 10:00:37 -07:00
password_entry.set_visibility(False)
password_entry.show()
password.pack_start(password_entry,False,False, 10)
password.show()
return password, password_entry
2012-02-06 09:01:35 -08:00
def password_dialog(parent):
dialog = Gtk.MessageDialog( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, "Please enter your password.")
2011-11-04 10:00:37 -07:00
dialog.get_image().set_visible(False)
current_pw, current_pw_entry = password_line('Password:')
current_pw_entry.connect("activate", lambda entry, dialog, response: dialog.response(response), dialog, Gtk.ResponseType.OK)
2011-11-04 10:00:37 -07:00
dialog.vbox.pack_start(current_pw, False, True, 0)
dialog.show()
result = dialog.run()
pw = current_pw_entry.get_text()
dialog.destroy()
if result != Gtk.ResponseType.CANCEL: return pw
2011-11-04 10:00:37 -07:00
def change_password_dialog(is_encrypted, parent):
2012-05-12 15:43:22 -07:00
if parent:
msg = 'Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password.' if is_encrypted else 'Your wallet keys are not encrypted'
else:
msg = "Please choose a password to encrypt your wallet keys"
dialog = Gtk.MessageDialog( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, msg)
2011-12-03 02:31:00 -08:00
dialog.set_title("Change password")
image = Gtk.Image()
image.set_from_stock(Gtk.STOCK_DIALOG_AUTHENTICATION, Gtk.IconSize.DIALOG)
2011-12-03 05:08:21 -08:00
image.show()
dialog.set_image(image)
2011-11-08 04:56:35 -08:00
if is_encrypted:
2011-11-04 16:15:02 -07:00
current_pw, current_pw_entry = password_line('Current password:')
2011-11-04 10:00:37 -07:00
dialog.vbox.pack_start(current_pw, False, True, 0)
password, password_entry = password_line('New password:')
dialog.vbox.pack_start(password, False, True, 5)
password2, password2_entry = password_line('Confirm password:')
dialog.vbox.pack_start(password2, False, True, 5)
dialog.show()
result = dialog.run()
password = current_pw_entry.get_text() if is_encrypted else None
2011-11-04 10:00:37 -07:00
new_password = password_entry.get_text()
new_password2 = password2_entry.get_text()
dialog.destroy()
if result == Gtk.ResponseType.CANCEL:
2011-11-04 10:00:37 -07:00
return
if new_password != new_password2:
show_message("passwords do not match")
return change_password_dialog(is_encrypted, parent)
2011-11-04 10:00:37 -07:00
if not new_password:
new_password = None
return True, password, new_password
2011-11-04 16:15:02 -07:00
2011-11-04 10:00:37 -07:00
def add_help_button(hbox, message):
button = Gtk.Button('?')
2011-11-04 10:00:37 -07:00
button.connect("clicked", lambda x: show_message(message))
button.show()
hbox.pack_start(button,False, False, 0)
2011-11-04 10:00:37 -07:00
2012-02-14 00:52:03 -08:00
class ElectrumWindow:
2011-11-04 10:00:37 -07:00
def show_message(self, msg):
show_message(msg, self.window)
2014-07-28 00:28:02 -07:00
def on_key(self, w, event):
if Gdk.ModifierType.CONTROL_MASK & event.state and event.keyval in [113,119]:
Gtk.main_quit()
return True
def __init__(self, wallet, config, network):
self.config = config
2011-11-04 10:00:37 -07:00
self.wallet = wallet
self.network = network
2012-02-08 04:37:14 -08:00
self.funds_error = False # True if not enough funds
2013-10-07 01:05:15 -07:00
self.num_zeros = int(self.config.get('num_zeros',0))
2014-07-28 00:28:02 -07:00
self.window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
self.window.connect('key-press-event', self.on_key)
title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
2012-05-13 01:54:03 -07:00
if not self.wallet.seed: title += ' [seedless]'
self.window.set_title(title)
self.window.connect("destroy", Gtk.main_quit)
2011-11-04 10:00:37 -07:00
self.window.set_border_width(0)
2014-07-28 00:28:02 -07:00
#self.window.connect('mykeypress', Gtk.main_quit)
self.window.set_default_size(720, 350)
self.wallet_updated = False
2011-11-04 10:00:37 -07:00
vbox = Gtk.VBox()
2011-11-04 10:00:37 -07:00
self.notebook = Gtk.Notebook()
2011-11-04 10:00:37 -07:00
self.create_history_tab()
if self.wallet.seed:
self.create_send_tab()
2011-11-04 10:00:37 -07:00
self.create_recv_tab()
self.create_book_tab()
self.create_about_tab()
self.notebook.show()
vbox.pack_start(self.notebook, True, True, 2)
self.status_bar = Gtk.Statusbar()
2011-11-04 10:00:37 -07:00
vbox.pack_start(self.status_bar, False, False, 0)
self.status_image = Gtk.Image()
self.status_image.set_from_stock(Gtk.STOCK_NO, Gtk.IconSize.MENU)
2011-11-04 10:00:37 -07:00
self.status_image.set_alignment(True, 0.5 )
self.status_image.show()
2011-12-02 07:40:48 -08:00
self.network_button = Gtk.Button()
self.network_button.connect("clicked", lambda x: run_network_dialog(self.network, self.window) )
2011-12-02 08:22:49 -08:00
self.network_button.add(self.status_image)
self.network_button.set_relief(Gtk.ReliefStyle.NONE)
2011-12-02 08:22:49 -08:00
self.network_button.show()
self.status_bar.pack_end(self.network_button, False, False, 0)
2011-11-08 04:56:35 -08:00
2012-05-13 01:54:03 -07:00
if self.wallet.seed:
def seedb(w, wallet):
if wallet.use_encryption:
password = password_dialog(self.window)
if not password: return
else: password = None
2014-04-30 00:31:20 -07:00
seed = wallet.get_mnemonic(password)
show_seed_dialog(seed, self.window)
button = Gtk.Button('S')
button.connect("clicked", seedb, self.wallet )
button.set_relief(Gtk.ReliefStyle.NONE)
2012-05-13 01:54:03 -07:00
button.show()
self.status_bar.pack_end(button,False, False, 0)
2011-11-08 04:56:35 -08:00
settings_icon = Gtk.Image()
settings_icon.set_from_stock(Gtk.STOCK_PREFERENCES, Gtk.IconSize.MENU)
2011-11-04 16:15:02 -07:00
settings_icon.set_alignment(0.5, 0.5)
settings_icon.set_size_request(16,16 )
2011-11-04 10:00:37 -07:00
settings_icon.show()
prefs_button = Gtk.Button()
2013-10-07 01:05:15 -07:00
prefs_button.connect("clicked", lambda x: run_settings_dialog(self) )
2011-11-04 10:00:37 -07:00
prefs_button.add(settings_icon)
prefs_button.set_tooltip_text("Settings")
prefs_button.set_relief(Gtk.ReliefStyle.NONE)
2011-11-04 10:00:37 -07:00
prefs_button.show()
self.status_bar.pack_end(prefs_button,False,False, 0)
2011-11-04 10:00:37 -07:00
self.pw_icon = Gtk.Image()
self.pw_icon.set_from_stock(Gtk.STOCK_DIALOG_AUTHENTICATION, Gtk.IconSize.MENU)
self.pw_icon.set_alignment(0.5, 0.5)
self.pw_icon.set_size_request(16,16 )
self.pw_icon.show()
2011-11-04 16:15:02 -07:00
2012-05-13 01:54:03 -07:00
if self.wallet.seed:
if self.wallet.use_encryption:
self.pw_icon.set_tooltip_text('Wallet is encrypted')
else:
self.pw_icon.set_tooltip_text('Wallet is unencrypted')
password_button = Gtk.Button()
password_button.connect("clicked", self.do_update_password, self.wallet)
password_button.add(self.pw_icon)
password_button.set_relief(Gtk.ReliefStyle.NONE)
2012-05-13 01:54:03 -07:00
password_button.show()
self.status_bar.pack_end(password_button,False,False, 0)
2011-11-04 16:15:02 -07:00
2011-11-04 10:00:37 -07:00
self.window.add(vbox)
self.window.show_all()
2012-02-03 02:48:09 -08:00
#self.fee_box.hide()
2011-11-04 10:00:37 -07:00
self.context_id = self.status_bar.get_context_id("statusbar")
self.update_status_bar()
self.network.register_callback('updated', self.update_callback)
2011-11-04 10:00:37 -07:00
def update_status_bar_thread():
while True:
GObject.idle_add( self.update_status_bar )
2011-11-04 10:00:37 -07:00
time.sleep(0.5)
2012-02-03 06:45:41 -08:00
def check_recipient_thread():
old_r = ''
while True:
time.sleep(0.5)
2012-02-03 06:57:45 -08:00
if self.payto_entry.is_focus():
continue
2012-02-03 06:45:41 -08:00
r = self.payto_entry.get_text()
if r != old_r:
old_r = r
2012-02-03 07:08:28 -08:00
r = r.strip()
2012-02-03 06:45:41 -08:00
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
2012-02-03 07:39:27 -08:00
try:
2012-02-14 00:52:03 -08:00
to_address = self.wallet.get_alias(r, interactive=False)
2013-11-10 12:30:57 -08:00
except Exception:
2012-02-03 07:39:27 -08:00
continue
2012-02-03 06:45:41 -08:00
if to_address:
2012-02-03 07:08:28 -08:00
s = r + ' <' + to_address + '>'
GObject.idle_add( lambda: self.payto_entry.set_text(s) )
2012-02-03 06:45:41 -08:00
2011-11-04 10:00:37 -07:00
thread.start_new_thread(update_status_bar_thread, ())
if self.wallet.seed:
thread.start_new_thread(check_recipient_thread, ())
2011-11-04 10:00:37 -07:00
self.notebook.set_current_page(0)
def update_callback(self):
self.wallet_updated = True
def do_update_password(self, button, wallet):
if not wallet.seed:
show_message("No seed")
return
res = change_password_dialog(wallet.use_encryption, self.window)
if res:
_, password, new_password = res
try:
wallet.get_seed(password)
except Exception:
show_message("Incorrect password")
return
wallet.update_password(password, new_password)
if wallet.use_encryption:
self.pw_icon.set_tooltip_text('Wallet is encrypted')
else:
self.pw_icon.set_tooltip_text('Wallet is unencrypted')
2011-11-04 10:00:37 -07:00
def add_tab(self, page, name):
tab_label = Gtk.Label(label=name)
2011-11-04 10:00:37 -07:00
tab_label.show()
self.notebook.append_page(page, tab_label)
def create_send_tab(self):
2012-02-03 02:48:09 -08:00
page = vbox = Gtk.VBox()
2011-11-04 10:00:37 -07:00
page.show()
payto = Gtk.HBox()
payto_label = Gtk.Label(label='Pay to:')
2012-02-03 02:48:09 -08:00
payto_label.set_size_request(100,-1)
payto.pack_start(payto_label, False, False, 0)
payto_entry = Gtk.Entry()
2012-02-03 06:45:41 -08:00
payto_entry.set_size_request(450, 26)
payto.pack_start(payto_entry, False, False, 0)
2011-11-04 10:00:37 -07:00
vbox.pack_start(payto, False, False, 5)
2012-02-03 02:48:09 -08:00
message = Gtk.HBox()
message_label = Gtk.Label(label='Description:')
2012-02-03 13:25:50 -08:00
message_label.set_size_request(100,-1)
message.pack_start(message_label, False, False, 0)
message_entry = Gtk.Entry()
2012-02-03 13:25:50 -08:00
message_entry.set_size_request(450, 26)
message.pack_start(message_entry, False, False, 0)
2012-02-03 13:25:50 -08:00
vbox.pack_start(message, False, False, 5)
2011-11-04 10:00:37 -07:00
amount_box = Gtk.HBox()
amount_label = Gtk.Label(label='Amount:')
amount_label.set_size_request(100,-1)
amount_box.pack_start(amount_label, False, False, 0)
amount_entry = Gtk.Entry()
amount_entry.set_size_request(120, -1)
amount_box.pack_start(amount_entry, False, False, 0)
vbox.pack_start(amount_box, False, False, 5)
2011-11-04 10:00:37 -07:00
self.fee_box = fee_box = Gtk.HBox()
fee_label = Gtk.Label(label='Fee:')
2012-02-03 02:48:09 -08:00
fee_label.set_size_request(100,-1)
fee_box.pack_start(fee_label, False, False, 0)
fee_entry = Gtk.Entry()
2012-02-02 09:56:57 -08:00
fee_entry.set_size_request(60, 26)
fee_box.pack_start(fee_entry, False, False, 0)
2012-02-03 13:25:50 -08:00
vbox.pack_start(fee_box, False, False, 5)
2012-02-02 09:44:50 -08:00
end_box = Gtk.HBox()
empty_label = Gtk.Label(label='')
2012-02-03 02:48:09 -08:00
empty_label.set_size_request(100,-1)
end_box.pack_start(empty_label, False, False, 0)
send_button = Gtk.Button("Send")
2012-02-02 09:44:50 -08:00
send_button.show()
2012-02-03 13:25:50 -08:00
end_box.pack_start(send_button, False, False, 0)
clear_button = Gtk.Button("Clear")
2012-02-02 09:44:50 -08:00
clear_button.show()
2012-02-03 13:25:50 -08:00
end_box.pack_start(clear_button, False, False, 15)
send_button.connect("clicked", self.do_send, (payto_entry, message_entry, amount_entry, fee_entry))
clear_button.connect("clicked", self.do_clear, (payto_entry, message_entry, amount_entry, fee_entry))
2012-02-03 02:48:09 -08:00
2012-02-02 09:44:50 -08:00
vbox.pack_start(end_box, False, False, 5)
2012-02-03 02:48:09 -08:00
# display this line only if there is a signature
payto_sig = Gtk.HBox()
payto_sig_id = Gtk.Label(label='')
payto_sig.pack_start(payto_sig_id, False, False, 0)
2012-02-03 02:48:09 -08:00
vbox.pack_start(payto_sig, True, True, 5)
self.user_fee = False
def entry_changed( entry, is_fee ):
2012-02-08 04:37:14 -08:00
self.funds_error = False
amount = numbify(amount_entry)
fee = numbify(fee_entry)
if not is_fee: fee = None
2012-02-08 04:37:14 -08:00
if amount is None:
2012-02-03 02:48:09 -08:00
return
2014-09-07 09:45:06 -07:00
tx = self.wallet.make_unsigned_transaction([('op_return', 'dummy_tx', amount)], fee)
if not is_fee:
2014-09-07 09:45:06 -07:00
if tx:
fee = tx.get_fee()
fee_entry.set_text( str( Decimal( fee ) / 100000000 ) )
self.fee_box.show()
if tx:
amount_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#000000"))
fee_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#000000"))
send_button.set_sensitive(True)
else:
send_button.set_sensitive(False)
amount_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#cc0000"))
fee_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#cc0000"))
2012-02-08 04:37:14 -08:00
self.funds_error = True
amount_entry.connect('changed', entry_changed, False)
2012-02-03 06:45:41 -08:00
fee_entry.connect('changed', entry_changed, True)
2011-11-04 10:00:37 -07:00
self.payto_entry = payto_entry
2012-02-02 09:44:50 -08:00
self.payto_fee_entry = fee_entry
2012-02-03 02:48:09 -08:00
self.payto_sig_id = payto_sig_id
self.payto_sig = payto_sig
2012-02-03 13:25:50 -08:00
self.amount_entry = amount_entry
self.message_entry = message_entry
2011-11-04 10:00:37 -07:00
self.add_tab(page, 'Send')
2012-02-03 02:48:09 -08:00
def set_frozen(self,entry,frozen):
if frozen:
entry.set_editable(False)
entry.set_has_frame(False)
entry.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse("#eeeeee"))
2012-02-03 02:48:09 -08:00
else:
entry.set_editable(True)
entry.set_has_frame(True)
entry.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse("#ffffff"))
2012-02-03 02:48:09 -08:00
2012-02-14 03:45:39 -08:00
def set_url(self, url):
2014-06-13 07:02:30 -07:00
payto, amount, label, message, payment_request = parse_URI(url)
self.notebook.set_current_page(1)
2012-02-03 06:45:41 -08:00
self.payto_entry.set_text(payto)
2012-02-03 13:25:50 -08:00
self.message_entry.set_text(message)
self.amount_entry.set_text(amount)
2014-05-07 08:35:38 -07:00
self.payto_sig.set_visible(False)
2012-01-15 09:45:30 -08:00
2011-11-04 10:00:37 -07:00
def create_about_tab(self):
from gi.repository import Pango
page = Gtk.VBox()
2011-11-04 10:00:37 -07:00
page.show()
tv = Gtk.TextView()
tv.set_editable(False)
tv.set_cursor_visible(False)
tv.modify_font(Pango.FontDescription(MONOSPACE_FONT))
scroll = Gtk.ScrolledWindow()
scroll.add(tv)
page.pack_start(scroll, True, True, 0)
self.info = tv.get_buffer()
2011-11-13 15:25:00 -08:00
self.add_tab(page, 'Wall')
2011-11-04 10:00:37 -07:00
2012-02-02 09:44:50 -08:00
def do_clear(self, w, data):
2012-02-03 02:48:09 -08:00
self.payto_sig.set_visible(False)
self.payto_fee_entry.set_text('')
2012-02-03 13:25:50 -08:00
for entry in [self.payto_entry,self.amount_entry,self.message_entry]:
2012-02-03 02:48:09 -08:00
self.set_frozen(entry,False)
entry.set_text('')
2012-02-03 06:45:41 -08:00
2012-02-06 08:59:31 -08:00
def question(self,msg):
dialog = Gtk.MessageDialog( self.window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, msg)
2012-02-06 08:59:31 -08:00
dialog.show()
result = dialog.run()
dialog.destroy()
return result == Gtk.ResponseType.OK
2012-02-06 08:59:31 -08:00
2011-11-04 10:00:37 -07:00
def do_send(self, w, data):
payto_entry, label_entry, amount_entry, fee_entry = data
2011-11-04 10:00:37 -07:00
label = label_entry.get_text()
2012-02-03 06:45:41 -08:00
r = payto_entry.get_text()
2012-02-03 07:39:27 -08:00
r = r.strip()
2012-02-03 08:17:33 -08:00
m1 = re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r)
m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
if m1:
2012-02-14 00:52:03 -08:00
to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
2012-02-03 08:17:33 -08:00
if not to_address:
return
2012-02-14 00:52:03 -08:00
else:
self.update_sending_tab()
2012-02-03 08:17:33 -08:00
elif m2:
to_address = m2.group(5)
2012-02-03 06:45:41 -08:00
else:
to_address = r
2012-02-03 08:17:33 -08:00
if not is_valid(to_address):
2012-02-03 06:45:41 -08:00
self.show_message( "invalid bitcoin address:\n"+to_address)
2011-11-04 10:00:37 -07:00
return
try:
2011-12-06 12:05:46 -08:00
amount = int( Decimal(amount_entry.get_text()) * 100000000 )
2013-11-10 12:30:57 -08:00
except Exception:
self.show_message( "invalid amount")
return
try:
fee = int( Decimal(fee_entry.get_text()) * 100000000 )
2013-11-10 12:30:57 -08:00
except Exception:
self.show_message( "invalid fee")
2011-11-04 10:00:37 -07:00
return
2012-01-17 02:15:17 -08:00
if self.wallet.use_encryption:
2012-02-06 09:01:35 -08:00
password = password_dialog(self.window)
2012-01-17 02:15:17 -08:00
if not password:
return
else:
password = None
2011-11-04 10:00:37 -07:00
2011-12-16 06:40:05 -08:00
try:
2013-02-27 03:47:11 -08:00
tx = self.wallet.mktx( [(to_address, amount)], password, fee )
2013-11-09 20:21:02 -08:00
except Exception as e:
self.show_message(str(e))
2011-11-16 06:16:36 -08:00
return
2013-03-24 03:25:17 -07:00
if tx.requires_fee(self.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
self.show_message( "This transaction requires a higher fee, or it will not be propagated by the network." )
return
2011-12-16 06:40:05 -08:00
2013-02-27 03:51:49 -08:00
if label:
self.wallet.labels[tx.hash()] = label
2011-11-16 06:16:36 -08:00
status, msg = self.wallet.sendtx( tx )
2011-11-04 10:00:37 -07:00
if status:
self.show_message( "payment sent.\n" + msg )
2011-11-04 10:00:37 -07:00
payto_entry.set_text("")
label_entry.set_text("")
amount_entry.set_text("")
fee_entry.set_text("")
2012-02-03 02:48:09 -08:00
#self.fee_box.hide()
2011-11-05 00:32:58 -07:00
self.update_sending_tab()
2011-11-04 10:00:37 -07:00
else:
self.show_message( msg )
2011-11-04 10:00:37 -07:00
2011-12-12 22:33:23 -08:00
def treeview_button_press(self, treeview, event):
if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
2011-12-12 22:33:23 -08:00
c = treeview.get_cursor()[0]
2012-02-07 06:00:12 -08:00
if treeview == self.history_treeview:
tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
self.show_message(tx_details)
elif treeview == self.contacts_treeview:
m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0)
2013-03-14 08:32:05 -07:00
#a = self.wallet.aliases.get(m)
#if a:
# if a[0] in self.wallet.authorities.keys():
# s = self.wallet.authorities.get(a[0])
# else:
# s = "self-signed"
# msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
# self.show_message(msg)
2012-02-07 06:00:12 -08:00
2011-12-12 22:33:23 -08:00
2011-11-04 10:00:37 -07:00
def treeview_key_press(self, treeview, event):
c = treeview.get_cursor()[0]
if event.keyval == Gdk.KEY_Up:
2011-11-04 10:00:37 -07:00
if c and c[0] == 0:
treeview.parent.grab_focus()
treeview.set_cursor((0,))
elif event.keyval == Gdk.KEY_Return:
2012-02-07 06:00:12 -08:00
if treeview == self.history_treeview:
tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
self.show_message(tx_details)
elif treeview == self.contacts_treeview:
m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0)
2013-03-14 08:32:05 -07:00
#a = self.wallet.aliases.get(m)
#if a:
# if a[0] in self.wallet.authorities.keys():
# s = self.wallet.authorities.get(a[0])
# else:
# s = "self"
# msg = 'Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + s + '\nSigning address:' + a[0]
# self.show_message(msg)
2012-02-07 06:00:12 -08:00
2011-11-04 10:00:37 -07:00
return False
def create_history_tab(self):
self.history_list = Gtk.ListStore(str, str, str, str, 'gboolean', str, str, str, str)
treeview = Gtk.TreeView(model=self.history_list)
2011-11-04 10:00:37 -07:00
self.history_treeview = treeview
treeview.set_tooltip_column(7)
treeview.show()
treeview.connect('key-press-event', self.treeview_key_press)
2011-12-12 22:33:23 -08:00
treeview.connect('button-press-event', self.treeview_button_press)
2011-11-04 10:00:37 -07:00
tvcolumn = Gtk.TreeViewColumn('')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererPixbuf()
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, False)
tvcolumn.set_attributes(cell, stock_id=1)
tvcolumn = Gtk.TreeViewColumn('Date')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, False)
tvcolumn.add_attribute(cell, 'text', 2)
tvcolumn = Gtk.TreeViewColumn('Description')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
cell.set_property('foreground', 'grey')
2012-03-30 14:44:54 -07:00
cell.set_property('family', MONOSPACE_FONT)
2011-11-04 10:00:37 -07:00
cell.set_property('editable', True)
def edited_cb(cell, path, new_text, h_list):
tx = h_list.get_value( h_list.get_iter(path), 0)
2013-11-07 16:18:12 -08:00
self.wallet.set_label(tx,new_text)
2011-11-04 10:00:37 -07:00
self.update_history_tab()
cell.connect('edited', edited_cb, self.history_list)
def editing_started(cell, entry, path, h_list):
tx = h_list.get_value( h_list.get_iter(path), 0)
if not self.wallet.labels.get(tx): entry.set_text('')
cell.connect('editing-started', editing_started, self.history_list)
tvcolumn.set_expand(True)
tvcolumn.pack_start(cell, True)
tvcolumn.set_attributes(cell, text=3, foreground_set = 4)
tvcolumn = Gtk.TreeViewColumn('Amount')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
cell.set_alignment(1, 0.5)
2012-03-30 14:44:54 -07:00
cell.set_property('family', MONOSPACE_FONT)
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, False)
tvcolumn.add_attribute(cell, 'text', 5)
tvcolumn = Gtk.TreeViewColumn('Balance')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
cell.set_alignment(1, 0.5)
2012-03-30 14:44:54 -07:00
cell.set_property('family', MONOSPACE_FONT)
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, False)
tvcolumn.add_attribute(cell, 'text', 6)
tvcolumn = Gtk.TreeViewColumn('Tooltip')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, False)
tvcolumn.add_attribute(cell, 'text', 7)
tvcolumn.set_visible(False)
scroll = Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
2011-11-04 10:00:37 -07:00
scroll.add(treeview)
self.add_tab(scroll, 'History')
self.update_history_tab()
def create_recv_tab(self):
self.recv_list = Gtk.ListStore(str, str, str, str, str)
2011-11-04 10:00:37 -07:00
self.add_tab( self.make_address_list(True), 'Receive')
self.update_receiving_tab()
def create_book_tab(self):
self.addressbook_list = Gtk.ListStore(str, str, str)
2011-11-04 10:00:37 -07:00
self.add_tab( self.make_address_list(False), 'Contacts')
self.update_sending_tab()
def make_address_list(self, is_recv):
liststore = self.recv_list if is_recv else self.addressbook_list
treeview = Gtk.TreeView(model= liststore)
2011-11-04 10:00:37 -07:00
treeview.connect('key-press-event', self.treeview_key_press)
2012-02-07 06:00:12 -08:00
treeview.connect('button-press-event', self.treeview_button_press)
2011-11-04 10:00:37 -07:00
treeview.show()
2012-02-07 06:00:12 -08:00
if not is_recv:
self.contacts_treeview = treeview
2011-11-04 10:00:37 -07:00
tvcolumn = Gtk.TreeViewColumn('Address')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2012-03-30 14:44:54 -07:00
cell.set_property('family', MONOSPACE_FONT)
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 0)
tvcolumn = Gtk.TreeViewColumn('Label')
2011-11-04 10:00:37 -07:00
tvcolumn.set_expand(True)
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
cell.set_property('editable', True)
def edited_cb2(cell, path, new_text, liststore):
address = liststore.get_value( liststore.get_iter(path), 0)
2013-11-07 16:18:12 -08:00
self.wallet.set_label(address, new_text)
2011-11-04 10:00:37 -07:00
self.update_receiving_tab()
self.update_sending_tab()
self.update_history_tab()
cell.connect('edited', edited_cb2, liststore)
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 1)
tvcolumn = Gtk.TreeViewColumn('Tx')
2011-11-04 10:00:37 -07:00
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
2011-11-04 10:00:37 -07:00
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 2)
if is_recv:
tvcolumn = Gtk.TreeViewColumn('Balance')
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 3)
tvcolumn = Gtk.TreeViewColumn('Type')
treeview.append_column(tvcolumn)
cell = Gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 4)
scroll = Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
2011-11-04 10:00:37 -07:00
scroll.add(treeview)
hbox = Gtk.HBox()
2011-12-16 03:22:04 -08:00
if not is_recv:
button = Gtk.Button("New")
2011-12-19 03:35:39 -08:00
button.connect("clicked", self.newaddress_dialog)
2011-12-16 03:22:04 -08:00
button.show()
hbox.pack_start(button,False, False, 0)
2011-11-04 10:00:37 -07:00
2011-11-22 05:41:41 -08:00
def showqrcode(w, treeview, liststore):
2014-07-13 16:24:29 -07:00
import qrcode
2011-11-22 05:41:41 -08:00
path, col = treeview.get_cursor()
if not path: return
address = liststore.get_value(liststore.get_iter(path), 0)
2014-07-13 16:24:29 -07:00
qr = qrcode.QRCode()
qr.add_data(address)
2011-11-22 05:41:41 -08:00
boxsize = 7
2014-07-13 16:24:29 -07:00
matrix = qr.get_matrix()
boxcount_row = len(matrix)
size = (boxcount_row + 4) * boxsize
def area_expose_cb(area, cr):
2011-11-22 05:41:41 -08:00
style = area.get_style()
Gdk.cairo_set_source_color(cr, style.white)
cr.rectangle(0, 0, size, size)
cr.fill()
Gdk.cairo_set_source_color(cr, style.black)
for r in range(boxcount_row):
for c in range(boxcount_row):
2014-07-13 16:24:29 -07:00
if matrix[r][c]:
cr.rectangle((c + 2) * boxsize, (r + 2) * boxsize, boxsize, boxsize)
cr.fill()
area = Gtk.DrawingArea()
2011-11-22 05:41:41 -08:00
area.set_size_request(size, size)
area.connect("draw", area_expose_cb)
2011-11-22 05:41:41 -08:00
area.show()
dialog = Gtk.Dialog(address, parent=self.window, flags=Gtk.DialogFlags.MODAL, buttons = ("ok",1))
2011-11-22 05:41:41 -08:00
dialog.vbox.add(area)
dialog.run()
dialog.destroy()
button = Gtk.Button("QR")
2011-11-22 05:41:41 -08:00
button.connect("clicked", showqrcode, treeview, liststore)
button.show()
hbox.pack_start(button,False, False, 0)
2011-11-22 05:41:41 -08:00
button = Gtk.Button("Copy to clipboard")
2011-11-04 10:00:37 -07:00
def copy2clipboard(w, treeview, liststore):
2011-11-13 15:20:49 -08:00
import platform
2011-11-04 10:00:37 -07:00
path, col = treeview.get_cursor()
if path:
address = liststore.get_value( liststore.get_iter(path), 0)
2011-11-13 15:20:49 -08:00
if platform.system() == 'Windows':
from Tkinter import Tk
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append( address )
r.destroy()
else:
atom = Gdk.atom_intern('CLIPBOARD', True)
c = Gtk.Clipboard.get(atom)
c.set_text( address, len(address) )
2011-11-04 10:00:37 -07:00
button.connect("clicked", copy2clipboard, treeview, liststore)
button.show()
hbox.pack_start(button,False, False, 0)
2011-11-04 10:00:37 -07:00
2013-11-10 13:36:52 -08:00
if is_recv:
button = Gtk.Button("Freeze")
2013-11-10 13:36:52 -08:00
def freeze_address(w, treeview, liststore, wallet):
path, col = treeview.get_cursor()
if path:
address = liststore.get_value( liststore.get_iter(path), 0)
if address in wallet.frozen_addresses:
wallet.unfreeze(address)
else:
wallet.freeze(address)
self.update_receiving_tab()
button.connect("clicked", freeze_address, treeview, liststore, self.wallet)
button.show()
hbox.pack_start(button,False, False, 0)
2013-11-10 13:36:52 -08:00
2011-11-04 10:00:37 -07:00
if not is_recv:
button = Gtk.Button("Pay to")
2011-11-04 10:00:37 -07:00
def payto(w, treeview, liststore):
path, col = treeview.get_cursor()
if path:
address = liststore.get_value( liststore.get_iter(path), 0)
self.payto_entry.set_text( address )
self.notebook.set_current_page(1)
2012-02-03 13:25:50 -08:00
self.amount_entry.grab_focus()
2011-11-04 10:00:37 -07:00
button.connect("clicked", payto, treeview, liststore)
button.show()
hbox.pack_start(button,False, False, 0)
2011-11-04 10:00:37 -07:00
vbox = Gtk.VBox()
vbox.pack_start(scroll,True, True, 0)
vbox.pack_start(hbox, False, False, 0)
2011-11-04 10:00:37 -07:00
return vbox
def update_status_bar(self):
2014-07-28 00:28:02 -07:00
2012-02-08 04:37:14 -08:00
if self.funds_error:
text = "Not enough funds"
2014-07-28 00:28:02 -07:00
elif self.network.is_connected():
2014-08-01 08:23:24 -07:00
host, port, _,_,_ = self.network.get_parameters()
port = int(port)
2014-07-28 00:28:02 -07:00
height = self.network.get_local_height()
self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks"%(host, port, height))
if not self.wallet.up_to_date:
self.status_image.set_from_stock(Gtk.STOCK_REFRESH, Gtk.IconSize.MENU)
2012-02-08 05:13:11 -08:00
text = "Synchronizing..."
else:
self.status_image.set_from_stock(Gtk.STOCK_YES, Gtk.IconSize.MENU)
2012-02-08 04:37:14 -08:00
c, u = self.wallet.get_balance()
2013-09-11 03:05:28 -07:00
text = "Balance: %s "%( format_satoshis(c,False,self.num_zeros) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.num_zeros).strip() )
2011-11-04 10:00:37 -07:00
else:
self.status_image.set_from_stock(Gtk.STOCK_NO, Gtk.IconSize.MENU)
self.network_button.set_tooltip_text("Not connected.")
2012-02-08 04:37:14 -08:00
text = "Not connected"
2011-11-04 10:00:37 -07:00
self.status_bar.pop(self.context_id)
2012-02-08 04:37:14 -08:00
self.status_bar.push(self.context_id, text)
if self.wallet.up_to_date and self.wallet_updated:
2012-02-08 04:37:14 -08:00
self.update_history_tab()
self.update_receiving_tab()
# addressbook too...
self.info.set_text( self.network.banner )
self.wallet_updated = False
2011-11-04 10:00:37 -07:00
def update_receiving_tab(self):
self.recv_list.clear()
for address in self.wallet.addresses(True):
Type = "R"
c = u = 0
if self.wallet.is_change(address): Type = "C"
if address in self.wallet.imported_keys.keys():
Type = "I"
c, u = self.wallet.get_addr_balance(address)
if address in self.wallet.frozen_addresses: Type = Type + "F"
2011-11-04 10:00:37 -07:00
label = self.wallet.labels.get(address)
2011-12-21 09:10:21 -08:00
h = self.wallet.history.get(address,[])
2012-11-04 02:57:12 -08:00
n = len(h)
tx = "0" if n==0 else "%d"%n
self.recv_list.append((address, label, tx, format_satoshis(c,False,self.num_zeros), Type ))
2011-11-04 10:00:37 -07:00
def update_sending_tab(self):
# detect addresses that are not mine in history, add them here...
self.addressbook_list.clear()
2013-03-14 08:32:05 -07:00
#for alias, v in self.wallet.aliases.items():
# s, target = v
# label = self.wallet.labels.get(alias)
# self.addressbook_list.append((alias, label, '-'))
2012-02-07 06:00:12 -08:00
2011-11-04 10:00:37 -07:00
for address in self.wallet.addressbook:
label = self.wallet.labels.get(address)
2013-03-16 10:17:50 -07:00
n = self.wallet.get_num_tx(address)
2013-02-27 12:22:52 -08:00
self.addressbook_list.append((address, label, "%d"%n))
2011-11-04 10:00:37 -07:00
def update_history_tab(self):
cursor = self.history_treeview.get_cursor()[0]
self.history_list.clear()
for item in self.wallet.get_tx_history():
tx_hash, conf, is_mine, value, fee, balance, timestamp = item
if conf > 0:
try:
time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
2013-11-10 12:30:57 -08:00
except Exception:
time_str = "------"
conf_icon = Gtk.STOCK_APPLY
elif conf == -1:
time_str = 'unverified'
conf_icon = None
2011-11-04 10:00:37 -07:00
else:
time_str = 'pending'
conf_icon = Gtk.STOCK_EXECUTE
2012-11-05 02:08:16 -08:00
label, is_default_label = self.wallet.get_label(tx_hash)
tooltip = tx_hash + "\n%d confirmations"%conf if tx_hash else ''
2013-03-14 08:32:05 -07:00
details = self.get_tx_details(tx_hash)
2011-11-22 09:54:10 -08:00
self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label,
2013-09-11 03:05:28 -07:00
format_satoshis(value,True,self.num_zeros, whitespaces=True),
format_satoshis(balance,False,self.num_zeros, whitespaces=True), tooltip, details] )
2011-11-04 10:00:37 -07:00
if cursor: self.history_treeview.set_cursor( cursor )
2013-03-14 08:32:05 -07:00
def get_tx_details(self, tx_hash):
import datetime
if not tx_hash: return ''
tx = self.wallet.transactions.get(tx_hash)
2013-04-06 12:20:23 -07:00
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
2013-03-14 08:32:05 -07:00
conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
if timestamp:
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
else:
time_str = 'pending'
inputs = map(lambda x: x.get('address'), tx.inputs)
2014-07-13 16:24:29 -07:00
outputs = map(lambda x: x[0], tx.get_outputs())
2013-03-14 08:32:05 -07:00
tx_details = "Transaction Details" +"\n\n" \
+ "Transaction ID:\n" + tx_hash + "\n\n" \
+ "Status: %d confirmations\n"%conf
if is_mine:
if fee:
tx_details += "Amount sent: %s\n"% format_satoshis(v-fee, False) \
+ "Transaction fee: %s\n"% format_satoshis(fee, False)
else:
tx_details += "Amount sent: %s\n"% format_satoshis(v, False) \
+ "Transaction fee: unknown\n"
else:
tx_details += "Amount received: %s\n"% format_satoshis(v, False) \
tx_details += "Date: %s\n\n"%time_str \
+ "Inputs:\n-"+ '\n-'.join(inputs) + "\n\n" \
+ "Outputs:\n-"+ '\n-'.join(outputs)
return tx_details
2011-11-04 10:00:37 -07:00
2011-12-19 03:35:39 -08:00
def newaddress_dialog(self, w):
2011-11-04 10:00:37 -07:00
2012-02-12 15:00:33 -08:00
title = "New Contact"
dialog = Gtk.Dialog(title, parent=self.window,
flags=Gtk.DialogFlags.MODAL,
2011-12-19 03:35:39 -08:00
buttons= ("cancel", 0, "ok",1) )
dialog.show()
2011-11-04 10:00:37 -07:00
label = Gtk.HBox()
label_label = Gtk.Label(label='Label:')
2011-12-19 03:35:39 -08:00
label_label.set_size_request(120,10)
label_label.show()
label.pack_start(label_label, True, True, 0)
label_entry = Gtk.Entry()
2011-12-19 03:35:39 -08:00
label_entry.show()
label.pack_start(label_entry, True, True, 0)
2011-12-19 03:35:39 -08:00
label.show()
dialog.vbox.pack_start(label, False, True, 5)
address = Gtk.HBox()
address_label = Gtk.Label(label='Address:')
2011-12-19 03:35:39 -08:00
address_label.set_size_request(120,10)
address_label.show()
address.pack_start(address_label, True, True, 0)
address_entry = Gtk.Entry()
2011-12-19 03:35:39 -08:00
address_entry.show()
address.pack_start(address_entry, True, True, 0)
2011-12-19 03:35:39 -08:00
address.show()
dialog.vbox.pack_start(address, False, True, 5)
result = dialog.run()
address = address_entry.get_text()
label = label_entry.get_text()
dialog.destroy()
2011-11-04 10:00:37 -07:00
2011-12-19 03:35:39 -08:00
if result == 1:
if is_valid(address):
2013-05-02 01:10:22 -07:00
self.wallet.add_contact(address,label)
2011-12-19 03:35:39 -08:00
self.update_sending_tab()
else:
errorDialog = Gtk.MessageDialog(
2011-12-19 03:35:39 -08:00
parent=self.window,
flags=Gtk.DialogFlags.MODAL,
buttons= Gtk.ButtonsType.CLOSE,
2011-12-19 03:35:39 -08:00
message_format = "Invalid address")
errorDialog.show()
errorDialog.run()
errorDialog.destroy()
2011-12-08 12:43:52 -08:00
2011-11-04 10:00:37 -07:00
2012-02-14 00:52:03 -08:00
class ElectrumGui():
2013-09-11 03:05:28 -07:00
def __init__(self, config, network):
self.network = network
self.config = config
2013-09-11 03:05:28 -07:00
2012-02-14 00:52:03 -08:00
2012-02-14 03:45:39 -08:00
def main(self, url=None):
storage = WalletStorage(self.config)
if not storage.file_exists:
action = self.restore_or_create()
if not action:
exit()
self.wallet = wallet = Wallet(storage)
gap = self.config.get('gap_limit', 5)
if gap != 5:
wallet.gap_limit = gap
wallet.storage.put('gap_limit', gap, True)
if action == 'create':
2014-04-30 00:31:20 -07:00
seed = wallet.make_seed()
show_seed_dialog(seed, None)
r = change_password_dialog(False, None)
password = r[2] if r else None
2014-04-30 00:31:20 -07:00
wallet.add_seed(seed, password)
wallet.create_master_keys(password)
wallet.create_main_account(password)
wallet.synchronize() # generate first addresses offline
elif action == 'restore':
seed = self.seed_dialog()
2014-04-30 00:31:20 -07:00
if not seed:
exit()
r = change_password_dialog(False, None)
password = r[2] if r else None
2014-04-30 00:31:20 -07:00
wallet.add_seed(seed, password)
wallet.create_master_keys(password)
wallet.create_main_account(password)
else:
exit()
else:
self.wallet = Wallet(storage)
action = None
self.wallet.start_threads(self.network)
if action == 'restore':
self.restore_wallet(wallet)
w = ElectrumWindow(self.wallet, self.config, self.network)
if url: w.set_url(url)
Gtk.main()
2011-11-04 10:00:37 -07:00
2012-02-14 00:52:03 -08:00
def restore_or_create(self):
return restore_create_dialog()
def seed_dialog(self):
return run_recovery_dialog()
def network_dialog(self):
return run_network_dialog( self.network, parent=None )
def restore_wallet(self, wallet):
dialog = Gtk.MessageDialog(
parent = None,
flags = Gtk.DialogFlags.MODAL,
buttons = Gtk.ButtonsType.CANCEL,
message_format = "Please wait..." )
dialog.show()
def recover_thread( wallet, dialog ):
wallet.restore(lambda x:x)
GObject.idle_add( dialog.destroy )
thread.start_new_thread( recover_thread, ( wallet, dialog ) )
r = dialog.run()
dialog.destroy()
if r==Gtk.ResponseType.CANCEL: return False
2012-11-20 12:36:06 -08:00
if not wallet.is_found():
show_message("No transactions found for this seed")
return True