2012-02-13 07:49:20 -08:00
import sys , time , datetime , re
2012-02-11 04:14:12 -08:00
2012-02-11 08:02:28 -08:00
# todo: see PySide
2012-02-11 04:14:12 -08:00
from PyQt4 . QtGui import *
2012-02-11 08:02:28 -08:00
from PyQt4 . QtCore import *
2012-02-11 04:14:12 -08:00
import PyQt4 . QtCore as QtCore
2012-02-11 08:02:28 -08:00
import PyQt4 . QtGui as QtGui
2012-02-11 04:14:12 -08:00
2012-02-11 13:19:22 -08:00
from wallet import format_satoshis
2012-02-13 06:22:15 -08:00
from decimal import Decimal
2012-02-11 13:19:22 -08:00
2012-02-14 00:52:03 -08:00
2012-02-11 04:14:12 -08:00
2012-02-11 13:19:22 -08:00
class Sender ( QtCore . QThread ) :
def run ( self ) :
while True :
self . emit ( QtCore . SIGNAL ( ' testsignal ' ) )
time . sleep ( 0.5 )
2012-02-13 12:36:41 -08:00
class StatusBarButton ( QPushButton ) :
def __init__ ( self , icon , tooltip , func ) :
QPushButton . __init__ ( self , icon , ' ' )
self . setToolTip ( tooltip )
self . setFlat ( True )
self . setMaximumWidth ( 25 )
self . clicked . connect ( func )
self . func = func
def keyPressEvent ( self , e ) :
if e . key ( ) == QtCore . Qt . Key_Return :
apply ( self . func , ( ) )
2012-02-11 04:14:12 -08:00
2012-02-14 00:52:03 -08:00
def ok_cancel_buttons ( dialog ) :
hbox = QHBoxLayout ( )
hbox . addStretch ( 1 )
b = QPushButton ( " OK " )
hbox . addWidget ( b )
b . clicked . connect ( dialog . accept )
b = QPushButton ( " Cancel " )
hbox . addWidget ( b )
b . clicked . connect ( dialog . reject )
return hbox
2012-02-12 00:52:26 -08:00
class ElectrumWindow ( QMainWindow ) :
2012-02-11 04:14:12 -08:00
def __init__ ( self , wallet ) :
2012-02-11 08:38:44 -08:00
QMainWindow . __init__ ( self )
2012-02-11 04:14:12 -08:00
self . wallet = wallet
2012-02-11 13:19:22 -08:00
2012-02-13 08:16:02 -08:00
self . tabs = tabs = QTabWidget ( self )
2012-02-12 00:52:26 -08:00
tabs . addTab ( self . create_history_tab ( ) , ' History ' )
2012-02-11 08:38:44 -08:00
tabs . addTab ( self . create_send_tab ( ) , ' Send ' )
2012-02-12 00:52:26 -08:00
tabs . addTab ( self . create_receive_tab ( ) , ' Receive ' )
tabs . addTab ( self . create_contacts_tab ( ) , ' Contacts ' )
tabs . addTab ( self . create_wall_tab ( ) , ' Wall ' )
2012-02-11 13:19:22 -08:00
tabs . setMinimumSize ( 600 , 400 )
2012-02-13 10:02:48 -08:00
tabs . setSizePolicy ( QSizePolicy . Expanding , QSizePolicy . Expanding )
2012-02-12 00:52:26 -08:00
self . setCentralWidget ( tabs )
2012-02-11 08:38:44 -08:00
self . create_status_bar ( )
2012-02-13 05:52:59 -08:00
self . setGeometry ( 100 , 100 , 840 , 400 )
2012-02-13 08:28:50 -08:00
self . setWindowTitle ( ' Electrum ' + self . wallet . electrum_version )
2012-02-11 08:38:44 -08:00
self . show ( )
2012-02-12 00:52:26 -08:00
QShortcut ( QKeySequence ( " Ctrl+W " ) , self , self . close )
QShortcut ( QKeySequence ( " Ctrl+Q " ) , self , self . close )
2012-02-13 05:52:59 -08:00
QShortcut ( QKeySequence ( " Ctrl+PgUp " ) , self , lambda : tabs . setCurrentIndex ( ( tabs . currentIndex ( ) - 1 ) % tabs . count ( ) ) )
QShortcut ( QKeySequence ( " Ctrl+PgDown " ) , self , lambda : tabs . setCurrentIndex ( ( tabs . currentIndex ( ) + 1 ) % tabs . count ( ) ) )
2012-02-12 00:52:26 -08:00
2012-02-14 00:52:03 -08:00
2012-02-12 00:52:26 -08:00
2012-02-11 13:19:22 -08:00
def connect_slots ( self , sender ) :
self . connect ( sender , QtCore . SIGNAL ( ' testsignal ' ) , self . update_wallet )
2012-02-12 00:52:26 -08:00
2012-02-11 13:19:22 -08:00
def update_wallet ( self ) :
if self . wallet . interface . is_connected :
if self . wallet . interface . blocks == 0 :
text = " Server not ready "
2012-02-13 08:28:50 -08:00
icon = QIcon ( " icons/status_disconnected.png " )
2012-02-11 13:19:22 -08:00
elif not self . wallet . interface . was_polled :
text = " Synchronizing... "
2012-02-12 08:19:16 -08:00
icon = QIcon ( " icons/status_waiting.svg " )
2012-02-11 13:19:22 -08:00
else :
c , u = self . wallet . get_balance ( )
text = " Balance: %s " % ( format_satoshis ( c ) )
if u : text + = " [ %s unconfirmed] " % ( format_satoshis ( u , True ) )
2012-02-12 08:19:16 -08:00
icon = QIcon ( " icons/status_connected.png " )
2012-02-11 13:19:22 -08:00
else :
text = " Not connected "
2012-02-13 08:28:50 -08:00
icon = QIcon ( " icons/status_disconnected.png " )
2012-02-12 08:19:16 -08:00
2012-02-11 13:19:22 -08:00
self . statusBar ( ) . showMessage ( text )
2012-02-12 08:19:16 -08:00
self . status_button . setIcon ( icon )
2012-02-11 13:19:22 -08:00
if self . wallet . interface . was_updated :
self . wallet . interface . was_updated = False
2012-02-12 00:52:26 -08:00
self . textbox . setText ( self . wallet . interface . message )
2012-02-11 22:27:32 -08:00
self . update_history_tab ( )
2012-02-11 22:45:18 -08:00
self . update_receive_tab ( )
self . update_contacts_tab ( )
2012-02-11 22:27:32 -08:00
2012-02-11 04:14:12 -08:00
2012-02-11 08:02:28 -08:00
def create_history_tab ( self ) :
2012-02-11 22:27:32 -08:00
self . history_list = w = QTreeWidget ( self )
2012-02-13 05:52:59 -08:00
#print w.getContentsMargins()
2012-02-11 22:27:32 -08:00
w . setColumnCount ( 5 )
2012-02-12 01:58:38 -08:00
w . setColumnWidth ( 0 , 40 )
w . setColumnWidth ( 1 , 140 )
2012-02-13 05:52:59 -08:00
w . setColumnWidth ( 2 , 350 )
w . setColumnWidth ( 3 , 140 )
w . setColumnWidth ( 4 , 140 )
2012-02-12 12:41:42 -08:00
w . setHeaderLabels ( [ ' ' , ' Date ' , ' Description ' , ' Amount ' , ' Balance ' ] )
2012-02-12 09:02:11 -08:00
self . connect ( w , SIGNAL ( ' itemActivated(QTreeWidgetItem*, int) ' ) , self . tx_details )
2012-02-12 12:41:42 -08:00
self . connect ( w , SIGNAL ( ' itemDoubleClicked(QTreeWidgetItem*, int) ' ) , self . tx_label_clicked )
self . connect ( w , SIGNAL ( ' itemChanged(QTreeWidgetItem*, int) ' ) , self . tx_label_changed )
2012-02-11 22:27:32 -08:00
return w
2012-02-12 09:02:11 -08:00
def tx_details ( self , item , column ) :
tx_hash = str ( item . toolTip ( 0 ) )
tx = self . wallet . tx_history . get ( tx_hash )
if tx [ ' height ' ] :
conf = self . wallet . interface . blocks - tx [ ' height ' ] + 1
time_str = datetime . datetime . fromtimestamp ( tx [ ' nTime ' ] ) . isoformat ( ' ' ) [ : - 3 ]
else :
conf = 0
time_str = ' pending '
tx_details = " Transaction Details: \n \n " \
+ " Transaction ID: \n " + tx_hash + " \n \n " \
+ " Status: %d confirmations \n \n " % conf \
+ " Date: %s \n \n " % time_str \
+ " Inputs: \n - " + ' \n - ' . join ( tx [ ' inputs ' ] ) + " \n \n " \
+ " Outputs: \n - " + ' \n - ' . join ( tx [ ' outputs ' ] )
r = self . wallet . receipts . get ( tx_hash )
if r :
tx_details + = " \n _______________________________________ " \
+ ' \n \n Signed URI: ' + r [ 2 ] \
+ " \n \n Signed by: " + r [ 0 ] \
+ ' \n \n Signature: ' + r [ 1 ]
QMessageBox . information ( self , ' Details ' , tx_details , ' OK ' )
2012-02-12 12:41:42 -08:00
def tx_label_clicked ( self , item , column ) :
if column == 2 and item . isSelected ( ) :
tx_hash = str ( item . toolTip ( 0 ) )
self . is_edit = True
#if not self.wallet.labels.get(tx_hash): item.setText(2,'')
item . setFlags ( Qt . ItemIsEditable | Qt . ItemIsSelectable | Qt . ItemIsUserCheckable | Qt . ItemIsEnabled | Qt . ItemIsDragEnabled )
self . history_list . editItem ( item , column )
item . setFlags ( Qt . ItemIsSelectable | Qt . ItemIsUserCheckable | Qt . ItemIsEnabled | Qt . ItemIsDragEnabled )
self . is_edit = False
def tx_label_changed ( self , item , column ) :
if self . is_edit :
return
self . is_edit = True
tx_hash = str ( item . toolTip ( 0 ) )
tx = self . wallet . tx_history . get ( tx_hash )
s = self . wallet . labels . get ( tx_hash )
text = str ( item . text ( 2 ) )
2012-02-12 15:00:33 -08:00
if text :
2012-02-12 12:41:42 -08:00
self . wallet . labels [ tx_hash ] = text
item . setForeground ( 2 , QBrush ( QColor ( ' black ' ) ) )
else :
if s : self . wallet . labels . pop ( tx_hash )
text = tx [ ' default_label ' ]
item . setText ( 2 , text )
item . setForeground ( 2 , QBrush ( QColor ( ' gray ' ) ) )
self . is_edit = False
2012-02-12 09:02:11 -08:00
2012-02-12 15:00:33 -08:00
def address_label_clicked ( self , item , column , l ) :
if column == 1 and item . isSelected ( ) :
item . setFlags ( Qt . ItemIsEditable | Qt . ItemIsSelectable | Qt . ItemIsUserCheckable | Qt . ItemIsEnabled | Qt . ItemIsDragEnabled )
l . editItem ( item , column )
item . setFlags ( Qt . ItemIsSelectable | Qt . ItemIsUserCheckable | Qt . ItemIsEnabled | Qt . ItemIsDragEnabled )
def address_label_changed ( self , item , column , l ) :
addr = str ( item . text ( 0 ) )
text = str ( item . text ( 1 ) )
if text :
self . wallet . labels [ addr ] = text
else :
s = self . wallet . labels . get ( addr )
if s : self . wallet . labels . pop ( addr )
self . update_history_tab ( )
2012-02-11 22:27:32 -08:00
def update_history_tab ( self ) :
self . history_list . clear ( )
balance = 0
for tx in self . wallet . get_tx_history ( ) :
tx_hash = tx [ ' tx_hash ' ]
if tx [ ' height ' ] :
conf = self . wallet . interface . blocks - tx [ ' height ' ] + 1
time_str = datetime . datetime . fromtimestamp ( tx [ ' nTime ' ] ) . isoformat ( ' ' ) [ : - 3 ]
2012-02-12 05:27:36 -08:00
icon = QIcon ( " icons/confirmed.png " )
2012-02-11 22:27:32 -08:00
else :
conf = 0
time_str = ' pending '
2012-02-12 05:27:36 -08:00
icon = QIcon ( " icons/unconfirmed.svg " )
2012-02-11 22:27:32 -08:00
v = tx [ ' value ' ]
balance + = v
label = self . wallet . labels . get ( tx_hash )
is_default_label = ( label == ' ' ) or ( label is None )
if is_default_label : label = tx [ ' default_label ' ]
2012-02-12 09:02:11 -08:00
2012-02-12 01:58:38 -08:00
item = QTreeWidgetItem ( [ ' ' , time_str , label , format_satoshis ( v , True ) , format_satoshis ( balance ) ] )
2012-02-12 02:47:35 -08:00
item . setFont ( 2 , QFont ( ' monospace ' ) )
2012-02-12 03:26:35 -08:00
item . setFont ( 3 , QFont ( ' monospace ' ) )
item . setFont ( 4 , QFont ( ' monospace ' ) )
2012-02-12 09:02:11 -08:00
item . setToolTip ( 0 , tx_hash )
2012-02-12 02:51:06 -08:00
if is_default_label :
2012-02-12 12:41:42 -08:00
item . setForeground ( 2 , QBrush ( QColor ( ' grey ' ) ) )
2012-02-12 01:58:38 -08:00
item . setIcon ( 0 , icon )
self . history_list . insertTopLevelItem ( 0 , item )
2012-02-11 22:27:32 -08:00
2012-02-11 08:02:28 -08:00
def create_send_tab ( self ) :
2012-02-11 22:27:32 -08:00
w = QWidget ( )
2012-02-13 10:02:48 -08:00
grid = QGridLayout ( )
2012-02-11 22:27:32 -08:00
grid . setSpacing ( 8 )
2012-02-13 05:52:59 -08:00
grid . setColumnMinimumWidth ( 3 , 300 )
grid . setColumnStretch ( 4 , 1 )
2012-02-11 22:27:32 -08:00
2012-02-13 10:02:48 -08:00
self . payto_entry = paytoEdit = QLineEdit ( )
2012-02-11 22:27:32 -08:00
grid . addWidget ( QLabel ( ' Pay to ' ) , 1 , 0 )
2012-02-13 05:52:59 -08:00
grid . addWidget ( paytoEdit , 1 , 1 , 1 , 3 )
2012-02-11 22:27:32 -08:00
2012-02-13 10:02:48 -08:00
descriptionEdit = QLineEdit ( )
2012-02-11 22:27:32 -08:00
grid . addWidget ( QLabel ( ' Description ' ) , 2 , 0 )
2012-02-13 05:52:59 -08:00
grid . addWidget ( descriptionEdit , 2 , 1 , 1 , 3 )
2012-02-11 22:27:32 -08:00
2012-02-13 10:02:48 -08:00
amountEdit = QLineEdit ( )
2012-02-11 22:27:32 -08:00
grid . addWidget ( QLabel ( ' Amount ' ) , 3 , 0 )
2012-02-13 05:52:59 -08:00
grid . addWidget ( amountEdit , 3 , 1 , 1 , 2 )
2012-02-11 22:27:32 -08:00
2012-02-13 10:02:48 -08:00
feeEdit = QLineEdit ( )
2012-02-11 22:27:32 -08:00
grid . addWidget ( QLabel ( ' Fee ' ) , 4 , 0 )
2012-02-13 05:52:59 -08:00
grid . addWidget ( feeEdit , 4 , 1 , 1 , 2 )
2012-02-11 22:27:32 -08:00
2012-02-13 07:49:20 -08:00
b = QPushButton ( " Send " )
b . clicked . connect ( lambda : self . do_send ( paytoEdit , descriptionEdit , amountEdit , feeEdit ) )
grid . addWidget ( b , 5 , 1 )
2012-02-13 05:52:59 -08:00
2012-02-13 07:49:20 -08:00
b = QPushButton ( " Clear " )
b . clicked . connect ( lambda : map ( lambda x : x . setText ( ' ' ) , [ paytoEdit , descriptionEdit , amountEdit , feeEdit ] ) )
grid . addWidget ( b , 5 , 2 )
2012-02-12 15:00:33 -08:00
2012-02-11 22:27:32 -08:00
w . setLayout ( grid )
w . show ( )
w2 = QWidget ( )
2012-02-13 10:02:48 -08:00
vbox = QVBoxLayout ( )
2012-02-11 22:27:32 -08:00
vbox . addWidget ( w )
vbox . addStretch ( 1 )
w2 . setLayout ( vbox )
return w2
2012-02-11 08:02:28 -08:00
2012-02-13 07:49:20 -08:00
def do_send ( self , payto_entry , label_entry , amount_entry , fee_entry ) :
label = str ( label_entry . text ( ) )
r = str ( payto_entry . text ( ) )
r = r . strip ( )
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-13 07:49:20 -08:00
if not to_address :
return
elif m2 :
to_address = m2 . group ( 5 )
else :
to_address = r
if not self . wallet . is_valid ( to_address ) :
QMessageBox . warning ( self , ' Error ' , ' Invalid Bitcoin Address: \n ' + to_address , ' OK ' )
return
try :
amount = int ( Decimal ( str ( amount_entry . text ( ) ) ) * 100000000 )
except :
QMessageBox . warning ( self , ' Error ' , ' Invalid Amount ' , ' OK ' )
return
try :
fee = int ( Decimal ( str ( fee_entry . text ( ) ) ) * 100000000 )
except :
QMessageBox . warning ( self , ' Error ' , ' Invalid Fee ' , ' OK ' )
return
if self . wallet . use_encryption :
password = self . password_dialog ( )
if not password :
return
else :
password = None
try :
tx = self . wallet . mktx ( to_address , amount , label , password , fee )
except BaseException , e :
self . show_message ( e . message )
return
status , msg = self . wallet . sendtx ( tx )
if status :
QMessageBox . information ( self , ' ' , ' Payment sent. \n ' + msg , ' OK ' )
payto_entry . setText ( " " )
label_entry . setText ( " " )
amount_entry . setText ( " " )
fee_entry . setText ( " " )
self . update_contacts_tab ( )
else :
QMessageBox . warning ( self , ' Error ' , msg , ' OK ' )
2012-02-12 01:58:38 -08:00
def make_address_list ( self , is_recv ) :
2012-02-11 08:02:28 -08:00
2012-02-12 02:26:38 -08:00
l = QTreeWidget ( self )
l . setColumnCount ( 3 )
2012-02-13 05:52:59 -08:00
l . setColumnWidth ( 0 , 350 )
l . setColumnWidth ( 1 , 330 )
2012-02-12 02:26:38 -08:00
l . setColumnWidth ( 2 , 20 )
l . setHeaderLabels ( [ ' Address ' , ' Label ' , ' Tx ' ] )
2012-02-12 01:58:38 -08:00
2012-02-13 10:02:48 -08:00
vbox = QVBoxLayout ( )
2012-02-12 03:37:43 -08:00
vbox . setMargin ( 0 )
2012-02-12 08:19:16 -08:00
vbox . setSpacing ( 0 )
2012-02-12 02:26:38 -08:00
vbox . addWidget ( l )
2012-02-13 10:02:48 -08:00
hbox = QHBoxLayout ( )
2012-02-12 03:37:43 -08:00
hbox . setMargin ( 0 )
2012-02-12 08:19:16 -08:00
hbox . setSpacing ( 0 )
2012-02-13 10:02:48 -08:00
qrButton = QPushButton ( " QR " )
copyButton = QPushButton ( " Copy to Clipboard " )
2012-02-13 08:16:02 -08:00
def copy2clipboard ( l ) :
i = l . currentItem ( )
if not i : return
addr = str ( i . text ( 0 ) )
self . app . clipboard ( ) . setText ( addr )
copyButton . clicked . connect ( lambda : copy2clipboard ( l ) )
2012-02-12 02:26:38 -08:00
hbox . addWidget ( qrButton )
hbox . addWidget ( copyButton )
if not is_recv :
2012-02-13 10:02:48 -08:00
addButton = QPushButton ( " New " )
2012-02-12 15:00:33 -08:00
addButton . clicked . connect ( self . newaddress_dialog )
2012-02-12 02:26:38 -08:00
hbox . addWidget ( addButton )
2012-02-13 10:02:48 -08:00
paytoButton = QPushButton ( ' Pay to ' )
2012-02-13 08:16:02 -08:00
def payto ( l ) :
i = l . currentItem ( )
if not i : return
addr = str ( i . text ( 0 ) )
self . tabs . setCurrentIndex ( 1 )
self . payto_entry . setText ( addr )
paytoButton . clicked . connect ( lambda : payto ( l ) )
2012-02-12 02:26:38 -08:00
hbox . addWidget ( paytoButton )
hbox . addStretch ( 1 )
buttons = QWidget ( )
buttons . setLayout ( hbox )
vbox . addWidget ( buttons )
w = QWidget ( )
w . setLayout ( vbox )
return w , l
def create_receive_tab ( self ) :
2012-02-12 15:00:33 -08:00
w , l = self . make_address_list ( True )
self . connect ( l , SIGNAL ( ' itemDoubleClicked(QTreeWidgetItem*, int) ' ) , lambda a , b : self . address_label_clicked ( a , b , l ) )
self . connect ( l , SIGNAL ( ' itemChanged(QTreeWidgetItem*, int) ' ) , lambda a , b : self . address_label_changed ( a , b , l ) )
self . receive_list = l
return w
def create_contacts_tab ( self ) :
w , l = self . make_address_list ( False )
self . connect ( l , SIGNAL ( ' itemDoubleClicked(QTreeWidgetItem*, int) ' ) , lambda a , b : self . address_label_clicked ( a , b , l ) )
self . connect ( l , SIGNAL ( ' itemChanged(QTreeWidgetItem*, int) ' ) , lambda a , b : self . address_label_changed ( a , b , l ) )
self . connect ( l , SIGNAL ( ' itemActivated(QTreeWidgetItem*, int) ' ) , self . show_contact_details )
self . contacts_list = l
2012-02-12 02:26:38 -08:00
return w
2012-02-12 01:58:38 -08:00
2012-02-11 22:45:18 -08:00
def update_receive_tab ( self ) :
self . receive_list . clear ( )
for address in self . wallet . all_addresses ( ) :
if self . wallet . is_change ( address ) : continue
label = self . wallet . labels . get ( address , ' ' )
n = 0
h = self . wallet . history . get ( address , [ ] )
for item in h :
if not item [ ' is_in ' ] : n = n + 1
tx = " None " if n == 0 else " %d " % n
item = QTreeWidgetItem ( [ address , label , tx ] )
2012-02-12 02:47:35 -08:00
item . setFont ( 0 , QFont ( ' monospace ' ) )
2012-02-11 22:45:18 -08:00
self . receive_list . addTopLevelItem ( item )
2012-02-12 12:41:42 -08:00
def show_contact_details ( self , item , column ) :
m = str ( item . text ( 0 ) )
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 + ' \n Target address: ' + a [ 1 ] + ' \n \n Signed by: ' + s + ' \n Signing address: ' + a [ 0 ]
QMessageBox . information ( self , ' Alias ' , msg , ' OK ' )
2012-02-11 22:45:18 -08:00
def update_contacts_tab ( self ) :
self . contacts_list . clear ( )
for alias , v in self . wallet . aliases . items ( ) :
s , target = v
2012-02-12 08:19:16 -08:00
label = self . wallet . labels . get ( alias , ' ' )
2012-02-11 22:45:18 -08:00
item = QTreeWidgetItem ( [ alias , label , ' - ' ] )
self . contacts_list . addTopLevelItem ( item )
for address in self . wallet . addressbook :
label = self . wallet . labels . get ( address , ' ' )
n = 0
for item in self . wallet . tx_history . values ( ) :
if address in item [ ' outputs ' ] : n = n + 1
tx = " None " if n == 0 else " %d " % n
item = QTreeWidgetItem ( [ address , label , tx ] )
2012-02-12 02:47:35 -08:00
item . setFont ( 0 , QFont ( ' monospace ' ) )
2012-02-11 22:45:18 -08:00
self . contacts_list . addTopLevelItem ( item )
2012-02-11 08:02:28 -08:00
def create_wall_tab ( self ) :
2012-02-11 13:19:22 -08:00
self . textbox = textbox = QTextEdit ( self )
textbox . setReadOnly ( True )
return textbox
2012-02-11 08:02:28 -08:00
2012-02-11 08:38:44 -08:00
def create_status_bar ( self ) :
sb = QStatusBar ( )
2012-02-12 08:19:16 -08:00
sb . setFixedHeight ( 35 )
2012-02-14 00:52:03 -08:00
sb . addPermanentWidget ( StatusBarButton ( QIcon ( " icons/lock.svg " ) , " Password " , lambda : self . change_password_dialog ( self . wallet , self ) ) )
2012-02-13 12:36:41 -08:00
sb . addPermanentWidget ( StatusBarButton ( QIcon ( " icons/preferences.png " ) , " Preferences " , self . settings_dialog ) )
2012-02-14 00:52:03 -08:00
sb . addPermanentWidget ( StatusBarButton ( QIcon ( " icons/seed.png " ) , " Seed " , lambda : self . show_seed_dialog ( self . wallet , self ) ) )
self . status_button = StatusBarButton ( QIcon ( " icons/status_disconnected.png " ) , " Network " , lambda : self . network_dialog ( self . wallet , self ) )
2012-02-13 12:36:41 -08:00
sb . addPermanentWidget ( self . status_button )
2012-02-11 08:38:44 -08:00
self . setStatusBar ( sb )
2012-02-12 15:00:33 -08:00
def newaddress_dialog ( self ) :
2012-02-13 10:02:48 -08:00
text , ok = QInputDialog . getText ( self , ' New Contact ' , ' Address: ' )
2012-02-12 15:00:33 -08:00
address = str ( text )
if ok :
if self . wallet . is_valid ( address ) :
self . wallet . addressbook . append ( address )
self . wallet . save ( )
self . update_contacts_tab ( )
else :
QMessageBox . warning ( self , ' Error ' , ' Invalid Address ' , ' OK ' )
2012-02-14 00:52:03 -08:00
@staticmethod
def show_seed_dialog ( wallet , parent = None ) :
2012-02-12 15:00:33 -08:00
import mnemonic
2012-02-14 00:52:03 -08:00
if wallet . use_encryption :
2012-02-13 06:11:31 -08:00
password = self . password_dialog ( )
if not password : return
else :
password = None
try :
2012-02-14 00:52:03 -08:00
seed = wallet . pw_decode ( wallet . seed , password )
2012-02-13 06:11:31 -08:00
except :
2012-02-14 00:52:03 -08:00
QMessageBox . warning ( parent , ' Error ' , ' Invalid Password ' , ' OK ' )
2012-02-13 06:11:31 -08:00
return
2012-02-12 15:00:33 -08:00
2012-02-13 06:11:31 -08:00
msg = " Your wallet generation seed is: \n \n " + seed \
+ " \n \n Please keep it in a safe place; if you lose it, you will not be able to restore your wallet. \n \n " \
+ " Equivalently, your wallet seed can be stored and recovered with the following mnemonic code: \n \n \" " \
+ ' ' . join ( mnemonic . mn_encode ( seed ) ) + " \" "
2012-02-14 00:52:03 -08:00
QMessageBox . information ( parent , ' Seed ' , msg , ' OK ' )
2012-02-13 06:11:31 -08:00
2012-02-14 00:52:03 -08:00
def question ( self , msg ) :
return QMessageBox . question ( self , ' Message ' , msg , QMessageBox . Yes | QMessageBox . No , QMessageBox . No )
2012-02-13 06:11:31 -08:00
2012-02-14 00:52:03 -08:00
def show_message ( self , msg ) :
QMessageBox . information ( self , ' Message ' , msg , ' OK ' )
@staticmethod
def password_dialog ( parent = None ) :
d = QDialog ( parent )
2012-02-13 05:52:59 -08:00
d . setModal ( 1 )
2012-02-13 06:11:31 -08:00
pw = QLineEdit ( )
pw . setEchoMode ( 2 )
2012-02-13 10:02:48 -08:00
vbox = QVBoxLayout ( )
2012-02-13 06:11:31 -08:00
msg = ' Please enter your password '
2012-02-13 10:02:48 -08:00
vbox . addWidget ( QLabel ( msg ) )
2012-02-13 06:11:31 -08:00
2012-02-13 10:02:48 -08:00
grid = QGridLayout ( )
grid . setSpacing ( 8 )
2012-02-13 06:11:31 -08:00
grid . addWidget ( QLabel ( ' Password ' ) , 1 , 0 )
grid . addWidget ( pw , 1 , 1 )
2012-02-13 10:02:48 -08:00
vbox . addLayout ( grid )
2012-02-13 06:11:31 -08:00
2012-02-14 00:52:03 -08:00
vbox . addLayout ( ok_cancel_buttons ( d ) )
2012-02-13 10:02:48 -08:00
d . setLayout ( vbox )
2012-02-13 06:11:31 -08:00
if not d . exec_ ( ) : return
return str ( pw . text ( ) )
2012-02-14 00:52:03 -08:00
@staticmethod
def change_password_dialog ( wallet , parent = None ) :
d = QDialog ( parent )
2012-02-13 06:11:31 -08:00
d . setModal ( 1 )
2012-02-13 05:52:59 -08:00
pw = QLineEdit ( )
pw . setEchoMode ( 2 )
new_pw = QLineEdit ( )
new_pw . setEchoMode ( 2 )
conf_pw = QLineEdit ( )
conf_pw . setEchoMode ( 2 )
2012-02-13 10:12:24 -08:00
vbox = QVBoxLayout ( )
2012-02-14 01:05:38 -08:00
if parent :
msg = ' Your wallet is encrypted. Use this dialog to change your password. \n To disable wallet encryption, enter an empty new password. ' if wallet . use_encryption else ' Your wallet keys are not encrypted '
else :
msg = " Please choose a password to encrypt your wallet keys. \n Leave these fields empty if you want to disable encryption. "
2012-02-13 10:12:24 -08:00
vbox . addWidget ( QLabel ( msg ) )
2012-02-13 05:52:59 -08:00
grid = QGridLayout ( )
grid . setSpacing ( 8 )
2012-02-14 00:52:03 -08:00
if wallet . use_encryption :
2012-02-13 05:56:31 -08:00
grid . addWidget ( QLabel ( ' Password ' ) , 1 , 0 )
grid . addWidget ( pw , 1 , 1 )
2012-02-13 05:52:59 -08:00
grid . addWidget ( QLabel ( ' New Password ' ) , 2 , 0 )
grid . addWidget ( new_pw , 2 , 1 )
grid . addWidget ( QLabel ( ' Confirm Password ' ) , 3 , 0 )
grid . addWidget ( conf_pw , 3 , 1 )
2012-02-13 10:12:24 -08:00
vbox . addLayout ( grid )
2012-02-13 05:52:59 -08:00
2012-02-14 00:52:03 -08:00
vbox . addLayout ( ok_cancel_buttons ( d ) )
2012-02-13 10:02:48 -08:00
d . setLayout ( vbox )
2012-02-13 05:52:59 -08:00
if not d . exec_ ( ) : return
2012-02-14 00:52:03 -08:00
password = str ( pw . text ( ) ) if wallet . use_encryption else None
2012-02-13 05:52:59 -08:00
new_password = str ( new_pw . text ( ) )
new_password2 = str ( conf_pw . text ( ) )
try :
2012-02-14 00:52:03 -08:00
seed = wallet . pw_decode ( wallet . seed , password )
2012-02-13 05:52:59 -08:00
except :
2012-02-14 00:52:03 -08:00
QMessageBox . warning ( parent , ' Error ' , ' Incorrect Password ' , ' OK ' )
2012-02-13 05:52:59 -08:00
return
if new_password != new_password2 :
2012-02-14 00:52:03 -08:00
QMessageBox . warning ( parent , ' Error ' , ' Passwords do not match ' , ' OK ' )
2012-02-13 05:52:59 -08:00
return
2012-02-14 00:52:03 -08:00
wallet . update_password ( seed , new_password )
@staticmethod
def seed_dialog ( wallet , parent = None ) :
d = QDialog ( parent )
d . setModal ( 1 )
vbox = QVBoxLayout ( )
msg = " Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet. "
vbox . addWidget ( QLabel ( msg ) )
grid = QGridLayout ( )
grid . setSpacing ( 8 )
seed_e = QLineEdit ( )
grid . addWidget ( QLabel ( ' Seed or mnemonic ' ) , 1 , 0 )
grid . addWidget ( seed_e , 1 , 1 )
gap_e = QLineEdit ( )
gap_e . setText ( " 5 " )
grid . addWidget ( QLabel ( ' Gap limit ' ) , 2 , 0 )
grid . addWidget ( gap_e , 2 , 1 )
vbox . addLayout ( grid )
vbox . addLayout ( ok_cancel_buttons ( d ) )
d . setLayout ( vbox )
if not d . exec_ ( ) : return
try :
gap = int ( str ( gap_e . text ( ) ) )
except :
show_message ( " error " )
sys . exit ( 1 )
try :
seed = str ( seed_e . text ( ) )
seed . decode ( ' hex ' )
except :
import mnemonic
print " not hex, trying decode "
seed = mnemonic . mn_decode ( seed . split ( ' ' ) )
if not seed :
show_message ( " no seed " )
sys . exit ( 1 )
wallet . seed = seed
wallet . gap_limit = gap
return True
2012-02-13 05:52:59 -08:00
2012-02-13 06:22:15 -08:00
def settings_dialog ( self ) :
d = QDialog ( self )
d . setModal ( 1 )
2012-02-13 10:02:48 -08:00
vbox = QVBoxLayout ( )
2012-02-13 06:22:15 -08:00
2012-02-14 00:52:03 -08:00
msg = ' Here are the settings of your wallet '
2012-02-13 10:02:48 -08:00
vbox . addWidget ( QLabel ( msg ) )
grid = QGridLayout ( )
grid . setSpacing ( 8 )
2012-02-13 06:22:15 -08:00
fee_line = QLineEdit ( )
fee_line . setText ( " %s " % str ( Decimal ( self . wallet . fee ) / 100000000 ) )
grid . addWidget ( QLabel ( ' Fee ' ) , 2 , 0 )
grid . addWidget ( fee_line , 2 , 1 )
2012-02-13 10:02:48 -08:00
vbox . addLayout ( grid )
2012-02-13 06:22:15 -08:00
2012-02-14 00:52:03 -08:00
vbox . addLayout ( ok_cancel_buttons ( d ) )
2012-02-13 10:02:48 -08:00
d . setLayout ( vbox )
2012-02-13 06:22:15 -08:00
if not d . exec_ ( ) : return
fee = str ( fee_line . text ( ) )
try :
fee = int ( 100000000 * Decimal ( fee ) )
except :
QMessageBox . warning ( self , ' Error ' , ' Invalid value: %s ' % fee , ' OK ' )
return
self . wallet . fee = fee
self . wallet . save ( )
2012-02-14 00:52:03 -08:00
@staticmethod
def network_dialog ( wallet , parent = None ) :
2012-02-14 01:05:38 -08:00
if parent :
2012-02-13 06:44:16 -08:00
if wallet . interface . is_connected :
status = " Connected to %s . \n %d blocks \n response time: %f " % ( wallet . interface . host , wallet . interface . blocks , wallet . interface . rtime )
else :
status = " Not connected "
2012-02-13 12:36:41 -08:00
host = wallet . interface . host
port = wallet . interface . port
2012-02-13 06:44:16 -08:00
else :
import random
status = " Please choose a server. "
host = random . choice ( wallet . interface . servers )
port = 50000
2012-02-14 00:52:03 -08:00
d = QDialog ( parent )
2012-02-13 06:44:16 -08:00
d . setModal ( 1 )
2012-02-13 10:02:48 -08:00
vbox = QVBoxLayout ( )
vbox . addWidget ( QLabel ( status ) )
2012-02-13 06:44:16 -08:00
grid = QGridLayout ( )
grid . setSpacing ( 8 )
host_line = QLineEdit ( )
host_line . setText ( " %s : %d " % ( host , port ) )
grid . addWidget ( QLabel ( ' Server ' ) , 2 , 0 )
grid . addWidget ( host_line , 2 , 1 )
2012-02-13 10:02:48 -08:00
vbox . addLayout ( grid )
2012-02-13 06:44:16 -08:00
2012-02-14 00:52:03 -08:00
vbox . addLayout ( ok_cancel_buttons ( d ) )
2012-02-13 10:02:48 -08:00
d . setLayout ( vbox )
2012-02-13 06:44:16 -08:00
if not d . exec_ ( ) : return
hh = str ( host_line . text ( ) )
try :
if ' : ' in hh :
host , port = hh . split ( ' : ' )
port = int ( port )
else :
host = hh
port = 50000
except :
show_message ( " error " )
if parent == None :
sys . exit ( 1 )
else :
return
wallet . interface . set_server ( host , port )
2012-02-14 00:52:03 -08:00
return True
2012-02-13 06:44:16 -08:00
2012-02-14 00:52:03 -08:00
class ElectrumGui ( ) :
2012-02-11 04:14:12 -08:00
def __init__ ( self , wallet ) :
self . wallet = wallet
2012-02-14 00:52:03 -08:00
self . app = QApplication ( sys . argv )
def restore_or_create ( self ) :
msg = " Wallet file not found. \n Do you want to create a new wallet, \n or to restore an existing one? "
r = QMessageBox . question ( None , ' Message ' , msg , ' create ' , ' restore ' , ' cancel ' , 0 , 2 )
if r == 2 : return False
is_recovery = ( r == 1 )
wallet = self . wallet
2012-02-14 01:05:38 -08:00
# ask for the server.
if not ElectrumWindow . network_dialog ( wallet , parent = None ) : return False
2012-02-14 00:52:03 -08:00
if not is_recovery :
wallet . new_seed ( None )
# generate first key
wallet . synchronize ( )
# run a dialog indicating the seed, ask the user to remember it
ElectrumWindow . show_seed_dialog ( wallet )
#ask for password
ElectrumWindow . change_password_dialog ( wallet )
else :
# ask for seed and gap.
2012-02-14 01:05:38 -08:00
if not ElectrumWindow . seed_dialog ( wallet ) : return False
2012-02-14 00:52:03 -08:00
wallet . init_mpk ( wallet . seed ) # not encrypted at this point
wallet . synchronize ( )
if wallet . is_found ( ) :
# history and addressbook
wallet . update_tx_history ( )
wallet . fill_addressbook ( )
print " recovery successful "
wallet . save ( )
else :
QMessageBox . information ( None , ' Message ' , " No transactions found for this seed " , ' OK ' )
wallet . save ( )
return True
2012-02-11 04:14:12 -08:00
def main ( self ) :
2012-02-11 13:19:22 -08:00
s = Sender ( )
s . start ( )
2012-02-12 00:52:26 -08:00
w = ElectrumWindow ( self . wallet )
2012-02-14 00:52:03 -08:00
w . app = self . app
2012-02-11 13:19:22 -08:00
w . connect_slots ( s )
2012-02-14 00:52:03 -08:00
self . app . exec_ ( )