You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

554 lines
18 KiB

13 years ago
import sys, time, datetime
13 years ago
# todo: see PySide
from PyQt4.QtGui import *
13 years ago
from PyQt4.QtCore import *
import PyQt4.QtCore as QtCore
13 years ago
import PyQt4.QtGui as QtGui
13 years ago
from wallet import format_satoshis
13 years ago
from decimal import Decimal
13 years ago
def restore_create_dialog(wallet):
pass
13 years ago
class Sender(QtCore.QThread):
def run(self):
while True:
self.emit(QtCore.SIGNAL('testsignal'))
time.sleep(0.5)
13 years ago
class ElectrumWindow(QMainWindow):
def __init__(self, wallet):
13 years ago
QMainWindow.__init__(self)
self.wallet = wallet
13 years ago
13 years ago
tabs = QTabWidget(self)
13 years ago
tabs.addTab(self.create_history_tab(), 'History')
13 years ago
tabs.addTab(self.create_send_tab(), 'Send')
13 years ago
tabs.addTab(self.create_receive_tab(), 'Receive')
tabs.addTab(self.create_contacts_tab(),'Contacts')
tabs.addTab(self.create_wall_tab(), 'Wall')
13 years ago
tabs.setMinimumSize(600, 400)
13 years ago
tabs.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
13 years ago
self.setCentralWidget(tabs)
13 years ago
self.create_status_bar()
13 years ago
self.setGeometry(100,100,840,400)
13 years ago
self.setWindowTitle( 'Electrum ' + self.wallet.electrum_version + ' - Qt')
13 years ago
self.show()
13 years ago
QShortcut(QKeySequence("Ctrl+W"), self, self.close)
QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
13 years ago
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() ))
13 years ago
13 years ago
def connect_slots(self, sender):
self.connect(sender, QtCore.SIGNAL('testsignal'), self.update_wallet)
13 years ago
13 years ago
def update_wallet(self):
if self.wallet.interface.is_connected:
if self.wallet.interface.blocks == 0:
text = "Server not ready"
13 years ago
icon = QIcon("icons/status_disconnected.svg")
13 years ago
elif not self.wallet.interface.was_polled:
text = "Synchronizing..."
13 years ago
icon = QIcon("icons/status_waiting.svg")
13 years ago
else:
c, u = self.wallet.get_balance()
text = "Balance: %s "%( format_satoshis(c) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
13 years ago
icon = QIcon("icons/status_connected.png")
13 years ago
else:
text = "Not connected"
13 years ago
icon = QIcon("icons/status_disconnected.svg")
13 years ago
self.statusBar().showMessage(text)
13 years ago
self.status_button.setIcon( icon )
13 years ago
if self.wallet.interface.was_updated:
self.wallet.interface.was_updated = False
13 years ago
self.textbox.setText( self.wallet.interface.message )
13 years ago
self.update_history_tab()
13 years ago
self.update_receive_tab()
self.update_contacts_tab()
13 years ago
13 years ago
def create_history_tab(self):
13 years ago
self.history_list = w = QTreeWidget(self)
13 years ago
#print w.getContentsMargins()
13 years ago
w.setColumnCount(5)
13 years ago
w.setColumnWidth(0, 40)
w.setColumnWidth(1, 140)
13 years ago
w.setColumnWidth(2, 350)
w.setColumnWidth(3, 140)
w.setColumnWidth(4, 140)
13 years ago
w.setHeaderLabels( [ '', 'Date', 'Description', 'Amount', 'Balance'] )
13 years ago
self.connect(w, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), self.tx_details)
13 years ago
self.connect(w, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
self.connect(w, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
13 years ago
return w
13 years ago
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\nSigned URI: ' + r[2] \
+ "\n\nSigned by: " + r[0] \
+ '\n\nSignature: ' + r[1]
QMessageBox.information(self, 'Details', tx_details, 'OK')
13 years ago
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) )
13 years ago
if text:
13 years ago
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
13 years ago
13 years ago
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()
13 years ago
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]
13 years ago
icon = QIcon("icons/confirmed.png")
13 years ago
else:
conf = 0
time_str = 'pending'
13 years ago
icon = QIcon("icons/unconfirmed.svg")
13 years ago
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']
13 years ago
13 years ago
item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True), format_satoshis(balance)] )
13 years ago
item.setFont(2, QFont('monospace'))
13 years ago
item.setFont(3, QFont('monospace'))
item.setFont(4, QFont('monospace'))
13 years ago
item.setToolTip(0, tx_hash)
13 years ago
if is_default_label:
13 years ago
item.setForeground(2, QBrush(QColor('grey')))
13 years ago
item.setIcon(0, icon)
self.history_list.insertTopLevelItem(0,item)
13 years ago
13 years ago
def create_send_tab(self):
13 years ago
w = QWidget()
paytoEdit = QtGui.QLineEdit()
descriptionEdit = QtGui.QLineEdit()
amountEdit = QtGui.QLineEdit()
feeEdit = QtGui.QLineEdit()
grid = QtGui.QGridLayout()
grid.setSpacing(8)
13 years ago
grid.setColumnMinimumWidth(3,300)
grid.setColumnStretch(4,1)
13 years ago
grid.addWidget(QLabel('Pay to'), 1, 0)
13 years ago
grid.addWidget(paytoEdit, 1, 1, 1, 3)
13 years ago
grid.addWidget(QLabel('Description'), 2, 0)
13 years ago
grid.addWidget(descriptionEdit, 2, 1, 1, 3)
13 years ago
grid.addWidget(QLabel('Amount'), 3, 0)
13 years ago
grid.addWidget(amountEdit, 3, 1, 1, 2)
13 years ago
grid.addWidget(QLabel('Fee'), 4, 0)
13 years ago
grid.addWidget(feeEdit, 4, 1, 1, 2)
13 years ago
13 years ago
sendButton = QPushButton("Send")
grid.addWidget(sendButton, 5, 1)
13 years ago
clearButton = QPushButton("Clear")
grid.addWidget(clearButton, 5, 2)
13 years ago
13 years ago
w.setLayout(grid)
w.show()
w2 = QWidget()
vbox = QtGui.QVBoxLayout()
vbox.addWidget(w)
vbox.addStretch(1)
w2.setLayout(vbox)
return w2
13 years ago
13 years ago
def make_address_list(self, is_recv):
13 years ago
13 years ago
l = QTreeWidget(self)
l.setColumnCount(3)
13 years ago
l.setColumnWidth(0, 350)
l.setColumnWidth(1, 330)
13 years ago
l.setColumnWidth(2, 20)
l.setHeaderLabels( ['Address', 'Label','Tx'])
13 years ago
13 years ago
vbox = QtGui.QVBoxLayout()
13 years ago
vbox.setMargin(0)
13 years ago
vbox.setSpacing(0)
13 years ago
vbox.addWidget(l)
hbox = QtGui.QHBoxLayout()
13 years ago
hbox.setMargin(0)
13 years ago
hbox.setSpacing(0)
13 years ago
qrButton = QtGui.QPushButton("QR")
copyButton = QtGui.QPushButton("Copy to Clipboard")
hbox.addWidget(qrButton)
hbox.addWidget(copyButton)
if not is_recv:
addButton = QtGui.QPushButton("New")
13 years ago
addButton.clicked.connect(self.newaddress_dialog)
13 years ago
paytoButton = QtGui.QPushButton("Pay to")
hbox.addWidget(addButton)
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):
13 years ago
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
13 years ago
return w
13 years ago
13 years ago
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] )
13 years ago
item.setFont(0, QFont('monospace'))
13 years ago
self.receive_list.addTopLevelItem(item)
13 years ago
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 + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
QMessageBox.information(self, 'Alias', msg, 'OK')
13 years ago
def update_contacts_tab(self):
self.contacts_list.clear()
for alias, v in self.wallet.aliases.items():
s, target = v
13 years ago
label = self.wallet.labels.get(alias,'')
13 years ago
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] )
13 years ago
item.setFont(0, QFont('monospace'))
13 years ago
self.contacts_list.addTopLevelItem(item)
13 years ago
def create_wall_tab(self):
13 years ago
self.textbox = textbox = QTextEdit(self)
textbox.setReadOnly(True)
return textbox
13 years ago
13 years ago
def create_status_bar(self):
sb = QStatusBar()
13 years ago
sb.setFixedHeight(35)
hbox = QtGui.QHBoxLayout()
hbox.setMargin(0)
buttons = QWidget()
buttons.setLayout(hbox)
13 years ago
13 years ago
icon = QIcon("icons/lock.svg")
13 years ago
b = QPushButton( icon, '' )
13 years ago
b.setToolTip("Password")
13 years ago
b.setFlat(True)
13 years ago
b.setMaximumWidth(25)
13 years ago
b.clicked.connect(self.change_password_dialog)
13 years ago
hbox.addWidget(b)
13 years ago
13 years ago
icon = QIcon("icons/preferences.svg")
13 years ago
b = QPushButton( icon, '' )
13 years ago
b.setToolTip("Preferences")
13 years ago
b.setFlat(True)
13 years ago
b.setMaximumWidth(25)
13 years ago
b.clicked.connect(self.settings_dialog)
13 years ago
hbox.addWidget(b)
13 years ago
13 years ago
icon = QIcon("icons/seed.png")
13 years ago
b = QPushButton( icon, '' )
13 years ago
b.setToolTip("Seed")
13 years ago
b.setFlat(True)
13 years ago
b.setMaximumWidth(20)
13 years ago
b.clicked.connect(self.show_seed_dialog)
13 years ago
hbox.addWidget(b)
13 years ago
13 years ago
icon = QIcon("icons/status_disconnected.svg")
13 years ago
self.status_button = b = QPushButton( icon, '' )
13 years ago
b.setToolTip("Network")
13 years ago
b.setFlat(True)
13 years ago
b.setMaximumWidth(25)
13 years ago
hbox.addWidget(b)
13 years ago
13 years ago
sb.addPermanentWidget(buttons)
13 years ago
self.setStatusBar(sb)
13 years ago
def newaddress_dialog(self):
text, ok = QtGui.QInputDialog.getText(self, 'New Contact', 'Address:')
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')
def show_seed_dialog(self):
import mnemonic
13 years ago
if self.wallet.use_encryption:
password = self.password_dialog()
if not password: return
else:
password = None
try:
seed = self.wallet.pw_decode( self.wallet.seed, password)
except:
QMessageBox.warning(self, 'Error', 'Invalid Password', 'OK')
return
13 years ago
13 years ago
msg = "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" \
+ "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:\n\n\"" \
+ ' '.join(mnemonic.mn_encode(seed)) + "\""
QMessageBox.information(self, 'Seed', msg, 'OK')
def password_dialog(self):
13 years ago
d = QDialog(self)
d.setModal(1)
13 years ago
pw = QLineEdit()
pw.setEchoMode(2)
grid = QGridLayout()
grid.setSpacing(8)
msg = 'Please enter your password'
grid.addWidget(QLabel(msg), 0, 0, 1, 2)
grid.addWidget(QLabel('Password'), 1, 0)
grid.addWidget(pw, 1, 1)
b = QPushButton("Cancel")
grid.addWidget(b, 5, 1)
b.clicked.connect(d.reject)
b = QPushButton("OK")
grid.addWidget(b, 5, 2)
b.clicked.connect(d.accept)
d.setLayout(grid)
if not d.exec_(): return
return str(pw.text())
def change_password_dialog(self):
d = QDialog(self)
d.setModal(1)
13 years ago
pw = QLineEdit()
pw.setEchoMode(2)
new_pw = QLineEdit()
new_pw.setEchoMode(2)
conf_pw = QLineEdit()
conf_pw.setEchoMode(2)
grid = QGridLayout()
grid.setSpacing(8)
13 years ago
msg = 'Your wallet is encrypted. Use this dialog to change your password.\nTo disable wallet encryption, enter an empty new password.' if self.wallet.use_encryption else 'Your wallet keys are not encrypted'
13 years ago
grid.addWidget(QLabel(msg), 0, 0, 1, 2)
13 years ago
if self.wallet.use_encryption:
grid.addWidget(QLabel('Password'), 1, 0)
grid.addWidget(pw, 1, 1)
13 years ago
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)
b = QPushButton("Cancel")
grid.addWidget(b, 5, 1)
b.clicked.connect(d.reject)
b = QPushButton("OK")
grid.addWidget(b, 5, 2)
b.clicked.connect(d.accept)
d.setLayout(grid)
if not d.exec_(): return
13 years ago
password = str(pw.text()) if self.wallet.use_encryption else None
13 years ago
new_password = str(new_pw.text())
new_password2 = str(conf_pw.text())
try:
seed = self.wallet.pw_decode( self.wallet.seed, password)
except:
QMessageBox.warning(self, 'Error', 'Incorrect Password', 'OK')
return
if new_password != new_password2:
QMessageBox.warning(self, 'Error', 'Passwords do not match', 'OK')
return
self.wallet.update_password(seed, new_password)
13 years ago
def settings_dialog(self):
d = QDialog(self)
d.setModal(1)
grid = QGridLayout()
grid.setSpacing(8)
msg = 'These are the settings of your wallet'
grid.addWidget(QLabel(msg), 0, 0, 1, 2)
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)
b = QPushButton("Cancel")
grid.addWidget(b, 5, 1)
b.clicked.connect(d.reject)
b = QPushButton("OK")
grid.addWidget(b, 5, 2)
b.clicked.connect(d.accept)
d.setLayout(grid)
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()
class BitcoinGUI():
def __init__(self, wallet):
self.wallet = wallet
def main(self):
13 years ago
s = Sender()
s.start()
app = QApplication(sys.argv)
13 years ago
w = ElectrumWindow(self.wallet)
13 years ago
w.connect_slots(s)
app.exec_()