import sys, time, datetime, re
from i18n import _
from util import print_error
+import os.path, json, util
try:
import PyQt4
from wallet import format_satoshis
import bmp, mnemonic, pyqrnative, qrscanner
+import exchange_rate
from decimal import Decimal
class QRCodeWidget(QWidget):
- def __init__(self, addr):
- super(QRCodeWidget, self).__init__()
- self.setGeometry(300, 300, 350, 350)
- self.set_addr(addr)
+ def __init__(self, data = None):
+ QWidget.__init__(self)
+ self.setMinimumSize(210, 210)
+ self.addr = None
+ self.qr = None
+ if data:
+ self.set_addr(data)
+ self.update_qr()
def set_addr(self, addr):
- self.addr = addr
- self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L)
- self.qr.addData(addr)
- self.qr.make()
-
+ if self.addr != addr:
+ self.addr = addr
+ self.qr = None
+ self.update()
+
+ def update_qr(self):
+ if self.addr and not self.qr:
+ self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L)
+ self.qr.addData(self.addr)
+ self.qr.make()
+ self.update()
+
def paintEvent(self, e):
- qp = QtGui.QPainter()
- qp.begin(self)
- boxsize = 6
- size = self.qr.getModuleCount()*boxsize
- k = self.qr.getModuleCount()
+
+ if not self.addr:
+ return
+
black = QColor(0, 0, 0, 255)
white = QColor(255, 255, 255, 255)
+
+ if not self.qr:
+ qp = QtGui.QPainter()
+ qp.begin(self)
+ qp.setBrush(white)
+ qp.setPen(white)
+ qp.drawRect(0, 0, 198, 198)
+ qp.end()
+ return
+
+ k = self.qr.getModuleCount()
+ qp = QtGui.QPainter()
+ qp.begin(self)
+ r = qp.viewport()
+ boxsize = min(r.width(), r.height())*0.8/k
+ size = k*boxsize
+ left = (r.width() - size)/2
+ top = (r.height() - size)/2
+
for r in range(k):
for c in range(k):
if self.qr.isDark(r, c):
else:
qp.setBrush(white)
qp.setPen(white)
- qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize)
+ qp.drawRect(left+c*boxsize, top+r*boxsize, boxsize, boxsize)
qp.end()
+
+class QR_Window(QWidget):
+
+ def __init__(self):
+ QWidget.__init__(self)
+ self.setWindowTitle('Electrum - Invoice')
+ self.setMinimumSize(800, 250)
+ self.address = ''
+ self.labe = ''
+ self.amount = 0
+ self.setFocusPolicy(QtCore.Qt.NoFocus)
+
+ main_box = QHBoxLayout()
+
+ self.qrw = QRCodeWidget()
+ main_box.addWidget(self.qrw, 1)
+
+ vbox = QVBoxLayout()
+ main_box.addLayout(vbox)
+
+ self.address_label = QLabel("")
+ self.address_label.setFont(QFont(MONOSPACE_FONT))
+ vbox.addWidget(self.address_label)
+
+ self.label_label = QLabel("")
+ vbox.addWidget(self.label_label)
+
+ self.amount_label = QLabel("")
+ vbox.addWidget(self.amount_label)
+
+ vbox.addStretch(1)
+ self.setLayout(main_box)
+
+
+ def set_content(self, addr, label, amount):
+ self.address = addr
+ address_text = "<span style='font-size: 18pt'>%s</span>" % addr if addr else ""
+ self.address_label.setText(address_text)
+
+ self.amount = amount
+ amount_text = "<span style='font-size: 21pt'>%s</span> <span style='font-size: 16pt'>BTC</span> " % format_satoshis(amount) if amount else ""
+ self.amount_label.setText(amount_text)
+
+ self.label = label
+ label_text = "<span style='font-size: 21pt'>%s</span>" % label if label else ""
+ self.label_label.setText(label_text)
+
+ msg = 'bitcoin:'+self.address
+ if self.amount is not None:
+ msg += '?amount=%s'%(str( Decimal(self.amount) /100000000))
+ if self.label is not None:
+ msg += '&label=%s'%(self.label)
+ elif self.label is not None:
+ msg += '?label=%s'%(self.label)
+
+ self.qrw.set_addr( msg )
+
+
+
+
def waiting_dialog(f):
s = Timer()
def __init__(self, wallet, config):
QMainWindow.__init__(self)
+ self.lite = None
self.wallet = wallet
self.config = config
self.wallet.interface.register_callback('updated', self.update_callback)
self.wallet.interface.register_callback('disconnected', self.update_callback)
self.wallet.interface.register_callback('disconnecting', self.update_callback)
- self.detailed_view = config.get('qt_detailed_view', False)
+ self.receive_tab_mode = config.get('qt_receive_tab_mode', 0)
+ self.merchant_name = config.get('merchant_name', 'Invoice')
+ self.qr_window = None
self.funds_error = False
self.completions = QStringListModel()
self.tabs = tabs = QTabWidget(self)
tabs.addTab(self.create_history_tab(), _('History') )
- if self.wallet.seed:
- tabs.addTab(self.create_send_tab(), _('Send') )
+ tabs.addTab(self.create_send_tab(), _('Send') )
tabs.addTab(self.create_receive_tab(), _('Receive') )
tabs.addTab(self.create_contacts_tab(), _('Contacts') )
tabs.addTab(self.create_wall_tab(), _('Wall') )
tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setCentralWidget(tabs)
self.create_status_bar()
+ self.toggle_QR_window(self.receive_tab_mode == 2)
g = self.config.get("winpos-qt",[100, 100, 840, 400])
self.setGeometry(g[0], g[1], g[2], g[3])
QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
+ #self.connect(self, SIGNAL('editamount'), self.edit_amount)
self.history_list.setFocus(True)
+
+ self.exchanger = exchange_rate.Exchanger(self)
+ self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet)
# dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
if platform.system() == 'Windows':
tabs.setCurrentIndex (n)
tabs.setCurrentIndex (0)
+ def close(self):
+ QMainWindow.close(self)
+ if self.qr_window:
+ self.qr_window.close()
+ self.qr_window = None
def connect_slots(self, sender):
- if self.wallet.seed:
- self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
- self.previous_payto_e=''
+ self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions)
+ self.previous_payto_e=''
- def check_recipient(self):
+ def timer_actions(self):
+ if self.qr_window:
+ self.qr_window.qrw.update_qr()
+
if self.payto_e.hasFocus():
return
r = unicode( self.payto_e.text() )
c, u = self.wallet.get_balance()
text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
+ text += self.create_quote_text(Decimal(c+u)/100000000)
icon = QIcon(":icons/status_connected.png")
else:
text = _( "Not connected" )
icon = QIcon(":icons/status_disconnected.png")
- if self.funds_error:
- text = _( "Not enough funds" )
-
+ self.status_text = text
self.statusBar().showMessage(text)
self.status_button.setIcon( icon )
self.update_contacts_tab()
self.update_completions()
-
+ def create_quote_text(self, btc_balance):
+ quote_currency = self.config.get("currency", "None")
+ quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
+ if quote_balance is None:
+ quote_text = ""
+ else:
+ quote_text = " (%.2f %s)" % (quote_balance, quote_currency)
+ return quote_text
+
def create_history_tab(self):
self.history_list = l = MyTreeWidget(self)
l.setColumnCount(5)
l.setHeaderLabels( [ '', _( 'Date' ), _( 'Description' ) , _('Amount'), _('Balance')] )
self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
+
l.setContextMenuPolicy(Qt.CustomContextMenu)
l.customContextMenuRequested.connect(self.create_history_menu)
return l
+
def create_history_menu(self, position):
self.history_list.selectedIndexes()
item = self.history_list.currentItem()
if not item: return
- tx_hash = str(item.toolTip(0))
+ tx_hash = str(item.data(0, Qt.UserRole).toString())
if not tx_hash: return
menu = QMenu()
menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
item.setForeground(2, QBrush(QColor('gray')))
self.is_edit=False
+
def edit_label(self, is_recv):
l = self.receive_list if is_recv else self.contacts_list
c = 2 if is_recv else 1
l.editItem( item, c )
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
+ def edit_amount(self):
+ l = self.receive_list
+ item = l.currentItem()
+ item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
+ l.editItem( item, 3 )
+ item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
+
+
def address_label_clicked(self, item, column, l, column_addr, column_label):
- if column==column_label and item.isSelected():
+ if column == column_label and item.isSelected():
addr = unicode( item.text(column_addr) )
label = unicode( item.text(column_label) )
if label in self.wallet.aliases.keys():
l.editItem( item, column )
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
+
def address_label_changed(self, item, column, l, column_addr, column_label):
- addr = unicode( item.text(column_addr) )
- text = unicode( item.text(column_label) )
- changed = False
-
- if text:
- if text not in self.wallet.aliases.keys():
- old_addr = self.wallet.labels.get(text)
- if old_addr != addr:
- self.wallet.labels[addr] = text
- changed = True
+
+ if column == column_label:
+ addr = unicode( item.text(column_addr) )
+ text = unicode( item.text(column_label) )
+ changed = False
+
+ if text:
+ if text not in self.wallet.aliases.keys():
+ old_addr = self.wallet.labels.get(text)
+ if old_addr != addr:
+ self.wallet.labels[addr] = text
+ changed = True
+ else:
+ print_error("Error: This is one of your aliases")
+ label = self.wallet.labels.get(addr,'')
+ item.setText(column_label, QString(label))
else:
- print_error("Error: This is one of your aliases")
- label = self.wallet.labels.get(addr,'')
- item.setText(column_label, QString(label))
- else:
- s = self.wallet.labels.get(addr)
- if s:
- self.wallet.labels.pop(addr)
- changed = True
+ s = self.wallet.labels.get(addr)
+ if s:
+ self.wallet.labels.pop(addr)
+ changed = True
- if changed:
- self.update_history_tab()
- self.update_completions()
+ if changed:
+ self.update_history_tab()
+ self.update_completions()
+
+ self.recv_changed(item)
+
+ if column == 3:
+ address = unicode( item.text(column_addr) )
+ text = unicode( item.text(3) )
+ try:
+ index = self.wallet.addresses.index(address)
+ except:
+ return
+
+ try:
+ amount = int( Decimal(text) * 100000000 )
+ item.setText(3,format_satoshis(amount,False, self.wallet.num_zeros))
+ except:
+ amount = self.wallet.requested_amounts.get(address)
+ if amount:
+ item.setText(3,format_satoshis(amount,False, self.wallet.num_zeros))
+ else:
+ item.setText(3,"")
+ return
+
+ self.wallet.requested_amounts[address] = amount
+
+ label = self.wallet.labels.get(address)
+ if label is None:
+ label = self.merchant_name + ' - %04d'%(index+1)
+ self.wallet.labels[address] = label
+
+ self.update_receive_item(self.receive_list.currentItem())
+ if self.qr_window:
+ self.qr_window.set_content( address, label, amount )
+
+
+ def recv_changed(self, a):
+ "current item changed"
+ if a is not None and self.qr_window and self.qr_window.isVisible():
+ address = str(a.text(1))
+ label = self.wallet.labels.get(address)
+ amount = self.wallet.requested_amounts.get(address)
+ self.qr_window.set_content( address, label, amount )
def update_history_tab(self):
item.setFont(2, QFont(MONOSPACE_FONT))
item.setFont(3, QFont(MONOSPACE_FONT))
item.setFont(4, QFont(MONOSPACE_FONT))
+ if value < 0:
+ item.setForeground(3, QBrush(QColor("#BC1E1E")))
if tx_hash:
- item.setToolTip(0, tx_hash)
+ item.setData(0, Qt.UserRole, tx_hash)
+ item.setToolTip(0, "%d %s\nTxId:%s" % (conf, _('Confirmations'), tx_hash) )
if is_default_label:
item.setForeground(2, QBrush(QColor('grey')))
if inputs:
palette = QPalette()
palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
+ text = self.status_text
else:
palette = QPalette()
palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
self.funds_error = True
+ text = _( "Not enough funds" )
+
+ self.statusBar().showMessage(text)
self.amount_e.setPalette(palette)
self.fee_e.setPalette(palette)
password = None
try:
- tx = self.wallet.mktx( to_address, amount, label, password, fee)
+ tx = self.wallet.mktx( [(to_address, amount)], label, password, fee)
except BaseException, e:
self.show_message(str(e))
return
-
- h = self.wallet.send_tx(tx)
- waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
- status, msg = self.wallet.receive_tx( h )
- if status:
- QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
- self.do_clear()
- self.update_contacts_tab()
+ if self.wallet.seed:
+ h = self.wallet.send_tx(tx)
+ waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
+ status, msg = self.wallet.receive_tx( h )
+ if status:
+ QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
+ self.do_clear()
+ self.update_contacts_tab()
+ else:
+ QMessageBox.warning(self, _('Error'), msg, _('OK'))
else:
- QMessageBox.warning(self, _('Error'), msg, _('OK'))
+ filename = 'unsigned_tx'
+ f = open(filename,'w')
+ f.write(tx)
+ f.close()
+ QMessageBox.information(self, _('Unsigned transaction'), _("Unsigned transaction was saved to file:") + " " +filename, _('OK'))
def set_url(self, url):
def create_receive_tab(self):
- l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
+ l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Requested'), _('Balance'), _('Tx')])
l.setContextMenuPolicy(Qt.CustomContextMenu)
l.customContextMenuRequested.connect(self.create_receive_menu)
self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
+ self.connect(l, SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'), lambda a,b: self.recv_changed(a))
self.receive_list = l
self.receive_buttons_hbox = hbox
- self.details_button = EnterButton(self.details_button_text(), self.toggle_detailed_view)
- hbox.addWidget(self.details_button)
hbox.addStretch(1)
return w
- def details_button_text(self):
- return _('Hide details') if self.detailed_view else _('Show details')
- def toggle_detailed_view(self):
- self.detailed_view = not self.detailed_view
- self.config.set_key('qt_detailed_view', self.detailed_view, True)
- self.details_button.setText(self.details_button_text())
+ def receive_tab_set_mode(self, i):
+ self.receive_tab_mode = i
+ self.config.set_key('qt_receive_tab_mode', self.receive_tab_mode, True)
self.wallet.save()
self.update_receive_tab()
- self.update_contacts_tab()
+ self.toggle_QR_window(self.receive_tab_mode == 2)
def create_contacts_tab(self):
return w
+ def delete_imported_key(self, addr):
+ if self.question("Do you want to remove %s from your wallet?"%addr):
+ self.wallet.imported_keys.pop(addr)
+ self.update_receive_tab()
+ self.update_history_tab()
+ self.wallet.save()
+
+
def create_receive_menu(self, position):
# fixme: this function apparently has a side effect.
# if it is not called the menu pops up several times
if not item: return
addr = unicode(item.text(1))
menu = QMenu()
- menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
- menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
+ menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr))
+ if self.receive_tab_mode == 2:
+ menu.addAction(_("Request amount"), lambda: self.edit_amount())
+ menu.addAction(_("View QR"), lambda: ElectrumWindow.show_qrcode("Address","bitcoin:"+addr) )
menu.addAction(_("Edit label"), lambda: self.edit_label(True))
-
- t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
- menu.addAction(t, lambda: self.toggle_freeze(addr))
- t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
- menu.addAction(t, lambda: self.toggle_priority(addr))
+ menu.addAction(_("Sign message"), lambda: self.sign_message(addr))
+ if addr in self.wallet.imported_keys:
+ menu.addAction(_("Remove from wallet"), lambda: self.delete_imported_key(addr))
+
+ if self.receive_tab_mode == 1:
+ t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
+ menu.addAction(t, lambda: self.toggle_freeze(addr))
+ t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
+ menu.addAction(t, lambda: self.toggle_priority(addr))
+
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
menu = QMenu()
menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
- menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
+ menu.addAction(_("View QR code"),lambda: self.show_qrcode("Address","bitcoin:"+addr))
if not is_alias:
menu.addAction(_("Edit label"), lambda: self.edit_label(False))
else:
menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
+ def update_receive_item(self, item):
+ address = str( item.data(1,0).toString() )
+
+ flags = self.wallet.get_address_flags(address)
+ item.setData(0,0,flags)
+
+ label = self.wallet.labels.get(address,'')
+ item.setData(2,0,label)
+
+ amount = self.wallet.requested_amounts.get(address,None)
+ amount_str = format_satoshis( amount, False, self.wallet.num_zeros ) if amount is not None else ""
+ item.setData(3,0,amount_str)
+
+ c, u = self.wallet.get_addr_balance(address)
+ balance = format_satoshis( c + u, False, self.wallet.num_zeros )
+ item.setData(4,0,balance)
+
+ if self.receive_tab_mode == 1:
+ if address in self.wallet.frozen_addresses:
+ item.setBackgroundColor(1, QColor('lightblue'))
+ elif address in self.wallet.prioritized_addresses:
+ item.setBackgroundColor(1, QColor('lightgreen'))
+
+
def update_receive_tab(self):
l = self.receive_list
+
l.clear()
- l.setColumnHidden(0,not self.detailed_view)
- l.setColumnHidden(3,not self.detailed_view)
- l.setColumnHidden(4,not self.detailed_view)
- l.setColumnWidth(0, 50)
+ l.setColumnHidden(0, not self.receive_tab_mode == 1)
+ l.setColumnHidden(3, not self.receive_tab_mode == 2)
+ l.setColumnHidden(4, self.receive_tab_mode == 0)
+ l.setColumnHidden(5, not self.receive_tab_mode == 1)
+ l.setColumnWidth(0, 50)
l.setColumnWidth(1, 310)
- l.setColumnWidth(2, 250)
- l.setColumnWidth(3, 130)
- l.setColumnWidth(4, 10)
+ l.setColumnWidth(2, 200)
+ l.setColumnWidth(3, 130)
+ l.setColumnWidth(4, 130)
+ l.setColumnWidth(5, 10)
gap = 0
is_red = False
for address in self.wallet.all_addresses():
- if self.wallet.is_change(address) and not self.detailed_view:
+ if self.wallet.is_change(address) and self.receive_tab_mode != 1:
continue
- label = self.wallet.labels.get(address,'')
n = 0
h = self.wallet.history.get(address,[])
if address in self.wallet.addresses:
gap = 0
- c, u = self.wallet.get_addr_balance(address)
- balance = format_satoshis( c + u, False, self.wallet.num_zeros )
- flags = self.wallet.get_address_flags(address)
- item = QTreeWidgetItem( [ flags, address, label, balance, num_tx] )
-
+ item = QTreeWidgetItem( [ '', address, '', '', '', num_tx] )
item.setFont(0, QFont(MONOSPACE_FONT))
item.setFont(1, QFont(MONOSPACE_FONT))
item.setFont(3, QFont(MONOSPACE_FONT))
- if address in self.wallet.frozen_addresses:
- item.setBackgroundColor(1, QColor('lightblue'))
- elif address in self.wallet.prioritized_addresses:
- item.setBackgroundColor(1, QColor('lightgreen'))
+ self.update_receive_item(item)
if is_red and address in self.wallet.addresses:
item.setBackgroundColor(1, QColor('red'))
l.addTopLevelItem(item)
l = self.contacts_list
l.clear()
- l.setColumnHidden(2, not self.detailed_view)
l.setColumnWidth(0, 350)
l.setColumnWidth(1, 330)
l.setColumnWidth(2, 100)
return textbox
def create_status_bar(self):
+ self.status_text = ""
sb = QStatusBar()
sb.setFixedHeight(35)
+ qtVersion = qVersion()
+ if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
+ sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) )
if self.wallet.seed:
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
sb.addPermanentWidget( self.status_button )
self.setStatusBar(sb)
+
+ def go_lite(self):
+ import gui_lite
+ self.hide()
+ if self.lite:
+ self.lite.mini.show()
+ else:
+ self.lite = gui_lite.ElectrumGui(self.wallet, self.config, self)
+ self.lite.main(None)
def new_contact_dialog(self):
text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
password = None
try:
- seed = wallet.pw_decode(wallet.seed, password)
+ seed = wallet.decode_seed(password)
except:
- QMessageBox.warning(parent, _('Error'),
- _('Incorrect Password'), _('OK'))
+ QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
return
dialog = QDialog(None)
copy_button = QPushButton(_("Copy to Clipboard"))
copy_button.clicked.connect(copy_function)
- show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
+ show_qr_function = lambda: ElectrumWindow.show_qrcode(_("Seed"), seed)
qr_button = QPushButton(_("View as QR Code"))
qr_button.clicked.connect(show_qr_function)
dialog.exec_()
@staticmethod
- def show_seed_qrcode(seed):
- if not seed: return
+ def show_qrcode(title, data):
+ if not data: return
d = QDialog(None)
d.setModal(1)
- d.setWindowTitle(_("Seed"))
+ d.setWindowTitle(title)
d.setMinimumSize(270, 300)
vbox = QVBoxLayout()
- vbox.addWidget(QRCodeWidget(seed))
+ qrw = QRCodeWidget(data)
+ vbox.addWidget(qrw, 1)
+ vbox.addWidget(QLabel(data), 0, Qt.AlignHCenter)
hbox = QHBoxLayout()
hbox.addStretch(1)
- b = QPushButton(_("OK"))
+
+ def print_qr(self):
+ filename = "qrcode.bmp"
+ bmp.save_qrcode(qrw.qr, filename)
+ QMessageBox.information(None, _('Message'), _("QR code saved to file") + " " + filename, _('OK'))
+
+ b = QPushButton(_("Print"))
+ hbox.addWidget(b)
+ b.clicked.connect(print_qr)
+
+ b = QPushButton(_("Close"))
hbox.addWidget(b)
b.clicked.connect(d.accept)
d.setLayout(vbox)
d.exec_()
-
- def show_address_qrcode(self,address):
+ def sign_message(self,address):
if not address: return
d = QDialog(self)
d.setModal(1)
- d.setWindowTitle(address)
- d.setMinimumSize(270, 350)
- vbox = QVBoxLayout()
- qrw = QRCodeWidget(address)
- vbox.addWidget(qrw)
+ d.setWindowTitle('Sign Message')
+ d.setMinimumSize(410, 290)
- hbox = QHBoxLayout()
- amount_e = QLineEdit()
- hbox.addWidget(QLabel(_('Amount')))
- hbox.addWidget(amount_e)
- vbox.addLayout(hbox)
+ tab_widget = QTabWidget()
+ tab = QWidget()
+ layout = QGridLayout(tab)
+
+ sign_address = QLineEdit()
+
+ sign_address.setText(address)
+ layout.addWidget(QLabel(_('Address')), 1, 0)
+ layout.addWidget(sign_address, 1, 1)
- #hbox = QHBoxLayout()
- #label_e = QLineEdit()
- #hbox.addWidget(QLabel('Label'))
- #hbox.addWidget(label_e)
- #vbox.addLayout(hbox)
-
- def amount_changed():
- amount = numbify(amount_e)
- #label = str( label_e.getText() )
- if amount is not None:
- qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
+ sign_message = QTextEdit()
+ layout.addWidget(QLabel(_('Message')), 2, 0)
+ layout.addWidget(sign_message, 2, 1)
+ layout.setRowStretch(2,3)
+
+ sign_signature = QTextEdit()
+ layout.addWidget(QLabel(_('Signature')), 3, 0)
+ layout.addWidget(sign_signature, 3, 1)
+ layout.setRowStretch(3,1)
+
+ def do_sign():
+ if self.wallet.use_encryption:
+ password = self.password_dialog()
+ if not password:
+ return
else:
- qrw.set_addr( address )
- qrw.repaint()
+ password = None
- def do_save():
- bmp.save_qrcode(qrw.qr, "qrcode.bmp")
- self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
-
- amount_e.textChanged.connect( amount_changed )
+ try:
+ signature = self.wallet.sign_message(str(sign_address.text()), str(sign_message.toPlainText()), password)
+ sign_signature.setText(signature)
+ except BaseException, e:
+ self.show_message(str(e))
+ return
hbox = QHBoxLayout()
- hbox.addStretch(1)
- b = QPushButton(_("Save"))
- b.clicked.connect(do_save)
+ b = QPushButton(_("Sign"))
hbox.addWidget(b)
+ b.clicked.connect(do_sign)
b = QPushButton(_("Close"))
+ b.clicked.connect(d.accept)
+ hbox.addWidget(b)
+ layout.addLayout(hbox, 4, 1)
+ tab_widget.addTab(tab, "Sign")
+
+
+ tab = QWidget()
+ layout = QGridLayout(tab)
+
+ verify_address = QLineEdit()
+ layout.addWidget(QLabel(_('Address')), 1, 0)
+ layout.addWidget(verify_address, 1, 1)
+
+ verify_message = QTextEdit()
+ layout.addWidget(QLabel(_('Message')), 2, 0)
+ layout.addWidget(verify_message, 2, 1)
+ layout.setRowStretch(2,3)
+
+ verify_signature = QTextEdit()
+ layout.addWidget(QLabel(_('Signature')), 3, 0)
+ layout.addWidget(verify_signature, 3, 1)
+ layout.setRowStretch(3,1)
+
+ def do_verify():
+ try:
+ self.wallet.verify_message(verify_address.text(), str(verify_signature.toPlainText()), str(verify_message.toPlainText()))
+ self.show_message("Signature verified")
+ except BaseException, e:
+ self.show_message(str(e))
+ return
+
+ hbox = QHBoxLayout()
+ b = QPushButton(_("Verify"))
+ b.clicked.connect(do_verify)
hbox.addWidget(b)
+ b = QPushButton(_("Close"))
b.clicked.connect(d.accept)
+ hbox.addWidget(b)
+ layout.addLayout(hbox, 4, 1)
+ tab_widget.addTab(tab, "Verify")
- vbox.addLayout(hbox)
+ vbox = QVBoxLayout()
+ vbox.addWidget(tab_widget)
d.setLayout(vbox)
d.exec_()
+
+ def toggle_QR_window(self, show):
+ if show and not self.qr_window:
+ self.qr_window = QR_Window()
+ self.qr_window.setVisible(True)
+ self.qr_window_geometry = self.qr_window.geometry()
+ item = self.receive_list.currentItem()
+ if item:
+ address = str(item.text(1))
+ label = self.wallet.labels.get(address)
+ amount = self.wallet.requested_amounts.get(address)
+ self.qr_window.set_content( address, label, amount )
+
+ elif show and self.qr_window and not self.qr_window.isVisible():
+ self.qr_window.setVisible(True)
+ self.qr_window.setGeometry(self.qr_window_geometry)
+
+ elif not show and self.qr_window and self.qr_window.isVisible():
+ self.qr_window_geometry = self.qr_window.geometry()
+ self.qr_window.setVisible(False)
+
+ #self.print_button.setHidden(self.qr_window is None or not self.qr_window.isVisible())
+ self.receive_list.setColumnHidden(3, self.qr_window is None or not self.qr_window.isVisible())
+ self.receive_list.setColumnWidth(2, 200)
+
+
def question(self, msg):
return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
vbox = QVBoxLayout()
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')
+ 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.")
+ msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
+ +_("Leave these fields empty if you want to disable encryption.")
vbox.addWidget(QLabel(msg))
grid = QGridLayout()
new_password2 = unicode(conf_pw.text())
try:
- seed = wallet.pw_decode( wallet.seed, password)
+ seed = wallet.decode_seed(password)
except:
QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
return
if new_password != new_password2:
QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
- return
+ return ElectrumWindow.change_password_dialog(wallet, parent) # Retry
wallet.update_password(seed, password, new_password)
return True
+ def do_import_labels(self):
+ labelsFile = QFileDialog.getOpenFileName(QWidget(), "Open text file", util.user_dir(), self.tr("Text Files (labels.dat)"))
+ if not labelsFile: return
+ try:
+ f = open(labelsFile, 'r')
+ data = f.read()
+ f.close()
+ self.wallet.labels = json.loads(data)
+ self.wallet.save()
+ QMessageBox.information(None, "Labels imported", "Your labels where imported from '%s'" % str(labelsFile))
+ except (IOError, os.error), reason:
+ QMessageBox.critical(None, "Unable to export labels", "Electrum was unable to export your labels.\n" + str(reason))
+
+
+ def do_export_labels(self):
+ labels = self.wallet.labels
+ try:
+ labelsFile = util.user_dir() + '/labels.dat'
+ f = open(labelsFile, 'w+')
+ json.dump(labels, f)
+ f.close()
+ QMessageBox.information(None, "Labels exported", "Your labels where exported to '%s'" % str(labelsFile))
+ except (IOError, os.error), reason:
+ QMessageBox.critical(None, "Unable to export labels", "Electrum was unable to export your labels.\n" + str(reason))
+
+ def do_export_history(self):
+ from gui_lite import csv_transaction
+ csv_transaction(self.wallet)
+
+ def do_import_privkey(self):
+ if not self.wallet.imported_keys:
+ r = QMessageBox.question(None, _('Warning'), _('Warning: Imported keys are not recoverable from seed.') + ' ' \
+ + _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '\n\n' \
+ + _('Are you sure you understand what you are doing?'), 3, 4)
+ if r == 4: return
+
+ text, ok = QInputDialog.getText(self, _('Import private key'), _('Private Key') + ':')
+ if not ok: return
+ sec = str(text)
+ if self.wallet.use_encryption:
+ password = self.password_dialog()
+ if not password:
+ return
+ else:
+ password = None
+ try:
+ addr = self.wallet.import_key(sec, password)
+ if not addr:
+ QMessageBox.critical(None, "Unable to import key", "error")
+ else:
+ QMessageBox.information(None, "Key imported", addr)
+ self.update_receive_tab()
+ self.update_history_tab()
+ except BaseException as e:
+ QMessageBox.critical(None, "Unable to import key", str(e))
def settings_dialog(self):
d = QDialog(self)
+ d.setWindowTitle(_('Electrum Settings'))
d.setModal(1)
vbox = QVBoxLayout()
- msg = _('Here are the settings of your wallet.') + '\n'\
- + _('For more explanations, click on the help buttons next to each field.')
- label = QLabel(msg)
- label.setFixedWidth(250)
- label.setWordWrap(True)
- label.setAlignment(Qt.AlignJustify)
- vbox.addWidget(label)
+ tabs = QTabWidget(self)
+ vbox.addWidget(tabs)
- grid = QGridLayout()
- grid.setSpacing(8)
- vbox.addLayout(grid)
+ tab1 = QWidget()
+ grid_ui = QGridLayout(tab1)
+ grid_ui.setColumnStretch(0,1)
+ tabs.addTab(tab1, _('Display') )
+
+ nz_label = QLabel(_('Display zeros'))
+ grid_ui.addWidget(nz_label, 3, 0)
+ nz_e = QLineEdit()
+ nz_e.setText("%d"% self.wallet.num_zeros)
+ grid_ui.addWidget(nz_e, 3, 1)
+ msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
+ grid_ui.addWidget(HelpButton(msg), 3, 2)
+ nz_e.textChanged.connect(lambda: numbify(nz_e,True))
+ if not self.config.is_modifiable('num_zeros'):
+ for w in [nz_e, nz_label]: w.setEnabled(False)
+
+ gui_label=QLabel(_('Default GUI') + ':')
+ grid_ui.addWidget(gui_label , 7, 0)
+ gui_combo = QComboBox()
+ gui_combo.addItems(['Lite', 'Classic'])
+ index = gui_combo.findText(self.config.get("gui","classic").capitalize())
+ if index==-1: index = 1
+ gui_combo.setCurrentIndex(index)
+ grid_ui.addWidget(gui_combo, 7, 1)
+ grid_ui.addWidget(HelpButton(_('Select which GUI mode to use at start up.'+'\n'+'Note: use the command line to access the "text" and "gtk" GUIs')), 7, 2)
+ if not self.config.is_modifiable('gui'):
+ for w in [gui_combo, gui_label]: w.setEnabled(False)
+
+ lang_label=QLabel(_('Language') + ':')
+ grid_ui.addWidget(lang_label , 8, 0)
+ lang_combo = QComboBox()
+ from i18n import languages
+ lang_combo.addItems(languages.values())
+ try:
+ index = languages.keys().index(self.config.get("language",''))
+ except:
+ index = 0
+ lang_combo.setCurrentIndex(index)
+ grid_ui.addWidget(lang_combo, 8, 1)
+ grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart). ')), 8, 2)
+ if not self.config.is_modifiable('language'):
+ for w in [lang_combo, lang_label]: w.setEnabled(False)
+
+ currencies = self.exchanger.get_currencies()
+ currencies.insert(0, "None")
+
+ cur_label=QLabel(_('Currency') + ':')
+ grid_ui.addWidget(cur_label , 9, 0)
+ cur_combo = QComboBox()
+ cur_combo.addItems(currencies)
+ try:
+ index = currencies.index(self.config.get('currency', "None"))
+ except:
+ index = 0
+ cur_combo.setCurrentIndex(index)
+ grid_ui.addWidget(cur_combo, 9, 1)
+ grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes. ')), 9, 2)
+
+ view_label=QLabel(_('Receive Tab') + ':')
+ grid_ui.addWidget(view_label , 10, 0)
+ view_combo = QComboBox()
+ view_combo.addItems([_('Simple'), _('Advanced'), _('Point of Sale')])
+ view_combo.setCurrentIndex(self.receive_tab_mode)
+ grid_ui.addWidget(view_combo, 10, 1)
+ hh = _('This selects the interaction mode of the "Receive" tab. ') + '\n\n' \
+ + _('Simple') + ': ' + _('Show only addresses and labels.') + '\n\n' \
+ + _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' \
+ + _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n'
+
+ grid_ui.addWidget(HelpButton(hh), 10, 2)
+ # wallet tab
+ tab2 = QWidget()
+ grid_wallet = QGridLayout(tab2)
+ grid_wallet.setColumnStretch(0,1)
+ tabs.addTab(tab2, _('Wallet') )
+
fee_label = QLabel(_('Transaction fee'))
- grid.addWidget(fee_label, 2, 0)
+ grid_wallet.addWidget(fee_label, 0, 0)
fee_e = QLineEdit()
fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
- grid.addWidget(fee_e, 2, 1)
+ grid_wallet.addWidget(fee_e, 0, 1)
msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
+ _('Recommended value') + ': 0.001'
- grid.addWidget(HelpButton(msg), 2, 2)
+ grid_wallet.addWidget(HelpButton(msg), 0, 2)
fee_e.textChanged.connect(lambda: numbify(fee_e,False))
if not self.config.is_modifiable('fee'):
for w in [fee_e, fee_label]: w.setEnabled(False)
- nz_label = QLabel(_('Display zeros'))
- grid.addWidget(nz_label, 3, 0)
- nz_e = QLineEdit()
- nz_e.setText("%d"% self.wallet.num_zeros)
- grid.addWidget(nz_e, 3, 1)
- msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
- grid.addWidget(HelpButton(msg), 3, 2)
- nz_e.textChanged.connect(lambda: numbify(nz_e,True))
- if not self.config.is_modifiable('num_zeros'):
- for w in [nz_e, nz_label]: w.setEnabled(False)
-
- usechange_cb = QCheckBox(_('Use change addresses'))
- grid.addWidget(usechange_cb, 5, 0)
- usechange_cb.setChecked(self.wallet.use_change)
- grid.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
- if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
+ usechange_label = QLabel(_('Use change addresses'))
+ grid_wallet.addWidget(usechange_label, 1, 0)
+ usechange_combo = QComboBox()
+ usechange_combo.addItems(['Yes', 'No'])
+ usechange_combo.setCurrentIndex(not self.wallet.use_change)
+ grid_wallet.addWidget(usechange_combo, 1, 1)
+ grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 1, 2)
+ if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False)
gap_label = QLabel(_('Gap limit'))
- grid.addWidget(gap_label, 6, 0)
+ grid_wallet.addWidget(gap_label, 2, 0)
gap_e = QLineEdit()
gap_e.setText("%d"% self.wallet.gap_limit)
- grid.addWidget(gap_e, 6, 1)
+ grid_wallet.addWidget(gap_e, 2, 1)
msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
+ _('You may increase it if you need more receiving addresses.') + '\n\n' \
+ _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
+ _('Warning') + ': ' \
+ _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
+ _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n'
- grid.addWidget(HelpButton(msg), 6, 2)
+ grid_wallet.addWidget(HelpButton(msg), 2, 2)
gap_e.textChanged.connect(lambda: numbify(nz_e,True))
if not self.config.is_modifiable('gap_limit'):
for w in [gap_e, gap_label]: w.setEnabled(False)
+
+ grid_wallet.setRowStretch(3,1)
+
+
+ # wallet tab
+ tab3 = QWidget()
+ grid_io = QGridLayout(tab3)
+ grid_io.setColumnStretch(0,1)
+ tabs.addTab(tab3, _('Import/Export') )
- gui_label=QLabel(_('Default GUI') + ':')
- grid.addWidget(gui_label , 7, 0)
- gui_combo = QComboBox()
- gui_combo.addItems(['Lite', 'Classic', 'Gtk', 'Text'])
- index = gui_combo.findText(self.config.get("gui","classic").capitalize())
- if index==-1: index = 1
- gui_combo.setCurrentIndex(index)
- grid.addWidget(gui_combo, 7, 1)
- grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
- if not self.config.is_modifiable('gui'):
- for w in [gui_combo, gui_label]: w.setEnabled(False)
+ grid_io.addWidget(QLabel(_('Labels')), 1, 0)
+ grid_io.addWidget(EnterButton(_("Export"), self.do_export_labels), 1, 1)
+ grid_io.addWidget(EnterButton(_("Import"), self.do_import_labels), 1, 2)
+ grid_io.addWidget(HelpButton('Export your labels as json'), 1, 3)
+
+ grid_io.addWidget(QLabel(_('History')), 2, 0)
+ grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1)
+ grid_io.addWidget(HelpButton('Export your transaction history as csv'), 2, 3)
+ grid_io.addWidget(QLabel(_('Private key')), 3, 0)
+ grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2)
+ grid_io.addWidget(HelpButton('Import private key'), 3, 3)
+
+ grid_io.setRowStretch(4,1)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
self.update_history_tab()
self.update_receive_tab()
- if self.wallet.use_change != usechange_cb.isChecked():
- self.wallet.use_change = usechange_cb.isChecked()
+ usechange_result = usechange_combo.currentIndex() == 0
+ if self.wallet.use_change != usechange_result:
+ self.wallet.use_change = usechange_result
self.config.set_key('use_change', self.wallet.use_change, True)
try:
self.config.set_key('gap_limit', self.wallet.gap_limit, True)
else:
QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
-
- self.config.set_key("gui", str(gui_combo.currentText()).lower(), True)
+ need_restart = False
+
+ gui_request = str(gui_combo.currentText()).lower()
+ if gui_request != self.config.get('gui'):
+ self.config.set_key('gui', gui_request, True)
+ need_restart = True
+
+ lang_request = languages.keys()[lang_combo.currentIndex()]
+ if lang_request != self.config.get('language'):
+ self.config.set_key("language", lang_request, True)
+ need_restart = True
+
+ cur_request = str(currencies[cur_combo.currentIndex()])
+ if cur_request != self.config.get('currency', "None"):
+ self.config.set_key('currency', cur_request, True)
+ self.update_wallet()
+
+ if need_restart:
+ QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK'))
+
+ self.receive_tab_set_mode(view_combo.currentIndex())
@staticmethod
servers_list_widget.setMaximumHeight(150)
servers_list_widget.setColumnWidth(0, 240)
for _host in servers_list.keys():
- _type = 'pruning' if servers_list[_host].get('pruning') else 'full'
+ _type = 'P' if servers_list[_host].get('pruning') else 'F'
servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ _host, _type ] ))
def change_server(host, protocol=None):
if not wallet.config.is_modifiable('server'):
for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False)
+ # auto cycle
+ autocycle_cb = QCheckBox('Try random servers if disconnected')
+ autocycle_cb.setChecked(wallet.config.get('auto_cycle', False))
+ grid.addWidget(autocycle_cb, 3, 1, 3, 2)
+ if not wallet.config.is_modifiable('auto_cycle'): autocycle_cb.setEnabled(False)
+
# proxy setting
proxy_mode = QComboBox()
proxy_host = QLineEdit()
wallet.config.set_key("proxy", proxy, True)
wallet.config.set_key("server", server, True)
interface.set_server(server, proxy)
-
+ wallet.config.set_key('auto_cycle', autocycle_cb.isChecked(), True)
return True
def closeEvent(self, event):